From huntse-agent-skills
Scaffold a new Python project with devenv (reproducible toolchain via Nix), secretspec (declarative secrets), uv (env/deps), uv_build (PEP 517 backend), ruff (format + lint), ty (type check), and pytest (TDD). Wires up a plain .git/hooks/pre-commit script, a trunk-based squash-rebase shipping flow, and a strict CLAUDE.md so coding agents follow the workflow. Use when the user asks to start, scaffold, bootstrap, or initialize a new Python project, library, or package — or to add this toolchain to an empty/minimal repo.
How this skill is triggered — by the user, by Claude, or both
Slash command
/huntse-agent-skills:setup-python-projectThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Scaffolds a new Python project with a strict, opinionated toolchain. Run once per new project.
Scaffolds a new Python project with a strict, opinionated toolchain. Run once per new project.
The dev environment itself is provisioned by devenv (Nix-based), so the only host prerequisites are devenv and git (plus direnv if you want the shell to auto-activate on cd — see Step 3). devenv supplies a pinned python, uv, and the secretspec CLI inside the shell; ruff, ty, and pytest are installed per-project as dev dependencies via uv. Secrets are declared (not stored) in secretspec.toml and supplied per-machine from a provider like the system keyring.
Works on both Linux and macOS (Intel and Apple Silicon) — devenv defaults to the host system and the
keyringsecretspec provider maps to the macOS Keychain on Mac and the Secret Service on Linux. The one macOS caveat is that building Linux containers needs a separately-configured Linux builder.
Before scaffolding, ask: library (importable package, no entry point) or application (importable package + console script). This changes the uv init flags and whether you create a __main__.py / [project.scripts] entry.
In an empty directory (run git init first if needed). If uv isn't yet on PATH, run it through Nix so you don't need a global install — nix run nixpkgs#uv -- init …:
# Library
uv init --lib --build-backend uv
# Application (CLI)
uv init --package --build-backend uv
Both produce a src/ layout and a pyproject.toml already configured with uv_build. Verify [build-system] in pyproject.toml shows build-backend = "uv_build"; if not, replace it with the snippet from templates/pyproject-snippets.toml.
Copy the devenv templates into the project root:
cp <skill-dir>/templates/devenv.nix devenv.nix
cp <skill-dir>/templates/devenv.yaml devenv.yaml
cp <skill-dir>/templates/envrc .envrc # auto-activation; see below
cp <skill-dir>/templates/gitignore .gitignore # merge if one already exists
devenv.nix enables languages.python pinned to Python 3.13 (languages.python.version) and puts uv, git, and the secretspec CLI on PATH as plain packages. To target a different version, change version in devenv.nix and keep requires-python in pyproject.toml in sync. It deliberately does not use languages.python.uv.enable / uv.sync.enable (the automatic venv sync has caused cross-project venv leakage); you run uv sync yourself. devenv.yaml pins nixpkgs and turns on the secretspec integration.
.envrc is what makes the toolchain load automatically when you cd into the project. It's committed (the .direnv cache is gitignored). It sources the host devenv's own direnvrc (eval "$(devenv direnvrc)") so there's no sha256 to keep in sync, then calls use devenv.
Enter the shell so the rest of the steps use the pinned tooling — pick one:
direnv allow # one-time trust of .envrc; thereafter auto-activates on cd (needs direnv installed)
# or, without direnv:
devenv shell # manual, per-session
From here, python, uv, and secretspec come from devenv — don't install them globally. If you're not using direnv, you can delete .envrc.
uv add --dev ruff ty pytest
Set requires-python = ">=3.13" in pyproject.toml's [project] table so it matches the interpreter devenv pins (uv init seeds this from the host Python, which may differ). Then append the [tool.ruff], [tool.ty], and [tool.pytest.ini_options] blocks from templates/pyproject-snippets.toml, adjusting target-version (e.g. py313) to match requires-python.
Create the tests directory:
mkdir -p tests && touch tests/__init__.py
Write one placeholder test so the suite has something to run (e.g. tests/test_smoke.py with def test_smoke(): assert True). Replace it as soon as real tests exist.
Copy the schema template and set the project name:
cp <skill-dir>/templates/secretspec.toml secretspec.toml
# replace PROJECT_NAME with the real project name
secretspec.toml is the schema — it lists which secrets the app needs and IS committed; it never holds values. Each developer/CI/prod supplies values from their own provider. Uncomment and adapt the example secrets under [profiles.default] (and tighten [profiles.production]) to match the project. Common operations:
secretspec set DATABASE_URL # store a value in the configured provider (keyring by default)
secretspec check # verify all required secrets are present
secretspec run -- uv run myapp # run a command with secrets injected as env vars
In devenv.nix, expose only the secrets a process needs via config.secretspec.secrets.<NAME> (there's a commented example in the template). Prefer secretspec run -- for production-style runs over leaking secrets into the whole shell.
Copy templates/pre-commit into the project as scripts/hooks/pre-commit, make it executable, and symlink it into .git/hooks/:
mkdir -p scripts/hooks
cp {skill-dir}/templates/pre-commit scripts/hooks/pre-commit
chmod +x scripts/hooks/pre-commit
ln -sf ../../scripts/hooks/pre-commit .git/hooks/pre-commit
The hook runs ruff format → ruff check --fix → ty check → pytest. Re-staging is automatic for files ruff rewrote. It calls tools via uv run, so run commits from inside the devenv shell.
Copy templates/CLAUDE.md to the project root. It encodes:
secretspec.toml, never commit values).uv-only tooling (no pip/poetry/venv).feat/* branches, squash-merge to main, never commit direct to main.--no-verify.Customize project-specific bits (name, domain rules) but keep the workflow section verbatim.
git config pull.rebase true
git config branch.autosetuprebase always
git config rerere.enabled true
If using GitHub, also set the repo's default merge method to "Squash and merge" in repo settings, or rely on gh pr merge --squash --delete-branch.
git add -A
git commit -m "Initial scaffold: devenv + secretspec + uv + ruff + ty + pytest + pre-commit hook"
This is the only commit ever made directly to main. From here on, all work goes through feat/* branches and squash-merge PRs.
uv run pytest -k {name} and confirm it fails for the right reason.uv run pytest before committing.The pre-commit hook enforces all of format/lint/typecheck/tests at commit time — but agents should run the relevant subset locally during the loop rather than waiting for the hook.
After scaffolding, confirm:
devenv shell enters cleanly and uv/python/secretspec resolve to the devenv-provided versions..envrc exists, direnv allow succeeds, and cd-ing out and back in auto-activates the shell.uv run pytest passes.uv run ruff check is clean.uv run ty check is clean.secretspec check runs (no required secrets unmet for the default profile)..git/hooks/pre-commit exists, is executable, and blocks a deliberately broken commit.CLAUDE.md, devenv.nix, devenv.yaml, and secretspec.toml are present at repo root..gitignore ignores .devenv* and local .env files.main with one initial commit.Provides behavioral guidelines to reduce common LLM coding mistakes, focusing on simplicity, surgical changes, assumption surfacing, and verifiable success criteria.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub huntse/agent-skills --plugin huntse-agent-skills