Hooks are bits of code that run on events, before a tool is called, after a session ends, when a specific prompt is submitted. They turn Claude Code from a manual agent into an automated one.
Defined in settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "/path/to/your/script.sh"
}
]
}
]
}
}
The matcher filters to specific tools. The command receives JSON on stdin describing the call, and returns JSON on stdout saying whether to proceed (and optionally what to change).
PostToolUse hook logs every tool call to a file or analytics system. Answers "what did the agent do last Thursday?"
PreToolUse hook inspects Bash commands for patterns your deny list might miss. Can block or warn.
SessionEnd hook uploads recent changes to a shared location (git commit, cloud backup, Notion page).
SessionStart hook injects the date, weather, recent GitHub issues into the agent's context so it doesn't have to fetch them.
Stop hook checks that the agent's final output doesn't contain secrets (scans for API keys, passwords).
PostToolUse hook on long-running commands sends a Telegram/Slack notification so you're pinged when the agent finishes.
Your hook script:
Example response to allow:
{ "continue": true }
Response to block with a message:
{ "continue": false, "stopReason": "detected a prod API key in the command" }
Once you start composing hooks, you're building an agent platform, not just an agent. PreToolUse for safety. SessionStart for context. SessionEnd for reporting. The agent becomes a service that runs with hooks as middleware.