spec-and-loop 1.0.7 → 2.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/QUICKSTART.md +38 -27
- package/README.md +99 -152
- package/lib/mini-ralph/context.js +99 -0
- package/lib/mini-ralph/history.js +99 -0
- package/lib/mini-ralph/index.js +91 -0
- package/lib/mini-ralph/invoker.js +205 -0
- package/lib/mini-ralph/prompt.js +116 -0
- package/lib/mini-ralph/runner.js +415 -0
- package/lib/mini-ralph/state.js +93 -0
- package/lib/mini-ralph/status.js +211 -0
- package/lib/mini-ralph/tasks.js +209 -0
- package/package.json +3 -4
- package/scripts/mini-ralph-cli.js +226 -0
- package/scripts/ralph-monitor.sh +53 -6
- package/scripts/ralph-run.sh +244 -71
- package/scripts/setup.js +39 -31
package/scripts/ralph-monitor.sh
CHANGED
|
@@ -13,6 +13,16 @@ detect_os() {
|
|
|
13
13
|
|
|
14
14
|
detect_os
|
|
15
15
|
|
|
16
|
+
# Cross-platform file modification time
|
|
17
|
+
get_file_mtime() {
|
|
18
|
+
local file="$1"
|
|
19
|
+
if [[ "$OS" == "macOS" ]]; then
|
|
20
|
+
stat -f %m "$file" 2>/dev/null || echo 0
|
|
21
|
+
else
|
|
22
|
+
stat -c %Y "$file" 2>/dev/null || echo 0
|
|
23
|
+
fi
|
|
24
|
+
}
|
|
25
|
+
|
|
16
26
|
# Cross-platform file modification time display
|
|
17
27
|
get_file_mtime_display() {
|
|
18
28
|
local file="$1"
|
|
@@ -23,20 +33,57 @@ get_file_mtime_display() {
|
|
|
23
33
|
fi
|
|
24
34
|
}
|
|
25
35
|
|
|
26
|
-
|
|
36
|
+
resolve_project_root() {
|
|
37
|
+
local git_root=""
|
|
38
|
+
git_root=$(git rev-parse --show-toplevel 2>/dev/null || echo "")
|
|
39
|
+
|
|
40
|
+
if [[ -n "$git_root" ]]; then
|
|
41
|
+
printf "%s" "$git_root"
|
|
42
|
+
else
|
|
43
|
+
pwd
|
|
44
|
+
fi
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
auto_detect_change() {
|
|
48
|
+
local project_root="$1"
|
|
49
|
+
local changes_dir="$project_root/openspec/changes"
|
|
50
|
+
|
|
51
|
+
if [[ ! -d "$changes_dir" ]]; then
|
|
52
|
+
echo ""
|
|
53
|
+
return 1
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
local latest_change=""
|
|
57
|
+
local latest_time=0
|
|
58
|
+
|
|
59
|
+
for change_dir in "$changes_dir"/*; do
|
|
60
|
+
if [[ -d "$change_dir" && -f "$change_dir/tasks.md" ]]; then
|
|
61
|
+
local mod_time
|
|
62
|
+
mod_time=$(get_file_mtime "$change_dir/tasks.md")
|
|
63
|
+
|
|
64
|
+
if [[ "$mod_time" -gt "$latest_time" ]]; then
|
|
65
|
+
latest_time="$mod_time"
|
|
66
|
+
latest_change=$(basename "$change_dir")
|
|
67
|
+
fi
|
|
68
|
+
fi
|
|
69
|
+
done
|
|
70
|
+
|
|
71
|
+
printf "%s" "$latest_change"
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
PROJECT_ROOT="$(resolve_project_root)"
|
|
27
75
|
CHANGE_NAME="${1:-auto-detect}"
|
|
28
76
|
|
|
29
77
|
if [[ "$CHANGE_NAME" == "auto-detect" ]]; then
|
|
30
|
-
|
|
31
|
-
CHANGE_NAME=$(ls -t openspec/changes/ 2>/dev/null | head -1)
|
|
78
|
+
CHANGE_NAME=$(auto_detect_change "$PROJECT_ROOT")
|
|
32
79
|
if [[ -z "$CHANGE_NAME" ]]; then
|
|
33
|
-
echo "No changes found in openspec/changes/"
|
|
80
|
+
echo "No changes found in $PROJECT_ROOT/openspec/changes/"
|
|
34
81
|
exit 1
|
|
35
82
|
fi
|
|
36
83
|
fi
|
|
37
84
|
|
|
38
|
-
TASKS_FILE="$
|
|
39
|
-
RALPH_STATE="$
|
|
85
|
+
TASKS_FILE="$PROJECT_ROOT/openspec/changes/$CHANGE_NAME/tasks.md"
|
|
86
|
+
RALPH_STATE="$PROJECT_ROOT/.ralph/ralph-loop.state.json"
|
|
40
87
|
|
|
41
88
|
if [[ ! -f "$TASKS_FILE" ]]; then
|
|
42
89
|
echo "Tasks file not found: $TASKS_FILE"
|
package/scripts/ralph-run.sh
CHANGED
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
|
|
3
|
-
set -e
|
|
4
|
-
|
|
5
3
|
VERSION="1.0.0"
|
|
6
4
|
|
|
7
|
-
# Add Bun to PATH if installed
|
|
8
|
-
if [[ -d "$HOME/.bun/bin" ]]; then
|
|
9
|
-
export PATH="$HOME/.bun/bin:$PATH"
|
|
10
|
-
fi
|
|
11
|
-
|
|
12
5
|
# Detect OS for cross-platform compatibility
|
|
13
6
|
detect_os() {
|
|
14
7
|
case "$(uname -s)" in
|
|
@@ -61,8 +54,62 @@ get_realpath() {
|
|
|
61
54
|
fi
|
|
62
55
|
}
|
|
63
56
|
|
|
57
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
58
|
+
LOCAL_NODE_BIN="$SCRIPT_DIR/../node_modules/.bin"
|
|
59
|
+
# Allow tests to inject a mock by setting MINI_RALPH_CLI_OVERRIDE in the environment
|
|
60
|
+
MINI_RALPH_CLI="${MINI_RALPH_CLI_OVERRIDE:-$SCRIPT_DIR/mini-ralph-cli.js}"
|
|
61
|
+
|
|
62
|
+
if [[ -d "$LOCAL_NODE_BIN" ]]; then
|
|
63
|
+
case ":$PATH:" in
|
|
64
|
+
*":$LOCAL_NODE_BIN:"*) ;;
|
|
65
|
+
*) export PATH="$LOCAL_NODE_BIN:$PATH" ;;
|
|
66
|
+
esac
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
get_temp_root() {
|
|
70
|
+
local temp_root="${TMPDIR:-/tmp}"
|
|
71
|
+
temp_root="${temp_root%/}"
|
|
72
|
+
|
|
73
|
+
if [[ -z "$temp_root" ]]; then
|
|
74
|
+
temp_root="/tmp"
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
printf "%s" "$temp_root"
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
make_temp_dir() {
|
|
81
|
+
local prefix="${1:-ralph-run}"
|
|
82
|
+
local temp_root
|
|
83
|
+
temp_root=$(get_temp_root)
|
|
84
|
+
|
|
85
|
+
local temp_dir=""
|
|
86
|
+
temp_dir=$(mktemp -d "${temp_root}/${prefix}-XXXXXX" 2>/dev/null) || \
|
|
87
|
+
temp_dir=$(mktemp -d -t "$prefix" 2>/dev/null) || \
|
|
88
|
+
temp_dir=""
|
|
89
|
+
|
|
90
|
+
if [[ -n "$temp_dir" ]]; then
|
|
91
|
+
printf "%s" "$temp_dir"
|
|
92
|
+
return 0
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
local fallback_dir="${temp_root}/${prefix}-$(date +"%Y%m%d_%H%M%S")-$$"
|
|
96
|
+
mkdir -p "$fallback_dir"
|
|
97
|
+
printf "%s" "$fallback_dir"
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
resolve_ralph_command() {
|
|
101
|
+
if [[ -f "$MINI_RALPH_CLI" ]] && command -v node >/dev/null 2>&1; then
|
|
102
|
+
return 0
|
|
103
|
+
fi
|
|
104
|
+
return 1
|
|
105
|
+
}
|
|
106
|
+
|
|
64
107
|
CHANGE_NAME=""
|
|
65
108
|
MAX_ITERATIONS=""
|
|
109
|
+
NO_COMMIT=false
|
|
110
|
+
SHOW_STATUS=false
|
|
111
|
+
ADD_CONTEXT=""
|
|
112
|
+
CLEAR_CONTEXT=false
|
|
66
113
|
ERROR_OCCURRED=false
|
|
67
114
|
CLEANUP_IN_PROGRESS=false
|
|
68
115
|
|
|
@@ -77,12 +124,10 @@ cleanup() {
|
|
|
77
124
|
local exit_code=$1
|
|
78
125
|
log_info "Cleaning up..."
|
|
79
126
|
|
|
80
|
-
# NOTE: We do NOT kill
|
|
81
|
-
# 1. Ralph runs synchronously in the foreground
|
|
127
|
+
# NOTE: We do NOT kill node processes here because:
|
|
128
|
+
# 1. The mini Ralph runtime runs synchronously in the foreground
|
|
82
129
|
# 2. Ctrl+C (SIGINT) naturally propagates to child processes
|
|
83
|
-
# 3.
|
|
84
|
-
# 4. Using pkill -f "ralph" could kill other user processes
|
|
85
|
-
# The shell's process group handling ensures clean termination.
|
|
130
|
+
# 3. The shell's process group handling ensures clean termination.
|
|
86
131
|
|
|
87
132
|
if [[ $exit_code -ne 0 ]]; then
|
|
88
133
|
log_error "Script terminated with exit code: $exit_code"
|
|
@@ -90,7 +135,9 @@ cleanup() {
|
|
|
90
135
|
exit $exit_code
|
|
91
136
|
}
|
|
92
137
|
|
|
93
|
-
|
|
138
|
+
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
139
|
+
trap 'cleanup $?' EXIT INT TERM QUIT
|
|
140
|
+
fi
|
|
94
141
|
|
|
95
142
|
handle_error() {
|
|
96
143
|
local exit_code=$1
|
|
@@ -113,20 +160,29 @@ USAGE:
|
|
|
113
160
|
OPTIONS:
|
|
114
161
|
--change <name> Specify the OpenSpec change to execute (default: auto-detect)
|
|
115
162
|
--max-iterations <n> Maximum iterations for Ralph loop (default: 50)
|
|
163
|
+
--no-commit Suppress automatic git commits during the loop
|
|
116
164
|
--verbose, -v Enable verbose mode for debugging
|
|
117
165
|
--help, -h Show this help message
|
|
118
166
|
|
|
167
|
+
OBSERVABILITY AND CONTROL:
|
|
168
|
+
--status Print the current loop status dashboard and exit
|
|
169
|
+
--add-context <text> Add pending context to inject into the next iteration and exit
|
|
170
|
+
--clear-context Clear any pending context and exit
|
|
171
|
+
|
|
119
172
|
EXAMPLES:
|
|
120
173
|
ralph-run # Auto-detect most recent change
|
|
121
174
|
ralph-run --change my-feature # Execute specific change
|
|
122
|
-
ralph-run --max-iterations 100
|
|
175
|
+
ralph-run --change my-feature --max-iterations 100
|
|
176
|
+
ralph-run --change my-feature --no-commit # Run without auto-committing
|
|
123
177
|
ralph-run --verbose # Run with debug output
|
|
178
|
+
ralph-run --status # Check status of the active loop
|
|
179
|
+
ralph-run --add-context "Focus on error handling in module X"
|
|
180
|
+
ralph-run --clear-context # Remove pending context
|
|
124
181
|
|
|
125
182
|
PREREQUISITES:
|
|
126
183
|
- Git repository (git init)
|
|
127
|
-
- OpenSpec artifacts created (openspec init,
|
|
128
|
-
-
|
|
129
|
-
- opencode CLI installed (npm install -g opencode)
|
|
184
|
+
- OpenSpec artifacts created (openspec init, openspec new, openspec ff)
|
|
185
|
+
- opencode CLI installed (npm install -g opencode-ai)
|
|
130
186
|
|
|
131
187
|
EOF
|
|
132
188
|
}
|
|
@@ -142,10 +198,26 @@ parse_arguments() {
|
|
|
142
198
|
MAX_ITERATIONS="$2"
|
|
143
199
|
shift 2
|
|
144
200
|
;;
|
|
201
|
+
--no-commit)
|
|
202
|
+
NO_COMMIT=true
|
|
203
|
+
shift
|
|
204
|
+
;;
|
|
145
205
|
--verbose|-v)
|
|
146
206
|
VERBOSE=true
|
|
147
207
|
shift
|
|
148
208
|
;;
|
|
209
|
+
--status)
|
|
210
|
+
SHOW_STATUS=true
|
|
211
|
+
shift
|
|
212
|
+
;;
|
|
213
|
+
--add-context)
|
|
214
|
+
ADD_CONTEXT="$2"
|
|
215
|
+
shift 2
|
|
216
|
+
;;
|
|
217
|
+
--clear-context)
|
|
218
|
+
CLEAR_CONTEXT=true
|
|
219
|
+
shift
|
|
220
|
+
;;
|
|
149
221
|
--help|-h)
|
|
150
222
|
SHOW_HELP=true
|
|
151
223
|
shift
|
|
@@ -193,21 +265,28 @@ validate_git_repository() {
|
|
|
193
265
|
validate_dependencies() {
|
|
194
266
|
log_verbose "Validating dependencies..."
|
|
195
267
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
log_error "
|
|
199
|
-
log_error "Please install open-ralph-wiggum: npm install -g @th0rgal/ralph-wiggum"
|
|
268
|
+
if ! resolve_ralph_command; then
|
|
269
|
+
log_error "Internal mini Ralph runtime not found: $MINI_RALPH_CLI"
|
|
270
|
+
log_error "Ensure node is installed and spec-and-loop dependencies are up to date (npm install)."
|
|
200
271
|
exit 1
|
|
201
272
|
fi
|
|
202
|
-
log_verbose "Found:
|
|
273
|
+
log_verbose "Found internal mini Ralph runtime: $MINI_RALPH_CLI"
|
|
203
274
|
|
|
204
275
|
# Check for opencode
|
|
205
276
|
if ! command -v opencode &> /dev/null; then
|
|
206
277
|
log_error "opencode CLI not found."
|
|
207
|
-
log_error "Please install opencode: npm install -g opencode"
|
|
278
|
+
log_error "Please install opencode: npm install -g opencode-ai"
|
|
208
279
|
exit 1
|
|
209
280
|
fi
|
|
210
281
|
log_verbose "Found: opencode"
|
|
282
|
+
|
|
283
|
+
# Check for jq
|
|
284
|
+
if ! command -v jq &> /dev/null; then
|
|
285
|
+
log_error "jq CLI not found."
|
|
286
|
+
log_error "Please install jq: brew install jq / apt-get install jq"
|
|
287
|
+
exit 1
|
|
288
|
+
fi
|
|
289
|
+
log_verbose "Found: jq"
|
|
211
290
|
|
|
212
291
|
log_verbose "All dependencies validated"
|
|
213
292
|
}
|
|
@@ -388,7 +467,7 @@ parse_tasks() {
|
|
|
388
467
|
((line_number++)) || true
|
|
389
468
|
|
|
390
469
|
if [[ "$line" == "- [ ]"* ]]; then
|
|
391
|
-
local task_desc="${line#- [ ] }"
|
|
470
|
+
local task_desc="${line#- \[ \] }"
|
|
392
471
|
TASKS+=("$task_desc")
|
|
393
472
|
TASK_IDS+=("$line_number")
|
|
394
473
|
log_verbose "Found incomplete task (line $line_number): $task_desc"
|
|
@@ -626,7 +705,7 @@ sync_tasks_to_ralph() {
|
|
|
626
705
|
rm "$old_ralph_tasks_file"
|
|
627
706
|
fi
|
|
628
707
|
|
|
629
|
-
# Use symlink so
|
|
708
|
+
# Use a symlink so the loop runtime always works against the OpenSpec tasks file
|
|
630
709
|
# Ensure parent directory for ralph_tasks_file exists
|
|
631
710
|
mkdir -p "$(dirname "$ralph_tasks_file")"
|
|
632
711
|
|
|
@@ -678,6 +757,10 @@ Include full context from openspec artifacts in {{change_dir}}:
|
|
|
678
757
|
|
|
679
758
|
{{tasks}}
|
|
680
759
|
|
|
760
|
+
## Fresh Task Context
|
|
761
|
+
|
|
762
|
+
{{task_context}}
|
|
763
|
+
|
|
681
764
|
## Instructions
|
|
682
765
|
|
|
683
766
|
1. **Identify** current task:
|
|
@@ -685,15 +768,15 @@ Include full context from openspec artifacts in {{change_dir}}:
|
|
|
685
768
|
- If no task is in progress, pick the first task marked as [ ] (incomplete)
|
|
686
769
|
- Mark the task as [/] in the tasks file before starting work
|
|
687
770
|
|
|
688
|
-
2. **Implement** task
|
|
689
|
-
-
|
|
690
|
-
-
|
|
691
|
-
-
|
|
692
|
-
- The openspec-apply-change skill will implement changes and update task status automatically
|
|
771
|
+
2. **Implement** the current task directly:
|
|
772
|
+
- Read the relevant OpenSpec artifacts for context (proposal.md, design.md, specs)
|
|
773
|
+
- Make the smallest maintainable change that fully satisfies the current task
|
|
774
|
+
- Run the most relevant validation or tests for the task before claiming completion
|
|
693
775
|
|
|
694
776
|
3. **Complete** task:
|
|
695
777
|
- Verify that the implementation meets the requirements
|
|
696
778
|
- When the task is successfully completed, mark it as [x] in the tasks file
|
|
779
|
+
- Create a git commit using the required format below
|
|
697
780
|
- Output: `<promise>{{task_promise}}</promise>`
|
|
698
781
|
|
|
699
782
|
4. **Continue** to the next task:
|
|
@@ -703,7 +786,9 @@ Include full context from openspec artifacts in {{change_dir}}:
|
|
|
703
786
|
## Critical Rules
|
|
704
787
|
|
|
705
788
|
- Work on ONE task at a time from the task list
|
|
706
|
-
-
|
|
789
|
+
- Read the full tasks file every iteration; do not rely on memory from prior iterations
|
|
790
|
+
- Do not rely on editor-specific slash commands or local-only skills; follow this prompt directly
|
|
791
|
+
- Treat tasks.md as the only source of truth for task state
|
|
707
792
|
- ONLY output `<promise>{{task_promise}}</promise>` when the current task is complete and marked as [x]
|
|
708
793
|
- ONLY output `<promise>{{completion_promise}}</promise>` when ALL tasks are [x]
|
|
709
794
|
- Output promise tags DIRECTLY - do not quote them, explain them, or say you "will" output them
|
|
@@ -815,12 +900,12 @@ get_current_task_context() {
|
|
|
815
900
|
while IFS= read -r line; do
|
|
816
901
|
if [[ "$line" =~ ^-\ \[/\] ]]; then
|
|
817
902
|
# Found in-progress task - extract description
|
|
818
|
-
current_task_desc="${line#- [
|
|
903
|
+
current_task_desc="${line#- \[/\] }"
|
|
819
904
|
found_task=true
|
|
820
905
|
break
|
|
821
906
|
elif [[ "$line" =~ ^-\ \[\ \] ]] && [[ "$found_task" == "false" ]]; then
|
|
822
907
|
# Found incomplete task - extract description
|
|
823
|
-
current_task_desc="${line#- [ ] }"
|
|
908
|
+
current_task_desc="${line#- \[ \] }"
|
|
824
909
|
found_task=true
|
|
825
910
|
break
|
|
826
911
|
fi
|
|
@@ -854,11 +939,9 @@ setup_output_capture() {
|
|
|
854
939
|
|
|
855
940
|
log_verbose "Setting up output capture..."
|
|
856
941
|
|
|
857
|
-
#
|
|
858
|
-
local
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
mkdir -p "$output_dir"
|
|
942
|
+
# Use the system temp directory so macOS and Linux both work naturally.
|
|
943
|
+
local output_dir
|
|
944
|
+
output_dir=$(make_temp_dir "ralph-run")
|
|
862
945
|
log_info "Output directory: $output_dir"
|
|
863
946
|
|
|
864
947
|
# Store output directory path in Ralph directory for reference
|
|
@@ -870,8 +953,11 @@ setup_output_capture() {
|
|
|
870
953
|
cleanup_old_output() {
|
|
871
954
|
log_verbose "Cleaning up old Ralph output directories..."
|
|
872
955
|
|
|
956
|
+
local temp_root
|
|
957
|
+
temp_root=$(get_temp_root)
|
|
958
|
+
|
|
873
959
|
# Keep last 3 Ralph output directories, delete older ones
|
|
874
|
-
find
|
|
960
|
+
find "$temp_root" -type d -name "ralph-run*" -mtime +7 2>/dev/null | while IFS= read -r old_dir; do
|
|
875
961
|
log_verbose "Removing old output directory: $old_dir"
|
|
876
962
|
rm -rf "$old_dir"
|
|
877
963
|
done
|
|
@@ -881,14 +967,18 @@ execute_ralph_loop() {
|
|
|
881
967
|
local change_dir="$1"
|
|
882
968
|
local ralph_dir="$2"
|
|
883
969
|
local max_iterations="${3:-50}"
|
|
970
|
+
local no_commit="${4:-false}"
|
|
884
971
|
|
|
885
|
-
log_info "Starting Ralph
|
|
972
|
+
log_info "Starting internal mini Ralph loop..."
|
|
886
973
|
log_info "Max iterations: $max_iterations"
|
|
887
974
|
log_info "Change directory: $change_dir"
|
|
975
|
+
if [[ "$no_commit" == true ]]; then
|
|
976
|
+
log_info "Auto-commit disabled (--no-commit)"
|
|
977
|
+
fi
|
|
888
978
|
|
|
889
|
-
if !
|
|
890
|
-
log_error "
|
|
891
|
-
log_error "
|
|
979
|
+
if ! resolve_ralph_command; then
|
|
980
|
+
log_error "Internal mini Ralph runtime not found: $MINI_RALPH_CLI"
|
|
981
|
+
log_error "Ensure node is installed and spec-and-loop dependencies are up to date (npm install)."
|
|
892
982
|
return 1
|
|
893
983
|
fi
|
|
894
984
|
|
|
@@ -906,51 +996,132 @@ execute_ralph_loop() {
|
|
|
906
996
|
prd_content=$(generate_prd "$change_dir")
|
|
907
997
|
echo "$prd_content" > "$ralph_dir/PRD.md"
|
|
908
998
|
|
|
909
|
-
# Get current task context for Ralph to use in commits
|
|
910
|
-
local task_context
|
|
911
|
-
task_context=$(get_current_task_context "$change_dir")
|
|
912
|
-
|
|
913
|
-
# Create context injection file for Ralph
|
|
914
|
-
local context_file="$ralph_dir/.context_injection"
|
|
915
|
-
if [[ -n "$task_context" ]]; then
|
|
916
|
-
log_verbose "Writing task context injection..."
|
|
917
|
-
echo "$task_context" > "$context_file"
|
|
918
|
-
fi
|
|
919
|
-
|
|
920
|
-
# Restore Ralph state from tasks.md before running
|
|
921
|
-
restore_ralph_state_from_tasks "$change_dir/tasks.md"
|
|
922
|
-
|
|
923
|
-
# Initialize context injections file for Ralph to read context
|
|
924
|
-
initialize_context_injections "$ralph_dir"
|
|
925
|
-
|
|
926
999
|
# Output files
|
|
927
1000
|
local stdout_log="$output_dir/ralph-stdout.log"
|
|
928
1001
|
local stderr_log="$output_dir/ralph-stderr.log"
|
|
929
1002
|
|
|
930
|
-
log_info "
|
|
1003
|
+
log_info "Invoking internal mini Ralph runtime..."
|
|
931
1004
|
log_info "Capturing output to: $output_dir"
|
|
932
1005
|
|
|
933
|
-
#
|
|
1006
|
+
# Build the mini-ralph-cli arguments
|
|
1007
|
+
local mini_ralph_args=(
|
|
1008
|
+
"--prompt-file" "$ralph_dir/PRD.md"
|
|
1009
|
+
"--prompt-template" "$template_file"
|
|
1010
|
+
"--ralph-dir" "$ralph_dir"
|
|
1011
|
+
"--tasks-file" "$change_dir/tasks.md"
|
|
1012
|
+
"--tasks"
|
|
1013
|
+
"--max-iterations" "$max_iterations"
|
|
1014
|
+
)
|
|
1015
|
+
|
|
1016
|
+
if [[ "$no_commit" == true ]]; then
|
|
1017
|
+
mini_ralph_args+=("--no-commit")
|
|
1018
|
+
fi
|
|
1019
|
+
|
|
1020
|
+
if [[ "$VERBOSE" == true ]]; then
|
|
1021
|
+
mini_ralph_args+=("--verbose")
|
|
1022
|
+
fi
|
|
1023
|
+
|
|
1024
|
+
# Run the internal mini Ralph CLI and capture output
|
|
934
1025
|
{
|
|
935
|
-
|
|
936
|
-
--agent opencode \
|
|
937
|
-
--tasks \
|
|
938
|
-
--max-iterations "$max_iterations" \
|
|
939
|
-
--prompt-template "$template_file" \
|
|
940
|
-
--verbose-tools
|
|
1026
|
+
node "$MINI_RALPH_CLI" "${mini_ralph_args[@]}"
|
|
941
1027
|
} > >(tee "$stdout_log") 2> >(tee "$stderr_log")
|
|
942
1028
|
|
|
943
1029
|
return $?
|
|
944
1030
|
}
|
|
945
1031
|
|
|
946
1032
|
|
|
1033
|
+
# ---------------------------------------------------------------------------
|
|
1034
|
+
# Observability and control commands
|
|
1035
|
+
#
|
|
1036
|
+
# These commands delegate to the internal mini-ralph-cli.js for status,
|
|
1037
|
+
# context management, and other loop controls without running the full loop.
|
|
1038
|
+
# ---------------------------------------------------------------------------
|
|
1039
|
+
|
|
1040
|
+
run_observability_command() {
|
|
1041
|
+
local change_name="$1"
|
|
1042
|
+
local command="$2"
|
|
1043
|
+
local arg="$3"
|
|
1044
|
+
|
|
1045
|
+
if [[ ! -f "$MINI_RALPH_CLI" ]]; then
|
|
1046
|
+
log_error "Internal mini-ralph-cli.js not found: $MINI_RALPH_CLI"
|
|
1047
|
+
exit 1
|
|
1048
|
+
fi
|
|
1049
|
+
|
|
1050
|
+
if [[ ! -x "$(command -v node)" ]]; then
|
|
1051
|
+
log_error "node is required but not found in PATH."
|
|
1052
|
+
exit 1
|
|
1053
|
+
fi
|
|
1054
|
+
|
|
1055
|
+
local change_dir="openspec/changes/$change_name"
|
|
1056
|
+
local ralph_dir="$change_dir/.ralph"
|
|
1057
|
+
|
|
1058
|
+
case "$command" in
|
|
1059
|
+
status)
|
|
1060
|
+
local tasks_file="$change_dir/tasks.md"
|
|
1061
|
+
local tasks_arg=""
|
|
1062
|
+
if [[ -f "$tasks_file" ]]; then
|
|
1063
|
+
tasks_arg="--tasks-file $tasks_file"
|
|
1064
|
+
fi
|
|
1065
|
+
# shellcheck disable=SC2086
|
|
1066
|
+
node "$MINI_RALPH_CLI" --ralph-dir "$ralph_dir" --status $tasks_arg
|
|
1067
|
+
;;
|
|
1068
|
+
add-context)
|
|
1069
|
+
node "$MINI_RALPH_CLI" --ralph-dir "$ralph_dir" --add-context "$arg"
|
|
1070
|
+
;;
|
|
1071
|
+
clear-context)
|
|
1072
|
+
node "$MINI_RALPH_CLI" --ralph-dir "$ralph_dir" --clear-context
|
|
1073
|
+
;;
|
|
1074
|
+
*)
|
|
1075
|
+
log_error "Unknown observability command: $command"
|
|
1076
|
+
exit 1
|
|
1077
|
+
;;
|
|
1078
|
+
esac
|
|
1079
|
+
}
|
|
947
1080
|
|
|
948
1081
|
main() {
|
|
1082
|
+
set -e
|
|
949
1083
|
parse_arguments "$@"
|
|
950
1084
|
|
|
951
1085
|
log_verbose "Starting ralph-run v$VERSION"
|
|
952
1086
|
log_verbose "Change name: ${CHANGE_NAME:-<auto-detect>}"
|
|
953
|
-
|
|
1087
|
+
|
|
1088
|
+
# Resolve change name first for observability commands that need it
|
|
1089
|
+
if [[ -z "$CHANGE_NAME" ]] && ( [[ "$SHOW_STATUS" == true ]] || [[ -n "$ADD_CONTEXT" ]] || [[ "$CLEAR_CONTEXT" == true ]] ); then
|
|
1090
|
+
validate_git_repository
|
|
1091
|
+
CHANGE_NAME=$(auto_detect_change)
|
|
1092
|
+
log_verbose "Auto-detected change for observability: $CHANGE_NAME"
|
|
1093
|
+
fi
|
|
1094
|
+
|
|
1095
|
+
# Handle observability commands (status, add-context, clear-context)
|
|
1096
|
+
# These exit early without running the full loop.
|
|
1097
|
+
if [[ "$SHOW_STATUS" == true ]]; then
|
|
1098
|
+
if [[ -z "$CHANGE_NAME" ]]; then
|
|
1099
|
+
validate_git_repository
|
|
1100
|
+
CHANGE_NAME=$(auto_detect_change)
|
|
1101
|
+
fi
|
|
1102
|
+
run_observability_command "$CHANGE_NAME" "status"
|
|
1103
|
+
exit $?
|
|
1104
|
+
fi
|
|
1105
|
+
|
|
1106
|
+
if [[ -n "$ADD_CONTEXT" ]]; then
|
|
1107
|
+
if [[ -z "$CHANGE_NAME" ]]; then
|
|
1108
|
+
validate_git_repository
|
|
1109
|
+
CHANGE_NAME=$(auto_detect_change)
|
|
1110
|
+
fi
|
|
1111
|
+
run_observability_command "$CHANGE_NAME" "add-context" "$ADD_CONTEXT"
|
|
1112
|
+
exit $?
|
|
1113
|
+
fi
|
|
1114
|
+
|
|
1115
|
+
if [[ "$CLEAR_CONTEXT" == true ]]; then
|
|
1116
|
+
if [[ -z "$CHANGE_NAME" ]]; then
|
|
1117
|
+
validate_git_repository
|
|
1118
|
+
CHANGE_NAME=$(auto_detect_change)
|
|
1119
|
+
fi
|
|
1120
|
+
run_observability_command "$CHANGE_NAME" "clear-context"
|
|
1121
|
+
exit $?
|
|
1122
|
+
fi
|
|
1123
|
+
|
|
1124
|
+
# Normal loop execution path
|
|
954
1125
|
validate_git_repository
|
|
955
1126
|
validate_dependencies
|
|
956
1127
|
|
|
@@ -978,9 +1149,11 @@ main() {
|
|
|
978
1149
|
|
|
979
1150
|
local max_iterations="${MAX_ITERATIONS:-50}"
|
|
980
1151
|
|
|
981
|
-
execute_ralph_loop "$change_dir" "$ralph_dir" "$max_iterations"
|
|
1152
|
+
execute_ralph_loop "$change_dir" "$ralph_dir" "$max_iterations" "$NO_COMMIT"
|
|
982
1153
|
|
|
983
1154
|
log_info "ralph-run.sh initialized successfully"
|
|
984
1155
|
}
|
|
985
1156
|
|
|
986
|
-
|
|
1157
|
+
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
1158
|
+
main "$@"
|
|
1159
|
+
fi
|
package/scripts/setup.js
CHANGED
|
@@ -4,40 +4,48 @@ const fs = require('fs');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const { execSync } = require('child_process');
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
function runSetup() {
|
|
8
|
+
console.log('Setting up spec-and-loop...');
|
|
8
9
|
|
|
9
|
-
// Get the installation directory
|
|
10
|
-
const installDir = __dirname;
|
|
11
|
-
const ralphRunScript = path.join(installDir, 'ralph-run.sh');
|
|
10
|
+
// Get the installation directory
|
|
11
|
+
const installDir = __dirname;
|
|
12
|
+
const ralphRunScript = path.join(installDir, 'ralph-run.sh');
|
|
12
13
|
|
|
13
|
-
console.log(`Installation directory: ${installDir}`);
|
|
14
|
+
console.log(`Installation directory: ${installDir}`);
|
|
14
15
|
|
|
15
|
-
// Make ralph-run.sh executable
|
|
16
|
-
if (fs.existsSync(ralphRunScript)) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
// Make ralph-run.sh executable
|
|
17
|
+
if (fs.existsSync(ralphRunScript)) {
|
|
18
|
+
try {
|
|
19
|
+
execSync(`chmod +x "${ralphRunScript}"`, { stdio: 'inherit' });
|
|
20
|
+
console.log('✓ Made ralph-run.sh executable');
|
|
21
|
+
} catch (err) {
|
|
22
|
+
console.warn(`Could not make ralph-run.sh executable: ${err.message}`);
|
|
23
|
+
}
|
|
24
|
+
} else {
|
|
25
|
+
console.error(`Error: ralph-run.sh not found at ${ralphRunScript}`);
|
|
26
|
+
process.exit(1);
|
|
22
27
|
}
|
|
23
|
-
|
|
24
|
-
console.
|
|
25
|
-
|
|
28
|
+
|
|
29
|
+
console.log('');
|
|
30
|
+
console.log('spec-and-loop setup complete!');
|
|
31
|
+
console.log('');
|
|
32
|
+
console.log('Usage:');
|
|
33
|
+
console.log(' cd /path/to/your/project');
|
|
34
|
+
console.log(' openspec init # Initialize OpenSpec');
|
|
35
|
+
console.log(' openspec new <name> # Create a new change');
|
|
36
|
+
console.log(' openspec ff <name> # Fast-forward artifacts');
|
|
37
|
+
console.log(' ralph-run --change <name> # Run ralph loop');
|
|
38
|
+
console.log(' ralph-run # Auto-detect change');
|
|
39
|
+
console.log('');
|
|
40
|
+
console.log('Prerequisites:');
|
|
41
|
+
console.log(' - openspec CLI: npm install -g openspec');
|
|
42
|
+
console.log(' - opencode CLI: npm install -g opencode-ai');
|
|
43
|
+
console.log(' - jq CLI: apt install jq / brew install jq');
|
|
44
|
+
console.log(' - git: git init');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (require.main === module) {
|
|
48
|
+
runSetup();
|
|
26
49
|
}
|
|
27
50
|
|
|
28
|
-
|
|
29
|
-
console.log('spec-and-loop setup complete!');
|
|
30
|
-
console.log('');
|
|
31
|
-
console.log('Usage:');
|
|
32
|
-
console.log(' cd /path/to/your/project');
|
|
33
|
-
console.log(' openspec init # Initialize OpenSpec');
|
|
34
|
-
console.log(' openspec new <name> # Create a new change');
|
|
35
|
-
console.log(' openspec ff <name> # Fast-forward artifacts');
|
|
36
|
-
console.log(' ralph-run --change <name> # Run ralph loop');
|
|
37
|
-
console.log(' ralph-run # Auto-detect change');
|
|
38
|
-
console.log('');
|
|
39
|
-
console.log('Prerequisites:');
|
|
40
|
-
console.log(' - openspec CLI: npm install -g openspec');
|
|
41
|
-
console.log(' - opencode CLI: npm install -g opencode');
|
|
42
|
-
console.log(' - jq CLI: apt install jq / brew install jq');
|
|
43
|
-
console.log(' - git: git init');
|
|
51
|
+
module.exports = { runSetup };
|