@rpamis/comet 0.3.6 → 0.3.8

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.
Files changed (95) hide show
  1. package/README.md +575 -443
  2. package/assets/manifest.json +41 -24
  3. package/assets/skills/comet/SKILL.md +282 -301
  4. package/assets/skills/comet/reference/auto-transition.md +27 -0
  5. package/assets/skills/comet/reference/comet-yaml-fields.md +68 -0
  6. package/assets/skills/comet/reference/context-recovery.md +35 -0
  7. package/assets/skills/comet/reference/debug-gate.md +17 -0
  8. package/assets/skills/comet/reference/decision-point.md +20 -0
  9. package/assets/skills/comet/reference/dirty-worktree.md +1 -0
  10. package/assets/skills/comet/reference/file-structure.md +28 -0
  11. package/assets/skills/comet/reference/subagent-dispatch.md +113 -0
  12. package/assets/skills/comet/rules/comet-phase-guard.en.md +99 -0
  13. package/assets/skills/comet/rules/comet-phase-guard.md +99 -0
  14. package/assets/skills/comet/scripts/comet-archive.sh +71 -55
  15. package/assets/skills/comet/scripts/comet-guard.sh +174 -18
  16. package/assets/skills/comet/scripts/comet-handoff.sh +133 -6
  17. package/assets/skills/comet/scripts/comet-hook-guard.sh +260 -0
  18. package/assets/skills/comet/scripts/comet-state.sh +362 -25
  19. package/assets/skills/comet/scripts/comet-yaml-validate.sh +24 -1
  20. package/assets/skills/comet-archive/SKILL.md +100 -73
  21. package/assets/skills/comet-build/SKILL.md +304 -221
  22. package/assets/skills/comet-design/SKILL.md +263 -166
  23. package/assets/skills/comet-hotfix/SKILL.md +204 -169
  24. package/assets/skills/comet-open/SKILL.md +203 -113
  25. package/assets/skills/comet-tweak/SKILL.md +180 -154
  26. package/assets/skills/comet-verify/SKILL.md +234 -201
  27. package/assets/skills-zh/comet/SKILL.md +269 -301
  28. package/assets/skills-zh/comet/reference/auto-transition.md +27 -0
  29. package/assets/skills-zh/comet/reference/comet-yaml-fields.md +68 -0
  30. package/assets/skills-zh/comet/reference/context-recovery.md +35 -0
  31. package/assets/skills-zh/comet/reference/debug-gate.md +17 -0
  32. package/assets/skills-zh/comet/reference/decision-point.md +20 -0
  33. package/assets/skills-zh/comet/reference/dirty-worktree.md +2 -1
  34. package/assets/skills-zh/comet/reference/file-structure.md +28 -0
  35. package/assets/skills-zh/comet/reference/subagent-dispatch.md +113 -0
  36. package/assets/skills-zh/comet-archive/SKILL.md +100 -73
  37. package/assets/skills-zh/comet-build/SKILL.md +304 -221
  38. package/assets/skills-zh/comet-design/SKILL.md +264 -166
  39. package/assets/skills-zh/comet-hotfix/SKILL.md +200 -169
  40. package/assets/skills-zh/comet-open/SKILL.md +203 -113
  41. package/assets/skills-zh/comet-tweak/SKILL.md +176 -154
  42. package/assets/skills-zh/comet-verify/SKILL.md +232 -201
  43. package/dist/cli/index.js +19 -0
  44. package/dist/cli/index.js.map +1 -1
  45. package/dist/commands/doctor.d.ts.map +1 -1
  46. package/dist/commands/doctor.js +22 -0
  47. package/dist/commands/doctor.js.map +1 -1
  48. package/dist/commands/init.d.ts +5 -1
  49. package/dist/commands/init.d.ts.map +1 -1
  50. package/dist/commands/init.js +64 -9
  51. package/dist/commands/init.js.map +1 -1
  52. package/dist/commands/uninstall.d.ts +9 -0
  53. package/dist/commands/uninstall.d.ts.map +1 -0
  54. package/dist/commands/uninstall.js +113 -0
  55. package/dist/commands/uninstall.js.map +1 -0
  56. package/dist/commands/update.d.ts +1 -1
  57. package/dist/commands/update.d.ts.map +1 -1
  58. package/dist/commands/update.js +120 -32
  59. package/dist/commands/update.js.map +1 -1
  60. package/dist/core/codegraph.d.ts +4 -0
  61. package/dist/core/codegraph.d.ts.map +1 -0
  62. package/dist/core/codegraph.js +63 -0
  63. package/dist/core/codegraph.js.map +1 -0
  64. package/dist/core/detect.d.ts +8 -1
  65. package/dist/core/detect.d.ts.map +1 -1
  66. package/dist/core/detect.js +41 -2
  67. package/dist/core/detect.js.map +1 -1
  68. package/dist/core/openspec.d.ts +1 -1
  69. package/dist/core/openspec.d.ts.map +1 -1
  70. package/dist/core/openspec.js +40 -17
  71. package/dist/core/openspec.js.map +1 -1
  72. package/dist/core/platforms.d.ts +10 -0
  73. package/dist/core/platforms.d.ts.map +1 -1
  74. package/dist/core/platforms.js +177 -16
  75. package/dist/core/platforms.js.map +1 -1
  76. package/dist/core/skills.d.ts +36 -1
  77. package/dist/core/skills.d.ts.map +1 -1
  78. package/dist/core/skills.js +451 -15
  79. package/dist/core/skills.js.map +1 -1
  80. package/dist/core/superpowers.d.ts.map +1 -1
  81. package/dist/core/superpowers.js +1 -0
  82. package/dist/core/superpowers.js.map +1 -1
  83. package/dist/core/uninstall.d.ts +28 -0
  84. package/dist/core/uninstall.d.ts.map +1 -0
  85. package/dist/core/uninstall.js +421 -0
  86. package/dist/core/uninstall.js.map +1 -0
  87. package/dist/core/version.d.ts +31 -0
  88. package/dist/core/version.d.ts.map +1 -0
  89. package/dist/core/version.js +111 -0
  90. package/dist/core/version.js.map +1 -0
  91. package/dist/utils/file-system.d.ts +23 -0
  92. package/dist/utils/file-system.d.ts.map +1 -1
  93. package/dist/utils/file-system.js +100 -5
  94. package/dist/utils/file-system.js.map +1 -1
  95. package/package.json +69 -62
@@ -10,6 +10,7 @@
10
10
  # check <change-name> <phase> — Verify entry requirements for a phase
11
11
  # check <change-name> <phase> --recover — Output structured recovery context for compaction resume
12
12
  # scale <change-name> — Assess and set verification mode based on metrics
13
+ # task-checkoff <file> <task-text> — Verify one unique task is checked
13
14
  #
14
15
  # Workflows: full, hotfix, tweak
15
16
  # Phases for check: open, design, build, verify, archive
@@ -65,6 +66,26 @@ validate_enum() {
65
66
  exit 1
66
67
  }
67
68
 
69
+ validate_path_field() {
70
+ local value="$1"
71
+ local field="$2"
72
+ # null and empty are acceptable (means "not set")
73
+ if [ -z "$value" ] || [ "$value" = "null" ]; then
74
+ return 0
75
+ fi
76
+ # Reject absolute paths and home-directory references
77
+ case "$value" in
78
+ /*|~*|[A-Za-z]:*|\\*)
79
+ red "ERROR: $field must be a relative path within the repo: '$value'" >&2
80
+ exit 1
81
+ ;;
82
+ esac
83
+ if [[ "$value" =~ \.\. ]]; then
84
+ red "ERROR: $field cannot contain '..' (path traversal not allowed): '$value'" >&2
85
+ exit 1
86
+ fi
87
+ }
88
+
68
89
  # --- Helper functions ---
69
90
 
70
91
  yaml_field() {
@@ -126,9 +147,17 @@ replace_yaml_field() {
126
147
  local tmp_file
127
148
 
128
149
  tmp_file=$(mktemp)
150
+ chmod 600 "$tmp_file"
151
+ # Replace the target field, then deduplicate all fields keeping only the
152
+ # last occurrence of each key. Prevents stale earlier values from
153
+ # persisting when a field is set multiple times.
129
154
  awk -v field="$field" -v value="$value" '
130
- index($0, field ":") == 1 { print field ": " value; next }
131
- { print }
155
+ index($0, field ":") == 1 { $0 = field ": " value }
156
+ { buf[NR] = $0; keys[NR] = $0; sub(/:.*$/, "", keys[NR]); n = NR }
157
+ END {
158
+ for (i = 1; i <= n; i++) last[keys[i]] = i
159
+ for (i = 1; i <= n; i++) if (last[keys[i]] == i) print buf[i]
160
+ }
132
161
  ' "$yaml_file" > "$tmp_file"
133
162
  mv "$tmp_file" "$yaml_file"
134
163
  }
@@ -155,6 +184,57 @@ yaml_file_for() {
155
184
  echo "$change_dir/.comet.yaml"
156
185
  }
157
186
 
187
+ project_context_compression() {
188
+ local value="off"
189
+ local source="default"
190
+ if [ -n "${COMET_CONTEXT_COMPRESSION:-}" ]; then
191
+ value="$COMET_CONTEXT_COMPRESSION"
192
+ source="COMET_CONTEXT_COMPRESSION"
193
+ elif [ -f ".comet/config.yaml" ]; then
194
+ value=$(yaml_field "context_compression" ".comet/config.yaml")
195
+ value="${value:-off}"
196
+ source=".comet/config.yaml"
197
+ fi
198
+
199
+ case "$value" in
200
+ off|beta)
201
+ printf '%s\n' "$value"
202
+ ;;
203
+ *)
204
+ red "ERROR: Invalid context_compression from ${source}: '$value'" >&2
205
+ red "Valid values: off, beta" >&2
206
+ exit 1
207
+ ;;
208
+ esac
209
+ }
210
+
211
+ project_auto_transition_default() {
212
+ local value="true"
213
+ local source="default"
214
+ if [ -n "${COMET_AUTO_TRANSITION:-}" ]; then
215
+ value="$COMET_AUTO_TRANSITION"
216
+ source="COMET_AUTO_TRANSITION"
217
+ elif [ -f ".comet/config.yaml" ]; then
218
+ local raw
219
+ raw=$(yaml_field "auto_transition" ".comet/config.yaml" 2>/dev/null || true)
220
+ if [ -n "$raw" ]; then
221
+ value="$raw"
222
+ source=".comet/config.yaml"
223
+ fi
224
+ fi
225
+
226
+ case "$value" in
227
+ true|false)
228
+ printf '%s\n' "$value"
229
+ ;;
230
+ *)
231
+ red "ERROR: Invalid auto_transition from ${source}: '$value'" >&2
232
+ red "Valid values: true, false" >&2
233
+ exit 1
234
+ ;;
235
+ esac
236
+ }
237
+
158
238
  # --- Subcommands ---
159
239
 
160
240
  cmd_init() {
@@ -178,17 +258,21 @@ cmd_init() {
178
258
  mkdir -p "$change_dir"
179
259
 
180
260
  # Set workflow-appropriate defaults
181
- local phase build_mode isolation verify_mode
261
+ local phase build_mode isolation verify_mode context_compression auto_transition
182
262
  phase="open"
263
+ context_compression=$(project_context_compression)
264
+ auto_transition="$(project_auto_transition_default)"
183
265
 
184
266
  case "$workflow" in
185
267
  full)
186
268
  build_mode="null"
269
+ tdd_mode="null"
187
270
  isolation="null"
188
271
  verify_mode="null"
189
272
  ;;
190
273
  hotfix|tweak)
191
274
  build_mode="direct"
275
+ tdd_mode="direct"
192
276
  isolation="branch"
193
277
  verify_mode="light"
194
278
  ;;
@@ -204,17 +288,21 @@ cmd_init() {
204
288
  cat > "$yaml_file" <<EOF
205
289
  workflow: $workflow
206
290
  phase: $phase
291
+ context_compression: $context_compression
207
292
  build_mode: $build_mode
208
293
  build_pause: null
294
+ subagent_dispatch: null
295
+ tdd_mode: $tdd_mode
209
296
  isolation: $isolation
210
297
  verify_mode: $verify_mode
298
+ auto_transition: $auto_transition
211
299
  base_ref: $base_ref
212
300
  design_doc: null
213
301
  plan: null
214
302
  verify_result: pending
215
303
  verification_report: null
216
304
  branch_status: pending
217
- created_at: $(date +%Y-%m-%d)
305
+ created_at: $(date -u +%Y-%m-%d)
218
306
  verified_at: null
219
307
  archived: false
220
308
  EOF
@@ -240,6 +328,9 @@ cmd_get() {
240
328
  # Read and output the field value
241
329
  local value
242
330
  value=$(yaml_field "$field" "$yaml_file")
331
+ if [ "$field" = "auto_transition" ] && { [ -z "$value" ] || [ "$value" = "null" ]; }; then
332
+ value="$(project_auto_transition_default)"
333
+ fi
243
334
  echo "${value:-}"
244
335
  }
245
336
 
@@ -265,14 +356,14 @@ cmd_set() {
265
356
  yellow "WARNING: Setting 'phase' directly bypasses state machine constraints." >&2
266
357
  yellow " Consider using: comet-state.sh transition <change-name> <event>" >&2
267
358
  ;;
268
- workflow|build_mode|build_pause|isolation|verify_mode|verify_result|verification_report|branch_status|archived|design_doc|plan|verified_at|created_at|direct_override|build_command|verify_command|handoff_context|handoff_hash|base_ref)
359
+ workflow|context_compression|build_mode|build_pause|subagent_dispatch|tdd_mode|isolation|verify_mode|auto_transition|verify_result|verification_report|branch_status|archived|design_doc|plan|verified_at|created_at|direct_override|build_command|verify_command|handoff_context|handoff_hash|base_ref)
269
360
  # Valid field
270
361
  ;;
271
362
  *)
272
363
  red "ERROR: Unknown field: '$field'" >&2
273
364
  red "Valid fields:" >&2
274
- red " workflow, phase, design_doc, plan, build_mode, build_pause, isolation," >&2
275
- red " verify_mode, verify_result, verification_report, branch_status," >&2
365
+ red " workflow, phase, context_compression, design_doc, plan, build_mode, build_pause, subagent_dispatch, tdd_mode, isolation," >&2
366
+ red " verify_mode, auto_transition, verify_result, verification_report, branch_status," >&2
276
367
  red " verified_at, created_at, archived, base_ref, direct_override," >&2
277
368
  red " build_command, verify_command, handoff_context, handoff_hash" >&2
278
369
  exit 1
@@ -284,6 +375,9 @@ cmd_set() {
284
375
  workflow)
285
376
  validate_enum "$value" "full" "hotfix" "tweak"
286
377
  ;;
378
+ context_compression)
379
+ validate_enum "$value" "off" "beta"
380
+ ;;
287
381
  phase)
288
382
  validate_enum "$value" "open" "design" "build" "verify" "archive"
289
383
  ;;
@@ -293,12 +387,21 @@ cmd_set() {
293
387
  build_pause)
294
388
  validate_enum "$value" "null" "plan-ready"
295
389
  ;;
390
+ subagent_dispatch)
391
+ validate_enum "$value" "null" "confirmed"
392
+ ;;
393
+ tdd_mode)
394
+ validate_enum "$value" "tdd" "direct"
395
+ ;;
296
396
  isolation)
297
397
  validate_enum "$value" "branch" "worktree"
298
398
  ;;
299
399
  verify_mode)
300
400
  validate_enum "$value" "light" "full"
301
401
  ;;
402
+ auto_transition)
403
+ validate_enum "$value" "true" "false"
404
+ ;;
302
405
  verify_result)
303
406
  validate_enum "$value" "pending" "pass" "fail"
304
407
  ;;
@@ -311,8 +414,11 @@ cmd_set() {
311
414
  direct_override)
312
415
  validate_enum "$value" "true" "false"
313
416
  ;;
314
- design_doc|plan|verification_report|verified_at|created_at|build_command|verify_command|handoff_context|handoff_hash)
315
- # No validation for path fields, date fields, or project command strings
417
+ design_doc|plan|verification_report|handoff_context|handoff_hash)
418
+ validate_path_field "$value" "$field"
419
+ ;;
420
+ verified_at|created_at|build_command|verify_command)
421
+ # No validation for date fields or project command strings
316
422
  ;;
317
423
  esac
318
424
 
@@ -357,11 +463,13 @@ require_verification_evidence() {
357
463
 
358
464
  require_build_decisions() {
359
465
  local change_name="$1"
360
- local workflow build_mode isolation direct_override
466
+ local workflow build_mode isolation direct_override subagent_dispatch tdd_mode
361
467
  workflow=$(cmd_get "$change_name" "workflow")
362
468
  build_mode=$(cmd_get "$change_name" "build_mode")
363
469
  isolation=$(cmd_get "$change_name" "isolation")
364
470
  direct_override=$(cmd_get "$change_name" "direct_override" 2>/dev/null || true)
471
+ subagent_dispatch=$(cmd_get "$change_name" "subagent_dispatch" 2>/dev/null || true)
472
+ tdd_mode=$(cmd_get "$change_name" "tdd_mode" 2>/dev/null || true)
365
473
 
366
474
  case "$isolation" in
367
475
  branch|worktree) ;;
@@ -383,6 +491,16 @@ require_build_decisions() {
383
491
  red "ERROR: Cannot transition '$change_name': build_mode=direct is only allowed for hotfix/tweak unless direct_override=true" >&2
384
492
  exit 1
385
493
  fi
494
+
495
+ if [ "$build_mode" = "subagent-driven-development" ] && [ "$subagent_dispatch" != "confirmed" ]; then
496
+ red "ERROR: Cannot transition '$change_name': subagent_dispatch must be confirmed before using build_mode=subagent-driven-development" >&2
497
+ exit 1
498
+ fi
499
+
500
+ if [ "$workflow" = "full" ] && { [ "$tdd_mode" = "null" ] || [ -z "$tdd_mode" ]; }; then
501
+ red "ERROR: Cannot transition '$change_name': tdd_mode must be selected before leaving build (full workflow)" >&2
502
+ exit 1
503
+ fi
386
504
  }
387
505
 
388
506
  cmd_transition() {
@@ -390,7 +508,7 @@ cmd_transition() {
390
508
  local event="$2"
391
509
 
392
510
  validate_change_name "$change_name"
393
- validate_enum "$event" "open-complete" "design-complete" "build-complete" "verify-pass" "verify-fail" "archived"
511
+ validate_enum "$event" "open-complete" "design-complete" "build-complete" "verify-pass" "verify-fail" "archive-reopen" "archived"
394
512
 
395
513
  case "$event" in
396
514
  open-complete)
@@ -410,23 +528,41 @@ cmd_transition() {
410
528
  build-complete)
411
529
  require_phase "$change_name" "build"
412
530
  require_build_decisions "$change_name"
531
+ local current_verify_result
532
+ current_verify_result=$(cmd_get "$change_name" "verify_result")
413
533
  cmd_set "$change_name" phase verify
414
534
  cmd_set "$change_name" verify_result pending
415
- cmd_set "$change_name" verification_report null
416
- cmd_set "$change_name" branch_status pending
535
+ # Preserve verification evidence on re-verify (verify-fail → build → build-complete)
536
+ # so the fix can reference the original failure report
537
+ if [ "$current_verify_result" != "fail" ]; then
538
+ cmd_set "$change_name" verification_report null
539
+ cmd_set "$change_name" branch_status pending
540
+ fi
417
541
  ;;
418
542
  verify-pass)
419
543
  require_phase "$change_name" "verify"
420
544
  require_verification_evidence "$change_name"
421
545
  cmd_set "$change_name" verify_result pass
422
546
  cmd_set "$change_name" phase archive
423
- cmd_set "$change_name" verified_at "$(date +%Y-%m-%d)"
547
+ cmd_set "$change_name" verified_at "$(date -u +%Y-%m-%d)"
424
548
  ;;
425
549
  verify-fail)
426
550
  require_phase "$change_name" "verify"
427
551
  cmd_set "$change_name" verify_result fail
428
552
  cmd_set "$change_name" phase build
429
- cmd_set "$change_name" branch_status pending
553
+ # Preserve branch_status so re-verify doesn't require re-handling branches
554
+ ;;
555
+ archive-reopen)
556
+ require_phase "$change_name" "archive"
557
+ local archived
558
+ archived=$(cmd_get "$change_name" "archived")
559
+ if [ "$archived" = "true" ]; then
560
+ red "ERROR: Cannot transition '$change_name': already archived" >&2
561
+ exit 1
562
+ fi
563
+ cmd_set "$change_name" verify_result pending
564
+ cmd_set "$change_name" phase verify
565
+ cmd_set "$change_name" verified_at null
430
566
  ;;
431
567
  archived)
432
568
  require_phase "$change_name" "archive"
@@ -636,7 +772,7 @@ cmd_recover() {
636
772
 
637
773
  # Read all relevant fields
638
774
  local design_doc plan verify_result verify_mode verification_report
639
- local branch_status handoff_context handoff_hash isolation build_mode build_pause direct_override
775
+ local branch_status handoff_context handoff_hash isolation build_mode build_pause subagent_dispatch tdd_mode direct_override
640
776
  design_doc=$(cmd_get "$change_name" "design_doc")
641
777
  plan=$(cmd_get "$change_name" "plan")
642
778
  verify_result=$(cmd_get "$change_name" "verify_result")
@@ -648,6 +784,8 @@ cmd_recover() {
648
784
  isolation=$(cmd_get "$change_name" "isolation")
649
785
  build_mode=$(cmd_get "$change_name" "build_mode")
650
786
  build_pause=$(cmd_get "$change_name" "build_pause" 2>/dev/null || true)
787
+ subagent_dispatch=$(cmd_get "$change_name" "subagent_dispatch" 2>/dev/null || true)
788
+ tdd_mode=$(cmd_get "$change_name" "tdd_mode" 2>/dev/null || true)
651
789
  direct_override=$(cmd_get "$change_name" "direct_override" 2>/dev/null || true)
652
790
 
653
791
  echo "State fields:"
@@ -656,15 +794,23 @@ cmd_recover() {
656
794
  case "$phase" in
657
795
  open)
658
796
  echo " Artifacts:"
797
+ local artifacts_done=0
659
798
  for f in proposal.md design.md tasks.md; do
660
799
  if file_nonempty "$change_dir/$f"; then
661
800
  echo " - ${f}: DONE"
801
+ artifacts_done=$((artifacts_done + 1))
662
802
  else
663
803
  echo " - ${f}: PENDING"
664
804
  fi
665
805
  done
666
806
  echo ""
667
- echo "Recovery action: Create or complete missing artifacts, then use AskUserQuestion for user confirmation."
807
+ if [ "$artifacts_done" -eq 3 ]; then
808
+ echo "Recovery action: All artifacts complete. Run /comet-open user confirmation, then guard to transition."
809
+ elif [ "$artifacts_done" -eq 0 ]; then
810
+ echo "Recovery action: No artifacts created yet. Start from /comet-open Step 1 (explore and clarify)."
811
+ else
812
+ echo "Recovery action: Some artifacts incomplete. Resume /comet-open from the first missing artifact."
813
+ fi
668
814
  ;;
669
815
  design)
670
816
  echo " Artifacts:"
@@ -694,6 +840,10 @@ cmd_recover() {
694
840
  field_status "isolation" "$isolation"
695
841
  field_status "build_mode" "$build_mode"
696
842
  field_status "build_pause" "$build_pause"
843
+ field_status "tdd_mode" "$tdd_mode"
844
+ if [ "$build_mode" = "subagent-driven-development" ] || { [ -n "$subagent_dispatch" ] && [ "$subagent_dispatch" != "null" ]; }; then
845
+ field_status "subagent_dispatch" "$subagent_dispatch"
846
+ fi
697
847
  if [ "$build_mode" = "direct" ] && [ "$workflow" != "hotfix" ] && [ "$workflow" != "tweak" ]; then
698
848
  field_status "direct_override" "$direct_override"
699
849
  fi
@@ -704,29 +854,72 @@ cmd_recover() {
704
854
  # Count completed vs pending tasks
705
855
  local tasks_file="$change_dir/tasks.md"
706
856
  local total=0 done=0 pending=0
857
+ local plan_total=0 plan_done=0 plan_pending=0
707
858
  if [ -f "$tasks_file" ]; then
708
- total=$(grep -c '^\- \[' "$tasks_file" 2>/dev/null || echo "0")
709
- done=$(grep -c '^\- \[x\]' "$tasks_file" 2>/dev/null || echo "0")
859
+ total=$(grep -c '^[[:space:]]*- \[' "$tasks_file" 2>/dev/null || true)
860
+ done=$(grep -c '^[[:space:]]*- \[x\]' "$tasks_file" 2>/dev/null || true)
861
+ total="${total:-0}"
862
+ done="${done:-0}"
710
863
  pending=$((total - done))
711
864
  echo " Tasks: ${done}/${total} done, ${pending} pending"
712
865
  else
713
866
  echo " Tasks: tasks.md MISSING"
714
867
  fi
868
+ if [ -n "$plan" ] && [ "$plan" != "null" ] && [ -f "$plan" ]; then
869
+ plan_total=$(grep -c '^[[:space:]]*- \[' "$plan" 2>/dev/null || true)
870
+ plan_done=$(grep -c '^[[:space:]]*- \[x\]' "$plan" 2>/dev/null || true)
871
+ plan_total="${plan_total:-0}"
872
+ plan_done="${plan_done:-0}"
873
+ plan_pending=$((plan_total - plan_done))
874
+ if [ "$plan_total" -gt 0 ]; then
875
+ echo " Plan tasks: ${plan_done}/${plan_total} done, ${plan_pending} pending"
876
+ fi
877
+ fi
715
878
  echo ""
716
879
  if [ "$build_pause" = "plan-ready" ] && [ -n "$plan" ] && [ "$plan" != "null" ] && [ -f "$plan" ] && { [ "$isolation" = "null" ] || [ -z "$isolation" ] || [ "$build_mode" = "null" ] || [ -z "$build_mode" ]; }; then
717
880
  echo "Recovery action: Plan-ready pause detected. Ask the user whether to continue, then choose isolation and build mode without regenerating the plan."
718
881
  elif [ "$build_pause" = "plan-ready" ] && { [ -z "$plan" ] || [ "$plan" = "null" ] || [ ! -f "$plan" ]; }; then
719
882
  echo "Recovery action: Plan-ready pause is recorded, but the plan file is missing. Restore the plan file or rerun writing-plans before choosing execution."
720
883
  elif [ "$build_pause" = "plan-ready" ]; then
721
- echo "Recovery action: Plan-ready pause is stale because build decisions are already selected. Clear build_pause to null, then continue from the first unchecked task."
884
+ if [ "$build_mode" = "subagent-driven-development" ] && { [ "$pending" -gt 0 ] || [ "$plan_pending" -gt 0 ]; }; then
885
+ if [ "$subagent_dispatch" = "confirmed" ]; then
886
+ echo "Recovery action: Plan-ready pause is stale because build decisions are already selected. Clear build_pause to null, then inspect the first unchecked task (OpenSpec or plan additions) against recent git history/diff. If implemented, check it off; otherwise dispatch a real background subagent. Do not execute the pending task directly in the main window."
887
+ else
888
+ echo "Recovery action: Plan-ready pause is stale and subagent dispatch is not confirmed. Confirm a real background subagent/Task/multi-agent dispatcher and set subagent_dispatch to confirmed, or set build_mode to executing-plans before continuing."
889
+ fi
890
+ elif [ "$pending" -gt 0 ] || [ "$plan_pending" -gt 0 ]; then
891
+ echo "Recovery action: Plan-ready pause is stale because build decisions are already selected. Clear build_pause to null, then continue from the first unchecked task."
892
+ else
893
+ echo "Recovery action: Plan-ready pause is stale and all tasks are done. Clear build_pause to null, then run guard to transition to verify."
894
+ fi
722
895
  elif [ "$isolation" = "null" ] || [ -z "$isolation" ]; then
723
- echo "Recovery action: Isolation not selected. Use AskUserQuestion to ask user for branch/worktree choice."
896
+ echo "Recovery action: Isolation not selected. Use the current platform's user confirmation mechanism to ask user for branch/worktree choice."
724
897
  elif [ "$build_mode" = "null" ] || [ -z "$build_mode" ]; then
725
- echo "Recovery action: Build mode not selected. Use AskUserQuestion to ask user for execution method."
898
+ echo "Recovery action: Build mode not selected. Use the current platform's user confirmation mechanism to ask user for execution method."
899
+ elif [ -z "$tdd_mode" ] || [ "$tdd_mode" = "null" ]; then
900
+ echo "Recovery action: TDD mode not selected. Use the current platform's user confirmation mechanism to ask user for tdd or direct."
726
901
  elif [ ! -f "$tasks_file" ]; then
727
902
  echo "Recovery action: tasks.md missing. Verify change directory integrity."
728
903
  elif [ "$pending" -gt 0 ]; then
729
- echo "Recovery action: Read tasks.md and continue from first unchecked task."
904
+ if [ "$build_mode" = "subagent-driven-development" ]; then
905
+ if [ "$subagent_dispatch" = "confirmed" ]; then
906
+ echo "Recovery action: Read tasks.md and the Superpowers plan (which may include additions beyond OpenSpec), then inspect the first unchecked task against recent git history/diff. If implemented, check it off; otherwise dispatch a real background subagent. Do not execute the pending task directly in the main window."
907
+ else
908
+ echo "Recovery action: Subagent dispatch is not confirmed. Confirm a real background subagent/Task/multi-agent dispatcher and set subagent_dispatch to confirmed, or set build_mode to executing-plans before continuing."
909
+ fi
910
+ else
911
+ echo "Recovery action: Read tasks.md and continue from first unchecked task."
912
+ fi
913
+ elif [ "$plan_pending" -gt 0 ]; then
914
+ if [ "$build_mode" = "subagent-driven-development" ]; then
915
+ if [ "$subagent_dispatch" = "confirmed" ]; then
916
+ echo "Recovery action: Read the Superpowers plan, then inspect the first unchecked Superpowers plan task against recent git history/diff. If implemented, check it off; otherwise dispatch a real background subagent. Do not execute the pending task directly in the main window."
917
+ else
918
+ echo "Recovery action: Subagent dispatch is not confirmed. Confirm a real background subagent/Task/multi-agent dispatcher and set subagent_dispatch to confirmed, or set build_mode to executing-plans before continuing."
919
+ fi
920
+ else
921
+ echo "Recovery action: Read the Superpowers plan and continue from the first unchecked plan task."
922
+ fi
730
923
  else
731
924
  echo "Recovery action: All tasks done. Run guard to transition to verify."
732
925
  fi
@@ -799,7 +992,7 @@ cmd_scale() {
799
992
  local plan_file base_ref=""
800
993
  plan_file=$(cmd_get "$change_name" "plan" 2>/dev/null || true)
801
994
  if [ -n "$plan_file" ] && [ "$plan_file" != "null" ] && [ -f "$plan_file" ]; then
802
- base_ref=$(grep '^base-ref:' "$plan_file" 2>/dev/null | head -1 | sed 's/^base-ref: *//')
995
+ base_ref=$(grep '^base-ref:' "$plan_file" 2>/dev/null | head -1 | sed 's/^base-ref: *//' || true)
803
996
  fi
804
997
  # Fallback to base_ref stored in .comet.yaml (set during init)
805
998
  if [ -z "$base_ref" ] || [ "$base_ref" = "null" ]; then
@@ -832,6 +1025,134 @@ cmd_scale() {
832
1025
  green "[SCALE] verify_mode=$result"
833
1026
  }
834
1027
 
1028
+ cmd_task_checkoff() {
1029
+ local task_file="$1"
1030
+ local task_text="$2"
1031
+
1032
+ validate_path_field "$task_file" "task file"
1033
+
1034
+ if [ -z "$task_text" ]; then
1035
+ red "ERROR: Task text cannot be empty" >&2
1036
+ exit 1
1037
+ fi
1038
+
1039
+ if [ ! -f "$task_file" ]; then
1040
+ red "ERROR: Task file not found: $task_file" >&2
1041
+ exit 1
1042
+ fi
1043
+
1044
+ local counts
1045
+ counts=$(TASK_TEXT="$task_text" awk '
1046
+ BEGIN {
1047
+ task = ENVIRON["TASK_TEXT"]
1048
+ }
1049
+ {
1050
+ sub(/\r$/, "")
1051
+ if ($0 == "- [ ] " task || $0 == "- [x] " task || $0 == "- [X] " task) {
1052
+ total++
1053
+ }
1054
+ if ($0 == "- [x] " task || $0 == "- [X] " task) {
1055
+ checked++
1056
+ }
1057
+ }
1058
+ END {
1059
+ printf "%d %d\n", total + 0, checked + 0
1060
+ }
1061
+ ' "$task_file")
1062
+
1063
+ local total="${counts%% *}"
1064
+ local checked="${counts##* }"
1065
+
1066
+ if [ "$total" -ne 1 ]; then
1067
+ red "ERROR: task text must appear exactly once in $task_file (found $total): $task_text" >&2
1068
+ exit 1
1069
+ fi
1070
+
1071
+ if [ "$checked" -ne 1 ]; then
1072
+ red "ERROR: task is not checked in $task_file: $task_text" >&2
1073
+ exit 1
1074
+ fi
1075
+
1076
+ echo "TASK_CHECKOFF: PASS"
1077
+ echo "FILE: $task_file"
1078
+ echo "TASK: $task_text"
1079
+ }
1080
+
1081
+ # Resolve the next workflow step after a guard --apply phase advance.
1082
+ # Reads the (already advanced) phase, workflow, and auto_transition, then emits
1083
+ # a deterministic next-step contract so skills don't hardcode the next skill name.
1084
+ #
1085
+ # Output contract (stdout):
1086
+ # NEXT: auto|manual|done
1087
+ # SKILL: <skill-name> (omitted when NEXT=done)
1088
+ # HINT: <message> (only when NEXT=manual)
1089
+ cmd_next() {
1090
+ local change_name="$1"
1091
+ validate_change_name "$change_name"
1092
+
1093
+ local change_dir="openspec/changes/$change_name"
1094
+ local yaml_file="$change_dir/.comet.yaml"
1095
+ if [ ! -f "$yaml_file" ]; then
1096
+ red "ERROR: .comet.yaml not found at $yaml_file" >&2
1097
+ exit 1
1098
+ fi
1099
+
1100
+ local phase workflow auto_transition archived
1101
+ phase=$(cmd_get "$change_name" "phase" 2>/dev/null || true)
1102
+ workflow=$(cmd_get "$change_name" "workflow" 2>/dev/null || true)
1103
+ auto_transition=$(cmd_get "$change_name" "auto_transition" 2>/dev/null || true)
1104
+ archived=$(cmd_get "$change_name" "archived" 2>/dev/null || true)
1105
+
1106
+ # Change-level auto_transition overrides project-level; fall back to project default
1107
+ if [ -z "$auto_transition" ] || [ "$auto_transition" = "null" ]; then
1108
+ auto_transition="$(project_auto_transition_default)"
1109
+ fi
1110
+
1111
+ # Terminal state: archived change has no next step.
1112
+ if [ "$archived" = "true" ]; then
1113
+ echo "NEXT: done"
1114
+ return 0
1115
+ fi
1116
+
1117
+ # Map the current (post-advance) phase to the skill that owns it.
1118
+ local skill=""
1119
+ case "$phase" in
1120
+ open)
1121
+ skill="comet-open"
1122
+ ;;
1123
+ design)
1124
+ skill="comet-design"
1125
+ ;;
1126
+ build)
1127
+ case "$workflow" in
1128
+ hotfix) skill="comet-hotfix" ;;
1129
+ tweak) skill="comet-tweak" ;;
1130
+ *) skill="comet-build" ;;
1131
+ esac
1132
+ ;;
1133
+ verify)
1134
+ skill="comet-verify"
1135
+ ;;
1136
+ archive)
1137
+ skill="comet-archive"
1138
+ ;;
1139
+ *)
1140
+ red "ERROR: Cannot resolve next step for '$change_name': unknown phase '${phase:-null}'" >&2
1141
+ exit 1
1142
+ ;;
1143
+ esac
1144
+
1145
+ # auto_transition=false pauses the next skill invocation only; phase is already advanced.
1146
+ if [ "$auto_transition" = "false" ]; then
1147
+ echo "NEXT: manual"
1148
+ echo "SKILL: $skill"
1149
+ echo "HINT: phase is '$phase'; run /$skill manually to continue"
1150
+ else
1151
+ echo "NEXT: auto"
1152
+ echo "SKILL: $skill"
1153
+ fi
1154
+ }
1155
+
835
1156
  # --- Main ---
836
1157
 
837
1158
  SUBCOMMAND="${1:-}"
@@ -863,7 +1184,7 @@ case "$SUBCOMMAND" in
863
1184
  transition)
864
1185
  if [ $# -lt 2 ]; then
865
1186
  red "Usage: comet-state.sh transition <change-name> <event>" >&2
866
- red "Events: open-complete, design-complete, build-complete, verify-pass, verify-fail, archived" >&2
1187
+ red "Events: open-complete, design-complete, build-complete, verify-pass, verify-fail, archive-reopen, archived" >&2
867
1188
  exit 1
868
1189
  fi
869
1190
  cmd_transition "$@"
@@ -888,6 +1209,20 @@ case "$SUBCOMMAND" in
888
1209
  fi
889
1210
  cmd_scale "$@"
890
1211
  ;;
1212
+ task-checkoff)
1213
+ if [ $# -lt 2 ]; then
1214
+ red "Usage: comet-state.sh task-checkoff <file> <task-text>" >&2
1215
+ exit 1
1216
+ fi
1217
+ cmd_task_checkoff "$@"
1218
+ ;;
1219
+ next)
1220
+ if [ $# -lt 1 ]; then
1221
+ red "Usage: comet-state.sh next <change-name>" >&2
1222
+ exit 1
1223
+ fi
1224
+ cmd_next "$@"
1225
+ ;;
891
1226
  *)
892
1227
  red "Unknown subcommand: $SUBCOMMAND" >&2
893
1228
  echo "" >&2
@@ -900,6 +1235,8 @@ case "$SUBCOMMAND" in
900
1235
  echo " transition <change-name> <event> — Apply a validated state transition" >&2
901
1236
  echo " check <change-name> <phase> — Verify entry requirements for a phase" >&2
902
1237
  echo " scale <change-name> — Assess and set verification mode based on metrics" >&2
1238
+ echo " task-checkoff <file> <task-text> — Verify one unique task is checked" >&2
1239
+ echo " next <change-name> — Resolve the next workflow step (auto/manual/done)" >&2
903
1240
  echo "" >&2
904
1241
  echo "Workflows: full, hotfix, tweak" >&2
905
1242
  echo "Phases for check: open, design, build, verify, archive" >&2