Explainer

What Claude Code hooks are, and why they matter

What Claude Code hooks are, and why they matter

Most people use Claude Code as a black box: you give it a task, it works, you read the output. But the agent is broadcasting events the whole time it runs — it's about to run a command, it's waiting on you, it just finished its turn. Hooks are how you tap that stream. They're the difference between staring at a terminal hoping to catch the moment it needs you, and having the agent tell you.

The model is refreshingly dumb in the best way: a hook is a shell command that gets a JSON payload on stdin when a lifecycle event fires. No SDK, no plugin API, no daemon to register with. If you can write a shell one-liner that reads stdin, you can hook Claude Code. That's the whole contract, and once it clicks, a lot of "how do I make the agent do X when Y happens" questions answer themselves.

agent runs event fires turn continues your hook reads JSON on stdin notify / gate toast, allow, deny
The agent runs, a lifecycle event fires, your handler reads the JSON and decides what to do — notify you, or allow/deny the action.

How a hook actually works

You register hooks in your Claude Code settings (settings.json), keyed by event name. When that event fires, Claude Code spawns your command and pipes a JSON object into its stdin describing what just happened — which tool, which session, the working directory, and so on. Your command does whatever it wants with that, and for some events its exit code and stdout feed a decision back into the agent.

A minimal handler is genuinely a one-liner. Read stdin, pull a field, do something:

#!/usr/bin/env bash
payload="$(cat)"
tool="$(echo "$payload" | jq -r '.tool_name')"
echo "Claude wants to run: $tool"

That's it — no special runtime. The important nuance is that hooks aren't all read-only. Some are pure side-effect (fire a notification, write a log); others are decision points where what your script returns changes what the agent does next. That second category is what makes hooks more than a logging mechanism.

One caution up front: exact event names, payload fields, and decision semantics shift between releases. Treat anything below as the shape, not the spec. Before you wire something up, read Anthropic's official hooks documentation for the current names and JSON schema — the structure has changed more than once.

The three events that matter most

Claude Code fires hooks across its lifecycle, but three carry almost all the value for staying out of the terminal. Learn these and the rest are variations.

There are more — events around subagents finishing, sessions ending, prompts being submitted — but if you only ever wire up these three, you've covered "is it asking me something," "is it done," and "should this even be allowed to run." Those are the three questions that otherwise force you to keep eyes on the terminal.

What each event is good for

The split is roughly: PreToolUse is for control, Notification and Stop are for awareness. Knowing which bucket you're in tells you whether your hook needs to return a decision or just fire and forget.

How hooks power gating and notifications

Put the two halves together and you have the whole "stop babysitting" pattern, built from primitives the agent already ships. Approval gating is PreToolUse returning a verdict — allow the safe stuff automatically, deny the dangerous stuff outright, and escalate the gray-area stuff to a human. Awareness is Notification and Stop shelling out to whatever can get your attention.

On macOS that's often terminal-notifier; on Linux, notify-send. A bare-bones Stop hook is just:

terminal-notifier -title "Claude Code" -message "Agent finished" -sound default

This is the right architecture, and it's underused. The catch is what it costs to maintain: you're writing and re-deploying shell glue per machine, parsing payloads by hand to say anything useful, and a bare toast still drops you back to square one — you see "something happened," then you still have to find the right terminal, focus it, and read the scrollback to learn what it wants. The hooks give you the signal; turning that signal into something you can act on without a context switch is the part that takes work. If you want the full ladder of notification options ranked honestly, we covered that in how to actually know when Claude Code needs you.

Where Backgrind fits

Backgrind consumes these same PreToolUse / Notification / Stop hooks for you, so you don't assemble the shell glue yourself. It wraps your real Claude Code CLI in an always-on-top overlay: when the agent needs a decision, asks a question, or finishes, the window flashes and the right tab gets an accent ring — and because the terminal is already floating over your editor, answering is just typing, no hunting for a buried window. See it in action in the demo, and if you haven't yet, install Claude Code first so the hooks have something to fire on.