# 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](#bash-command-logger) | PreToolUse | Log all bash commands | Bash + jq | | [file-protection](#file-protection) | PreToolUse | Block edits to sensitive files | Python | | [auto-formatter](#auto-formatter) | PostToolUse | Format code on save | Bash | | [story-context-enforcer](#story-context-enforcer) | PreToolUse | Ensure PRISM story context | Python | | [workflow-tracker](#workflow-tracker) | PostToolUse | Track workflow progress | Python | | [desktop-notifier](#desktop-notifier) | Stop | Desktop notifications | Bash | | [git-safety-guard](#git-safety-guard) | PreToolUse | Prevent dangerous git ops | Python | | [test-runner](#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): ```json { "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`): ```python #!/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): ```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**: ```json { "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`): ```python #!/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**: ```json { "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`): ```python #!/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**: ```json { "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**: ```json { "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`): ```bash #!/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**: ```json { "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`): ```bash #!/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**: ```json { "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`): ```bash #!/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**: ```json { "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`): ```bash #!/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**: ```json { "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`): ```python #!/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**: ```json { "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`): ```bash #!/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**: ```json { "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**: ```json { "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**: ```json { "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