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.
@@ -1,6 +1,27 @@
1
1
  #!/bin/bash
2
2
 
3
- VERSION="1.0.0"
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
- local task_context
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
- local line_number=0
466
- while IFS= read -r line; do
467
- ((line_number++)) || true
468
-
469
- if [[ "$line" == "- [ ]"* ]]; then
470
- local task_desc="${line#- \[ \] }"
471
- TASKS+=("$task_desc")
472
- TASK_IDS+=("$line_number")
473
- log_verbose "Found incomplete task (line $line_number): $task_desc"
474
- fi
475
- done < "$tasks_file"
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 Context
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
- {{base_prompt}}
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
- 1. **Identify** current task:
775
- - Find any task marked as [/] (in progress)
776
- - If no task is in progress, pick the first task marked as [ ] (incomplete)
777
- - Mark the task as [/] in the tasks file before starting work
778
-
779
- 2. **Implement** the current task directly:
780
- - Read the relevant OpenSpec artifacts for context (proposal.md, design.md, specs)
781
- - Make the smallest maintainable change that fully satisfies the current task
782
- - Run the most relevant validation or tests for the task before claiming completion
783
-
784
- 3. **Complete** task:
785
- - Verify that the implementation meets the requirements
786
- - When the task is successfully completed, mark it as [x] in the tasks file
787
- - Output: `<promise>{{task_promise}}</promise>`
788
-
789
- 4. **Continue** to the next task:
790
- - The loop will continue with the next iteration
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
- # Use a portable inplace replace: write to temp file then move into place
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
- return $?
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}"