@rpamis/comet 0.3.5 → 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 +180 -56
- package/assets/manifest.json +11 -1
- package/assets/skills/comet/SKILL.md +59 -24
- 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 +75 -57
- package/assets/skills/comet/scripts/comet-env.sh +57 -2
- package/assets/skills/comet/scripts/comet-guard.sh +187 -29
- package/assets/skills/comet/scripts/comet-handoff.sh +137 -8
- package/assets/skills/comet/scripts/comet-hook-guard.sh +260 -0
- package/assets/skills/comet/scripts/comet-state.sh +312 -25
- package/assets/skills/comet/scripts/comet-yaml-validate.sh +26 -1
- package/assets/skills/comet-archive/SKILL.md +45 -12
- package/assets/skills/comet-build/SKILL.md +159 -33
- package/assets/skills/comet-design/SKILL.md +129 -23
- package/assets/skills/comet-hotfix/SKILL.md +57 -15
- package/assets/skills/comet-open/SKILL.md +90 -17
- package/assets/skills/comet-tweak/SKILL.md +40 -15
- package/assets/skills/comet-verify/SKILL.md +64 -25
- package/assets/skills-zh/comet/SKILL.md +63 -25
- package/assets/skills-zh/comet/reference/dirty-worktree.md +2 -1
- package/assets/skills-zh/comet-archive/SKILL.md +46 -13
- package/assets/skills-zh/comet-build/SKILL.md +161 -35
- package/assets/skills-zh/comet-design/SKILL.md +132 -25
- package/assets/skills-zh/comet-hotfix/SKILL.md +53 -15
- package/assets/skills-zh/comet-open/SKILL.md +90 -17
- package/assets/skills-zh/comet-tweak/SKILL.md +36 -15
- package/assets/skills-zh/comet-verify/SKILL.md +64 -27
- 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,16 +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
|
|
292
|
+
build_pause: null
|
|
293
|
+
subagent_dispatch: null
|
|
294
|
+
tdd_mode: $tdd_mode
|
|
208
295
|
isolation: $isolation
|
|
209
296
|
verify_mode: $verify_mode
|
|
297
|
+
auto_transition: $auto_transition
|
|
210
298
|
base_ref: $base_ref
|
|
211
299
|
design_doc: null
|
|
212
300
|
plan: null
|
|
213
301
|
verify_result: pending
|
|
214
302
|
verification_report: null
|
|
215
303
|
branch_status: pending
|
|
216
|
-
created_at: $(date +%Y-%m-%d)
|
|
304
|
+
created_at: $(date -u +%Y-%m-%d)
|
|
217
305
|
verified_at: null
|
|
218
306
|
archived: false
|
|
219
307
|
EOF
|
|
@@ -239,6 +327,9 @@ cmd_get() {
|
|
|
239
327
|
# Read and output the field value
|
|
240
328
|
local value
|
|
241
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
|
|
242
333
|
echo "${value:-}"
|
|
243
334
|
}
|
|
244
335
|
|
|
@@ -264,14 +355,14 @@ cmd_set() {
|
|
|
264
355
|
yellow "WARNING: Setting 'phase' directly bypasses state machine constraints." >&2
|
|
265
356
|
yellow " Consider using: comet-state.sh transition <change-name> <event>" >&2
|
|
266
357
|
;;
|
|
267
|
-
workflow|build_mode|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)
|
|
268
359
|
# Valid field
|
|
269
360
|
;;
|
|
270
361
|
*)
|
|
271
362
|
red "ERROR: Unknown field: '$field'" >&2
|
|
272
363
|
red "Valid fields:" >&2
|
|
273
|
-
red " workflow, phase, design_doc, plan, build_mode, isolation," >&2
|
|
274
|
-
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
|
|
275
366
|
red " verified_at, created_at, archived, base_ref, direct_override," >&2
|
|
276
367
|
red " build_command, verify_command, handoff_context, handoff_hash" >&2
|
|
277
368
|
exit 1
|
|
@@ -283,18 +374,33 @@ cmd_set() {
|
|
|
283
374
|
workflow)
|
|
284
375
|
validate_enum "$value" "full" "hotfix" "tweak"
|
|
285
376
|
;;
|
|
377
|
+
context_compression)
|
|
378
|
+
validate_enum "$value" "off" "beta"
|
|
379
|
+
;;
|
|
286
380
|
phase)
|
|
287
381
|
validate_enum "$value" "open" "design" "build" "verify" "archive"
|
|
288
382
|
;;
|
|
289
383
|
build_mode)
|
|
290
384
|
validate_enum "$value" "subagent-driven-development" "executing-plans" "direct"
|
|
291
385
|
;;
|
|
386
|
+
build_pause)
|
|
387
|
+
validate_enum "$value" "null" "plan-ready"
|
|
388
|
+
;;
|
|
389
|
+
subagent_dispatch)
|
|
390
|
+
validate_enum "$value" "null" "confirmed"
|
|
391
|
+
;;
|
|
392
|
+
tdd_mode)
|
|
393
|
+
validate_enum "$value" "tdd" "direct"
|
|
394
|
+
;;
|
|
292
395
|
isolation)
|
|
293
396
|
validate_enum "$value" "branch" "worktree"
|
|
294
397
|
;;
|
|
295
398
|
verify_mode)
|
|
296
399
|
validate_enum "$value" "light" "full"
|
|
297
400
|
;;
|
|
401
|
+
auto_transition)
|
|
402
|
+
validate_enum "$value" "true" "false"
|
|
403
|
+
;;
|
|
298
404
|
verify_result)
|
|
299
405
|
validate_enum "$value" "pending" "pass" "fail"
|
|
300
406
|
;;
|
|
@@ -307,8 +413,11 @@ cmd_set() {
|
|
|
307
413
|
direct_override)
|
|
308
414
|
validate_enum "$value" "true" "false"
|
|
309
415
|
;;
|
|
310
|
-
design_doc|plan|verification_report|
|
|
311
|
-
|
|
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
|
|
312
421
|
;;
|
|
313
422
|
esac
|
|
314
423
|
|
|
@@ -353,11 +462,13 @@ require_verification_evidence() {
|
|
|
353
462
|
|
|
354
463
|
require_build_decisions() {
|
|
355
464
|
local change_name="$1"
|
|
356
|
-
local workflow build_mode isolation direct_override
|
|
465
|
+
local workflow build_mode isolation direct_override subagent_dispatch tdd_mode
|
|
357
466
|
workflow=$(cmd_get "$change_name" "workflow")
|
|
358
467
|
build_mode=$(cmd_get "$change_name" "build_mode")
|
|
359
468
|
isolation=$(cmd_get "$change_name" "isolation")
|
|
360
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)
|
|
361
472
|
|
|
362
473
|
case "$isolation" in
|
|
363
474
|
branch|worktree) ;;
|
|
@@ -379,6 +490,16 @@ require_build_decisions() {
|
|
|
379
490
|
red "ERROR: Cannot transition '$change_name': build_mode=direct is only allowed for hotfix/tweak unless direct_override=true" >&2
|
|
380
491
|
exit 1
|
|
381
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
|
|
382
503
|
}
|
|
383
504
|
|
|
384
505
|
cmd_transition() {
|
|
@@ -386,7 +507,7 @@ cmd_transition() {
|
|
|
386
507
|
local event="$2"
|
|
387
508
|
|
|
388
509
|
validate_change_name "$change_name"
|
|
389
|
-
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"
|
|
390
511
|
|
|
391
512
|
case "$event" in
|
|
392
513
|
open-complete)
|
|
@@ -406,23 +527,41 @@ cmd_transition() {
|
|
|
406
527
|
build-complete)
|
|
407
528
|
require_phase "$change_name" "build"
|
|
408
529
|
require_build_decisions "$change_name"
|
|
530
|
+
local current_verify_result
|
|
531
|
+
current_verify_result=$(cmd_get "$change_name" "verify_result")
|
|
409
532
|
cmd_set "$change_name" phase verify
|
|
410
533
|
cmd_set "$change_name" verify_result pending
|
|
411
|
-
|
|
412
|
-
|
|
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
|
|
413
540
|
;;
|
|
414
541
|
verify-pass)
|
|
415
542
|
require_phase "$change_name" "verify"
|
|
416
543
|
require_verification_evidence "$change_name"
|
|
417
544
|
cmd_set "$change_name" verify_result pass
|
|
418
545
|
cmd_set "$change_name" phase archive
|
|
419
|
-
cmd_set "$change_name" verified_at "$(date +%Y-%m-%d)"
|
|
546
|
+
cmd_set "$change_name" verified_at "$(date -u +%Y-%m-%d)"
|
|
420
547
|
;;
|
|
421
548
|
verify-fail)
|
|
422
549
|
require_phase "$change_name" "verify"
|
|
423
550
|
cmd_set "$change_name" verify_result fail
|
|
424
551
|
cmd_set "$change_name" phase build
|
|
425
|
-
|
|
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
|
|
426
565
|
;;
|
|
427
566
|
archived)
|
|
428
567
|
require_phase "$change_name" "archive"
|
|
@@ -632,7 +771,7 @@ cmd_recover() {
|
|
|
632
771
|
|
|
633
772
|
# Read all relevant fields
|
|
634
773
|
local design_doc plan verify_result verify_mode verification_report
|
|
635
|
-
local branch_status handoff_context handoff_hash isolation build_mode direct_override
|
|
774
|
+
local branch_status handoff_context handoff_hash isolation build_mode build_pause subagent_dispatch tdd_mode direct_override
|
|
636
775
|
design_doc=$(cmd_get "$change_name" "design_doc")
|
|
637
776
|
plan=$(cmd_get "$change_name" "plan")
|
|
638
777
|
verify_result=$(cmd_get "$change_name" "verify_result")
|
|
@@ -643,6 +782,9 @@ cmd_recover() {
|
|
|
643
782
|
handoff_hash=$(cmd_get "$change_name" "handoff_hash")
|
|
644
783
|
isolation=$(cmd_get "$change_name" "isolation")
|
|
645
784
|
build_mode=$(cmd_get "$change_name" "build_mode")
|
|
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)
|
|
646
788
|
direct_override=$(cmd_get "$change_name" "direct_override" 2>/dev/null || true)
|
|
647
789
|
|
|
648
790
|
echo "State fields:"
|
|
@@ -651,15 +793,23 @@ cmd_recover() {
|
|
|
651
793
|
case "$phase" in
|
|
652
794
|
open)
|
|
653
795
|
echo " Artifacts:"
|
|
796
|
+
local artifacts_done=0
|
|
654
797
|
for f in proposal.md design.md tasks.md; do
|
|
655
798
|
if file_nonempty "$change_dir/$f"; then
|
|
656
799
|
echo " - ${f}: DONE"
|
|
800
|
+
artifacts_done=$((artifacts_done + 1))
|
|
657
801
|
else
|
|
658
802
|
echo " - ${f}: PENDING"
|
|
659
803
|
fi
|
|
660
804
|
done
|
|
661
805
|
echo ""
|
|
662
|
-
|
|
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
|
|
663
813
|
;;
|
|
664
814
|
design)
|
|
665
815
|
echo " Artifacts:"
|
|
@@ -688,6 +838,11 @@ cmd_recover() {
|
|
|
688
838
|
echo " Build decisions:"
|
|
689
839
|
field_status "isolation" "$isolation"
|
|
690
840
|
field_status "build_mode" "$build_mode"
|
|
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
|
|
691
846
|
if [ "$build_mode" = "direct" ] && [ "$workflow" != "hotfix" ] && [ "$workflow" != "tweak" ]; then
|
|
692
847
|
field_status "direct_override" "$direct_override"
|
|
693
848
|
fi
|
|
@@ -698,23 +853,72 @@ cmd_recover() {
|
|
|
698
853
|
# Count completed vs pending tasks
|
|
699
854
|
local tasks_file="$change_dir/tasks.md"
|
|
700
855
|
local total=0 done=0 pending=0
|
|
856
|
+
local plan_total=0 plan_done=0 plan_pending=0
|
|
701
857
|
if [ -f "$tasks_file" ]; then
|
|
702
|
-
total=$(grep -c '
|
|
703
|
-
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}"
|
|
704
862
|
pending=$((total - done))
|
|
705
863
|
echo " Tasks: ${done}/${total} done, ${pending} pending"
|
|
706
864
|
else
|
|
707
865
|
echo " Tasks: tasks.md MISSING"
|
|
708
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
|
|
709
877
|
echo ""
|
|
710
|
-
if [ "$isolation" = "null" ] || [ -z "$isolation" ]; then
|
|
711
|
-
echo "Recovery action:
|
|
878
|
+
if [ "$build_pause" = "plan-ready" ] && [ -n "$plan" ] && [ "$plan" != "null" ] && [ -f "$plan" ] && { [ "$isolation" = "null" ] || [ -z "$isolation" ] || [ "$build_mode" = "null" ] || [ -z "$build_mode" ]; }; then
|
|
879
|
+
echo "Recovery action: Plan-ready pause detected. Ask the user whether to continue, then choose isolation and build mode without regenerating the plan."
|
|
880
|
+
elif [ "$build_pause" = "plan-ready" ] && { [ -z "$plan" ] || [ "$plan" = "null" ] || [ ! -f "$plan" ]; }; then
|
|
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."
|
|
882
|
+
elif [ "$build_pause" = "plan-ready" ]; then
|
|
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
|
|
894
|
+
elif [ "$isolation" = "null" ] || [ -z "$isolation" ]; then
|
|
895
|
+
echo "Recovery action: Isolation not selected. Use the current platform's user confirmation mechanism to ask user for branch/worktree choice."
|
|
712
896
|
elif [ "$build_mode" = "null" ] || [ -z "$build_mode" ]; then
|
|
713
|
-
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."
|
|
714
900
|
elif [ ! -f "$tasks_file" ]; then
|
|
715
901
|
echo "Recovery action: tasks.md missing. Verify change directory integrity."
|
|
716
902
|
elif [ "$pending" -gt 0 ]; then
|
|
717
|
-
|
|
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
|
|
718
922
|
else
|
|
719
923
|
echo "Recovery action: All tasks done. Run guard to transition to verify."
|
|
720
924
|
fi
|
|
@@ -787,7 +991,7 @@ cmd_scale() {
|
|
|
787
991
|
local plan_file base_ref=""
|
|
788
992
|
plan_file=$(cmd_get "$change_name" "plan" 2>/dev/null || true)
|
|
789
993
|
if [ -n "$plan_file" ] && [ "$plan_file" != "null" ] && [ -f "$plan_file" ]; then
|
|
790
|
-
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)
|
|
791
995
|
fi
|
|
792
996
|
# Fallback to base_ref stored in .comet.yaml (set during init)
|
|
793
997
|
if [ -z "$base_ref" ] || [ "$base_ref" = "null" ]; then
|
|
@@ -820,6 +1024,81 @@ cmd_scale() {
|
|
|
820
1024
|
green "[SCALE] verify_mode=$result"
|
|
821
1025
|
}
|
|
822
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
|
+
|
|
823
1102
|
# --- Main ---
|
|
824
1103
|
|
|
825
1104
|
SUBCOMMAND="${1:-}"
|
|
@@ -851,7 +1130,7 @@ case "$SUBCOMMAND" in
|
|
|
851
1130
|
transition)
|
|
852
1131
|
if [ $# -lt 2 ]; then
|
|
853
1132
|
red "Usage: comet-state.sh transition <change-name> <event>" >&2
|
|
854
|
-
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
|
|
855
1134
|
exit 1
|
|
856
1135
|
fi
|
|
857
1136
|
cmd_transition "$@"
|
|
@@ -876,6 +1155,13 @@ case "$SUBCOMMAND" in
|
|
|
876
1155
|
fi
|
|
877
1156
|
cmd_scale "$@"
|
|
878
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
|
+
;;
|
|
879
1165
|
*)
|
|
880
1166
|
red "Unknown subcommand: $SUBCOMMAND" >&2
|
|
881
1167
|
echo "" >&2
|
|
@@ -888,6 +1174,7 @@ case "$SUBCOMMAND" in
|
|
|
888
1174
|
echo " transition <change-name> <event> — Apply a validated state transition" >&2
|
|
889
1175
|
echo " check <change-name> <phase> — Verify entry requirements for a phase" >&2
|
|
890
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
|
|
891
1178
|
echo "" >&2
|
|
892
1179
|
echo "Workflows: full, hotfix, tweak" >&2
|
|
893
1180
|
echo "Phases for check: open, design, build, verify, archive" >&2
|
|
@@ -123,11 +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")
|
|
143
|
+
build_pause=$(field_value "build_pause")
|
|
144
|
+
subagent_dispatch=$(field_value "subagent_dispatch")
|
|
145
|
+
tdd_mode=$(field_value "tdd_mode")
|
|
129
146
|
isolation=$(field_value "isolation")
|
|
130
147
|
verify_mode=$(field_value "verify_mode")
|
|
148
|
+
auto_transition=$(field_value "auto_transition")
|
|
131
149
|
verify_result=$(field_value "verify_result")
|
|
132
150
|
branch_status=$(field_value "branch_status")
|
|
133
151
|
archived=$(field_value "archived")
|
|
@@ -139,9 +157,16 @@ handoff_hash=$(field_value "handoff_hash")
|
|
|
139
157
|
|
|
140
158
|
validate_enum "workflow" "$workflow" "full hotfix tweak"
|
|
141
159
|
validate_enum "phase" "$phase" "open design build verify archive"
|
|
160
|
+
validate_enum "context_compression" "$context_compression" "off beta"
|
|
142
161
|
validate_enum "build_mode" "$build_mode" "subagent-driven-development executing-plans direct"
|
|
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"
|
|
143
165
|
validate_enum "isolation" "$isolation" "branch worktree"
|
|
144
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
|
|
145
170
|
validate_enum "verify_result" "$verify_result" "pending pass fail"
|
|
146
171
|
validate_enum "branch_status" "$branch_status" "pending handled"
|
|
147
172
|
validate_enum "archived" "$archived" "true false"
|
|
@@ -174,7 +199,7 @@ if [ -n "$handoff_hash" ] && [ "$handoff_hash" != "null" ]; then
|
|
|
174
199
|
fi
|
|
175
200
|
|
|
176
201
|
# --- Unknown keys check ---
|
|
177
|
-
KNOWN_KEYS="workflow phase design_doc plan build_mode 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"
|
|
178
203
|
while IFS=: read -r key _; do
|
|
179
204
|
key="${key// /}"
|
|
180
205
|
[ -z "$key" ] && continue
|