@rpamis/comet 0.3.6 → 0.3.7
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/LICENSE +21 -21
- package/README.md +175 -63
- package/assets/manifest.json +11 -1
- package/assets/skills/comet/SKILL.md +44 -16
- package/assets/skills/comet/reference/dirty-worktree.md +1 -0
- package/assets/skills/comet/rules/comet-phase-guard.md +90 -0
- package/assets/skills/comet/scripts/comet-archive.sh +71 -55
- package/assets/skills/comet/scripts/comet-guard.sh +174 -18
- package/assets/skills/comet/scripts/comet-handoff.sh +133 -6
- package/assets/skills/comet/scripts/comet-hook-guard.sh +260 -0
- package/assets/skills/comet/scripts/comet-state.sh +300 -25
- package/assets/skills/comet/scripts/comet-yaml-validate.sh +24 -1
- package/assets/skills/comet-archive/SKILL.md +43 -10
- package/assets/skills/comet-build/SKILL.md +117 -22
- package/assets/skills/comet-design/SKILL.md +119 -13
- package/assets/skills/comet-hotfix/SKILL.md +51 -9
- package/assets/skills/comet-open/SKILL.md +86 -13
- package/assets/skills/comet-tweak/SKILL.md +33 -8
- package/assets/skills/comet-verify/SKILL.md +48 -9
- package/assets/skills-zh/comet/SKILL.md +47 -16
- package/assets/skills-zh/comet/reference/dirty-worktree.md +2 -1
- package/assets/skills-zh/comet-archive/SKILL.md +44 -11
- package/assets/skills-zh/comet-build/SKILL.md +120 -25
- package/assets/skills-zh/comet-design/SKILL.md +123 -16
- package/assets/skills-zh/comet-hotfix/SKILL.md +47 -9
- package/assets/skills-zh/comet-open/SKILL.md +87 -14
- package/assets/skills-zh/comet-tweak/SKILL.md +29 -8
- package/assets/skills-zh/comet-verify/SKILL.md +49 -12
- package/bin/comet.js +3 -3
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +22 -0
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/init.d.ts +5 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +64 -9
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +60 -1
- package/dist/commands/update.js.map +1 -1
- package/dist/core/codegraph.d.ts +9 -0
- package/dist/core/codegraph.d.ts.map +1 -0
- package/dist/core/codegraph.js +96 -0
- package/dist/core/codegraph.js.map +1 -0
- package/dist/core/platforms.d.ts +10 -0
- package/dist/core/platforms.d.ts.map +1 -1
- package/dist/core/platforms.js +163 -15
- package/dist/core/platforms.js.map +1 -1
- package/dist/core/skills.d.ts +34 -1
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +360 -1
- package/dist/core/skills.js.map +1 -1
- package/package.json +3 -1
- package/scripts/postinstall.js +44 -44
|
@@ -65,6 +65,26 @@ validate_enum() {
|
|
|
65
65
|
exit 1
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
validate_path_field() {
|
|
69
|
+
local value="$1"
|
|
70
|
+
local field="$2"
|
|
71
|
+
# null and empty are acceptable (means "not set")
|
|
72
|
+
if [ -z "$value" ] || [ "$value" = "null" ]; then
|
|
73
|
+
return 0
|
|
74
|
+
fi
|
|
75
|
+
# Reject absolute paths and home-directory references
|
|
76
|
+
case "$value" in
|
|
77
|
+
/*|~*|[A-Za-z]:*|\\*)
|
|
78
|
+
red "ERROR: $field must be a relative path within the repo: '$value'" >&2
|
|
79
|
+
exit 1
|
|
80
|
+
;;
|
|
81
|
+
esac
|
|
82
|
+
if [[ "$value" =~ \.\. ]]; then
|
|
83
|
+
red "ERROR: $field cannot contain '..' (path traversal not allowed): '$value'" >&2
|
|
84
|
+
exit 1
|
|
85
|
+
fi
|
|
86
|
+
}
|
|
87
|
+
|
|
68
88
|
# --- Helper functions ---
|
|
69
89
|
|
|
70
90
|
yaml_field() {
|
|
@@ -126,9 +146,17 @@ replace_yaml_field() {
|
|
|
126
146
|
local tmp_file
|
|
127
147
|
|
|
128
148
|
tmp_file=$(mktemp)
|
|
149
|
+
chmod 600 "$tmp_file"
|
|
150
|
+
# Replace the target field, then deduplicate all fields keeping only the
|
|
151
|
+
# last occurrence of each key. Prevents stale earlier values from
|
|
152
|
+
# persisting when a field is set multiple times.
|
|
129
153
|
awk -v field="$field" -v value="$value" '
|
|
130
|
-
index($0, field ":") == 1 {
|
|
131
|
-
{
|
|
154
|
+
index($0, field ":") == 1 { $0 = field ": " value }
|
|
155
|
+
{ buf[NR] = $0; keys[NR] = $0; sub(/:.*$/, "", keys[NR]); n = NR }
|
|
156
|
+
END {
|
|
157
|
+
for (i = 1; i <= n; i++) last[keys[i]] = i
|
|
158
|
+
for (i = 1; i <= n; i++) if (last[keys[i]] == i) print buf[i]
|
|
159
|
+
}
|
|
132
160
|
' "$yaml_file" > "$tmp_file"
|
|
133
161
|
mv "$tmp_file" "$yaml_file"
|
|
134
162
|
}
|
|
@@ -155,6 +183,57 @@ yaml_file_for() {
|
|
|
155
183
|
echo "$change_dir/.comet.yaml"
|
|
156
184
|
}
|
|
157
185
|
|
|
186
|
+
project_context_compression() {
|
|
187
|
+
local value="off"
|
|
188
|
+
local source="default"
|
|
189
|
+
if [ -n "${COMET_CONTEXT_COMPRESSION:-}" ]; then
|
|
190
|
+
value="$COMET_CONTEXT_COMPRESSION"
|
|
191
|
+
source="COMET_CONTEXT_COMPRESSION"
|
|
192
|
+
elif [ -f ".comet/config.yaml" ]; then
|
|
193
|
+
value=$(yaml_field "context_compression" ".comet/config.yaml")
|
|
194
|
+
value="${value:-off}"
|
|
195
|
+
source=".comet/config.yaml"
|
|
196
|
+
fi
|
|
197
|
+
|
|
198
|
+
case "$value" in
|
|
199
|
+
off|beta)
|
|
200
|
+
printf '%s\n' "$value"
|
|
201
|
+
;;
|
|
202
|
+
*)
|
|
203
|
+
red "ERROR: Invalid context_compression from ${source}: '$value'" >&2
|
|
204
|
+
red "Valid values: off, beta" >&2
|
|
205
|
+
exit 1
|
|
206
|
+
;;
|
|
207
|
+
esac
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
project_auto_transition_default() {
|
|
211
|
+
local value="true"
|
|
212
|
+
local source="default"
|
|
213
|
+
if [ -n "${COMET_AUTO_TRANSITION:-}" ]; then
|
|
214
|
+
value="$COMET_AUTO_TRANSITION"
|
|
215
|
+
source="COMET_AUTO_TRANSITION"
|
|
216
|
+
elif [ -f ".comet/config.yaml" ]; then
|
|
217
|
+
local raw
|
|
218
|
+
raw=$(yaml_field "auto_transition" ".comet/config.yaml" 2>/dev/null || true)
|
|
219
|
+
if [ -n "$raw" ]; then
|
|
220
|
+
value="$raw"
|
|
221
|
+
source=".comet/config.yaml"
|
|
222
|
+
fi
|
|
223
|
+
fi
|
|
224
|
+
|
|
225
|
+
case "$value" in
|
|
226
|
+
true|false)
|
|
227
|
+
printf '%s\n' "$value"
|
|
228
|
+
;;
|
|
229
|
+
*)
|
|
230
|
+
red "ERROR: Invalid auto_transition from ${source}: '$value'" >&2
|
|
231
|
+
red "Valid values: true, false" >&2
|
|
232
|
+
exit 1
|
|
233
|
+
;;
|
|
234
|
+
esac
|
|
235
|
+
}
|
|
236
|
+
|
|
158
237
|
# --- Subcommands ---
|
|
159
238
|
|
|
160
239
|
cmd_init() {
|
|
@@ -178,17 +257,21 @@ cmd_init() {
|
|
|
178
257
|
mkdir -p "$change_dir"
|
|
179
258
|
|
|
180
259
|
# Set workflow-appropriate defaults
|
|
181
|
-
local phase build_mode isolation verify_mode
|
|
260
|
+
local phase build_mode isolation verify_mode context_compression auto_transition
|
|
182
261
|
phase="open"
|
|
262
|
+
context_compression=$(project_context_compression)
|
|
263
|
+
auto_transition="$(project_auto_transition_default)"
|
|
183
264
|
|
|
184
265
|
case "$workflow" in
|
|
185
266
|
full)
|
|
186
267
|
build_mode="null"
|
|
268
|
+
tdd_mode="null"
|
|
187
269
|
isolation="null"
|
|
188
270
|
verify_mode="null"
|
|
189
271
|
;;
|
|
190
272
|
hotfix|tweak)
|
|
191
273
|
build_mode="direct"
|
|
274
|
+
tdd_mode="direct"
|
|
192
275
|
isolation="branch"
|
|
193
276
|
verify_mode="light"
|
|
194
277
|
;;
|
|
@@ -204,17 +287,21 @@ cmd_init() {
|
|
|
204
287
|
cat > "$yaml_file" <<EOF
|
|
205
288
|
workflow: $workflow
|
|
206
289
|
phase: $phase
|
|
290
|
+
context_compression: $context_compression
|
|
207
291
|
build_mode: $build_mode
|
|
208
292
|
build_pause: null
|
|
293
|
+
subagent_dispatch: null
|
|
294
|
+
tdd_mode: $tdd_mode
|
|
209
295
|
isolation: $isolation
|
|
210
296
|
verify_mode: $verify_mode
|
|
297
|
+
auto_transition: $auto_transition
|
|
211
298
|
base_ref: $base_ref
|
|
212
299
|
design_doc: null
|
|
213
300
|
plan: null
|
|
214
301
|
verify_result: pending
|
|
215
302
|
verification_report: null
|
|
216
303
|
branch_status: pending
|
|
217
|
-
created_at: $(date +%Y-%m-%d)
|
|
304
|
+
created_at: $(date -u +%Y-%m-%d)
|
|
218
305
|
verified_at: null
|
|
219
306
|
archived: false
|
|
220
307
|
EOF
|
|
@@ -240,6 +327,9 @@ cmd_get() {
|
|
|
240
327
|
# Read and output the field value
|
|
241
328
|
local value
|
|
242
329
|
value=$(yaml_field "$field" "$yaml_file")
|
|
330
|
+
if [ "$field" = "auto_transition" ] && { [ -z "$value" ] || [ "$value" = "null" ]; }; then
|
|
331
|
+
value="$(project_auto_transition_default)"
|
|
332
|
+
fi
|
|
243
333
|
echo "${value:-}"
|
|
244
334
|
}
|
|
245
335
|
|
|
@@ -265,14 +355,14 @@ cmd_set() {
|
|
|
265
355
|
yellow "WARNING: Setting 'phase' directly bypasses state machine constraints." >&2
|
|
266
356
|
yellow " Consider using: comet-state.sh transition <change-name> <event>" >&2
|
|
267
357
|
;;
|
|
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)
|
|
358
|
+
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
359
|
# Valid field
|
|
270
360
|
;;
|
|
271
361
|
*)
|
|
272
362
|
red "ERROR: Unknown field: '$field'" >&2
|
|
273
363
|
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
|
|
364
|
+
red " workflow, phase, context_compression, design_doc, plan, build_mode, build_pause, subagent_dispatch, tdd_mode, isolation," >&2
|
|
365
|
+
red " verify_mode, auto_transition, verify_result, verification_report, branch_status," >&2
|
|
276
366
|
red " verified_at, created_at, archived, base_ref, direct_override," >&2
|
|
277
367
|
red " build_command, verify_command, handoff_context, handoff_hash" >&2
|
|
278
368
|
exit 1
|
|
@@ -284,6 +374,9 @@ cmd_set() {
|
|
|
284
374
|
workflow)
|
|
285
375
|
validate_enum "$value" "full" "hotfix" "tweak"
|
|
286
376
|
;;
|
|
377
|
+
context_compression)
|
|
378
|
+
validate_enum "$value" "off" "beta"
|
|
379
|
+
;;
|
|
287
380
|
phase)
|
|
288
381
|
validate_enum "$value" "open" "design" "build" "verify" "archive"
|
|
289
382
|
;;
|
|
@@ -293,12 +386,21 @@ cmd_set() {
|
|
|
293
386
|
build_pause)
|
|
294
387
|
validate_enum "$value" "null" "plan-ready"
|
|
295
388
|
;;
|
|
389
|
+
subagent_dispatch)
|
|
390
|
+
validate_enum "$value" "null" "confirmed"
|
|
391
|
+
;;
|
|
392
|
+
tdd_mode)
|
|
393
|
+
validate_enum "$value" "tdd" "direct"
|
|
394
|
+
;;
|
|
296
395
|
isolation)
|
|
297
396
|
validate_enum "$value" "branch" "worktree"
|
|
298
397
|
;;
|
|
299
398
|
verify_mode)
|
|
300
399
|
validate_enum "$value" "light" "full"
|
|
301
400
|
;;
|
|
401
|
+
auto_transition)
|
|
402
|
+
validate_enum "$value" "true" "false"
|
|
403
|
+
;;
|
|
302
404
|
verify_result)
|
|
303
405
|
validate_enum "$value" "pending" "pass" "fail"
|
|
304
406
|
;;
|
|
@@ -311,8 +413,11 @@ cmd_set() {
|
|
|
311
413
|
direct_override)
|
|
312
414
|
validate_enum "$value" "true" "false"
|
|
313
415
|
;;
|
|
314
|
-
design_doc|plan|verification_report|
|
|
315
|
-
|
|
416
|
+
design_doc|plan|verification_report|handoff_context|handoff_hash)
|
|
417
|
+
validate_path_field "$value" "$field"
|
|
418
|
+
;;
|
|
419
|
+
verified_at|created_at|build_command|verify_command)
|
|
420
|
+
# No validation for date fields or project command strings
|
|
316
421
|
;;
|
|
317
422
|
esac
|
|
318
423
|
|
|
@@ -357,11 +462,13 @@ require_verification_evidence() {
|
|
|
357
462
|
|
|
358
463
|
require_build_decisions() {
|
|
359
464
|
local change_name="$1"
|
|
360
|
-
local workflow build_mode isolation direct_override
|
|
465
|
+
local workflow build_mode isolation direct_override subagent_dispatch tdd_mode
|
|
361
466
|
workflow=$(cmd_get "$change_name" "workflow")
|
|
362
467
|
build_mode=$(cmd_get "$change_name" "build_mode")
|
|
363
468
|
isolation=$(cmd_get "$change_name" "isolation")
|
|
364
469
|
direct_override=$(cmd_get "$change_name" "direct_override" 2>/dev/null || true)
|
|
470
|
+
subagent_dispatch=$(cmd_get "$change_name" "subagent_dispatch" 2>/dev/null || true)
|
|
471
|
+
tdd_mode=$(cmd_get "$change_name" "tdd_mode" 2>/dev/null || true)
|
|
365
472
|
|
|
366
473
|
case "$isolation" in
|
|
367
474
|
branch|worktree) ;;
|
|
@@ -383,6 +490,16 @@ require_build_decisions() {
|
|
|
383
490
|
red "ERROR: Cannot transition '$change_name': build_mode=direct is only allowed for hotfix/tweak unless direct_override=true" >&2
|
|
384
491
|
exit 1
|
|
385
492
|
fi
|
|
493
|
+
|
|
494
|
+
if [ "$build_mode" = "subagent-driven-development" ] && [ "$subagent_dispatch" != "confirmed" ]; then
|
|
495
|
+
red "ERROR: Cannot transition '$change_name': subagent_dispatch must be confirmed before using build_mode=subagent-driven-development" >&2
|
|
496
|
+
exit 1
|
|
497
|
+
fi
|
|
498
|
+
|
|
499
|
+
if [ "$workflow" = "full" ] && { [ "$tdd_mode" = "null" ] || [ -z "$tdd_mode" ]; }; then
|
|
500
|
+
red "ERROR: Cannot transition '$change_name': tdd_mode must be selected before leaving build (full workflow)" >&2
|
|
501
|
+
exit 1
|
|
502
|
+
fi
|
|
386
503
|
}
|
|
387
504
|
|
|
388
505
|
cmd_transition() {
|
|
@@ -390,7 +507,7 @@ cmd_transition() {
|
|
|
390
507
|
local event="$2"
|
|
391
508
|
|
|
392
509
|
validate_change_name "$change_name"
|
|
393
|
-
validate_enum "$event" "open-complete" "design-complete" "build-complete" "verify-pass" "verify-fail" "archived"
|
|
510
|
+
validate_enum "$event" "open-complete" "design-complete" "build-complete" "verify-pass" "verify-fail" "archive-reopen" "archived"
|
|
394
511
|
|
|
395
512
|
case "$event" in
|
|
396
513
|
open-complete)
|
|
@@ -410,23 +527,41 @@ cmd_transition() {
|
|
|
410
527
|
build-complete)
|
|
411
528
|
require_phase "$change_name" "build"
|
|
412
529
|
require_build_decisions "$change_name"
|
|
530
|
+
local current_verify_result
|
|
531
|
+
current_verify_result=$(cmd_get "$change_name" "verify_result")
|
|
413
532
|
cmd_set "$change_name" phase verify
|
|
414
533
|
cmd_set "$change_name" verify_result pending
|
|
415
|
-
|
|
416
|
-
|
|
534
|
+
# Preserve verification evidence on re-verify (verify-fail → build → build-complete)
|
|
535
|
+
# so the fix can reference the original failure report
|
|
536
|
+
if [ "$current_verify_result" != "fail" ]; then
|
|
537
|
+
cmd_set "$change_name" verification_report null
|
|
538
|
+
cmd_set "$change_name" branch_status pending
|
|
539
|
+
fi
|
|
417
540
|
;;
|
|
418
541
|
verify-pass)
|
|
419
542
|
require_phase "$change_name" "verify"
|
|
420
543
|
require_verification_evidence "$change_name"
|
|
421
544
|
cmd_set "$change_name" verify_result pass
|
|
422
545
|
cmd_set "$change_name" phase archive
|
|
423
|
-
cmd_set "$change_name" verified_at "$(date +%Y-%m-%d)"
|
|
546
|
+
cmd_set "$change_name" verified_at "$(date -u +%Y-%m-%d)"
|
|
424
547
|
;;
|
|
425
548
|
verify-fail)
|
|
426
549
|
require_phase "$change_name" "verify"
|
|
427
550
|
cmd_set "$change_name" verify_result fail
|
|
428
551
|
cmd_set "$change_name" phase build
|
|
429
|
-
|
|
552
|
+
# Preserve branch_status so re-verify doesn't require re-handling branches
|
|
553
|
+
;;
|
|
554
|
+
archive-reopen)
|
|
555
|
+
require_phase "$change_name" "archive"
|
|
556
|
+
local archived
|
|
557
|
+
archived=$(cmd_get "$change_name" "archived")
|
|
558
|
+
if [ "$archived" = "true" ]; then
|
|
559
|
+
red "ERROR: Cannot transition '$change_name': already archived" >&2
|
|
560
|
+
exit 1
|
|
561
|
+
fi
|
|
562
|
+
cmd_set "$change_name" verify_result pending
|
|
563
|
+
cmd_set "$change_name" phase verify
|
|
564
|
+
cmd_set "$change_name" verified_at null
|
|
430
565
|
;;
|
|
431
566
|
archived)
|
|
432
567
|
require_phase "$change_name" "archive"
|
|
@@ -636,7 +771,7 @@ cmd_recover() {
|
|
|
636
771
|
|
|
637
772
|
# Read all relevant fields
|
|
638
773
|
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
|
|
774
|
+
local branch_status handoff_context handoff_hash isolation build_mode build_pause subagent_dispatch tdd_mode direct_override
|
|
640
775
|
design_doc=$(cmd_get "$change_name" "design_doc")
|
|
641
776
|
plan=$(cmd_get "$change_name" "plan")
|
|
642
777
|
verify_result=$(cmd_get "$change_name" "verify_result")
|
|
@@ -648,6 +783,8 @@ cmd_recover() {
|
|
|
648
783
|
isolation=$(cmd_get "$change_name" "isolation")
|
|
649
784
|
build_mode=$(cmd_get "$change_name" "build_mode")
|
|
650
785
|
build_pause=$(cmd_get "$change_name" "build_pause" 2>/dev/null || true)
|
|
786
|
+
subagent_dispatch=$(cmd_get "$change_name" "subagent_dispatch" 2>/dev/null || true)
|
|
787
|
+
tdd_mode=$(cmd_get "$change_name" "tdd_mode" 2>/dev/null || true)
|
|
651
788
|
direct_override=$(cmd_get "$change_name" "direct_override" 2>/dev/null || true)
|
|
652
789
|
|
|
653
790
|
echo "State fields:"
|
|
@@ -656,15 +793,23 @@ cmd_recover() {
|
|
|
656
793
|
case "$phase" in
|
|
657
794
|
open)
|
|
658
795
|
echo " Artifacts:"
|
|
796
|
+
local artifacts_done=0
|
|
659
797
|
for f in proposal.md design.md tasks.md; do
|
|
660
798
|
if file_nonempty "$change_dir/$f"; then
|
|
661
799
|
echo " - ${f}: DONE"
|
|
800
|
+
artifacts_done=$((artifacts_done + 1))
|
|
662
801
|
else
|
|
663
802
|
echo " - ${f}: PENDING"
|
|
664
803
|
fi
|
|
665
804
|
done
|
|
666
805
|
echo ""
|
|
667
|
-
|
|
806
|
+
if [ "$artifacts_done" -eq 3 ]; then
|
|
807
|
+
echo "Recovery action: All artifacts complete. Run /comet-open user confirmation, then guard to transition."
|
|
808
|
+
elif [ "$artifacts_done" -eq 0 ]; then
|
|
809
|
+
echo "Recovery action: No artifacts created yet. Start from /comet-open Step 1 (explore and clarify)."
|
|
810
|
+
else
|
|
811
|
+
echo "Recovery action: Some artifacts incomplete. Resume /comet-open from the first missing artifact."
|
|
812
|
+
fi
|
|
668
813
|
;;
|
|
669
814
|
design)
|
|
670
815
|
echo " Artifacts:"
|
|
@@ -694,6 +839,10 @@ cmd_recover() {
|
|
|
694
839
|
field_status "isolation" "$isolation"
|
|
695
840
|
field_status "build_mode" "$build_mode"
|
|
696
841
|
field_status "build_pause" "$build_pause"
|
|
842
|
+
field_status "tdd_mode" "$tdd_mode"
|
|
843
|
+
if [ "$build_mode" = "subagent-driven-development" ] || { [ -n "$subagent_dispatch" ] && [ "$subagent_dispatch" != "null" ]; }; then
|
|
844
|
+
field_status "subagent_dispatch" "$subagent_dispatch"
|
|
845
|
+
fi
|
|
697
846
|
if [ "$build_mode" = "direct" ] && [ "$workflow" != "hotfix" ] && [ "$workflow" != "tweak" ]; then
|
|
698
847
|
field_status "direct_override" "$direct_override"
|
|
699
848
|
fi
|
|
@@ -704,29 +853,72 @@ cmd_recover() {
|
|
|
704
853
|
# Count completed vs pending tasks
|
|
705
854
|
local tasks_file="$change_dir/tasks.md"
|
|
706
855
|
local total=0 done=0 pending=0
|
|
856
|
+
local plan_total=0 plan_done=0 plan_pending=0
|
|
707
857
|
if [ -f "$tasks_file" ]; then
|
|
708
|
-
total=$(grep -c '
|
|
709
|
-
done=$(grep -c '
|
|
858
|
+
total=$(grep -c '^[[:space:]]*- \[' "$tasks_file" 2>/dev/null || true)
|
|
859
|
+
done=$(grep -c '^[[:space:]]*- \[x\]' "$tasks_file" 2>/dev/null || true)
|
|
860
|
+
total="${total:-0}"
|
|
861
|
+
done="${done:-0}"
|
|
710
862
|
pending=$((total - done))
|
|
711
863
|
echo " Tasks: ${done}/${total} done, ${pending} pending"
|
|
712
864
|
else
|
|
713
865
|
echo " Tasks: tasks.md MISSING"
|
|
714
866
|
fi
|
|
867
|
+
if [ -n "$plan" ] && [ "$plan" != "null" ] && [ -f "$plan" ]; then
|
|
868
|
+
plan_total=$(grep -c '^[[:space:]]*- \[' "$plan" 2>/dev/null || true)
|
|
869
|
+
plan_done=$(grep -c '^[[:space:]]*- \[x\]' "$plan" 2>/dev/null || true)
|
|
870
|
+
plan_total="${plan_total:-0}"
|
|
871
|
+
plan_done="${plan_done:-0}"
|
|
872
|
+
plan_pending=$((plan_total - plan_done))
|
|
873
|
+
if [ "$plan_total" -gt 0 ]; then
|
|
874
|
+
echo " Plan tasks: ${plan_done}/${plan_total} done, ${plan_pending} pending"
|
|
875
|
+
fi
|
|
876
|
+
fi
|
|
715
877
|
echo ""
|
|
716
878
|
if [ "$build_pause" = "plan-ready" ] && [ -n "$plan" ] && [ "$plan" != "null" ] && [ -f "$plan" ] && { [ "$isolation" = "null" ] || [ -z "$isolation" ] || [ "$build_mode" = "null" ] || [ -z "$build_mode" ]; }; then
|
|
717
879
|
echo "Recovery action: Plan-ready pause detected. Ask the user whether to continue, then choose isolation and build mode without regenerating the plan."
|
|
718
880
|
elif [ "$build_pause" = "plan-ready" ] && { [ -z "$plan" ] || [ "$plan" = "null" ] || [ ! -f "$plan" ]; }; then
|
|
719
881
|
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
882
|
elif [ "$build_pause" = "plan-ready" ]; then
|
|
721
|
-
|
|
883
|
+
if [ "$build_mode" = "subagent-driven-development" ] && { [ "$pending" -gt 0 ] || [ "$plan_pending" -gt 0 ]; }; then
|
|
884
|
+
if [ "$subagent_dispatch" = "confirmed" ]; then
|
|
885
|
+
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."
|
|
886
|
+
else
|
|
887
|
+
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."
|
|
888
|
+
fi
|
|
889
|
+
elif [ "$pending" -gt 0 ] || [ "$plan_pending" -gt 0 ]; then
|
|
890
|
+
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."
|
|
891
|
+
else
|
|
892
|
+
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."
|
|
893
|
+
fi
|
|
722
894
|
elif [ "$isolation" = "null" ] || [ -z "$isolation" ]; then
|
|
723
|
-
echo "Recovery action: Isolation not selected. Use
|
|
895
|
+
echo "Recovery action: Isolation not selected. Use the current platform's user confirmation mechanism to ask user for branch/worktree choice."
|
|
724
896
|
elif [ "$build_mode" = "null" ] || [ -z "$build_mode" ]; then
|
|
725
|
-
echo "Recovery action: Build mode not selected. Use
|
|
897
|
+
echo "Recovery action: Build mode not selected. Use the current platform's user confirmation mechanism to ask user for execution method."
|
|
898
|
+
elif [ -z "$tdd_mode" ] || [ "$tdd_mode" = "null" ]; then
|
|
899
|
+
echo "Recovery action: TDD mode not selected. Use the current platform's user confirmation mechanism to ask user for tdd or direct."
|
|
726
900
|
elif [ ! -f "$tasks_file" ]; then
|
|
727
901
|
echo "Recovery action: tasks.md missing. Verify change directory integrity."
|
|
728
902
|
elif [ "$pending" -gt 0 ]; then
|
|
729
|
-
|
|
903
|
+
if [ "$build_mode" = "subagent-driven-development" ]; then
|
|
904
|
+
if [ "$subagent_dispatch" = "confirmed" ]; then
|
|
905
|
+
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."
|
|
906
|
+
else
|
|
907
|
+
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."
|
|
908
|
+
fi
|
|
909
|
+
else
|
|
910
|
+
echo "Recovery action: Read tasks.md and continue from first unchecked task."
|
|
911
|
+
fi
|
|
912
|
+
elif [ "$plan_pending" -gt 0 ]; then
|
|
913
|
+
if [ "$build_mode" = "subagent-driven-development" ]; then
|
|
914
|
+
if [ "$subagent_dispatch" = "confirmed" ]; then
|
|
915
|
+
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."
|
|
916
|
+
else
|
|
917
|
+
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."
|
|
918
|
+
fi
|
|
919
|
+
else
|
|
920
|
+
echo "Recovery action: Read the Superpowers plan and continue from the first unchecked plan task."
|
|
921
|
+
fi
|
|
730
922
|
else
|
|
731
923
|
echo "Recovery action: All tasks done. Run guard to transition to verify."
|
|
732
924
|
fi
|
|
@@ -799,7 +991,7 @@ cmd_scale() {
|
|
|
799
991
|
local plan_file base_ref=""
|
|
800
992
|
plan_file=$(cmd_get "$change_name" "plan" 2>/dev/null || true)
|
|
801
993
|
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: *//')
|
|
994
|
+
base_ref=$(grep '^base-ref:' "$plan_file" 2>/dev/null | head -1 | sed 's/^base-ref: *//' || true)
|
|
803
995
|
fi
|
|
804
996
|
# Fallback to base_ref stored in .comet.yaml (set during init)
|
|
805
997
|
if [ -z "$base_ref" ] || [ "$base_ref" = "null" ]; then
|
|
@@ -832,6 +1024,81 @@ cmd_scale() {
|
|
|
832
1024
|
green "[SCALE] verify_mode=$result"
|
|
833
1025
|
}
|
|
834
1026
|
|
|
1027
|
+
# Resolve the next workflow step after a guard --apply phase advance.
|
|
1028
|
+
# Reads the (already advanced) phase, workflow, and auto_transition, then emits
|
|
1029
|
+
# a deterministic next-step contract so skills don't hardcode the next skill name.
|
|
1030
|
+
#
|
|
1031
|
+
# Output contract (stdout):
|
|
1032
|
+
# NEXT: auto|manual|done
|
|
1033
|
+
# SKILL: <skill-name> (omitted when NEXT=done)
|
|
1034
|
+
# HINT: <message> (only when NEXT=manual)
|
|
1035
|
+
cmd_next() {
|
|
1036
|
+
local change_name="$1"
|
|
1037
|
+
validate_change_name "$change_name"
|
|
1038
|
+
|
|
1039
|
+
local change_dir="openspec/changes/$change_name"
|
|
1040
|
+
local yaml_file="$change_dir/.comet.yaml"
|
|
1041
|
+
if [ ! -f "$yaml_file" ]; then
|
|
1042
|
+
red "ERROR: .comet.yaml not found at $yaml_file" >&2
|
|
1043
|
+
exit 1
|
|
1044
|
+
fi
|
|
1045
|
+
|
|
1046
|
+
local phase workflow auto_transition archived
|
|
1047
|
+
phase=$(cmd_get "$change_name" "phase" 2>/dev/null || true)
|
|
1048
|
+
workflow=$(cmd_get "$change_name" "workflow" 2>/dev/null || true)
|
|
1049
|
+
auto_transition=$(cmd_get "$change_name" "auto_transition" 2>/dev/null || true)
|
|
1050
|
+
archived=$(cmd_get "$change_name" "archived" 2>/dev/null || true)
|
|
1051
|
+
|
|
1052
|
+
# Change-level auto_transition overrides project-level; fall back to project default
|
|
1053
|
+
if [ -z "$auto_transition" ] || [ "$auto_transition" = "null" ]; then
|
|
1054
|
+
auto_transition="$(project_auto_transition_default)"
|
|
1055
|
+
fi
|
|
1056
|
+
|
|
1057
|
+
# Terminal state: archived change has no next step.
|
|
1058
|
+
if [ "$archived" = "true" ]; then
|
|
1059
|
+
echo "NEXT: done"
|
|
1060
|
+
return 0
|
|
1061
|
+
fi
|
|
1062
|
+
|
|
1063
|
+
# Map the current (post-advance) phase to the skill that owns it.
|
|
1064
|
+
local skill=""
|
|
1065
|
+
case "$phase" in
|
|
1066
|
+
open)
|
|
1067
|
+
skill="comet-open"
|
|
1068
|
+
;;
|
|
1069
|
+
design)
|
|
1070
|
+
skill="comet-design"
|
|
1071
|
+
;;
|
|
1072
|
+
build)
|
|
1073
|
+
case "$workflow" in
|
|
1074
|
+
hotfix) skill="comet-hotfix" ;;
|
|
1075
|
+
tweak) skill="comet-tweak" ;;
|
|
1076
|
+
*) skill="comet-build" ;;
|
|
1077
|
+
esac
|
|
1078
|
+
;;
|
|
1079
|
+
verify)
|
|
1080
|
+
skill="comet-verify"
|
|
1081
|
+
;;
|
|
1082
|
+
archive)
|
|
1083
|
+
skill="comet-archive"
|
|
1084
|
+
;;
|
|
1085
|
+
*)
|
|
1086
|
+
red "ERROR: Cannot resolve next step for '$change_name': unknown phase '${phase:-null}'" >&2
|
|
1087
|
+
exit 1
|
|
1088
|
+
;;
|
|
1089
|
+
esac
|
|
1090
|
+
|
|
1091
|
+
# auto_transition=false pauses the next skill invocation only; phase is already advanced.
|
|
1092
|
+
if [ "$auto_transition" = "false" ]; then
|
|
1093
|
+
echo "NEXT: manual"
|
|
1094
|
+
echo "SKILL: $skill"
|
|
1095
|
+
echo "HINT: phase is '$phase'; run /$skill manually to continue"
|
|
1096
|
+
else
|
|
1097
|
+
echo "NEXT: auto"
|
|
1098
|
+
echo "SKILL: $skill"
|
|
1099
|
+
fi
|
|
1100
|
+
}
|
|
1101
|
+
|
|
835
1102
|
# --- Main ---
|
|
836
1103
|
|
|
837
1104
|
SUBCOMMAND="${1:-}"
|
|
@@ -863,7 +1130,7 @@ case "$SUBCOMMAND" in
|
|
|
863
1130
|
transition)
|
|
864
1131
|
if [ $# -lt 2 ]; then
|
|
865
1132
|
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
|
|
1133
|
+
red "Events: open-complete, design-complete, build-complete, verify-pass, verify-fail, archive-reopen, archived" >&2
|
|
867
1134
|
exit 1
|
|
868
1135
|
fi
|
|
869
1136
|
cmd_transition "$@"
|
|
@@ -888,6 +1155,13 @@ case "$SUBCOMMAND" in
|
|
|
888
1155
|
fi
|
|
889
1156
|
cmd_scale "$@"
|
|
890
1157
|
;;
|
|
1158
|
+
next)
|
|
1159
|
+
if [ $# -lt 1 ]; then
|
|
1160
|
+
red "Usage: comet-state.sh next <change-name>" >&2
|
|
1161
|
+
exit 1
|
|
1162
|
+
fi
|
|
1163
|
+
cmd_next "$@"
|
|
1164
|
+
;;
|
|
891
1165
|
*)
|
|
892
1166
|
red "Unknown subcommand: $SUBCOMMAND" >&2
|
|
893
1167
|
echo "" >&2
|
|
@@ -900,6 +1174,7 @@ case "$SUBCOMMAND" in
|
|
|
900
1174
|
echo " transition <change-name> <event> — Apply a validated state transition" >&2
|
|
901
1175
|
echo " check <change-name> <phase> — Verify entry requirements for a phase" >&2
|
|
902
1176
|
echo " scale <change-name> — Assess and set verification mode based on metrics" >&2
|
|
1177
|
+
echo " next <change-name> — Resolve the next workflow step (auto/manual/done)" >&2
|
|
903
1178
|
echo "" >&2
|
|
904
1179
|
echo "Workflows: full, hotfix, tweak" >&2
|
|
905
1180
|
echo "Phases for check: open, design, build, verify, archive" >&2
|
|
@@ -123,12 +123,29 @@ validate_enum() {
|
|
|
123
123
|
fail "$field='$value' is not valid. Expected: $valid_values"
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
+
validate_required_enum() {
|
|
127
|
+
local field="$1" value="$2"
|
|
128
|
+
shift 2
|
|
129
|
+
local valid_values="$*"
|
|
130
|
+
|
|
131
|
+
if [ -z "$value" ] || [ "$value" = "null" ]; then
|
|
132
|
+
fail "$field='${value:-}' is not valid. Expected: $valid_values"
|
|
133
|
+
return 0
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
validate_enum "$field" "$value" "$@"
|
|
137
|
+
}
|
|
138
|
+
|
|
126
139
|
workflow=$(field_value "workflow")
|
|
127
140
|
phase=$(field_value "phase")
|
|
141
|
+
context_compression=$(field_value "context_compression")
|
|
128
142
|
build_mode=$(field_value "build_mode")
|
|
129
143
|
build_pause=$(field_value "build_pause")
|
|
144
|
+
subagent_dispatch=$(field_value "subagent_dispatch")
|
|
145
|
+
tdd_mode=$(field_value "tdd_mode")
|
|
130
146
|
isolation=$(field_value "isolation")
|
|
131
147
|
verify_mode=$(field_value "verify_mode")
|
|
148
|
+
auto_transition=$(field_value "auto_transition")
|
|
132
149
|
verify_result=$(field_value "verify_result")
|
|
133
150
|
branch_status=$(field_value "branch_status")
|
|
134
151
|
archived=$(field_value "archived")
|
|
@@ -140,10 +157,16 @@ handoff_hash=$(field_value "handoff_hash")
|
|
|
140
157
|
|
|
141
158
|
validate_enum "workflow" "$workflow" "full hotfix tweak"
|
|
142
159
|
validate_enum "phase" "$phase" "open design build verify archive"
|
|
160
|
+
validate_enum "context_compression" "$context_compression" "off beta"
|
|
143
161
|
validate_enum "build_mode" "$build_mode" "subagent-driven-development executing-plans direct"
|
|
144
162
|
validate_enum "build_pause" "$build_pause" "null plan-ready"
|
|
163
|
+
validate_enum "subagent_dispatch" "$subagent_dispatch" "null confirmed"
|
|
164
|
+
validate_enum "tdd_mode" "$tdd_mode" "tdd direct null"
|
|
145
165
|
validate_enum "isolation" "$isolation" "branch worktree"
|
|
146
166
|
validate_enum "verify_mode" "$verify_mode" "light full"
|
|
167
|
+
if grep -q "^auto_transition:" "$YAML" 2>/dev/null; then
|
|
168
|
+
validate_required_enum "auto_transition" "$auto_transition" "true false"
|
|
169
|
+
fi
|
|
147
170
|
validate_enum "verify_result" "$verify_result" "pending pass fail"
|
|
148
171
|
validate_enum "branch_status" "$branch_status" "pending handled"
|
|
149
172
|
validate_enum "archived" "$archived" "true false"
|
|
@@ -176,7 +199,7 @@ if [ -n "$handoff_hash" ] && [ "$handoff_hash" != "null" ]; then
|
|
|
176
199
|
fi
|
|
177
200
|
|
|
178
201
|
# --- Unknown keys check ---
|
|
179
|
-
KNOWN_KEYS="workflow phase design_doc plan build_mode build_pause isolation verify_mode verify_result verification_report branch_status verified_at created_at archived direct_override build_command verify_command handoff_context handoff_hash base_ref"
|
|
202
|
+
KNOWN_KEYS="workflow phase context_compression design_doc plan build_mode build_pause subagent_dispatch tdd_mode isolation verify_mode auto_transition verify_result verification_report branch_status verified_at created_at archived direct_override build_command verify_command handoff_context handoff_hash base_ref"
|
|
180
203
|
while IFS=: read -r key _; do
|
|
181
204
|
key="${key// /}"
|
|
182
205
|
[ -z "$key" ] && continue
|