spec-and-loop 1.0.7 → 1.2.0
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 +265 -53
- package/README.md +210 -175
- package/lib/mini-ralph/context.js +99 -0
- package/lib/mini-ralph/errors.js +112 -0
- package/lib/mini-ralph/history.js +99 -0
- package/lib/mini-ralph/index.js +93 -0
- package/lib/mini-ralph/invoker.js +243 -0
- package/lib/mini-ralph/prompt.js +116 -0
- package/lib/mini-ralph/runner.js +495 -0
- package/lib/mini-ralph/state.js +93 -0
- package/lib/mini-ralph/status.js +238 -0
- package/lib/mini-ralph/tasks.js +209 -0
- package/package.json +3 -3
- package/scripts/mini-ralph-cli.js +226 -0
- package/scripts/ralph-monitor.sh +53 -6
- package/scripts/ralph-run.sh +248 -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"
|
|
@@ -417,6 +496,10 @@ check_tasks_modified() {
|
|
|
417
496
|
return 1
|
|
418
497
|
}
|
|
419
498
|
|
|
499
|
+
# DEPRECATED: The following functions are superseded by lib/mini-ralph/errors.js.
|
|
500
|
+
# Do not add new callers. These will be removed in a future cleanup.
|
|
501
|
+
# See: lib/mini-ralph/errors.js for the current implementation.
|
|
502
|
+
|
|
420
503
|
format_error_entry() {
|
|
421
504
|
local task_id="$1"
|
|
422
505
|
local task_description="$2"
|
|
@@ -626,7 +709,7 @@ sync_tasks_to_ralph() {
|
|
|
626
709
|
rm "$old_ralph_tasks_file"
|
|
627
710
|
fi
|
|
628
711
|
|
|
629
|
-
# Use symlink so
|
|
712
|
+
# Use a symlink so the loop runtime always works against the OpenSpec tasks file
|
|
630
713
|
# Ensure parent directory for ralph_tasks_file exists
|
|
631
714
|
mkdir -p "$(dirname "$ralph_tasks_file")"
|
|
632
715
|
|
|
@@ -678,6 +761,10 @@ Include full context from openspec artifacts in {{change_dir}}:
|
|
|
678
761
|
|
|
679
762
|
{{tasks}}
|
|
680
763
|
|
|
764
|
+
## Fresh Task Context
|
|
765
|
+
|
|
766
|
+
{{task_context}}
|
|
767
|
+
|
|
681
768
|
## Instructions
|
|
682
769
|
|
|
683
770
|
1. **Identify** current task:
|
|
@@ -685,15 +772,15 @@ Include full context from openspec artifacts in {{change_dir}}:
|
|
|
685
772
|
- If no task is in progress, pick the first task marked as [ ] (incomplete)
|
|
686
773
|
- Mark the task as [/] in the tasks file before starting work
|
|
687
774
|
|
|
688
|
-
2. **Implement** task
|
|
689
|
-
-
|
|
690
|
-
-
|
|
691
|
-
-
|
|
692
|
-
- The openspec-apply-change skill will implement changes and update task status automatically
|
|
775
|
+
2. **Implement** the current task directly:
|
|
776
|
+
- Read the relevant OpenSpec artifacts for context (proposal.md, design.md, specs)
|
|
777
|
+
- Make the smallest maintainable change that fully satisfies the current task
|
|
778
|
+
- Run the most relevant validation or tests for the task before claiming completion
|
|
693
779
|
|
|
694
780
|
3. **Complete** task:
|
|
695
781
|
- Verify that the implementation meets the requirements
|
|
696
782
|
- When the task is successfully completed, mark it as [x] in the tasks file
|
|
783
|
+
- Create a git commit using the required format below
|
|
697
784
|
- Output: `<promise>{{task_promise}}</promise>`
|
|
698
785
|
|
|
699
786
|
4. **Continue** to the next task:
|
|
@@ -703,7 +790,9 @@ Include full context from openspec artifacts in {{change_dir}}:
|
|
|
703
790
|
## Critical Rules
|
|
704
791
|
|
|
705
792
|
- Work on ONE task at a time from the task list
|
|
706
|
-
-
|
|
793
|
+
- Read the full tasks file every iteration; do not rely on memory from prior iterations
|
|
794
|
+
- Do not rely on editor-specific slash commands or local-only skills; follow this prompt directly
|
|
795
|
+
- Treat tasks.md as the only source of truth for task state
|
|
707
796
|
- ONLY output `<promise>{{task_promise}}</promise>` when the current task is complete and marked as [x]
|
|
708
797
|
- ONLY output `<promise>{{completion_promise}}</promise>` when ALL tasks are [x]
|
|
709
798
|
- Output promise tags DIRECTLY - do not quote them, explain them, or say you "will" output them
|
|
@@ -815,12 +904,12 @@ get_current_task_context() {
|
|
|
815
904
|
while IFS= read -r line; do
|
|
816
905
|
if [[ "$line" =~ ^-\ \[/\] ]]; then
|
|
817
906
|
# Found in-progress task - extract description
|
|
818
|
-
current_task_desc="${line#- [
|
|
907
|
+
current_task_desc="${line#- \[/\] }"
|
|
819
908
|
found_task=true
|
|
820
909
|
break
|
|
821
910
|
elif [[ "$line" =~ ^-\ \[\ \] ]] && [[ "$found_task" == "false" ]]; then
|
|
822
911
|
# Found incomplete task - extract description
|
|
823
|
-
current_task_desc="${line#- [ ] }"
|
|
912
|
+
current_task_desc="${line#- \[ \] }"
|
|
824
913
|
found_task=true
|
|
825
914
|
break
|
|
826
915
|
fi
|
|
@@ -854,11 +943,9 @@ setup_output_capture() {
|
|
|
854
943
|
|
|
855
944
|
log_verbose "Setting up output capture..."
|
|
856
945
|
|
|
857
|
-
#
|
|
858
|
-
local
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
mkdir -p "$output_dir"
|
|
946
|
+
# Use the system temp directory so macOS and Linux both work naturally.
|
|
947
|
+
local output_dir
|
|
948
|
+
output_dir=$(make_temp_dir "ralph-run")
|
|
862
949
|
log_info "Output directory: $output_dir"
|
|
863
950
|
|
|
864
951
|
# Store output directory path in Ralph directory for reference
|
|
@@ -870,8 +957,11 @@ setup_output_capture() {
|
|
|
870
957
|
cleanup_old_output() {
|
|
871
958
|
log_verbose "Cleaning up old Ralph output directories..."
|
|
872
959
|
|
|
960
|
+
local temp_root
|
|
961
|
+
temp_root=$(get_temp_root)
|
|
962
|
+
|
|
873
963
|
# Keep last 3 Ralph output directories, delete older ones
|
|
874
|
-
find
|
|
964
|
+
find "$temp_root" -type d -name "ralph-run*" -mtime +7 2>/dev/null | while IFS= read -r old_dir; do
|
|
875
965
|
log_verbose "Removing old output directory: $old_dir"
|
|
876
966
|
rm -rf "$old_dir"
|
|
877
967
|
done
|
|
@@ -881,14 +971,18 @@ execute_ralph_loop() {
|
|
|
881
971
|
local change_dir="$1"
|
|
882
972
|
local ralph_dir="$2"
|
|
883
973
|
local max_iterations="${3:-50}"
|
|
974
|
+
local no_commit="${4:-false}"
|
|
884
975
|
|
|
885
|
-
log_info "Starting Ralph
|
|
976
|
+
log_info "Starting internal mini Ralph loop..."
|
|
886
977
|
log_info "Max iterations: $max_iterations"
|
|
887
978
|
log_info "Change directory: $change_dir"
|
|
979
|
+
if [[ "$no_commit" == true ]]; then
|
|
980
|
+
log_info "Auto-commit disabled (--no-commit)"
|
|
981
|
+
fi
|
|
888
982
|
|
|
889
|
-
if !
|
|
890
|
-
log_error "
|
|
891
|
-
log_error "
|
|
983
|
+
if ! resolve_ralph_command; then
|
|
984
|
+
log_error "Internal mini Ralph runtime not found: $MINI_RALPH_CLI"
|
|
985
|
+
log_error "Ensure node is installed and spec-and-loop dependencies are up to date (npm install)."
|
|
892
986
|
return 1
|
|
893
987
|
fi
|
|
894
988
|
|
|
@@ -906,51 +1000,132 @@ execute_ralph_loop() {
|
|
|
906
1000
|
prd_content=$(generate_prd "$change_dir")
|
|
907
1001
|
echo "$prd_content" > "$ralph_dir/PRD.md"
|
|
908
1002
|
|
|
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
1003
|
# Output files
|
|
927
1004
|
local stdout_log="$output_dir/ralph-stdout.log"
|
|
928
1005
|
local stderr_log="$output_dir/ralph-stderr.log"
|
|
929
1006
|
|
|
930
|
-
log_info "
|
|
1007
|
+
log_info "Invoking internal mini Ralph runtime..."
|
|
931
1008
|
log_info "Capturing output to: $output_dir"
|
|
932
1009
|
|
|
933
|
-
#
|
|
1010
|
+
# Build the mini-ralph-cli arguments
|
|
1011
|
+
local mini_ralph_args=(
|
|
1012
|
+
"--prompt-file" "$ralph_dir/PRD.md"
|
|
1013
|
+
"--prompt-template" "$template_file"
|
|
1014
|
+
"--ralph-dir" "$ralph_dir"
|
|
1015
|
+
"--tasks-file" "$change_dir/tasks.md"
|
|
1016
|
+
"--tasks"
|
|
1017
|
+
"--max-iterations" "$max_iterations"
|
|
1018
|
+
)
|
|
1019
|
+
|
|
1020
|
+
if [[ "$no_commit" == true ]]; then
|
|
1021
|
+
mini_ralph_args+=("--no-commit")
|
|
1022
|
+
fi
|
|
1023
|
+
|
|
1024
|
+
if [[ "$VERBOSE" == true ]]; then
|
|
1025
|
+
mini_ralph_args+=("--verbose")
|
|
1026
|
+
fi
|
|
1027
|
+
|
|
1028
|
+
# Run the internal mini Ralph CLI and capture output
|
|
934
1029
|
{
|
|
935
|
-
|
|
936
|
-
--agent opencode \
|
|
937
|
-
--tasks \
|
|
938
|
-
--max-iterations "$max_iterations" \
|
|
939
|
-
--prompt-template "$template_file" \
|
|
940
|
-
--verbose-tools
|
|
1030
|
+
node "$MINI_RALPH_CLI" "${mini_ralph_args[@]}"
|
|
941
1031
|
} > >(tee "$stdout_log") 2> >(tee "$stderr_log")
|
|
942
1032
|
|
|
943
1033
|
return $?
|
|
944
1034
|
}
|
|
945
1035
|
|
|
946
1036
|
|
|
1037
|
+
# ---------------------------------------------------------------------------
|
|
1038
|
+
# Observability and control commands
|
|
1039
|
+
#
|
|
1040
|
+
# These commands delegate to the internal mini-ralph-cli.js for status,
|
|
1041
|
+
# context management, and other loop controls without running the full loop.
|
|
1042
|
+
# ---------------------------------------------------------------------------
|
|
1043
|
+
|
|
1044
|
+
run_observability_command() {
|
|
1045
|
+
local change_name="$1"
|
|
1046
|
+
local command="$2"
|
|
1047
|
+
local arg="$3"
|
|
1048
|
+
|
|
1049
|
+
if [[ ! -f "$MINI_RALPH_CLI" ]]; then
|
|
1050
|
+
log_error "Internal mini-ralph-cli.js not found: $MINI_RALPH_CLI"
|
|
1051
|
+
exit 1
|
|
1052
|
+
fi
|
|
1053
|
+
|
|
1054
|
+
if [[ ! -x "$(command -v node)" ]]; then
|
|
1055
|
+
log_error "node is required but not found in PATH."
|
|
1056
|
+
exit 1
|
|
1057
|
+
fi
|
|
1058
|
+
|
|
1059
|
+
local change_dir="openspec/changes/$change_name"
|
|
1060
|
+
local ralph_dir="$change_dir/.ralph"
|
|
1061
|
+
|
|
1062
|
+
case "$command" in
|
|
1063
|
+
status)
|
|
1064
|
+
local tasks_file="$change_dir/tasks.md"
|
|
1065
|
+
local tasks_arg=""
|
|
1066
|
+
if [[ -f "$tasks_file" ]]; then
|
|
1067
|
+
tasks_arg="--tasks-file $tasks_file"
|
|
1068
|
+
fi
|
|
1069
|
+
# shellcheck disable=SC2086
|
|
1070
|
+
node "$MINI_RALPH_CLI" --ralph-dir "$ralph_dir" --status $tasks_arg
|
|
1071
|
+
;;
|
|
1072
|
+
add-context)
|
|
1073
|
+
node "$MINI_RALPH_CLI" --ralph-dir "$ralph_dir" --add-context "$arg"
|
|
1074
|
+
;;
|
|
1075
|
+
clear-context)
|
|
1076
|
+
node "$MINI_RALPH_CLI" --ralph-dir "$ralph_dir" --clear-context
|
|
1077
|
+
;;
|
|
1078
|
+
*)
|
|
1079
|
+
log_error "Unknown observability command: $command"
|
|
1080
|
+
exit 1
|
|
1081
|
+
;;
|
|
1082
|
+
esac
|
|
1083
|
+
}
|
|
947
1084
|
|
|
948
1085
|
main() {
|
|
1086
|
+
set -e
|
|
949
1087
|
parse_arguments "$@"
|
|
950
1088
|
|
|
951
1089
|
log_verbose "Starting ralph-run v$VERSION"
|
|
952
1090
|
log_verbose "Change name: ${CHANGE_NAME:-<auto-detect>}"
|
|
953
|
-
|
|
1091
|
+
|
|
1092
|
+
# Resolve change name first for observability commands that need it
|
|
1093
|
+
if [[ -z "$CHANGE_NAME" ]] && ( [[ "$SHOW_STATUS" == true ]] || [[ -n "$ADD_CONTEXT" ]] || [[ "$CLEAR_CONTEXT" == true ]] ); then
|
|
1094
|
+
validate_git_repository
|
|
1095
|
+
CHANGE_NAME=$(auto_detect_change)
|
|
1096
|
+
log_verbose "Auto-detected change for observability: $CHANGE_NAME"
|
|
1097
|
+
fi
|
|
1098
|
+
|
|
1099
|
+
# Handle observability commands (status, add-context, clear-context)
|
|
1100
|
+
# These exit early without running the full loop.
|
|
1101
|
+
if [[ "$SHOW_STATUS" == true ]]; then
|
|
1102
|
+
if [[ -z "$CHANGE_NAME" ]]; then
|
|
1103
|
+
validate_git_repository
|
|
1104
|
+
CHANGE_NAME=$(auto_detect_change)
|
|
1105
|
+
fi
|
|
1106
|
+
run_observability_command "$CHANGE_NAME" "status"
|
|
1107
|
+
exit $?
|
|
1108
|
+
fi
|
|
1109
|
+
|
|
1110
|
+
if [[ -n "$ADD_CONTEXT" ]]; then
|
|
1111
|
+
if [[ -z "$CHANGE_NAME" ]]; then
|
|
1112
|
+
validate_git_repository
|
|
1113
|
+
CHANGE_NAME=$(auto_detect_change)
|
|
1114
|
+
fi
|
|
1115
|
+
run_observability_command "$CHANGE_NAME" "add-context" "$ADD_CONTEXT"
|
|
1116
|
+
exit $?
|
|
1117
|
+
fi
|
|
1118
|
+
|
|
1119
|
+
if [[ "$CLEAR_CONTEXT" == true ]]; then
|
|
1120
|
+
if [[ -z "$CHANGE_NAME" ]]; then
|
|
1121
|
+
validate_git_repository
|
|
1122
|
+
CHANGE_NAME=$(auto_detect_change)
|
|
1123
|
+
fi
|
|
1124
|
+
run_observability_command "$CHANGE_NAME" "clear-context"
|
|
1125
|
+
exit $?
|
|
1126
|
+
fi
|
|
1127
|
+
|
|
1128
|
+
# Normal loop execution path
|
|
954
1129
|
validate_git_repository
|
|
955
1130
|
validate_dependencies
|
|
956
1131
|
|
|
@@ -978,9 +1153,11 @@ main() {
|
|
|
978
1153
|
|
|
979
1154
|
local max_iterations="${MAX_ITERATIONS:-50}"
|
|
980
1155
|
|
|
981
|
-
execute_ralph_loop "$change_dir" "$ralph_dir" "$max_iterations"
|
|
1156
|
+
execute_ralph_loop "$change_dir" "$ralph_dir" "$max_iterations" "$NO_COMMIT"
|
|
982
1157
|
|
|
983
1158
|
log_info "ralph-run.sh initialized successfully"
|
|
984
1159
|
}
|
|
985
1160
|
|
|
986
|
-
|
|
1161
|
+
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
1162
|
+
main "$@"
|
|
1163
|
+
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 };
|