$ man how-to/how-to-setup-claude-code-hooks
CLI Toolsintermediate
How to Set Up Claude Code Hooks
Automate workflows at every stage of the agent lifecycle
What Hooks Are
Hooks are shell commands that run automatically at specific points in Claude Code's lifecycle. They give you deterministic control over the agent's behavior. Instead of hoping Claude remembers to format code after an edit, a hook guarantees it.
The key word is deterministic. CLAUDE.md instructions are suggestions - the model usually follows them but might not. Hooks are guarantees. A pre-command hook that blocks rm -rf will always block it. A post-edit hook that runs Prettier will always format the file.
Hooks run your code, not Claude's. They execute shell commands on your machine. This makes them powerful (you can do anything a shell script can do) and dangerous (a bad hook can break your workflow). Start simple, add complexity as needed.
PATTERN
Hook Types and Lifecycle
Claude Code supports hooks at these lifecycle points:
PreToolUse: Runs before Claude executes a tool (file edit, bash command, etc.). Use this to block dangerous commands, validate file paths, or inject context. If your hook returns a non-zero exit code, the tool call is blocked.
PostToolUse: Runs after Claude executes a tool. Use this to format edited files, run linters, send notifications, or log changes. The tool call has already happened - you are reacting to it.
Notification: Runs when Claude needs human input or wants to notify you. Use this to send Slack messages, play sounds, or trigger system notifications when Claude is waiting for approval.
Stop: Runs when Claude finishes a response. Use this for cleanup, logging, or triggering post-response workflows.
Each hook receives context about what triggered it: the tool name, the file path, the command, or the notification message. You use this context to decide what your hook should do.
CODE
Setting Up Your First Hook
Run /hooks in Claude Code to open the interactive hook configuration. Or edit your settings directly.
Hooks live in your Claude Code settings (either project-level in .claude/settings.json or user-level in ~/.claude/settings.json). The format is a JSON object mapping hook types to arrays of hook configurations.
Each hook has: a matcher (which tool or event triggers it), a command (what to run), and optionally a timeout.
Example: a PostToolUse hook that formats TypeScript files after edits.
The matcher checks if the tool was "Edit" or "Write" and the file ends in .ts or .tsx. The command runs Prettier on the edited file. Every time Claude edits a TypeScript file, it gets auto-formatted.
Example: a PreToolUse hook that blocks dangerous bash commands.
The matcher checks if the tool is "Bash" and the command contains "rm -rf" or "drop table" or "force push." If matched, the hook exits with code 1 and blocks the command. Claude sees the block and adjusts its approach.
Start with these two patterns: auto-format after edits and block dangerous commands. They provide immediate value with minimal complexity.
PATTERN
Real-World Hook Patterns
Security scanning: A PreToolUse hook that checks edited files for hardcoded secrets (API keys, passwords, tokens) before the edit lands. Uses a simple grep pattern against the new content. Blocks the edit if secrets are detected.
Notification on idle: A Notification hook that sends a macOS notification or Slack message when Claude is waiting for approval. Useful when running long tasks - you can work on something else and get pinged when Claude needs you.
Auto-testing: A PostToolUse hook that runs the relevant test file after any source file edit. If you edit src/auth.ts, the hook runs tests/auth.test.ts automatically. Claude sees the test results in its next turn.
Git safety: A PreToolUse hook that prevents Claude from running git push --force, git reset --hard, or git checkout . without explicit confirmation. These are the destructive git commands that can lose work.
Context injection: A PreToolUse hook that runs at session start and appends today's date, current git branch, and recent commit messages to the context. Claude starts every session with fresh situational awareness.
PRO TIP
Debugging and Gotchas
Hooks run synchronously. A slow hook blocks Claude from proceeding. Keep hooks fast - under 2 seconds. If you need to run something slow (a full test suite, a build), do it in the background and report results asynchronously.
Hook errors are visible to Claude. If your hook prints to stderr or exits with a non-zero code, Claude sees it as feedback. This is useful: a hook that blocks a command and prints "Blocked: never force push to main" teaches Claude to avoid that command in future turns.
Test hooks outside Claude Code first. Write the shell command, run it manually, verify it works. Then add it as a hook. Debugging a broken hook inside an active Claude Code session is frustrating.
Matchers are string matches, not regex (in the basic configuration). Use the command field for complex matching logic. Your hook script can inspect the full context and decide whether to act or pass through.
Hook order matters. Hooks of the same type run in array order. If you have two PreToolUse hooks, the first one runs first. If it blocks the tool, the second one never runs.
knowledge guide
related guides