Claude Code hooks: where your agent's behavior actually lives

You can write the perfect rule in CLAUDE.md and watch your agent ignore it on the third turn. That is not a prompting failure. It is a category error. A rule in markdown is a request. A hook is code that runs.

The kitstarter robot standing at a gate on a track, checking each thing that passes

Everyone who has used Claude Code for more than a week has had this moment. You add a line to your CLAUDE.md that says do not create new files unless asked. The agent follows it beautifully. Then, forty minutes into a long session, it creates four files you did not ask for.

The instinct is to write the rule more forcefully. Bold it. Put it at the top. Add ALL CAPS. That instinct is wrong, and understanding why is the whole point of hooks.

What are Claude Code hooks?

Claude Code hooks are scripts the runtime executes automatically at fixed points in a session, such as before a tool call, when you submit a prompt, or when the agent tries to stop. Claude Code hands the hook a JSON description of the event on stdin, runs it, and reads the exit code and stdout. The model is not consulted.

That last sentence is the entire value. Everything you put in a markdown file is context competing for a model's attention against everything else in the window. Everything you put in a hook is a program. The model can skim your CLAUDE.md. It cannot skim an exit code.

A rule is a request. A hook is enforcement.

Both belong in a healthy setup, doing different jobs. CLAUDE.md explains intent, taste, and the things that need judgment. A hook carries any rule that must hold every single time, regardless of how long the session has run or how confident the model feels. Ask yourself one question about each rule: what happens if the agent ignores this once?

If the answer is "a slightly worse commit," leave it in markdown. If the answer is "it deletes a table" or "it writes four hundred lines before I could say what I wanted," that rule does not belong in prose. It belongs in code.

A hook answers in one of two ways. It exits with a code, where 2 is the blocking signal and Claude Code feeds your stderr back to the model as the reason. Or it exits 0 and prints JSON, which is how you return a real permission decision of allow, deny, or ask. The trap that catches people is that exit 1, the conventional Unix failure code, does not block anything.

The eight events, and what we wired to each

Claude Code fires hooks across a session lifecycle, not just around tool calls. kitstarter ships eight scripts across eight of those events. This is the table our installer writes into your settings.json, and it is the most honest answer we can give to "what would you actually do with hooks."

SessionStarta navigator, so you never open a blank terminal, plus the tutor arming itself
UserPromptSubmitthe clarity engine reads your prompt and decides whether the goal is clear enough to build
PreToolUse (Bash)a safety guard warns before a destructive command. It warns. It never blocks
PreToolUse (Edit, Write)the clarity lock: writing is denied until you confirm the goal
PostToolUse (Edit, Write)scan the file just written for anti-slop tells, automatically, not on request
PreCompactteach context hygiene at the exact moment the window is about to be compacted
SubagentStartre-inject the rules, because they die at the Task boundary otherwise
Stop"done" must be proven. Unfixed slop tells never ship silently
SessionEnda receipts card: what the engine quietly saved you this session
The real wiring table kitstarter installs. Nine rows, because PreToolUse carries two hooks with different matchers.

Two of those rows deserve a closer look, because they are opposites, and the difference between them is the whole design philosophy.

The one place we hard-deny, and the one place we refuse to

The clarity hook denies. On any Edit, Write, MultiEdit or NotebookEdit, it returns a permission decision of deny until you have confirmed what you actually want, then it lifts. This is a real lock, not a nudge. The agent cannot write your first file until the goal is agreed.

The safety guard refuses to block. It sits on Bash, it recognizes destructive commands, and it only warns. The reasoning is written in the file itself: a kit that hard-blocks your commands gets uninstalled. Being right about a dangerous command is worth nothing if the user rips the whole thing out a week later.

That is the rule worth stealing. Block where the cost of being wrong is high and the friction of asking is low. Warn everywhere else. A hook that blocks too much is a hook that gets deleted, and a deleted hook enforces nothing.

Both fail open. Any error inside the hook and it stays silent and lets you work. A coaching feature is never worth breaking a session over.

What hooks actually changed, measured

Every other page about Claude Code hooks explains the API. Almost none of them tell you what changes when the hooks are running, because that requires putting them into production and measuring. We ran a clean-room suite: same model, same scripted vibe-coder conversations, fresh sandbox per arm, one arm with the hooks and one without.

Asked first5 of 5 tasks with hooks, 2 of 5 without. The clarity lock enforces it; vanilla asking is run-to-run luck
Code writtenabout 40% less for the same outcome. 842 lines became 479. 606 became 311
Slop tellsnever worse, and 5 down to 0 on the task where vanilla spiralled
Cost of askingfaster and cheaper on 3 of 5 tasks. The questions paid for themselves in avoided rework
Where it loston an already-clear task: about 2x slower, about 75% pricier, near-identical output
The standing billabout 25% average cost premium from hook context tokens. Real, and disclosed
Single run per task, so read every number as directional. The losses are printed because hiding them would be the slop we are trying to prevent.

The last two rows matter more than the first four. Hooks are not free. On a task that was already unambiguous, the clarity engine is pure overhead: it asks a question nobody needed and you pay for the tokens. That is the honest cost of a floor, and it is the open product problem we are tuning.

Where to start with Claude Code hooks

You do not need eight. Start with one, and pick it by asking which failure has actually cost you time this month.

If your agent builds before you finish explaining, put a lock on Edit and Write. If it runs commands that scare you, warn on Bash. If it says done without checking, use Stop to make done a claim the runtime verifies. If you spawn subagents and notice they ignore rules the main agent follows, re-inject on SubagentStart. And if you simply want to see what is happening, a status line costs nothing and pays back immediately.

Write it, log first, block later. Roll out gradually: log the event without blocking, then warn on the patterns you recognize, and only block the things that have genuinely caused damage. A hook you trust is worth ten you disabled.

None of this requires a kit. Hooks are a Claude Code feature and the docs are good. What a kit gives you is the part that took us months: knowing which events are worth wiring, which rules deserve enforcement instead of prose, and where blocking backfires. If you would rather not assemble that yourself, we shipped ours, and you can read exactly what it does before you install it.

Put the rules where the agent cannot skim them

kitstarter wires eight hooks into Claude Code, Codex, and Antigravity so they ask before they build, stay lean, and do not look AI-made.

Get the kit · $29Read the docs