ralph-cli/ralph
2026-01-22 14:46:48 -05:00

354 lines
7.6 KiB
Bash
Executable file

#!/bin/bash
set -e
# Ralph - AI Coding Loop Runner
# Based on Matt Pocock's Ralph Wiggum pattern
# https://github.com/rathi/ralph-cli
VERSION="1.0.0"
RALPH_DIR=".ralph"
PROGRESS_FILE="ralph-progress.txt"
# Colors (optional)
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
usage() {
cat << EOF
Ralph v$VERSION - AI Coding Loop Runner
USAGE:
ralph <command> [options]
COMMANDS:
run <iterations> [prd-file] Run Ralph loop with PRD file
init Initialize Ralph in current directory
generate <type> Generate PRD from codebase analysis
status Show current progress
reset Clear progress file
OPTIONS:
-p, --prd <file> PRD file to use (default: .ralph/prd.md)
-m, --module <name> Run specific module only
--docker Run in Docker sandbox (safer for AFK)
--dry-run Show what would happen without executing
-h, --help Show this help
EXAMPLES:
ralph init # Set up Ralph in current repo
ralph run 10 # Run 10 iterations with default PRD
ralph run 5 -p my-prd.md # Run 5 iterations with custom PRD
ralph generate tests # Generate test PRD from codebase
ralph status # Check progress
EOF
}
# Initialize Ralph in a repo
init_ralph() {
echo "Initializing Ralph in $(pwd)..."
mkdir -p "$RALPH_DIR"
touch "$PROGRESS_FILE"
# Create template PRD
cat > "$RALPH_DIR/prd.md" << 'EOF'
# Project PRD
## Overview
Describe what you want Ralph to accomplish.
## Tasks (Priority Order)
- [ ] CRITICAL: First high-priority task
Description and acceptance criteria.
- [ ] HIGH: Second task
Description.
- [ ] MEDIUM: Third task
Description.
- [ ] LOW: Lower priority task
Description.
## Completion Criteria
All tasks checked off and verified.
## Notes
- Add any context Claude needs
- Reference specific files if helpful
EOF
# Create .gitignore entry
if [ -f .gitignore ]; then
if ! grep -q "ralph-progress.txt" .gitignore; then
echo "" >> .gitignore
echo "# Ralph" >> .gitignore
echo "ralph-progress.txt" >> .gitignore
fi
fi
echo ""
echo "Ralph initialized!"
echo ""
echo "Next steps:"
echo " 1. Edit $RALPH_DIR/prd.md with your tasks"
echo " 2. Run: ralph run 10"
echo ""
}
# Generate PRD from codebase
generate_prd() {
local type=${1:-"general"}
local output="$RALPH_DIR/prd-$type.md"
mkdir -p "$RALPH_DIR"
echo "Generating $type PRD..."
# Use Claude to analyze codebase and generate PRD
claude --dangerously-skip-permissions -p \
"Analyze this codebase and generate a PRD for: $type
Create a markdown PRD file with:
1. Overview of what needs to be done
2. Tasks in priority order (CRITICAL, HIGH, MEDIUM, LOW)
3. Each task should have:
- [ ] checkbox
- Priority label
- Clear description
- File paths if relevant
4. Completion criteria
Output ONLY the PRD content, no explanations.
Format it exactly like this example:
# [Type] PRD
## Overview
Brief description.
## Tasks (Priority Order)
- [ ] CRITICAL: Task name
Description. Source: path/to/file.ts
- [ ] HIGH: Another task
Description.
## Completion Criteria
What done looks like.
" > "$output"
echo "Generated: $output"
echo ""
cat "$output"
}
# Show status
show_status() {
echo "=== Ralph Status ==="
echo ""
if [ -f "$PROGRESS_FILE" ]; then
echo "Progress file: $PROGRESS_FILE"
echo "---"
cat "$PROGRESS_FILE"
else
echo "No progress file found. Run 'ralph init' first."
fi
echo ""
echo "PRD files in $RALPH_DIR/:"
ls -la "$RALPH_DIR"/*.md 2>/dev/null || echo " (none)"
}
# Reset progress
reset_progress() {
if [ -f "$PROGRESS_FILE" ]; then
rm "$PROGRESS_FILE"
touch "$PROGRESS_FILE"
echo "Progress reset."
else
echo "No progress file to reset."
fi
}
# Main Ralph loop
run_ralph() {
local iterations=$1
local prd_file=${2:-"$RALPH_DIR/prd.md"}
local use_docker=${3:-false}
if [ ! -f "$prd_file" ]; then
echo "Error: PRD file not found: $prd_file"
echo "Run 'ralph init' first or specify a PRD file."
exit 1
fi
touch "$PROGRESS_FILE"
echo "=== Ralph Loop Starting ==="
echo "PRD: $prd_file"
echo "Progress: $PROGRESS_FILE"
echo "Iterations: $iterations"
echo ""
for ((i=1; i<=$iterations; i++)); do
echo ""
echo "=============================================="
echo "=== RALPH ITERATION $i of $iterations ==="
echo "=============================================="
echo ""
local claude_cmd="claude --dangerously-skip-permissions"
if [ "$use_docker" = true ]; then
claude_cmd="docker sandbox run claude --permission-mode acceptEdits"
fi
result=$($claude_cmd -p \
"@$prd_file @$PROGRESS_FILE
You are an AI coding assistant running in a loop (Ralph pattern).
PROCESS:
1. Read the PRD to see what needs to be done.
2. Read ralph-progress.txt to see what's already completed.
3. Choose the HIGHEST PRIORITY incomplete task.
Priority order: CRITICAL > HIGH > MEDIUM > LOW
4. Implement the task thoroughly.
5. Run any relevant checks (tests, types, linting).
6. Append your progress to ralph-progress.txt:
- Task completed
- Files created/modified
- Any issues or notes
7. Make a git commit with a clear message.
RULES:
- ONLY WORK ON ONE TASK PER ITERATION.
- Be thorough - quality over speed.
- If a task is blocked, note it and move to the next.
If ALL tasks in the PRD are complete, output: <promise>COMPLETE</promise>
")
echo "$result"
# Check for completion
if [[ "$result" == *"<promise>COMPLETE</promise>"* ]]; then
echo ""
echo "=============================================="
echo -e "${GREEN}ALL TASKS COMPLETE after $i iteration(s)${NC}"
echo "=============================================="
return 0
fi
if [ $i -lt $iterations ]; then
echo ""
echo "--- Iteration $i done, continuing in 2s ---"
sleep 2
fi
done
echo ""
echo "=============================================="
echo -e "${YELLOW}Reached max iterations ($iterations)${NC}"
echo "Run 'ralph status' to see progress."
echo "=============================================="
}
# Parse arguments
COMMAND=""
ITERATIONS=""
PRD_FILE=""
USE_DOCKER=false
DRY_RUN=false
while [[ $# -gt 0 ]]; do
case $1 in
init)
COMMAND="init"
shift
;;
run)
COMMAND="run"
shift
ITERATIONS=${1:-10}
shift
;;
generate)
COMMAND="generate"
shift
GEN_TYPE=${1:-"general"}
shift
;;
status)
COMMAND="status"
shift
;;
reset)
COMMAND="reset"
shift
;;
-p|--prd)
PRD_FILE="$2"
shift 2
;;
--docker)
USE_DOCKER=true
shift
;;
--dry-run)
DRY_RUN=true
shift
;;
-h|--help)
usage
exit 0
;;
-v|--version)
echo "Ralph v$VERSION"
exit 0
;;
*)
# Check if it's a number (iterations) or file path
if [[ $1 =~ ^[0-9]+$ ]]; then
ITERATIONS=$1
elif [[ -f $1 ]]; then
PRD_FILE=$1
fi
shift
;;
esac
done
# Execute command
case $COMMAND in
init)
init_ralph
;;
run)
if [ -z "$ITERATIONS" ]; then
echo "Error: Specify number of iterations"
echo "Usage: ralph run <iterations> [prd-file]"
exit 1
fi
run_ralph "$ITERATIONS" "$PRD_FILE" "$USE_DOCKER"
;;
generate)
generate_prd "$GEN_TYPE"
;;
status)
show_status
;;
reset)
reset_progress
;;
*)
usage
exit 1
;;
esac