Your first hook
Let’s make one. We’ll write a hook that scribbles a note in a log file every time Claude edits something — harmless, and you’ll be able to see it fire.
Hooks live in your settings file, which you met in Level 9. Open the project one at .claude/settings.json (or your personal ~/.claude/settings.json) and add:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "echo \"$(date): Claude edited a file\" >> ~/claude-edits.log"
}
]
}
]
}
}
Four pieces, reading inward:
PostToolUse— the moment: just after Claude uses a tool (Lesson 11.4).matcher: "Edit|Write"— which tools count. Only the ones that change files. The|means “Edit or Write.”type: "command"— what kind of hook. A shell command.command— the actual line that runs: append the date and a note to~/claude-edits.log.
Watch it fire
Start a session, and ask Claude to make any small edit — “add a blank line to the bottom of README.” Then look at your log:
cat ~/claude-edits.log
Mon May 24 14:02:11 2026: Claude edited a file
You never typed the echo. You never reminded Claude. The harness saw a file get edited and ran your command on its own.
That’s the feeling to remember: you wired a behavior into the machine, and now it happens every time, forever, with zero attention from you.
From toy to useful
Swap the command line and the same skeleton does real work. Two of the most common:
Auto-format edited files — never review a messy diff again:
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
(The harness hands your hook a little packet of JSON on its standard input describing what just changed; jq plucks out the edited file’s path and passes it to the formatter.)
Block something dangerous — switch the event to PreToolUse (which runs before the action and can stop it), match the Bash tool, and exit with an error if the command looks scary. That’s a guardrail Claude physically cannot talk its way past, because the harness, not the model, enforces it.
A gentler way to set them up
You don’t have to hand-edit JSON every time. Inside a session, run:
/hooks
It walks you through choosing an event, a matcher, and a command — then writes the JSON for you. Handy once you know what the pieces mean, which now you do.
One caution: a hook runs automatically, so a careless one bites automatically. Test new hooks on a throwaway project first — a hook that deletes files on every edit will do exactly that, every edit.
What’s next
Hooks automate things inside a session. The next two lessons leave the session entirely: headless mode, where Claude runs with no chat window at all — just a command you can pipe into or drop in a script.