How to Use Claude Code Hooks
Stop retyping the same instruction every session. Hooks turn 'please run the formatter' into a rule the tool can't forget — four steps, straight from the docs.
- A hook is a shell command Claude Code runs itself at a fixed point in its lifecycle — deterministic, not a suggestion the model can skip.
- Config is a `hooks` block in a settings file. Which file you pick decides the blast radius: your machine, the whole repo, or just you on this project.
- PostToolUse runs after a tool succeeds. PreToolUse runs before it and can block it — exit code 2 is the kill switch, and your stderr becomes the reason Claude sees.
- Type `/hooks` to confirm it registered. If it isn't in that list, your JSON is wrong — stop debugging the behavior.
Claude Code hooks are user-defined shell commands that Claude Code runs automatically at specific points in its lifecycle. You configure them in a `hooks` block in a settings file, scoped by event name and a matcher. PostToolUse hooks fire after a tool succeeds; PreToolUse hooks fire before a tool call and can block it.
Every session, same sentence out of me: “after you edit a file, run the formatter.” Sometimes it did. Sometimes it got four tasks deep, filled its head with the actual problem, and dropped it.
That’s not a model failure. That’s me asking an LLM to be a cron job.
Hooks are the fix. A hook is a shell command Claude Code runs itself at a fixed point in its own lifecycle, and Anthropic’s framing in the hooks guide is the whole pitch: deterministic control, instead of relying on the model to choose to run it.
I got my start in Ops. Deterministic beats persuasive… every single time.
Step 1: Decide the blast radius
Hooks live in a hooks block in a settings file. Which file you pick is the decision:
~/.claude/settings.json— you, every project on the machine..claude/settings.json— everyone who clones the repo. Commit it..claude/settings.local.json— you, this project, gitignored.
Local beats shared, shared beats user — the full precedence chain is in the settings docs. Team standards go in the shared file. Your personal desktop notification does not.
Step 2: Format on every edit
PostToolUse fires after a tool call succeeds. Scope it with a matcher so it only runs on the editing tools:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}
]
}
]
}
}
Claude Code pipes the event JSON to your command on stdin. jq pulls the edited path out of it. Swap Prettier for ruff, gofmt, whatever your repo actually runs.
Step 3: Put a fence around the files it must never touch
PreToolUse fires before a tool call and can block it. Point it at a script that reads stdin, checks the target path against a protected list — .env, lockfiles, anything under .git/ — prints a reason to stderr, and exits 2.
Exit code 2 is the whole trick. On PreToolUse it’s a blocking error: the call never executes, and your stderr text goes back to Claude as feedback so it can adjust instead of retrying blind. Exit 0 means proceed. Anything else is a non-blocking error — you see a notice, the action still happens.
Read the per-event table in the hooks reference before you get clever. The exceptions are real: PostToolUse can’t block anything, because the tool already ran.
Step 4: Verify it registered
Type /hooks. You get a read-only browser of every event, a count of what’s configured, and for each hook the matcher, type, source file, and command.
If your hook isn’t in that list, the JSON is wrong. Don’t debug the behavior. Debug the file.
Where they bite
Hooks run shell commands on your machine, automatically, with your credentials. A hooks block in a repo you cloned is executable code you didn’t write. Read it like a postinstall script.
And keep matchers narrow. A wildcard on a hook that makes permission decisions is a hook that auto-approves everything, which is the opposite of a guardrail.
Last one, and this is the line I keep having to redraw: hooks are for rules, not judgment. “Format every edited file” is a rule. “Is this refactor any good” is not — that’s subagent territory, or a prompt- or agent-type hook that spends a model call on purpose. Claude Code also ships an mcp_tool hook type, so a rule can call straight into your MCP servers.
Twenty minutes of setup buys back an instruction you’d otherwise retype forever. Same energy as Claude Code artifacts — small surface, disproportionate payoff.
Stop asking the agent to remember. Make the tool enforce it.
#TheAIMogul
Bottom lineHooks are the best twenty minutes you'll spend on an AI coding setup. Use them for rules, never for judgment.