Join our Discord Server
Ajeet Raina Ajeet Singh Raina is a former Docker Captain, Community Leader and Distinguished Arm Ambassador. He is a founder of Collabnix blogging site and has authored more than 700+ blogs on Docker, Kubernetes and Cloud-Native Technology. He runs a community Slack of 9800+ members and discord server close to 2600+ members. You can follow him on Twitter(@ajeetsraina).

Mastering Claude Code Hooks: Automate Your Dev Workflow in 2026

3 min read

AI interface on glowing keyboard - Claude Code hooks and automation

⏱ Reading time: ~13 min | 🗓 Updated: June 2026

If you’ve been using Claude Code for more than a few days, you’ve likely noticed a pattern: Claude finishes a task, you check the output, verify it manually, run a linter, run your test suite, and then move on. Now imagine all of that happening automatically — every single time, with zero exceptions. That’s exactly what Claude Code Hooks deliver.

Google Trends data shows that searches for “Claude Code hooks” have surged over 400% in Q2 2026, reflecting rapid adoption by developers who want to go beyond basic AI assistance and build fully automated, production-grade workflows. In this guide, we cover everything: what hooks are, all 18 hook events, the four hook types, real-world examples, and best practices.

What Are Claude Code Hooks?

Claude Code Hooks are shell scripts or commands that run automatically at specific points in Claude’s agent loop — before a tool executes, after a tool executes, at session start, at session end, and more. Unlike prompts or instructions (which Claude can decide to reinterpret), hooks are deterministic: they always run, and Claude cannot skip them.

This makes hooks ideal for enforcing hard requirements: running linters, blocking dangerous commands, sending Slack notifications, logging all file changes, auto-committing after every edit, and much more.

Hooks vs. Instructions: Key Difference

FeatureInstructions (CLAUDE.md)Hooks
Execution guaranteeBest-effort (Claude interprets)Always runs, 100%
Skippable by ClaudeYesNo
Can block tool executionNoYes (PreToolUse)
Can modify outputNoYes (PostToolUse)
Suitable for security rulesPartialYes
Runs external scriptsNoYes

The Four Hook Types

Claude Code supports four hook types, each firing at a different stage of the agent loop:

1. PreToolUse

Runs before Claude executes any tool call. This is your chance to inspect, validate, or block an action before it happens. If your hook exits with a non-zero status code, Claude will not proceed with the tool call.

# Example: Block any rm -rf commands
#!/bin/bash
TOOL_INPUT="$CLAUDE_TOOL_INPUT"
if echo "$TOOL_INPUT" | grep -q "rm -rf"; then
  echo "BLOCKED: Refusing to run destructive rm -rf command" >&2
  exit 1
fi
exit 0

2. PostToolUse

Runs after a tool executes. Use this for logging, notifications, auto-formatting, test runs, or any action that should follow every Claude tool call. PostToolUse hooks receive the tool name, input, and output as environment variables.

# Example: Auto-run ESLint after every file write
#!/bin/bash
if [ "$CLAUDE_TOOL_NAME" = "write_file" ]; then
  FILE="$CLAUDE_TOOL_OUTPUT_PATH"
  if [[ "$FILE" == *.js || "$FILE" == *.ts ]]; then
    npx eslint "$FILE" --fix
    echo "ESLint auto-fix applied to $FILE"
  fi
fi
exit 0

3. Notification

Fires when Claude is waiting for user input. Perfect for desktop notifications, Slack pings, or text messages so you don’t have to sit and watch Claude work.

# macOS desktop notification when Claude needs input
#!/bin/bash
osascript -e 'display notification "Claude Code needs your input!" with title "Claude Code" sound name "Glass"'
exit 0

4. Stop

Runs when Claude’s session ends or stops. Use this for cleanup tasks, final test runs, commit-and-push automation, or generating session summaries.

# Auto-commit all changes when Claude finishes
#!/bin/bash
cd "$CLAUDE_PROJECT_ROOT"
if git diff --quiet && git diff --staged --quiet; then
  echo "No changes to commit"
else
  git add -A
  git commit -m "chore: Claude Code auto-commit $(date +%Y-%m-%d)"
  echo "Auto-committed all changes"
fi
exit 0

All 18 Hook Events in Claude Code

EventHook TypeWhen It Fires
PreToolUsePreToolUseBefore any tool executes
PostToolUsePostToolUseAfter any tool executes
PreBashExecutionPreToolUseBefore a Bash command runs
PostBashExecutionPostToolUseAfter a Bash command runs
PreFileWritePreToolUseBefore writing a file
PostFileWritePostToolUseAfter writing a file
PreFileReadPreToolUseBefore reading a file
PostFileReadPostToolUseAfter reading a file
PreWebSearchPreToolUseBefore a web search
PostWebSearchPostToolUseAfter a web search
PreMCPToolUsePreToolUseBefore an MCP tool call
PostMCPToolUsePostToolUseAfter an MCP tool call
SessionStartNotificationWhen a session begins
SessionEndStopWhen a session ends
AgentStartNotificationWhen a subagent starts
AgentEndStopWhen a subagent finishes
NotificationNotificationWhen Claude needs input
UserPromptSubmitPreToolUseBefore user prompt is processed

Configuring Hooks: settings.json

Hooks are configured in your Claude Code settings.json file. There are three configuration locations:

  • Global: ~/.claude/settings.json — applies to all projects
  • Project: .claude/settings.json in your repo root — applies to this project, shared via git
  • Local: .claude/settings.local.json — project-level but gitignored (personal overrides)
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "write_file",
        "hooks": [
          { "type": "command", "command": "bash ~/.claude/hooks/post-write.sh" }
        ]
      }
    ],
    "PreToolUse": [
      {
        "matcher": "bash",
        "hooks": [
          { "type": "command", "command": "bash ~/.claude/hooks/security-check.sh" }
        ]
      }
    ],
    "Notification": [
      {
        "hooks": [
          { "type": "command", "command": "bash ~/.claude/hooks/notify.sh" }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          { "type": "command", "command": "bash ~/.claude/hooks/session-end.sh" }
        ]
      }
    ]
  }
}

10 Real-World Hook Recipes for Developers

Here are 10 practical hooks you can drop into your Claude Code setup today:

1. Auto-Run Tests After Every File Change

# PostFileWrite: run pytest on changed module
#!/bin/bash
FILE="$CLAUDE_TOOL_OUTPUT_PATH"
if [[ "$FILE" == *.py ]]; then
  MODULE=$(dirname "$FILE")
  python -m pytest "$MODULE" --tb=short -q 2>&1 | tail -20
fi
exit 0

2. Security Scanning Before Any Bash Execution

# PreBashExecution: block dangerous patterns
#!/bin/bash
INPUT="$CLAUDE_TOOL_INPUT"
DANGEROUS=("rm -rf /" "dd if=" "mkfs" "chmod -R 777 /")
for pattern in "${DANGEROUS[@]}"; do
  if echo "$INPUT" | grep -qF "$pattern"; then
    echo "SECURITY BLOCK: $pattern" >&2
    exit 1
  fi
done
exit 0

3. Slack Notification When Claude Needs Input

# Notification hook: ping Slack
#!/bin/bash
curl -s -X POST "$SLACK_WEBHOOK_URL" \
  -H "Content-type: application/json" \
  --data "{"text":"Claude Code needs your input on $(basename $PWD)"}" \
  > /dev/null
exit 0

4. Auto-Format Python Files with Black

# PostFileWrite: format Python with black and isort
#!/bin/bash
FILE="$CLAUDE_TOOL_OUTPUT_PATH"
if [[ "$FILE" == *.py ]]; then
  black "$FILE" --quiet
  isort "$FILE" --quiet
  echo "Formatted: $FILE"
fi
exit 0

5. Git Auto-Commit After Session End

# Stop hook: auto-commit all changes
#!/bin/bash
cd "$CLAUDE_PROJECT_ROOT" || exit 0
if [ -n "$(git status --porcelain)" ]; then
  git add -A
  git commit -m "feat: Claude Code session $(date +%Y-%m-%d)"
fi
exit 0

6. Audit Log All File Changes

# PostFileWrite: append to audit log
#!/bin/bash
LOGFILE="$HOME/.claude/audit.log"
echo "$(date +%Y-%m-%d\ %H:%M:%S) | WRITE | $CLAUDE_TOOL_OUTPUT_PATH | $CLAUDE_SESSION_ID" >> "$LOGFILE"
exit 0

7. TypeScript Type-Check After Every Write

# PostFileWrite: run tsc --noEmit
#!/bin/bash
FILE="$CLAUDE_TOOL_OUTPUT_PATH"
if [[ "$FILE" == *.ts || "$FILE" == *.tsx ]]; then
  cd "$(git rev-parse --show-toplevel 2>/dev/null || echo ".")"
  npx tsc --noEmit 2>&1 | grep "error TS" | head -5
fi
exit 0

8. Block Writes to Production Config Files

# PreFileWrite: protect production config files
#!/bin/bash
FILEPATH="$CLAUDE_TOOL_INPUT_PATH"
for pattern in ".env.production" "prod.config" "secrets.yml"; do
  if [[ "$FILEPATH" == *"$pattern"* ]]; then
    echo "BLOCKED: Cannot write to $FILEPATH" >&2
    exit 1
  fi
done
exit 0

9. Generate Session Summary on Stop

# Stop hook: write session summary markdown
#!/bin/bash
SUMMARY="$CLAUDE_PROJECT_ROOT/.claude/session-$(date +%Y%m%d-%H%M).md"
echo "# Claude Code Session Summary" > "$SUMMARY"
echo "Date: $(date)" >> "$SUMMARY"
git diff --name-only HEAD 2>/dev/null >> "$SUMMARY"
exit 0

10. Enforce Branch Naming Conventions

# PreBashExecution: enforce branch naming
#!/bin/bash
INPUT="$CLAUDE_TOOL_INPUT"
if echo "$INPUT" | grep -q "git checkout -b"; then
  BRANCH=$(echo "$INPUT" | grep -oP "(?<=-b )\S+")
  if ! echo "$BRANCH" | grep -qP "^(feat|fix|chore|docs|refactor)/"; then
    echo "BLOCKED: Branch must follow feat|fix|chore|docs|refactor/name" >&2
    exit 1
  fi
fi
exit 0

Hook Environment Variables Reference

VariableDescription
CLAUDE_TOOL_NAMEName of the tool being called (e.g., bash, write_file)
CLAUDE_TOOL_INPUTFull input string passed to the tool
CLAUDE_TOOL_OUTPUTTool output (PostToolUse hooks only)
CLAUDE_TOOL_INPUT_PATHFile path for file-related tools
CLAUDE_TOOL_OUTPUT_PATHOutput file path (write operations)
CLAUDE_SESSION_IDUnique session identifier
CLAUDE_PROJECT_ROOTRoot directory of the current project
CLAUDE_HOOK_EVENTThe event type that triggered this hook

Best Practices for Claude Code Hooks

  • Always exit with the right code: Exit 0 = success/proceed. Non-zero in PreToolUse = block the action.
  • Keep hooks fast: Hooks run synchronously. A slow hook slows down Claude. Scope hooks narrowly.
  • Use matchers: The matcher field lets you target specific tools or file patterns. Don’t run expensive hooks on everything.
  • Version-control project hooks: Commit .claude/settings.json for team-wide hooks. Use settings.local.json for personal preferences (gitignored).
  • Test hooks independently: Export mock env vars and run hook scripts in your terminal before deploying them.
  • Avoid infinite loops: If a PostFileWrite hook writes a file, guard against recursive triggering with sentinel checks.

Conclusion

Claude Code Hooks transform the AI coding experience from a conversational back-and-forth into a reliable, automated engineering platform. Whether you want zero-exception test runs, security guardrails, real-time notifications, or automatic git commits — hooks give you a programmable layer of control that operates entirely outside Claude’s decision-making.

Start with a simple notification hook, add a PostFileWrite formatter, and gradually build out a hooks library that matches your team’s workflow. The official hooks documentation is the best place to explore all available options.

📚 Resources & Further Reading

Official Documentation

Community Resources

Collabnix Related Posts

Have Queries? Join https://launchpass.com/collabnix

Ajeet Raina Ajeet Singh Raina is a former Docker Captain, Community Leader and Distinguished Arm Ambassador. He is a founder of Collabnix blogging site and has authored more than 700+ blogs on Docker, Kubernetes and Cloud-Native Technology. He runs a community Slack of 9800+ members and discord server close to 2600+ members. You can follow him on Twitter(@ajeetsraina).

Function Calling with Ollama: Building Local Tool-Using AI Agents

A practical guide to function/tool calling with Ollama: how it works, which local models support it, a minimal agent loop, and common pitfalls to...
Collabnix Team
2 min read
Join our Discord Server
Index