#!/bin/bash # Ralph Auto-Trigger Hook - Enhanced with Background Task Spawning # Automatically starts Ralph CLI in background when needed # # Modes (via RALPH_AUTO_MODE environment variable): # "always" - Start Ralph for every request # "agents" - Only for agent requests (default) # "off" - Disable auto-trigger # # Background Execution: # - Ralph runs as background process (non-blocking) # - Claude Code continues immediately # - Ralph output logged to: ~/.claude/ralph-output.log # - Ralph PID tracked in: ~/.claude/ralph.pid set -euo pipefail # Configuration CLAUDE_DIR="$HOME/.claude" RALPH_STATE_FILE="$CLAUDE_DIR/ralph-loop.local.md" RALPH_PID_FILE="$CLAUDE_DIR/ralph.pid" RALPH_LOG_FILE="$CLAUDE_DIR/ralph-output.log" RALPH_LOCK_FILE="$CLAUDE_DIR/ralph.lock" # Read hook input from stdin HOOK_INPUT=$(cat) USER_PROMPT=$(echo "$HOOK_INPUT" | jq -r '.prompt // empty' 2>/dev/null || echo "") # Fallback: if no JSON input, use first argument if [[ -z "$USER_PROMPT" && $# -gt 0 ]]; then USER_PROMPT="$1" fi # Get Ralph mode (default: agents) RALPH_AUTO_MODE="${RALPH_AUTO_MODE:-agents}" RALPH_MAX_ITERATIONS="${RALPH_MAX_ITERATIONS:-50}" # Exit if auto-trigger is disabled if [[ "$RALPH_AUTO_MODE" == "off" ]]; then exit 0 fi # Check if Ralph is already running (via lock file) if [[ -f "$RALPH_LOCK_FILE" ]]; then # Check if process is still alive LOCK_PID=$(cat "$RALPH_LOCK_FILE" 2>/dev/null || echo "") if [[ -n "$LOCKPD" ]] && kill -0 "$LOCK_PID" 2>/dev/null; then # Ralph is already running, don't start another instance exit 0 else # Lock file exists but process is dead, clean up rm -f "$RALPH_LOCK_FILE" "$RALPH_PID_FILE" fi fi # Agent detection list (lowercase for matching) AGENTS=( "ai-engineer" "backend-architect" "devops-automator" "frontend-developer" "mobile-app-builder" "rapid-prototyper" "test-writer-fixer" "tiktok-strategist" "growth-hacker" "content-creator" "instagram-curator" "reddit-builder" "twitter-engager" "app-store-optimizer" "brand-guardian" "ui-designer" "ux-researcher" "visual-storyteller" "whimsy-injector" "ui-ux-pro-max" "feedback-synthesizer" "sprint-prioritizer" "trend-researcher" "experiment-tracker" "project-shipper" "studio-producer" "studio-coach" "analytics-reporter" "finance-tracker" "infrastructure-maintainer" "legal-compliance-checker" "support-responder" "api-tester" "performance-benchmarker" "test-results-analyzer" "tool-evaluator" "workflow-optimizer" "joker" "agent-updater" "explore" "plan" "general-purpose" ) # Detect agent request (case-insensitive) agent_detected=false detected_agent="" for agent in "${AGENTS[@]}"; do if echo "$USER_PROMPT" | grep -iq "$agent"; then agent_detected=true detected_agent="$agent" break fi done # Determine if we should start Ralph should_trigger=false case "$RALPH_AUTO_MODE" in "always") # Trigger on all prompts should_trigger=true ;; "agents") # Only trigger on agent requests OR development keywords if [[ "$agent_detected" == true ]]; then should_trigger=true elif echo "$USER_PROMPT" | grep -qiE "build|create|implement|develop|fix|add|refactor|optimize|write|generate|delegate|autonomous"; then should_trigger=true detected_agent="general-development" fi ;; esac if [[ "$should_trigger" == true ]]; then # Create Ralph state file mkdir -p "$CLAUDE_DIR" cat > "$RALPH_STATE_FILE" << EOF # Ralph Loop State - Auto-Triggered # Generated: $(date -u +"%Y-%m-%d %H:%M:%S UTC") **User Request:** $USER_PROMPT **Detected Agent:** $detected_agent **Mode:** $RALPH_AUTO_MODE **Max Iterations:** $RALPH_MAX_ITERATIONS **Timestamp:** $(date -Iseconds) ## Context This state file was automatically generated by the Ralph auto-trigger hook. Ralph CLI will read this file and autonomously execute the request. ## Auto-Trigger Details - Triggered by: Claude Code UserPromptSubmit hook - Trigger mode: $RALPH_AUTO_MODE - Background execution: Yes (non-blocking) - Log file: $RALPH_LOG_FILE ## Usage Ralph is running autonomously in the background. Monitor progress: \`\`\`bash # View Ralph output in real-time tail -f ~/.claude/ralph-output.log # Check if Ralph is still running ps aux | grep ralph # Stop Ralph manually kill \$(cat ~/.claude/ralph.pid) rm ~/.claude/ralph.lock \`\`\` EOF # Spawn Ralph in background (NON-BLOCKING) if command -v ralph &> /dev/null; then # Create log file touch "$RALPH_LOG_FILE" # Start Ralph in background with nohup (survives terminal close) echo "[$(date -u +"%Y-%m-%d %H:%M:%S UTC")] Starting Ralph in background..." >> "$RALPH_LOG_FILE" echo "Mode: $RALPH_AUTO_MODE" >> "$RALPH_LOG_FILE" echo "Agent: $detected_agent" >> "$RALPH_LOG_FILE" echo "Max iterations: $RALPH_MAX_ITERATIONS" >> "$RALPH_LOG_FILE" echo "---" >> "$RALPH_LOG_FILE" # Start Ralph in background nohup ralph build "$RALPH_MAX_ITERATIONS" >> "$RALPH_LOG_FILE" 2>&1 & RALPH_PID=$! # Save PID for tracking echo "$RALPH_PID" > "$RALPH_PID_FILE" echo "$RALPH_PID" > "$RALPH_LOCK_FILE" # Log the trigger { echo "[$(date -u +"%Y-%m-%d %H:%M:%S UTC")] Ralph auto-triggered" echo " Mode: $RALPH_AUTO_MODE" echo " Agent: $detected_agent" echo " PID: $RALPH_PID" echo " Log: $RALPH_LOG_FILE" } >> "$CLAUDE_DIR/ralph-trigger.log" 2>/dev/null || true # Notify user via stderr (visible in Claude Code) echo "🔄 Ralph CLI auto-started in background" >&2 echo " PID: $RALPH_PID" >&2 echo " Agent: $detected_agent" >&2 echo " Monitor: tail -f ~/.claude/ralph-output.log" >&2 echo " Stop: kill \$(cat ~/.claude/ralph.pid)" >&2 else # Ralph not installed, just create state file echo "⚠️ Ralph CLI not installed. State file created for manual use." >&2 echo " Install: npm install -g @iannuttall/ralph" >&2 fi fi # Exit immediately (NON-BLOCKING - Claude Code continues) exit 0