Claude Code Hooks: Automate Your Dev Workflow
Most of what you do with Claude Code is a conversation: you ask, it edits, you review. But some things should never depend on the model remembering to do them. You always want your files formatted after an edit. You never want a stray rm -rf to reach your shell. You want a desktop ping the moment a long task finishes so you can stop staring at the terminal. Hooks are how you turn those wishes into guarantees.
A hook is a shell command that Claude Code runs automatically at a defined point in its lifecycle. Because hooks are executed by the harness and not generated by the model, they are deterministic: they fire every single time, regardless of how the conversation is going. This guide covers what hooks are, the events you can attach them to, how the configuration works, and the practical automations that make them worth setting up. When you want ready-made examples, the Claude Code hooks catalog on ToolZip has curated, installable configurations.
What Are Claude Code Hooks?
Instructions in a CLAUDE.md file are suggestions. The model reads them and usually follows them, but "usually" is not a policy you can build a workflow on. Hooks are different. They are configuration, not prompting, and the harness enforces them.
Concretely, a hook is a command line that Claude Code runs when a specific event occurs. The command receives structured JSON on standard input describing what just happened, and it communicates back through its exit code:
- Exit code 0 means success. The workflow continues as normal.
- Exit code 2 is a blocking error. Claude Code reads whatever the hook printed to stderr and treats it as a reason to stop or change course. This is the mechanism that lets a hook veto a tool call.
- Any other non-zero code is a non-blocking error: it is surfaced to you but does not halt the action.
Because the hook is an ordinary shell command, it can be anything your machine can run: a one-line jq filter, a Prettier invocation, a Python script, or a call to a notification tool. That flexibility is the whole point. Hooks sit alongside settings, slash commands, and subagents as one of the core ways to shape how Claude Code behaves in your project.
The Hook Lifecycle Events
Hooks attach to named events. Knowing which event fires when is the key to designing an automation that does what you expect. The events you will use most often are:
- PreToolUse — runs before Claude Code executes a tool such as Bash, Edit, or Write. This is your gate: block the action with exit code 2, or let it proceed.
- PostToolUse — runs after a tool completes successfully. Ideal for reacting to a change, such as formatting a file that was just written.
- UserPromptSubmit — runs when you submit a message, before the model processes it. Useful for injecting context or validating input.
- Notification — runs when Claude Code sends a notification, for example when it needs permission or has gone idle waiting on you.
- Stop — runs when the main agent finishes its response. A natural place to trigger a build, a test run, or an alert.
- SubagentStop — runs when a subagent spawned by the Task tool finishes.
- PreCompact — runs before Claude Code compacts the conversation to save context.
- SessionStart and SessionEnd — run when a session begins or resumes, and when it ends.
For tool-related events, a matcher narrows which tools trigger the hook, so a formatter can watch only Edit and Write while a safety check watches only Bash.
How Hook Configuration Works
Hooks live in your settings JSON — .claude/settings.json for project-wide rules committed to the repo, or ~/.claude/settings.json for personal defaults across every project. The structure groups hooks by event, then by matcher. Here is a hook that runs Prettier on any file Claude edits or writes:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}
]
}
]
}
}
Read it from the inside out. The event is PostToolUse. The matcher is a regular expression that matches the Edit and Write tool names. When either runs, the command executes: it reads the event JSON on stdin, uses jq to pull out the path of the file that changed, and pipes that path into Prettier. Nothing about this depends on the model choosing to format — it happens by construction.
A few details that save debugging time later:
- The matcher is a regex, so
"Edit|Write"matches either tool and"*"(or an empty matcher) matches all of them. - The JSON on stdin includes fields like
tool_nameandtool_input. For a Bash call,tool_input.commandholds the exact command string, which is what safety hooks inspect. - Claude Code exposes the project root as the
$CLAUDE_PROJECT_DIRenvironment variable, which is handy when your hook calls a script checked into the repo.
Practical Use Cases
The events and the exit-code contract combine into a small set of automations that most teams reach for first.
Lint and format on save
Attach a formatter or linter to PostToolUse matching Edit and Write. Every file Claude touches comes back formatted to your standard, so diffs stay clean and review noise disappears. A ready-made version lives at formatting/prettier-on-save in the catalog.
Block dangerous commands
Attach a validator to PreToolUse matching Bash. The hook reads the proposed command, checks it against a denylist — rm -rf on protected paths, force pushes to main, destructive database calls — and exits with code 2 to veto anything that matches, printing an explanation to stderr so the model understands why. This is a guardrail the model cannot talk its way past. See security/block-dangerous-commands for a starting point.
Desktop and Slack notifications
Attach a notifier to the Stop or Notification event so you get pinged when Claude finishes a long run or needs your input. Instead of babysitting the terminal, you find out the moment your attention is actually required. The notifications/desktop-notify hook wires this up in one line.
Run tests automatically
Attach your test command to PostToolUse or Stop so the suite runs after Claude changes source files. Fast feedback catches a broken edit while the context is still fresh, and the failing output can flow straight back into the conversation. Browse testing/run-tests-on-save for an example.
Installing a Hook with npx
You can write hook JSON by hand, but the fastest path is to install a curated one. ToolZip catalogs each hook with a copy-paste command that merges the configuration into your settings for you. The install format uses the --hook flag with a category/name path:
# Install a curated hook into your project npx claude-code-templates@latest --hook="security/block-dangerous-commands" --yes
Swap the path for any hook you find in the hooks hub — for example --hook="formatting/prettier-on-save" or --hook="notifications/desktop-notify". The --yes flag skips the confirmation prompt for a fully non-interactive install, which is convenient inside a setup script. After installing, open your .claude/settings.json to confirm the hook landed under the event you expected, then trigger the matching action to watch it fire.
Best Practices
Hooks run real commands on your machine with your permissions, so a little discipline keeps them safe and predictable:
- Read a hook before you install it. Because hooks execute shell commands automatically, treat third-party ones the way you would any script — review what the command does first.
- Keep hooks fast. They run inline in the workflow. A slow hook on
PostToolUseadds latency to every edit, so push heavy work toStopwhere it runs once. - Scope with matchers. A precise matcher avoids running a formatter on a Bash call or a command validator on a file read.
- Fail loudly, block deliberately. Reserve exit code 2 for genuine guardrails. Overusing it turns helpful automation into a wall Claude keeps hitting.
- Commit project hooks. Putting shared rules in
.claude/settings.jsonand checking them in gives your whole team the same guardrails without extra setup.
Hooks are the quiet layer that makes Claude Code feel like it fits your project rather than the other way around. Start with one — a formatter on save is the easiest win — then layer in a safety gate and a notification as you learn where the friction is. When you are ready to expand, the hooks catalog and the broader Claude Code component library on ToolZip give you tested building blocks to install in a single command.
Browse Claude Code Hooks
Shell commands that fire on Claude Code lifecycle events for automation.