Automatically formats files after Claude edits them, keeping code consistently styled without manual intervention.
This PostToolUse hook fires after every Edit, MultiEdit, or Write tool call. It detects the file type and runs the appropriate formatter:
| File Type | Formatter | What It Does |
|---|---|---|
Python (.py) |
ruff | Formats code + auto-fixes lint issues |
Go (.go) |
goimports | Formats code + manages imports |
| Everything else | prettier | JS, TS, JSON, CSS, HTML, MD, YAML, etc. |
Python files:
- Runs
ruff formatto format the code - Runs
ruff check --fixto auto-fix linting issues - Preserves unused imports (allows Claude to add imports before using them)
Go files:
- Runs
goimports -wto format and manage imports
Other files:
- Runs
prettier --writefor supported file types - Prettier auto-detects supported formats
The hook exits successfully even if formatting fails — it won't block Claude's work.
Install the formatters you need:
# Python
pip install ruff
# Go
go install golang.org/x/tools/cmd/goimports@latest
# JavaScript/TypeScript/etc
npm install -g prettiercp auto-format.sh ~/.claude/hooks/auto-format.sh
chmod +x ~/.claude/hooks/auto-format.shAdd to ~/.claude/settings.json:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|MultiEdit|Write",
"hooks": [
{
"type": "command",
"command": "$HOME/.claude/hooks/auto-format.sh"
}
]
}
]
}
}The hook uses mise for tool version management if available. It also checks common paths:
$HOME/.local/share/mise/shims/usr/local/bin/opt/homebrew/bin$HOME/go/bin
If your tools are installed elsewhere, add the paths to the script.
Edit the script to add support for other file types:
# Example: Add Rust formatting
if [[ "$file_path" == *.rs ]]; then
rustfmt "$file_path" 2>&1
exit $?
fiModify the formatter commands in the script. For example, to change ruff's line length:
ruff format --line-length 100 "$file_path" 2>&1Add early returns for file types you don't want formatted:
# Skip formatting for generated files
if [[ "$file_path" == *_generated.* ]]; then
exit 0
fiThe hook silently skips if a formatter isn't installed. Check that the formatter is in your PATH:
which ruff
which goimports
which prettierIf using mise, ensure the correct version is activated. The hook runs eval "$(mise activate bash)" to load mise.
The hook uses default formatter settings. If your project has specific config (.prettierrc, ruff.toml, etc.), the formatter should pick it up automatically.
- change-summary — Runs TypeScript type checking at session end (complements this hook by checking types after all edits are done)