Hooks
Running your own shell commands automatically around Claude Code's lifecycle events, for formatting, enforcement, and logging.
A hook is a shell command you configure to run automatically when something specific happens during a session — before a tool executes, after a response, when a session starts or ends. Hooks are how you attach your own deterministic automation to an otherwise non-deterministic agent loop.
What hooks are for
The agent decides what to do; a hook reacts to that decision with guaranteed, scriptable behavior that doesn't depend on the model remembering to do it. Common uses:
- Auto-formatting every edited file immediately after it's written, so formatting is never a matter of whether the agent remembered to run the formatter.
- Blocking specific actions outright — a hook that inspects a proposed command before it runs and refuses to let it execute if it matches a pattern you've decided is never acceptable, regardless of context or how the request was phrased.
- Logging and notifications — recording every file change for an audit trail, or sending a notification when a long session finishes or needs input.
- Enforcing policy mechanically — a hook can require a specific check to pass before allowing a commit, independent of whether the agent thought to run that check itself.
A representative example
A hook that runs a formatter after every file edit, configured roughly like this:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"command": "prettier --write \"$CLAUDE_FILE_PATH\""
}
]
}
}The exact event names and available variables depend on your version, but the shape is consistent: match an event (and optionally which tool triggered it), then run a command, with relevant context passed in.
Why hooks matter even with good prompting
You could ask Claude, in CLAUDE.md, to always format files after editing them — and most of the
time it would. A hook makes that guaranteed rather than likely, because it runs as deterministic
code outside the model's reasoning entirely. The distinction matters most for anything where "almost
always" isn't good enough: a security check that must run before every commit is exactly the kind of
thing that belongs in a hook rather than a polite request in a memory file.
Treat hooks like any other automation with real privileges
A hook runs with whatever permissions the shell it executes in has — the same caution that applies to running shell commands generally applies here, just running automatically instead of through the agent's own decision-making. Review what a hook actually does before relying on it, the same way you would for any script you'd let run unattended.
Next: connecting Claude Code to external tools and data through MCP.