Hooks are where Claude Code goes from "a tool you use" to "a system you build on." They're little scripts that run automatically when something happens inside the agent, before a tool is called, when a session starts, when the model finishes thinking. They let you inject your own logic into the agent's loop without modifying Claude Code itself. If permissions are the agent's safety rail, hooks are the agent's instrumentation, automation, and custom glue all at once.
If you've worked with web servers, you know middleware: code that runs between the request and the response, doing things like logging, authentication, and rate limiting. Hooks are the same idea, but for an AI agent. Each hook is a script that runs at a specific moment in the agent's lifecycle. The script can observe, modify, or block what's happening.
This is the mental frame: Claude Code is doing its work, and hooks are things that fire on the side. They don't change Claude's behavior in the model. They change what happens around each Claude step.
Each one receives a JSON payload telling it what's happening (which tool, which args, what the model is about to say), and each one can return a JSON response telling Claude Code to proceed, modify, or stop.
Hooks live in the same settings.json file as your permissions. A hook entry has three parts: which event to hook into, what to match on (so you only run it for certain tools), and what command to run.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{ "type": "command", "command": "/path/to/safety-check.sh" }
]
}
]
}
}
What this does: every time Claude wants to run a Bash command, the agent pauses, sends a JSON description of the call to safety-check.sh via stdin, waits for it to return a JSON response on stdout, and then decides based on that response whether to proceed. Exit code 0 = allow; non-zero = block.
Every tool call gets appended to a log file with a timestamp. Answers the question "what did the agent actually do in that 4-hour session?" If something goes sideways, the log is how you find out exactly what happened. Set this up once, on every agent, before you do anything else.
Your deny list catches the obvious stuff. A PreToolUse hook catches the subtler stuff: "don't run this Bash command if it contains DROP TABLE," "don't let Edit touch any file under production/," "don't send a POST with this customer ID." Anything that would require a regex smarter than the deny list can handle.
Before the agent's final message is shown or saved, scan it for things that look like API keys, tokens, passwords. If anything matches, redact or refuse. This is how you make sure the agent never leaks secrets back to a user or a log, even if it accidentally reads them from somewhere.
When a session starts, run a script that gathers useful context and injects it into the agent's system prompt: today's date, recent open GitHub issues, what team is oncall, the URL of your team dashboard. The agent starts every session already up to speed.
If your agent is running a long task, you probably don't want to sit and watch. A hook can ping Telegram, Slack, or email when a long job finishes, or when something surprising happens. The agent becomes something you check back on, not something you babysit.
When the session ends, commit any uncommitted changes, upload your notes to a team page, or write a summary of what was done to a log. The agent becomes a team member whose work is automatically captured.
Every hook script follows the same protocol:
The two canonical responses:
# Allow the call to proceed
{ "continue": true }
# Block the call with a human-readable reason
{ "continue": false, "stopReason": "detected a production API key in the command" }
.claude/hooks/ into git. Your team inherits the same safety rails you do.Once you start composing hooks, something interesting happens: you're not building an agent anymore, you're building a platform that the agent runs on. PreToolUse for safety. SessionStart for context. PostToolUse for audit. SessionEnd for reporting. The agent itself becomes a service, and your hooks are the middleware that turns it from a chatbot-in-a-loop into something production-shaped.
This is roughly where the line between "I use Claude Code" and "I've built a system on top of Claude Code" gets crossed. The pages on going autonomous depend heavily on hooks, because that's how you make an agent safe to run without you watching.
Andrej Karpathy - Let's build GPT from scratch