spec-and-loop 2.1.2 → 3.0.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/OPENSPEC-RALPH-BP.md +564 -0
- package/QUICKSTART.md +32 -10
- package/README.md +70 -6
- package/lib/mini-ralph/history.js +37 -0
- package/lib/mini-ralph/invoker.js +108 -7
- package/lib/mini-ralph/lessons.js +93 -0
- package/lib/mini-ralph/progress.js +404 -0
- package/lib/mini-ralph/prompt.js +78 -6
- package/lib/mini-ralph/runner.js +592 -33
- package/lib/mini-ralph/state.js +57 -5
- package/lib/mini-ralph/tasks.js +5 -10
- package/package.json +4 -4
- package/scripts/mini-ralph-cli.js +18 -2
- package/scripts/ralph-run.sh +402 -79
package/scripts/ralph-run.sh
CHANGED
|
@@ -1,6 +1,27 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
resolve_version() {
|
|
4
|
+
local pkg_json="$SCRIPT_DIR/../package.json"
|
|
5
|
+
if [[ ! -f "$pkg_json" ]]; then
|
|
6
|
+
echo "Error: package.json not found at $pkg_json" >&2
|
|
7
|
+
exit 1
|
|
8
|
+
fi
|
|
9
|
+
if [[ ! -r "$pkg_json" ]]; then
|
|
10
|
+
echo "Error: package.json not readable at $pkg_json" >&2
|
|
11
|
+
exit 1
|
|
12
|
+
fi
|
|
13
|
+
local version
|
|
14
|
+
version=$(node -e "console.log(require('$pkg_json').version)" 2>/dev/null) || {
|
|
15
|
+
echo "Error: Failed to read version from $pkg_json" >&2
|
|
16
|
+
exit 1
|
|
17
|
+
}
|
|
18
|
+
if [[ -z "$version" ]]; then
|
|
19
|
+
echo "Error: Empty version read from $pkg_json" >&2
|
|
20
|
+
exit 1
|
|
21
|
+
fi
|
|
22
|
+
echo "$version"
|
|
23
|
+
}
|
|
24
|
+
VERSION=""
|
|
4
25
|
|
|
5
26
|
# Detect OS for cross-platform compatibility
|
|
6
27
|
detect_os() {
|
|
@@ -55,6 +76,7 @@ get_realpath() {
|
|
|
55
76
|
}
|
|
56
77
|
|
|
57
78
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
79
|
+
VERSION=$(resolve_version)
|
|
58
80
|
LOCAL_NODE_BIN="$SCRIPT_DIR/../node_modules/.bin"
|
|
59
81
|
# Allow tests to inject a mock by setting MINI_RALPH_CLI_OVERRIDE in the environment
|
|
60
82
|
MINI_RALPH_CLI="${MINI_RALPH_CLI_OVERRIDE:-$SCRIPT_DIR/mini-ralph-cli.js}"
|
|
@@ -108,8 +130,10 @@ CHANGE_NAME=""
|
|
|
108
130
|
MAX_ITERATIONS=""
|
|
109
131
|
NO_COMMIT=false
|
|
110
132
|
SHOW_STATUS=false
|
|
133
|
+
SHOW_VERSION=false
|
|
111
134
|
ADD_CONTEXT=""
|
|
112
135
|
CLEAR_CONTEXT=false
|
|
136
|
+
SUBCOMMAND=""
|
|
113
137
|
ERROR_OCCURRED=false
|
|
114
138
|
CLEANUP_IN_PROGRESS=false
|
|
115
139
|
|
|
@@ -148,6 +172,7 @@ handle_error() {
|
|
|
148
172
|
fi
|
|
149
173
|
}
|
|
150
174
|
VERBOSE=false
|
|
175
|
+
QUIET=false
|
|
151
176
|
SHOW_HELP=false
|
|
152
177
|
|
|
153
178
|
usage() {
|
|
@@ -162,8 +187,13 @@ OPTIONS:
|
|
|
162
187
|
--max-iterations <n> Maximum iterations for Ralph loop (default: 50)
|
|
163
188
|
--no-commit Suppress automatic git commits during the loop
|
|
164
189
|
--verbose, -v Enable verbose mode for debugging
|
|
190
|
+
--quiet Suppress the per-iteration progress stream
|
|
191
|
+
--version Print the version and exit
|
|
165
192
|
--help, -h Show this help message
|
|
166
193
|
|
|
194
|
+
SUBCOMMANDS:
|
|
195
|
+
init Configure the project for Ralph-friendly artifact generation
|
|
196
|
+
|
|
167
197
|
OBSERVABILITY AND CONTROL:
|
|
168
198
|
--status Print the current loop status dashboard and exit
|
|
169
199
|
--add-context <text> Add pending context to inject into the next iteration and exit
|
|
@@ -206,6 +236,10 @@ parse_arguments() {
|
|
|
206
236
|
VERBOSE=true
|
|
207
237
|
shift
|
|
208
238
|
;;
|
|
239
|
+
--quiet)
|
|
240
|
+
QUIET=true
|
|
241
|
+
shift
|
|
242
|
+
;;
|
|
209
243
|
--status)
|
|
210
244
|
SHOW_STATUS=true
|
|
211
245
|
shift
|
|
@@ -222,6 +256,14 @@ parse_arguments() {
|
|
|
222
256
|
SHOW_HELP=true
|
|
223
257
|
shift
|
|
224
258
|
;;
|
|
259
|
+
--version)
|
|
260
|
+
SHOW_VERSION=true
|
|
261
|
+
shift
|
|
262
|
+
;;
|
|
263
|
+
init)
|
|
264
|
+
SUBCOMMAND="init"
|
|
265
|
+
shift
|
|
266
|
+
;;
|
|
225
267
|
*)
|
|
226
268
|
echo "Error: Unknown option: $1"
|
|
227
269
|
usage
|
|
@@ -421,13 +463,7 @@ generate_prd() {
|
|
|
421
463
|
prd_content+="$OPENSPEC_DESIGN"$'\n'$'\n'
|
|
422
464
|
|
|
423
465
|
# Add current task context for Ralph to use in commits
|
|
424
|
-
|
|
425
|
-
task_context=$(get_current_task_context "$change_dir")
|
|
426
|
-
|
|
427
|
-
if [[ -n "$task_context" ]]; then
|
|
428
|
-
prd_content+="## Current Task Context"$'\n'$'\n'
|
|
429
|
-
prd_content+="$task_context"$'\n'$'\n'
|
|
430
|
-
fi
|
|
466
|
+
# (Removed: task context is now provided via {{task_context}} template variable only)
|
|
431
467
|
|
|
432
468
|
echo "$prd_content"
|
|
433
469
|
}
|
|
@@ -462,17 +498,19 @@ parse_tasks() {
|
|
|
462
498
|
TASKS=()
|
|
463
499
|
TASK_IDS=()
|
|
464
500
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
501
|
+
if [[ -f "$tasks_file" ]]; then
|
|
502
|
+
local line_number=0
|
|
503
|
+
while IFS= read -r line; do
|
|
504
|
+
((line_number++)) || true
|
|
505
|
+
|
|
506
|
+
if [[ "$line" == "- [ ]"* ]]; then
|
|
507
|
+
local task_desc="${line#- \[ \] }"
|
|
508
|
+
TASKS+=("$task_desc")
|
|
509
|
+
TASK_IDS+=("$line_number")
|
|
510
|
+
log_verbose "Found incomplete task (line $line_number): $task_desc"
|
|
511
|
+
fi
|
|
512
|
+
done < "$tasks_file"
|
|
513
|
+
fi
|
|
476
514
|
|
|
477
515
|
log_verbose "Found ${#TASKS[@]} incomplete tasks"
|
|
478
516
|
}
|
|
@@ -750,20 +788,9 @@ create_prompt_template() {
|
|
|
750
788
|
|
|
751
789
|
Change directory: {{change_dir}}
|
|
752
790
|
|
|
753
|
-
## OpenSpec Artifacts
|
|
754
|
-
|
|
755
|
-
Include full context from openspec artifacts in {{change_dir}}:
|
|
756
|
-
- Read {{change_dir}}/proposal.md for the overall project goal
|
|
757
|
-
- Read {{change_dir}}/design.md for the technical design approach
|
|
758
|
-
- Read {{change_dir}}/specs/*/spec.md for the detailed specifications
|
|
759
|
-
|
|
760
|
-
## Invocation-Time PRD Snapshot
|
|
791
|
+
## OpenSpec Artifacts
|
|
761
792
|
|
|
762
|
-
{{
|
|
763
|
-
|
|
764
|
-
## Task List
|
|
765
|
-
|
|
766
|
-
{{tasks}}
|
|
793
|
+
{{_openspec_manifest}}
|
|
767
794
|
|
|
768
795
|
## Fresh Task Context
|
|
769
796
|
|
|
@@ -771,53 +798,97 @@ Include full context from openspec artifacts in {{change_dir}}:
|
|
|
771
798
|
|
|
772
799
|
## Instructions
|
|
773
800
|
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
2.
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
- Find the next incomplete task and repeat the process
|
|
792
|
-
|
|
793
|
-
## Critical Rules
|
|
794
|
-
|
|
795
|
-
- Work on ONE task at a time from the task list
|
|
796
|
-
- Read the full tasks file every iteration; do not rely on memory from prior iterations
|
|
797
|
-
- Do not rely on editor-specific slash commands or local-only skills; follow this prompt directly
|
|
798
|
-
- Treat tasks.md as the only source of truth for task state
|
|
799
|
-
- ONLY output `<promise>{{task_promise}}</promise>` when the current task is complete and marked as [x]
|
|
800
|
-
- ONLY output `<promise>{{completion_promise}}</promise>` when ALL tasks are [x]
|
|
801
|
-
- Output promise tags DIRECTLY - do not quote them, explain them, or say you "will" output them
|
|
802
|
-
- Do NOT lie or output false promises to exit the loop
|
|
803
|
-
- If stuck, try a different approach
|
|
804
|
-
- Check your work before claiming completion
|
|
801
|
+
Before implementing, read the OpenSpec artifacts listed above that are relevant to the current task.
|
|
802
|
+
|
|
803
|
+
Follow this loop contract EXACTLY. Do not skip steps. Do not batch. Do not output a promise until every step is done.
|
|
804
|
+
|
|
805
|
+
1. Work on the task shown in `## Fresh Task Context` above. Before editing any marker, open `tasks.md` at `{{change_dir}}/tasks.md` and verify that same task is still `- [ ] ` or `- [/] ` on disk (it may have been closed by a prior iteration if you are resuming).
|
|
806
|
+
2. Edit `tasks.md` in place to change that line's marker to `- [/] ` (in-progress). You MUST use your file edit tool to modify the file on disk — a shell `cp`, `sed`, or print-to-stdout does not count. Verify by re-reading the file.
|
|
807
|
+
3. Implement the smallest change that fully satisfies the task's Done-when conditions. Run the task's verification command if one is specified.
|
|
808
|
+
4. On success, edit `tasks.md` again in place to change that line's marker from `- [/] ` to `- [x] `. Verify by re-reading the file and confirming the `[x]` is present on that exact line.
|
|
809
|
+
5. ONLY after step 4 writes `[x]` to disk, output `<promise>{{task_promise}}</promise>` on its own line.
|
|
810
|
+
6. If and only if EVERY task line in `tasks.md` is `- [x] `, output `<promise>{{completion_promise}}</promise>` instead.
|
|
811
|
+
|
|
812
|
+
Hard rules:
|
|
813
|
+
- If you do not actually modify `tasks.md` on disk in this iteration, DO NOT output any promise tag. Output a short failure note instead and stop.
|
|
814
|
+
- Never output `<promise>{{task_promise}}</promise>` while the task you just worked on is still `- [ ]` on disk. That causes the same task to repeat forever.
|
|
815
|
+
- Promise tags must be on their own line, literal, unquoted, and not described in prose.
|
|
816
|
+
- If an approach fails twice, try a different one.
|
|
817
|
+
- If the task is already satisfied by prior work (e.g. target file already exists with the right content), you STILL must flip the checkbox to `[x]` in `tasks.md` before emitting the promise.
|
|
805
818
|
|
|
806
819
|
## Commit Contract
|
|
807
820
|
|
|
808
821
|
{{commit_contract}}
|
|
809
|
-
|
|
810
|
-
{{context}}
|
|
811
822
|
EOF
|
|
812
823
|
|
|
813
|
-
#
|
|
824
|
+
# Determine repo root for AGENTS.md probe
|
|
825
|
+
local repo_root
|
|
826
|
+
repo_root=$(git rev-parse --show-toplevel 2>/dev/null) || repo_root=""
|
|
827
|
+
|
|
828
|
+
# Build the manifest body
|
|
829
|
+
local manifest_body
|
|
830
|
+
manifest_body="Read these as needed (source of truth for this change):"$'\n'$'\n'
|
|
831
|
+
manifest_body+="- $abs_change_dir/proposal.md"$'\n'
|
|
832
|
+
manifest_body+="- $abs_change_dir/design.md"$'\n'
|
|
833
|
+
|
|
834
|
+
# Pre-expand specs/*/spec.md into concrete paths
|
|
835
|
+
if [[ -d "$abs_change_dir/specs" ]]; then
|
|
836
|
+
while IFS= read -r spec_path; do
|
|
837
|
+
[[ -n "$spec_path" ]] && manifest_body+="- $spec_path"$'\n'
|
|
838
|
+
done < <(find "$abs_change_dir/specs" -name spec.md -type f 2>/dev/null | sort)
|
|
839
|
+
fi
|
|
840
|
+
|
|
841
|
+
# Optionally append AGENTS.md reference
|
|
842
|
+
local agents_line
|
|
843
|
+
agents_line=$(probe_agents_md "$repo_root")
|
|
844
|
+
if [[ -n "$agents_line" ]]; then
|
|
845
|
+
manifest_body+=$'\n'"$agents_line"
|
|
846
|
+
fi
|
|
847
|
+
|
|
848
|
+
# Append Ralph best practices guide if project is ralphified
|
|
849
|
+
if check_ralphified; then
|
|
850
|
+
local bp_manifest_path="$abs_change_dir/../../OPENSPEC-RALPH-BP.md"
|
|
851
|
+
if [[ ! -f "$bp_manifest_path" ]]; then
|
|
852
|
+
bp_manifest_path="$repo_root/openspec/OPENSPEC-RALPH-BP.md"
|
|
853
|
+
fi
|
|
854
|
+
if [[ -f "$bp_manifest_path" ]]; then
|
|
855
|
+
manifest_body+=$'\n'"- $bp_manifest_path (Ralph best practices guide)"
|
|
856
|
+
fi
|
|
857
|
+
fi
|
|
858
|
+
|
|
859
|
+
# Substitute {{_openspec_manifest}} using awk with a manifest temp file
|
|
860
|
+
# (awk -v cannot handle multi-line values; use getline from a file instead)
|
|
861
|
+
local _manifest_file
|
|
862
|
+
_manifest_file=$(mktemp 2>/dev/null || mktemp -t ralph-manifest)
|
|
863
|
+
printf '%s' "$manifest_body" > "$_manifest_file"
|
|
814
864
|
local _tmpfile
|
|
815
865
|
_tmpfile=$(mktemp 2>/dev/null || mktemp -t ralph-template)
|
|
866
|
+
awk -v mf="$_manifest_file" '
|
|
867
|
+
{
|
|
868
|
+
if ($0 == "{{_openspec_manifest}}") {
|
|
869
|
+
while ((getline line < mf) > 0) { print line }
|
|
870
|
+
close(mf)
|
|
871
|
+
} else { print }
|
|
872
|
+
}
|
|
873
|
+
' "$template_file" > "$_tmpfile" && mv "$_tmpfile" "$template_file"
|
|
874
|
+
rm -f "$_manifest_file"
|
|
875
|
+
|
|
876
|
+
# Substitute {{change_dir}}
|
|
877
|
+
_tmpfile=$(mktemp 2>/dev/null || mktemp -t ralph-template)
|
|
816
878
|
sed "s|{{change_dir}}|$abs_change_dir|g" "$template_file" > "$_tmpfile" && mv "$_tmpfile" "$template_file"
|
|
817
879
|
|
|
818
880
|
log_verbose "Prompt template created: $template_file"
|
|
819
881
|
}
|
|
820
882
|
|
|
883
|
+
probe_agents_md() {
|
|
884
|
+
local repo_root="$1"
|
|
885
|
+
if [[ -n "$repo_root" && -r "$repo_root/AGENTS.md" ]]; then
|
|
886
|
+
echo "- AGENTS.md (project-level build/test guide)"
|
|
887
|
+
else
|
|
888
|
+
echo ""
|
|
889
|
+
fi
|
|
890
|
+
}
|
|
891
|
+
|
|
821
892
|
restore_ralph_state_from_tasks() {
|
|
822
893
|
local tasks_file="$1"
|
|
823
894
|
local ralph_loop_file=".ralph/ralph-loop.state.json"
|
|
@@ -966,11 +1037,6 @@ execute_ralph_loop() {
|
|
|
966
1037
|
sync_tasks_to_ralph "$change_dir" "$ralph_dir"
|
|
967
1038
|
create_prompt_template "$change_dir" "$template_file"
|
|
968
1039
|
|
|
969
|
-
# Generate PRD and write to file
|
|
970
|
-
local prd_content
|
|
971
|
-
prd_content=$(generate_prd "$change_dir")
|
|
972
|
-
echo "$prd_content" > "$ralph_dir/PRD.md"
|
|
973
|
-
|
|
974
1040
|
# Output files
|
|
975
1041
|
local stdout_log="$output_dir/ralph-stdout.log"
|
|
976
1042
|
local stderr_log="$output_dir/ralph-stderr.log"
|
|
@@ -980,7 +1046,6 @@ execute_ralph_loop() {
|
|
|
980
1046
|
|
|
981
1047
|
# Build the mini-ralph-cli arguments
|
|
982
1048
|
local mini_ralph_args=(
|
|
983
|
-
"--prompt-file" "$ralph_dir/PRD.md"
|
|
984
1049
|
"--prompt-template" "$template_file"
|
|
985
1050
|
"--ralph-dir" "$ralph_dir"
|
|
986
1051
|
"--tasks-file" "$change_dir/tasks.md"
|
|
@@ -996,12 +1061,17 @@ execute_ralph_loop() {
|
|
|
996
1061
|
mini_ralph_args+=("--verbose")
|
|
997
1062
|
fi
|
|
998
1063
|
|
|
1064
|
+
if [[ "$QUIET" == true ]]; then
|
|
1065
|
+
mini_ralph_args+=("--quiet")
|
|
1066
|
+
fi
|
|
1067
|
+
|
|
999
1068
|
# Run the internal mini Ralph CLI and capture output
|
|
1000
1069
|
{
|
|
1001
1070
|
node "$MINI_RALPH_CLI" "${mini_ralph_args[@]}"
|
|
1002
1071
|
} > >(tee "$stdout_log") 2> >(tee "$stderr_log")
|
|
1003
|
-
|
|
1004
|
-
|
|
1072
|
+
local node_exit_code=$?
|
|
1073
|
+
wait
|
|
1074
|
+
return $node_exit_code
|
|
1005
1075
|
}
|
|
1006
1076
|
|
|
1007
1077
|
|
|
@@ -1053,10 +1123,259 @@ run_observability_command() {
|
|
|
1053
1123
|
esac
|
|
1054
1124
|
}
|
|
1055
1125
|
|
|
1126
|
+
resolve_bp_path() {
|
|
1127
|
+
local repo_root="${1:-.}"
|
|
1128
|
+
local bp_candidates=(
|
|
1129
|
+
"$repo_root/node_modules/spec-and-loop/OPENSPEC-RALPH-BP.md"
|
|
1130
|
+
"$SCRIPT_DIR/../OPENSPEC-RALPH-BP.md"
|
|
1131
|
+
)
|
|
1132
|
+
|
|
1133
|
+
for candidate in "${bp_candidates[@]}"; do
|
|
1134
|
+
local resolved
|
|
1135
|
+
resolved=$(get_realpath "$candidate")
|
|
1136
|
+
if [[ -n "$resolved" && -f "$resolved" ]]; then
|
|
1137
|
+
printf "%s" "$resolved"
|
|
1138
|
+
return 0
|
|
1139
|
+
fi
|
|
1140
|
+
done
|
|
1141
|
+
|
|
1142
|
+
return 1
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
ralphify_init() {
|
|
1146
|
+
local config_file="openspec/config.yaml"
|
|
1147
|
+
local agents_file="AGENTS.md"
|
|
1148
|
+
local bp_local_path="openspec/OPENSPEC-RALPH-BP.md"
|
|
1149
|
+
|
|
1150
|
+
if ! git rev-parse --git-dir > /dev/null 2>&1; then
|
|
1151
|
+
log_error "Not a git repository. Please run: git init"
|
|
1152
|
+
return 1
|
|
1153
|
+
fi
|
|
1154
|
+
|
|
1155
|
+
if [[ ! -d "openspec" ]]; then
|
|
1156
|
+
log_error "openspec/ directory not found. Please run: openspec init"
|
|
1157
|
+
return 1
|
|
1158
|
+
fi
|
|
1159
|
+
|
|
1160
|
+
local repo_root
|
|
1161
|
+
repo_root=$(git rev-parse --show-toplevel 2>/dev/null) || repo_root="$(pwd)"
|
|
1162
|
+
|
|
1163
|
+
local bp_source
|
|
1164
|
+
bp_source=$(resolve_bp_path "$repo_root")
|
|
1165
|
+
if [[ -z "$bp_source" ]]; then
|
|
1166
|
+
log_error "OPENSPEC-RALPH-BP.md not found in node_modules or package directory"
|
|
1167
|
+
log_error "Package installation may be incomplete. Run: npm install"
|
|
1168
|
+
return 1
|
|
1169
|
+
fi
|
|
1170
|
+
|
|
1171
|
+
if ! cmp -s "$bp_source" "$bp_local_path" 2>/dev/null; then
|
|
1172
|
+
cp "$bp_source" "$bp_local_path"
|
|
1173
|
+
log_info "Copied OPENSPEC-RALPH-BP.md to openspec/"
|
|
1174
|
+
else
|
|
1175
|
+
log_verbose "OPENSPEC-RALPH-BP.md already up to date in openspec/"
|
|
1176
|
+
fi
|
|
1177
|
+
|
|
1178
|
+
if ! grep -q "Ralph Wiggum" "$config_file" 2>/dev/null; then
|
|
1179
|
+
cat >> "$config_file" << 'RALPH_CONFIG'
|
|
1180
|
+
|
|
1181
|
+
# --- Ralph Wiggum ---
|
|
1182
|
+
# This project uses the Ralph Wiggum method for iterative development.
|
|
1183
|
+
# See OPENSPEC-RALPH-BP.md for the detailed authoring guide shipped with spec-and-loop.
|
|
1184
|
+
context: |
|
|
1185
|
+
This project follows the Ralph Wiggum method for task authoring.
|
|
1186
|
+
Read openspec/OPENSPEC-RALPH-BP.md before generating OpenSpec artifacts.
|
|
1187
|
+
Verify proposals against the Ralph checklist before approval.
|
|
1188
|
+
rules:
|
|
1189
|
+
proposal:
|
|
1190
|
+
- Include explicit scope, non-goals, and first-rollout boundaries
|
|
1191
|
+
- Resolve all policy decisions before implementation tasks
|
|
1192
|
+
tasks:
|
|
1193
|
+
- Use the task template from OPENSPEC-RALPH-BP.md
|
|
1194
|
+
- Each task has one dominant outcome and one verification cluster
|
|
1195
|
+
- Include explicit stop-and-hand-off conditions
|
|
1196
|
+
design:
|
|
1197
|
+
- Do not leave core policy choices unresolved
|
|
1198
|
+
- Specify algorithms, config shapes, and failure semantics
|
|
1199
|
+
RALPH_CONFIG
|
|
1200
|
+
log_verbose "Updated $config_file with Ralph Wiggum rules"
|
|
1201
|
+
else
|
|
1202
|
+
log_verbose "Ralph Wiggum rules already present in $config_file"
|
|
1203
|
+
fi
|
|
1204
|
+
|
|
1205
|
+
if ! grep -q "Ralph Wiggum Compliance" "$agents_file" 2>/dev/null; then
|
|
1206
|
+
cat >> "$agents_file" << 'RALPH_AGENTS'
|
|
1207
|
+
|
|
1208
|
+
## Ralph Wiggum Compliance
|
|
1209
|
+
|
|
1210
|
+
This project follows the Ralph Wiggum method for iterative OpenSpec development.
|
|
1211
|
+
|
|
1212
|
+
Before generating any OpenSpec artifacts, you MUST:
|
|
1213
|
+
- Read `openspec/OPENSPEC-RALPH-BP.md` (Ralph Wiggum authoring guide)
|
|
1214
|
+
- Verify proposals against the Ralph authoring checklist
|
|
1215
|
+
- Ensure tasks use the task template with objective done-when conditions
|
|
1216
|
+
- Include explicit stop-and-hand-off conditions in every task
|
|
1217
|
+
RALPH_AGENTS
|
|
1218
|
+
log_verbose "Updated $agents_file with Ralph Wiggum compliance section"
|
|
1219
|
+
else
|
|
1220
|
+
log_verbose "Ralph Wiggum compliance section already present in $agents_file"
|
|
1221
|
+
fi
|
|
1222
|
+
|
|
1223
|
+
log_info "Project ralphified successfully. Proposals will now follow Ralph Wiggum best practices."
|
|
1224
|
+
return 0
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
check_ralphified() {
|
|
1228
|
+
local config_file="openspec/config.yaml"
|
|
1229
|
+
local agents_file="AGENTS.md"
|
|
1230
|
+
|
|
1231
|
+
if [[ ! -f "$config_file" ]]; then
|
|
1232
|
+
return 1
|
|
1233
|
+
fi
|
|
1234
|
+
|
|
1235
|
+
if [[ ! -f "$agents_file" ]]; then
|
|
1236
|
+
return 1
|
|
1237
|
+
fi
|
|
1238
|
+
|
|
1239
|
+
if ! grep -q "Ralph Wiggum" "$config_file" 2>/dev/null; then
|
|
1240
|
+
return 1
|
|
1241
|
+
fi
|
|
1242
|
+
|
|
1243
|
+
if ! grep -q "Ralph Wiggum Compliance" "$agents_file" 2>/dev/null; then
|
|
1244
|
+
return 1
|
|
1245
|
+
fi
|
|
1246
|
+
|
|
1247
|
+
return 0
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
show_ralphify_warning() {
|
|
1251
|
+
local change_name="$1"
|
|
1252
|
+
|
|
1253
|
+
cat >&2 << 'WARNING_BOX'
|
|
1254
|
+
┌─────────────────────────────────────────────────────────────────────┐
|
|
1255
|
+
│ │
|
|
1256
|
+
│ WARNING: Project not configured for Ralph Wiggum best practices │
|
|
1257
|
+
│ │
|
|
1258
|
+
│ This project has not been ralphified. Proposals and artifacts │
|
|
1259
|
+
│ may not follow Ralph Wiggum conventions. │
|
|
1260
|
+
│ │
|
|
1261
|
+
│ It is recommended to run: ralph-run init │
|
|
1262
|
+
│ │
|
|
1263
|
+
└─────────────────────────────────────────────────────────────────────┘
|
|
1264
|
+
WARNING_BOX
|
|
1265
|
+
|
|
1266
|
+
while true; do
|
|
1267
|
+
echo "" >&2
|
|
1268
|
+
echo "Choose an option:" >&2
|
|
1269
|
+
echo " [A] Run ralphify init and redo the proposal, then continue" >&2
|
|
1270
|
+
echo " [C] Continue without init" >&2
|
|
1271
|
+
echo " [Q] Quit" >&2
|
|
1272
|
+
printf "Enter choice: " >&2
|
|
1273
|
+
if ! read -r choice; then
|
|
1274
|
+
log_info "Non-interactive environment detected. Continuing without Ralph Wiggum configuration."
|
|
1275
|
+
return 0
|
|
1276
|
+
fi
|
|
1277
|
+
|
|
1278
|
+
case "$choice" in
|
|
1279
|
+
[Aa])
|
|
1280
|
+
log_info "Running ralphify init..."
|
|
1281
|
+
if ! ralphify_init; then
|
|
1282
|
+
log_error "ralphify init failed. Aborting."
|
|
1283
|
+
exit 1
|
|
1284
|
+
fi
|
|
1285
|
+
|
|
1286
|
+
local change_dir="openspec/changes/$change_name"
|
|
1287
|
+
local backup_dir
|
|
1288
|
+
backup_dir=$(make_temp_dir "ralph-artifact-backup")
|
|
1289
|
+
|
|
1290
|
+
local proposal_backup=""
|
|
1291
|
+
if [[ -f "$change_dir/proposal.md" ]]; then
|
|
1292
|
+
proposal_backup="$backup_dir/proposal.md"
|
|
1293
|
+
mv "$change_dir/proposal.md" "$proposal_backup"
|
|
1294
|
+
log_info "Backed up proposal.md"
|
|
1295
|
+
fi
|
|
1296
|
+
|
|
1297
|
+
local tasks_backup=""
|
|
1298
|
+
if [[ -f "$change_dir/tasks.md" ]]; then
|
|
1299
|
+
tasks_backup="$backup_dir/tasks.md"
|
|
1300
|
+
mv "$change_dir/tasks.md" "$tasks_backup"
|
|
1301
|
+
log_info "Backed up tasks.md"
|
|
1302
|
+
fi
|
|
1303
|
+
|
|
1304
|
+
local bp_file="openspec/OPENSPEC-RALPH-BP.md"
|
|
1305
|
+
if [[ ! -f "$bp_file" ]]; then
|
|
1306
|
+
bp_file="$SCRIPT_DIR/../OPENSPEC-RALPH-BP.md"
|
|
1307
|
+
fi
|
|
1308
|
+
local ralph_guidance=""
|
|
1309
|
+
if [[ -f "$bp_file" ]]; then
|
|
1310
|
+
ralph_guidance=" When creating artifacts, read ${bp_file} and follow the Ralph Wiggum task template and authoring checklist. Ensure the proposal includes explicit scope, non-goals, first-rollout boundaries, and capabilities that map to Ralph-friendly tasks. Ensure tasks use the task template with objective done-when conditions and explicit stop-and-hand-off conditions. Do NOT restore or copy from any .bak backup files - write fresh artifacts from scratch."
|
|
1311
|
+
fi
|
|
1312
|
+
|
|
1313
|
+
log_info "Invoking opencode to regenerate proposal and tasks with Ralph Wiggum best practices..."
|
|
1314
|
+
opencode run "/opsx-continue $change_name${ralph_guidance}" || true
|
|
1315
|
+
local proposal_ok=false
|
|
1316
|
+
if [[ -f "$change_dir/proposal.md" ]]; then
|
|
1317
|
+
proposal_ok=true
|
|
1318
|
+
log_info "Proposal regenerated successfully"
|
|
1319
|
+
else
|
|
1320
|
+
log_error "opencode did not create a new proposal.md"
|
|
1321
|
+
fi
|
|
1322
|
+
|
|
1323
|
+
if [[ -f "$change_dir/tasks.md" ]]; then
|
|
1324
|
+
log_info "Tasks regenerated successfully"
|
|
1325
|
+
else
|
|
1326
|
+
log_error "opencode did not create a new tasks.md (only proposal may have been created; tasks may require a second /opsx-continue)"
|
|
1327
|
+
fi
|
|
1328
|
+
|
|
1329
|
+
if [[ "$proposal_ok" == true ]]; then
|
|
1330
|
+
log_info "Artifact regeneration complete"
|
|
1331
|
+
else
|
|
1332
|
+
log_error "Restoring backed-up artifacts"
|
|
1333
|
+
if [[ -n "$proposal_backup" && -f "$proposal_backup" ]]; then
|
|
1334
|
+
mv "$proposal_backup" "$change_dir/proposal.md"
|
|
1335
|
+
log_info "Restored original proposal.md"
|
|
1336
|
+
fi
|
|
1337
|
+
if [[ -n "$tasks_backup" && -f "$tasks_backup" ]]; then
|
|
1338
|
+
mv "$tasks_backup" "$change_dir/tasks.md"
|
|
1339
|
+
log_info "Restored original tasks.md"
|
|
1340
|
+
fi
|
|
1341
|
+
fi
|
|
1342
|
+
|
|
1343
|
+
log_info "Returning to loop execution..."
|
|
1344
|
+
return 0
|
|
1345
|
+
;;
|
|
1346
|
+
[Cc])
|
|
1347
|
+
log_info "Continuing without Ralph Wiggum configuration."
|
|
1348
|
+
return 0
|
|
1349
|
+
;;
|
|
1350
|
+
[Qq])
|
|
1351
|
+
log_info "Exiting."
|
|
1352
|
+
exit 0
|
|
1353
|
+
;;
|
|
1354
|
+
*)
|
|
1355
|
+
echo "Invalid choice '$choice'. Please enter A, C, or Q." >&2
|
|
1356
|
+
;;
|
|
1357
|
+
esac
|
|
1358
|
+
done
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1056
1361
|
main() {
|
|
1057
1362
|
set -e
|
|
1058
1363
|
parse_arguments "$@"
|
|
1059
1364
|
|
|
1365
|
+
if [[ "$SHOW_VERSION" == true ]]; then
|
|
1366
|
+
echo "$VERSION"
|
|
1367
|
+
exit 0
|
|
1368
|
+
fi
|
|
1369
|
+
|
|
1370
|
+
if [[ "$SUBCOMMAND" == "init" ]]; then
|
|
1371
|
+
if [[ -n "$CHANGE_NAME" ]]; then
|
|
1372
|
+
log_error "Cannot use --change with the init subcommand"
|
|
1373
|
+
exit 1
|
|
1374
|
+
fi
|
|
1375
|
+
ralphify_init
|
|
1376
|
+
exit $?
|
|
1377
|
+
fi
|
|
1378
|
+
|
|
1060
1379
|
log_verbose "Starting ralph-run v$VERSION"
|
|
1061
1380
|
log_verbose "Change name: ${CHANGE_NAME:-<auto-detect>}"
|
|
1062
1381
|
|
|
@@ -1100,6 +1419,15 @@ main() {
|
|
|
1100
1419
|
validate_git_repository
|
|
1101
1420
|
validate_dependencies
|
|
1102
1421
|
|
|
1422
|
+
# Ralphify guard: check if project is configured for Ralph Wiggum best practices
|
|
1423
|
+
if ! check_ralphified; then
|
|
1424
|
+
if [[ -z "$CHANGE_NAME" ]]; then
|
|
1425
|
+
CHANGE_NAME=$(auto_detect_change)
|
|
1426
|
+
log_info "Auto-detected change for ralphify guard: $CHANGE_NAME"
|
|
1427
|
+
fi
|
|
1428
|
+
show_ralphify_warning "$CHANGE_NAME"
|
|
1429
|
+
fi
|
|
1430
|
+
|
|
1103
1431
|
if [[ -z "$CHANGE_NAME" ]]; then
|
|
1104
1432
|
CHANGE_NAME=$(auto_detect_change)
|
|
1105
1433
|
log_info "Auto-detected change: $CHANGE_NAME"
|
|
@@ -1113,13 +1441,8 @@ main() {
|
|
|
1113
1441
|
log_info "Change directory: $change_dir"
|
|
1114
1442
|
log_info "Ralph directory: $ralph_dir"
|
|
1115
1443
|
|
|
1116
|
-
read_openspec_artifacts "$change_dir"
|
|
1117
|
-
local prd_content=$(generate_prd "$change_dir")
|
|
1118
|
-
write_prd "$ralph_dir" "$prd_content"
|
|
1119
|
-
|
|
1120
1444
|
parse_tasks "$change_dir"
|
|
1121
1445
|
|
|
1122
|
-
log_info "PRD generation complete"
|
|
1123
1446
|
log_info "Found ${#TASKS[@]} tasks to execute"
|
|
1124
1447
|
|
|
1125
1448
|
local max_iterations="${MAX_ITERATIONS:-50}"
|