14 KiB
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:
BurntToastPowerShell 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:
- Create Slack webhook: https://api.slack.com/messaging/webhooks
- 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
- Copy hook script to
hooks/directory - Make executable:
chmod +x hooks/script.sh - Add configuration to
.claude/settings.json - Test:
*test-hook [hook-name]
Customization
All examples can be customized by:
- Editing hook scripts directly
- Modifying patterns/thresholds
- Adding additional logic
- Changing matchers
- 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?
- Create hook script with clear documentation
- Test thoroughly in safe environment
- Security review (no credentials, safe operations)
- Submit via
*export-hooksand share
Version: 1.0.0 Last Updated: 2025-10-24 Total Examples: 13