diff options
| author | sillylaird <sillyfanboy@gmail.com> | 2026-09-03 00:33:59 +0000 |
|---|---|---|
| committer | sillylaird <sillyfanboy@gmail.com> | 2026-09-03 00:33:59 +0000 |
| commit | 898b52edcb47bcb3e9d6106e74ca73e74ea01e70 (patch) | |
| tree | 85c6ee5ad58b860144551184d4cf86b560c62b91 /.agents/skills/hook-development/references | |
| download | www-main.tar.gz www-main.zip | |
Diffstat (limited to '.agents/skills/hook-development/references')
| -rw-r--r-- | .agents/skills/hook-development/references/advanced.md | 479 | ||||
| -rw-r--r-- | .agents/skills/hook-development/references/migration.md | 369 | ||||
| -rw-r--r-- | .agents/skills/hook-development/references/patterns.md | 346 |
3 files changed, 1194 insertions, 0 deletions
diff --git a/.agents/skills/hook-development/references/advanced.md b/.agents/skills/hook-development/references/advanced.md new file mode 100644 index 0000000..a84a38f --- /dev/null +++ b/.agents/skills/hook-development/references/advanced.md @@ -0,0 +1,479 @@ +# Advanced Hook Use Cases + +This reference covers advanced hook patterns and techniques for sophisticated automation workflows. + +## Multi-Stage Validation + +Combine command and prompt hooks for layered validation: + +```json +{ + "PreToolUse": [ + { + "matcher": "Bash", + "hooks": [ + { + "type": "command", + "command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/quick-check.sh", + "timeout": 5 + }, + { + "type": "prompt", + "prompt": "Deep analysis of bash command: $TOOL_INPUT", + "timeout": 15 + } + ] + } + ] +} +``` + +**Use case:** Fast deterministic checks followed by intelligent analysis + +**Example quick-check.sh:** +```bash +#!/bin/bash +input=$(cat) +command=$(echo "$input" | jq -r '.tool_input.command') + +# Immediate approval for safe commands +if [[ "$command" =~ ^(ls|pwd|echo|date|whoami)$ ]]; then + exit 0 +fi + +# Let prompt hook handle complex cases +exit 0 +``` + +The command hook quickly approves obviously safe commands, while the prompt hook analyzes everything else. + +## Conditional Hook Execution + +Execute hooks based on environment or context: + +```bash +#!/bin/bash +# Only run in CI environment +if [ -z "$CI" ]; then + echo '{"continue": true}' # Skip in non-CI + exit 0 +fi + +# Run validation logic in CI +input=$(cat) +# ... validation code ... +``` + +**Use cases:** +- Different behavior in CI vs local development +- Project-specific validation +- User-specific rules + +**Example: Skip certain checks for trusted users:** +```bash +#!/bin/bash +# Skip detailed checks for admin users +if [ "$USER" = "admin" ]; then + exit 0 +fi + +# Full validation for other users +input=$(cat) +# ... validation code ... +``` + +## Hook Chaining via State + +Share state between hooks using temporary files: + +```bash +# Hook 1: Analyze and save state +#!/bin/bash +input=$(cat) +command=$(echo "$input" | jq -r '.tool_input.command') + +# Analyze command +risk_level=$(calculate_risk "$command") +echo "$risk_level" > /tmp/hook-state-$$ + +exit 0 +``` + +```bash +# Hook 2: Use saved state +#!/bin/bash +risk_level=$(cat /tmp/hook-state-$$ 2>/dev/null || echo "unknown") + +if [ "$risk_level" = "high" ]; then + echo "High risk operation detected" >&2 + exit 2 +fi +``` + +**Important:** This only works for sequential hook events (e.g., PreToolUse then PostToolUse), not parallel hooks. + +## Dynamic Hook Configuration + +Modify hook behavior based on project configuration: + +```bash +#!/bin/bash +cd "$CLAUDE_PROJECT_DIR" || exit 1 + +# Read project-specific config +if [ -f ".claude-hooks-config.json" ]; then + strict_mode=$(jq -r '.strict_mode' .claude-hooks-config.json) + + if [ "$strict_mode" = "true" ]; then + # Apply strict validation + # ... + else + # Apply lenient validation + # ... + fi +fi +``` + +**Example .claude-hooks-config.json:** +```json +{ + "strict_mode": true, + "allowed_commands": ["ls", "pwd", "grep"], + "forbidden_paths": ["/etc", "/sys"] +} +``` + +## Context-Aware Prompt Hooks + +Use transcript and session context for intelligent decisions: + +```json +{ + "Stop": [ + { + "matcher": "*", + "hooks": [ + { + "type": "prompt", + "prompt": "Review the full transcript at $TRANSCRIPT_PATH. Check: 1) Were tests run after code changes? 2) Did the build succeed? 3) Were all user questions answered? 4) Is there any unfinished work? Return 'approve' only if everything is complete." + } + ] + } + ] +} +``` + +The LLM can read the transcript file and make context-aware decisions. + +## Performance Optimization + +### Caching Validation Results + +```bash +#!/bin/bash +input=$(cat) +file_path=$(echo "$input" | jq -r '.tool_input.file_path') +cache_key=$(echo -n "$file_path" | md5sum | cut -d' ' -f1) +cache_file="/tmp/hook-cache-$cache_key" + +# Check cache +if [ -f "$cache_file" ]; then + cache_age=$(($(date +%s) - $(stat -f%m "$cache_file" 2>/dev/null || stat -c%Y "$cache_file"))) + if [ "$cache_age" -lt 300 ]; then # 5 minute cache + cat "$cache_file" + exit 0 + fi +fi + +# Perform validation +result='{"decision": "approve"}' + +# Cache result +echo "$result" > "$cache_file" +echo "$result" +``` + +### Parallel Execution Optimization + +Since hooks run in parallel, design them to be independent: + +```json +{ + "PreToolUse": [ + { + "matcher": "Write", + "hooks": [ + { + "type": "command", + "command": "bash check-size.sh", // Independent + "timeout": 2 + }, + { + "type": "command", + "command": "bash check-path.sh", // Independent + "timeout": 2 + }, + { + "type": "prompt", + "prompt": "Check content safety", // Independent + "timeout": 10 + } + ] + } + ] +} +``` + +All three hooks run simultaneously, reducing total latency. + +## Cross-Event Workflows + +Coordinate hooks across different events: + +**SessionStart - Set up tracking:** +```bash +#!/bin/bash +# Initialize session tracking +echo "0" > /tmp/test-count-$$ +echo "0" > /tmp/build-count-$$ +``` + +**PostToolUse - Track events:** +```bash +#!/bin/bash +input=$(cat) +tool_name=$(echo "$input" | jq -r '.tool_name') + +if [ "$tool_name" = "Bash" ]; then + command=$(echo "$input" | jq -r '.tool_result') + if [[ "$command" == *"test"* ]]; then + count=$(cat /tmp/test-count-$$ 2>/dev/null || echo "0") + echo $((count + 1)) > /tmp/test-count-$$ + fi +fi +``` + +**Stop - Verify based on tracking:** +```bash +#!/bin/bash +test_count=$(cat /tmp/test-count-$$ 2>/dev/null || echo "0") + +if [ "$test_count" -eq 0 ]; then + echo '{"decision": "block", "reason": "No tests were run"}' >&2 + exit 2 +fi +``` + +## Integration with External Systems + +### Slack Notifications + +```bash +#!/bin/bash +input=$(cat) +tool_name=$(echo "$input" | jq -r '.tool_name') +decision="blocked" + +# Send notification to Slack +curl -X POST "$SLACK_WEBHOOK" \ + -H 'Content-Type: application/json' \ + -d "{\"text\": \"Hook ${decision} ${tool_name} operation\"}" \ + 2>/dev/null + +echo '{"decision": "deny"}' >&2 +exit 2 +``` + +### Database Logging + +```bash +#!/bin/bash +input=$(cat) + +# Log to database +psql "$DATABASE_URL" -c "INSERT INTO hook_logs (event, data) VALUES ('PreToolUse', '$input')" \ + 2>/dev/null + +exit 0 +``` + +### Metrics Collection + +```bash +#!/bin/bash +input=$(cat) +tool_name=$(echo "$input" | jq -r '.tool_name') + +# Send metrics to monitoring system +echo "hook.pretooluse.${tool_name}:1|c" | nc -u -w1 statsd.local 8125 + +exit 0 +``` + +## Security Patterns + +### Rate Limiting + +```bash +#!/bin/bash +input=$(cat) +command=$(echo "$input" | jq -r '.tool_input.command') + +# Track command frequency +rate_file="/tmp/hook-rate-$$" +current_minute=$(date +%Y%m%d%H%M) + +if [ -f "$rate_file" ]; then + last_minute=$(head -1 "$rate_file") + count=$(tail -1 "$rate_file") + + if [ "$current_minute" = "$last_minute" ]; then + if [ "$count" -gt 10 ]; then + echo '{"decision": "deny", "reason": "Rate limit exceeded"}' >&2 + exit 2 + fi + count=$((count + 1)) + else + count=1 + fi +else + count=1 +fi + +echo "$current_minute" > "$rate_file" +echo "$count" >> "$rate_file" + +exit 0 +``` + +### Audit Logging + +```bash +#!/bin/bash +input=$(cat) +tool_name=$(echo "$input" | jq -r '.tool_name') +timestamp=$(date -Iseconds) + +# Append to audit log +echo "$timestamp | $USER | $tool_name | $input" >> ~/.claude/audit.log + +exit 0 +``` + +### Secret Detection + +```bash +#!/bin/bash +input=$(cat) +content=$(echo "$input" | jq -r '.tool_input.content') + +# Check for common secret patterns +if echo "$content" | grep -qE "(api[_-]?key|password|secret|token).{0,20}['\"]?[A-Za-z0-9]{20,}"; then + echo '{"decision": "deny", "reason": "Potential secret detected in content"}' >&2 + exit 2 +fi + +exit 0 +``` + +## Testing Advanced Hooks + +### Unit Testing Hook Scripts + +```bash +# test-hook.sh +#!/bin/bash + +# Test 1: Approve safe command +result=$(echo '{"tool_input": {"command": "ls"}}' | bash validate-bash.sh) +if [ $? -eq 0 ]; then + echo "✓ Test 1 passed" +else + echo "✗ Test 1 failed" +fi + +# Test 2: Block dangerous command +result=$(echo '{"tool_input": {"command": "rm -rf /"}}' | bash validate-bash.sh) +if [ $? -eq 2 ]; then + echo "✓ Test 2 passed" +else + echo "✗ Test 2 failed" +fi +``` + +### Integration Testing + +Create test scenarios that exercise the full hook workflow: + +```bash +# integration-test.sh +#!/bin/bash + +# Set up test environment +export CLAUDE_PROJECT_DIR="/tmp/test-project" +export CLAUDE_PLUGIN_ROOT="$(pwd)" +mkdir -p "$CLAUDE_PROJECT_DIR" + +# Test SessionStart hook +echo '{}' | bash hooks/session-start.sh +if [ -f "/tmp/session-initialized" ]; then + echo "✓ SessionStart hook works" +else + echo "✗ SessionStart hook failed" +fi + +# Clean up +rm -rf "$CLAUDE_PROJECT_DIR" +``` + +## Best Practices for Advanced Hooks + +1. **Keep hooks independent**: Don't rely on execution order +2. **Use timeouts**: Set appropriate limits for each hook type +3. **Handle errors gracefully**: Provide clear error messages +4. **Document complexity**: Explain advanced patterns in README +5. **Test thoroughly**: Cover edge cases and failure modes +6. **Monitor performance**: Track hook execution time +7. **Version configuration**: Use version control for hook configs +8. **Provide escape hatches**: Allow users to bypass hooks when needed + +## Common Pitfalls + +### ❌ Assuming Hook Order + +```bash +# BAD: Assumes hooks run in specific order +# Hook 1 saves state, Hook 2 reads it +# This can fail because hooks run in parallel! +``` + +### ❌ Long-Running Hooks + +```bash +# BAD: Hook takes 2 minutes to run +sleep 120 +# This will timeout and block the workflow +``` + +### ❌ Uncaught Exceptions + +```bash +# BAD: Script crashes on unexpected input +file_path=$(echo "$input" | jq -r '.tool_input.file_path') +cat "$file_path" # Fails if file doesn't exist +``` + +### ✅ Proper Error Handling + +```bash +# GOOD: Handles errors gracefully +file_path=$(echo "$input" | jq -r '.tool_input.file_path') +if [ ! -f "$file_path" ]; then + echo '{"continue": true, "systemMessage": "File not found, skipping check"}' >&2 + exit 0 +fi +``` + +## Conclusion + +Advanced hook patterns enable sophisticated automation while maintaining reliability and performance. Use these techniques when basic hooks are insufficient, but always prioritize simplicity and maintainability. diff --git a/.agents/skills/hook-development/references/migration.md b/.agents/skills/hook-development/references/migration.md new file mode 100644 index 0000000..587cae3 --- /dev/null +++ b/.agents/skills/hook-development/references/migration.md @@ -0,0 +1,369 @@ +# Migrating from Basic to Advanced Hooks + +This guide shows how to migrate from basic command hooks to advanced prompt-based hooks for better maintainability and flexibility. + +## Why Migrate? + +Prompt-based hooks offer several advantages: + +- **Natural language reasoning**: LLM understands context and intent +- **Better edge case handling**: Adapts to unexpected scenarios +- **No bash scripting required**: Simpler to write and maintain +- **More flexible validation**: Can handle complex logic without coding + +## Migration Example: Bash Command Validation + +### Before (Basic Command Hook) + +**Configuration:** +```json +{ + "PreToolUse": [ + { + "matcher": "Bash", + "hooks": [ + { + "type": "command", + "command": "bash validate-bash.sh" + } + ] + } + ] +} +``` + +**Script (validate-bash.sh):** +```bash +#!/bin/bash +input=$(cat) +command=$(echo "$input" | jq -r '.tool_input.command') + +# Hard-coded validation logic +if [[ "$command" == *"rm -rf"* ]]; then + echo "Dangerous command detected" >&2 + exit 2 +fi +``` + +**Problems:** +- Only checks for exact "rm -rf" pattern +- Doesn't catch variations like `rm -fr` or `rm -r -f` +- Misses other dangerous commands (`dd`, `mkfs`, etc.) +- No context awareness +- Requires bash scripting knowledge + +### After (Advanced Prompt Hook) + +**Configuration:** +```json +{ + "PreToolUse": [ + { + "matcher": "Bash", + "hooks": [ + { + "type": "prompt", + "prompt": "Command: $TOOL_INPUT.command. Analyze for: 1) Destructive operations (rm -rf, dd, mkfs, etc) 2) Privilege escalation (sudo) 3) Network operations without user consent. Return 'approve' or 'deny' with explanation.", + "timeout": 15 + } + ] + } + ] +} +``` + +**Benefits:** +- Catches all variations and patterns +- Understands intent, not just literal strings +- No script file needed +- Easy to extend with new criteria +- Context-aware decisions +- Natural language explanation in denial + +## Migration Example: File Write Validation + +### Before (Basic Command Hook) + +**Configuration:** +```json +{ + "PreToolUse": [ + { + "matcher": "Write", + "hooks": [ + { + "type": "command", + "command": "bash validate-write.sh" + } + ] + } + ] +} +``` + +**Script (validate-write.sh):** +```bash +#!/bin/bash +input=$(cat) +file_path=$(echo "$input" | jq -r '.tool_input.file_path') + +# Check for path traversal +if [[ "$file_path" == *".."* ]]; then + echo '{"decision": "deny", "reason": "Path traversal detected"}' >&2 + exit 2 +fi + +# Check for system paths +if [[ "$file_path" == "/etc/"* ]] || [[ "$file_path" == "/sys/"* ]]; then + echo '{"decision": "deny", "reason": "System file"}' >&2 + exit 2 +fi +``` + +**Problems:** +- Hard-coded path patterns +- Doesn't understand symlinks +- Missing edge cases (e.g., `/etc` vs `/etc/`) +- No consideration of file content + +### After (Advanced Prompt Hook) + +**Configuration:** +```json +{ + "PreToolUse": [ + { + "matcher": "Write|Edit", + "hooks": [ + { + "type": "prompt", + "prompt": "File path: $TOOL_INPUT.file_path. Content preview: $TOOL_INPUT.content (first 200 chars). Verify: 1) Not system directories (/etc, /sys, /usr) 2) Not credentials (.env, tokens, secrets) 3) No path traversal 4) Content doesn't expose secrets. Return 'approve' or 'deny'." + } + ] + } + ] +} +``` + +**Benefits:** +- Context-aware (considers content too) +- Handles symlinks and edge cases +- Natural understanding of "system directories" +- Can detect secrets in content +- Easy to extend criteria + +## When to Keep Command Hooks + +Command hooks still have their place: + +### 1. Deterministic Performance Checks + +```bash +#!/bin/bash +# Check file size quickly +file_path=$(echo "$input" | jq -r '.tool_input.file_path') +size=$(stat -f%z "$file_path" 2>/dev/null || stat -c%s "$file_path" 2>/dev/null) + +if [ "$size" -gt 10000000 ]; then + echo '{"decision": "deny", "reason": "File too large"}' >&2 + exit 2 +fi +``` + +**Use command hooks when:** Validation is purely mathematical or deterministic. + +### 2. External Tool Integration + +```bash +#!/bin/bash +# Run security scanner +file_path=$(echo "$input" | jq -r '.tool_input.file_path') +scan_result=$(security-scanner "$file_path") + +if [ "$?" -ne 0 ]; then + echo "Security scan failed: $scan_result" >&2 + exit 2 +fi +``` + +**Use command hooks when:** Integrating with external tools that provide yes/no answers. + +### 3. Very Fast Checks (< 50ms) + +```bash +#!/bin/bash +# Quick regex check +command=$(echo "$input" | jq -r '.tool_input.command') + +if [[ "$command" =~ ^(ls|pwd|echo)$ ]]; then + exit 0 # Safe commands +fi +``` + +**Use command hooks when:** Performance is critical and logic is simple. + +## Hybrid Approach + +Combine both for multi-stage validation: + +```json +{ + "PreToolUse": [ + { + "matcher": "Bash", + "hooks": [ + { + "type": "command", + "command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/quick-check.sh", + "timeout": 5 + }, + { + "type": "prompt", + "prompt": "Deep analysis of bash command: $TOOL_INPUT", + "timeout": 15 + } + ] + } + ] +} +``` + +The command hook does fast deterministic checks, while the prompt hook handles complex reasoning. + +## Migration Checklist + +When migrating hooks: + +- [ ] Identify the validation logic in the command hook +- [ ] Convert hard-coded patterns to natural language criteria +- [ ] Test with edge cases the old hook missed +- [ ] Verify LLM understands the intent +- [ ] Set appropriate timeout (usually 15-30s for prompt hooks) +- [ ] Document the new hook in README +- [ ] Remove or archive old script files + +## Migration Tips + +1. **Start with one hook**: Don't migrate everything at once +2. **Test thoroughly**: Verify prompt hook catches what command hook caught +3. **Look for improvements**: Use migration as opportunity to enhance validation +4. **Keep scripts for reference**: Archive old scripts in case you need to reference the logic +5. **Document reasoning**: Explain why prompt hook is better in README + +## Complete Migration Example + +### Original Plugin Structure + +``` +my-plugin/ +├── .claude-plugin/plugin.json +├── hooks/hooks.json +└── scripts/ + ├── validate-bash.sh + ├── validate-write.sh + └── check-tests.sh +``` + +### After Migration + +``` +my-plugin/ +├── .claude-plugin/plugin.json +├── hooks/hooks.json # Now uses prompt hooks +└── scripts/ # Archive or delete + └── archive/ + ├── validate-bash.sh + ├── validate-write.sh + └── check-tests.sh +``` + +### Updated hooks.json + +```json +{ + "PreToolUse": [ + { + "matcher": "Bash", + "hooks": [ + { + "type": "prompt", + "prompt": "Validate bash command safety: destructive ops, privilege escalation, network access" + } + ] + }, + { + "matcher": "Write|Edit", + "hooks": [ + { + "type": "prompt", + "prompt": "Validate file write safety: system paths, credentials, path traversal, content secrets" + } + ] + } + ], + "Stop": [ + { + "matcher": "*", + "hooks": [ + { + "type": "prompt", + "prompt": "Verify tests were run if code was modified" + } + ] + } + ] +} +``` + +**Result:** Simpler, more maintainable, more powerful. + +## Common Migration Patterns + +### Pattern: String Contains → Natural Language + +**Before:** +```bash +if [[ "$command" == *"sudo"* ]]; then + echo "Privilege escalation" >&2 + exit 2 +fi +``` + +**After:** +``` +"Check for privilege escalation (sudo, su, etc)" +``` + +### Pattern: Regex → Intent + +**Before:** +```bash +if [[ "$file" =~ \.(env|secret|key|token)$ ]]; then + echo "Credential file" >&2 + exit 2 +fi +``` + +**After:** +``` +"Verify not writing to credential files (.env, secrets, keys, tokens)" +``` + +### Pattern: Multiple Conditions → Criteria List + +**Before:** +```bash +if [ condition1 ] || [ condition2 ] || [ condition3 ]; then + echo "Invalid" >&2 + exit 2 +fi +``` + +**After:** +``` +"Check: 1) condition1 2) condition2 3) condition3. Deny if any fail." +``` + +## Conclusion + +Migrating to prompt-based hooks makes plugins more maintainable, flexible, and powerful. Reserve command hooks for deterministic checks and external tool integration. diff --git a/.agents/skills/hook-development/references/patterns.md b/.agents/skills/hook-development/references/patterns.md new file mode 100644 index 0000000..4475386 --- /dev/null +++ b/.agents/skills/hook-development/references/patterns.md @@ -0,0 +1,346 @@ +# Common Hook Patterns + +This reference provides common, proven patterns for implementing Claude Code hooks. Use these patterns as starting points for typical hook use cases. + +## Pattern 1: Security Validation + +Block dangerous file writes using prompt-based hooks: + +```json +{ + "PreToolUse": [ + { + "matcher": "Write|Edit", + "hooks": [ + { + "type": "prompt", + "prompt": "File path: $TOOL_INPUT.file_path. Verify: 1) Not in /etc or system directories 2) Not .env or credentials 3) Path doesn't contain '..' traversal. Return 'approve' or 'deny'." + } + ] + } + ] +} +``` + +**Use for:** Preventing writes to sensitive files or system directories. + +## Pattern 2: Test Enforcement + +Ensure tests run before stopping: + +```json +{ + "Stop": [ + { + "matcher": "*", + "hooks": [ + { + "type": "prompt", + "prompt": "Review transcript. If code was modified (Write/Edit tools used), verify tests were executed. If no tests were run, block with reason 'Tests must be run after code changes'." + } + ] + } + ] +} +``` + +**Use for:** Enforcing quality standards and preventing incomplete work. + +## Pattern 3: Context Loading + +Load project-specific context at session start: + +```json +{ + "SessionStart": [ + { + "matcher": "*", + "hooks": [ + { + "type": "command", + "command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/load-context.sh" + } + ] + } + ] +} +``` + +**Example script (load-context.sh):** +```bash +#!/bin/bash +cd "$CLAUDE_PROJECT_DIR" || exit 1 + +# Detect project type +if [ -f "package.json" ]; then + echo "📦 Node.js project detected" + echo "export PROJECT_TYPE=nodejs" >> "$CLAUDE_ENV_FILE" +elif [ -f "Cargo.toml" ]; then + echo "🦀 Rust project detected" + echo "export PROJECT_TYPE=rust" >> "$CLAUDE_ENV_FILE" +fi +``` + +**Use for:** Automatically detecting and configuring project-specific settings. + +## Pattern 4: Notification Logging + +Log all notifications for audit or analysis: + +```json +{ + "Notification": [ + { + "matcher": "*", + "hooks": [ + { + "type": "command", + "command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/log-notification.sh" + } + ] + } + ] +} +``` + +**Use for:** Tracking user notifications or integration with external logging systems. + +## Pattern 5: MCP Tool Monitoring + +Monitor and validate MCP tool usage: + +```json +{ + "PreToolUse": [ + { + "matcher": "mcp__.*__delete.*", + "hooks": [ + { + "type": "prompt", + "prompt": "Deletion operation detected. Verify: Is this deletion intentional? Can it be undone? Are there backups? Return 'approve' only if safe." + } + ] + } + ] +} +``` + +**Use for:** Protecting against destructive MCP operations. + +## Pattern 6: Build Verification + +Ensure project builds after code changes: + +```json +{ + "Stop": [ + { + "matcher": "*", + "hooks": [ + { + "type": "prompt", + "prompt": "Check if code was modified. If Write/Edit tools were used, verify the project was built (npm run build, cargo build, etc). If not built, block and request build." + } + ] + } + ] +} +``` + +**Use for:** Catching build errors before committing or stopping work. + +## Pattern 7: Permission Confirmation + +Ask user before dangerous operations: + +```json +{ + "PreToolUse": [ + { + "matcher": "Bash", + "hooks": [ + { + "type": "prompt", + "prompt": "Command: $TOOL_INPUT.command. If command contains 'rm', 'delete', 'drop', or other destructive operations, return 'ask' to confirm with user. Otherwise 'approve'." + } + ] + } + ] +} +``` + +**Use for:** User confirmation on potentially destructive commands. + +## Pattern 8: Code Quality Checks + +Run linters or formatters on file edits: + +```json +{ + "PostToolUse": [ + { + "matcher": "Write|Edit", + "hooks": [ + { + "type": "command", + "command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/check-quality.sh" + } + ] + } + ] +} +``` + +**Example script (check-quality.sh):** +```bash +#!/bin/bash +input=$(cat) +file_path=$(echo "$input" | jq -r '.tool_input.file_path') + +# Run linter if applicable +if [[ "$file_path" == *.js ]] || [[ "$file_path" == *.ts ]]; then + npx eslint "$file_path" 2>&1 || true +fi +``` + +**Use for:** Automatic code quality enforcement. + +## Pattern Combinations + +Combine multiple patterns for comprehensive protection: + +```json +{ + "PreToolUse": [ + { + "matcher": "Write|Edit", + "hooks": [ + { + "type": "prompt", + "prompt": "Validate file write safety" + } + ] + }, + { + "matcher": "Bash", + "hooks": [ + { + "type": "prompt", + "prompt": "Validate bash command safety" + } + ] + } + ], + "Stop": [ + { + "matcher": "*", + "hooks": [ + { + "type": "prompt", + "prompt": "Verify tests run and build succeeded" + } + ] + } + ], + "SessionStart": [ + { + "matcher": "*", + "hooks": [ + { + "type": "command", + "command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/load-context.sh" + } + ] + } + ] +} +``` + +This provides multi-layered protection and automation. + +## Pattern 9: Temporarily Active Hooks + +Create hooks that only run when explicitly enabled via flag files: + +```bash +#!/bin/bash +# Hook only active when flag file exists +FLAG_FILE="$CLAUDE_PROJECT_DIR/.enable-security-scan" + +if [ ! -f "$FLAG_FILE" ]; then + # Quick exit when disabled + exit 0 +fi + +# Flag present, run validation +input=$(cat) +file_path=$(echo "$input" | jq -r '.tool_input.file_path') + +# Run security scan +security-scanner "$file_path" +``` + +**Activation:** +```bash +# Enable the hook +touch .enable-security-scan + +# Disable the hook +rm .enable-security-scan +``` + +**Use for:** +- Temporary debugging hooks +- Feature flags for development +- Project-specific validation that's opt-in +- Performance-intensive checks only when needed + +**Note:** Must restart Claude Code after creating/removing flag files for hooks to recognize changes. + +## Pattern 10: Configuration-Driven Hooks + +Use JSON configuration to control hook behavior: + +```bash +#!/bin/bash +CONFIG_FILE="$CLAUDE_PROJECT_DIR/.claude/my-plugin.local.json" + +# Read configuration +if [ -f "$CONFIG_FILE" ]; then + strict_mode=$(jq -r '.strictMode // false' "$CONFIG_FILE") + max_file_size=$(jq -r '.maxFileSize // 1000000' "$CONFIG_FILE") +else + # Defaults + strict_mode=false + max_file_size=1000000 +fi + +# Skip if not in strict mode +if [ "$strict_mode" != "true" ]; then + exit 0 +fi + +# Apply configured limits +input=$(cat) +file_size=$(echo "$input" | jq -r '.tool_input.content | length') + +if [ "$file_size" -gt "$max_file_size" ]; then + echo '{"decision": "deny", "reason": "File exceeds configured size limit"}' >&2 + exit 2 +fi +``` + +**Configuration file (.claude/my-plugin.local.json):** +```json +{ + "strictMode": true, + "maxFileSize": 500000, + "allowedPaths": ["/tmp", "/home/user/projects"] +} +``` + +**Use for:** +- User-configurable hook behavior +- Per-project settings +- Team-specific rules +- Dynamic validation criteria |
