-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathloop.sh
More file actions
executable file
·157 lines (131 loc) · 5.92 KB
/
Copy pathloop.sh
File metadata and controls
executable file
·157 lines (131 loc) · 5.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#!/usr/bin/env bash
set -euo pipefail
# ──────────────────────────────────────────────
# Ralph Loop — Spec-Driven Development Runner
# ──────────────────────────────────────────────
#
# Usage:
# ralph/loop.sh build [N] — Run N build iterations (default: 1)
# ralph/loop.sh plan [N] — Run N plan iterations (default: 1)
# ralph/loop.sh test [N] — Run N test iterations (default: 1)
# ralph/loop.sh spec [N] — Run N spec iterations (default: 1)
#
# Each iteration:
# 1. Invokes Claude with the matching PROMPT_{mode}.md
# 2. Waits for completion
# 3. Pushes to git
# 4. Loops (if N > 1)
#
# Environment variables:
# RALPH_MODEL — Claude model to use (default: opus)
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
RALPH_DIR="$SCRIPT_DIR"
MODE="${1:-build}"
ITERATIONS="${2:-0}" # 0 = infinite (run until all specs/builds complete)
# Claude model (override with RALPH_MODEL env var)
CLAUDE_MODEL="${RALPH_MODEL:-opus}"
# Validate mode
case "$MODE" in
build|plan|test|spec) ;;
*)
echo "Usage: ralph/loop.sh {build|plan|test|spec} [iterations]"
echo ""
echo "Environment variables:"
echo " RALPH_MODEL — Claude model to use (default: opus)"
exit 1
;;
esac
PROMPT_FILE="$RALPH_DIR/PROMPT_${MODE}.md"
if [[ ! -f "$PROMPT_FILE" ]]; then
echo "ERROR: Prompt file not found: $PROMPT_FILE"
exit 1
fi
# ── Read configurable paths from rules.md ──
RULES_FILE="$RALPH_DIR/rules.md"
SPECS_DIR=""
IMPL_PLAN=""
SPEC_REGISTRY_PATH=""
if [[ -f "$RULES_FILE" ]]; then
# Parse paths from the "## File Paths" code block in rules.md
SPECS_DIR=$(grep '^\s*specs_dir:' "$RULES_FILE" 2>/dev/null | head -1 | sed 's/.*specs_dir:\s*//' | tr -d '[:space:]') || true
IMPL_PLAN=$(grep '^\s*implementation_plan:' "$RULES_FILE" 2>/dev/null | head -1 | sed 's/.*implementation_plan:\s*//' | tr -d '[:space:]') || true
SPEC_REGISTRY_PATH=$(grep '^\s*spec_registry:' "$RULES_FILE" 2>/dev/null | head -1 | sed 's/.*spec_registry:\s*//' | tr -d '[:space:]') || true
fi
# Defaults if not found in rules.md
SPECS_DIR="${SPECS_DIR:-docs/specs}"
IMPL_PLAN="${IMPL_PLAN:-docs/IMPLEMENTATION_PLAN.md}"
SPEC_REGISTRY_PATH="${SPEC_REGISTRY_PATH:-ralph/SPEC_REGISTRY.md}"
# Ctrl+C trap — clean exit
trap 'echo -e "\n\n🛑 Loop interrupted. Progress saved in git."; exit 0' INT
if [[ "$ITERATIONS" -eq 0 ]]; then
ITER_DISPLAY="infinite (until complete)"
else
ITER_DISPLAY="$ITERATIONS"
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " Ralph Loop — $MODE mode"
echo " Model: $CLAUDE_MODEL"
echo " Iterations: $ITER_DISPLAY"
echo " Project: $PROJECT_ROOT"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
cd "$PROJECT_ROOT"
i=0
while true; do
i=$((i + 1))
# If a finite limit was set, stop when reached
if [[ "$ITERATIONS" -gt 0 && "$i" -gt "$ITERATIONS" ]]; then
break
fi
echo ""
if [[ "$ITERATIONS" -eq 0 ]]; then
echo "╔═══════════════════════════════════════════╗"
echo "║ Iteration $i — $MODE (until complete)"
echo "╚═══════════════════════════════════════════╝"
else
echo "╔═══════════════════════════════════════════╗"
echo "║ Iteration $i / $ITERATIONS — $MODE"
echo "╚═══════════════════════════════════════════╝"
fi
echo ""
# Run Claude with the prompt file, capture output
OUTPUT=$(claude -p \
--dangerously-skip-permissions \
--model "$CLAUDE_MODEL" \
--verbose \
"$(cat "$PROMPT_FILE")" 2>&1) || true
echo "$OUTPUT"
# Check if all work is done by inspecting actual project state (not agent output text)
ALL_DONE=false
if [[ "$MODE" == "spec" ]]; then
# Count specs in registry vs spec files on disk
REGISTRY_COUNT=$(grep -c '^### [0-9]' "$SPEC_REGISTRY_PATH" 2>/dev/null) || REGISTRY_COUNT=0
SPEC_FILES=$(find "$SPECS_DIR" -name '*.md' 2>/dev/null | wc -l | tr -d ' ') || SPEC_FILES=0
if [[ "$SPEC_FILES" -ge "$REGISTRY_COUNT" && "$REGISTRY_COUNT" -gt 0 ]]; then
ALL_DONE=true
fi
elif [[ "$MODE" == "build" ]]; then
# Count remaining [ ] (NOT STARTED) spec entries (must have a number after the checkbox)
REMAINING=$(grep -c '^\- \[ \] [0-9]' "$IMPL_PLAN" 2>/dev/null) || REMAINING=0
if [[ "$REMAINING" -eq 0 ]]; then
ALL_DONE=true
fi
fi
if [[ "$ALL_DONE" == "true" ]]; then
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " All done! No remaining work in $MODE mode."
echo " Completed $i iteration(s)."
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
break
fi
echo ""
echo "── Iteration $i complete. Pushing to git... ──"
# Push whatever was committed during the iteration
git push 2>/dev/null || echo " (nothing to push or push failed)"
echo ""
done
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " Ralph Loop complete. $i iteration(s) of $MODE done."
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"