A CLAUDE.md rule is a suggestion. An agent under load -- token-tired, mid-task, optimizing for "did I do what the user asked" -- will quietly route around it. A hook is a script that runs in your shell, gets a JSON payload describing the tool call, and decides to allow it, deny it, or rewrite it. The agent cannot ignore a hook. That is the entire reason hooks exist.

This guide covers the four hook events that matter for production, the JSON contract, the patterns that pay off, and the failure modes that show up the first time a hook saves you. It is the operational complement to the rules in your CLAUDE.md -- Chapter 3 of Claude Code in Production goes deeper.

The four hook events worth knowing

The shape of a hook

A hook is any executable on disk -- bash, python, node, whatever. Wired in ~/.claude/settings.json (global) or <project>/.claude/settings.json (project):

{ "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [{"type": "command", "command": "~/.claude/hooks/gh-pr-merge-admin-gate.sh"}] } ] } }

The matcher narrows which tool calls fire the hook. "Bash" matches every Bash invocation; you filter inside the script. The script receives the full tool call as JSON on stdin and decides:

#!/bin/bash # gh-pr-merge-admin-gate.sh input=$(cat) cmd=$(echo "$input" | jq -r '.tool_input.command // ""') if echo "$cmd" | grep -qE 'gh pr merge.*--admin'; then if echo "$cmd" | grep -q '# yaw-admin-authorized'; then exit 0 # explicitly authorized this call fi if [ "${YAW_ADMIN_AUTHORIZED}" = "1" ]; then exit 0 # authorized via env for this single call fi echo '{"decision": "deny", "reason": "gh pr merge --admin is gated. Either add '\''# yaw-admin-authorized'\'' to the command, or set YAW_ADMIN_AUTHORIZED=1 in the env for this one call."}' >&2 exit 1 fi exit 0

The agent sees the deny message. It cannot retry the same command. It has to come back to you.

What hooks earn their keep on

Gating destructive commands

The classic pattern: gh pr merge --admin, git push --force, rm -rf against paths that look like a home directory, kubectl delete namespace. The hook does not block the operation forever -- it requires explicit authorization on that single command (an inline comment marker, an env var). The override is one-shot; the next invocation of the same command shape is gated again.

Forcing conventions before they break CI

A PostToolUse hook that runs biome check against any file the agent just edited will catch formatting violations before they hit a release tag. A PreToolUse hook that blocks writes to /tmp/yaw-mode-<id>/ overlay paths forces the agent to edit the canonical source instead of a disposable template.

Injecting context the agent should always have

A UserPromptSubmit hook can prepend the current git branch, the on-call rotation, or the active deploy environment to every prompt. The agent never has to guess "am I on main?" -- it sees the answer before it parses the user's question.

The failure modes

Hooks that block legitimate work

A hook that denies too aggressively will frustrate every tool call until you disable it. The fix is to make the deny message actionable -- spell out the exact override (the marker comment, the env var) the user can use to proceed when they mean it. A hook that says "denied" with no path forward is worse than no hook at all.

Hooks that silently corrupt state

A PostToolUse hook that auto-formats every file the agent writes can mask real issues -- you stop seeing what the agent produced because the hook smoothed it over. Prefer hooks that warn over hooks that rewrite. The agent (and the user) need to see the unmodified output to trust it.

Hooks that are slow

Every PreToolUse hook runs synchronously before every matching tool call. A 200ms hook running on every Bash call adds up fast. Keep hook scripts cheap; defer expensive checks to PostToolUse or a separate verification step.

Where to keep hook scripts

Personal hooks go in ~/.claude/hooks/. Project hooks go in <project>/.claude/hooks/ and check into git so the whole team gets them. The settings file (~/.claude/settings.json or the project equivalent) wires the hook to the event. Keep the scripts executable and small; one hook, one job.

Want the patterns?

This guide is the surface. Claude Code in Production Chapter 3 covers the full hook reference, the canonical-source-gate pattern, the admin-override pattern (with the one-shot enforcement that survives a session-long batch of merges), and the way hooks integrate with the rule-layering pattern from Chapter 2. Chapter 10 covers the seven hazards hooks are designed to catch.

Claude Code in Production

The Claude Code book and practitioner's guide. Twelve chapters. PDF + EPUB. Free updates. $39 one-time, secure checkout.

Read more & buy $39

Published by Yaw Labs.

Related