Hook lifecycle
Claude Code runs hooks at specific points during a session. When an event fires and a matcher matches, Claude Code passes JSON context about the event to your hook handler. For command hooks, input arrives on stdin. For HTTP hooks, it arrives as the POST request body. Your handler can then inspect the input, take action, and optionally return a decision. Events fall into three cadences:- once per session:
SessionStartandSessionEnd - once per turn:
UserPromptSubmit,Stop, andStopFailure - on every tool call inside the agentic loop:
PreToolUseandPostToolUse, exceptEndConversationcalls, which skip both
How a hook resolves
To see how the event, the matcher, and the handler fit together, consider thisPreToolUse hook that blocks destructive shell commands.
- macOS/Linux
- Windows (PowerShell)
The The script reads the JSON input from stdin, extracts the command, and returns a This script, like the other Bash examples on this page that parse JSON input, uses
matcher narrows to Bash tool calls and the if condition narrows further to Bash subcommands matching rm *, so block-rm.sh only spawns when both filters match:permissionDecision of "deny" if it contains rm -rf. Save it to .claude/hooks/block-rm.sh in your project and make it executable with chmod +x .claude/hooks/block-rm.sh so Claude Code can run it:jq, so install jq and make sure it is on your PATH before trying them.