Hooks: Automating Behavior Around Claude Code
CLAUDE.md and permissions both shape how Claude Code reasons and what itโs allowed to do. Hooks are a third mechanism entirely โ deterministic shell commands that fire automatically in response to specific events during a session, independent of what the model decides to do at all.
Why Hooks Exist
Some things you want to happen every single time a certain event occurs, with zero chance of the agent forgetting or deciding itโs not necessary this time โ running a formatter after every file edit, blocking a specific dangerous command pattern outright, or logging every tool call for audit purposes. Asking the agent nicely in CLAUDE.md (โplease run the formatter after editing filesโ) is advisory; a hook makes it happen unconditionally, enforced by the harness itself rather than by the modelโs judgment.
The Basic Shape
Hooks are configured in .claude/settings.json, tied to specific event types:
{ "hooks": { "PostToolUse": [ { "matcher": "Edit|Write", "hooks": [ { "type": "command", "command": "npx prettier --write \"$CLAUDE_FILE_PATH\"" } ] } ] }}This runs Prettier automatically on any file the agent edits or writes, every time, without needing the agent to remember to do it or CLAUDE.md to remind it. The formatter runs regardless of whether the current instruction even mentioned formatting.
Common Hook Events
PreToolUse โ fires before a tool call executes, and can block it outright (useful for hard-blocking a dangerous command pattern that a permission deny rule doesnโt already cover, or for validation specific to your project).
PostToolUse โ fires after a tool call completes, commonly used for auto-formatting, auto-linting, or triggering a follow-up action like re-running affected tests.
Session-level events (start/stop) โ useful for one-time setup at the start of a session, or cleanup/notification at the end.
The exact event names and matcher syntax are part of Claude Codeโs hooks configuration reference โ the pattern above illustrates the shape, not an exhaustive list of every available event.
Hooks vs CLAUDE.md: A Clear Division
<!-- CLAUDE.md โ advisory, the agent decides whether/how to apply it -->## Conventions- Run the formatter before committing changes.
<!-- hooks โ deterministic, happens regardless of agent judgment -->"PostToolUse": [{ "matcher": "Edit|Write", "hooks": [{ "type": "command", "command": "npx prettier --write \"$CLAUDE_FILE_PATH\"" }] }]If an outcome genuinely must happen every time with no exceptions, a hook is the right tool โ it doesnโt rely on the instruction being read, remembered, or judged applicable in the moment. If itโs guidance that benefits from judgment (when to apply a convention, how to interpret an edge case), thatโs what CLAUDE.md and the modelโs reasoning are for.
A Practical Example: Blocking a Dangerous Pattern
{ "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", "command": "scripts/check-safe-command.sh" } ] } ] }}A small script here can inspect the proposed command and exit non-zero to block it โ a hard, scriptable guarantee that goes beyond what a CLAUDE.md warning or even a permission deny pattern can express when the logic needed is more nuanced than a simple pattern match (e.g., โblock force-push only if the target branch is mainโ).
Common Mistakes
Using CLAUDE.md instructions for things that need hard guarantees. If a formatter run, a specific validation, or a blocked command pattern must happen with zero exceptions, encode it as a hook โ relying on the agent to remember and apply a written instruction every time is a weaker guarantee than a hook enforced by the harness.
Overusing hooks for things that benefit from judgment. Not everything should be a rigid, unconditional hook โ conventions that genuinely require contextual judgment (when a particular pattern applies, how to handle an edge case) are better served by CLAUDE.md guidance the agent can reason about, not a blunt script that canโt account for nuance.
Writing hooks that are slow or fragile enough to disrupt the workflow theyโre meant to support. A PostToolUse formatter hook that takes many seconds on every single edit, or that fails loudly on files it doesnโt understand, creates more friction than the automation saves โ hooks should be fast and robust for the common case.
Frequently Asked Questions
Do hooks run even if the agent doesnโt โwantโ to trigger them? Yes โ thatโs the point. Hooks are triggered by the harness based on matching tool events, independent of the modelโs own decision-making, which is exactly what makes them suitable for guarantees CLAUDE.md alone canโt provide.
Can hooks reference CLAUDE.md content? Not directly โ hooks are shell commands operating on tool-event data (like the file path being edited), not aware of CLAUDE.mdโs prose content; the two systems are complementary but donโt read from each other.
Should every project have hooks configured? Not necessarily โ hooks add real value for teams with specific, repeatable, zero-exception automation needs (formatting, validation, blocking specific patterns); a smaller project relying primarily on CLAUDE.md guidance and normal permission prompts is a completely reasonable setup without them.
Summary
Hooks are the deterministic-enforcement layer in Claude Codeโs configuration surface โ where CLAUDE.md informs and permissions gate, hooks guarantee. Reach for a hook specifically when an outcome needs to happen every time with no dependency on the agentโs judgment or memory; keep genuinely judgment-dependent guidance in CLAUDE.md, where the modelโs reasoning is actually the right tool for the job.