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.

$ 安裝

git clone https://github.com/vasilyu1983/AI-Agents-public /tmp/AI-Agents-public && cp -r /tmp/AI-Agents-public/frameworks/claude-code-kit/framework/skills/claude-code-hooks ~/.claude/skills/AI-Agents-public

// tip: Run this command in your terminal to install the skill


name: claude-code-hooks description: 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.

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

EventTriggerUse Case
PreToolUseBefore tool executionValidate, block dangerous commands
PostToolUseAfter tool executionFormat, audit, notify
StopWhen Claude finishesRun tests, summarize
NotificationOn notificationsAlert integrations
SessionStartSession beginsInitialize environment
SessionEndSession endsCleanup, save state
UserPromptSubmitUser sends messagePreprocessing

Hook Structure

.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

{
  "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

VariableDescriptionAvailable In
CLAUDE_PROJECT_DIRProject root pathAll hooks
CLAUDE_TOOL_NAMECurrent tool namePre/PostToolUse
CLAUDE_TOOL_INPUTTool input (JSON)PreToolUse
CLAUDE_TOOL_OUTPUTTool outputPostToolUse
CLAUDE_FILE_PATHSAffected filesPostToolUse
CLAUDE_SESSION_IDSession identifierAll hooks

Exit Codes

CodeMeaningEffect
0SuccessContinue execution
1ErrorReport error, continue
2BlockBlock tool execution (PreToolUse only)

Hook Templates

Pre-Tool Validation

#!/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

#!/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

#!/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)

#!/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

#!/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:

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

Security Best Practices

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

{
  "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

# 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

Repository

vasilyu1983
vasilyu1983
Author
vasilyu1983/AI-Agents-public/frameworks/claude-code-kit/framework/skills/claude-code-hooks
21
Stars
6
Forks
Updated5d ago
Added1w ago