Claude Code Hooks — 52 Tested Automation Templates | AI Code Toolkit
52 tested hooks · 10 categories · Nine lifecycle phases covered

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.

52
Hooks
10
Categories
9
Lifecycle Phases
48h
Update SLA

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.

The one-line version: A hook is a shell command configured in .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 as Stop, 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).

json .claude/settings.json
{ "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/prevent-push-main.sh" } ] } ], "PostToolUse": [ { "matcher": "Write|Edit", "hooks": [ { "type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/auto-format.sh" } ] } ] } }

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 includes tool_name, tool_input, session ID, and cwd.
  • Exit 0 — success. Continue normally.
  • Exit 2block 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

bash .claude/hooks/prevent-push-main.sh
#!/bin/bash # Blocks any Bash tool call that pushes to main. input=$(cat) command=$(echo "$input" | jq -r '.tool_input.command // ""') if echo "$command" | grep -qE "git push .*(main|master)|git push -u .* (main|master)"; then echo "Blocked: pushing directly to main/master is not allowed." >&2 echo "Use a feature branch and open a PR instead." exit 2 fi exit 0

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 SessionStart hook that checks daily spend and blocks new sessions above the cap is 20 lines of bash.
  • Audit trail. A PreToolUse hook that logs every tool call to a JSON file gives you a complete audit trail for security reviews.
  • Auto-hygiene. A PostToolUse hook that runs your formatter after every Edit/Write means Claude's output is always formatted correctly.
  • Team standards. Commit .claude/settings.json and 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.

Browse by Category

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.

🛑 Approval / Gating 8

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
Browse all 8 →
🔒 Security 5

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
Browse all 5 →
📊 Logging & Observability 6

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
Browse all 6 →
💰 Cost Tracking 4

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
Browse all 4 →
✨ Formatter / Linter 7

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
Browse all 7 →
🧪 Test Hooks 3

Run tests on file save, coverage delta on write, related-file test runs.

  • auto-test-related
  • coverage-on-save
  • auto-test-changed
Browse all 3 →
🌿 Git Hooks 3

Auto-commit checkpoints, branch-per-session, prevent commits to main from Claude.

  • auto-commit-checkpoint
  • auto-branch-per-session
  • prevent-commit-main
Browse all 3 →
🔔 Notifications 4

Slack messages, Discord webhooks, macOS notifications, email on error or completion.

  • notify-slack-on-complete
  • notify-slack-on-error
  • discord-webhook
  • macos-notification
Browse all 4 →
🔄 CI/CD Integration 6

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
Browse all 6 →
⏱️ Session Lifecycle 6

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
Browse all 6 →
How to Use

How to write and ship your first hook in 5 steps

From an idea to enforced team-wide policy in under an hour.

1

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.

2

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.

3

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.

4

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.

5

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.

🐛 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.

Visit AI Error Hub →
FAQ

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 begins
  • UserPromptSubmit — user sends a message
  • PreToolUse — before a tool call (most used)
  • PostToolUse — after a tool call (second most used)
  • Notification — Claude shows a notification
  • PreCompact — before context compaction
  • Stop — Claude finishes responding
  • SubagentStop — subagent finishes
  • SessionEnd — session closes

Two locations:

  • .claude/settings.json at the project root — team-shared. Commit to git.
  • ~/.claude/settings.json in 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 2block. 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:

  1. Is the script executable? chmod +x.
  2. Does the matcher pattern actually match your tool name? (Bash, Write, Edit — case-sensitive.)
  3. Is the file path in settings.json correct? Use $CLAUDE_PROJECT_DIR for portable paths.

Also check .claude/logs/ or run Claude Code with verbose logging to see hook lifecycle events.

Five hooks cover most teams' needs:

  1. prevent-commit-main — git safety.
  2. secrets-scan-on-write — never leak credentials.
  3. auto-prettier or auto-black-python — formatting hygiene.
  4. daily-cost-limit — spend cap.
  5. 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.

Share with