Agent skill

claude-code-hooks

Create event-driven hooks for Claude Code automation. Configure PreToolUse, PostToolUse, Stop, and other hook events with bash scripts, environment variables, matchers, and exit codes.

Stars 163
Forks 31

Install this agent skill to your Project

npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/development/claude-code-hooks

SKILL.md

Claude Code Hooks — Meta Reference

This skill provides the definitive reference for creating Claude Code hooks. Use this when building automation that triggers on Claude Code events.


When to Use This Skill

  • Building event-driven automation for Claude Code
  • Creating PreToolUse guards to block dangerous commands
  • Implementing PostToolUse formatters, linters, or auditors
  • Adding Stop hooks for testing or notifications
  • Setting up SessionStart/SessionEnd for environment management
  • Integrating Claude Code with CI/CD pipelines (headless mode)

Quick Reference

Event Trigger Use Case
PreToolUse Before tool execution Validate, block dangerous commands
PostToolUse After tool execution Format, audit, notify
Stop When Claude finishes Run tests, summarize
Notification On notifications Alert integrations
SessionStart Session begins Initialize environment
SessionEnd Session ends Cleanup, save state
UserPromptSubmit User sends message Preprocessing

Hook Structure

text
.claude/hooks/
├── pre-tool-validate.sh
├── post-tool-format.sh
├── post-tool-audit.sh
├── stop-run-tests.sh
└── session-start-init.sh

Configuration

settings.json

json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/post-tool-format.sh"
          }
        ]
      }
    ],
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/pre-tool-validate.sh"
          }
        ]
      }
    ],
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/stop-run-tests.sh"
          }
        ]
      }
    ]
  }
}

Environment Variables

Variable Description Available In
CLAUDE_PROJECT_DIR Project root path All hooks
CLAUDE_TOOL_NAME Current tool name Pre/PostToolUse
CLAUDE_TOOL_INPUT Tool input (JSON) PreToolUse
CLAUDE_TOOL_OUTPUT Tool output PostToolUse
CLAUDE_FILE_PATHS Affected files PostToolUse
CLAUDE_SESSION_ID Session identifier All hooks

Exit Codes

Code Meaning Effect
0 Success Continue execution
1 Error Report error, continue
2 Block Block tool execution (PreToolUse only)

Hook Templates

Pre-Tool Validation

bash
#!/bin/bash
set -euo pipefail

# Block dangerous commands
if [[ "$CLAUDE_TOOL_NAME" == "Bash" ]]; then
  INPUT="$CLAUDE_TOOL_INPUT"

  # Block rm -rf /
  if echo "$INPUT" | grep -qE 'rm\s+-rf\s+/'; then
    echo "BLOCKED: Dangerous rm command detected"
    exit 2
  fi

  # Block force push to main
  if echo "$INPUT" | grep -qE 'git\s+push.*--force.*(main|master)'; then
    echo "BLOCKED: Force push to main/master not allowed"
    exit 2
  fi

  # Block credential exposure
  if echo "$INPUT" | grep -qE '(password|secret|api_key)\s*='; then
    echo "WARNING: Possible credential exposure"
  fi
fi

exit 0

Post-Tool Formatting

bash
#!/bin/bash
set -euo pipefail

# Auto-format modified files
if [[ "$CLAUDE_TOOL_NAME" =~ ^(Edit|Write)$ ]]; then
  FILES="$CLAUDE_FILE_PATHS"

  for file in $FILES; do
    if [[ -f "$file" ]]; then
      case "$file" in
        *.js|*.ts|*.jsx|*.tsx|*.json|*.md)
          npx prettier --write "$file" 2>/dev/null || true
          ;;
        *.py)
          ruff format "$file" 2>/dev/null || true
          ;;
        *.go)
          gofmt -w "$file" 2>/dev/null || true
          ;;
        *.rs)
          rustfmt "$file" 2>/dev/null || true
          ;;
      esac
    fi
  done
fi

exit 0

Post-Tool Security Audit

bash
#!/bin/bash
set -euo pipefail

# Audit file changes for security issues
if [[ "$CLAUDE_TOOL_NAME" =~ ^(Edit|Write)$ ]]; then
  FILES="$CLAUDE_FILE_PATHS"

  for file in $FILES; do
    if [[ -f "$file" ]]; then
      # Check for hardcoded secrets
      if grep -qE '(password|secret|api_key|token)\s*[:=]\s*["\x27][^"\x27]+["\x27]' "$file"; then
        echo "WARNING: Possible hardcoded secret in $file"
      fi

      # Check for console.log in production code
      if [[ "$file" =~ \.(ts|js|tsx|jsx)$ ]] && grep -q 'console.log' "$file"; then
        echo "NOTE: console.log found in $file"
      fi
    fi
  done
fi

exit 0

Stop Hook (Run Tests)

bash
#!/bin/bash
set -euo pipefail

# Run tests after Claude finishes
cd "$CLAUDE_PROJECT_DIR"

# Detect test framework
if [[ -f "package.json" ]]; then
  if grep -q '"vitest"' package.json; then
    npm run test 2>&1 | head -50
  elif grep -q '"jest"' package.json; then
    npm test 2>&1 | head -50
  fi
elif [[ -f "pytest.ini" ]] || [[ -f "pyproject.toml" ]]; then
  pytest --tb=short 2>&1 | head -50
fi

exit 0

Session Start

bash
#!/bin/bash
set -euo pipefail

cd "$CLAUDE_PROJECT_DIR"

# Check git status
echo "=== Git Status ==="
git status --short

# Check for uncommitted changes
if ! git diff --quiet; then
  echo "WARNING: Uncommitted changes detected"
fi

# Verify dependencies
if [[ -f "package.json" ]]; then
  if [[ ! -d "node_modules" ]]; then
    echo "NOTE: node_modules missing, run npm install"
  fi
fi

exit 0

Matchers

Matchers filter which tool triggers the hook:

Matcher Matches
"" (empty) All tools
"Bash" Bash tool only
"Edit|Write" Edit OR Write
"Edit.*" Edit and variants (regex)

Security Best Practices

text
HOOK SECURITY CHECKLIST

[ ] Validate all inputs with regex
[ ] Quote all variables: "$VAR" not $VAR
[ ] Use absolute paths
[ ] No eval with untrusted input
[ ] Set -euo pipefail at top
[ ] Keep hooks fast (<1 second)
[ ] Log actions for audit
[ ] Test manually before deploying

Hook Composition

Multiple Hooks on Same Event

json
{
  "PostToolUse": [
    {
      "matcher": "Edit|Write",
      "hooks": [
        { "type": "command", "command": ".claude/hooks/format.sh" },
        { "type": "command", "command": ".claude/hooks/audit.sh" },
        { "type": "command", "command": ".claude/hooks/notify.sh" }
      ]
    }
  ]
}

Hooks execute in order. If one fails, subsequent hooks may not run.


Debugging Hooks

bash
# Test hook manually
CLAUDE_TOOL_NAME="Edit" \
CLAUDE_FILE_PATHS="src/app.ts" \
CLAUDE_PROJECT_DIR="$(pwd)" \
bash .claude/hooks/post-tool-format.sh

# Check exit code
echo $?

Navigation

Resources

Related Skills

Didn't find tool you were looking for?

Be as detailed as possible for better results