Guides

Claude Code setup for Python projects

A good Python setup for Claude Code has three parts: a short CLAUDE.md that tells it which commands to run, a hook that runs your linters after every edit so it can't forget, and a fast test command it can loop on. This guide uses uv, ruff, mypy and pytest, but the pattern works with any toolchain.

1. Write a CLAUDE.md with the exact commands

Put it at ./CLAUDE.md or ./.claude/CLAUDE.md. Run /init to generate a starting point, then trim it. Claude Code's docs suggest keeping each file under about 200 lines and making instructions concrete enough to verify. See the CLAUDE.md template for a fuller structure.

# Project

## Commands
- Install: `uv sync`
- Run anything through uv: `uv run <cmd>` (never bare `python` or `pip`)
- Add a dependency: `uv add <pkg>` (dev: `uv add --dev <pkg>`); never edit uv.lock by hand
- Lint: `uv run ruff check .`   Format: `uv run ruff format .`
- Types: `uv run mypy src`
- Tests: `uv run pytest -q`; one test: `uv run pytest -q path/to/test_x.py -k name`

## Conventions
- Python 3.12, type hints on all public functions
- Source in `src/`, tests mirror it under `tests/`
- Never modify or skip existing tests to make them pass; ask first

CLAUDE.md is context, not enforcement. Claude usually follows it, but anything that must happen every time belongs in a hook.

2. Configure the tools in pyproject.toml

Keep tool config in the repo so Claude, you and CI all agree:

[tool.ruff]
line-length = 100

[tool.ruff.lint]
select = ["E", "F", "I", "B", "UP"]

[tool.mypy]
strict = true
files = ["src"]

[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-q"

3. Run ruff and mypy after every edit with a hook

A PostToolUse hook runs after Claude writes a file. If it exits with code 2, its stderr is shown to Claude, so type errors come back as feedback it can fix immediately. Add this to .claude/settings.json (committed, so the team shares it):

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          { "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/py-check.sh", "timeout": 120 }
        ]
      }
    ]
  }
}

The script reads the JSON payload from stdin and takes tool_input.file_path:

#!/usr/bin/env bash
# .claude/hooks/py-check.sh   (chmod +x it)
f=$(python3 -c 'import json,sys;print(json.load(sys.stdin).get("tool_input",{}).get("file_path",""))')
case "$f" in *.py) ;; *) exit 0 ;; esac
cd "$CLAUDE_PROJECT_DIR" || exit 0

uv run ruff format -q "$f"
uv run ruff check --fix -q "$f" || { echo "ruff found unfixable issues in $f" >&2; exit 2; }
case "$f" in
  */src/*) uv run mypy "$f" 2>&1 >&2 || exit 2 ;;
esac
exit 0

Test it without Claude first:

echo '{"tool_input":{"file_path":"'"$PWD"'/src/app.py"}}' | .claude/hooks/py-check.sh; echo "exit=$?"

Run /hooks in Claude Code to confirm it is registered. You can also generate and test hook configs with the free hook builder.

4. Let Claude loop on pytest

Don't run the whole suite in a hook; it is slow and runs on every edit. Instead, tell Claude to run the narrowest test that covers the change, then the full suite before it finishes:

Fix the bug described below. First write a failing pytest that reproduces it and
show me it fails. Then fix it, run that test, then run `uv run pytest -q`.
Do not edit existing tests to make them pass.

For the full red-green workflow, see test-driven development with Claude Code.

Common mistakes

MistakeFix
Claude runs bare pip install or pythonState "always use uv run / uv add" in CLAUDE.md
Hook fails with command not foundUse uv run ruff, not ruff, so the project environment is used
mypy on one file reports errors from other modulesExpected. Fix the cause, or use mypy src if you prefer whole-project checks
Hook blocks on a legacy file full of errorsScope the case pattern to new code, or add per-file ignores in pyproject.toml
Hook path breaks after Claude cdsReference scripts via "$CLAUDE_PROJECT_DIR"

Where to go next

Skip the setup: get the tested versions

Keelwork bundles 10 workflow skills, 5 tested safety hooks (including a full guard-bash and a secret scanner), 3 subagents and 5 CLAUDE.md templates, with a one-command installer that safely merges into your settings.

Get Keelwork — $24 →