Claude Code hooks: auto-format, auto-lint, auto-test on every save
If you've used Claude Code for more than a few sessions, you've hit this pattern:
- Claude writes a function
- You run the linter — 12 warnings
- You tell Claude to fix them
- Claude fixes them and breaks a test
- Repeat
Hooks eliminate this loop. Instead of telling Claude to fix things after they break, you wire up hooks that run automatically — before Claude commits to an approach.
What are Claude Code hooks?
Hooks are shell commands that Claude Code runs at specific points in its workflow. Think of them as lifecycle events:
- PreToolUse — runs before Claude uses a tool (file write, bash, etc.)
- PostToolUse — runs after Claude uses a tool
- Stop — runs when Claude finishes a task
- Notification — runs when Claude sends you a message
You configure them in your settings.json.
The self-healing loop setup
Here's the hooks config I use on every project:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{
"type": "command",
"command": "npm run lint --silent 2>&1 | head -20"
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "npm test --silent 2>&1 | tail -10"
}
]
}
]
}
}
What this does:
- Every time Claude writes or edits a file → lint runs automatically
- When Claude finishes a task → tests run automatically
- Output is fed back into Claude's context
Claude sees the lint output and fixes violations before moving to the next file. It sees test failures and backtracks.
Auto-format hook
For Prettier users:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{
"type": "command",
"command": "prettier --write $CLAUDE_TOOL_OUTPUT_PATH 2>&1"
}
]
}
]
}
}
$CLAUDE_TOOL_OUTPUT_PATH is the file Claude just wrote. Prettier formats it in place immediately after every write.
TypeScript type-check hook
For TypeScript projects, type errors caught early save enormous context waste:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{
"type": "command",
"command": "npx tsc --noEmit 2>&1 | head -30"
}
]
}
]
}
}
Note the head -30 — you want to show Claude enough to understand the error, not flood its context with 200 lines of cascading type failures.
The notification hook (for long tasks)
For tasks that take 10+ minutes, you can get a terminal bell or system notification:
{
"hooks": {
"Notification": [
{
"hooks": [
{
"type": "command",
"command": "echo '\a'"
}
]
}
]
}
}
Or on macOS with osascript:
{
"command": "osascript -e 'display notification \"Claude needs input\" with title \"Claude Code\"\'"
}
Combining hooks with CLAUDE.md
Hooks define when things run. CLAUDE.md defines how Claude behaves. They complement each other:
CLAUDE.md:
# Code standards
- Always run prettier before marking a task done
- If lint fails after a write, fix before proceeding
- Run tests after every component change
settings.json hooks: enforce this automatically, even if Claude forgets.
The CLAUDE.md is the contract. The hooks are the enforcement.
One thing to watch
Hooks add latency. If your lint takes 8 seconds and Claude writes 20 files, that's 160 seconds of hook time per task. Keep hook commands fast:
- Use
--silentto suppress progress output - Use
head -Nto cap output length - Use file-specific commands (
prettier --write $FILE) not full project scans - For slow type-checks, hook on
Stopinstead ofPostToolUse
The rate limit problem
Hooks make Claude Code sessions longer and more intensive. A session that used to take 20 minutes of Claude time might now take 35 minutes with auto-linting and auto-testing feedback loops.
This accelerates how fast you hit rate limits.
The fix most Claude Code power users use: set ANTHROPIC_BASE_URL to a proxy that removes the rate limits.
export ANTHROPIC_BASE_URL=https://simplylouie.com
At ✌️2/month (vs $20/month for Claude.ai Pro), it removes the interruptions without changing anything about how Claude Code works — same model, same quality, just no 'overloaded' stops mid-hook-loop.
What hooks are you running? Drop them in the comments — I'll add the best ones to a follow-up article.
Top comments (0)