Reading the Anthropic Claude Code hooks documentation properly
A practitioner's guide to the Anthropic Claude Code hooks documentation: which events matter, how exit codes work, and the mistakes teams make first.

A hook is a shell command the harness runs at a fixed point in the session. You register it in a settings file, it receives JSON about what is happening on stdin, and what it writes and what it exits with can change what happens next. That is the whole mechanism.
The reason to care is that instructions in a markdown file are advice. The model can ignore advice. A hook is executed by the harness, not by the model, so it cannot be talked out of running. Anything you genuinely require, put in a hook.
Two things trip up almost everyone.
First, exit codes are the interface. Exit 0 and the session continues. Exit 2 from a hook that fires before a tool runs blocks that tool call, and what your script printed to stderr is handed back to the model as the reason. That is the difference between a warning nobody reads and a correction the agent acts on. Any other non-zero code is treated as a hook that failed rather than a decision.
Second, hook scope follows settings scope. Put a hook in .claude/settings.json and it is committed and applies to everyone on the repo. Put it in .claude/settings.local.json and it is yours alone. Put it in the settings file in your home directory and it follows you across every project. Teams that put a team rule in a local file spend a week wondering why it works for one person.
Which events are worth wiring
- Before a tool runs: the gatekeeper. Match on the tool name and block edits to generated directories, or refuse a shell command that touches production.
- After a tool runs: the janitor. Format the file that was just written, or run the type checker on it and feed errors back.
- On user prompt submit: inject context. Current branch, open incident, whatever the model should always know.
- On session start: load state. Print the last deploy status or a short list of known-broken areas.
- On stop: the audit trail. Append what changed to a log, or run the full test suite once at the end rather than after every edit.
The one that pays for itself immediately is formatting after edits. It removes an entire category of diff noise, and it removes the temptation to tell the model about formatting in prose.
A hook that is worth copying
Block writes to a lockfile the agent should never hand-edit. Register a command for the pre-tool event, matched to the edit tools, pointing at a small script:
#!/bin/bash
path=$(jq -r '.tool_input.file_path // ""')
case "$path" in *package-lock.json) echo "Edit package.json and run npm install instead." >&2; exit 2;; esac
Note the shape. Read JSON on stdin, decide, print the reason to stderr, exit 2. Every blocking hook you write will look like this.
Where hooks fall down
They run with your permissions, so a careless hook is a shell injection you wrote yourself. Quote your variables. They also run often, so a hook that takes two seconds will make every edit feel slow, and people will start disabling them. Keep them fast or make them asynchronous. And a blocking hook with a bad message just produces a loop where the model retries the same thing three times. Write the fix into the message, not the complaint.
What to do next
Run /hooks in a session to see what is registered. Then pick the one rule your team repeats most often in code review, and convert it into a blocking hook this week. One rule, committed to the repo, is worth more than a page of instructions nobody re-reads.
If you want help putting this into practice, talk to us.
Related training topics
Related research

AI agent boundaries that hold under pressure
A boundary-setting guide to AI agent boundaries: connector cards, scope ledgers, child receipts, and decision stubs that stop permission drift.

Eval platform governance for AI coding teams
A governance memo on eval platform governance: receipts behind scores, scoped harness access, and owners that stop Goodhart drift.

Agent boundaries for teams running coding agents
How to set agent boundaries for teams: connector ownership, written scopes, and review receipts that keep agent diffs explainable after the session ends.