Hooks are shell commands that Claude Code runs automatically in response to specific events: every time a tool is about to run, every time a file is saved, every time a session starts or ends. They turn "rules I keep forgetting" into "rules the system enforces for me."
What can be hooked
The main events you'll care about:
- PreToolUse — fires before a tool runs. Can block the tool by exiting non-zero. Useful for guardrails.
- PostToolUse— fires after a tool runs. Can't undo it, but can run follow-up work (format the file, run a linter, log the action).
- SessionStart — fires when Claude Code opens a session in a project. Good for setting up project-specific state.
- Stop — fires when a session ends. Good for cleanup or sending a notification.
- UserPromptSubmit — fires when you press Enter on a prompt. Can prepend extra context if needed.
For each event, you can match by tool name, by file path, or by other conditions, and run any shell command in response.
Where hooks live
Hooks are defined in .claude/settings.json (project) or ~/.claude/settings.json (global). Each entry points at the event, an optional matcher pattern, and the command to run.
Here's a real, useful config:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "npx prettier --write \"$CLAUDE_TOOL_INPUT_path\""
}
]
}
],
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "echo \"$(date) $CLAUDE_TOOL_INPUT_command\" >> .claude/bash-log.txt"
}
]
}
]
}
}What that does:
- After Claude edits or writes a file, Prettier formats it. Consistent style without you having to ask.
- Before Claude runs any Bash command, the command is appended to a log file with a timestamp. Easy audit trail.
Useful hook recipes
Auto-format on edit
Whatever your formatter is — Prettier, Black, gofmt, rustfmt — wire it into a PostToolUse hook on Edit and Write. The code is always formatted; you never have to remember to run the formatter; reviews stop arguing about whitespace.
Run tests after edits in a specific folder
For a particularly fragile module, a PostToolUse hook can run the related tests every time Claude edits a file in that folder. You catch regressions within seconds of them landing.
Block dangerous commands
A PreToolUse hook can scan the command string and exit non-zero if it matches a dangerous pattern (rm -rf /, git push --force main). Even if you accidentally approve, the hook stops the command from running.
Notify on long-running session
A Stop hook can send a desktop notification or a Slack message when a session ends — useful if you've been working in the background and want to know when Claude finishes.
Inject context on session start
A SessionStart hook can run a script that adds dynamic context — the current git branch, the open issues, today's date — and prepends it to the session. Useful when the context is moving fast.
Writing hook commands carefully
A hook command runs on your real shell with your real permissions. A poorly-written hook can do as much damage as a bad rm -rf typed manually. Three guidelines:
- Quote your variables. File paths can have spaces.
"$CLAUDE_TOOL_INPUT_path", not$CLAUDE_TOOL_INPUT_path. - Test the command standalone first. Run it in a terminal with example values before wiring it to a hook.
- Make hooks fast. A 30-second hook on PostToolUse makes every edit feel sluggish. Keep them under a second when possible.
Debugging hooks
When a hook isn't firing, the first questions are: is the matcher correct, and is the event the right one? Claude Code prints hook output in the session, including errors. If a hook exits non-zero, the tool it was attached to is blocked — useful when you mean it, alarming when you don't.
For PreToolUse blocking hooks, the convention is to print a short reason to stderr and exit non-zero. Claude sees the message and can route around the block — for example, by asking you to override.
Hooks for teams
Project-level hooks (committed to git) apply to anyone who clones the repo. This is how teams enforce shared behaviour without trusting every engineer to remember every rule. Formatting standards, dangerous-command blocks, audit logging — all best as project hooks rather than tribal knowledge.
.claude/settings.json, opening it in Claude Code would run their code on your machine. Claude Code prompts you before activating hooks from a new project for exactly this reason. Treat the prompt seriously.- Hooks are shell commands triggered by Claude Code events (PreToolUse, PostToolUse, SessionStart, Stop, etc.).
- Common uses: auto-format on edit, audit-log shell commands, block dangerous patterns, run tests after edits.
- Defined in
.claude/settings.json. Project-level hooks are committed to git and apply to the whole team. - Hooks run on your real machine with your real permissions. Test commands standalone first.