Enhance Ralph hook with background task spawning
BREAKING CHANGE: Ralph now runs as background process (non-blocking) Changes: - Enhanced ralph-auto-trigger.sh to spawn Ralph in background - Hook immediately returns to Claude Code (NON-BLOCKING) - Ralph runs via nohup (survives terminal close) - Added PID tracking: ~/.claude/ralph.pid - Added lock file: ~/.claude/ralph.lock - Added log file: ~/.claude/ralph-output.log - Added trigger log: ~/.claude/ralph-trigger.log - Added RALPH_MAX_ITERATIONS environment variable (default: 50) Features: - Background execution: Claude Code continues immediately - Process tracking: PID saved for monitoring/stopping - Lock mechanism: Prevents duplicate Ralph instances - Real-time monitoring: tail -f ~/.claude/ralph-output.log - Graceful cleanup: Auto-cleanup of dead process locks Updated files: - MASTER-PROMPT.md: Enhanced hook script + usage instructions - interactive-install-claude.sh: Enhanced install_ralph() function - ~/.claude/hooks/ralph-auto-trigger.sh: Updated local installation Usage: - Ralph auto-starts in background when agents are requested - Monitor: tail -f ~/.claude/ralph-output.log - Stop: kill $(cat ~/.claude/ralph.pid) - Configure: export RALPH_AUTO_MODE=agents|always|off - Configure: export RALPH_MAX_ITERATIONS=100 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
279
MASTER-PROMPT.md
279
MASTER-PROMPT.md
@@ -504,80 +504,198 @@ ralph --version
|
|||||||
mkdir -p ~/.claude/hooks
|
mkdir -p ~/.claude/hooks
|
||||||
cat > ~/.claude/hooks/ralph-auto-trigger.sh << 'EOF'
|
cat > ~/.claude/hooks/ralph-auto-trigger.sh << 'EOF'
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
# Ralph Auto-Trigger Hook - Enhanced with Background Task Spawning
|
||||||
# Ralph Auto-Trigger Hook
|
# Automatically starts Ralph CLI in background when needed
|
||||||
# Automatically starts Ralph loop when user requests something
|
#
|
||||||
# Set RALPH_AUTO_MODE environment variable to control behavior:
|
# Modes (via RALPH_AUTO_MODE environment variable):
|
||||||
# "always" - Always start Ralph for every request
|
# "always" - Start Ralph for every request
|
||||||
# "agents" - Only start Ralph for agent requests (default)
|
# "agents" - Only for agent requests (default)
|
||||||
# "off" - Disable auto-trigger
|
# "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
|
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
|
# Read hook input from stdin
|
||||||
HOOK_INPUT=$(cat)
|
HOOK_INPUT=$(cat)
|
||||||
|
USER_PROMPT=$(echo "$HOOK_INPUT" | jq -r '.prompt // empty' 2>/dev/null || echo "")
|
||||||
|
|
||||||
# Get the user prompt from hook input
|
# Fallback: if no JSON input, use first argument
|
||||||
USER_PROMPT=$(echo "$HOOK_INPUT" | jq -r '.prompt // empty')
|
if [[ -z "$USER_PROMPT" && $# -gt 0 ]]; then
|
||||||
|
USER_PROMPT="$1"
|
||||||
|
fi
|
||||||
|
|
||||||
# Check if auto-trigger is enabled (default: agents)
|
# Get Ralph mode (default: agents)
|
||||||
RALPH_AUTO_MODE="${RALPH_AUTO_MODE:-agents}"
|
RALPH_AUTO_MODE="${RALPH_AUTO_MODE:-agents}"
|
||||||
|
RALPH_MAX_ITERATIONS="${RALPH_MAX_ITERATIONS:-50}"
|
||||||
|
|
||||||
# Exit if auto-trigger is disabled
|
# Exit if auto-trigger is disabled
|
||||||
if [[ "$RALPH_AUTO_MODE" == "off" ]]; then
|
if [[ "$RALPH_AUTO_MODE" == "off" ]]; then
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Exit if Ralph is already active
|
# Check if Ralph is already running (via lock file)
|
||||||
RALPH_STATE_FILE=".claude/ralph-loop.local.md"
|
if [[ -f "$RALPH_LOCK_FILE" ]]; then
|
||||||
if [[ -f "$RALPH_STATE_FILE" ]]; then
|
# Check if process is still alive
|
||||||
exit 0
|
LOCK_PID=$(cat "$RALPH_LOCK_FILE" 2>/dev/null || echo "")
|
||||||
|
if [[ -n "$LOCK_PID" ]] && 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
|
fi
|
||||||
|
|
||||||
# Check if this is an agent request (list of known agents)
|
# Agent detection list (lowercase for matching)
|
||||||
KNOWN_AGENTS="frontend-developer|backend-architect|mobile-app-builder|ai-engineer|devops-automator|rapid-prototyper|test-writer-fixer|studio-coach|studio-producer|sprint-prioritizer|experiment-tracker|whimsy-injector|joker|trend-researcher|tiktok-strategist|feedback-synthesizer|ui-ux-pro-max"
|
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
|
# Determine if we should start Ralph
|
||||||
START_RALPH=false
|
should_trigger=false
|
||||||
|
|
||||||
case "$RALPH_AUTO_MODE" in
|
case "$RALPH_AUTO_MODE" in
|
||||||
"always")
|
"always")
|
||||||
# Start Ralph for everything
|
# Trigger on all prompts
|
||||||
START_RALPH=true
|
should_trigger=true
|
||||||
;;
|
;;
|
||||||
"agents")
|
"agents")
|
||||||
# Check if prompt mentions an agent or uses agent-like language
|
# Only trigger on agent requests OR development keywords
|
||||||
if echo "$USER_PROMPT" | grep -qiE "use (the |a )?($KNOWN_AGENTS) agent|launch ($KNOWN_AGENTS)|agent:|delegate to|run (the |a )?($KNOWN_AGENTS)" || \
|
if [[ "$agent_detected" == true ]]; then
|
||||||
echo "$USER_PROMPT" | grep -qiE "build|create|implement|develop|fix|add|refactor|optimize"; then
|
should_trigger=true
|
||||||
START_RALPH=true
|
elif echo "$USER_PROMPT" | grep -qiE "build|create|implement|develop|fix|add|refactor|optimize|write|generate|delegate|autonomous"; then
|
||||||
fi
|
should_trigger=true
|
||||||
;;
|
detected_agent="general-development"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
if [[ "$START_RALPH" == "true" ]]; then
|
if [[ "$should_trigger" == true ]]; then
|
||||||
# Create Ralph state file with default settings
|
# Create Ralph state file
|
||||||
mkdir -p .claude
|
mkdir -p "$CLAUDE_DIR"
|
||||||
|
|
||||||
cat > "$RALPH_STATE_FILE" <<EOF
|
cat > "$RALPH_STATE_FILE" << EOF
|
||||||
---
|
# Ralph Loop State - Auto-Triggered
|
||||||
active: true
|
# Generated: $(date -u +"%Y-%m-%d %H:%M:%S UTC")
|
||||||
iteration: 1
|
|
||||||
max_iterations: 0
|
|
||||||
completion_promise: null
|
|
||||||
started_at: "$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
||||||
auto_triggered: true
|
|
||||||
---
|
|
||||||
|
|
||||||
|
**User Request:**
|
||||||
$USER_PROMPT
|
$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
|
EOF
|
||||||
|
|
||||||
# Notify that Ralph was auto-triggered
|
# Spawn Ralph in background (NON-BLOCKING)
|
||||||
echo "🔄 Ralph auto-triggered!" >&2
|
if command -v ralph &> /dev/null; then
|
||||||
echo " Mode: $RALPH_AUTO_MODE" >&2
|
# Create log file
|
||||||
echo " Ralph will loop this request indefinitely." >&2
|
touch "$RALPH_LOG_FILE"
|
||||||
echo " Set RALPH_AUTO_MODE=off to disable, or use /cancel-ralph to stop." >&2
|
|
||||||
|
# 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
|
fi
|
||||||
|
|
||||||
|
# Exit immediately (NON-BLOCKING - Claude Code continues)
|
||||||
exit 0
|
exit 0
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
@@ -609,8 +727,11 @@ Note: Replace "/home/uroma" with your actual home directory if different.
|
|||||||
|
|
||||||
### What This Provides:
|
### What This Provides:
|
||||||
|
|
||||||
**Autonomous Looping:**
|
**Background Autonomous Looping:**
|
||||||
- Ralph keeps iterating until task completion
|
- 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
|
||||||
- State preserved in `.claude/ralph-loop.local.md`
|
- State preserved in `.claude/ralph-loop.local.md`
|
||||||
- Can handle complex multi-step workflows
|
- Can handle complex multi-step workflows
|
||||||
|
|
||||||
@@ -625,46 +746,70 @@ export RALPH_AUTO_MODE="always"
|
|||||||
# Mode 3: Disable auto-loop
|
# Mode 3: Disable auto-loop
|
||||||
export RALPH_AUTO_MODE="off"
|
export RALPH_AUTO_MODE="off"
|
||||||
|
|
||||||
### How It Works:
|
# Set max iterations (default: 50)
|
||||||
|
export RALPH_MAX_ITERATIONS="100"
|
||||||
|
|
||||||
1. You request: "Build a TikTok app in 2 weeks"
|
### How It Works (Background Execution):
|
||||||
|
|
||||||
|
1. You request in Claude Code: "Build a TikTok app in 2 weeks"
|
||||||
2. Hook detects agent language
|
2. Hook detects agent language
|
||||||
3. Ralph creates state file and starts loop
|
3. Hook spawns Ralph in BACKGROUND (non-blocking)
|
||||||
4. Ralph iterates through agents:
|
4. Claude Code immediately continues (you can keep working!)
|
||||||
|
5. Ralph autonomously iterates in background:
|
||||||
- Iteration 1: rapid-prototyper builds MVP
|
- Iteration 1: rapid-prototyper builds MVP
|
||||||
- Iteration 2: tiktok-strategist plans viral features
|
- Iteration 2: tiktok-strategist plans viral features
|
||||||
- Iteration 3: frontend-developer builds UI
|
- Iteration 3: frontend-developer builds UI
|
||||||
- Continues until Ralph is satisfied
|
- Continues until Ralph completes max iterations
|
||||||
5. Ralph updates `.claude/ralph-loop.local.md` each iteration
|
6. Monitor Ralph: tail -f ~/.claude/ralph-output.log
|
||||||
6. Stop with: /cancel-ralph command or delete state file
|
7. Stop Ralph anytime: kill $(cat ~/.claude/ralph.pid)
|
||||||
|
|
||||||
|
### Monitoring Ralph:
|
||||||
|
|
||||||
|
# View Ralph output in real-time
|
||||||
|
tail -f ~/.claude/ralph-output.log
|
||||||
|
|
||||||
|
# Check if Ralph is still running
|
||||||
|
ps aux | grep ralph
|
||||||
|
# Or check the PID file
|
||||||
|
cat ~/.claude/ralph.pid
|
||||||
|
|
||||||
|
# View Ralph trigger log
|
||||||
|
cat ~/.claude/ralph-trigger.log
|
||||||
|
|
||||||
### Canceling Ralph:
|
### Canceling Ralph:
|
||||||
|
|
||||||
If Ralph is looping and you want to stop:
|
If Ralph is running in background and you want to stop:
|
||||||
|
|
||||||
# Method 1: Use cancel command
|
# Method 1: Kill by PID (recommended)
|
||||||
/cancel-ralph
|
kill $(cat ~/.claude/ralph.pid)
|
||||||
|
rm ~/.claude/ralph.lock
|
||||||
|
|
||||||
# Method 2: Delete state file
|
# Method 2: Kill by process name
|
||||||
rm .claude/ralph-loop.local.md
|
pkill -f "ralph build"
|
||||||
|
|
||||||
# Method 3: Disable auto-mode
|
# Method 3: Disable auto-mode (prevents future triggers)
|
||||||
export RALPH_AUTO_MODE="off"
|
export RALPH_AUTO_MODE="off"
|
||||||
|
|
||||||
|
# Method 4: Delete state file (Ralph will stop on next iteration)
|
||||||
|
rm ~/.claude/ralph-loop.local.md
|
||||||
|
|
||||||
### Ralph vs PROACTIVELY Agents:
|
### Ralph vs PROACTIVELY Agents:
|
||||||
|
|
||||||
| Feature | Ralph CLI | PROACTIVELY Agents |
|
| Feature | Ralph CLI (Background) | PROACTIVELY Agents |
|
||||||
|---------|-----------|-------------------|
|
|---------|------------------------|-------------------|
|
||||||
| **Execution** | Multi-loop iterations | Single interaction |
|
| **Execution** | Multi-loop, background process | Single interaction |
|
||||||
| **State** | Persisted in file | Context only |
|
| **Blocking** | NON-BLOCKING (runs in bg) | Blocking (waits for response) |
|
||||||
| **Control** | Manual stop required | Automatic |
|
| **State** | Persisted in file + log | Context only |
|
||||||
|
| **Control** | Manual stop via PID | Automatic completion |
|
||||||
| **Complexity** | Handles complex workflows | Quick coordination |
|
| **Complexity** | Handles complex workflows | Quick coordination |
|
||||||
| **Setup** | Requires hook + script | Built-in to agents |
|
| **Setup** | Requires hook + script | Built-in to agents |
|
||||||
|
| **Best For** | Large projects, overnight coding | Quick tasks, human collaboration |
|
||||||
|
|
||||||
**You can use BOTH together:**
|
**You can use BOTH together:**
|
||||||
- Use Ralph for complex, multi-step projects
|
- Use Ralph for complex, multi-step projects (autonomous)
|
||||||
- Use PROACTIVELY agents for quick tasks
|
- Use PROACTIVELY agents for quick tasks (interactive)
|
||||||
- Set RALPH_AUTO_MODE="agents" for hybrid approach
|
- Set RALPH_AUTO_MODE="agents" for hybrid approach
|
||||||
|
- Ralph runs in background while you continue working in Claude Code
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1151,15 +1151,63 @@ install_ralph() {
|
|||||||
# Create hooks directory
|
# Create hooks directory
|
||||||
mkdir -p "$CLAUDE_DIR/hooks"
|
mkdir -p "$CLAUDE_DIR/hooks"
|
||||||
|
|
||||||
# Create ralph-auto-trigger.sh script
|
# Create ralph-auto-trigger.sh script (Enhanced with Background Spawning)
|
||||||
log_info "Creating Ralph auto-trigger hook..."
|
log_info "Creating Ralph auto-trigger hook with background spawning..."
|
||||||
cat > "$CLAUDE_DIR/hooks/ralph-auto-trigger.sh" << 'RALPH_HOOK_EOF'
|
cat > "$CLAUDE_DIR/hooks/ralph-auto-trigger.sh" << 'RALPH_HOOK_EOF'
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Ralph Auto-Trigger Hook
|
# Ralph Auto-Trigger Hook - Enhanced with Background Task Spawning
|
||||||
# Detects agent requests and manages Ralph state for autonomous looping
|
# 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
|
||||||
|
|
||||||
STATE_FILE="$HOME/.claude/ralph-loop.local.md"
|
set -euo pipefail
|
||||||
RALPH_MODE="${RALPH_AUTO_MODE:-agents}" # Options: always, agents, off
|
|
||||||
|
# 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 "$LOCK_PID" ]] && 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)
|
# Agent detection list (lowercase for matching)
|
||||||
AGENTS=(
|
AGENTS=(
|
||||||
@@ -1176,16 +1224,9 @@ AGENTS=(
|
|||||||
"api-tester" "performance-benchmarker" "test-results-analyzer"
|
"api-tester" "performance-benchmarker" "test-results-analyzer"
|
||||||
"tool-evaluator" "workflow-optimizer"
|
"tool-evaluator" "workflow-optimizer"
|
||||||
"joker" "agent-updater"
|
"joker" "agent-updater"
|
||||||
|
"explore" "plan" "general-purpose"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Check if mode is off
|
|
||||||
if [ "$RALPH_MODE" = "off" ]; then
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Read the user prompt
|
|
||||||
USER_PROMPT="$1"
|
|
||||||
|
|
||||||
# Detect agent request (case-insensitive)
|
# Detect agent request (case-insensitive)
|
||||||
agent_detected=false
|
agent_detected=false
|
||||||
detected_agent=""
|
detected_agent=""
|
||||||
@@ -1198,51 +1239,114 @@ for agent in "${AGENTS[@]}"; do
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Check if we should trigger
|
# Determine if we should start Ralph
|
||||||
should_trigger=false
|
should_trigger=false
|
||||||
|
|
||||||
if [ "$RALPH_MODE" = "always" ]; then
|
case "$RALPH_AUTO_MODE" in
|
||||||
# Trigger on all prompts
|
"always")
|
||||||
should_trigger=true
|
# Trigger on all prompts
|
||||||
elif [ "$RALPH_MODE" = "agents" ]; then
|
|
||||||
# Only trigger on agent requests
|
|
||||||
if [ "$agent_detected" = true ]; then
|
|
||||||
should_trigger=true
|
should_trigger=true
|
||||||
fi
|
;;
|
||||||
fi
|
"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
|
if [[ "$should_trigger" == true ]]; then
|
||||||
# Create/update state file
|
# Create Ralph state file
|
||||||
cat > "$STATE_FILE" << EOF
|
mkdir -p "$CLAUDE_DIR"
|
||||||
# Ralph Loop State
|
|
||||||
# Generated: $(date)
|
cat > "$RALPH_STATE_FILE" << EOF
|
||||||
|
# Ralph Loop State - Auto-Triggered
|
||||||
|
# Generated: $(date -u +"%Y-%m-%d %H:%M:%S UTC")
|
||||||
|
|
||||||
**User Request:**
|
**User Request:**
|
||||||
$USER_PROMPT
|
$USER_PROMPT
|
||||||
|
|
||||||
**Detected Agent:** $detected_agent
|
**Detected Agent:** $detected_agent
|
||||||
**Mode:** $RALPH_MODE
|
**Mode:** $RALPH_AUTO_MODE
|
||||||
|
**Max Iterations:** $RALPH_MAX_ITERATIONS
|
||||||
**Timestamp:** $(date -Iseconds)
|
**Timestamp:** $(date -Iseconds)
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
This state file was automatically generated by the Ralph auto-trigger hook.
|
This state file was automatically generated by the Ralph auto-trigger hook.
|
||||||
Ralph CLI can read this file to continue autonomous looping based on the user's request.
|
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
|
## Usage
|
||||||
|
|
||||||
To run Ralph autonomously:
|
Ralph is running autonomously in the background. Monitor progress:
|
||||||
\`\`\`bash
|
|
||||||
ralph build 50 # Run 50 iterations
|
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
|
||||||
|
|
||||||
|
|
||||||
Ralph will read this state file and continue working until the task is complete.
|
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
# Log the trigger (silent, no output)
|
# Spawn Ralph in background (NON-BLOCKING)
|
||||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Ralph auto-triggered: mode=$RALPH_MODE, agent=$detected_agent" >> "$HOME/.claude/ralph-trigger.log" 2>/dev/null || true
|
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
|
fi
|
||||||
|
|
||||||
|
# Exit immediately (NON-BLOCKING - Claude Code continues)
|
||||||
exit 0
|
exit 0
|
||||||
RALPH_HOOK_EOF
|
RALPH_HOOK_EOF
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user