architecture
Hooks Setup: From Zero to Production
Step-by-step setup of the 6 hooks every serious Claude Code user needs. settings.json schema, hook lifecycle, and debugging tips.
Hooks Setup: From Zero to Production
Claude Code can run shell commands, edit files, and interact with git on your behalf. That power is useful until it touches something it should not. Hooks are your guardrails: shell scripts that fire automatically before or after Claude acts, blocking dangerous operations and logging everything silently in the background.
The 6 hooks
Activate all hooks: settings.json
Add this configuration to ~/.claude/settings.json to wire up all six hooks. Each entry maps a hook lifecycle event to the script that handles it. Once saved, hooks activate immediately on the next Claude Code session.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{ "type": "command", "command": "~/.claude/hooks/bash-safety.sh" }
]
},
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": "~/.claude/hooks/file-protection.sh" }
]
},
{
"matcher": "Edit",
"hooks": [
{ "type": "command", "command": "~/.claude/hooks/file-backup.sh" }
]
},
{
"matcher": "WebSearch",
"hooks": [
{ "type": "command", "command": "~/.claude/hooks/search-year.sh" }
]
}
],
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{ "type": "command", "command": "~/.claude/hooks/file-logger.sh" }
]
}
],
"Stop": [
{
"hooks": [
{ "type": "command", "command": "~/.claude/hooks/stop-notify.sh" }
]
}
]
}
}After saving, run: chmod +x ~/.claude/hooks/*.sh
Hook lifecycle
Understanding when each hook fires helps you choose the right event for your use case. PreToolUse hooks can block an action by returning exit code 2. PostToolUse hooks run after the fact and are observation-only. The Stop hook fires once at the end of every turn.
Claude receives your message
│
▼
┌───────────────────────┐
│ PreToolUse hooks │ ← bash-safety, file-protection, file-backup, search-year
│ (can block action) │
└──────────┬────────────┘
│ approved
▼
┌───────────────────────┐
│ Tool executes │ Bash / Edit / Write / WebSearch / …
└──────────┬────────────┘
│ done
▼
┌───────────────────────┐
│ PostToolUse hooks │ ← file-logger
│ (observe only) │
└──────────┬────────────┘
│
▼
(repeat per tool call)
│
▼
┌───────────────────────┐
│ Stop hook │ ← stop-notify (fires once at end of turn)
└───────────────────────┘Debugging cheatsheet
Check settings.json syntax (valid JSON?). Verify the matcher string matches the exact tool name Claude Code uses. Run: cat ~/.claude/settings.json | python3 -m json.tool
The script is not executable. Run: chmod +x ~/.claude/hooks/*.sh
Only exit code 2 triggers a block in PreToolUse. exit 1 is treated as an error but may not block. Make sure your block path returns exactly exit 2.
The env var name differs per tool. Edit uses CLAUDE_FILE_PATH. Bash uses CLAUDE_TOOL_INPUT. Log all env vars with: env | grep CLAUDE_ >> /tmp/hook-debug.log
file-backup.sh prunes to 10 per file, but many distinct files accumulate. Run: du -sh ~/.claude/backups and periodically: rm ~/.claude/backups/*
Check System Settings > Notifications > Terminal (or iTerm2) and make sure notifications are allowed. The afplay line runs in background so sound should still play.
Want this running in your stack?
AY Automate builds agent systems, RAG pipelines, and Claude Code setups for production teams.