claude-flow
Version:
Enterprise-grade AI agent orchestration with ruv-swarm integration (Alpha Release)
381 lines (332 loc) • 10.3 kB
JavaScript
/**
* Safe Hook Patterns - Templates for safe Claude Code hook configurations
*
* These patterns prevent infinite loops that could cost thousands of dollars
* by avoiding recursive hook execution when hooks call 'claude' commands.
*/
/**
* DANGEROUS PATTERN - DO NOT USE
* This creates an infinite loop that can cost thousands of dollars!
*/
export const DANGEROUS_PATTERN_EXAMPLE = {
name: "DANGEROUS: Stop hook calling claude command",
description: "❌ NEVER USE THIS - Creates infinite recursion loop",
pattern: {
"hooks": {
"Stop": [{
"matcher": "",
"hooks": [{
"type": "command",
"command": "claude -c -p \"Update all changes to history.md\""
}]
}]
}
},
problems: [
"🚨 Creates infinite loop: Stop → claude command → Stop → claude command...",
"💰 Can cost $3600+ per day by bypassing rate limits",
"🚫 Makes system unresponsive",
"⚡ No built-in protection in Claude Code"
]
};
/**
* SAFE PATTERN 1: Flag-based updates
* Set a flag instead of calling claude directly
*/
export const SAFE_FLAG_PATTERN = {
name: "Safe Pattern: Flag-based updates",
description: "✅ Set flag when update needed, run manually",
pattern: {
"hooks": {
"Stop": [{
"matcher": "",
"hooks": [{
"type": "command",
"command": "bash -c 'echo \"History update needed at $(date)\" > ~/.claude/needs_update && echo \"📝 History update flagged. Run: claude -c -p \\\"Update history.md\\\"\"'"
}]
}]
}
},
benefits: [
"✅ No recursion - just sets a flag",
"💰 Zero risk of infinite API calls",
"🔄 User controls when update runs",
"📝 Clear instructions for manual execution"
],
usage: [
"1. Hook sets flag when update is needed",
"2. User sees notification",
"3. User manually runs: claude -c -p \"Update history.md\"",
"4. Update runs once safely"
]
};
/**
* SAFE PATTERN 2: PostToolUse hooks instead of Stop hooks
* React to specific tool usage rather than session end
*/
export const SAFE_POST_TOOL_PATTERN = {
name: "Safe Pattern: PostToolUse hooks",
description: "✅ React to specific file operations instead of Stop events",
pattern: {
"hooks": {
"PostToolUse": [{
"matcher": "Write|Edit|MultiEdit",
"hooks": [{
"type": "command",
"command": "echo 'File modified: {file}' >> ~/.claude/modifications.log"
}]
}]
}
},
benefits: [
"✅ Only triggers on actual file changes",
"🎯 More precise than Stop hooks",
"📝 Logs specific modifications",
"🔄 No risk of Stop hook recursion"
],
usage: [
"1. Triggers only when files are written/edited",
"2. Logs the specific file that was modified",
"3. Can be used for change tracking",
"4. Safe to use with any logging command"
]
};
/**
* SAFE PATTERN 3: Conditional execution with skip-hooks
* Check for hook context before executing claude commands
*/
export const SAFE_CONDITIONAL_PATTERN = {
name: "Safe Pattern: Conditional execution with context check",
description: "✅ Check if running in hook context before calling claude",
pattern: {
"hooks": {
"Stop": [{
"matcher": "",
"hooks": [{
"type": "command",
"command": "bash -c 'if [ -z \"$CLAUDE_HOOK_CONTEXT\" ]; then claude -c -p \"Update history.md\" --skip-hooks; else echo \"Skipping update - in hook context\"; fi'"
}]
}]
}
},
benefits: [
"✅ Checks hook context before execution",
"🛡️ Uses --skip-hooks flag for safety",
"🔄 Prevents recursive hook calls",
"📊 Provides clear feedback"
],
usage: [
"1. Checks CLAUDE_HOOK_CONTEXT environment variable",
"2. Only runs claude if not in hook context",
"3. Uses --skip-hooks to prevent triggering more hooks",
"4. Shows clear message when skipping"
]
};
/**
* SAFE PATTERN 4: Batch processing with scheduled execution
* Accumulate changes and process them on a schedule
*/
export const SAFE_BATCH_PATTERN = {
name: "Safe Pattern: Batch processing with scheduled execution",
description: "✅ Accumulate changes and process them separately",
pattern: {
"hooks": {
"Stop": [{
"matcher": "",
"hooks": [{
"type": "command",
"command": "echo \"$(date): Session ended\" >> ~/.claude/session_log.txt"
}]
}]
}
},
additionalSetup: {
cronJob: "# Add to crontab (run every hour):\n# 0 * * * * /path/to/update-history.sh",
updateScript: `#!/bin/bash
# update-history.sh - Safe batch update script
LOCK_FILE="~/.claude/update.lock"
LOG_FILE="~/.claude/session_log.txt"
# Check if update is already running
if [ -f "$LOCK_FILE" ]; then
echo "Update already in progress"
exit 1
fi
# Create lock file
touch "$LOCK_FILE"
# Check if there are new sessions to process
if [ -f "$LOG_FILE" ] && [ -s "$LOG_FILE" ]; then
echo "Processing accumulated changes..."
claude -c -p "Update history.md with recent session data" --skip-hooks
# Archive the log
mv "$LOG_FILE" "~/.claude/session_log_$(date +%Y%m%d_%H%M%S).txt"
fi
# Remove lock file
rm "$LOCK_FILE"`
},
benefits: [
"✅ No risk of recursion",
"⏰ Scheduled processing prevents overload",
"🔒 Lock file prevents concurrent updates",
"📦 Batches multiple sessions efficiently"
]
};
/**
* SAFE PATTERN 5: Database/file-based queue system
* Use external queue for processing commands
*/
export const SAFE_QUEUE_PATTERN = {
name: "Safe Pattern: Queue-based command processing",
description: "✅ Queue commands for external processing",
pattern: {
"hooks": {
"Stop": [{
"matcher": "",
"hooks": [{
"type": "command",
"command": "echo '{\"command\": \"update-history\", \"timestamp\": \"'$(date -Iseconds)'\", \"session\": \"'$CLAUDE_SESSION_ID'\"}' >> ~/.claude/command_queue.jsonl"
}]
}]
}
},
processor: `#!/usr/bin/env python3
# queue-processor.py - Safe command queue processor
import json
import subprocess
import time
import os
from pathlib import Path
QUEUE_FILE = Path.home() / '.claude' / 'command_queue.jsonl'
PROCESSING_INTERVAL = 300 # 5 minutes
def process_queue():
if not QUEUE_FILE.exists():
return
# Read and clear queue atomically
with open(QUEUE_FILE, 'r') as f:
lines = f.readlines()
# Clear the queue
QUEUE_FILE.unlink()
# Process commands
for line in lines:
try:
cmd_data = json.loads(line.strip())
if cmd_data['command'] == 'update-history':
print(f"Processing history update for session {cmd_data['session']}")
subprocess.run([
'claude', '-c', '-p', 'Update history.md', '--skip-hooks'
], check=True)
time.sleep(2) # Rate limiting
except Exception as e:
print(f"Error processing command: {e}")
if __name__ == '__main__':
while True:
try:
process_queue()
time.sleep(PROCESSING_INTERVAL)
except KeyboardInterrupt:
break`,
benefits: [
"✅ Complete separation of hook and claude execution",
"⏰ Rate limited processing",
"🔄 Handles multiple queued commands",
"🛡️ No risk of infinite loops"
]
};
/**
* Get all safe patterns for documentation generation
*/
export const ALL_SAFE_PATTERNS = [
SAFE_FLAG_PATTERN,
SAFE_POST_TOOL_PATTERN,
SAFE_CONDITIONAL_PATTERN,
SAFE_BATCH_PATTERN,
SAFE_QUEUE_PATTERN
];
/**
* Generate safe hooks documentation
*/
export function generateSafeHooksGuide() {
return `# 🛡️ Safe Hook Patterns for Claude Code
⚠️ **CRITICAL WARNING**: Stop hooks that call 'claude' commands create infinite loops that can cost thousands of dollars per day!
## 🚨 DANGEROUS PATTERN (NEVER USE)
${DANGEROUS_PATTERN_EXAMPLE.description}
\`\`\`json
${JSON.stringify(DANGEROUS_PATTERN_EXAMPLE.pattern, null, 2)}
\`\`\`
**Problems:**
${DANGEROUS_PATTERN_EXAMPLE.problems.map(p => `- ${p}`).join('\n')}
---
## ✅ SAFE PATTERNS
${ALL_SAFE_PATTERNS.map(pattern => `
### ${pattern.name}
${pattern.description}
**Configuration:**
\`\`\`json
${JSON.stringify(pattern.pattern, null, 2)}
\`\`\`
**Benefits:**
${pattern.benefits.map(b => `- ${b}`).join('\n')}
${pattern.usage ? `**Usage:**
${pattern.usage.map((u, i) => `${i + 1}. ${u}`).join('\n')}` : ''}
${pattern.additionalSetup ? `**Additional Setup:**
${pattern.additionalSetup.cronJob ? `
**Cron Job:**
\`\`\`bash
${pattern.additionalSetup.cronJob}
\`\`\`
` : ''}
${pattern.additionalSetup.updateScript ? `
**Update Script:**
\`\`\`bash
${pattern.additionalSetup.updateScript}
\`\`\`
` : ''}` : ''}
${pattern.processor ? `
**Queue Processor:**
\`\`\`python
${pattern.processor}
\`\`\`
` : ''}
---
`).join('')}
## 🚀 Quick Migration Guide
### If you currently have this DANGEROUS pattern:
\`\`\`json
{
"hooks": {
"Stop": [{"hooks": [{"type": "command", "command": "claude -c -p 'Update history'"}]}]
}
}
\`\`\`
### Replace with this SAFE pattern:
\`\`\`json
{
"hooks": {
"Stop": [{"hooks": [{"type": "command", "command": "touch ~/.claude/needs_update && echo 'Run: claude -c -p \"Update history\"'"}]}]
}
}
\`\`\`
## 🛡️ Hook Safety Tools
Use claude-flow's built-in safety tools:
\`\`\`bash
# Check your configuration for dangerous patterns
claude-flow hook-safety validate
# Enable safe mode (skips all hooks)
claude-flow hook-safety safe-mode
# Check current safety status
claude-flow hook-safety status
# Reset circuit breakers if triggered
claude-flow hook-safety reset
\`\`\`
## 📚 Additional Resources
- Issue #166: https://github.com/ruvnet/claude-flow/issues/166
- Claude Code Hooks Documentation: https://docs.anthropic.com/en/docs/claude-code/hooks
- Reddit Discussion: https://www.reddit.com/r/ClaudeAI/comments/1ltvi6x/anyone_else_accidentally_create_an_infinite_loop/
---
**Remember**: When in doubt, use flag-based patterns or PostToolUse hooks instead of Stop hooks!
`;
}
export default {
DANGEROUS_PATTERN_EXAMPLE,
ALL_SAFE_PATTERNS,
generateSafeHooksGuide
};