Files
2025-11-30 08:51:34 +08:00

14 KiB
Raw Permalink Blame History

Hook Examples Library

Pre-built hook patterns for common use cases. All examples are production-ready and security-reviewed.

Configuration Format Note: All JSON examples below show the complete hooks.json structure. For plugin hooks (like PRISM), use ${CLAUDE_PLUGIN_ROOT} in paths. For user-level hooks, use absolute paths.

Quick Reference

Example Event Purpose Language
bash-command-logger PreToolUse Log all bash commands Bash + jq
file-protection PreToolUse Block edits to sensitive files Python
auto-formatter PostToolUse Format code on save Bash
story-context-enforcer PreToolUse Ensure PRISM story context Python
workflow-tracker PostToolUse Track workflow progress Python
desktop-notifier Stop Desktop notifications Bash
git-safety-guard PreToolUse Prevent dangerous git ops Python
test-runner PostToolUse Auto-run tests Bash

Logging & Auditing

bash-command-logger

Purpose: Log all bash commands for compliance and debugging

Event: PreToolUse Matcher: Bash Language: Bash + jq

Configuration (~/.claude/settings.json for user-level):

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '\"\(.tool_input.command) - \(.tool_input.description // \"No description\")\"' >> ~/.claude/bash-command-log.txt"
          }
        ]
      }
    ]
  }
}

Features:

  • Logs command and description
  • Timestamps automatically (file modification time)
  • Non-blocking (exit 0)
  • Low overhead

Dependencies: jq

Install:

*install-example bash-command-logger

file-change-tracker

Purpose: Track all file modifications with timestamps

Event: PostToolUse Matcher: Edit|Write Language: Python

Hook Script (hooks/file-change-tracker.py):

#!/usr/bin/env python3
import json
import sys
from datetime import datetime

data = json.load(sys.stdin)
file_path = data.get('tool_input', {}).get('file_path', 'unknown')

timestamp = datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
with open('.file-changes.log', 'a') as f:
    f.write(f"{timestamp} | MODIFIED | {file_path}\n")

print(f"✅ Tracked change: {file_path}")

Configuration (plugin hooks.json):

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "python ${CLAUDE_PLUGIN_ROOT}/hooks/file-change-tracker.py"
          }
        ]
      }
    ]
  }
}

workflow-auditor

Purpose: Comprehensive workflow event logging

Event: Multiple (PreToolUse, PostToolUse, Stop) Matcher: * Language: Python

Features:

  • Logs all tool usage
  • Captures exit codes
  • Records execution time
  • Creates structured audit trail

Configuration:

{
  "event": "PostToolUse",
  "matcher": "*",
  "command": "python hooks/workflow-auditor.py"
}

Validation & Safety

file-protection

Purpose: Block edits to sensitive files (.env, package-lock.json, .git/)

Event: PreToolUse Matcher: Edit|Write Language: Python

Hook Script (hooks/file-protection.py):

#!/usr/bin/env python3
import json
import sys
from pathlib import Path

data = json.load(sys.stdin)
file_path = data.get('tool_input', {}).get('file_path', '')

# Protected patterns
protected = [
    '.env',
    'package-lock.json',
    'yarn.lock',
    '.git/',
    'secrets.json',
    'credentials'
]

for pattern in protected:
    if pattern in file_path:
        print(f"❌ ERROR: Cannot edit protected file: {file_path}", file=sys.stderr)
        print(f"   Pattern matched: {pattern}", file=sys.stderr)
        print(f"   Protected files cannot be modified by AI", file=sys.stderr)
        sys.exit(2)  # Block operation

sys.exit(0)  # Allow operation

Configuration:

{
  "event": "PreToolUse",
  "matcher": "Edit|Write",
  "command": "python hooks/file-protection.py"
}

Customization: Edit protected list to add/remove patterns


git-safety-guard

Purpose: Prevent dangerous git operations (force push, hard reset)

Event: PreToolUse Matcher: Bash Language: Python

Hook Script (hooks/git-safety-guard.py):

#!/usr/bin/env python3
import json
import sys
import re

data = json.load(sys.stdin)
command = data.get('tool_input', {}).get('command', '')

# Dangerous git patterns
dangerous = [
    (r'git\s+push.*--force', 'Force push'),
    (r'git\s+reset.*--hard', 'Hard reset'),
    (r'git\s+clean.*-[dfx]', 'Git clean'),
    (r'rm\s+-rf\s+\.git', 'Delete .git'),
    (r'git\s+rebase.*-i.*main', 'Rebase main branch')
]

for pattern, name in dangerous:
    if re.search(pattern, command, re.IGNORECASE):
        print(f"❌ ERROR: Dangerous git operation blocked: {name}", file=sys.stderr)
        print(f"   Command: {command}", file=sys.stderr)
        print(f"   Reason: High risk of data loss", file=sys.stderr)
        print(f"   Override: Run manually if absolutely necessary", file=sys.stderr)
        sys.exit(2)  # Block

sys.exit(0)  # Allow

Configuration:

{
  "event": "PreToolUse",
  "matcher": "Bash",
  "command": "python hooks/git-safety-guard.py"
}

syntax-validator

Purpose: Validate code syntax before saving

Event: PreToolUse Matcher: Edit|Write Language: Python

Features:

  • Checks Python syntax with ast.parse()
  • Validates JSON with json.loads()
  • Checks YAML with yaml.safe_load()
  • Blocks on syntax errors

Configuration:

{
  "event": "PreToolUse",
  "matcher": "Edit|Write",
  "command": "python hooks/syntax-validator.py"
}

Automation

auto-formatter

Purpose: Automatically format code on save

Event: PostToolUse Matcher: Edit|Write Language: Bash

Hook Script (hooks/auto-formatter.sh):

#!/bin/bash
set -euo pipefail

INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path')

# Format based on file extension
if [[ "$FILE_PATH" =~ \.ts$ ]] || [[ "$FILE_PATH" =~ \.js$ ]]; then
  prettier --write "$FILE_PATH" 2>/dev/null
  echo "✅ Formatted TypeScript/JavaScript: $FILE_PATH"
elif [[ "$FILE_PATH" =~ \.py$ ]]; then
  black "$FILE_PATH" 2>/dev/null
  echo "✅ Formatted Python: $FILE_PATH"
elif [[ "$FILE_PATH" =~ \.go$ ]]; then
  gofmt -w "$FILE_PATH" 2>/dev/null
  echo "✅ Formatted Go: $FILE_PATH"
fi

exit 0

Configuration:

{
  "event": "PostToolUse",
  "matcher": "Edit|Write",
  "command": "bash hooks/auto-formatter.sh"
}

Dependencies: prettier, black, gofmt (based on languages used)


test-runner

Purpose: Automatically run tests when code changes

Event: PostToolUse Matcher: Edit|Write Language: Bash

Hook Script (hooks/test-runner.sh):

#!/bin/bash
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path')

# Only run for source files
if [[ ! "$FILE_PATH" =~ \.(ts|js|py|go)$ ]]; then
  exit 0
fi

echo "🧪 Running tests for: $FILE_PATH"

# Run tests based on project type
if [ -f "package.json" ]; then
  npm test -- "$FILE_PATH" 2>&1 | tail -20
elif [ -f "pytest.ini" ] || [ -f "setup.py" ]; then
  pytest "$FILE_PATH" 2>&1 | tail -20
elif [ -f "go.mod" ]; then
  go test ./... 2>&1 | tail -20
fi

if [ ${PIPESTATUS[0]} -ne 0 ]; then
  echo "⚠️  Tests failed - review output above"
else
  echo "✅ Tests passed"
fi

exit 0  # Don't block even if tests fail

Configuration:

{
  "event": "PostToolUse",
  "matcher": "Edit|Write",
  "command": "bash hooks/test-runner.sh"
}

auto-commit

Purpose: Create automatic backup commits

Event: PostToolUse Matcher: Edit|Write Language: Bash

Hook Script (hooks/auto-commit.sh):

#!/bin/bash
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path')

# Create backup commit
git add "$FILE_PATH" 2>/dev/null
git commit -m "Auto-backup: $FILE_PATH [Claude Code]" 2>/dev/null

if [ $? -eq 0 ]; then
  echo "💾 Auto-commit created for: $FILE_PATH"
else
  echo "  No changes to commit"
fi

exit 0

Configuration:

{
  "event": "PostToolUse",
  "matcher": "Edit|Write",
  "command": "bash hooks/auto-commit.sh"
}

⚠️ Warning: Creates many commits! Consider using only during development.


Notifications

desktop-notifier

Purpose: Send desktop notifications when Claude needs input

Event: Stop Matcher: * Language: Bash

Hook Script (hooks/desktop-notifier.sh):

#!/bin/bash
# macOS
command -v osascript >/dev/null && osascript -e 'display notification "Claude Code awaiting input" with title "Claude Code"'

# Linux
command -v notify-send >/dev/null && notify-send "Claude Code" "Awaiting your input"

# Windows (requires BurntToast PowerShell module)
command -v powershell.exe >/dev/null && powershell.exe -Command "New-BurntToastNotification -Text 'Claude Code', 'Awaiting your input'"

exit 0

Configuration:

{
  "event": "Stop",
  "matcher": "*",
  "command": "bash hooks/desktop-notifier.sh"
}

Dependencies:

  • macOS: Built-in osascript
  • Linux: notify-send (libnotify)
  • Windows: BurntToast PowerShell module

slack-integration

Purpose: Send updates to Slack when tasks complete

Event: Stop Matcher: * Language: Python

Hook Script (hooks/slack-notifier.py):

#!/usr/bin/env python3
import json
import sys
import os
import requests

SLACK_WEBHOOK_URL = os.environ.get('SLACK_WEBHOOK_URL')

if not SLACK_WEBHOOK_URL:
    sys.exit(0)  # Silently skip if not configured

data = json.load(sys.stdin)

message = {
    "text": "Claude Code task completed",
    "blocks": [
        {
            "type": "section",
            "text": {
                "type": "mrkdwn",
                "text": "✅ *Claude Code Task Completed*\nReady for your review"
            }
        }
    ]
}

try:
    response = requests.post(SLACK_WEBHOOK_URL, json=message, timeout=5)
    if response.status_code == 200:
        print("✅ Slack notification sent")
except Exception as e:
    print(f"⚠️  Slack notification failed: {e}", file=sys.stderr)

sys.exit(0)

Configuration:

{
  "event": "Stop",
  "matcher": "*",
  "command": "python hooks/slack-notifier.py"
}

Setup:

  1. Create Slack webhook: https://api.slack.com/messaging/webhooks
  2. Set environment variable: export SLACK_WEBHOOK_URL=https://hooks.slack.com/services/...

Dependencies: requests library


completion-notifier

Purpose: Play sound when Claude finishes

Event: Stop Matcher: * Language: Bash

Hook Script (hooks/completion-notifier.sh):

#!/bin/bash
# macOS
command -v afplay >/dev/null && afplay /System/Library/Sounds/Glass.aiff

# Linux
command -v paplay >/dev/null && paplay /usr/share/sounds/freedesktop/stereo/complete.oga

# Cross-platform with ffplay (if installed)
command -v ffplay >/dev/null && ffplay -nodisp -autoexit /path/to/notification.mp3

exit 0

Configuration:

{
  "event": "Stop",
  "matcher": "*",
  "command": "bash hooks/completion-notifier.sh"
}

PRISM-Specific

story-context-enforcer

Purpose: Ensure PRISM workflow commands have active story context

Event: PreToolUse Matcher: Bash Language: Python

Hook Script: See hooks/enforce-story-context.py in PRISM plugin

Configuration:

{
  "event": "PreToolUse",
  "matcher": "Bash",
  "command": "python hooks/enforce-story-context.py"
}

Blocks commands: *develop-story, *review, *risk, *design, etc.

Required: .prism-current-story.txt file with active story path


workflow-tracker

Purpose: Track PRISM workflow progress and log events

Event: PostToolUse Matcher: Write Language: Python

Hook Script: See hooks/track-current-story.py in PRISM plugin

Configuration:

{
  "event": "PostToolUse",
  "matcher": "Write",
  "command": "python hooks/track-current-story.py"
}

Creates:

  • .prism-current-story.txt (active story)
  • .prism-workflow.log (audit trail)

Installation

Quick Install

All examples can be installed with:

*install-example [example-name]

Manual Installation

  1. Copy hook script to hooks/ directory
  2. Make executable: chmod +x hooks/script.sh
  3. Add configuration to .claude/settings.json
  4. Test: *test-hook [hook-name]

Customization

All examples can be customized by:

  1. Editing hook scripts directly
  2. Modifying patterns/thresholds
  3. Adding additional logic
  4. Changing matchers
  5. Combining multiple hooks

Dependencies Summary

Example Dependencies Installation
bash-command-logger jq brew install jq
file-protection Python 3 Built-in
auto-formatter prettier, black, gofmt Via package managers
test-runner npm, pytest, go Project-specific
desktop-notifier OS-specific Built-in or system package
slack-integration requests pip install requests
git-safety-guard Python 3 Built-in

Contributing

Want to add your own example?

  1. Create hook script with clear documentation
  2. Test thoroughly in safe environment
  3. Security review (no credentials, safe operations)
  4. Submit via *export-hooks and share

Version: 1.0.0 Last Updated: 2025-10-24 Total Examples: 13