Deep Dive into Claude Code Hooks: 16 Events and 6 Real‑World Scenarios for Automating Your Workflow

This article explains Claude Code Hooks, detailing the 16 lifecycle events, four hook types, and six practical scenarios—from task‑completion notifications to file protection—showing how deterministic hooks can automate workflows, enforce policies, and improve reliability when using Claude Code for AI‑assisted development.

Shuge Unlimited
Shuge Unlimited
Shuge Unlimited
Deep Dive into Claude Code Hooks: 16 Events and 6 Real‑World Scenarios for Automating Your Workflow

What are Claude Code Hooks?

Claude Code Hooks are user‑defined shell commands that run automatically at specific points in Claude Code’s lifecycle. They provide deterministic control over the AI’s behavior, ensuring that critical actions always happen regardless of the model’s memory or randomness.

Lifecycle events

Claude Code follows a fixed flow. Hooks can be attached to any of the sixteen events, grouped by stage:

Session management : SessionStart, SessionEnd, InstructionsLoaded, ConfigChange

User interaction : UserPromptSubmit, Notification

Tool calls : PreToolUse, PermissionRequest, PostToolUse, PostToolUseFailure

Sub‑agent & team : SubagentStart, SubagentStop, TeammateIdle, TaskCompleted

Other : Stop, PreCompact

Events marked ✅ can be blocked by returning exit 2. Matcher support (✅) lets you filter by tool, file pattern, or custom scenario. PostToolUseFailure cannot block because the tool has already failed.

Hook types

Command Hook – Executes a shell command (default timeout 600 s). Ideal for deterministic rules such as file‑path checks, running formatters, or sending notifications.

HTTP Hook – Sends a POST request with JSON payload to a remote endpoint. Useful for team‑wide audit logs, webhook integrations, or calling external services.

Prompt Hook – Runs a single‑round LLM evaluation (default timeout 30 s). Suitable for decisions that require AI judgment, e.g., checking task completion.

Agent Hook – Starts a sub‑agent that can read files, run commands, and perform multi‑round verification (default timeout 60 s). Best for complex state validation such as running test suites.

Practical scenarios (from simple to advanced)

Scenario 1 – Task completion notification (Stop Hook)

Problem : Long‑running Claude tasks (refactoring, test runs) require you to watch the terminal.

Solution : Use a Stop Hook that triggers a macOS notification.

#!/bin/bash
# Send macOS desktop notification
osascript -e 'display notification "Claude has completed the task" with title "Claude Code"'

Configuration ( ~/.claude/settings.json)

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {"type": "command", "command": "/Users/youruser/.claude/complete-notification.sh"}
        ]
      }
    ]
  }
}

Key points:

Stop Hook fires after every Claude response.

No matcher needed – it always runs.

Absolute script path avoids resolution issues.

macOS native notification requires no extra dependencies.

Scenario 2 – Input‑waiting reminder (Notification Hook)

Problem : When running multiple Claude instances, you may miss the prompt that a session is waiting for input.

Solution : Notification Hook with sound and project name.

#!/bin/bash
DIR_NAME=$(basename "$PWD")
osascript -e "display notification \"Claude Code is waiting for your input
Project: $DIR_NAME\" with title \"Claude Code\" sound name \"Glass\""

Configuration

{
  "hooks": {
    "Notification": [
      {
        "matcher": "idle_prompt",
        "hooks": [
          {"type": "command", "command": "/Users/youruser/.claude/idle-notification.sh"}
        ]
      }
    ]
  }
}

Key points:

Matcher "idle_prompt" ensures the hook only runs for input‑waiting events.

\n creates a line break in the notification.

Sound "Glass" provides an audible cue.

Scenario 3 – Automatic code formatting (PostToolUse Hook)

Problem : Claude‑generated code often violates project formatting rules, causing CI failures.

Solution : PostToolUse Hook that runs the appropriate formatter based on file extension.

#!/bin/bash
set -euo pipefail
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
if [[ -n "$FILE_PATH" && -f "$FILE_PATH" ]]; then
  EXT="${FILE_PATH##*.}"
  case "$EXT" in
    js|jsx|ts|tsx|json|css|scss|md) npx prettier --write "$FILE_PATH" 2>/dev/null ;;
    py) black "$FILE_PATH" 2>/dev/null ;;
    go) gofmt -w "$FILE_PATH" 2>/dev/null ;;
  esac
fi
exit 0

Configuration

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit|MultiEdit",
        "hooks": [
          {"type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/format-code.sh", "timeout": 15}
        ]
      }
    ]
  }
}

Key points:

Matcher captures all edit/write actions.

Timeout prevents long formatting of huge files.

Supports JavaScript/TypeScript, Python, Go, JSON, CSS, Markdown.

Scenario 4 – Protect sensitive files (PreToolUse Hook)

Problem : Claude may accidentally modify .env, package-lock.json, or delete node_modules, exposing secrets or causing long reinstall times.

Solution : PreToolUse Hook that blocks operations on a whitelist of protected patterns.

#!/bin/bash
set -euo pipefail
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
PROTECTED_PATTERNS=(".env" "package-lock.json" ".git/" "*.pem" "*.key" "secrets.yaml")
for pattern in "${PROTECTED_PATTERNS[@]}"; do
  if [[ "$FILE_PATH" == *$pattern* ]]; then
    echo "Blocked: $FILE_PATH matches protected pattern '$pattern'" >&2
    exit 2
  fi
done
exit 0

Configuration

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {"type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"}
        ]
      }
    ]
  }
}

Key points:

Returning exit 2 stops the operation and shows Claude an error message.

Wildcard patterns (e.g., *.pem) allow flexible matching. set -euo pipefail makes the script robust.

Scenario 5 – Re‑inject context after compression (SessionStart Hook)

Problem : After context compression Claude forgets project conventions (e.g., "use Bun, not npm").

Solution : SessionStart Hook that echoes reminders or dynamically injects Git history.

Static version (reminder only):

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "compact",
        "hooks": [
          {"type": "command", "command": "echo 'Reminder: use Bun, not npm. Run bun test before committing. Current sprint: auth refactor.'"}
        ]
      }
    ]
  }
}

Dynamic version (inject recent commits, branch, uncommitted changes, and optional PROJECT_RULES.md):

#!/bin/bash
# Recent commits
echo "Recent commits:"
git log --oneline -5

# Current branch
echo -e "
Current branch: $(git branch --show-current)"

# Uncommitted changes
if [ -n "$(git status --porcelain)" ]; then
  echo -e "
Uncommitted changes:"
  git status --short
fi

# Project rules
if [ -f PROJECT_RULES.md ]; then
  echo -e "
Project Rules:"
  cat PROJECT_RULES.md
fi

Configuration

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "compact",
        "hooks": [
          {"type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/inject-context.sh"}
        ]
      }
    ]
  }
}

Scenario 6 – Command audit log (PostToolUse Hook)

Problem : Need a complete record of every Bash command Claude runs for audit or debugging.

Solution : PostToolUse Hook that appends a structured line to a log file.

#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command')
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id')
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
CWD=$(echo "$INPUT" | jq -r '.cwd')
printf "[%s] [%s] [%s] %s
" "$TIMESTAMP" "$SESSION_ID" "$CWD" "$COMMAND" >> ~/.claude/command-audit.log

Configuration

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {"type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/audit-commands.sh"}
        ]
      }
    ]
  }
}

Sample log lines:

[2026-03-07 14:23:15] [abc123] [/Users/dev/myproject] npm test
[2026-03-07 14:24:32] [abc123] [/Users/dev/myproject] git add .
[2026-03-07 14:25:01] [abc123] [/Users/dev/myproject] git commit -m "feat: add auth"
[2026-03-07 14:30:45] [def456] [/Users/dev/another] docker build -t app .

Key points:

Timestamp, session ID, and cwd make each entry traceable.

Structured logs enable SIEM analysis, command frequency stats, and security audits.

Advanced hook types

Prompt Hook – AI decision check

Example: after a Stop event, verify that all tasks mentioned in the conversation are truly complete.

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Check if all tasks mentioned in the conversation are complete. If not, respond with {\"ok\": false, \"reason\": \"what remains to be done\"}. If complete, respond with {\"ok\": true}.",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

Key points:

Runs a single‑round LLM evaluation.

Response must be valid JSON; Claude Code parses it.

Timeout can be increased for more complex reasoning.

Agent Hook – Run tests and verify

Example: after a Stop event, launch a sub‑agent that runs the test suite and reports failures.

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "agent",
            "prompt": "Verify that all unit tests pass. Run the test suite and check the results. If any tests fail, report the failures. $ARGUMENTS",
            "timeout": 120
          }
        ]
      }
    ]
  }
}

Key points:

Agent Hook provides multi‑round interaction and can read/write files.

Default timeout 60 s; extended here for longer test runs.

HTTP Hook – Remote audit service

Example: POST structured data to a central audit endpoint.

{
  "hooks": {
    "PostToolUse": [
      {
        "hooks": [
          {
            "type": "http",
            "url": "https://audit.company.com/hooks/tool-use",
            "headers": {
              "Authorization": "Bearer $MY_TOKEN",
              "Content-Type": "application/json"
            },
            "allowedEnvVars": ["MY_TOKEN"]
          }
        ]
      }
    ]
  }
}

Key points:

HTTP Hook posts JSON to any service.

Environment variables must be whitelisted via allowedEnvVars for security.

Best practices

Configuration layers

Claude supports hierarchical configuration. A typical layering strategy:

User‑level ( ~/.claude/settings.json) – generic notifications, global protection rules.

Project‑level ( .claude/settings.json) – project‑specific formatters, protected file lists, convention injection.

Local‑level ( .claude/settings.local.json) – private script paths, secret tokens (git‑ignored).

Policy‑level (enterprise) – admin‑controlled mandatory security policies that cannot be overridden.

Robust script template

#!/bin/bash
set -euo pipefail  # strict mode
# Read JSON input
INPUT=$(cat)
# Validate JSON
if ! echo "$INPUT" | jq empty 2>/dev/null; then
  echo "Invalid JSON input" >&2
  exit 1
fi
# Extract fields with defaults
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
# Main logic (example placeholder)
if [[ -n "$FILE_PATH" && -f "$FILE_PATH" ]]; then
  # ... do something ...
fi
exit 0

Key points: set -euo pipefail aborts on errors.

Validate JSON before processing.

Use exit 0 to allow, exit 2 to block.

Performance optimisation

Keep high‑frequency events ( SessionStart, Notification) under 1 s.

Set sensible timeouts (e.g., formatting 15 s, test verification 120 s).

Run non‑critical tasks asynchronously with "async": true to avoid blocking the main flow.

Avoid shared‑resource conflicts when multiple hooks run in parallel.

Debugging tips

Enable detailed mode in the Claude UI (Ctrl+O) or run claude --debug to see hook execution logs.

Manually test a hook script by piping a simulated JSON event, e.g.:

# Simulate PreToolUse event
echo '{"tool_name":"Bash","tool_input":{"command":"npm test"},"session_id":"test123","cwd":"/Users/dev/myproject"}' | ./my-hook.sh

Ensure the script is executable: chmod +x script.sh.

Common issues & fixes

Hook not firing : Verify the event name, matcher case‑sensitivity, and that the script is executable.

Hook outputs error : Ensure no stray output from shell profiles (e.g., .zshrc) interferes with JSON parsing. Guard prints with if [[ $- == *i* ]]; then echo "..."; fi.

Stop Hook infinite loop : Guard against repeated execution by checking a custom flag in the input JSON (e.g., .stop_hook_active) and exit early.

JSON parsing failure : Remove any non‑JSON text printed before the JSON payload; keep scripts silent on stdout unless returning valid JSON.

Conclusion

Claude Code Hooks turn a “black‑box” AI assistant into a deterministic automation engine. By defining explicit hooks you guarantee that critical actions—such as formatting, security checks, notifications, and audit logging—always happen, regardless of Claude’s internal memory or randomness.

The sixteen lifecycle events cover the entire session, from start to end, and the four hook types (Command, HTTP, Prompt, Agent) let you choose the appropriate level of complexity for each task. A sensible configuration hierarchy (user → project → local → policy) keeps shared rules reusable while allowing private overrides.

Start with essential hooks (task‑completion notification, input reminder, automatic formatting) and gradually add advanced ones (file protection, context injection, audit logging, test verification) to build a robust, secure, and fully automated Claude Code workflow.

Resources:

Hooks Guide: https://code.claude.com/docs/en/hooks-guide

Hooks Reference: https://code.claude.com/docs/en/hooks

Community article: https://mp.weixin.qq.com/s/evF1bkuUm88uFKejQDGGdA

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

AIautomationworkflowhooksShell ScriptsClaude CodeDeterministic Control
Shuge Unlimited
Written by

Shuge Unlimited

Formerly "Ops with Skill", now officially upgraded. Fully dedicated to AI, we share both the why (fundamental insights) and the how (practical implementation). From technical operations to breakthrough thinking, we help you understand AI's transformation and master the core abilities needed to shape the future. ShugeX: boundless exploration, skillful execution.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.