Migrates 20 topic files from claude-foundations/best-practices/ to this standalone repo. Adds BESTPRACTICES.md index, CLAUDE.md conventions, and updated README.md. Container agents clone this repo to /best-practices. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
95 lines
3.9 KiB
Markdown
95 lines
3.9 KiB
Markdown
# Linting & Formatting
|
|
|
|
Automated code formatting and linting integrated into the Claude Code workflow via PostToolUse hooks and git pre-commit hooks.
|
|
|
|
## Architecture
|
|
|
|
```
|
|
claude-foundations/formatters/ ← canonical formatter scripts (one per extension)
|
|
project/formatters/ ← symlinks to the formatters this project uses
|
|
~/.claude/hooks/post-edit-lint.sh ← dispatches to project formatters on Edit/Write
|
|
.git/hooks/pre-commit ← symlink to pre-commit-lint.sh
|
|
```
|
|
|
|
Projects opt in by symlinking only the formatters they need. No formatter = no-op.
|
|
|
|
## Tool Choices by Language
|
|
|
|
| Language | Formatter | Linter | Config File |
|
|
|----------|-----------|--------|-------------|
|
|
| Python | `ruff format` | `ruff check` | `pyproject.toml` |
|
|
| Shell | `shfmt` | `shellcheck` | `.editorconfig` |
|
|
| TypeScript/JavaScript | `biome format` | `biome check` | `biome.json` |
|
|
| SQL | `sqlfluff fix` | `sqlfluff lint` | `.sqlfluff` |
|
|
| JSON/YAML/Markdown | `prettier` | — | `.prettierrc` |
|
|
|
|
### Why these tools?
|
|
- **ruff**: Rust-based, extremely fast, replaces black + isort + flake8 + pyflakes in one tool
|
|
- **biome**: Rust-based, replaces prettier + eslint for JS/TS in one tool
|
|
- **shfmt + shellcheck**: The standard combo for shell scripts; shfmt formats, shellcheck catches bugs
|
|
- **prettier**: Handles JSON/YAML/MD well; biome doesn't cover these yet
|
|
|
|
## Formatter Script Contract
|
|
|
|
Every script in `formatters/` follows the same interface:
|
|
|
|
- **Input:** `$1` = absolute file path
|
|
- **Behaviour:** format the file in place, then lint it
|
|
- **Stdout:** suppressed
|
|
- **Stderr:** lint warnings/errors that couldn't be auto-fixed
|
|
- **Exit code:** `0` if clean, `1` if lint errors remain
|
|
- **Missing tools:** exit `0` silently (`command -v` check)
|
|
- **No `set -e`:** individual commands may fail; execution must continue
|
|
|
|
## PostToolUse Integration
|
|
|
|
The `post-edit-lint.sh` hook fires on Edit/Write/MultiEdit:
|
|
|
|
1. Extracts `file_path` and `cwd` from stdin JSON
|
|
2. Walks up from `cwd` to find `formatters/` directory
|
|
3. Creates a checkpoint using `git hash-object` (fast, no commits)
|
|
4. Runs the matching formatter
|
|
5. On clean pass: removes checkpoint, exits 0 (silent)
|
|
6. On lint errors: keeps checkpoint, exits 2 (feeds errors to Claude)
|
|
|
|
Exit code 2 is special for PostToolUse — it feeds stderr back to Claude as feedback without blocking the edit.
|
|
|
|
## Checkpoint Mechanism
|
|
|
|
Uses `git hash-object -w` to store pre-format content as an orphan blob (~1ms, no commits, no stash). The `.pre-lint` file stores only a 40-char SHA. Falls back to `cp` outside git repos.
|
|
|
|
```bash
|
|
# Revert after lint errors:
|
|
git cat-file blob "$(cat file.py.pre-lint)" > file.py
|
|
rm file.py.pre-lint
|
|
```
|
|
|
|
## Subagent Fix Pattern
|
|
|
|
When lint errors occur, Claude sees the errors via stderr. The recommended workflow:
|
|
|
|
1. Claude reports the errors to the user
|
|
2. If the user says "fix", Claude spawns a subagent via the Agent tool
|
|
3. The subagent reads the file and errors, makes Edit calls to fix them
|
|
4. Each Edit triggers the hook again (re-format, re-lint) in the subagent
|
|
5. Fix iterations stay in the subagent's context, not the main conversation
|
|
|
|
## Pre-commit Integration
|
|
|
|
`pre-commit-lint.sh` reuses the same formatter scripts:
|
|
|
|
1. Iterates staged files (Added/Modified only)
|
|
2. Runs matching formatters
|
|
3. Re-stages formatted files
|
|
4. Exits non-zero if lint errors remain (blocks commit)
|
|
|
|
Install: symlink `.git/hooks/pre-commit` → `pre-commit-lint.sh`, or use `setup-formatters.sh` which does this automatically.
|
|
|
|
## Setup Checklist
|
|
|
|
1. Run `scripts/setup-formatters.sh <project> <ext> [<ext> ...]` to symlink formatters
|
|
2. Install the required tools (`ruff`, `shfmt`, `shellcheck`, `biome`, `prettier`, `sqlfluff`)
|
|
3. Create per-project config files as needed (`pyproject.toml`, `.editorconfig`, etc.)
|
|
4. Run `scripts/install-hooks.sh` to install the PostToolUse hook (one-time global setup)
|
|
5. Use `/linter scan` to verify everything is connected
|