Claude Code Hooks: auto-format, block dangerous commands, enforce tests — no reminders needed
TL;DR
A hook is a shell command (or HTTP, prompt…) that runs on events like PreToolUse, PostToolUse, Stop. Exit code 2 in PreToolUse blocks the tool call and feeds the reason to Claude; in Stop it forces Claude to keep going. Configure in .claude/settings.json, inspect with /hooks.
Example 1: format after every edit
{
"hooks": {
"PostToolUse": [{
"matcher": "Edit|Write",
"hooks": [{ "type": "command", "command": "npx prettier --write \"$(jq -r .tool_input.file_path)\"" }]
}]
}
}Example 2: block rm -rf
{
"hooks": {
"PreToolUse": [{
"matcher": "Bash",
"hooks": [{ "type": "command", "if": "Bash(rm *)",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh" }]
}]
}
}
# block-rm.sh
echo "Deleting files with rm is not allowed. Ask the user." >&2
exit 2Exit codes that matter
| Event | Exit 2 means |
|---|---|
| PreToolUse | Block the tool; stderr becomes Claude's reason |
| PostToolUse | No block (tool already ran); stderr goes to Claude |
| Stop | Claude can't stop — e.g. while tests are red |
- Exit 1 does not block — use exit 2 for hard rules.
- Hooks in
.claude/settings.jsonare committed so the whole team shares them. - Disable all temporarily:
"disableAllHooks": true.
Docs: Hooks reference · Hooks guide