Get startedGet started for free

Video: Hooks

1. Video: Hooks

A hook is a deterministic code at a fixed point in the loop, which means it can guarantee far more than formatting on a run you're not watching. It turns a rule from "Claude usually listens" into "Claude can't skip it". Claude Code fires around 30 hook events. The handful you'll reach for is PreToolUse, which fires before a tool call, the enforcement primitive, PostToolUse, which fires after a successful tool call. This is usually where auto-formatting or an auto-lint would go. Stop, which fires when Claude wants to end its turn. Refuse, if conditions aren't met. SubAgentStop is the same signal, but for subagent finishing. Precompact and Postcompact fire before and after compaction. To re-inject context after compaction, use SessionStart with the compact matcher, not postcompact. InstructionsLoaded fires when a CLAUDE.md or rule file loads. Audit what made it into context. SessionStart primes the environment. Check SourceStartup to run only on fresh starts. For PreToolUse, return JSON and exit 0 with a permission, decision field, allow, deny, or ask. There's a fourth value, defer, but it only applies to non-interactive, dash p runs, where a calling process pauses the tool and resumes it later, so you'll rarely reach for it. You can also return updatedInput to modify the tool call without blocking, which can be useful for redacting secrets out of a bash command. One catch is that updatedInput replaces the whole input object, so echo back the fields that you aren't changing. For hooks that don't return JSON, exit codes work too. So, here's the three numbers that matter. 0 is a success, so if the standard out is JSON, Claude parses it. Plain text is ignored on most events, but on SessionStart, userPromPsubmit, and userPromPromPexp, it gets added to context, which is exactly what makes the state preserver work. 2 is a blocking error, so standard error gets fed back to Claude as a context. This is the blocking exit code almost everywhere, where the lone exception, worktree create, aborts any non-zero code. Watch out, because exit1 feels like an error, but it actually does not block, so Claude runs the command anyway. And then anything else is non-blocking and is locked. Exit2 can block stop, which is how you say, no, you're not done. PostToolUse fires after the tool already ran, so it's too late to stop the call, though it can still feed text back to Claude. A few events ignore blocking, like notification, SessionStart, and FileChange, show your standard error, and carry on. A pretool use guardrail that redacts instead of blocking. The matcher picks the tool to watch, say, bash, and an optional if clause narrows it to a specific command. So returning deny stops a dangerous call. The lesser known move is return updated input to rewrite the call, which is how you strip a secret out of a command and still let it run. When Claude compacts a long conversation, it drops a lot of details, but a SessionStart hook with the compact matcher runs right after a print short summary of the files you've been working on. That summary goes back into context, so Claude picks up where it was instead of starting cold. Hooks turns a rule Claude usually follows into one that it always follows. Reach past auto formatting, guard tools with pretool use, gate the turn with stop, and preserve stay across compact. The setup pays back the first time it catches something on a run you weren't watching.

2. Let's practice!

Create Your Free Account

or

By continuing, you accept our Terms of Use, our Privacy Policy and that your data is stored in the USA.