Master Claude Code in a Week (Part 7): Hooks

· 6 min read

This is the seventh part of the "Master Claude Code in a Week" series. In this article, we'll explore Hooks — event-driven automation for Claude Code.

What are Hooks?

Hooks are event-driven shell commands that automatically execute when Claude Code events occur. Hooks let you "hook into" the Claude Code lifecycle to perform actions like formatting, validation, and notification.

┌────────────────────────────────────────────────────────────────┐
│                          HOOKS                                  │
├────────────────────────────────────────────────────────────────┤
│                                                                 │
│   Claude Code Event                                            │
│          │                                                     │
│          ▼                                                     │
│   ┌─────────────────┐     ┌─────────────────┐                 │
│   │  PreToolUse     │────►│ format-code.sh  │                 │
│   │  (Write file)   │     │ (Auto-format)   │                 │
│   └─────────────────┘     └─────────────────┘                 │
│          │                                                     │
│          ▼                                                     │
│   ┌─────────────────┐                                         │
│   │   Tool executes │                                         │
│   └─────────────────┘                                         │
│          │                                                     │
│          ▼                                                     │
│   ┌─────────────────┐     ┌─────────────────┐                 │
│   │  PostToolUse    │────►│ security-scan.sh│                 │
│   │  (Write done)   │     │ (Scan for issues)│                 │
│   └─────────────────┘     └─────────────────┘                 │
│                                                                 │
└────────────────────────────────────────────────────────────────┘

Hook Types

Claude Code has 4 hook types with 25 events:

1. Tool Hooks

Event When Use case
PreToolUse Before tool runs Validation, formatting
PostToolUse After tool completes Scanning, logging
PostToolUseFailure When tool fails Error logging, recovery
PermissionRequest When permission needed Custom permission logic

2. Session Hooks

Event When Use case
SessionStart Session starts Setup, initialization
SessionEnd Session ends Cleanup, summary
Stop User stops Save state
StopFailure Stop fails Recovery
SubagentStart Subagent starts Logging
SubagentStop Subagent ends Cleanup

3. Task Hooks

Event When Use case
UserPromptSubmit User sends prompt Validation, logging
TaskCompleted Task completes Notification
TaskCreated New task created Tracking
TeammateIdle Teammate idle Workload balance

4. Lifecycle Hooks

Event When Use case
ConfigChange Config changes Reload
CwdChanged Directory changes Context update
FileChanged File changes Auto-refresh
PreCompact Before compact Save important context
PostCompact After compact Restore context

Configuring Hooks

settings.json Location

Global:

~/.claude/settings.json

Project:

.claude/settings.json

Configuration Structure

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write",
        "hooks": ["~/.claude/hooks/format-code.sh"]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Write",
        "hooks": ["~/.claude/hooks/security-scan.sh"]
      }
    ],
    "SessionStart": [
      {
        "hooks": ["~/.claude/hooks/session-start.sh"]
      }
    ],
    "TaskCompleted": [
      {
        "hooks": ["~/.claude/hooks/notify-team.sh"]
      }
    ]
  }
}

Matcher Options

Matcher Description Example
"Write" File write operations Format before write
"Read" File read operations Log file access
"Bash" Shell commands Validate commands
"*" All tools Global logging
"Edit" File edits Backup before edit

Installing Hooks

Step 1: Create directory

mkdir -p ~/.claude/hooks

Step 2: Copy hooks from claude-howto

cp claude-howto/06-hooks/*.sh ~/.claude/hooks/
chmod +x ~/.claude/hooks/*.sh

Step 3: Configure settings.json

# Create or edit settings.json
cat > ~/.claude/settings.json << 'EOF'
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write",
        "hooks": ["~/.claude/hooks/format-code.sh"]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Write",
        "hooks": ["~/.claude/hooks/security-scan.sh"]
      }
    ]
  }
}
EOF

Example Hooks

1. Auto-format Code

File: ~/.claude/hooks/format-code.sh

#!/bin/bash
# Auto-format code before writing

FILE_PATH="$CLAUDE_TOOL_INPUT_PATH"

if [ -z "$FILE_PATH" ]; then
    exit 0
fi

case "$FILE_PATH" in
    *.php)
        if command -v pint &> /dev/null; then
            pint "$FILE_PATH" 2>/dev/null
        fi
        ;;
    *.js|*.ts|*.jsx|*.tsx)
        if command -v prettier &> /dev/null; then
            prettier --write "$FILE_PATH" 2>/dev/null
        fi
        ;;
    *.py)
        if command -v black &> /dev/null; then
            black "$FILE_PATH" 2>/dev/null
        fi
        ;;
esac

exit 0

2. Security Scan

File: ~/.claude/hooks/security-scan.sh

#!/bin/bash
# Scan for security issues after file write

FILE_PATH="$CLAUDE_TOOL_INPUT_PATH"
ISSUES_FOUND=0

if [ -z "$FILE_PATH" ] || [ ! -f "$FILE_PATH" ]; then
    exit 0
fi

# Check for hardcoded secrets
if grep -iE "(password|api_key|secret)\s*=\s*['\"]" "$FILE_PATH" > /dev/null 2>&1; then
    echo "⚠️  Potential hardcoded secret found"
    ISSUES_FOUND=1
fi

# Check for debug statements
if grep -E "(console\.log|var_dump|dd\()" "$FILE_PATH" > /dev/null 2>&1; then
    echo "⚠️  Debug statement found"
    ISSUES_FOUND=1
fi

if [ $ISSUES_FOUND -eq 1 ]; then
    echo "Security scan found issues in: $FILE_PATH"
fi

exit 0

3. Log Bash Commands

File: ~/.claude/hooks/log-bash.sh

#!/bin/bash
# Log all bash commands executed by Claude

LOG_FILE="${HOME}/.claude/logs/bash-$(date +%Y-%m-%d).log"
mkdir -p "$(dirname "$LOG_FILE")"

COMMAND="$CLAUDE_TOOL_INPUT_COMMAND"

if [ -n "$COMMAND" ]; then
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $COMMAND" >> "$LOG_FILE"
fi

exit 0

4. Team Notification

File: ~/.claude/hooks/notify-team.sh

#!/bin/bash
# Notify team when task is completed

WEBHOOK_URL="${SLACK_WEBHOOK_URL:-}"

if [ -z "$WEBHOOK_URL" ]; then
    exit 0
fi

TASK_NAME="${CLAUDE_TASK_NAME:-Unknown task}"
PROJECT_NAME="$(basename "$(pwd)")"

curl -s -X POST "$WEBHOOK_URL" \
    -H "Content-Type: application/json" \
    -d "{
        \"text\": \"✅ Task completed in $PROJECT_NAME: $TASK_NAME\"
    }" > /dev/null

exit 0

Environment Variables

Hooks receive information via environment variables:

Tool Hooks

Variable Description
CLAUDE_TOOL_NAME Name of tool running
CLAUDE_TOOL_INPUT_PATH File path (for file operations)
CLAUDE_TOOL_INPUT_COMMAND Command (for bash)
CLAUDE_TOOL_OUTPUT Tool output (PostToolUse)

Session Hooks

Variable Description
CLAUDE_SESSION_ID Session ID
CLAUDE_SESSION_START Start timestamp
CLAUDE_PROJECT_DIR Project directory

Best Practices

1. Exit codes matter

# ✅ Good - Use exit codes properly
if [ -f "$FILE" ]; then
    do_something
    exit 0  # Success
else
    echo "File not found"
    exit 1  # Failure - may block operation
fi

2. Keep hooks fast

# ❌ Bad - Slow hook
npm install  # Don't do heavy operations

# ✅ Good - Quick check
if [ ! -d "node_modules" ]; then
    echo "⚠️  Dependencies not installed. Run: npm install"
fi

3. Fail gracefully

#!/bin/bash
# ✅ Good - Handle missing tools
if ! command -v prettier &> /dev/null; then
    exit 0
fi

prettier --write "$FILE_PATH"

4. Use logging

#!/bin/bash
LOG_FILE="${HOME}/.claude/logs/hooks.log"
mkdir -p "$(dirname "$LOG_FILE")"

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> "$LOG_FILE"
}

log "Starting security scan for $FILE_PATH"

Complete Configuration

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write",
        "hooks": ["~/.claude/hooks/format-code.sh"]
      },
      {
        "matcher": "Bash",
        "hooks": ["~/.claude/hooks/log-bash.sh"]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Write",
        "hooks": ["~/.claude/hooks/security-scan.sh"]
      }
    ],
    "TaskCompleted": [
      {
        "hooks": ["~/.claude/hooks/notify-team.sh"]
      }
    ]
  }
}

Summary

Hooks provide powerful automation:

  • ✅ Event-driven execution
  • ✅ 25 hook events
  • ✅ Custom validation and formatting
  • ✅ Integration with external tools
  • ✅ Team notifications

Next Up

In the next part, we'll explore MCP Protocol — how to connect Claude Code with external tools and APIs.

References


This series is translated and expanded from claude-howto — MIT License.

Comments