Claude Code Hooks
Shell commands that fire automatically at Claude Code lifecycle events. Approval gates, security scanners, auto-formatters, cost trackers, Slack integrations — enforcement, not persuasion.
What are Claude Code hooks?
Hooks are shell commands (or scripts) that Claude Code runs automatically at defined lifecycle events — before a tool is used, after a session ends, when a prompt is submitted, and so on. Configured in settings.json, they let you enforce policies, log activity, block dangerous operations, and integrate Claude Code into the rest of your dev workflow — without ever asking Claude to do anything.
Think of hooks as the middleware layer for Claude Code. A slash command runs when you type it. A subagent runs when you delegate to it. A hook runs whether you asked or not, at exactly the lifecycle event you configured. That makes hooks the right place for anything that must always happen: security gates, cost tracking, auto-formatters, audit logs.
.claude/settings.json that runs at a specific lifecycle event and can approve, block, or observe Claude Code's actions.
The hook lifecycle — every phase
Claude Code fires hooks at these events. Each event can have multiple hooks attached; they run in order.
SessionStart— when Claude Code starts a session. Good for context warmup, environment checks, cost-cap resets.UserPromptSubmit— when you submit a message. Can enrich the prompt (inject context) or block it.PreToolUse— before Claude calls a tool. This is the most important phase — approval gates, security checks, secret scans all live here.PostToolUse— after a tool call completes. Auto-format files that were written, log the operation, run tests on changed files.Notification— when Claude Code shows a notification (e.g., waiting for input). Send it to Slack, macOS notifications, ntfy.PreCompact— before Claude compacts context. Chance to snapshot state before it's lost.Stop— when Claude finishes responding. Trigger auto-tests, deploys, or handoff notes.SubagentStop— when a subagent finishes. Same asStop, but for delegated work.SessionEnd— when the session closes. Persist logs, sync team state, save cost totals.
Anatomy of a hook
Hooks are configured in .claude/settings.json (project) or ~/.claude/settings.json (user). Each entry has a matcher (which tool or event triggers it) and a command (what to run).
The hook script contract
Every hook script follows the same simple I/O contract:
- Stdin — Claude Code sends the event payload as JSON. For
PreToolUse, that includestool_name,tool_input, session ID, and cwd. - Exit 0 — success. Continue normally.
- Exit 2 — block the operation. Stdout is shown to Claude as the reason.
- Any other exit code — warn but continue. Stderr goes to logs.
- Timeout — default 60 seconds. Configurable per hook.
A real hook — block git push main
That's the entire hook. Twelve lines of bash, and Claude Code can never push to main again from this project — whether the request came from you, a slash command, a subagent, or Claude's own reasoning. That's the guarantee hooks give you: enforcement, not persuasion.
Why hooks matter for real teams
- Enforcement > instructions. Telling Claude "don't push to main" in the system prompt works most of the time. A hook works every time.
- Cost control. A
SessionStarthook that checks daily spend and blocks new sessions above the cap is 20 lines of bash. - Audit trail. A
PreToolUsehook that logs every tool call to a JSON file gives you a complete audit trail for security reviews. - Auto-hygiene. A
PostToolUsehook that runs your formatter after everyEdit/Writemeans Claude's output is always formatted correctly. - Team standards. Commit
.claude/settings.jsonand your hook scripts. Every dev's Claude Code enforces the same rules. - Integration. Hooks are just shell commands. That means anything you can shell to — Slack, Sentry, PagerDuty, a ticketing system — can be a Claude Code integration.
Hooks vs slash commands vs subagents
All three shape Claude Code behavior; they solve different problems:
- Hook = automatic, lifecycle-driven, enforced at the tool layer. Best for policy, observability, and integration — things you want to happen without being asked.
- Slash command = manual, prompt-driven, invoked by typing. Best for repeatable workflows and prompt templates. See commands hub.
- Subagent = delegated, role-based, isolated context. Best for specialized work with its own system prompt. See agents hub.
Mature Claude Code setups use all three: subagents for specialized roles, slash commands for repeatable workflows, hooks for enforced policy.
10 categories covering the full lifecycle
From PreToolUse security gates to SessionEnd summaries. Each sub-category page has the complete listing with real bash scripts and settings.json snippets.
PreToolUse blocks: force pushes, prod deploys, dangerous npm packages, destructive DB commands.
- approve-destructive-bash
- approve-git-force-push
- approve-npm-install
- approve-prod-deploy
- approve-drop-table
Secret scanning on writes, .env file protection, blocking curl-to-shell, credential lookups.
- secrets-scan-on-write
- env-file-protect
- block-curl-shell
- prevent-secret-commit
- block-dangerous-domains
Structured logs, Sentry integration, Datadog metrics, session recordings, tool-call audit trails.
- log-all-commands
- log-to-sentry
- log-to-datadog
- session-recording
- tool-call-audit
Daily spend caps, per-project cost tracking, high-cost alerts, monthly report generation.
- daily-cost-limit
- per-project-cost-tracker
- alert-high-cost
- monthly-cost-report
Auto-format on write, lint on edit, TypeScript type-check, Prettier, Black, Ruff, gofmt.
- auto-prettier
- auto-eslint
- auto-typecheck
- auto-black-python
- auto-ruff
Run tests on file save, coverage delta on write, related-file test runs.
- auto-test-related
- coverage-on-save
- auto-test-changed
Auto-commit checkpoints, branch-per-session, prevent commits to main from Claude.
- auto-commit-checkpoint
- auto-branch-per-session
- prevent-commit-main
Slack messages, Discord webhooks, macOS notifications, email on error or completion.
- notify-slack-on-complete
- notify-slack-on-error
- discord-webhook
- macos-notification
Trigger GitHub Actions, create preview deploys, rollback on failure, status reporting.
- trigger-gh-actions
- deploy-preview-on-commit
- rollback-on-fail
- status-check-report
- vercel-preview
- netlify-preview
Session start context warmup, session end summaries, context snapshotting, handoff notes.
- session-start-warmup
- session-end-summary
- context-snapshot
- handoff-note
- check-daily-limit-on-start
- clean-temp-on-end
40 most-installed hooks right now
The hooks devs install first, based on newsletter downloads and community submissions. Click any to see the full script, settings.json entry, and edge cases.
How to write and ship your first hook in 5 steps
From an idea to enforced team-wide policy in under an hour.
Write the hook script
Create a script anywhere in your repo (convention: .claude/hooks/<name>.sh). It reads JSON from stdin, does its work, and exits with 0 (allow), 2 (block with reason on stdout), or any other code (warn).
Bash is fine for simple hooks. For complex logic, use Python, Node, or Go. The interpreter must be available on $PATH.
Make it executable
Run chmod +x .claude/hooks/<name>.sh. Claude Code executes hook commands directly — if the file isn't executable, the hook silently fails.
Register the hook in settings.json
Add an entry to .claude/settings.json (project) or ~/.claude/settings.json (user). Choose the lifecycle phase, choose the matcher (e.g., Bash or Write|Edit), and set the command path. Use $CLAUDE_PROJECT_DIR to make paths portable.
Test with a controlled trigger
Trigger the hook manually. For a PreToolUse hook on Bash, ask Claude Code to run any Bash command. Watch for the hook's stdout/stderr in the session. If it doesn't fire, check the matcher pattern and file permissions.
Commit and share
Commit .claude/settings.json and .claude/hooks/ to git. Every teammate gets the same enforcement policies on their next pull. This is how you turn ad-hoc "please don't push to main" agreements into automatic team-wide guarantees.
Related long-form guides
In-depth reference material for when you need more than a snippet.
Claude Code Hooks — Complete Reference
Every phase, every JSON schema field, every gotcha. Includes 12 copy-paste hook recipes.
SetupThe Complete Claude Code Setup Guide
From npm install to production-ready. Where hooks fit in the setup order and configuration hierarchy.
SecurityClaude Code Security Best Practices
Hooks are the primary security layer. This guide covers the seven hooks every security-conscious team needs.
EnterpriseEnterprise Claude Code Rollout Playbook
Governance, audit trails, and policy hooks at scale. How to deploy Claude Code across 100+ developers.
Free tools for hooks
Generators, builders, and analyzers — no signup, no account needed.
Hook Builder (Visual)
Click-and-drop UI to configure Claude Code hooks. Auto-detects phase and exports valid JSON.
Free ToolHook Simulator
Simulate hook fires with mock tool calls. Debug your hook logic before shipping.
Free ToolRepo Analyzer
Paste a GitHub URL, get a tailored list of hooks to install for that stack.
Free ToolClaude Code Setup Score
Grade your Claude Code setup on best practices, including hook coverage for security and cost.
🐛 Hit an error while using hooks?
Our sister site AI Error Hub covers Claude Code errors, MCP connection failures, and stack traces — cross-referenced with everything on this site.
Frequently asked questions
The questions developers ask most about hooks.
A Claude Code hook is a shell command configured in .claude/settings.json that runs automatically at a specific lifecycle event — before a tool call, after a session ends, when a prompt is submitted, and so on.
Hooks can approve, block, or observe Claude Code's actions, giving you enforcement-level control that prompt engineering can't guarantee.
Nine phases in the current Claude Code:
SessionStart— session beginsUserPromptSubmit— user sends a messagePreToolUse— before a tool call (most used)PostToolUse— after a tool call (second most used)Notification— Claude shows a notificationPreCompact— before context compactionStop— Claude finishes respondingSubagentStop— subagent finishesSessionEnd— session closes
Two locations:
.claude/settings.jsonat the project root — team-shared. Commit to git.~/.claude/settings.jsonin your home directory — personal, applies across all projects.
Both merge, with project hooks running before user hooks in the same phase.
Exit with code 2. Whatever the script writes to stdout becomes the block reason shown to Claude.
exit 0— allow, continue normally.exit 2— block. Stdout is the reason.- Any other code — warn, but continue. Stderr goes to logs.
This design means hook scripts are trivial to write and hard to break.
Read stdin. Claude Code sends a JSON payload with tool_name, tool_input, session_id, and cwd. Parse it with jq in bash or the standard JSON library in Python/Node.
Example: echo $(cat) | jq -r '.tool_input.command' gives you the exact shell command about to run in a PreToolUse hook on the Bash tool.
Some phases support inline modification. PreToolUse and UserPromptSubmit can return modified inputs by writing a specific JSON response to stdout. This lets you enrich prompts, add safety flags to commands, or redirect writes.
Check the specific phase documentation for the exact schema — the shape differs by phase.
Commit .claude/settings.json and .claude/hooks/ to git. Teammates get every hook on their next pull — no install step.
Same workflow as slash commands and subagents. For sensitive environment-specific configs (API keys, endpoints), use environment variable references rather than hardcoding.
Yes, and you should think about them. A hook runs arbitrary shell commands as your user. A malicious .claude/settings.json in a cloned repo could do anything.
Best practices:
- Review hook scripts in unfamiliar repos before opening them.
- Keep hooks in reviewed code — never gitignored.
- Use narrow matchers so hooks fire only when needed.
- Prefer read-only hooks by default; require explicit review for hooks that write files or call external services.
Three checks:
- Is the script executable?
chmod +x. - Does the matcher pattern actually match your tool name? (
Bash,Write,Edit— case-sensitive.) - Is the file path in
settings.jsoncorrect? Use$CLAUDE_PROJECT_DIRfor portable paths.
Also check .claude/logs/ or run Claude Code with verbose logging to see hook lifecycle events.
Five hooks cover most teams' needs:
- prevent-commit-main — git safety.
- secrets-scan-on-write — never leak credentials.
- auto-prettier or auto-black-python — formatting hygiene.
- daily-cost-limit — spend cap.
- log-all-commands — audit trail.
All are lightweight and pay for themselves the first time they save you. Browse the 40 most-used hooks on this page to add more.
Get the weekly Claude Code digest
Every Tuesday: new hooks, Anthropic release recap, and the best community submission of the week. 13,000+ developers read it.