salesforce-appraiser-review.../ralph-loop.sh

199 lines
5.6 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# Ralph Wiggum Loop — Autonomous agent iteration
#
# Based on Geoffrey Huntley's approach:
# - Each iteration spawns a FRESH agent with clean context
# - Agent reads the plan, picks ONE task, implements, tests, commits, exits
# - Loop restarts until all tasks are done
#
# No context compaction. No stale reasoning. Just fresh starts.
#
# Usage:
# ./ralph-loop.sh # Build mode (default)
# ./ralph-loop.sh plan # Planning mode (create IMPLEMENTATION_PLAN.md)
# ./ralph-loop.sh --max 20 # Limit to 20 iterations
# ./ralph-loop.sh --agent claude # Use claude (default)
# ./ralph-loop.sh --agent codex # Use OpenAI Codex CLI
# ./ralph-loop.sh --agent aider # Use Aider
# ./ralph-loop.sh --agent gemini # Use Gemini CLI
# ./ralph-loop.sh --agent custom # Use custom agent (see below)
#
# Extensibility:
# To add support for other AI coding agents (aider, cursor, windsurf, etc.):
# 1. Add a new case in the run_agent() function's agent selection block
# 2. Format the prompt appropriately for that agent's CLI interface
# 3. Ensure the agent outputs to the logfile for promise detection
#
# Example for Aider:
# aider)
# aider --message "$prompt" --yes 2>&1 | tee "$logfile"
# ;;
#
# Example for custom script:
# custom)
# ./my-agent-wrapper.sh "$prompt" 2>&1 | tee "$logfile"
# ;;
#
set -euo pipefail
MODE="${1:-build}"
MAX_ITERATIONS=50
AGENT="claude"
PLAN_FILE="IMPLEMENTATION_PLAN.md"
SPEC_FILE="PROJECT-SPEC.md"
AGENT_FILE="AGENT.md"
LOG_DIR=".ralph-logs"
# Parse arguments
shift 2>/dev/null || true
while [[ $# -gt 0 ]]; do
case "$1" in
--max) MAX_ITERATIONS="$2"; shift 2 ;;
--agent) AGENT="$2"; shift 2 ;;
*) echo "Unknown option: $1"; exit 1 ;;
esac
done
mkdir -p "$LOG_DIR"
# Colors
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
BLUE='\033[0;34m'
NC='\033[0m'
log() { echo -e "${BLUE}[ralph]${NC} $1"; }
success() { echo -e "${GREEN}[ralph]${NC} $1"; }
warn() { echo -e "${YELLOW}[ralph]${NC} $1"; }
error() { echo -e "${RED}[ralph]${NC} $1"; }
# Check prerequisites
if [[ ! -f "$SPEC_FILE" ]]; then
error "Missing $SPEC_FILE — create your project spec first."
exit 1
fi
if [[ ! -f "$AGENT_FILE" ]]; then
warn "No $AGENT_FILE found. Using default agent instructions."
fi
run_agent() {
local iteration=$1
local mode=$2
local logfile="$LOG_DIR/iteration-${iteration}.log"
local prompt=""
if [[ "$mode" == "plan" ]]; then
prompt="Read PROJECT-SPEC.md. Decompose the project into discrete, testable tasks ordered by dependency. Write the plan to IMPLEMENTATION_PLAN.md with checkboxes. Output <promise>PLANNED</promise> when done."
else
prompt="Read AGENT.md (if it exists) for your instructions. Follow the core loop: orient, pick one task, implement, verify, commit, exit."
fi
log "Iteration $iteration ($mode mode) — starting fresh agent..."
# Agent selection block
# Extend this case statement to support additional agents
case "$AGENT" in
claude)
echo "$prompt" | claude -p --output-format text 2>&1 | tee "$logfile"
;;
codex)
echo "$prompt" | codex 2>&1 | tee "$logfile"
;;
aider)
# Aider: AI pair programming in your terminal
# https://aider.chat
aider --message "$prompt" --yes 2>&1 | tee "$logfile"
;;
gemini)
# Google Gemini CLI (if available)
# Adjust command based on actual Gemini CLI interface
echo "$prompt" | gemini-cli 2>&1 | tee "$logfile"
;;
custom)
# Custom agent integration
# Replace this with your own agent wrapper script
# The script should:
# 1. Accept prompt as first argument or via stdin
# 2. Perform the requested work (read files, write code, run tests, commit)
# 3. Output promise signals: <promise>PLANNED|DONE|STUCK|ERROR</promise>
# 4. Exit with appropriate code
if [[ -x "./custom-agent.sh" ]]; then
./custom-agent.sh "$prompt" 2>&1 | tee "$logfile"
else
error "Custom agent selected but ./custom-agent.sh not found or not executable"
exit 1
fi
;;
*)
error "Unknown agent: $AGENT"
error "Supported agents: claude, codex, aider, gemini, custom"
error "To add support for other agents, edit the run_agent() function in this script"
exit 1
;;
esac
return 0
}
check_output() {
local logfile="$1"
if grep -q '<promise>DONE</promise>' "$logfile" 2>/dev/null; then
return 0 # Done
elif grep -q '<promise>STUCK</promise>' "$logfile" 2>/dev/null; then
return 2 # Stuck
elif grep -q '<promise>ERROR</promise>' "$logfile" 2>/dev/null; then
return 3 # Error
else
return 1 # Continue
fi
}
# Main loop
if [[ "$MODE" == "plan" ]]; then
log "Planning mode — creating implementation plan..."
run_agent 0 plan
success "Plan created. Review $PLAN_FILE, then run: ./ralph-loop.sh"
exit 0
fi
log "Starting Ralph Wiggum loop (max $MAX_ITERATIONS iterations)"
log "Agent: $AGENT"
log "Spec: $SPEC_FILE"
log "Plan: $PLAN_FILE"
echo ""
for i in $(seq 1 "$MAX_ITERATIONS"); do
run_agent "$i" build
logfile="$LOG_DIR/iteration-${i}.log"
check_output "$logfile"
status=$?
case $status in
0)
success "🎉 ALL TASKS COMPLETE after $i iterations!"
exit 0
;;
2)
warn "Agent is stuck. Review $logfile and intervene."
exit 1
;;
3)
error "Agent encountered an error. Review $logfile."
exit 1
;;
1)
log "Iteration $i complete. Restarting with fresh context..."
echo ""
sleep 2
;;
esac
done
warn "Reached max iterations ($MAX_ITERATIONS). Review progress in $PLAN_FILE."
exit 1