@rpamis/comet 0.4.0-beta.3 → 0.4.0-beta.4
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/README.md +10 -0
- package/assets/manifest.json +2 -1
- package/assets/skills/comet/SKILL.md +25 -1
- package/assets/skills/comet/reference/comet-yaml-fields.md +5 -2
- package/assets/skills/comet/reference/context-recovery.md +10 -0
- package/assets/skills/comet/reference/scripts.md +47 -15
- package/assets/skills/comet/rules/comet-phase-guard.en.md +8 -0
- package/assets/skills/comet/rules/comet-phase-guard.md +9 -1
- package/assets/skills/comet/scripts/comet-resume-probe.mjs +3 -0
- package/assets/skills/comet/scripts/comet-runtime.mjs +1387 -424
- package/assets/skills/comet-archive/SKILL.md +24 -11
- package/assets/skills/comet-build/SKILL.md +42 -25
- package/assets/skills/comet-design/SKILL.md +1 -0
- package/assets/skills/comet-hotfix/SKILL.md +3 -1
- package/assets/skills/comet-open/SKILL.md +11 -1
- package/assets/skills/comet-tweak/SKILL.md +3 -1
- package/assets/skills/comet-verify/SKILL.md +29 -20
- package/assets/skills-zh/comet/SKILL.md +25 -1
- package/assets/skills-zh/comet/reference/comet-yaml-fields.md +5 -2
- package/assets/skills-zh/comet/reference/context-recovery.md +10 -0
- package/assets/skills-zh/comet/reference/scripts.md +47 -15
- package/assets/skills-zh/comet-archive/SKILL.md +24 -11
- package/assets/skills-zh/comet-build/SKILL.md +42 -25
- package/assets/skills-zh/comet-design/SKILL.md +1 -0
- package/assets/skills-zh/comet-hotfix/SKILL.md +3 -1
- package/assets/skills-zh/comet-open/SKILL.md +11 -1
- package/assets/skills-zh/comet-tweak/SKILL.md +3 -1
- package/assets/skills-zh/comet-verify/SKILL.md +30 -21
- package/dist/app/cli/comet-banner.d.ts +25 -0
- package/dist/app/cli/comet-banner.d.ts.map +1 -0
- package/dist/app/cli/comet-banner.js +265 -0
- package/dist/app/cli/comet-banner.js.map +1 -0
- package/dist/app/cli/index.js +36 -2
- package/dist/app/cli/index.js.map +1 -1
- package/dist/app/commands/classic.d.ts +4 -0
- package/dist/app/commands/classic.d.ts.map +1 -0
- package/dist/app/commands/classic.js +11 -0
- package/dist/app/commands/classic.js.map +1 -0
- package/dist/app/commands/doctor.d.ts.map +1 -1
- package/dist/app/commands/doctor.js +39 -24
- package/dist/app/commands/doctor.js.map +1 -1
- package/dist/app/commands/eval.d.ts.map +1 -1
- package/dist/app/commands/eval.js +57 -20
- package/dist/app/commands/eval.js.map +1 -1
- package/dist/app/commands/i18n.d.ts +1 -1
- package/dist/app/commands/i18n.d.ts.map +1 -1
- package/dist/app/commands/i18n.js +12 -4
- package/dist/app/commands/i18n.js.map +1 -1
- package/dist/app/commands/init.d.ts.map +1 -1
- package/dist/app/commands/init.js +16 -13
- package/dist/app/commands/init.js.map +1 -1
- package/dist/app/commands/project-scope-selection.d.ts +13 -0
- package/dist/app/commands/project-scope-selection.d.ts.map +1 -0
- package/dist/app/commands/project-scope-selection.js +33 -0
- package/dist/app/commands/project-scope-selection.js.map +1 -0
- package/dist/app/commands/resume-probe.d.ts +11 -0
- package/dist/app/commands/resume-probe.d.ts.map +1 -0
- package/dist/app/commands/resume-probe.js +63 -0
- package/dist/app/commands/resume-probe.js.map +1 -0
- package/dist/app/commands/status.d.ts +31 -0
- package/dist/app/commands/status.d.ts.map +1 -1
- package/dist/app/commands/status.js +109 -20
- package/dist/app/commands/status.js.map +1 -1
- package/dist/app/commands/uninstall.d.ts +2 -0
- package/dist/app/commands/uninstall.d.ts.map +1 -1
- package/dist/app/commands/uninstall.js +217 -40
- package/dist/app/commands/uninstall.js.map +1 -1
- package/dist/app/commands/update.d.ts +6 -0
- package/dist/app/commands/update.d.ts.map +1 -1
- package/dist/app/commands/update.js +361 -68
- package/dist/app/commands/update.js.map +1 -1
- package/dist/config/repository-layout.json +4 -2
- package/dist/domains/bundle/bundle-platform.d.ts.map +1 -1
- package/dist/domains/bundle/bundle-platform.js +3 -1
- package/dist/domains/bundle/bundle-platform.js.map +1 -1
- package/dist/domains/bundle/eval-manifest-runtime.d.ts +6 -0
- package/dist/domains/bundle/eval-manifest-runtime.d.ts.map +1 -0
- package/dist/domains/bundle/eval-manifest-runtime.js +58 -0
- package/dist/domains/bundle/eval-manifest-runtime.js.map +1 -0
- package/dist/domains/bundle/types.d.ts +1 -1
- package/dist/domains/bundle/types.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-archive.d.ts +1 -0
- package/dist/domains/comet-classic/classic-archive.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-archive.js +35 -38
- package/dist/domains/comet-classic/classic-archive.js.map +1 -1
- package/dist/domains/comet-classic/classic-cli.d.ts +1 -1
- package/dist/domains/comet-classic/classic-cli.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-cli.js +3 -0
- package/dist/domains/comet-classic/classic-cli.js.map +1 -1
- package/dist/domains/comet-classic/classic-command-checks.d.ts +20 -0
- package/dist/domains/comet-classic/classic-command-checks.d.ts.map +1 -0
- package/dist/domains/comet-classic/classic-command-checks.js +97 -0
- package/dist/domains/comet-classic/classic-command-checks.js.map +1 -0
- package/dist/domains/comet-classic/classic-current-change.d.ts +19 -0
- package/dist/domains/comet-classic/classic-current-change.d.ts.map +1 -0
- package/dist/domains/comet-classic/classic-current-change.js +132 -0
- package/dist/domains/comet-classic/classic-current-change.js.map +1 -0
- package/dist/domains/comet-classic/classic-diagnostics.d.ts +2 -1
- package/dist/domains/comet-classic/classic-diagnostics.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-diagnostics.js +54 -1
- package/dist/domains/comet-classic/classic-diagnostics.js.map +1 -1
- package/dist/domains/comet-classic/classic-evidence.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-evidence.js +4 -0
- package/dist/domains/comet-classic/classic-evidence.js.map +1 -1
- package/dist/domains/comet-classic/classic-guard.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-guard.js +68 -69
- package/dist/domains/comet-classic/classic-guard.js.map +1 -1
- package/dist/domains/comet-classic/classic-hook-guard.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-hook-guard.js +66 -23
- package/dist/domains/comet-classic/classic-hook-guard.js.map +1 -1
- package/dist/domains/comet-classic/classic-project-config.d.ts +16 -0
- package/dist/domains/comet-classic/classic-project-config.d.ts.map +1 -0
- package/dist/domains/comet-classic/classic-project-config.js +40 -0
- package/dist/domains/comet-classic/classic-project-config.js.map +1 -0
- package/dist/domains/comet-classic/classic-resolver.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-resolver.js +3 -3
- package/dist/domains/comet-classic/classic-resolver.js.map +1 -1
- package/dist/domains/comet-classic/classic-resume-probe-command.d.ts +3 -0
- package/dist/domains/comet-classic/classic-resume-probe-command.d.ts.map +1 -0
- package/dist/domains/comet-classic/classic-resume-probe-command.js +58 -0
- package/dist/domains/comet-classic/classic-resume-probe-command.js.map +1 -0
- package/dist/domains/comet-classic/classic-resume-probe-entry.d.ts +2 -0
- package/dist/domains/comet-classic/classic-resume-probe-entry.d.ts.map +1 -0
- package/dist/domains/comet-classic/classic-resume-probe-entry.js +4 -0
- package/dist/domains/comet-classic/classic-resume-probe-entry.js.map +1 -0
- package/dist/domains/comet-classic/classic-resume-probe.d.ts +29 -0
- package/dist/domains/comet-classic/classic-resume-probe.d.ts.map +1 -0
- package/dist/domains/comet-classic/classic-resume-probe.js +386 -0
- package/dist/domains/comet-classic/classic-resume-probe.js.map +1 -0
- package/dist/domains/comet-classic/classic-runtime-run.d.ts +2 -1
- package/dist/domains/comet-classic/classic-runtime-run.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-runtime-run.js +38 -0
- package/dist/domains/comet-classic/classic-runtime-run.js.map +1 -1
- package/dist/domains/comet-classic/classic-state-command.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-state-command.js +128 -12
- package/dist/domains/comet-classic/classic-state-command.js.map +1 -1
- package/dist/domains/comet-classic/classic-state.d.ts +3 -1
- package/dist/domains/comet-classic/classic-state.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-state.js +4 -0
- package/dist/domains/comet-classic/classic-state.js.map +1 -1
- package/dist/domains/comet-classic/classic-store.d.ts +4 -1
- package/dist/domains/comet-classic/classic-store.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-store.js +4 -3
- package/dist/domains/comet-classic/classic-store.js.map +1 -1
- package/dist/domains/comet-classic/classic-transitions.d.ts +1 -1
- package/dist/domains/comet-classic/classic-transitions.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-transitions.js +20 -1
- package/dist/domains/comet-classic/classic-transitions.js.map +1 -1
- package/dist/domains/comet-classic/classic-validate-command.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-validate-command.js +1 -0
- package/dist/domains/comet-classic/classic-validate-command.js.map +1 -1
- package/dist/domains/comet-classic/index.d.ts +3 -0
- package/dist/domains/comet-classic/index.d.ts.map +1 -1
- package/dist/domains/comet-classic/index.js +3 -0
- package/dist/domains/comet-classic/index.js.map +1 -1
- package/dist/domains/dashboard/collector.js +10 -2
- package/dist/domains/dashboard/collector.js.map +1 -1
- package/dist/domains/dashboard/web/assets/{index-CjU3VXVu.js → index-CIvN-PL8.js} +11 -11
- package/dist/domains/dashboard/web/assets/{index-BcO3_QlT.css → index-DcyRPBnT.css} +1 -1
- package/dist/domains/dashboard/web/index.html +2 -2
- package/dist/domains/engine/types.d.ts +1 -1
- package/dist/domains/engine/types.d.ts.map +1 -1
- package/dist/domains/integrations/openspec.d.ts.map +1 -1
- package/dist/domains/integrations/openspec.js +6 -6
- package/dist/domains/integrations/openspec.js.map +1 -1
- package/dist/domains/skill/find.d.ts.map +1 -1
- package/dist/domains/skill/find.js +1 -6
- package/dist/domains/skill/find.js.map +1 -1
- package/dist/domains/skill/managed-markdown.d.ts +14 -0
- package/dist/domains/skill/managed-markdown.d.ts.map +1 -0
- package/dist/domains/skill/managed-markdown.js +126 -0
- package/dist/domains/skill/managed-markdown.js.map +1 -0
- package/dist/domains/skill/platform-install.d.ts +4 -3
- package/dist/domains/skill/platform-install.d.ts.map +1 -1
- package/dist/domains/skill/platform-install.js +96 -26
- package/dist/domains/skill/platform-install.js.map +1 -1
- package/dist/domains/skill/project-instructions.d.ts +22 -0
- package/dist/domains/skill/project-instructions.d.ts.map +1 -0
- package/dist/domains/skill/project-instructions.js +61 -0
- package/dist/domains/skill/project-instructions.js.map +1 -0
- package/dist/domains/skill/uninstall.d.ts +3 -1
- package/dist/domains/skill/uninstall.d.ts.map +1 -1
- package/dist/domains/skill/uninstall.js +86 -39
- package/dist/domains/skill/uninstall.js.map +1 -1
- package/dist/platform/install/detect.d.ts +2 -1
- package/dist/platform/install/detect.d.ts.map +1 -1
- package/dist/platform/install/detect.js +10 -1
- package/dist/platform/install/detect.js.map +1 -1
- package/dist/platform/install/platforms.d.ts +5 -1
- package/dist/platform/install/platforms.d.ts.map +1 -1
- package/dist/platform/install/platforms.js +21 -4
- package/dist/platform/install/platforms.js.map +1 -1
- package/dist/platform/install/project-registry.d.ts +37 -0
- package/dist/platform/install/project-registry.d.ts.map +1 -0
- package/dist/platform/install/project-registry.js +205 -0
- package/dist/platform/install/project-registry.js.map +1 -0
- package/eval/.env +28 -0
- package/eval/.env.example +52 -0
- package/eval/CLAUDE.md +43 -0
- package/eval/README.md +538 -0
- package/eval/langsmith/.env.example +16 -0
- package/eval/langsmith/README.md +93 -0
- package/eval/langsmith/skills/README.md +5 -0
- package/eval/langsmith/tasks/README.md +5 -0
- package/eval/langsmith/tests/conftest.py +187 -0
- package/eval/langsmith/tests/tasks/test_tasks.py +287 -0
- package/eval/langsmith/treatments/README.md +5 -0
- package/eval/local/README.md +80 -0
- package/eval/local/regression_baseline.json +13 -0
- package/eval/local/report-style-demo-assets/quality_cost.en.svg +41 -0
- package/eval/local/report-style-demo-assets/quality_cost.zh.svg +41 -0
- package/eval/local/report-style-demo-assets/rubric_delta.en.svg +56 -0
- package/eval/local/report-style-demo-assets/rubric_delta.zh.svg +56 -0
- package/eval/local/report-style-demo-assets/task_outcomes.en.svg +46 -0
- package/eval/local/report-style-demo-assets/task_outcomes.zh.svg +46 -0
- package/eval/local/report-style-demo.html +867 -0
- package/eval/local/scripts/compare_baselines.py +1018 -0
- package/eval/local/scripts/generate_report_style_demo_charts.py +322 -0
- package/eval/local/scripts/regression_check.py +171 -0
- package/eval/local/scripts/render_readme_benchmark_figures.py +416 -0
- package/eval/local/scripts/rescore_rubric.py +120 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039/SKILL.md +282 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039/reference/auto-transition.md +27 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039/reference/comet-yaml-fields.md +68 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039/reference/context-recovery.md +35 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039/reference/debug-gate.md +17 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039/reference/decision-point.md +20 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039/reference/dirty-worktree.md +59 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039/reference/file-structure.md +28 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039/reference/subagent-dispatch.md +113 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039/rules/comet-phase-guard.md +125 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039/scripts/comet-archive.sh +311 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039/scripts/comet-env.sh +110 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039/scripts/comet-guard.sh +778 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039/scripts/comet-handoff.sh +390 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039/scripts/comet-hook-guard.sh +336 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039/scripts/comet-state.sh +1338 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039/scripts/comet-yaml-validate.sh +225 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039-archive/SKILL.md +100 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039-build/SKILL.md +304 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039-design/SKILL.md +263 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039-hotfix/SKILL.md +204 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039-open/SKILL.md +220 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039-tweak/SKILL.md +180 -0
- package/eval/local/skills/benchmarks/039-release/comet-classic-039-verify/SKILL.md +234 -0
- package/eval/local/skills/benchmarks/040-beta/comet/SKILL.md +261 -0
- package/eval/local/skills/benchmarks/040-beta/comet/reference/auto-transition.md +27 -0
- package/eval/local/skills/benchmarks/040-beta/comet/reference/comet-yaml-fields.md +73 -0
- package/eval/local/skills/benchmarks/040-beta/comet/reference/context-recovery.md +45 -0
- package/eval/local/skills/benchmarks/040-beta/comet/reference/debug-gate.md +33 -0
- package/eval/local/skills/benchmarks/040-beta/comet/reference/decision-point.md +31 -0
- package/eval/local/skills/benchmarks/040-beta/comet/reference/dirty-worktree.md +59 -0
- package/eval/local/skills/benchmarks/040-beta/comet/reference/file-structure.md +28 -0
- package/eval/local/skills/benchmarks/040-beta/comet/reference/intent-frame.md +74 -0
- package/eval/local/skills/benchmarks/040-beta/comet/reference/scripts.md +69 -0
- package/eval/local/skills/benchmarks/040-beta/comet/reference/subagent-dispatch.md +169 -0
- package/eval/local/skills/benchmarks/040-beta/comet/rules/comet-phase-guard.md +130 -0
- package/eval/local/skills/benchmarks/040-beta/comet/runtime/classic/checks.yaml +6 -0
- package/eval/local/skills/benchmarks/040-beta/comet/runtime/classic/guardrails.yaml +13 -0
- package/eval/local/skills/benchmarks/040-beta/comet/runtime/classic/skill.yaml +122 -0
- package/eval/local/skills/benchmarks/040-beta/comet/scripts/comet-archive.mjs +9515 -0
- package/eval/local/skills/benchmarks/040-beta/comet/scripts/comet-env.mjs +15 -0
- package/eval/local/skills/benchmarks/040-beta/comet/scripts/comet-guard.mjs +10173 -0
- package/eval/local/skills/benchmarks/040-beta/comet/scripts/comet-handoff.mjs +9491 -0
- package/eval/local/skills/benchmarks/040-beta/comet/scripts/comet-hook-guard.mjs +9175 -0
- package/eval/local/skills/benchmarks/040-beta/comet/scripts/comet-intent.mjs +404 -0
- package/eval/local/skills/benchmarks/040-beta/comet/scripts/comet-state.mjs +9273 -0
- package/eval/local/skills/benchmarks/040-beta/comet/scripts/comet-yaml-validate.mjs +7627 -0
- package/eval/local/skills/benchmarks/040-beta/comet-archive/SKILL.md +110 -0
- package/eval/local/skills/benchmarks/040-beta/comet-build/SKILL.md +315 -0
- package/eval/local/skills/benchmarks/040-beta/comet-design/SKILL.md +260 -0
- package/eval/local/skills/benchmarks/040-beta/comet-hotfix/SKILL.md +190 -0
- package/eval/local/skills/benchmarks/040-beta/comet-open/SKILL.md +210 -0
- package/eval/local/skills/benchmarks/040-beta/comet-tweak/SKILL.md +183 -0
- package/eval/local/skills/benchmarks/040-beta/comet-verify/SKILL.md +230 -0
- package/eval/local/skills/benchmarks/dependency/claude-md/comet-workflow/CLAUDE.md +12 -0
- package/eval/local/skills/benchmarks/dependency/openspec/openspec-apply-change/SKILL.md +156 -0
- package/eval/local/skills/benchmarks/dependency/openspec/openspec-archive-change/SKILL.md +114 -0
- package/eval/local/skills/benchmarks/dependency/openspec/openspec-bulk-archive-change/SKILL.md +246 -0
- package/eval/local/skills/benchmarks/dependency/openspec/openspec-continue-change/SKILL.md +118 -0
- package/eval/local/skills/benchmarks/dependency/openspec/openspec-explore/SKILL.md +288 -0
- package/eval/local/skills/benchmarks/dependency/openspec/openspec-ff-change/SKILL.md +101 -0
- package/eval/local/skills/benchmarks/dependency/openspec/openspec-new-change/SKILL.md +74 -0
- package/eval/local/skills/benchmarks/dependency/openspec/openspec-onboard/SKILL.md +554 -0
- package/eval/local/skills/benchmarks/dependency/openspec/openspec-propose/SKILL.md +110 -0
- package/eval/local/skills/benchmarks/dependency/openspec/openspec-sync-specs/SKILL.md +138 -0
- package/eval/local/skills/benchmarks/dependency/openspec/openspec-verify-change/SKILL.md +168 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/brainstorming/SKILL.md +159 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/brainstorming/scripts/frame-template.html +213 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/brainstorming/scripts/helper.js +167 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/brainstorming/scripts/server.cjs +723 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/brainstorming/scripts/start-server.sh +209 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/brainstorming/scripts/stop-server.sh +120 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/brainstorming/spec-document-reviewer-prompt.md +49 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/brainstorming/visual-companion.md +291 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/dispatching-parallel-agents/SKILL.md +185 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/executing-plans/SKILL.md +70 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/finishing-a-development-branch/SKILL.md +241 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/receiving-code-review/SKILL.md +213 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/requesting-code-review/SKILL.md +103 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/requesting-code-review/code-reviewer.md +172 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/subagent-driven-development/SKILL.md +418 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/subagent-driven-development/implementer-prompt.md +139 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/subagent-driven-development/scripts/review-package +44 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/subagent-driven-development/scripts/sdd-workspace +22 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/subagent-driven-development/scripts/task-brief +40 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/subagent-driven-development/task-reviewer-prompt.md +188 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/CREATION-LOG.md +119 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/SKILL.md +296 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/condition-based-waiting-example.ts +158 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/condition-based-waiting.md +115 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/defense-in-depth.md +122 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/find-polluter.sh +63 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/root-cause-tracing.md +169 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/test-academic.md +14 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/test-pressure-1.md +58 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/test-pressure-2.md +68 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/systematic-debugging/test-pressure-3.md +69 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/test-driven-development/SKILL.md +371 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/test-driven-development/testing-anti-patterns.md +299 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/using-git-worktrees/SKILL.md +202 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/verification-before-completion/SKILL.md +139 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/writing-plans/SKILL.md +174 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/writing-plans/plan-document-reviewer-prompt.md +49 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/writing-skills/SKILL.md +689 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/writing-skills/anthropic-best-practices.md +1150 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/writing-skills/graphviz-conventions.dot +172 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/writing-skills/persuasion-principles.md +187 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/writing-skills/render-graphs.js +168 -0
- package/eval/local/skills/benchmarks/dependency/superpowers/writing-skills/testing-skills-with-subagents.md +384 -0
- package/eval/local/tasks/authoring-skill-smoke/environment/Dockerfile +2 -0
- package/eval/local/tasks/authoring-skill-smoke/instruction.md +4 -0
- package/eval/local/tasks/authoring-skill-smoke/task.toml +21 -0
- package/eval/local/tasks/authoring-skill-smoke/validation/test_authoring_skill_smoke.py +54 -0
- package/eval/local/tasks/comet-agent-memory-routing/environment/Dockerfile +28 -0
- package/eval/local/tasks/comet-agent-memory-routing/environment/agent_system.py +40 -0
- package/eval/local/tasks/comet-agent-memory-routing/environment/test_agent_system.py +29 -0
- package/eval/local/tasks/comet-agent-memory-routing/instruction.md +20 -0
- package/eval/local/tasks/comet-agent-memory-routing/task.toml +24 -0
- package/eval/local/tasks/comet-agent-memory-routing/validation/test_agent_memory_routing.py +24 -0
- package/eval/local/tasks/comet-api-cache-ttl/environment/Dockerfile +39 -0
- package/eval/local/tasks/comet-api-cache-ttl/environment/cache.py +39 -0
- package/eval/local/tasks/comet-api-cache-ttl/environment/test_cache.py +81 -0
- package/eval/local/tasks/comet-api-cache-ttl/instruction.md +33 -0
- package/eval/local/tasks/comet-api-cache-ttl/task.toml +24 -0
- package/eval/local/tasks/comet-api-cache-ttl/validation/test_api_cache_ttl.py +58 -0
- package/eval/local/tasks/comet-cross-file-refactor/environment/Dockerfile +15 -0
- package/eval/local/tasks/comet-cross-file-refactor/environment/cli.py +13 -0
- package/eval/local/tasks/comet-cross-file-refactor/environment/counting.py +13 -0
- package/eval/local/tasks/comet-cross-file-refactor/environment/test_textkit.py +21 -0
- package/eval/local/tasks/comet-cross-file-refactor/instruction.md +16 -0
- package/eval/local/tasks/comet-cross-file-refactor/task.toml +20 -0
- package/eval/local/tasks/comet-cross-file-refactor/validation/test_cross_file_refactor.py +20 -0
- package/eval/local/tasks/comet-dependency-confusion/environment/Dockerfile +28 -0
- package/eval/local/tasks/comet-dependency-confusion/environment/app.py +8 -0
- package/eval/local/tasks/comet-dependency-confusion/environment/settings_loader.py +14 -0
- package/eval/local/tasks/comet-dependency-confusion/environment/test_settings_service.py +19 -0
- package/eval/local/tasks/comet-dependency-confusion/instruction.md +18 -0
- package/eval/local/tasks/comet-dependency-confusion/task.toml +24 -0
- package/eval/local/tasks/comet-dependency-confusion/validation/test_dependency_confusion.py +24 -0
- package/eval/local/tasks/comet-fix-median/environment/Dockerfile +50 -0
- package/eval/local/tasks/comet-fix-median/environment/stats.py +54 -0
- package/eval/local/tasks/comet-fix-median/environment/test_stats.py +68 -0
- package/eval/local/tasks/comet-fix-median/instruction.md +25 -0
- package/eval/local/tasks/comet-fix-median/task.toml +24 -0
- package/eval/local/tasks/comet-fix-median/validation/test_fix_median.py +60 -0
- package/eval/local/tasks/comet-framework-selection/environment/Dockerfile +28 -0
- package/eval/local/tasks/comet-framework-selection/environment/architecture.py +12 -0
- package/eval/local/tasks/comet-framework-selection/environment/test_architecture.py +30 -0
- package/eval/local/tasks/comet-framework-selection/instruction.md +22 -0
- package/eval/local/tasks/comet-framework-selection/task.toml +24 -0
- package/eval/local/tasks/comet-framework-selection/validation/test_framework_selection.py +24 -0
- package/eval/local/tasks/comet-full-workflow/environment/Dockerfile +53 -0
- package/eval/local/tasks/comet-full-workflow/environment/test_wordcount.py +29 -0
- package/eval/local/tasks/comet-full-workflow/environment/wordcount.py +40 -0
- package/eval/local/tasks/comet-full-workflow/instruction.md +18 -0
- package/eval/local/tasks/comet-full-workflow/task.toml +24 -0
- package/eval/local/tasks/comet-full-workflow/validation/test_full_workflow.py +171 -0
- package/eval/local/tasks/comet-graph-execution-review/environment/Dockerfile +28 -0
- package/eval/local/tasks/comet-graph-execution-review/environment/pipeline.py +19 -0
- package/eval/local/tasks/comet-graph-execution-review/environment/test_pipeline.py +53 -0
- package/eval/local/tasks/comet-graph-execution-review/instruction.md +21 -0
- package/eval/local/tasks/comet-graph-execution-review/task.toml +24 -0
- package/eval/local/tasks/comet-graph-execution-review/validation/test_graph_execution_review.py +24 -0
- package/eval/local/tasks/comet-human-approval-flow/environment/Dockerfile +15 -0
- package/eval/local/tasks/comet-human-approval-flow/environment/approvals.py +17 -0
- package/eval/local/tasks/comet-human-approval-flow/environment/test_approvals.py +30 -0
- package/eval/local/tasks/comet-human-approval-flow/instruction.md +15 -0
- package/eval/local/tasks/comet-human-approval-flow/task.toml +20 -0
- package/eval/local/tasks/comet-human-approval-flow/validation/test_human_approval_flow.py +20 -0
- package/eval/local/tasks/comet-layered-streaming-fix/environment/Dockerfile +20 -0
- package/eval/local/tasks/comet-layered-streaming-fix/environment/chat_app.py +19 -0
- package/eval/local/tasks/comet-layered-streaming-fix/environment/test_chat_app.py +15 -0
- package/eval/local/tasks/comet-layered-streaming-fix/instruction.md +14 -0
- package/eval/local/tasks/comet-layered-streaming-fix/task.toml +20 -0
- package/eval/local/tasks/comet-layered-streaming-fix/validation/test_layered_streaming_fix.py +20 -0
- package/eval/local/tasks/comet-noise-distractor/environment/Dockerfile +15 -0
- package/eval/local/tasks/comet-noise-distractor/environment/distractor.md +4 -0
- package/eval/local/tasks/comet-noise-distractor/environment/invoice.py +5 -0
- package/eval/local/tasks/comet-noise-distractor/environment/test_invoice.py +18 -0
- package/eval/local/tasks/comet-noise-distractor/instruction.md +17 -0
- package/eval/local/tasks/comet-noise-distractor/task.toml +20 -0
- package/eval/local/tasks/comet-noise-distractor/validation/test_noise_distractor.py +20 -0
- package/eval/local/tasks/comet-observability-env-template/environment/Dockerfile +15 -0
- package/eval/local/tasks/comet-observability-env-template/environment/env_writer.py +17 -0
- package/eval/local/tasks/comet-observability-env-template/environment/test_env_writer.py +17 -0
- package/eval/local/tasks/comet-observability-env-template/instruction.md +20 -0
- package/eval/local/tasks/comet-observability-env-template/task.toml +20 -0
- package/eval/local/tasks/comet-observability-env-template/validation/test_observability_env_template.py +46 -0
- package/eval/local/tasks/comet-perf-dedupe/environment/Dockerfile +39 -0
- package/eval/local/tasks/comet-perf-dedupe/environment/dedupe.py +22 -0
- package/eval/local/tasks/comet-perf-dedupe/environment/test_dedupe.py +43 -0
- package/eval/local/tasks/comet-perf-dedupe/instruction.md +25 -0
- package/eval/local/tasks/comet-perf-dedupe/task.toml +24 -0
- package/eval/local/tasks/comet-perf-dedupe/validation/test_perf_dedupe.py +40 -0
- package/eval/local/tasks/comet-persistence-threading/environment/Dockerfile +15 -0
- package/eval/local/tasks/comet-persistence-threading/environment/session_store.py +15 -0
- package/eval/local/tasks/comet-persistence-threading/environment/test_session_store.py +21 -0
- package/eval/local/tasks/comet-persistence-threading/instruction.md +14 -0
- package/eval/local/tasks/comet-persistence-threading/task.toml +20 -0
- package/eval/local/tasks/comet-persistence-threading/validation/test_persistence_threading.py +20 -0
- package/eval/local/tasks/comet-refactor-counter/environment/Dockerfile +39 -0
- package/eval/local/tasks/comet-refactor-counter/environment/test_text_processor.py +57 -0
- package/eval/local/tasks/comet-refactor-counter/environment/text_processor.py +49 -0
- package/eval/local/tasks/comet-refactor-counter/instruction.md +31 -0
- package/eval/local/tasks/comet-refactor-counter/task.toml +24 -0
- package/eval/local/tasks/comet-refactor-counter/validation/test_refactor_counter.py +75 -0
- package/eval/local/tasks/comet-robust-config/environment/Dockerfile +39 -0
- package/eval/local/tasks/comet-robust-config/environment/config_loader.py +56 -0
- package/eval/local/tasks/comet-robust-config/environment/test_config_loader.py +72 -0
- package/eval/local/tasks/comet-robust-config/instruction.md +30 -0
- package/eval/local/tasks/comet-robust-config/task.toml +24 -0
- package/eval/local/tasks/comet-robust-config/validation/test_robust_config.py +37 -0
- package/eval/local/tasks/generic-skill-smoke/environment/Dockerfile +2 -0
- package/eval/local/tasks/generic-skill-smoke/instruction.md +7 -0
- package/eval/local/tasks/generic-skill-smoke/task.toml +21 -0
- package/eval/local/tasks/generic-skill-smoke/validation/test_generic_skill_smoke.py +36 -0
- package/eval/local/tasks/index.yaml +155 -0
- package/eval/local/tasks/workflow-overlay-contract/environment/Dockerfile +5 -0
- package/eval/local/tasks/workflow-overlay-contract/instruction.md +5 -0
- package/eval/local/tasks/workflow-overlay-contract/task.toml +21 -0
- package/eval/local/tasks/workflow-overlay-contract/validation/test_workflow_overlay_contract.py +235 -0
- package/eval/local/tasks/workflow-route-conformance/environment/Dockerfile +2 -0
- package/eval/local/tasks/workflow-route-conformance/instruction.md +5 -0
- package/eval/local/tasks/workflow-route-conformance/task.toml +21 -0
- package/eval/local/tasks/workflow-route-conformance/validation/test_workflow_route_conformance.py +84 -0
- package/eval/local/tests/conftest.py +1148 -0
- package/eval/local/tests/scaffold/test_attribution.py +48 -0
- package/eval/local/tests/scaffold/test_compare_baselines.py +999 -0
- package/eval/local/tests/scaffold/test_conftest_helpers.py +534 -0
- package/eval/local/tests/scaffold/test_evidence.py +15 -0
- package/eval/local/tests/scaffold/test_langsmith_conftest.py +244 -0
- package/eval/local/tests/scaffold/test_logging.py +243 -0
- package/eval/local/tests/scaffold/test_manifests.py +132 -0
- package/eval/local/tests/scaffold/test_profiles.py +866 -0
- package/eval/local/tests/scaffold/test_regression_check.py +19 -0
- package/eval/local/tests/scaffold/test_report_style_demo_charts.py +45 -0
- package/eval/local/tests/scaffold/test_sample_quality.py +211 -0
- package/eval/local/tests/scaffold/test_tasks.py +168 -0
- package/eval/local/tests/scaffold/test_treatments.py +244 -0
- package/eval/local/tests/scaffold/test_utils.py +139 -0
- package/eval/local/tests/tasks/test_tasks.py +445 -0
- package/eval/local/tests/tasks/test_validation_scripts.py +143 -0
- package/eval/local/treatments/comet/comet_full_039.yaml +141 -0
- package/eval/local/treatments/comet/comet_full_040_beta.yaml +140 -0
- package/eval/local/treatments/common/control.yaml +4 -0
- package/eval/pyproject.toml +41 -0
- package/eval/report-html-config.json +6 -0
- package/eval/scaffold/__init__.py +67 -0
- package/eval/scaffold/python/__init__.py +95 -0
- package/eval/scaffold/python/attribution.py +43 -0
- package/eval/scaffold/python/evidence.py +38 -0
- package/eval/scaffold/python/external_data_handler.py +18 -0
- package/eval/scaffold/python/generic_llm_judge.py +235 -0
- package/eval/scaffold/python/judge_config.py +168 -0
- package/eval/scaffold/python/llm_judge.py +191 -0
- package/eval/scaffold/python/logging.py +705 -0
- package/eval/scaffold/python/manifests.py +129 -0
- package/eval/scaffold/python/paper_charts.py +25 -0
- package/eval/scaffold/python/pass_at_k.py +107 -0
- package/eval/scaffold/python/paths.py +56 -0
- package/eval/scaffold/python/profiles.py +127 -0
- package/eval/scaffold/python/report_outputs.py +1391 -0
- package/eval/scaffold/python/sample_quality.py +339 -0
- package/eval/scaffold/python/schema.py +39 -0
- package/eval/scaffold/python/skill_parser.py +469 -0
- package/eval/scaffold/python/tasks.py +330 -0
- package/eval/scaffold/python/treatments.py +271 -0
- package/eval/scaffold/python/utils.py +366 -0
- package/eval/scaffold/python/validation/__init__.py +21 -0
- package/eval/scaffold/python/validation/authoring_rubric.py +326 -0
- package/eval/scaffold/python/validation/comet_workflow.py +187 -0
- package/eval/scaffold/python/validation/core.py +264 -0
- package/eval/scaffold/python/validation/dataset.py +337 -0
- package/eval/scaffold/python/validation/docker.py +106 -0
- package/eval/scaffold/python/validation/evaluator.py +549 -0
- package/eval/scaffold/python/validation/generic_rubric.py +169 -0
- package/eval/scaffold/python/validation/rubric.py +740 -0
- package/eval/scaffold/python/validation/runner.py +237 -0
- package/eval/scaffold/python/validation/scripts.py +58 -0
- package/eval/scaffold/python/validation/tracing.py +313 -0
- package/eval/scaffold/shell/common.sh +126 -0
- package/eval/scaffold/shell/docker.sh +482 -0
- package/eval/scaffold/shell/run-claude-loop.sh +181 -0
- package/eval/scaffold/shell/setup.sh +259 -0
- package/eval/simulator-instruction.md +9 -0
- package/package.json +24 -2
|
@@ -126,17 +126,17 @@ var require_visit = __commonJS({
|
|
|
126
126
|
visit.BREAK = BREAK;
|
|
127
127
|
visit.SKIP = SKIP;
|
|
128
128
|
visit.REMOVE = REMOVE;
|
|
129
|
-
function visit_(key, node, visitor,
|
|
130
|
-
const ctrl = callVisitor(key, node, visitor,
|
|
129
|
+
function visit_(key, node, visitor, path22) {
|
|
130
|
+
const ctrl = callVisitor(key, node, visitor, path22);
|
|
131
131
|
if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
|
|
132
|
-
replaceNode(key,
|
|
133
|
-
return visit_(key, ctrl, visitor,
|
|
132
|
+
replaceNode(key, path22, ctrl);
|
|
133
|
+
return visit_(key, ctrl, visitor, path22);
|
|
134
134
|
}
|
|
135
135
|
if (typeof ctrl !== "symbol") {
|
|
136
136
|
if (identity.isCollection(node)) {
|
|
137
|
-
|
|
137
|
+
path22 = Object.freeze(path22.concat(node));
|
|
138
138
|
for (let i = 0; i < node.items.length; ++i) {
|
|
139
|
-
const ci = visit_(i, node.items[i], visitor,
|
|
139
|
+
const ci = visit_(i, node.items[i], visitor, path22);
|
|
140
140
|
if (typeof ci === "number")
|
|
141
141
|
i = ci - 1;
|
|
142
142
|
else if (ci === BREAK)
|
|
@@ -147,13 +147,13 @@ var require_visit = __commonJS({
|
|
|
147
147
|
}
|
|
148
148
|
}
|
|
149
149
|
} else if (identity.isPair(node)) {
|
|
150
|
-
|
|
151
|
-
const ck = visit_("key", node.key, visitor,
|
|
150
|
+
path22 = Object.freeze(path22.concat(node));
|
|
151
|
+
const ck = visit_("key", node.key, visitor, path22);
|
|
152
152
|
if (ck === BREAK)
|
|
153
153
|
return BREAK;
|
|
154
154
|
else if (ck === REMOVE)
|
|
155
155
|
node.key = null;
|
|
156
|
-
const cv = visit_("value", node.value, visitor,
|
|
156
|
+
const cv = visit_("value", node.value, visitor, path22);
|
|
157
157
|
if (cv === BREAK)
|
|
158
158
|
return BREAK;
|
|
159
159
|
else if (cv === REMOVE)
|
|
@@ -174,17 +174,17 @@ var require_visit = __commonJS({
|
|
|
174
174
|
visitAsync.BREAK = BREAK;
|
|
175
175
|
visitAsync.SKIP = SKIP;
|
|
176
176
|
visitAsync.REMOVE = REMOVE;
|
|
177
|
-
async function visitAsync_(key, node, visitor,
|
|
178
|
-
const ctrl = await callVisitor(key, node, visitor,
|
|
177
|
+
async function visitAsync_(key, node, visitor, path22) {
|
|
178
|
+
const ctrl = await callVisitor(key, node, visitor, path22);
|
|
179
179
|
if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
|
|
180
|
-
replaceNode(key,
|
|
181
|
-
return visitAsync_(key, ctrl, visitor,
|
|
180
|
+
replaceNode(key, path22, ctrl);
|
|
181
|
+
return visitAsync_(key, ctrl, visitor, path22);
|
|
182
182
|
}
|
|
183
183
|
if (typeof ctrl !== "symbol") {
|
|
184
184
|
if (identity.isCollection(node)) {
|
|
185
|
-
|
|
185
|
+
path22 = Object.freeze(path22.concat(node));
|
|
186
186
|
for (let i = 0; i < node.items.length; ++i) {
|
|
187
|
-
const ci = await visitAsync_(i, node.items[i], visitor,
|
|
187
|
+
const ci = await visitAsync_(i, node.items[i], visitor, path22);
|
|
188
188
|
if (typeof ci === "number")
|
|
189
189
|
i = ci - 1;
|
|
190
190
|
else if (ci === BREAK)
|
|
@@ -195,13 +195,13 @@ var require_visit = __commonJS({
|
|
|
195
195
|
}
|
|
196
196
|
}
|
|
197
197
|
} else if (identity.isPair(node)) {
|
|
198
|
-
|
|
199
|
-
const ck = await visitAsync_("key", node.key, visitor,
|
|
198
|
+
path22 = Object.freeze(path22.concat(node));
|
|
199
|
+
const ck = await visitAsync_("key", node.key, visitor, path22);
|
|
200
200
|
if (ck === BREAK)
|
|
201
201
|
return BREAK;
|
|
202
202
|
else if (ck === REMOVE)
|
|
203
203
|
node.key = null;
|
|
204
|
-
const cv = await visitAsync_("value", node.value, visitor,
|
|
204
|
+
const cv = await visitAsync_("value", node.value, visitor, path22);
|
|
205
205
|
if (cv === BREAK)
|
|
206
206
|
return BREAK;
|
|
207
207
|
else if (cv === REMOVE)
|
|
@@ -228,23 +228,23 @@ var require_visit = __commonJS({
|
|
|
228
228
|
}
|
|
229
229
|
return visitor;
|
|
230
230
|
}
|
|
231
|
-
function callVisitor(key, node, visitor,
|
|
231
|
+
function callVisitor(key, node, visitor, path22) {
|
|
232
232
|
if (typeof visitor === "function")
|
|
233
|
-
return visitor(key, node,
|
|
233
|
+
return visitor(key, node, path22);
|
|
234
234
|
if (identity.isMap(node))
|
|
235
|
-
return visitor.Map?.(key, node,
|
|
235
|
+
return visitor.Map?.(key, node, path22);
|
|
236
236
|
if (identity.isSeq(node))
|
|
237
|
-
return visitor.Seq?.(key, node,
|
|
237
|
+
return visitor.Seq?.(key, node, path22);
|
|
238
238
|
if (identity.isPair(node))
|
|
239
|
-
return visitor.Pair?.(key, node,
|
|
239
|
+
return visitor.Pair?.(key, node, path22);
|
|
240
240
|
if (identity.isScalar(node))
|
|
241
|
-
return visitor.Scalar?.(key, node,
|
|
241
|
+
return visitor.Scalar?.(key, node, path22);
|
|
242
242
|
if (identity.isAlias(node))
|
|
243
|
-
return visitor.Alias?.(key, node,
|
|
243
|
+
return visitor.Alias?.(key, node, path22);
|
|
244
244
|
return void 0;
|
|
245
245
|
}
|
|
246
|
-
function replaceNode(key,
|
|
247
|
-
const parent =
|
|
246
|
+
function replaceNode(key, path22, node) {
|
|
247
|
+
const parent = path22[path22.length - 1];
|
|
248
248
|
if (identity.isCollection(parent)) {
|
|
249
249
|
parent.items[key] = node;
|
|
250
250
|
} else if (identity.isPair(parent)) {
|
|
@@ -854,10 +854,10 @@ var require_Collection = __commonJS({
|
|
|
854
854
|
var createNode = require_createNode();
|
|
855
855
|
var identity = require_identity();
|
|
856
856
|
var Node = require_Node();
|
|
857
|
-
function collectionFromPath(schema,
|
|
857
|
+
function collectionFromPath(schema, path22, value) {
|
|
858
858
|
let v = value;
|
|
859
|
-
for (let i =
|
|
860
|
-
const k =
|
|
859
|
+
for (let i = path22.length - 1; i >= 0; --i) {
|
|
860
|
+
const k = path22[i];
|
|
861
861
|
if (typeof k === "number" && Number.isInteger(k) && k >= 0) {
|
|
862
862
|
const a = [];
|
|
863
863
|
a[k] = v;
|
|
@@ -876,7 +876,7 @@ var require_Collection = __commonJS({
|
|
|
876
876
|
sourceObjects: /* @__PURE__ */ new Map()
|
|
877
877
|
});
|
|
878
878
|
}
|
|
879
|
-
var isEmptyPath = (
|
|
879
|
+
var isEmptyPath = (path22) => path22 == null || typeof path22 === "object" && !!path22[Symbol.iterator]().next().done;
|
|
880
880
|
var Collection = class extends Node.NodeBase {
|
|
881
881
|
constructor(type, schema) {
|
|
882
882
|
super(type);
|
|
@@ -906,11 +906,11 @@ var require_Collection = __commonJS({
|
|
|
906
906
|
* be a Pair instance or a `{ key, value }` object, which may not have a key
|
|
907
907
|
* that already exists in the map.
|
|
908
908
|
*/
|
|
909
|
-
addIn(
|
|
910
|
-
if (isEmptyPath(
|
|
909
|
+
addIn(path22, value) {
|
|
910
|
+
if (isEmptyPath(path22))
|
|
911
911
|
this.add(value);
|
|
912
912
|
else {
|
|
913
|
-
const [key, ...rest] =
|
|
913
|
+
const [key, ...rest] = path22;
|
|
914
914
|
const node = this.get(key, true);
|
|
915
915
|
if (identity.isCollection(node))
|
|
916
916
|
node.addIn(rest, value);
|
|
@@ -924,8 +924,8 @@ var require_Collection = __commonJS({
|
|
|
924
924
|
* Removes a value from the collection.
|
|
925
925
|
* @returns `true` if the item was found and removed.
|
|
926
926
|
*/
|
|
927
|
-
deleteIn(
|
|
928
|
-
const [key, ...rest] =
|
|
927
|
+
deleteIn(path22) {
|
|
928
|
+
const [key, ...rest] = path22;
|
|
929
929
|
if (rest.length === 0)
|
|
930
930
|
return this.delete(key);
|
|
931
931
|
const node = this.get(key, true);
|
|
@@ -939,8 +939,8 @@ var require_Collection = __commonJS({
|
|
|
939
939
|
* scalar values from their surrounding node; to disable set `keepScalar` to
|
|
940
940
|
* `true` (collections are always returned intact).
|
|
941
941
|
*/
|
|
942
|
-
getIn(
|
|
943
|
-
const [key, ...rest] =
|
|
942
|
+
getIn(path22, keepScalar) {
|
|
943
|
+
const [key, ...rest] = path22;
|
|
944
944
|
const node = this.get(key, true);
|
|
945
945
|
if (rest.length === 0)
|
|
946
946
|
return !keepScalar && identity.isScalar(node) ? node.value : node;
|
|
@@ -958,8 +958,8 @@ var require_Collection = __commonJS({
|
|
|
958
958
|
/**
|
|
959
959
|
* Checks if the collection includes a value with the key `key`.
|
|
960
960
|
*/
|
|
961
|
-
hasIn(
|
|
962
|
-
const [key, ...rest] =
|
|
961
|
+
hasIn(path22) {
|
|
962
|
+
const [key, ...rest] = path22;
|
|
963
963
|
if (rest.length === 0)
|
|
964
964
|
return this.has(key);
|
|
965
965
|
const node = this.get(key, true);
|
|
@@ -969,8 +969,8 @@ var require_Collection = __commonJS({
|
|
|
969
969
|
* Sets a value in this collection. For `!!set`, `value` needs to be a
|
|
970
970
|
* boolean to add/remove the item from the set.
|
|
971
971
|
*/
|
|
972
|
-
setIn(
|
|
973
|
-
const [key, ...rest] =
|
|
972
|
+
setIn(path22, value) {
|
|
973
|
+
const [key, ...rest] = path22;
|
|
974
974
|
if (rest.length === 0) {
|
|
975
975
|
this.set(key, value);
|
|
976
976
|
} else {
|
|
@@ -3485,9 +3485,9 @@ var require_Document = __commonJS({
|
|
|
3485
3485
|
this.contents.add(value);
|
|
3486
3486
|
}
|
|
3487
3487
|
/** Adds a value to the document. */
|
|
3488
|
-
addIn(
|
|
3488
|
+
addIn(path22, value) {
|
|
3489
3489
|
if (assertCollection(this.contents))
|
|
3490
|
-
this.contents.addIn(
|
|
3490
|
+
this.contents.addIn(path22, value);
|
|
3491
3491
|
}
|
|
3492
3492
|
/**
|
|
3493
3493
|
* Create a new `Alias` node, ensuring that the target `node` has the required anchor.
|
|
@@ -3562,14 +3562,14 @@ var require_Document = __commonJS({
|
|
|
3562
3562
|
* Removes a value from the document.
|
|
3563
3563
|
* @returns `true` if the item was found and removed.
|
|
3564
3564
|
*/
|
|
3565
|
-
deleteIn(
|
|
3566
|
-
if (Collection.isEmptyPath(
|
|
3565
|
+
deleteIn(path22) {
|
|
3566
|
+
if (Collection.isEmptyPath(path22)) {
|
|
3567
3567
|
if (this.contents == null)
|
|
3568
3568
|
return false;
|
|
3569
3569
|
this.contents = null;
|
|
3570
3570
|
return true;
|
|
3571
3571
|
}
|
|
3572
|
-
return assertCollection(this.contents) ? this.contents.deleteIn(
|
|
3572
|
+
return assertCollection(this.contents) ? this.contents.deleteIn(path22) : false;
|
|
3573
3573
|
}
|
|
3574
3574
|
/**
|
|
3575
3575
|
* Returns item at `key`, or `undefined` if not found. By default unwraps
|
|
@@ -3584,10 +3584,10 @@ var require_Document = __commonJS({
|
|
|
3584
3584
|
* scalar values from their surrounding node; to disable set `keepScalar` to
|
|
3585
3585
|
* `true` (collections are always returned intact).
|
|
3586
3586
|
*/
|
|
3587
|
-
getIn(
|
|
3588
|
-
if (Collection.isEmptyPath(
|
|
3587
|
+
getIn(path22, keepScalar) {
|
|
3588
|
+
if (Collection.isEmptyPath(path22))
|
|
3589
3589
|
return !keepScalar && identity.isScalar(this.contents) ? this.contents.value : this.contents;
|
|
3590
|
-
return identity.isCollection(this.contents) ? this.contents.getIn(
|
|
3590
|
+
return identity.isCollection(this.contents) ? this.contents.getIn(path22, keepScalar) : void 0;
|
|
3591
3591
|
}
|
|
3592
3592
|
/**
|
|
3593
3593
|
* Checks if the document includes a value with the key `key`.
|
|
@@ -3598,10 +3598,10 @@ var require_Document = __commonJS({
|
|
|
3598
3598
|
/**
|
|
3599
3599
|
* Checks if the document includes a value at `path`.
|
|
3600
3600
|
*/
|
|
3601
|
-
hasIn(
|
|
3602
|
-
if (Collection.isEmptyPath(
|
|
3601
|
+
hasIn(path22) {
|
|
3602
|
+
if (Collection.isEmptyPath(path22))
|
|
3603
3603
|
return this.contents !== void 0;
|
|
3604
|
-
return identity.isCollection(this.contents) ? this.contents.hasIn(
|
|
3604
|
+
return identity.isCollection(this.contents) ? this.contents.hasIn(path22) : false;
|
|
3605
3605
|
}
|
|
3606
3606
|
/**
|
|
3607
3607
|
* Sets a value in this document. For `!!set`, `value` needs to be a
|
|
@@ -3618,13 +3618,13 @@ var require_Document = __commonJS({
|
|
|
3618
3618
|
* Sets a value in this document. For `!!set`, `value` needs to be a
|
|
3619
3619
|
* boolean to add/remove the item from the set.
|
|
3620
3620
|
*/
|
|
3621
|
-
setIn(
|
|
3622
|
-
if (Collection.isEmptyPath(
|
|
3621
|
+
setIn(path22, value) {
|
|
3622
|
+
if (Collection.isEmptyPath(path22)) {
|
|
3623
3623
|
this.contents = value;
|
|
3624
3624
|
} else if (this.contents == null) {
|
|
3625
|
-
this.contents = Collection.collectionFromPath(this.schema, Array.from(
|
|
3625
|
+
this.contents = Collection.collectionFromPath(this.schema, Array.from(path22), value);
|
|
3626
3626
|
} else if (assertCollection(this.contents)) {
|
|
3627
|
-
this.contents.setIn(
|
|
3627
|
+
this.contents.setIn(path22, value);
|
|
3628
3628
|
}
|
|
3629
3629
|
}
|
|
3630
3630
|
/**
|
|
@@ -5584,9 +5584,9 @@ var require_cst_visit = __commonJS({
|
|
|
5584
5584
|
visit.BREAK = BREAK;
|
|
5585
5585
|
visit.SKIP = SKIP;
|
|
5586
5586
|
visit.REMOVE = REMOVE;
|
|
5587
|
-
visit.itemAtPath = (cst,
|
|
5587
|
+
visit.itemAtPath = (cst, path22) => {
|
|
5588
5588
|
let item = cst;
|
|
5589
|
-
for (const [field2, index] of
|
|
5589
|
+
for (const [field2, index] of path22) {
|
|
5590
5590
|
const tok = item?.[field2];
|
|
5591
5591
|
if (tok && "items" in tok) {
|
|
5592
5592
|
item = tok.items[index];
|
|
@@ -5595,23 +5595,23 @@ var require_cst_visit = __commonJS({
|
|
|
5595
5595
|
}
|
|
5596
5596
|
return item;
|
|
5597
5597
|
};
|
|
5598
|
-
visit.parentCollection = (cst,
|
|
5599
|
-
const parent = visit.itemAtPath(cst,
|
|
5600
|
-
const field2 =
|
|
5598
|
+
visit.parentCollection = (cst, path22) => {
|
|
5599
|
+
const parent = visit.itemAtPath(cst, path22.slice(0, -1));
|
|
5600
|
+
const field2 = path22[path22.length - 1][0];
|
|
5601
5601
|
const coll = parent?.[field2];
|
|
5602
5602
|
if (coll && "items" in coll)
|
|
5603
5603
|
return coll;
|
|
5604
5604
|
throw new Error("Parent collection not found");
|
|
5605
5605
|
};
|
|
5606
|
-
function _visit(
|
|
5607
|
-
let ctrl = visitor(item,
|
|
5606
|
+
function _visit(path22, item, visitor) {
|
|
5607
|
+
let ctrl = visitor(item, path22);
|
|
5608
5608
|
if (typeof ctrl === "symbol")
|
|
5609
5609
|
return ctrl;
|
|
5610
5610
|
for (const field2 of ["key", "value"]) {
|
|
5611
5611
|
const token = item[field2];
|
|
5612
5612
|
if (token && "items" in token) {
|
|
5613
5613
|
for (let i = 0; i < token.items.length; ++i) {
|
|
5614
|
-
const ci = _visit(Object.freeze(
|
|
5614
|
+
const ci = _visit(Object.freeze(path22.concat([[field2, i]])), token.items[i], visitor);
|
|
5615
5615
|
if (typeof ci === "number")
|
|
5616
5616
|
i = ci - 1;
|
|
5617
5617
|
else if (ci === BREAK)
|
|
@@ -5622,10 +5622,10 @@ var require_cst_visit = __commonJS({
|
|
|
5622
5622
|
}
|
|
5623
5623
|
}
|
|
5624
5624
|
if (typeof ctrl === "function" && field2 === "key")
|
|
5625
|
-
ctrl = ctrl(item,
|
|
5625
|
+
ctrl = ctrl(item, path22);
|
|
5626
5626
|
}
|
|
5627
5627
|
}
|
|
5628
|
-
return typeof ctrl === "function" ? ctrl(item,
|
|
5628
|
+
return typeof ctrl === "function" ? ctrl(item, path22) : ctrl;
|
|
5629
5629
|
}
|
|
5630
5630
|
exports.visit = visit;
|
|
5631
5631
|
}
|
|
@@ -6927,14 +6927,14 @@ var require_parser = __commonJS({
|
|
|
6927
6927
|
case "scalar":
|
|
6928
6928
|
case "single-quoted-scalar":
|
|
6929
6929
|
case "double-quoted-scalar": {
|
|
6930
|
-
const
|
|
6930
|
+
const fs21 = this.flowScalar(this.type);
|
|
6931
6931
|
if (atNextItem || it.value) {
|
|
6932
|
-
map.items.push({ start, key:
|
|
6932
|
+
map.items.push({ start, key: fs21, sep: [] });
|
|
6933
6933
|
this.onKeyLine = true;
|
|
6934
6934
|
} else if (it.sep) {
|
|
6935
|
-
this.stack.push(
|
|
6935
|
+
this.stack.push(fs21);
|
|
6936
6936
|
} else {
|
|
6937
|
-
Object.assign(it, { key:
|
|
6937
|
+
Object.assign(it, { key: fs21, sep: [] });
|
|
6938
6938
|
this.onKeyLine = true;
|
|
6939
6939
|
}
|
|
6940
6940
|
return;
|
|
@@ -7062,13 +7062,13 @@ var require_parser = __commonJS({
|
|
|
7062
7062
|
case "scalar":
|
|
7063
7063
|
case "single-quoted-scalar":
|
|
7064
7064
|
case "double-quoted-scalar": {
|
|
7065
|
-
const
|
|
7065
|
+
const fs21 = this.flowScalar(this.type);
|
|
7066
7066
|
if (!it || it.value)
|
|
7067
|
-
fc.items.push({ start: [], key:
|
|
7067
|
+
fc.items.push({ start: [], key: fs21, sep: [] });
|
|
7068
7068
|
else if (it.sep)
|
|
7069
|
-
this.stack.push(
|
|
7069
|
+
this.stack.push(fs21);
|
|
7070
7070
|
else
|
|
7071
|
-
Object.assign(it, { key:
|
|
7071
|
+
Object.assign(it, { key: fs21, sep: [] });
|
|
7072
7072
|
return;
|
|
7073
7073
|
}
|
|
7074
7074
|
case "flow-map-end":
|
|
@@ -7257,7 +7257,7 @@ var require_public_api = __commonJS({
|
|
|
7257
7257
|
return docs;
|
|
7258
7258
|
return Object.assign([], { empty: true }, composer$1.streamInfo());
|
|
7259
7259
|
}
|
|
7260
|
-
function
|
|
7260
|
+
function parseDocument7(source, options = {}) {
|
|
7261
7261
|
const { lineCounter: lineCounter2, prettyErrors } = parseOptions(options);
|
|
7262
7262
|
const parser$1 = new parser.Parser(lineCounter2?.addNewLine);
|
|
7263
7263
|
const composer$1 = new composer.Composer(options);
|
|
@@ -7283,7 +7283,7 @@ var require_public_api = __commonJS({
|
|
|
7283
7283
|
} else if (options === void 0 && reviver && typeof reviver === "object") {
|
|
7284
7284
|
options = reviver;
|
|
7285
7285
|
}
|
|
7286
|
-
const doc =
|
|
7286
|
+
const doc = parseDocument7(src, options);
|
|
7287
7287
|
if (!doc)
|
|
7288
7288
|
return null;
|
|
7289
7289
|
doc.warnings.forEach((warning) => log.warn(doc.options.logLevel, warning));
|
|
@@ -7319,7 +7319,7 @@ var require_public_api = __commonJS({
|
|
|
7319
7319
|
}
|
|
7320
7320
|
exports.parse = parse2;
|
|
7321
7321
|
exports.parseAllDocuments = parseAllDocuments;
|
|
7322
|
-
exports.parseDocument =
|
|
7322
|
+
exports.parseDocument = parseDocument7;
|
|
7323
7323
|
exports.stringify = stringify;
|
|
7324
7324
|
}
|
|
7325
7325
|
});
|
|
@@ -7573,6 +7573,10 @@ Valid format: lowercase kebab-case (a-z, 0-9, single hyphens)`;
|
|
|
7573
7573
|
if (name.includes("..")) return "Change name cannot contain '..' (path traversal not allowed)";
|
|
7574
7574
|
return null;
|
|
7575
7575
|
}
|
|
7576
|
+
function assertOpenSpecChangeName(name) {
|
|
7577
|
+
const error = openSpecChangeNameError(name);
|
|
7578
|
+
if (error) throw new Error(error);
|
|
7579
|
+
}
|
|
7576
7580
|
async function resolveClassicChangeDirectory(name) {
|
|
7577
7581
|
const active = `openspec/changes/${name}`;
|
|
7578
7582
|
if (await exists(filesystemPath(active))) {
|
|
@@ -7622,26 +7626,26 @@ function projectRootFor(changeDir) {
|
|
|
7622
7626
|
}
|
|
7623
7627
|
throw new Error(`Classic change is not inside an openspec directory: ${changeDir}`);
|
|
7624
7628
|
}
|
|
7625
|
-
function relativeSource(
|
|
7626
|
-
return path2.relative(
|
|
7629
|
+
function relativeSource(projectRoot2, file) {
|
|
7630
|
+
return path2.relative(projectRoot2, file).split(path2.sep).join("/");
|
|
7627
7631
|
}
|
|
7628
|
-
async function linkedFileEvidence(
|
|
7632
|
+
async function linkedFileEvidence(projectRoot2, code, relativePath2) {
|
|
7629
7633
|
if (!relativePath2) return { code, satisfied: false };
|
|
7630
|
-
const file = path2.resolve(
|
|
7634
|
+
const file = path2.resolve(projectRoot2, relativePath2);
|
|
7631
7635
|
return {
|
|
7632
7636
|
code,
|
|
7633
7637
|
satisfied: await fileExists(file),
|
|
7634
|
-
source: relativeSource(
|
|
7638
|
+
source: relativeSource(projectRoot2, file)
|
|
7635
7639
|
};
|
|
7636
7640
|
}
|
|
7637
|
-
async function directFileEvidence(
|
|
7641
|
+
async function directFileEvidence(projectRoot2, code, file) {
|
|
7638
7642
|
return {
|
|
7639
7643
|
code,
|
|
7640
7644
|
satisfied: await fileExists(file),
|
|
7641
|
-
source: relativeSource(
|
|
7645
|
+
source: relativeSource(projectRoot2, file)
|
|
7642
7646
|
};
|
|
7643
7647
|
}
|
|
7644
|
-
async function deltaSpecEvidence(
|
|
7648
|
+
async function deltaSpecEvidence(projectRoot2, changeDir) {
|
|
7645
7649
|
const specsDir = path2.join(changeDir, "specs");
|
|
7646
7650
|
let entries;
|
|
7647
7651
|
try {
|
|
@@ -7657,11 +7661,11 @@ async function deltaSpecEvidence(projectRoot, changeDir) {
|
|
|
7657
7661
|
return {
|
|
7658
7662
|
code: "openspec.delta-spec",
|
|
7659
7663
|
satisfied: existing.length > 0,
|
|
7660
|
-
source: existing[0] ? relativeSource(
|
|
7664
|
+
source: existing[0] ? relativeSource(projectRoot2, existing[0]) : void 0,
|
|
7661
7665
|
detail: `${existing.length} delta spec${existing.length === 1 ? "" : "s"}`
|
|
7662
7666
|
};
|
|
7663
7667
|
}
|
|
7664
|
-
async function taskEvidence(
|
|
7668
|
+
async function taskEvidence(projectRoot2, tasksFile) {
|
|
7665
7669
|
let source;
|
|
7666
7670
|
try {
|
|
7667
7671
|
source = await fs2.readFile(tasksFile, "utf8");
|
|
@@ -7676,7 +7680,7 @@ async function taskEvidence(projectRoot, tasksFile) {
|
|
|
7676
7680
|
return {
|
|
7677
7681
|
code: "build.tasks-complete",
|
|
7678
7682
|
satisfied: tasks.length > 0 && complete === tasks.length,
|
|
7679
|
-
source: relativeSource(
|
|
7683
|
+
source: relativeSource(projectRoot2, tasksFile),
|
|
7680
7684
|
detail: `${complete} of ${tasks.length} tasks complete`
|
|
7681
7685
|
};
|
|
7682
7686
|
}
|
|
@@ -7684,29 +7688,33 @@ function evidenceSatisfied(evidence, code) {
|
|
|
7684
7688
|
return evidence.some((item) => item.code === code && item.satisfied);
|
|
7685
7689
|
}
|
|
7686
7690
|
async function collectClassicEvidence(changeDir, projection) {
|
|
7687
|
-
const
|
|
7691
|
+
const projectRoot2 = projectRootFor(changeDir);
|
|
7688
7692
|
const classic = projection.classic;
|
|
7689
7693
|
const proposal = path2.join(changeDir, "proposal.md");
|
|
7690
7694
|
const design = path2.join(changeDir, "design.md");
|
|
7691
7695
|
const tasks = path2.join(changeDir, "tasks.md");
|
|
7692
7696
|
const checkpoint = projection.run ? path2.resolve(changeDir, projection.run.checkpointRef) : path2.join(changeDir, ".comet", "checkpoint.json");
|
|
7693
7697
|
const evidence = await Promise.all([
|
|
7694
|
-
directFileEvidence(
|
|
7695
|
-
directFileEvidence(
|
|
7696
|
-
directFileEvidence(
|
|
7697
|
-
deltaSpecEvidence(
|
|
7698
|
-
linkedFileEvidence(
|
|
7699
|
-
linkedFileEvidence(
|
|
7700
|
-
taskEvidence(
|
|
7701
|
-
linkedFileEvidence(
|
|
7702
|
-
linkedFileEvidence(
|
|
7703
|
-
directFileEvidence(
|
|
7698
|
+
directFileEvidence(projectRoot2, "openspec.proposal", proposal),
|
|
7699
|
+
directFileEvidence(projectRoot2, "openspec.design", design),
|
|
7700
|
+
directFileEvidence(projectRoot2, "openspec.tasks", tasks),
|
|
7701
|
+
deltaSpecEvidence(projectRoot2, changeDir),
|
|
7702
|
+
linkedFileEvidence(projectRoot2, "design.document", classic?.designDoc ?? null),
|
|
7703
|
+
linkedFileEvidence(projectRoot2, "build.plan", classic?.plan ?? null),
|
|
7704
|
+
taskEvidence(projectRoot2, tasks),
|
|
7705
|
+
linkedFileEvidence(projectRoot2, "verification.report", classic?.verificationReport ?? null),
|
|
7706
|
+
linkedFileEvidence(projectRoot2, "design.handoff", classic?.handoffContext ?? null),
|
|
7707
|
+
directFileEvidence(projectRoot2, "run.checkpoint", checkpoint)
|
|
7704
7708
|
]);
|
|
7705
7709
|
const handoff = evidence.find((item) => item.code === "design.handoff");
|
|
7706
7710
|
if (handoff && !classic?.handoffHash) {
|
|
7707
7711
|
handoff.satisfied = false;
|
|
7708
7712
|
handoff.detail = "handoff hash is missing";
|
|
7709
7713
|
}
|
|
7714
|
+
evidence.push({
|
|
7715
|
+
code: "archive.confirmed",
|
|
7716
|
+
satisfied: classic?.archiveConfirmation === "confirmed"
|
|
7717
|
+
});
|
|
7710
7718
|
return evidence;
|
|
7711
7719
|
}
|
|
7712
7720
|
|
|
@@ -7753,11 +7761,11 @@ function resolveVerify(profile, classic, evidence) {
|
|
|
7753
7761
|
}
|
|
7754
7762
|
return `${profile}.verify.branch`;
|
|
7755
7763
|
}
|
|
7756
|
-
function resolveArchive(profile, classic
|
|
7764
|
+
function resolveArchive(profile, classic) {
|
|
7757
7765
|
if (classic.verifyResult !== "pass") {
|
|
7758
7766
|
throw new Error("archive requires verify_result=pass");
|
|
7759
7767
|
}
|
|
7760
|
-
return
|
|
7768
|
+
return classic.archiveConfirmation === "confirmed" ? `${profile}.archive.execute` : `${profile}.archive.confirm`;
|
|
7761
7769
|
}
|
|
7762
7770
|
function resolveClassicStepId(classic, evidence) {
|
|
7763
7771
|
const profile = profileFor(classic);
|
|
@@ -7778,7 +7786,7 @@ function resolveClassicStepId(classic, evidence) {
|
|
|
7778
7786
|
case "verify":
|
|
7779
7787
|
return resolveVerify(profile, classic, evidence);
|
|
7780
7788
|
case "archive":
|
|
7781
|
-
return resolveArchive(profile, classic
|
|
7789
|
+
return resolveArchive(profile, classic);
|
|
7782
7790
|
}
|
|
7783
7791
|
}
|
|
7784
7792
|
|
|
@@ -7804,6 +7812,7 @@ var ISOLATIONS = ["branch", "worktree"];
|
|
|
7804
7812
|
var VERIFY_MODES = ["light", "full"];
|
|
7805
7813
|
var VERIFY_RESULTS = ["pending", "pass", "fail"];
|
|
7806
7814
|
var BRANCH_STATUSES = ["pending", "handled"];
|
|
7815
|
+
var ARCHIVE_CONFIRMATIONS = ["pending", "confirmed"];
|
|
7807
7816
|
var CLASSIC_WIRE_KEYS = [
|
|
7808
7817
|
"workflow",
|
|
7809
7818
|
"language",
|
|
@@ -7825,6 +7834,7 @@ var CLASSIC_WIRE_KEYS = [
|
|
|
7825
7834
|
"branch_status",
|
|
7826
7835
|
"created_at",
|
|
7827
7836
|
"verified_at",
|
|
7837
|
+
"archive_confirmation",
|
|
7828
7838
|
"archived",
|
|
7829
7839
|
"direct_override",
|
|
7830
7840
|
"handoff_context",
|
|
@@ -7933,6 +7943,7 @@ function classicStateFromDocument(doc) {
|
|
|
7933
7943
|
branchStatus: enumValue(doc, "branch_status", BRANCH_STATUSES),
|
|
7934
7944
|
createdAt: nullableString(doc, "created_at"),
|
|
7935
7945
|
verifiedAt: nullableString(doc, "verified_at"),
|
|
7946
|
+
archiveConfirmation: enumValue(doc, "archive_confirmation", ARCHIVE_CONFIRMATIONS),
|
|
7936
7947
|
archived: booleanValue(doc, "archived", false),
|
|
7937
7948
|
directOverride: booleanValue(doc, "direct_override"),
|
|
7938
7949
|
handoffContext: relativePath(doc, "handoff_context"),
|
|
@@ -7996,6 +8007,7 @@ function classicStateToDocument(state) {
|
|
|
7996
8007
|
branch_status: state.branchStatus,
|
|
7997
8008
|
created_at: state.createdAt,
|
|
7998
8009
|
verified_at: state.verifiedAt,
|
|
8010
|
+
archive_confirmation: state.archiveConfirmation,
|
|
7999
8011
|
archived: state.archived,
|
|
8000
8012
|
direct_override: state.directOverride,
|
|
8001
8013
|
handoff_context: state.handoffContext,
|
|
@@ -8077,7 +8089,8 @@ async function readDocument(file) {
|
|
|
8077
8089
|
documentRecord(document);
|
|
8078
8090
|
return document;
|
|
8079
8091
|
}
|
|
8080
|
-
async function readClassicState(changeDir) {
|
|
8092
|
+
async function readClassicState(changeDir, options = {}) {
|
|
8093
|
+
const shouldMigrate = options.migrate !== false;
|
|
8081
8094
|
const file = path4.join(changeDir, ".comet.yaml");
|
|
8082
8095
|
const document = await readDocument(file);
|
|
8083
8096
|
let doc = documentRecord(document);
|
|
@@ -8087,13 +8100,13 @@ async function readClassicState(changeDir) {
|
|
|
8087
8100
|
if (!run && doc.run_id && doc.skill) {
|
|
8088
8101
|
const { runStateFromDocument: runStateFromDocument2 } = await Promise.resolve().then(() => (init_state(), state_exports));
|
|
8089
8102
|
run = runStateFromDocument2(doc);
|
|
8090
|
-
if (run) {
|
|
8103
|
+
if (run && shouldMigrate) {
|
|
8091
8104
|
await writeRunState(changeDir, run);
|
|
8092
8105
|
stripLegacyRunFields(document);
|
|
8093
8106
|
migrated = true;
|
|
8094
8107
|
}
|
|
8095
8108
|
}
|
|
8096
|
-
if (migrated) {
|
|
8109
|
+
if (migrated && shouldMigrate) {
|
|
8097
8110
|
const temporary = path4.join(changeDir, `.comet.yaml.${randomUUID2()}.tmp`);
|
|
8098
8111
|
await fs4.writeFile(temporary, document.toString(), "utf8");
|
|
8099
8112
|
await fs4.rename(temporary, file);
|
|
@@ -8830,13 +8843,13 @@ function artifactKey(code) {
|
|
|
8830
8843
|
return code.replaceAll(".", "_").replaceAll("-", "_");
|
|
8831
8844
|
}
|
|
8832
8845
|
async function migrationArtifacts(changeDir, evidence) {
|
|
8833
|
-
const
|
|
8846
|
+
const projectRoot2 = projectRootFor2(changeDir);
|
|
8834
8847
|
const artifacts = Object.fromEntries(
|
|
8835
8848
|
evidence.filter((item) => item.satisfied && item.source).map((item) => [artifactKey(item.code), item.source])
|
|
8836
8849
|
);
|
|
8837
8850
|
const progress = path9.join(changeDir, "subagent-progress.md");
|
|
8838
8851
|
if (await pathExists2(progress)) {
|
|
8839
|
-
artifacts.subagent_progress = path9.relative(
|
|
8852
|
+
artifacts.subagent_progress = path9.relative(projectRoot2, progress).split(path9.sep).join("/");
|
|
8840
8853
|
}
|
|
8841
8854
|
const handoff = evidence.find((item) => item.code === "design.handoff" && item.satisfied);
|
|
8842
8855
|
if (handoff?.source) artifacts.handoff_context = handoff.source;
|
|
@@ -8928,11 +8941,11 @@ async function ensureClassicRun(changeDir, options) {
|
|
|
8928
8941
|
classicMigration: CLASSIC_MIGRATION_VERSION
|
|
8929
8942
|
};
|
|
8930
8943
|
const artifacts = await migrationArtifacts(changeDir, evidence);
|
|
8931
|
-
const
|
|
8944
|
+
const projectRoot2 = projectRootFor2(changeDir);
|
|
8932
8945
|
const handoff = evidence.find((item) => item.code === "design.handoff" && item.satisfied);
|
|
8933
8946
|
let context = null;
|
|
8934
8947
|
if (handoff?.source) {
|
|
8935
|
-
context = await fs8.readFile(path9.resolve(
|
|
8948
|
+
context = await fs8.readFile(path9.resolve(projectRoot2, handoff.source), "utf8");
|
|
8936
8949
|
await writeContext(changeDir, run.contextRef, context);
|
|
8937
8950
|
createdFiles.push(path9.resolve(changeDir, run.contextRef));
|
|
8938
8951
|
}
|
|
@@ -9251,6 +9264,46 @@ async function ensureStrictClassicRuntimeRun(changeDir) {
|
|
|
9251
9264
|
}
|
|
9252
9265
|
return ensureClassicRuntimeRun(changeDir);
|
|
9253
9266
|
}
|
|
9267
|
+
async function validateClassicRuntimeRun(changeDir, existingProjection) {
|
|
9268
|
+
const projection = existingProjection ?? await readClassicState(changeDir, { migrate: false });
|
|
9269
|
+
const unknownKeys = Array.from(new Set(projection.unknownKeys)).sort();
|
|
9270
|
+
if (unknownKeys.length > 0) {
|
|
9271
|
+
throw new Error(`Invalid Classic state: unknown field(s): ${unknownKeys.join(", ")}`);
|
|
9272
|
+
}
|
|
9273
|
+
if (!projection.classic || !projection.run) {
|
|
9274
|
+
throw new Error("Classic runtime validation requires synchronized Classic and Run projections");
|
|
9275
|
+
}
|
|
9276
|
+
if (projection.classic.classicMigration !== CLASSIC_MIGRATION_VERSION) {
|
|
9277
|
+
throw new Error("Classic Run exists without a supported classic_migration marker");
|
|
9278
|
+
}
|
|
9279
|
+
const root = await classicRuntimeRoot();
|
|
9280
|
+
const skillPackage = root ? await loadClassicRuntimePackage(root) : embeddedClassicRuntimePackage(path10.dirname(fileURLToPath(import.meta.url)));
|
|
9281
|
+
if (projection.run.skill !== skillPackage.definition.metadata.name) {
|
|
9282
|
+
throw new Error(
|
|
9283
|
+
`Classic Run skill mismatch: expected ${skillPackage.definition.metadata.name}, got ${projection.run.skill}`
|
|
9284
|
+
);
|
|
9285
|
+
}
|
|
9286
|
+
const snapshot = await readSkillSnapshot(changeDir, projection.run.skillHash);
|
|
9287
|
+
if (snapshot.definition.metadata.name !== projection.run.skill) {
|
|
9288
|
+
throw new Error(
|
|
9289
|
+
`Classic Run snapshot skill mismatch: expected ${projection.run.skill}, got ${snapshot.definition.metadata.name}`
|
|
9290
|
+
);
|
|
9291
|
+
}
|
|
9292
|
+
const evidence = await collectClassicEvidence(changeDir, projection);
|
|
9293
|
+
const currentStep = resolveClassicStepId(projection.classic, evidence);
|
|
9294
|
+
if (projection.run.currentStep !== currentStep) {
|
|
9295
|
+
throw new Error(
|
|
9296
|
+
`Classic Run step mismatch: expected ${currentStep}, got ${projection.run.currentStep}`
|
|
9297
|
+
);
|
|
9298
|
+
}
|
|
9299
|
+
return {
|
|
9300
|
+
classic: projection.classic,
|
|
9301
|
+
run: projection.run,
|
|
9302
|
+
evidence,
|
|
9303
|
+
migrated: false,
|
|
9304
|
+
snapshotDir: path10.join(changeDir, ".comet", "skill-snapshots", projection.run.skillHash)
|
|
9305
|
+
};
|
|
9306
|
+
}
|
|
9254
9307
|
async function transitionClassicRuntimeRun(changeDir, classic, run, data) {
|
|
9255
9308
|
const projection = await readClassicState(changeDir);
|
|
9256
9309
|
if (!projection.classic || !projection.run) {
|
|
@@ -9313,6 +9366,7 @@ var CLASSIC_TRANSITION_EVENTS = [
|
|
|
9313
9366
|
"build-complete",
|
|
9314
9367
|
"verify-pass",
|
|
9315
9368
|
"verify-fail",
|
|
9369
|
+
"archive-confirm",
|
|
9316
9370
|
"archive-reopen",
|
|
9317
9371
|
"archived",
|
|
9318
9372
|
"preset-escalate"
|
|
@@ -9343,6 +9397,11 @@ var CLASSIC_TRANSITION_TABLE = {
|
|
|
9343
9397
|
from: "verify",
|
|
9344
9398
|
guardRefs: ["verification-failed"]
|
|
9345
9399
|
},
|
|
9400
|
+
"archive-confirm": {
|
|
9401
|
+
event: "archive-confirm",
|
|
9402
|
+
from: "archive",
|
|
9403
|
+
guardRefs: ["archive-final-confirmation"]
|
|
9404
|
+
},
|
|
9346
9405
|
"archive-reopen": {
|
|
9347
9406
|
event: "archive-reopen",
|
|
9348
9407
|
from: "archive",
|
|
@@ -9351,7 +9410,7 @@ var CLASSIC_TRANSITION_TABLE = {
|
|
|
9351
9410
|
archived: {
|
|
9352
9411
|
event: "archived",
|
|
9353
9412
|
from: "archive",
|
|
9354
|
-
guardRefs: ["verify-result-pass"]
|
|
9413
|
+
guardRefs: ["verify-result-pass", "archive-confirmed"]
|
|
9355
9414
|
},
|
|
9356
9415
|
"preset-escalate": {
|
|
9357
9416
|
event: "preset-escalate",
|
|
@@ -9399,6 +9458,7 @@ function applyClassicTransition(current, event, options = {}) {
|
|
|
9399
9458
|
setField(classic, effects, "verifyResult", "pass");
|
|
9400
9459
|
setField(classic, effects, "phase", "archive");
|
|
9401
9460
|
setField(classic, effects, "verifiedAt", dateOnly(now));
|
|
9461
|
+
setField(classic, effects, "archiveConfirmation", "pending");
|
|
9402
9462
|
} else if (event === "verify-fail") {
|
|
9403
9463
|
setField(classic, effects, "verifyResult", "fail");
|
|
9404
9464
|
setField(classic, effects, "phase", "build");
|
|
@@ -9412,15 +9472,25 @@ function applyClassicTransition(current, event, options = {}) {
|
|
|
9412
9472
|
setField(classic, effects, "classicProfile", "full");
|
|
9413
9473
|
setField(classic, effects, "phase", "design");
|
|
9414
9474
|
setField(classic, effects, "designDoc", null);
|
|
9475
|
+
} else if (event === "archive-confirm") {
|
|
9476
|
+
if (classic.verifyResult !== "pass") {
|
|
9477
|
+
throw new Error(`Cannot apply ${event}: verifyResult must be pass`);
|
|
9478
|
+
}
|
|
9479
|
+
if (classic.archived) throw new Error(`Cannot apply ${event}: already archived`);
|
|
9480
|
+
setField(classic, effects, "archiveConfirmation", "confirmed");
|
|
9415
9481
|
} else if (event === "archive-reopen") {
|
|
9416
9482
|
if (classic.archived) throw new Error(`Cannot apply ${event}: already archived`);
|
|
9417
9483
|
setField(classic, effects, "verifyResult", "pending");
|
|
9418
9484
|
setField(classic, effects, "phase", "verify");
|
|
9419
9485
|
setField(classic, effects, "verifiedAt", null);
|
|
9486
|
+
setField(classic, effects, "archiveConfirmation", null);
|
|
9420
9487
|
} else {
|
|
9421
9488
|
if (classic.verifyResult !== "pass") {
|
|
9422
9489
|
throw new Error(`Cannot apply ${event}: verifyResult must be pass`);
|
|
9423
9490
|
}
|
|
9491
|
+
if (classic.archiveConfirmation !== "confirmed") {
|
|
9492
|
+
throw new Error(`Cannot apply ${event}: archiveConfirmation must be confirmed`);
|
|
9493
|
+
}
|
|
9424
9494
|
setField(classic, effects, "archived", true);
|
|
9425
9495
|
}
|
|
9426
9496
|
return { classic, effects, definition };
|
|
@@ -9483,6 +9553,33 @@ function artifactsHash(artifacts) {
|
|
|
9483
9553
|
)
|
|
9484
9554
|
);
|
|
9485
9555
|
}
|
|
9556
|
+
function exactlyOneFinalNewline(markdown) {
|
|
9557
|
+
return `${markdown.replace(/\n+$/u, "")}
|
|
9558
|
+
`;
|
|
9559
|
+
}
|
|
9560
|
+
function annotatedMarkdown(original, archiveName, extraFields) {
|
|
9561
|
+
const normalized2 = original.replace(/\r\n/gu, "\n");
|
|
9562
|
+
const lines = normalized2.split("\n");
|
|
9563
|
+
const closingDelimiter = lines[0] === "---" ? lines.indexOf("---", 1) : -1;
|
|
9564
|
+
const extraFieldName = extraFields.match(/^([^:\n]+):/u)?.[1]?.trim();
|
|
9565
|
+
if (closingDelimiter !== -1) {
|
|
9566
|
+
const frontmatter = lines.slice(1, closingDelimiter).filter((line) => {
|
|
9567
|
+
const fieldName = line.match(/^([^:\n]+):/u)?.[1]?.trim();
|
|
9568
|
+
if (fieldName === void 0) return true;
|
|
9569
|
+
return fieldName !== "archived-with" && fieldName !== extraFieldName;
|
|
9570
|
+
});
|
|
9571
|
+
frontmatter.push(`archived-with: ${archiveName}`);
|
|
9572
|
+
if (extraFields) frontmatter.push(extraFields);
|
|
9573
|
+
return exactlyOneFinalNewline(
|
|
9574
|
+
["---", ...frontmatter, "---", ...lines.slice(closingDelimiter + 1)].join("\n")
|
|
9575
|
+
);
|
|
9576
|
+
}
|
|
9577
|
+
const header = ["---", `archived-with: ${archiveName}`];
|
|
9578
|
+
if (extraFields) header.push(extraFields);
|
|
9579
|
+
if (extraFieldName !== "status") header.push("status: final");
|
|
9580
|
+
header.push("---");
|
|
9581
|
+
return exactlyOneFinalNewline([...header, normalized2].join("\n"));
|
|
9582
|
+
}
|
|
9486
9583
|
async function findArchiveDir(change, preferred) {
|
|
9487
9584
|
if (await exists2(preferred)) return preferred;
|
|
9488
9585
|
const archiveRoot = "openspec/changes/archive";
|
|
@@ -9521,37 +9618,7 @@ async function annotateFrontmatter(output, file, archiveName, extraFields, dryRu
|
|
|
9521
9618
|
return;
|
|
9522
9619
|
}
|
|
9523
9620
|
const original = await fs11.readFile(file, "utf8");
|
|
9524
|
-
const
|
|
9525
|
-
let updated;
|
|
9526
|
-
if (firstLine === "---") {
|
|
9527
|
-
const lines = original.split(/\r?\n/u);
|
|
9528
|
-
const out = [];
|
|
9529
|
-
let sawFirst = false;
|
|
9530
|
-
for (const line of lines) {
|
|
9531
|
-
if (/^archived-with:/u.test(line)) continue;
|
|
9532
|
-
if (line === "---") {
|
|
9533
|
-
if (!sawFirst) {
|
|
9534
|
-
out.push(line);
|
|
9535
|
-
sawFirst = true;
|
|
9536
|
-
continue;
|
|
9537
|
-
}
|
|
9538
|
-
out.push(`archived-with: ${archiveName}`);
|
|
9539
|
-
if (extraFields) out.push(extraFields);
|
|
9540
|
-
out.push(line);
|
|
9541
|
-
} else {
|
|
9542
|
-
out.push(line);
|
|
9543
|
-
}
|
|
9544
|
-
}
|
|
9545
|
-
updated = `${out.join("\n")}
|
|
9546
|
-
`;
|
|
9547
|
-
} else {
|
|
9548
|
-
const header = ["---", `archived-with: ${archiveName}`];
|
|
9549
|
-
if (extraFields) header.push(extraFields);
|
|
9550
|
-
header.push("status: final", "---");
|
|
9551
|
-
updated = `${header.join("\n")}
|
|
9552
|
-
${original}`;
|
|
9553
|
-
if (!updated.endsWith("\n")) updated += "\n";
|
|
9554
|
-
}
|
|
9621
|
+
const updated = annotatedMarkdown(original, archiveName, extraFields);
|
|
9555
9622
|
await fs11.writeFile(file, updated);
|
|
9556
9623
|
output.stderr.push(green(` [OK] Annotated: ${file}`));
|
|
9557
9624
|
output.stepsOk += 1;
|
|
@@ -9636,6 +9703,13 @@ var classicArchiveCommand = async (args) => {
|
|
|
9636
9703
|
if (runtime.run.pending && runtime.run.pending !== actionId) {
|
|
9637
9704
|
throw new ArchiveFailure(red(`FATAL: another action is pending: ${runtime.run.pending}`));
|
|
9638
9705
|
}
|
|
9706
|
+
if (!recovering && !classic.archived && classic.archiveConfirmation !== "confirmed") {
|
|
9707
|
+
throw new ArchiveFailure(
|
|
9708
|
+
red(
|
|
9709
|
+
`FATAL: archive_confirmation is '${classic.archiveConfirmation ?? "null"}', expected 'confirmed'. Run final archive confirmation first.`
|
|
9710
|
+
)
|
|
9711
|
+
);
|
|
9712
|
+
}
|
|
9639
9713
|
if (!recovering) {
|
|
9640
9714
|
const action = {
|
|
9641
9715
|
id: actionId,
|
|
@@ -9707,7 +9781,10 @@ var classicArchiveCommand = async (args) => {
|
|
|
9707
9781
|
archive_directory: archiveDir
|
|
9708
9782
|
};
|
|
9709
9783
|
await writeArtifacts(archiveDir, archivedProjection.run.artifactsRef, artifacts);
|
|
9710
|
-
const archiveTransition = applyClassicTransition(
|
|
9784
|
+
const archiveTransition = applyClassicTransition(
|
|
9785
|
+
recovering && archivedProjection.classic.archiveConfirmation !== "confirmed" ? { ...archivedProjection.classic, archiveConfirmation: "confirmed" } : archivedProjection.classic,
|
|
9786
|
+
"archived"
|
|
9787
|
+
);
|
|
9711
9788
|
const archivedClassic = archiveTransition.classic;
|
|
9712
9789
|
let transitionedRun = archivedProjection.run;
|
|
9713
9790
|
if (archivedProjection.run.currentStep !== "completed" || archivedProjection.run.status !== "completed") {
|
|
@@ -9805,11 +9882,98 @@ var classicArchiveCommand = async (args) => {
|
|
|
9805
9882
|
};
|
|
9806
9883
|
|
|
9807
9884
|
// domains/comet-classic/classic-guard.ts
|
|
9808
|
-
var
|
|
9885
|
+
var import_yaml5 = __toESM(require_dist(), 1);
|
|
9809
9886
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
9810
9887
|
import { createHash as createHash4 } from "crypto";
|
|
9811
|
-
import { existsSync, promises as
|
|
9812
|
-
import
|
|
9888
|
+
import { existsSync, promises as fs15, readFileSync } from "fs";
|
|
9889
|
+
import path16 from "path";
|
|
9890
|
+
|
|
9891
|
+
// domains/comet-classic/classic-command-checks.ts
|
|
9892
|
+
import path13 from "path";
|
|
9893
|
+
function validateScope(scope) {
|
|
9894
|
+
if (scope !== "build" && scope !== "verify") {
|
|
9895
|
+
throw new Error(`Invalid command check scope: '${String(scope)}'`);
|
|
9896
|
+
}
|
|
9897
|
+
}
|
|
9898
|
+
function projectRoot(changeDir) {
|
|
9899
|
+
return path13.resolve(changeDir, "..", "..", "..");
|
|
9900
|
+
}
|
|
9901
|
+
function normalizedCwd(changeDir, cwd = ".") {
|
|
9902
|
+
if (cwd.trim().length === 0) throw new Error("Command check cwd cannot be blank");
|
|
9903
|
+
const root = projectRoot(changeDir);
|
|
9904
|
+
const target = path13.resolve(root, cwd);
|
|
9905
|
+
if (target !== root && !target.startsWith(root + path13.sep)) {
|
|
9906
|
+
throw new Error(`Command check cwd must resolve within the project root: '${cwd}'`);
|
|
9907
|
+
}
|
|
9908
|
+
return path13.relative(root, target).replaceAll("\\", "/") || ".";
|
|
9909
|
+
}
|
|
9910
|
+
function validRecord(changeDir, event) {
|
|
9911
|
+
if (event.type !== "command_check_recorded") return null;
|
|
9912
|
+
const data = event.data;
|
|
9913
|
+
if (typeof data !== "object" || data === null || Array.isArray(data)) return null;
|
|
9914
|
+
const { scope, command, exitCode, cwd } = data;
|
|
9915
|
+
if (scope !== "build" && scope !== "verify" || typeof command !== "string" || command.trim().length === 0 || !Number.isInteger(exitCode) || typeof cwd !== "string") {
|
|
9916
|
+
return null;
|
|
9917
|
+
}
|
|
9918
|
+
let normalized2;
|
|
9919
|
+
try {
|
|
9920
|
+
normalized2 = normalizedCwd(changeDir, cwd);
|
|
9921
|
+
} catch {
|
|
9922
|
+
return null;
|
|
9923
|
+
}
|
|
9924
|
+
return {
|
|
9925
|
+
sequence: event.sequence,
|
|
9926
|
+
timestamp: event.timestamp,
|
|
9927
|
+
runId: event.runId,
|
|
9928
|
+
scope,
|
|
9929
|
+
command,
|
|
9930
|
+
exitCode,
|
|
9931
|
+
cwd: normalized2
|
|
9932
|
+
};
|
|
9933
|
+
}
|
|
9934
|
+
async function recordCommandCheck(changeDir, run, input) {
|
|
9935
|
+
validateScope(input.scope);
|
|
9936
|
+
if (typeof input.command !== "string" || input.command.trim().length === 0) {
|
|
9937
|
+
throw new Error("Command check command cannot be blank");
|
|
9938
|
+
}
|
|
9939
|
+
if (!Number.isInteger(input.exitCode)) {
|
|
9940
|
+
throw new Error("Command check exitCode must be an integer");
|
|
9941
|
+
}
|
|
9942
|
+
const trajectory = await readTrajectory(changeDir, run.trajectoryRef);
|
|
9943
|
+
const recorded = {
|
|
9944
|
+
sequence: trajectory.reduce((maximum, event) => Math.max(maximum, event.sequence), 0) + 1,
|
|
9945
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9946
|
+
runId: run.runId,
|
|
9947
|
+
scope: input.scope,
|
|
9948
|
+
command: input.command,
|
|
9949
|
+
exitCode: input.exitCode,
|
|
9950
|
+
cwd: normalizedCwd(changeDir, input.cwd)
|
|
9951
|
+
};
|
|
9952
|
+
await appendTrajectory(changeDir, run.trajectoryRef, {
|
|
9953
|
+
sequence: recorded.sequence,
|
|
9954
|
+
timestamp: recorded.timestamp,
|
|
9955
|
+
type: "command_check_recorded",
|
|
9956
|
+
runId: recorded.runId,
|
|
9957
|
+
data: {
|
|
9958
|
+
scope: recorded.scope,
|
|
9959
|
+
command: recorded.command,
|
|
9960
|
+
exitCode: recorded.exitCode,
|
|
9961
|
+
cwd: recorded.cwd
|
|
9962
|
+
}
|
|
9963
|
+
});
|
|
9964
|
+
return recorded;
|
|
9965
|
+
}
|
|
9966
|
+
async function latestCommandCheck(changeDir, run, scope) {
|
|
9967
|
+
validateScope(scope);
|
|
9968
|
+
const trajectory = await readTrajectory(changeDir, run.trajectoryRef);
|
|
9969
|
+
for (let index = trajectory.length - 1; index >= 0; index -= 1) {
|
|
9970
|
+
const event = trajectory[index];
|
|
9971
|
+
if (event.runId !== run.runId) continue;
|
|
9972
|
+
const record = validRecord(changeDir, event);
|
|
9973
|
+
if (record?.scope === scope) return record;
|
|
9974
|
+
}
|
|
9975
|
+
return null;
|
|
9976
|
+
}
|
|
9813
9977
|
|
|
9814
9978
|
// domains/comet-classic/classic-runtime-evals.ts
|
|
9815
9979
|
var STEP_EVIDENCE = {
|
|
@@ -9904,7 +10068,7 @@ async function inspectClassicChange(changeDir, name) {
|
|
|
9904
10068
|
// domains/comet-classic/classic-validate-command.ts
|
|
9905
10069
|
var import_yaml3 = __toESM(require_dist(), 1);
|
|
9906
10070
|
import { promises as fs12 } from "fs";
|
|
9907
|
-
import
|
|
10071
|
+
import path14 from "path";
|
|
9908
10072
|
var GREEN2 = "\x1B[32m";
|
|
9909
10073
|
var RED2 = "\x1B[31m";
|
|
9910
10074
|
var YELLOW2 = "\x1B[33m";
|
|
@@ -9936,6 +10100,7 @@ var ENUMS = {
|
|
|
9936
10100
|
auto_transition: ["true", "false"],
|
|
9937
10101
|
verify_result: ["pending", "pass", "fail"],
|
|
9938
10102
|
branch_status: ["pending", "handled"],
|
|
10103
|
+
archive_confirmation: ["pending", "confirmed"],
|
|
9939
10104
|
archived: ["true", "false"],
|
|
9940
10105
|
direct_override: ["true", "false"],
|
|
9941
10106
|
classic_profile: ["full", "hotfix", "tweak"],
|
|
@@ -9974,7 +10139,7 @@ var classicValidateCommand = async (args) => {
|
|
|
9974
10139
|
};
|
|
9975
10140
|
}
|
|
9976
10141
|
const { directory, label } = await resolveClassicChangeDirectory(name);
|
|
9977
|
-
const yamlFile =
|
|
10142
|
+
const yamlFile = path14.join(directory, ".comet.yaml");
|
|
9978
10143
|
const lines = [`[VALIDATE] ${label}/.comet.yaml`];
|
|
9979
10144
|
let errors = 0;
|
|
9980
10145
|
let warnings = 0;
|
|
@@ -10025,7 +10190,7 @@ var classicValidateCommand = async (args) => {
|
|
|
10025
10190
|
}
|
|
10026
10191
|
for (const field2 of ["design_doc", "plan", "handoff_context"]) {
|
|
10027
10192
|
const value = text(record[field2]);
|
|
10028
|
-
if (value && !await exists3(
|
|
10193
|
+
if (value && !await exists3(path14.resolve(value))) {
|
|
10029
10194
|
fail3(`${field2}='${value}' does not exist on disk`);
|
|
10030
10195
|
}
|
|
10031
10196
|
}
|
|
@@ -10047,6 +10212,62 @@ var classicValidateCommand = async (args) => {
|
|
|
10047
10212
|
return { exitCode: 0, stderr: lines.join("\n") };
|
|
10048
10213
|
};
|
|
10049
10214
|
|
|
10215
|
+
// domains/comet-classic/classic-project-config.ts
|
|
10216
|
+
var import_yaml4 = __toESM(require_dist(), 1);
|
|
10217
|
+
import os from "os";
|
|
10218
|
+
import { promises as fs14 } from "fs";
|
|
10219
|
+
import path15 from "path";
|
|
10220
|
+
|
|
10221
|
+
// platform/fs/file-system.ts
|
|
10222
|
+
import { promises as fs13 } from "fs";
|
|
10223
|
+
async function fileExists3(filePath) {
|
|
10224
|
+
try {
|
|
10225
|
+
await fs13.access(filePath);
|
|
10226
|
+
return true;
|
|
10227
|
+
} catch {
|
|
10228
|
+
return false;
|
|
10229
|
+
}
|
|
10230
|
+
}
|
|
10231
|
+
async function readDir(dirPath) {
|
|
10232
|
+
try {
|
|
10233
|
+
return await fs13.readdir(dirPath);
|
|
10234
|
+
} catch (error) {
|
|
10235
|
+
const code = error?.code;
|
|
10236
|
+
if (code === "ENOENT" || code === "ENOTDIR") {
|
|
10237
|
+
return [];
|
|
10238
|
+
}
|
|
10239
|
+
throw error;
|
|
10240
|
+
}
|
|
10241
|
+
}
|
|
10242
|
+
|
|
10243
|
+
// domains/comet-classic/classic-project-config.ts
|
|
10244
|
+
function configCandidates(options = {}) {
|
|
10245
|
+
const cwd = options.cwd ?? process.cwd();
|
|
10246
|
+
const homeDir = options.homeDir ?? os.homedir();
|
|
10247
|
+
const candidates = [
|
|
10248
|
+
{ file: path15.resolve(cwd, ".comet", "config.yaml"), source: ".comet/config.yaml" },
|
|
10249
|
+
{
|
|
10250
|
+
file: path15.resolve(homeDir, ".comet", "config.yaml"),
|
|
10251
|
+
source: "~/.comet/config.yaml"
|
|
10252
|
+
}
|
|
10253
|
+
];
|
|
10254
|
+
return candidates.filter(
|
|
10255
|
+
(candidate, index) => candidates.findIndex((entry2) => entry2.file === candidate.file) === index
|
|
10256
|
+
);
|
|
10257
|
+
}
|
|
10258
|
+
async function readClassicConfigValue(field2, options = {}) {
|
|
10259
|
+
for (const candidate of configCandidates(options)) {
|
|
10260
|
+
if (!await fileExists3(candidate.file)) continue;
|
|
10261
|
+
const document = (0, import_yaml4.parseDocument)(await fs14.readFile(candidate.file, "utf8"), {
|
|
10262
|
+
uniqueKeys: false
|
|
10263
|
+
});
|
|
10264
|
+
const value = document.get(field2);
|
|
10265
|
+
if (value === null || value === void 0) continue;
|
|
10266
|
+
return { value: String(value), source: candidate.source };
|
|
10267
|
+
}
|
|
10268
|
+
return null;
|
|
10269
|
+
}
|
|
10270
|
+
|
|
10050
10271
|
// domains/comet-classic/classic-guard.ts
|
|
10051
10272
|
var GREEN3 = "\x1B[32m";
|
|
10052
10273
|
var RED3 = "\x1B[31m";
|
|
@@ -10111,7 +10332,7 @@ var GuardOutput = class {
|
|
|
10111
10332
|
};
|
|
10112
10333
|
async function exists4(file) {
|
|
10113
10334
|
try {
|
|
10114
|
-
await
|
|
10335
|
+
await fs15.access(file);
|
|
10115
10336
|
return true;
|
|
10116
10337
|
} catch (error) {
|
|
10117
10338
|
if (error.code === "ENOENT") return false;
|
|
@@ -10120,7 +10341,7 @@ async function exists4(file) {
|
|
|
10120
10341
|
}
|
|
10121
10342
|
async function nonempty(file) {
|
|
10122
10343
|
try {
|
|
10123
|
-
return (await
|
|
10344
|
+
return (await fs15.stat(file)).size > 0;
|
|
10124
10345
|
} catch (error) {
|
|
10125
10346
|
if (error.code === "ENOENT") return false;
|
|
10126
10347
|
throw error;
|
|
@@ -10133,32 +10354,9 @@ function validateChangeName2(name) {
|
|
|
10133
10354
|
async function resolveChangeDir(name) {
|
|
10134
10355
|
return (await resolveClassicChangeDirectory(name)).label;
|
|
10135
10356
|
}
|
|
10136
|
-
function stripInlineComment(value) {
|
|
10137
|
-
let out = "";
|
|
10138
|
-
let quote = "";
|
|
10139
|
-
for (let i = 0; i < value.length; i += 1) {
|
|
10140
|
-
const c = value[i];
|
|
10141
|
-
if (quote === "") {
|
|
10142
|
-
if (c === '"' || c === "'") {
|
|
10143
|
-
quote = c;
|
|
10144
|
-
} else if (c === "#" && (i === 0 || /\s/u.test(value[i - 1]))) {
|
|
10145
|
-
return out.replace(/\s+$/u, "");
|
|
10146
|
-
}
|
|
10147
|
-
} else if (c === quote) {
|
|
10148
|
-
quote = "";
|
|
10149
|
-
}
|
|
10150
|
-
out += c;
|
|
10151
|
-
}
|
|
10152
|
-
return out;
|
|
10153
|
-
}
|
|
10154
|
-
function stripWrappingQuotes(value) {
|
|
10155
|
-
if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) return value.slice(1, -1);
|
|
10156
|
-
if (value.length >= 2 && value.startsWith("'") && value.endsWith("'")) return value.slice(1, -1);
|
|
10157
|
-
return value;
|
|
10158
|
-
}
|
|
10159
10357
|
async function readField(changeDir, field2) {
|
|
10160
|
-
const file =
|
|
10161
|
-
const document = (0,
|
|
10358
|
+
const file = path16.join(changeDir, ".comet.yaml");
|
|
10359
|
+
const document = (0, import_yaml5.parseDocument)(await fs15.readFile(file, "utf8"), { uniqueKeys: false });
|
|
10162
10360
|
if (document.errors.length > 0) {
|
|
10163
10361
|
throw new GuardFailure(`ERROR: Invalid .comet.yaml: ${document.errors[0].message}`);
|
|
10164
10362
|
}
|
|
@@ -10171,18 +10369,7 @@ async function readField(changeDir, field2) {
|
|
|
10171
10369
|
async function projectConfigValue(field2, changeDir) {
|
|
10172
10370
|
const changeValue = await readField(changeDir, field2);
|
|
10173
10371
|
if (changeValue && changeValue !== "null") return changeValue;
|
|
10174
|
-
|
|
10175
|
-
if (!await exists4(config)) continue;
|
|
10176
|
-
for (const line of (await fs13.readFile(config, "utf8")).split(/\r?\n/u)) {
|
|
10177
|
-
if (new RegExp(`^${field2}:`, "u").test(line)) {
|
|
10178
|
-
const value = stripWrappingQuotes(
|
|
10179
|
-
stripInlineComment(line.replace(new RegExp(`^${field2}:\\s*`, "u"), ""))
|
|
10180
|
-
);
|
|
10181
|
-
if (value && value !== "null") return value;
|
|
10182
|
-
}
|
|
10183
|
-
}
|
|
10184
|
-
}
|
|
10185
|
-
return "";
|
|
10372
|
+
return (await readClassicConfigValue(field2))?.value ?? "";
|
|
10186
10373
|
}
|
|
10187
10374
|
async function configuredLanguage(changeDir) {
|
|
10188
10375
|
const language = await projectConfigValue("language", changeDir);
|
|
@@ -10210,7 +10397,7 @@ function countEnglishWords(source) {
|
|
|
10210
10397
|
}
|
|
10211
10398
|
async function documentLanguageMatchesConfigured(changeDir, file) {
|
|
10212
10399
|
const language = await configuredLanguage(changeDir);
|
|
10213
|
-
const source = stripFencedCodeBlocks(await
|
|
10400
|
+
const source = stripFencedCodeBlocks(await fs15.readFile(file, "utf8"));
|
|
10214
10401
|
const cjk = countCjkChars(source);
|
|
10215
10402
|
const englishWords = countEnglishWords(source);
|
|
10216
10403
|
if (language === "zh-CN" && cjk < 20 && englishWords >= 20) {
|
|
@@ -10234,7 +10421,7 @@ async function handoffSourceFiles(changeDir) {
|
|
|
10234
10421
|
const files = [`${changeDir}/proposal.md`, `${changeDir}/design.md`, `${changeDir}/tasks.md`];
|
|
10235
10422
|
const specs = `${changeDir}/specs`;
|
|
10236
10423
|
if (await exists4(specs)) {
|
|
10237
|
-
for (const entry2 of (await
|
|
10424
|
+
for (const entry2 of (await fs15.readdir(specs)).sort()) {
|
|
10238
10425
|
const spec = `${specs}/${entry2}/spec.md`;
|
|
10239
10426
|
if (await exists4(spec)) files.push(spec);
|
|
10240
10427
|
}
|
|
@@ -10254,13 +10441,13 @@ async function preflight(changeDir, name) {
|
|
|
10254
10441
|
if (!await exists4(changeDir)) {
|
|
10255
10442
|
throw new GuardFailure(red2(`FATAL: change directory not found: ${changeDir}`));
|
|
10256
10443
|
}
|
|
10257
|
-
if (!await exists4(
|
|
10444
|
+
if (!await exists4(path16.join(changeDir, ".comet.yaml"))) {
|
|
10258
10445
|
throw new GuardFailure(red2(`FATAL: .comet.yaml not found in ${changeDir}`));
|
|
10259
10446
|
}
|
|
10260
|
-
const
|
|
10261
|
-
if (
|
|
10262
|
-
if (
|
|
10263
|
-
process.stderr.write(
|
|
10447
|
+
const result5 = await classicValidateCommand([name], { json: false });
|
|
10448
|
+
if (result5.exitCode !== 0) {
|
|
10449
|
+
if (result5.stderr)
|
|
10450
|
+
process.stderr.write(result5.stderr.endsWith("\n") ? result5.stderr : `${result5.stderr}
|
|
10264
10451
|
`);
|
|
10265
10452
|
throw new GuardFailure(red2("FATAL: .comet.yaml schema validation failed"));
|
|
10266
10453
|
}
|
|
@@ -10275,6 +10462,9 @@ async function preflight(changeDir, name) {
|
|
|
10275
10462
|
function pushCheck(output, outcome) {
|
|
10276
10463
|
if (outcome.passed) {
|
|
10277
10464
|
output.stderr.push(green2(` [PASS] ${outcome.description}`));
|
|
10465
|
+
if (outcome.detail) {
|
|
10466
|
+
for (const line of outcome.detail.split("\n")) output.stderr.push(green2(` ${line}`));
|
|
10467
|
+
}
|
|
10278
10468
|
} else {
|
|
10279
10469
|
output.stderr.push(red2(` [FAIL] ${outcome.description}`));
|
|
10280
10470
|
if (outcome.detail) {
|
|
@@ -10285,11 +10475,11 @@ function pushCheck(output, outcome) {
|
|
|
10285
10475
|
function check(description, run) {
|
|
10286
10476
|
return async () => {
|
|
10287
10477
|
try {
|
|
10288
|
-
const
|
|
10478
|
+
const result5 = await run();
|
|
10289
10479
|
return {
|
|
10290
10480
|
description,
|
|
10291
|
-
passed:
|
|
10292
|
-
detail: ("detail" in
|
|
10481
|
+
passed: result5.passed,
|
|
10482
|
+
detail: ("detail" in result5 ? result5.detail : "") ?? ""
|
|
10293
10483
|
};
|
|
10294
10484
|
} catch (error) {
|
|
10295
10485
|
return {
|
|
@@ -10300,8 +10490,8 @@ function check(description, run) {
|
|
|
10300
10490
|
}
|
|
10301
10491
|
};
|
|
10302
10492
|
}
|
|
10303
|
-
function pass() {
|
|
10304
|
-
return { passed: true };
|
|
10493
|
+
function pass(detail) {
|
|
10494
|
+
return { passed: true, ...detail ? { detail } : {} };
|
|
10305
10495
|
}
|
|
10306
10496
|
function fail(detail) {
|
|
10307
10497
|
return { passed: false, detail };
|
|
@@ -10315,10 +10505,15 @@ async function runChecks(output, builders) {
|
|
|
10315
10505
|
}
|
|
10316
10506
|
return blocked2;
|
|
10317
10507
|
}
|
|
10508
|
+
var INFERRED_COMMAND_SOURCES = [
|
|
10509
|
+
"package.json with a build script",
|
|
10510
|
+
"pom.xml",
|
|
10511
|
+
"Cargo.toml"
|
|
10512
|
+
];
|
|
10318
10513
|
async function removedProjectCommandField(field2) {
|
|
10319
|
-
const config =
|
|
10514
|
+
const config = path16.join(".comet", "config.yaml");
|
|
10320
10515
|
if (!await exists4(config)) return false;
|
|
10321
|
-
const document = (0,
|
|
10516
|
+
const document = (0, import_yaml5.parseDocument)(await fs15.readFile(config, "utf8"));
|
|
10322
10517
|
if (document.errors.length > 0) {
|
|
10323
10518
|
throw new Error(
|
|
10324
10519
|
`.comet/config.yaml is invalid YAML (${document.errors[0].message}); cannot check for removed "${field2}" field. Fix the config and retry.`
|
|
@@ -10334,47 +10529,79 @@ function removedProjectCommandRun(field2) {
|
|
|
10334
10529
|
};
|
|
10335
10530
|
}
|
|
10336
10531
|
function runInferred(command) {
|
|
10337
|
-
const
|
|
10532
|
+
const result5 = spawnSync2(command, { shell: true, encoding: "utf8", timeout: 3e5 });
|
|
10338
10533
|
return {
|
|
10339
|
-
status:
|
|
10340
|
-
output: `${
|
|
10534
|
+
status: result5.status ?? 1,
|
|
10535
|
+
output: `${result5.stdout ?? ""}${result5.stderr ?? ""}`.replace(/\n+$/u, "")
|
|
10341
10536
|
};
|
|
10342
10537
|
}
|
|
10343
|
-
async function
|
|
10344
|
-
if (
|
|
10345
|
-
|
|
10346
|
-
return
|
|
10347
|
-
}
|
|
10348
|
-
|
|
10349
|
-
return runInferred("npm run build");
|
|
10538
|
+
async function inferredBuildCommand() {
|
|
10539
|
+
if (await exists4("package.json") && (() => {
|
|
10540
|
+
const parsed = JSON.parse(readFileSync("package.json", "utf8"));
|
|
10541
|
+
return typeof parsed.scripts?.build === "string";
|
|
10542
|
+
})()) {
|
|
10543
|
+
return "npm run build";
|
|
10350
10544
|
}
|
|
10351
10545
|
if (await exists4("pom.xml")) {
|
|
10352
10546
|
if (process.platform === "win32") {
|
|
10353
|
-
if (existsSync("mvnw.cmd")) return
|
|
10354
|
-
return
|
|
10547
|
+
if (existsSync("mvnw.cmd")) return "mvnw.cmd compile -q";
|
|
10548
|
+
return "mvn.cmd compile -q";
|
|
10355
10549
|
}
|
|
10356
|
-
if (existsSync("mvnw")) return
|
|
10357
|
-
return
|
|
10550
|
+
if (existsSync("mvnw")) return "./mvnw compile -q";
|
|
10551
|
+
return "mvn compile -q";
|
|
10358
10552
|
}
|
|
10359
|
-
if (await exists4("Cargo.toml")) return
|
|
10360
|
-
return
|
|
10553
|
+
if (await exists4("Cargo.toml")) return "cargo build";
|
|
10554
|
+
return null;
|
|
10555
|
+
}
|
|
10556
|
+
function evidenceDetail(record) {
|
|
10557
|
+
return `Evidence: recorded command-check at ${record.timestamp}; command: ${record.command}; cwd: ${record.cwd}`;
|
|
10558
|
+
}
|
|
10559
|
+
function recoveryCommand(change, scope, command) {
|
|
10560
|
+
return `comet state record-check ${change} ${scope} --command "${command}" --exit-code 0`;
|
|
10361
10561
|
}
|
|
10362
|
-
async function
|
|
10363
|
-
if (process.env.COMET_SKIP_BUILD === "1")
|
|
10364
|
-
|
|
10365
|
-
return removedProjectCommandRun("verify_command");
|
|
10562
|
+
async function commandCheckPasses(changeDir, change, run, scope) {
|
|
10563
|
+
if (process.env.COMET_SKIP_BUILD === "1") {
|
|
10564
|
+
return { status: 0, output: "SKIPPED via COMET_SKIP_BUILD=1" };
|
|
10366
10565
|
}
|
|
10367
|
-
|
|
10566
|
+
const removedFields = scope === "build" ? ["build_command"] : ["verify_command", "build_command"];
|
|
10567
|
+
for (const removedField of removedFields) {
|
|
10568
|
+
if (await removedProjectCommandField(removedField)) {
|
|
10569
|
+
return removedProjectCommandRun(removedField);
|
|
10570
|
+
}
|
|
10571
|
+
}
|
|
10572
|
+
const inferred = scope === "build" ? await inferredBuildCommand() : null;
|
|
10573
|
+
if (inferred) return runInferred(inferred);
|
|
10574
|
+
const recorded = await latestCommandCheck(changeDir, run, scope);
|
|
10575
|
+
if (!recorded) {
|
|
10576
|
+
return {
|
|
10577
|
+
status: 1,
|
|
10578
|
+
output: scope === "build" ? `No inferred build command or recorded build check. Detection searched: ${INFERRED_COMMAND_SOURCES.join(", ")}.
|
|
10579
|
+
Next: run the required command, then record it with:
|
|
10580
|
+
${recoveryCommand(change, scope, "<command>")}` : `No recorded verify check.
|
|
10581
|
+
Next: run the required verification command, then record it with:
|
|
10582
|
+
${recoveryCommand(change, scope, "<command>")}`
|
|
10583
|
+
};
|
|
10584
|
+
}
|
|
10585
|
+
if (recorded.exitCode !== 0) {
|
|
10586
|
+
return {
|
|
10587
|
+
status: recorded.exitCode,
|
|
10588
|
+
output: `Latest recorded ${scope} check failed with exit code ${recorded.exitCode}.
|
|
10589
|
+
${evidenceDetail(recorded)}
|
|
10590
|
+
Next: rerun the command successfully, then record it with:
|
|
10591
|
+
${recoveryCommand(change, scope, recorded.command)}`
|
|
10592
|
+
};
|
|
10593
|
+
}
|
|
10594
|
+
return { status: 0, output: evidenceDetail(recorded) };
|
|
10368
10595
|
}
|
|
10369
10596
|
async function tasksAllDone(changeDir) {
|
|
10370
|
-
const tasks =
|
|
10597
|
+
const tasks = path16.join(changeDir, "tasks.md");
|
|
10371
10598
|
if (!await exists4(tasks)) {
|
|
10372
10599
|
return fail(
|
|
10373
10600
|
`tasks.md is missing at ${tasks}
|
|
10374
10601
|
Next: restore or create tasks.md for this change before leaving build.`
|
|
10375
10602
|
);
|
|
10376
10603
|
}
|
|
10377
|
-
const source = await
|
|
10604
|
+
const source = await fs15.readFile(tasks, "utf8");
|
|
10378
10605
|
if (!/- \[x\]/u.test(source)) {
|
|
10379
10606
|
return fail(
|
|
10380
10607
|
"tasks.md has no completed tasks.\nNext: complete implementation tasks and mark them with '- [x]'."
|
|
@@ -10391,9 +10618,9 @@ Next: complete or explicitly remove unfinished tasks, then mark tasks.md with '-
|
|
|
10391
10618
|
return pass();
|
|
10392
10619
|
}
|
|
10393
10620
|
async function tasksHasAny(changeDir) {
|
|
10394
|
-
const tasks =
|
|
10621
|
+
const tasks = path16.join(changeDir, "tasks.md");
|
|
10395
10622
|
if (!await exists4(tasks)) return false;
|
|
10396
|
-
return /- \[/u.test(await
|
|
10623
|
+
return /- \[/u.test(await fs15.readFile(tasks, "utf8"));
|
|
10397
10624
|
}
|
|
10398
10625
|
async function planTasksAllDone(changeDir) {
|
|
10399
10626
|
const plan = await readField(changeDir, "plan");
|
|
@@ -10404,7 +10631,7 @@ async function planTasksAllDone(changeDir) {
|
|
|
10404
10631
|
Next: restore the Superpowers plan file or update .comet.yaml plan before leaving build.`
|
|
10405
10632
|
);
|
|
10406
10633
|
}
|
|
10407
|
-
const source = await
|
|
10634
|
+
const source = await fs15.readFile(plan, "utf8");
|
|
10408
10635
|
const unfinished = source.split(/\r?\n/u).map((line, index) => ({ line, number: index + 1 })).filter((entry2) => /^\s*- \[ \]/u.test(entry2.line));
|
|
10409
10636
|
if (unfinished.length > 0) {
|
|
10410
10637
|
return fail(
|
|
@@ -10493,7 +10720,7 @@ async function archivedIsTrue(changeDir) {
|
|
|
10493
10720
|
return await readField(changeDir, "archived") === "true";
|
|
10494
10721
|
}
|
|
10495
10722
|
async function designDocFrontmatterHas(designDoc, field2, expected) {
|
|
10496
|
-
const source = (await
|
|
10723
|
+
const source = (await fs15.readFile(designDoc, "utf8")).replace(/^\uFEFF/u, "");
|
|
10497
10724
|
let inFrontmatter = false;
|
|
10498
10725
|
for (const line of source.split(/\r?\n/u)) {
|
|
10499
10726
|
if (!inFrontmatter) {
|
|
@@ -10558,7 +10785,7 @@ async function designHandoffMarkdownTraceable(changeDir) {
|
|
|
10558
10785
|
const markdown = `${context.replace(/\.json$/u, "")}.md`;
|
|
10559
10786
|
if (!await nonempty(markdown))
|
|
10560
10787
|
return fail(`design handoff markdown is missing or empty: ${markdown}`);
|
|
10561
|
-
const source = await
|
|
10788
|
+
const source = await fs15.readFile(markdown, "utf8");
|
|
10562
10789
|
const problems = [];
|
|
10563
10790
|
if (!/^Generated-by: comet-handoff\.sh$/mu.test(source)) {
|
|
10564
10791
|
problems.push("handoff markdown is missing Generated-by marker");
|
|
@@ -10585,7 +10812,7 @@ async function betaSpecJsonStructurallyValid(changeDir) {
|
|
|
10585
10812
|
const context = await readField(changeDir, "handoff_context");
|
|
10586
10813
|
if (!context || context === "null") return fail("handoff_context is missing from .comet.yaml");
|
|
10587
10814
|
if (!await nonempty(context)) return fail(`spec-context.json is missing or empty: ${context}`);
|
|
10588
|
-
const source = await
|
|
10815
|
+
const source = await fs15.readFile(context, "utf8");
|
|
10589
10816
|
const problems = [];
|
|
10590
10817
|
let parsed;
|
|
10591
10818
|
try {
|
|
@@ -10622,19 +10849,19 @@ async function guardOpenChecks(output, changeDir) {
|
|
|
10622
10849
|
const checks = [
|
|
10623
10850
|
check(
|
|
10624
10851
|
"proposal.md exists and non-empty",
|
|
10625
|
-
async () => await nonempty(
|
|
10852
|
+
async () => await nonempty(path16.join(changeDir, "proposal.md")) ? pass() : fail("")
|
|
10626
10853
|
),
|
|
10627
10854
|
check(
|
|
10628
10855
|
"proposal.md matches configured language",
|
|
10629
|
-
() => documentLanguageMatchesConfigured(changeDir,
|
|
10856
|
+
() => documentLanguageMatchesConfigured(changeDir, path16.join(changeDir, "proposal.md"))
|
|
10630
10857
|
),
|
|
10631
10858
|
check(
|
|
10632
10859
|
"tasks.md exists and non-empty",
|
|
10633
|
-
async () => await nonempty(
|
|
10860
|
+
async () => await nonempty(path16.join(changeDir, "tasks.md")) ? pass() : fail("")
|
|
10634
10861
|
),
|
|
10635
10862
|
check(
|
|
10636
10863
|
"tasks.md matches configured language",
|
|
10637
|
-
() => documentLanguageMatchesConfigured(changeDir,
|
|
10864
|
+
() => documentLanguageMatchesConfigured(changeDir, path16.join(changeDir, "tasks.md"))
|
|
10638
10865
|
),
|
|
10639
10866
|
check(
|
|
10640
10867
|
"tasks.md has at least one task",
|
|
@@ -10647,11 +10874,11 @@ async function guardOpenChecks(output, changeDir) {
|
|
|
10647
10874
|
0,
|
|
10648
10875
|
check(
|
|
10649
10876
|
"design.md exists and non-empty",
|
|
10650
|
-
async () => await nonempty(
|
|
10877
|
+
async () => await nonempty(path16.join(changeDir, "design.md")) ? pass() : fail("")
|
|
10651
10878
|
),
|
|
10652
10879
|
check(
|
|
10653
10880
|
"design.md matches configured language",
|
|
10654
|
-
() => documentLanguageMatchesConfigured(changeDir,
|
|
10881
|
+
() => documentLanguageMatchesConfigured(changeDir, path16.join(changeDir, "design.md"))
|
|
10655
10882
|
)
|
|
10656
10883
|
);
|
|
10657
10884
|
}
|
|
@@ -10663,27 +10890,27 @@ async function guardDesignChecks(output, changeDir, change) {
|
|
|
10663
10890
|
const builders = [
|
|
10664
10891
|
check(
|
|
10665
10892
|
"proposal.md exists",
|
|
10666
|
-
async () => await nonempty(
|
|
10893
|
+
async () => await nonempty(path16.join(changeDir, "proposal.md")) ? pass() : fail("")
|
|
10667
10894
|
),
|
|
10668
10895
|
check(
|
|
10669
10896
|
"proposal.md matches configured language",
|
|
10670
|
-
() => documentLanguageMatchesConfigured(changeDir,
|
|
10897
|
+
() => documentLanguageMatchesConfigured(changeDir, path16.join(changeDir, "proposal.md"))
|
|
10671
10898
|
),
|
|
10672
10899
|
check(
|
|
10673
10900
|
"design.md exists",
|
|
10674
|
-
async () => await nonempty(
|
|
10901
|
+
async () => await nonempty(path16.join(changeDir, "design.md")) ? pass() : fail("")
|
|
10675
10902
|
),
|
|
10676
10903
|
check(
|
|
10677
10904
|
"design.md matches configured language",
|
|
10678
|
-
() => documentLanguageMatchesConfigured(changeDir,
|
|
10905
|
+
() => documentLanguageMatchesConfigured(changeDir, path16.join(changeDir, "design.md"))
|
|
10679
10906
|
),
|
|
10680
10907
|
check(
|
|
10681
10908
|
"tasks.md exists",
|
|
10682
|
-
async () => await nonempty(
|
|
10909
|
+
async () => await nonempty(path16.join(changeDir, "tasks.md")) ? pass() : fail("")
|
|
10683
10910
|
),
|
|
10684
10911
|
check(
|
|
10685
10912
|
"tasks.md matches configured language",
|
|
10686
|
-
() => documentLanguageMatchesConfigured(changeDir,
|
|
10913
|
+
() => documentLanguageMatchesConfigured(changeDir, path16.join(changeDir, "tasks.md"))
|
|
10687
10914
|
),
|
|
10688
10915
|
check("design handoff context exists", () => designHandoffContextValid(changeDir, change)),
|
|
10689
10916
|
check("design handoff markdown is traceable", () => designHandoffMarkdownTraceable(changeDir))
|
|
@@ -10732,7 +10959,7 @@ async function guardDesignChecks(output, changeDir, change) {
|
|
|
10732
10959
|
}
|
|
10733
10960
|
return blocked2;
|
|
10734
10961
|
}
|
|
10735
|
-
async function guardBuildChecks(output, changeDir, change) {
|
|
10962
|
+
async function guardBuildChecks(output, changeDir, change, run) {
|
|
10736
10963
|
return runChecks(output, [
|
|
10737
10964
|
check("isolation selected", () => isolationSelected(changeDir, change)),
|
|
10738
10965
|
check("build_mode selected", () => buildModeSelected(changeDir, change)),
|
|
@@ -10744,11 +10971,11 @@ async function guardBuildChecks(output, changeDir, change) {
|
|
|
10744
10971
|
check("Superpowers plan all tasks checked", () => planTasksAllDone(changeDir)),
|
|
10745
10972
|
check(
|
|
10746
10973
|
"proposal.md exists",
|
|
10747
|
-
async () => await nonempty(
|
|
10974
|
+
async () => await nonempty(path16.join(changeDir, "proposal.md")) ? pass() : fail("")
|
|
10748
10975
|
),
|
|
10749
10976
|
check(
|
|
10750
10977
|
"proposal.md matches configured language",
|
|
10751
|
-
() => documentLanguageMatchesConfigured(changeDir,
|
|
10978
|
+
() => documentLanguageMatchesConfigured(changeDir, path16.join(changeDir, "proposal.md"))
|
|
10752
10979
|
),
|
|
10753
10980
|
check("Superpowers plan matches configured language", async () => {
|
|
10754
10981
|
const plan = await readField(changeDir, "plan");
|
|
@@ -10758,19 +10985,19 @@ async function guardBuildChecks(output, changeDir, change) {
|
|
|
10758
10985
|
// Build check runs last — only after all config checks pass — to avoid
|
|
10759
10986
|
// wasting time on a build that would be rejected by a config failure.
|
|
10760
10987
|
check("Build passes", async () => {
|
|
10761
|
-
const buildResult = await
|
|
10762
|
-
return buildResult.status === 0 ? pass() : fail(buildResult.output);
|
|
10988
|
+
const buildResult = await commandCheckPasses(changeDir, change, run, "build");
|
|
10989
|
+
return buildResult.status === 0 ? pass(buildResult.output) : fail(buildResult.output);
|
|
10763
10990
|
})
|
|
10764
10991
|
]);
|
|
10765
10992
|
}
|
|
10766
|
-
async function guardVerifyChecks(output, changeDir) {
|
|
10993
|
+
async function guardVerifyChecks(output, changeDir, change, run) {
|
|
10767
10994
|
return runChecks(output, [
|
|
10768
10995
|
check("tasks.md all tasks checked", () => tasksAllDone(changeDir)),
|
|
10769
10996
|
// Verification command runs after tasks check — no point running tests
|
|
10770
10997
|
// if tasks.md is incomplete.
|
|
10771
10998
|
check("Verification passes", async () => {
|
|
10772
|
-
const verifyResult = await
|
|
10773
|
-
return verifyResult.status === 0 ? pass() : fail(verifyResult.output);
|
|
10999
|
+
const verifyResult = await commandCheckPasses(changeDir, change, run, "verify");
|
|
11000
|
+
return verifyResult.status === 0 ? pass(verifyResult.output) : fail(verifyResult.output);
|
|
10774
11001
|
}),
|
|
10775
11002
|
check(
|
|
10776
11003
|
"verification_report exists",
|
|
@@ -10792,11 +11019,11 @@ async function guardArchiveChecks(output, changeDir) {
|
|
|
10792
11019
|
check("archived is true", async () => await archivedIsTrue(changeDir) ? pass() : fail("")),
|
|
10793
11020
|
check(
|
|
10794
11021
|
"proposal.md exists",
|
|
10795
|
-
async () => await nonempty(
|
|
11022
|
+
async () => await nonempty(path16.join(changeDir, "proposal.md")) ? pass() : fail("")
|
|
10796
11023
|
),
|
|
10797
11024
|
check(
|
|
10798
11025
|
"design.md exists",
|
|
10799
|
-
async () => await nonempty(
|
|
11026
|
+
async () => await nonempty(path16.join(changeDir, "design.md")) ? pass() : fail("")
|
|
10800
11027
|
),
|
|
10801
11028
|
check("tasks.md all tasks checked", () => tasksAllDone(changeDir))
|
|
10802
11029
|
]);
|
|
@@ -10804,8 +11031,8 @@ async function guardArchiveChecks(output, changeDir) {
|
|
|
10804
11031
|
async function applyStateUpdate(output, change, changeDir, phase, context) {
|
|
10805
11032
|
const event = CLASSIC_GUARD_TRANSITION_EVENT[phase];
|
|
10806
11033
|
if (!event) return;
|
|
10807
|
-
const
|
|
10808
|
-
await transitionClassicRuntimeRun(changeDir,
|
|
11034
|
+
const result5 = applyClassicTransition(context.classic, event);
|
|
11035
|
+
await transitionClassicRuntimeRun(changeDir, result5.classic, context.run, {
|
|
10809
11036
|
event,
|
|
10810
11037
|
phase,
|
|
10811
11038
|
source: "comet-guard"
|
|
@@ -10815,15 +11042,15 @@ async function applyStateUpdate(output, change, changeDir, phase, context) {
|
|
|
10815
11042
|
event,
|
|
10816
11043
|
source: "comet-guard",
|
|
10817
11044
|
from: context.classic,
|
|
10818
|
-
to:
|
|
10819
|
-
effects:
|
|
11045
|
+
to: result5.classic,
|
|
11046
|
+
effects: result5.effects
|
|
10820
11047
|
});
|
|
10821
|
-
for (const effect of
|
|
11048
|
+
for (const effect of result5.effects) {
|
|
10822
11049
|
output.stderr.push(green2(`[SET] ${wireField(effect.field)}=${wireValue(effect.to)}`));
|
|
10823
11050
|
}
|
|
10824
11051
|
output.stderr.push(green2(`[TRANSITION] ${event}`));
|
|
10825
11052
|
const template = APPLY_MESSAGE[phase];
|
|
10826
|
-
const message = phase === "open" ? template.replace("PLACEHOLDER",
|
|
11053
|
+
const message = phase === "open" ? template.replace("PLACEHOLDER", result5.classic.phase) : template;
|
|
10827
11054
|
output.stderr.push(green2(message));
|
|
10828
11055
|
}
|
|
10829
11056
|
var classicGuardCommand = async (args, options) => {
|
|
@@ -10853,8 +11080,10 @@ Valid phases: open, design, build, verify, archive`
|
|
|
10853
11080
|
let blocked2;
|
|
10854
11081
|
if (phase === "open") blocked2 = await guardOpenChecks(output, changeDir);
|
|
10855
11082
|
else if (phase === "design") blocked2 = await guardDesignChecks(output, changeDir, change);
|
|
10856
|
-
else if (phase === "build")
|
|
10857
|
-
|
|
11083
|
+
else if (phase === "build")
|
|
11084
|
+
blocked2 = await guardBuildChecks(output, changeDir, change, runContext.run);
|
|
11085
|
+
else if (phase === "verify")
|
|
11086
|
+
blocked2 = await guardVerifyChecks(output, changeDir, change, runContext.run);
|
|
10858
11087
|
else blocked2 = await guardArchiveChecks(output, changeDir);
|
|
10859
11088
|
if (blocked2) {
|
|
10860
11089
|
output.stderr.push("");
|
|
@@ -10877,10 +11106,10 @@ Valid phases: open, design, build, verify, archive`
|
|
|
10877
11106
|
};
|
|
10878
11107
|
|
|
10879
11108
|
// domains/comet-classic/classic-handoff.ts
|
|
10880
|
-
var
|
|
11109
|
+
var import_yaml6 = __toESM(require_dist(), 1);
|
|
10881
11110
|
import { createHash as createHash5 } from "crypto";
|
|
10882
|
-
import { promises as
|
|
10883
|
-
import
|
|
11111
|
+
import { promises as fs16, readFileSync as readFileSync2 } from "fs";
|
|
11112
|
+
import path17 from "path";
|
|
10884
11113
|
var GREEN4 = "\x1B[32m";
|
|
10885
11114
|
var RED4 = "\x1B[31m";
|
|
10886
11115
|
var YELLOW4 = "\x1B[33m";
|
|
@@ -10914,7 +11143,7 @@ var HandoffOutput = class {
|
|
|
10914
11143
|
};
|
|
10915
11144
|
async function exists5(file) {
|
|
10916
11145
|
try {
|
|
10917
|
-
await
|
|
11146
|
+
await fs16.access(file);
|
|
10918
11147
|
return true;
|
|
10919
11148
|
} catch (error) {
|
|
10920
11149
|
if (error.code === "ENOENT") return false;
|
|
@@ -10923,7 +11152,7 @@ async function exists5(file) {
|
|
|
10923
11152
|
}
|
|
10924
11153
|
async function nonempty2(file) {
|
|
10925
11154
|
try {
|
|
10926
|
-
return (await
|
|
11155
|
+
return (await fs16.stat(file)).size > 0;
|
|
10927
11156
|
} catch (error) {
|
|
10928
11157
|
if (error.code === "ENOENT") return false;
|
|
10929
11158
|
throw error;
|
|
@@ -10952,7 +11181,7 @@ async function handoffSourceFiles2(changeDir) {
|
|
|
10952
11181
|
const files = [`${changeDir}/proposal.md`, `${changeDir}/design.md`, `${changeDir}/tasks.md`];
|
|
10953
11182
|
const specs = `${changeDir}/specs`;
|
|
10954
11183
|
if (await exists5(specs)) {
|
|
10955
|
-
for (const entry2 of (await
|
|
11184
|
+
for (const entry2 of (await fs16.readdir(specs)).sort()) {
|
|
10956
11185
|
const spec = `${specs}/${entry2}/spec.md`;
|
|
10957
11186
|
if (await exists5(spec)) files.push(spec);
|
|
10958
11187
|
}
|
|
@@ -11000,7 +11229,7 @@ async function writeMarkdownContext(changeDir, change, mode, contextHash, output
|
|
|
11000
11229
|
];
|
|
11001
11230
|
for (const file of await handoffSourceFiles2(changeDir)) {
|
|
11002
11231
|
if (!await exists5(file)) continue;
|
|
11003
|
-
const content = await
|
|
11232
|
+
const content = await fs16.readFile(file, "utf8");
|
|
11004
11233
|
const total = lineCount(content);
|
|
11005
11234
|
lines.push(
|
|
11006
11235
|
`## ${file}`,
|
|
@@ -11025,7 +11254,7 @@ async function writeMarkdownContext(changeDir, change, mode, contextHash, output
|
|
|
11025
11254
|
}
|
|
11026
11255
|
lines.push("");
|
|
11027
11256
|
}
|
|
11028
|
-
await
|
|
11257
|
+
await fs16.writeFile(output, lines.join("\n"));
|
|
11029
11258
|
}
|
|
11030
11259
|
async function writeJsonContext(changeDir, change, mode, contextHash, output) {
|
|
11031
11260
|
const entries = [];
|
|
@@ -11048,7 +11277,7 @@ async function writeJsonContext(changeDir, change, mode, contextHash, output) {
|
|
|
11048
11277
|
"}",
|
|
11049
11278
|
""
|
|
11050
11279
|
].join("\n");
|
|
11051
|
-
await
|
|
11280
|
+
await fs16.writeFile(output, document);
|
|
11052
11281
|
}
|
|
11053
11282
|
async function writeSpecProjectionForFile(file, content) {
|
|
11054
11283
|
return [
|
|
@@ -11088,11 +11317,11 @@ async function writeSpecMarkdownContext(changeDir, change, contextHash, output)
|
|
|
11088
11317
|
const specs = `${changeDir}/specs`;
|
|
11089
11318
|
let projected = false;
|
|
11090
11319
|
if (await exists5(specs)) {
|
|
11091
|
-
for (const entry2 of (await
|
|
11320
|
+
for (const entry2 of (await fs16.readdir(specs)).sort()) {
|
|
11092
11321
|
const spec = `${specs}/${entry2}/spec.md`;
|
|
11093
11322
|
if (!await exists5(spec)) continue;
|
|
11094
11323
|
projected = true;
|
|
11095
|
-
lines.push(...await writeSpecProjectionForFile(spec, await
|
|
11324
|
+
lines.push(...await writeSpecProjectionForFile(spec, await fs16.readFile(spec, "utf8")));
|
|
11096
11325
|
}
|
|
11097
11326
|
}
|
|
11098
11327
|
if (!projected) {
|
|
@@ -11101,7 +11330,7 @@ async function writeSpecMarkdownContext(changeDir, change, contextHash, output)
|
|
|
11101
11330
|
lines.push(
|
|
11102
11331
|
"Full source files remain canonical. If a required heading or scenario is missing here, regenerate the handoff or read the source spec directly. Supporting files (proposal, design, tasks) are referenced by hash only."
|
|
11103
11332
|
);
|
|
11104
|
-
await
|
|
11333
|
+
await fs16.writeFile(output, lines.join("\n"));
|
|
11105
11334
|
}
|
|
11106
11335
|
async function writeSpecJsonContext(changeDir, change, contextHash, output) {
|
|
11107
11336
|
const entries = [];
|
|
@@ -11110,7 +11339,7 @@ async function writeSpecJsonContext(changeDir, change, contextHash, output) {
|
|
|
11110
11339
|
const role = /\/specs\/[^/]+\/spec\.md$/u.test(file) ? "spec" : "supporting";
|
|
11111
11340
|
entries.push({ path: file, sha256: hashFile2(file), role });
|
|
11112
11341
|
}
|
|
11113
|
-
await
|
|
11342
|
+
await fs16.writeFile(
|
|
11114
11343
|
output,
|
|
11115
11344
|
`${JSON.stringify(
|
|
11116
11345
|
{
|
|
@@ -11129,8 +11358,8 @@ async function writeSpecJsonContext(changeDir, change, contextHash, output) {
|
|
|
11129
11358
|
);
|
|
11130
11359
|
}
|
|
11131
11360
|
async function readField2(changeDir, field2) {
|
|
11132
|
-
const file =
|
|
11133
|
-
const document = (0,
|
|
11361
|
+
const file = path17.join(changeDir, ".comet.yaml");
|
|
11362
|
+
const document = (0, import_yaml6.parseDocument)(await fs16.readFile(file, "utf8"), { uniqueKeys: false });
|
|
11134
11363
|
if (document.errors.length > 0) {
|
|
11135
11364
|
throw new HandoffFailure(`ERROR: Invalid .comet.yaml: ${document.errors[0].message}`);
|
|
11136
11365
|
}
|
|
@@ -11164,7 +11393,7 @@ async function completedHandoffIsCurrent(changeDir, run, contextHash, contextJso
|
|
|
11164
11393
|
readCheckpoint(changeDir, run.checkpointRef)
|
|
11165
11394
|
]);
|
|
11166
11395
|
if (!await exists5(contextJson) || !await exists5(contextMd)) return false;
|
|
11167
|
-
if (context !== await
|
|
11396
|
+
if (context !== await fs16.readFile(contextMd, "utf8")) return false;
|
|
11168
11397
|
if (artifacts.handoff_context !== contextJson || artifacts.handoff_markdown !== contextMd) {
|
|
11169
11398
|
return false;
|
|
11170
11399
|
}
|
|
@@ -11287,7 +11516,7 @@ var classicHandoffCommand = async (args) => {
|
|
|
11287
11516
|
run: pendingRun,
|
|
11288
11517
|
unknownKeys: (await readClassicState(changeDir)).unknownKeys
|
|
11289
11518
|
});
|
|
11290
|
-
await
|
|
11519
|
+
await fs16.mkdir(handoffDir, { recursive: true });
|
|
11291
11520
|
if (handoffMode === "beta") {
|
|
11292
11521
|
await writeSpecMarkdownContext(changeDir, change, contextHash, contextMd);
|
|
11293
11522
|
await writeSpecJsonContext(changeDir, change, contextHash, contextJson);
|
|
@@ -11295,7 +11524,7 @@ var classicHandoffCommand = async (args) => {
|
|
|
11295
11524
|
await writeMarkdownContext(changeDir, change, handoffMode, contextHash, contextMd);
|
|
11296
11525
|
await writeJsonContext(changeDir, change, handoffMode, contextHash, contextJson);
|
|
11297
11526
|
}
|
|
11298
|
-
const context = await
|
|
11527
|
+
const context = await fs16.readFile(contextMd, "utf8");
|
|
11299
11528
|
await writeContext(changeDir, pendingRun.contextRef, context);
|
|
11300
11529
|
const artifacts = {
|
|
11301
11530
|
...await readArtifacts(changeDir, pendingRun.artifactsRef),
|
|
@@ -11352,8 +11581,140 @@ var classicHandoffCommand = async (args) => {
|
|
|
11352
11581
|
};
|
|
11353
11582
|
|
|
11354
11583
|
// domains/comet-classic/classic-hook-guard.ts
|
|
11355
|
-
import { existsSync as existsSync2, promises as
|
|
11356
|
-
import
|
|
11584
|
+
import { existsSync as existsSync2, promises as fs18, readFileSync as readFileSync3 } from "fs";
|
|
11585
|
+
import path19 from "path";
|
|
11586
|
+
|
|
11587
|
+
// domains/comet-classic/classic-current-change.ts
|
|
11588
|
+
import { execFileSync } from "child_process";
|
|
11589
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
11590
|
+
import { promises as fs17 } from "fs";
|
|
11591
|
+
import path18 from "path";
|
|
11592
|
+
function currentChangeFile(projectRoot2) {
|
|
11593
|
+
return path18.join(projectRoot2, ".comet", "current-change.json");
|
|
11594
|
+
}
|
|
11595
|
+
function currentBranch(projectRoot2) {
|
|
11596
|
+
try {
|
|
11597
|
+
const branch = execFileSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
11598
|
+
cwd: projectRoot2,
|
|
11599
|
+
encoding: "utf8",
|
|
11600
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
11601
|
+
}).trim();
|
|
11602
|
+
return branch && branch !== "HEAD" ? branch : null;
|
|
11603
|
+
} catch {
|
|
11604
|
+
return null;
|
|
11605
|
+
}
|
|
11606
|
+
}
|
|
11607
|
+
function changeDirectory(projectRoot2, changeName) {
|
|
11608
|
+
return path18.join(projectRoot2, "openspec", "changes", changeName);
|
|
11609
|
+
}
|
|
11610
|
+
async function validateActiveChange(projectRoot2, changeName) {
|
|
11611
|
+
assertOpenSpecChangeName(changeName);
|
|
11612
|
+
const changeDir = changeDirectory(projectRoot2, changeName);
|
|
11613
|
+
try {
|
|
11614
|
+
await fs17.access(path18.join(changeDir, ".comet.yaml"));
|
|
11615
|
+
} catch (error) {
|
|
11616
|
+
if (error.code === "ENOENT") {
|
|
11617
|
+
throw new Error(
|
|
11618
|
+
`Cannot select current change '${changeName}': active change state not found`,
|
|
11619
|
+
{
|
|
11620
|
+
cause: error
|
|
11621
|
+
}
|
|
11622
|
+
);
|
|
11623
|
+
}
|
|
11624
|
+
throw error;
|
|
11625
|
+
}
|
|
11626
|
+
const projection = await readClassicState(changeDir, { migrate: false });
|
|
11627
|
+
if (!projection.classic) {
|
|
11628
|
+
throw new Error(`Cannot select current change '${changeName}': Classic state is incomplete`);
|
|
11629
|
+
}
|
|
11630
|
+
if (projection.classic.archived) {
|
|
11631
|
+
throw new Error(`Cannot select current change '${changeName}': change is archived`);
|
|
11632
|
+
}
|
|
11633
|
+
}
|
|
11634
|
+
function parseSelection(source) {
|
|
11635
|
+
let value;
|
|
11636
|
+
try {
|
|
11637
|
+
value = JSON.parse(source);
|
|
11638
|
+
} catch (error) {
|
|
11639
|
+
throw new Error(
|
|
11640
|
+
`current change selection contains invalid JSON: ${error instanceof Error ? error.message : String(error)}`,
|
|
11641
|
+
{ cause: error }
|
|
11642
|
+
);
|
|
11643
|
+
}
|
|
11644
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
11645
|
+
throw new Error("current change selection must be a JSON object");
|
|
11646
|
+
}
|
|
11647
|
+
const record = value;
|
|
11648
|
+
if (record.version !== 1) {
|
|
11649
|
+
throw new Error("current change selection version must be 1");
|
|
11650
|
+
}
|
|
11651
|
+
if (typeof record.change !== "string") {
|
|
11652
|
+
throw new Error("current change selection change must be a string");
|
|
11653
|
+
}
|
|
11654
|
+
assertOpenSpecChangeName(record.change);
|
|
11655
|
+
if (record.branch !== null && typeof record.branch !== "string") {
|
|
11656
|
+
throw new Error("current change selection branch must be a string or null");
|
|
11657
|
+
}
|
|
11658
|
+
return {
|
|
11659
|
+
version: 1,
|
|
11660
|
+
change: record.change,
|
|
11661
|
+
branch: record.branch
|
|
11662
|
+
};
|
|
11663
|
+
}
|
|
11664
|
+
async function selectCurrentChange(projectRoot2, changeName) {
|
|
11665
|
+
await validateActiveChange(projectRoot2, changeName);
|
|
11666
|
+
const selection = {
|
|
11667
|
+
version: 1,
|
|
11668
|
+
change: changeName,
|
|
11669
|
+
branch: currentBranch(projectRoot2)
|
|
11670
|
+
};
|
|
11671
|
+
const file = currentChangeFile(projectRoot2);
|
|
11672
|
+
const temporary = `${file}.${randomUUID6()}.tmp`;
|
|
11673
|
+
await fs17.mkdir(path18.dirname(file), { recursive: true });
|
|
11674
|
+
try {
|
|
11675
|
+
await fs17.writeFile(temporary, JSON.stringify(selection, null, 2) + "\n", "utf8");
|
|
11676
|
+
await fs17.rename(temporary, file);
|
|
11677
|
+
} catch (error) {
|
|
11678
|
+
await fs17.rm(temporary, { force: true });
|
|
11679
|
+
throw error;
|
|
11680
|
+
}
|
|
11681
|
+
return selection;
|
|
11682
|
+
}
|
|
11683
|
+
async function resolveCurrentChange(projectRoot2) {
|
|
11684
|
+
let source;
|
|
11685
|
+
try {
|
|
11686
|
+
source = await fs17.readFile(currentChangeFile(projectRoot2), "utf8");
|
|
11687
|
+
} catch (error) {
|
|
11688
|
+
if (error.code === "ENOENT") return { status: "missing" };
|
|
11689
|
+
return {
|
|
11690
|
+
status: "stale",
|
|
11691
|
+
reason: `cannot read current change selection: ${error instanceof Error ? error.message : String(error)}`
|
|
11692
|
+
};
|
|
11693
|
+
}
|
|
11694
|
+
let selection;
|
|
11695
|
+
try {
|
|
11696
|
+
selection = parseSelection(source);
|
|
11697
|
+
await validateActiveChange(projectRoot2, selection.change);
|
|
11698
|
+
} catch (error) {
|
|
11699
|
+
return {
|
|
11700
|
+
status: "stale",
|
|
11701
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
11702
|
+
};
|
|
11703
|
+
}
|
|
11704
|
+
const branch = currentBranch(projectRoot2);
|
|
11705
|
+
if (selection.branch !== null && branch !== selection.branch) {
|
|
11706
|
+
return {
|
|
11707
|
+
status: "stale",
|
|
11708
|
+
reason: `current change '${selection.change}' was selected on branch '${selection.branch}', current branch is '${branch ?? "detached HEAD"}'`
|
|
11709
|
+
};
|
|
11710
|
+
}
|
|
11711
|
+
return { status: "selected", selection };
|
|
11712
|
+
}
|
|
11713
|
+
async function clearCurrentChange(projectRoot2) {
|
|
11714
|
+
await fs17.rm(currentChangeFile(projectRoot2), { force: true });
|
|
11715
|
+
}
|
|
11716
|
+
|
|
11717
|
+
// domains/comet-classic/classic-hook-guard.ts
|
|
11357
11718
|
function result(exitCode, message) {
|
|
11358
11719
|
return { exitCode, stderr: message + "\n" };
|
|
11359
11720
|
}
|
|
@@ -11378,52 +11739,52 @@ function normalized(value) {
|
|
|
11378
11739
|
function parseProjectRoot(args) {
|
|
11379
11740
|
const index = args.indexOf("--project-root");
|
|
11380
11741
|
const value = index >= 0 ? args[index + 1] : void 0;
|
|
11381
|
-
return
|
|
11742
|
+
return path19.resolve(value && !value.startsWith("--") ? value : process.cwd());
|
|
11382
11743
|
}
|
|
11383
|
-
function relativeToProjectRoot(target,
|
|
11384
|
-
const relative = normalized(
|
|
11744
|
+
function relativeToProjectRoot(target, projectRoot2) {
|
|
11745
|
+
const relative = normalized(path19.relative(projectRoot2, target));
|
|
11385
11746
|
if (relative === "") return "";
|
|
11386
|
-
if (relative.startsWith("../") || relative === ".." ||
|
|
11747
|
+
if (relative.startsWith("../") || relative === ".." || path19.isAbsolute(relative)) return null;
|
|
11387
11748
|
return relative;
|
|
11388
11749
|
}
|
|
11389
11750
|
async function physicalPathForPossiblyMissingTarget(target) {
|
|
11390
|
-
const resolved =
|
|
11391
|
-
const root =
|
|
11751
|
+
const resolved = path19.resolve(target);
|
|
11752
|
+
const root = path19.parse(resolved).root;
|
|
11392
11753
|
const missingSegments = [];
|
|
11393
11754
|
let cursor = resolved;
|
|
11394
11755
|
while (cursor && cursor !== root) {
|
|
11395
11756
|
try {
|
|
11396
|
-
const physicalBase = await
|
|
11397
|
-
return
|
|
11757
|
+
const physicalBase = await fs18.realpath(cursor);
|
|
11758
|
+
return path19.join(physicalBase, ...missingSegments.reverse());
|
|
11398
11759
|
} catch (error) {
|
|
11399
11760
|
const code = error.code;
|
|
11400
11761
|
if (code !== "ENOENT" && code !== "ENOTDIR") throw error;
|
|
11401
|
-
missingSegments.push(
|
|
11402
|
-
cursor =
|
|
11762
|
+
missingSegments.push(path19.basename(cursor));
|
|
11763
|
+
cursor = path19.dirname(cursor);
|
|
11403
11764
|
}
|
|
11404
11765
|
}
|
|
11405
11766
|
try {
|
|
11406
|
-
const physicalRoot = await
|
|
11407
|
-
return
|
|
11767
|
+
const physicalRoot = await fs18.realpath(root);
|
|
11768
|
+
return path19.join(physicalRoot, ...missingSegments.reverse());
|
|
11408
11769
|
} catch {
|
|
11409
11770
|
return null;
|
|
11410
11771
|
}
|
|
11411
11772
|
}
|
|
11412
|
-
async function projectRelative(target,
|
|
11413
|
-
const rawCandidate =
|
|
11773
|
+
async function projectRelative(target, projectRoot2) {
|
|
11774
|
+
const rawCandidate = path19.isAbsolute(target) ? target : path19.resolve(process.cwd(), target);
|
|
11414
11775
|
let candidate = normalized(rawCandidate);
|
|
11415
|
-
const rootRelative = relativeToProjectRoot(rawCandidate,
|
|
11776
|
+
const rootRelative = relativeToProjectRoot(rawCandidate, projectRoot2);
|
|
11416
11777
|
if (rootRelative !== null) return rootRelative;
|
|
11417
11778
|
try {
|
|
11418
11779
|
const physicalCandidate = await physicalPathForPossiblyMissingTarget(rawCandidate);
|
|
11419
|
-
const physicalRoot = await
|
|
11780
|
+
const physicalRoot = await fs18.realpath(projectRoot2);
|
|
11420
11781
|
if (physicalCandidate) {
|
|
11421
11782
|
const physicalRootRelative = relativeToProjectRoot(physicalCandidate, physicalRoot);
|
|
11422
11783
|
if (physicalRootRelative !== null) return physicalRootRelative;
|
|
11423
11784
|
candidate = normalized(physicalCandidate);
|
|
11424
11785
|
}
|
|
11425
11786
|
} catch {
|
|
11426
|
-
if (!
|
|
11787
|
+
if (!path19.isAbsolute(target)) return normalized(target).replace(/^\.\//u, "");
|
|
11427
11788
|
}
|
|
11428
11789
|
return candidate.replace(/^\.\//u, "");
|
|
11429
11790
|
}
|
|
@@ -11447,28 +11808,22 @@ async function loadGoverningChange(changeDir) {
|
|
|
11447
11808
|
};
|
|
11448
11809
|
}
|
|
11449
11810
|
}
|
|
11450
|
-
async function activeChanges(
|
|
11451
|
-
const changesDir =
|
|
11811
|
+
async function activeChanges(projectRoot2) {
|
|
11812
|
+
const changesDir = path19.join(projectRoot2, "openspec", "changes");
|
|
11452
11813
|
const governingChanges = [];
|
|
11453
11814
|
if (!existsSync2(changesDir)) return governingChanges;
|
|
11454
|
-
for (const entry2 of (await
|
|
11815
|
+
for (const entry2 of (await fs18.readdir(changesDir, { withFileTypes: true })).sort(
|
|
11455
11816
|
(left, right) => left.name.localeCompare(right.name)
|
|
11456
11817
|
)) {
|
|
11457
11818
|
if (!entry2.isDirectory() || entry2.name === "archive") continue;
|
|
11458
|
-
const changeDir =
|
|
11459
|
-
if (!existsSync2(
|
|
11819
|
+
const changeDir = path19.join(changesDir, entry2.name);
|
|
11820
|
+
if (!existsSync2(path19.join(changeDir, ".comet.yaml"))) continue;
|
|
11460
11821
|
const governing = await loadGoverningChange(changeDir);
|
|
11461
11822
|
if (!governing || governing.archived) continue;
|
|
11462
11823
|
governingChanges.push(governing);
|
|
11463
11824
|
}
|
|
11464
11825
|
return governingChanges;
|
|
11465
11826
|
}
|
|
11466
|
-
function blocksSourceWrites(governing) {
|
|
11467
|
-
if (governing.phase === "open" || governing.phase === "design" || governing.phase === "archive") {
|
|
11468
|
-
return true;
|
|
11469
|
-
}
|
|
11470
|
-
return governing.phase === "build" && governing.classic?.workflow === "full" && !governing.classic.designDoc;
|
|
11471
|
-
}
|
|
11472
11827
|
function isSuperpowersArtifactPath(relativePath2) {
|
|
11473
11828
|
return relativePath2.startsWith("docs/superpowers/");
|
|
11474
11829
|
}
|
|
@@ -11476,7 +11831,7 @@ function allowsSuperpowersArtifacts(governing) {
|
|
|
11476
11831
|
return governing.phase === "design" || governing.phase === "build" || governing.phase === "verify";
|
|
11477
11832
|
}
|
|
11478
11833
|
function governingChangeName(governing) {
|
|
11479
|
-
return governing.changeDir ?
|
|
11834
|
+
return governing.changeDir ? path19.basename(governing.changeDir) : null;
|
|
11480
11835
|
}
|
|
11481
11836
|
var SUPERPOWERS_ARTIFACT_SUFFIXES = /* @__PURE__ */ new Set([
|
|
11482
11837
|
"design",
|
|
@@ -11507,8 +11862,8 @@ function matchesSuperpowersArtifactName(relativePath2, changeName) {
|
|
|
11507
11862
|
const pattern = new RegExp(`(^|[-_.])${escapeRegex(changeName)}[-_.](${suffixes})$`, "u");
|
|
11508
11863
|
return pattern.test(stem);
|
|
11509
11864
|
}
|
|
11510
|
-
async function superpowersArtifactGoverningChange(relativePath2,
|
|
11511
|
-
const active = await activeChanges(
|
|
11865
|
+
async function superpowersArtifactGoverningChange(relativePath2, projectRoot2) {
|
|
11866
|
+
const active = await activeChanges(projectRoot2);
|
|
11512
11867
|
const recorded = active.find(
|
|
11513
11868
|
(governing) => matchesRecordedSuperpowersArtifact(relativePath2, governing)
|
|
11514
11869
|
);
|
|
@@ -11523,18 +11878,41 @@ async function superpowersArtifactGoverningChange(relativePath2, projectRoot) {
|
|
|
11523
11878
|
if (named) return named;
|
|
11524
11879
|
return null;
|
|
11525
11880
|
}
|
|
11526
|
-
async function repoSourceGoverningChange(
|
|
11527
|
-
const active = await activeChanges(
|
|
11528
|
-
|
|
11881
|
+
async function repoSourceGoverningChange(projectRoot2, relativePath2) {
|
|
11882
|
+
const active = await activeChanges(projectRoot2);
|
|
11883
|
+
if (active.length === 0) return null;
|
|
11884
|
+
const current = await resolveCurrentChange(projectRoot2);
|
|
11885
|
+
if (current.status === "stale") {
|
|
11886
|
+
return { blockedResult: blockedStaleSelection(relativePath2, current.reason) };
|
|
11887
|
+
}
|
|
11888
|
+
if (current.status === "selected") {
|
|
11889
|
+
const selected = active.find(
|
|
11890
|
+
(governing) => governingChangeName(governing) === current.selection.change
|
|
11891
|
+
);
|
|
11892
|
+
if (selected) return selected;
|
|
11893
|
+
return {
|
|
11894
|
+
blockedResult: blockedStaleSelection(
|
|
11895
|
+
relativePath2,
|
|
11896
|
+
`selected change '${current.selection.change}' is no longer active`
|
|
11897
|
+
)
|
|
11898
|
+
};
|
|
11899
|
+
}
|
|
11900
|
+
if (active.length === 1) return active[0];
|
|
11901
|
+
return {
|
|
11902
|
+
blockedResult: blockedMultipleChanges(
|
|
11903
|
+
relativePath2,
|
|
11904
|
+
active.map((governing) => governingChangeName(governing)).filter(Boolean)
|
|
11905
|
+
)
|
|
11906
|
+
};
|
|
11529
11907
|
}
|
|
11530
|
-
async function governingChange(relativePath2,
|
|
11908
|
+
async function governingChange(relativePath2, projectRoot2) {
|
|
11531
11909
|
const prefix = "openspec/changes/";
|
|
11532
11910
|
if (relativePath2.startsWith(prefix)) {
|
|
11533
11911
|
const rest = relativePath2.slice(prefix.length);
|
|
11534
11912
|
const [name] = rest.split("/");
|
|
11535
11913
|
if (name && name !== "archive") {
|
|
11536
|
-
const changeDir =
|
|
11537
|
-
const stateFile2 =
|
|
11914
|
+
const changeDir = path19.join(projectRoot2, "openspec", "changes", name);
|
|
11915
|
+
const stateFile2 = path19.join(changeDir, ".comet.yaml");
|
|
11538
11916
|
if (existsSync2(stateFile2)) {
|
|
11539
11917
|
const governing = await loadGoverningChange(changeDir);
|
|
11540
11918
|
if (governing) return governing;
|
|
@@ -11544,12 +11922,12 @@ async function governingChange(relativePath2, projectRoot) {
|
|
|
11544
11922
|
}
|
|
11545
11923
|
}
|
|
11546
11924
|
if (isSuperpowersArtifactPath(relativePath2)) {
|
|
11547
|
-
const superpowers = await superpowersArtifactGoverningChange(relativePath2,
|
|
11925
|
+
const superpowers = await superpowersArtifactGoverningChange(relativePath2, projectRoot2);
|
|
11548
11926
|
if (superpowers) return { ...superpowers, superpowersArtifact: "matched" };
|
|
11549
|
-
const fallback = await
|
|
11927
|
+
const fallback = (await activeChanges(projectRoot2))[0] ?? null;
|
|
11550
11928
|
return fallback ? { ...fallback, superpowersArtifact: "unmatched" } : null;
|
|
11551
11929
|
}
|
|
11552
|
-
return repoSourceGoverningChange(
|
|
11930
|
+
return repoSourceGoverningChange(projectRoot2, relativePath2);
|
|
11553
11931
|
}
|
|
11554
11932
|
function isRootMarkdown(relativePath2) {
|
|
11555
11933
|
return !relativePath2.includes("/") && relativePath2.endsWith(".md");
|
|
@@ -11653,22 +12031,47 @@ function blockedUnmatchedSuperpowersArtifact(relativePath2, phase) {
|
|
|
11653
12031
|
].join("\n")
|
|
11654
12032
|
);
|
|
11655
12033
|
}
|
|
12034
|
+
function blockedMultipleChanges(relativePath2, changeNames) {
|
|
12035
|
+
return result(
|
|
12036
|
+
2,
|
|
12037
|
+
[
|
|
12038
|
+
"",
|
|
12039
|
+
"╔══════════════════════════════════════════╗",
|
|
12040
|
+
"║ COMET PHASE GUARD — WRITE BLOCKED ║",
|
|
12041
|
+
"╚══════════════════════════════════════════╝",
|
|
12042
|
+
"",
|
|
12043
|
+
" BLOCKED: multiple active changes require a current change",
|
|
12044
|
+
` Target file: ${relativePath2}`,
|
|
12045
|
+
` Active changes: ${changeNames.join(", ")}`,
|
|
12046
|
+
"",
|
|
12047
|
+
" NEXT: run comet state select <change-name>, then retry the source write",
|
|
12048
|
+
""
|
|
12049
|
+
].join("\n")
|
|
12050
|
+
);
|
|
12051
|
+
}
|
|
12052
|
+
function blockedStaleSelection(relativePath2, reason) {
|
|
12053
|
+
return result(
|
|
12054
|
+
2,
|
|
12055
|
+
[
|
|
12056
|
+
"",
|
|
12057
|
+
"╔══════════════════════════════════════════╗",
|
|
12058
|
+
"║ COMET PHASE GUARD — WRITE BLOCKED ║",
|
|
12059
|
+
"╚══════════════════════════════════════════╝",
|
|
12060
|
+
"",
|
|
12061
|
+
" BLOCKED: current change selection is stale or invalid",
|
|
12062
|
+
` Target file: ${relativePath2}`,
|
|
12063
|
+
` Reason: ${reason}`,
|
|
12064
|
+
"",
|
|
12065
|
+
" NEXT: run comet state select <change-name>, then retry the source write",
|
|
12066
|
+
""
|
|
12067
|
+
].join("\n")
|
|
12068
|
+
);
|
|
12069
|
+
}
|
|
11656
12070
|
var classicHookGuardCommand = async (args) => {
|
|
11657
|
-
const
|
|
12071
|
+
const projectRoot2 = parseProjectRoot(args);
|
|
11658
12072
|
const target = inputTarget();
|
|
11659
12073
|
if (!target) return allowed("no file path in tool input");
|
|
11660
|
-
const relativePath2 = await projectRelative(target,
|
|
11661
|
-
let governing;
|
|
11662
|
-
try {
|
|
11663
|
-
governing = await governingChange(relativePath2, projectRoot);
|
|
11664
|
-
} catch (error) {
|
|
11665
|
-
return result(
|
|
11666
|
-
2,
|
|
11667
|
-
`[COMET-HOOK] blocked: ${error instanceof Error ? error.message : String(error)}`
|
|
11668
|
-
);
|
|
11669
|
-
}
|
|
11670
|
-
if (!governing) return allowed("no active comet change");
|
|
11671
|
-
if (governing.archived) return allowed(`${relativePath2} (own change archived)`);
|
|
12074
|
+
const relativePath2 = await projectRelative(target, projectRoot2);
|
|
11672
12075
|
if (isCometConfig(relativePath2)) {
|
|
11673
12076
|
return allowed(`${relativePath2} (whitelist: comet config)`);
|
|
11674
12077
|
}
|
|
@@ -11681,6 +12084,18 @@ var classicHookGuardCommand = async (args) => {
|
|
|
11681
12084
|
if (relativePath2 === "CLAUDE.md" || relativePath2 === "CHANGELOG.md" || relativePath2 === "README.md" || isRootMarkdown(relativePath2)) {
|
|
11682
12085
|
return allowed(`${relativePath2} (whitelist: root markdown)`);
|
|
11683
12086
|
}
|
|
12087
|
+
let governing;
|
|
12088
|
+
try {
|
|
12089
|
+
governing = await governingChange(relativePath2, projectRoot2);
|
|
12090
|
+
} catch (error) {
|
|
12091
|
+
return result(
|
|
12092
|
+
2,
|
|
12093
|
+
`[COMET-HOOK] blocked: ${error instanceof Error ? error.message : String(error)}`
|
|
12094
|
+
);
|
|
12095
|
+
}
|
|
12096
|
+
if (!governing) return allowed("no active comet change");
|
|
12097
|
+
if ("blockedResult" in governing) return governing.blockedResult;
|
|
12098
|
+
if (governing.archived) return allowed(`${relativePath2} (own change archived)`);
|
|
11684
12099
|
const phase = governing.phase;
|
|
11685
12100
|
const openSpec = openSpecAllowed(relativePath2, phase);
|
|
11686
12101
|
if (openSpec) return allowed(openSpec);
|
|
@@ -12069,12 +12484,438 @@ var classicIntentCommand = async (args, _options) => {
|
|
|
12069
12484
|
}
|
|
12070
12485
|
};
|
|
12071
12486
|
|
|
12487
|
+
// domains/comet-classic/classic-resume-probe.ts
|
|
12488
|
+
import path20 from "path";
|
|
12489
|
+
import { promises as fs19 } from "fs";
|
|
12490
|
+
import { spawn } from "child_process";
|
|
12491
|
+
var COMET_RESUME_PROBE_SCHEMA_VERSION = "comet.resume_probe.v1";
|
|
12492
|
+
function isRecord2(value) {
|
|
12493
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12494
|
+
}
|
|
12495
|
+
function normalizeInput(input) {
|
|
12496
|
+
if (!isRecord2(input)) {
|
|
12497
|
+
throw new Error("Invalid CometResumeProbeInput: input must be an object");
|
|
12498
|
+
}
|
|
12499
|
+
if (input.schema_version !== COMET_RESUME_PROBE_SCHEMA_VERSION) {
|
|
12500
|
+
throw new Error(
|
|
12501
|
+
`Invalid CometResumeProbeInput: schema_version must be ${COMET_RESUME_PROBE_SCHEMA_VERSION}`
|
|
12502
|
+
);
|
|
12503
|
+
}
|
|
12504
|
+
if (typeof input.utterance !== "string") {
|
|
12505
|
+
throw new Error("Invalid CometResumeProbeInput: utterance must be a string");
|
|
12506
|
+
}
|
|
12507
|
+
const context = isRecord2(input.agent_context) ? input.agent_context : {};
|
|
12508
|
+
return {
|
|
12509
|
+
schema_version: COMET_RESUME_PROBE_SCHEMA_VERSION,
|
|
12510
|
+
utterance: input.utterance,
|
|
12511
|
+
locale: typeof input.locale === "string" ? input.locale : "unknown",
|
|
12512
|
+
agent_context: {
|
|
12513
|
+
non_trivial_work: context.non_trivial_work === true,
|
|
12514
|
+
already_in_comet_flow: context.already_in_comet_flow === true
|
|
12515
|
+
}
|
|
12516
|
+
};
|
|
12517
|
+
}
|
|
12518
|
+
function result3(action, change, confidence, reason, evidence = []) {
|
|
12519
|
+
return {
|
|
12520
|
+
schema_version: COMET_RESUME_PROBE_SCHEMA_VERSION,
|
|
12521
|
+
action,
|
|
12522
|
+
changeName: change?.name ?? null,
|
|
12523
|
+
phase: change?.phase ?? null,
|
|
12524
|
+
nextCommand: action === "auto_resume" || action === "ask_user" ? change?.nextCommand ?? null : null,
|
|
12525
|
+
confidence,
|
|
12526
|
+
reason,
|
|
12527
|
+
evidence
|
|
12528
|
+
};
|
|
12529
|
+
}
|
|
12530
|
+
async function readIfExists(filePath) {
|
|
12531
|
+
if (!await fileExists3(filePath)) return "";
|
|
12532
|
+
return fs19.readFile(filePath, "utf8");
|
|
12533
|
+
}
|
|
12534
|
+
async function changeSearchText(changeDir, classic) {
|
|
12535
|
+
const files = ["proposal.md", "design.md", "tasks.md"];
|
|
12536
|
+
const parts = [classic.name, classic.workflow, classic.phase];
|
|
12537
|
+
for (const file of files) {
|
|
12538
|
+
parts.push(await readIfExists(path20.join(changeDir, file)));
|
|
12539
|
+
}
|
|
12540
|
+
return parts.join("\n").toLowerCase();
|
|
12541
|
+
}
|
|
12542
|
+
function nextCommandForPhase2(phase) {
|
|
12543
|
+
switch (phase) {
|
|
12544
|
+
case "open":
|
|
12545
|
+
return "/comet-open";
|
|
12546
|
+
case "design":
|
|
12547
|
+
return "/comet-design";
|
|
12548
|
+
case "build":
|
|
12549
|
+
return "/comet-build";
|
|
12550
|
+
case "verify":
|
|
12551
|
+
return "/comet-verify";
|
|
12552
|
+
case "archive":
|
|
12553
|
+
return "/comet-archive";
|
|
12554
|
+
default:
|
|
12555
|
+
return null;
|
|
12556
|
+
}
|
|
12557
|
+
}
|
|
12558
|
+
function diagnosticFromProjection(changeDir, name, projection) {
|
|
12559
|
+
const classic = projection.classic;
|
|
12560
|
+
const unknownKeys = projection.unknownKeys.filter((key) => key !== "run_id");
|
|
12561
|
+
if (!classic) {
|
|
12562
|
+
return {
|
|
12563
|
+
name,
|
|
12564
|
+
valid: false,
|
|
12565
|
+
workflow: "unknown",
|
|
12566
|
+
phase: "invalid",
|
|
12567
|
+
currentStep: null,
|
|
12568
|
+
nextCommand: null,
|
|
12569
|
+
runtimeMode: "invalid",
|
|
12570
|
+
runtimeEval: null,
|
|
12571
|
+
evidence: [],
|
|
12572
|
+
error: `${changeDir} does not contain valid Comet state`
|
|
12573
|
+
};
|
|
12574
|
+
}
|
|
12575
|
+
if (unknownKeys.length > 0) {
|
|
12576
|
+
return {
|
|
12577
|
+
name,
|
|
12578
|
+
valid: false,
|
|
12579
|
+
workflow: classic.workflow,
|
|
12580
|
+
phase: classic.phase,
|
|
12581
|
+
currentStep: null,
|
|
12582
|
+
nextCommand: null,
|
|
12583
|
+
runtimeMode: "invalid",
|
|
12584
|
+
runtimeEval: null,
|
|
12585
|
+
evidence: [],
|
|
12586
|
+
error: `unknown field(s): ${unknownKeys.join(", ")}`
|
|
12587
|
+
};
|
|
12588
|
+
}
|
|
12589
|
+
return {
|
|
12590
|
+
name,
|
|
12591
|
+
valid: true,
|
|
12592
|
+
workflow: classic.workflow,
|
|
12593
|
+
phase: classic.phase,
|
|
12594
|
+
currentStep: null,
|
|
12595
|
+
nextCommand: nextCommandForPhase2(classic.phase),
|
|
12596
|
+
runtimeMode: "engine-projection",
|
|
12597
|
+
runtimeEval: null,
|
|
12598
|
+
evidence: []
|
|
12599
|
+
};
|
|
12600
|
+
}
|
|
12601
|
+
async function hasOpenSpecChangeFiles(changeDir) {
|
|
12602
|
+
return await fileExists3(path20.join(changeDir, "proposal.md")) || await fileExists3(path20.join(changeDir, "design.md")) || await fileExists3(path20.join(changeDir, "tasks.md"));
|
|
12603
|
+
}
|
|
12604
|
+
async function discoverActiveChanges(projectRoot2) {
|
|
12605
|
+
const changesDir = path20.join(projectRoot2, "openspec", "changes");
|
|
12606
|
+
if (!await fileExists3(changesDir)) return [];
|
|
12607
|
+
const entries = await readDir(changesDir);
|
|
12608
|
+
const changes = [];
|
|
12609
|
+
for (const entry2 of entries) {
|
|
12610
|
+
if (entry2 === "archive") continue;
|
|
12611
|
+
const changeDir = path20.join(changesDir, entry2);
|
|
12612
|
+
const stat = await fs19.stat(changeDir).catch(() => null);
|
|
12613
|
+
if (!stat?.isDirectory()) continue;
|
|
12614
|
+
const hasCometState = await fileExists3(path20.join(changeDir, ".comet.yaml"));
|
|
12615
|
+
if (!hasCometState) {
|
|
12616
|
+
if (!await hasOpenSpecChangeFiles(changeDir)) continue;
|
|
12617
|
+
const missingStateChange = {
|
|
12618
|
+
name: entry2,
|
|
12619
|
+
workflow: "unknown",
|
|
12620
|
+
phase: "invalid",
|
|
12621
|
+
nextCommand: null,
|
|
12622
|
+
diagnostic: {
|
|
12623
|
+
name: entry2,
|
|
12624
|
+
valid: false,
|
|
12625
|
+
workflow: "unknown",
|
|
12626
|
+
phase: "invalid",
|
|
12627
|
+
currentStep: null,
|
|
12628
|
+
nextCommand: null,
|
|
12629
|
+
runtimeMode: "invalid",
|
|
12630
|
+
runtimeEval: null,
|
|
12631
|
+
evidence: [],
|
|
12632
|
+
error: "missing Comet state"
|
|
12633
|
+
},
|
|
12634
|
+
buildPause: null,
|
|
12635
|
+
hasClassicProjection: false,
|
|
12636
|
+
verifyResult: null,
|
|
12637
|
+
text: "",
|
|
12638
|
+
missingCometState: true
|
|
12639
|
+
};
|
|
12640
|
+
missingStateChange.text = await changeSearchText(changeDir, missingStateChange);
|
|
12641
|
+
changes.push(missingStateChange);
|
|
12642
|
+
continue;
|
|
12643
|
+
}
|
|
12644
|
+
const projection = await readClassicState(changeDir, { migrate: false });
|
|
12645
|
+
const classic = projection.classic;
|
|
12646
|
+
const diagnostic = diagnosticFromProjection(changeDir, entry2, projection);
|
|
12647
|
+
const hasClassicProjection = Boolean(classic);
|
|
12648
|
+
const phase = classic?.phase ?? diagnostic.phase;
|
|
12649
|
+
const workflow = classic?.workflow ?? diagnostic.workflow;
|
|
12650
|
+
if (phase === "archive" || classic?.archived) continue;
|
|
12651
|
+
const change = {
|
|
12652
|
+
name: entry2,
|
|
12653
|
+
workflow,
|
|
12654
|
+
phase,
|
|
12655
|
+
nextCommand: diagnostic.nextCommand,
|
|
12656
|
+
diagnostic,
|
|
12657
|
+
buildPause: classic?.buildPause ?? null,
|
|
12658
|
+
hasClassicProjection,
|
|
12659
|
+
verifyResult: classic?.verifyResult ?? null,
|
|
12660
|
+
text: "",
|
|
12661
|
+
missingCometState: false
|
|
12662
|
+
};
|
|
12663
|
+
change.text = await changeSearchText(changeDir, change);
|
|
12664
|
+
changes.push(change);
|
|
12665
|
+
}
|
|
12666
|
+
return changes;
|
|
12667
|
+
}
|
|
12668
|
+
var RESUME_WORDS = [
|
|
12669
|
+
"continue",
|
|
12670
|
+
"resume",
|
|
12671
|
+
"carry on",
|
|
12672
|
+
"finish",
|
|
12673
|
+
"run it",
|
|
12674
|
+
"commit",
|
|
12675
|
+
"verify",
|
|
12676
|
+
"archive",
|
|
12677
|
+
"继续",
|
|
12678
|
+
"接着",
|
|
12679
|
+
"恢复",
|
|
12680
|
+
"跑完",
|
|
12681
|
+
"提交",
|
|
12682
|
+
"验证",
|
|
12683
|
+
"归档",
|
|
12684
|
+
"修刚才"
|
|
12685
|
+
];
|
|
12686
|
+
var QUESTION_WORDS = [
|
|
12687
|
+
"what",
|
|
12688
|
+
"why",
|
|
12689
|
+
"how",
|
|
12690
|
+
"explain",
|
|
12691
|
+
"summarize",
|
|
12692
|
+
"reliable",
|
|
12693
|
+
"靠谱吗",
|
|
12694
|
+
"是什么",
|
|
12695
|
+
"为什么",
|
|
12696
|
+
"解释",
|
|
12697
|
+
"总结",
|
|
12698
|
+
"取名",
|
|
12699
|
+
"命名"
|
|
12700
|
+
];
|
|
12701
|
+
var GENERIC_RELATED_TOKENS = /* @__PURE__ */ new Set([
|
|
12702
|
+
"add",
|
|
12703
|
+
"build",
|
|
12704
|
+
"cache",
|
|
12705
|
+
"change",
|
|
12706
|
+
"code",
|
|
12707
|
+
"design",
|
|
12708
|
+
"docs",
|
|
12709
|
+
"file",
|
|
12710
|
+
"fix",
|
|
12711
|
+
"implement",
|
|
12712
|
+
"plan",
|
|
12713
|
+
"readme",
|
|
12714
|
+
"task",
|
|
12715
|
+
"test",
|
|
12716
|
+
"update",
|
|
12717
|
+
"修改",
|
|
12718
|
+
"更新",
|
|
12719
|
+
"修复",
|
|
12720
|
+
"添加",
|
|
12721
|
+
"文档",
|
|
12722
|
+
"任务",
|
|
12723
|
+
"计划",
|
|
12724
|
+
"实现"
|
|
12725
|
+
]);
|
|
12726
|
+
var OPT_OUT_WORDS = [
|
|
12727
|
+
"do not resume",
|
|
12728
|
+
"don't resume",
|
|
12729
|
+
"without comet",
|
|
12730
|
+
"skip comet",
|
|
12731
|
+
"不要恢复",
|
|
12732
|
+
"不走 comet",
|
|
12733
|
+
"不要走 comet",
|
|
12734
|
+
"直接解释",
|
|
12735
|
+
"只回答"
|
|
12736
|
+
];
|
|
12737
|
+
function includesAny(text2, words) {
|
|
12738
|
+
return words.some((word) => text2.includes(word));
|
|
12739
|
+
}
|
|
12740
|
+
function hasDecisionPoint(change) {
|
|
12741
|
+
if (change.missingCometState) return true;
|
|
12742
|
+
if (!change.hasClassicProjection) return true;
|
|
12743
|
+
if (!change.diagnostic.valid) return true;
|
|
12744
|
+
if (change.phase === "archive") return true;
|
|
12745
|
+
if (change.verifyResult === "fail") return true;
|
|
12746
|
+
if (change.diagnostic.runtimeEval && !change.diagnostic.runtimeEval.passed) return true;
|
|
12747
|
+
if (change.phase !== "build") return false;
|
|
12748
|
+
if (change.buildPause === "plan-ready") return true;
|
|
12749
|
+
return false;
|
|
12750
|
+
}
|
|
12751
|
+
function relatedEvidence(utterance, change) {
|
|
12752
|
+
const text2 = utterance.toLowerCase();
|
|
12753
|
+
const evidence = [];
|
|
12754
|
+
if (text2.includes(change.name.toLowerCase())) {
|
|
12755
|
+
evidence.push({ source: "user", quote: change.name });
|
|
12756
|
+
}
|
|
12757
|
+
const tokens = change.text.split(/[^a-zA-Z0-9_\-\u4e00-\u9fff/]+/u).map((token) => token.trim().toLowerCase()).filter((token) => token.length >= 4 && !GENERIC_RELATED_TOKENS.has(token));
|
|
12758
|
+
const matched = [...new Set(tokens.filter((token) => text2.includes(token)))].slice(0, 3);
|
|
12759
|
+
for (const token of matched) {
|
|
12760
|
+
evidence.push({ source: "repo", quote: token });
|
|
12761
|
+
}
|
|
12762
|
+
return evidence;
|
|
12763
|
+
}
|
|
12764
|
+
async function gitDirtyFiles(projectRoot2) {
|
|
12765
|
+
return new Promise((resolve) => {
|
|
12766
|
+
const child = spawn("git", ["status", "--short", "--untracked-files=all"], {
|
|
12767
|
+
cwd: projectRoot2,
|
|
12768
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
12769
|
+
shell: false
|
|
12770
|
+
});
|
|
12771
|
+
const chunks = [];
|
|
12772
|
+
child.stdout.on("data", (chunk) => {
|
|
12773
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
|
|
12774
|
+
});
|
|
12775
|
+
child.on("error", () => resolve([]));
|
|
12776
|
+
child.on("exit", (code) => {
|
|
12777
|
+
if (code !== 0) {
|
|
12778
|
+
resolve([]);
|
|
12779
|
+
return;
|
|
12780
|
+
}
|
|
12781
|
+
const dirtyFiles = Buffer.concat(chunks).toString("utf8").split(/\r?\n/u).map((line) => line.trim()).filter(Boolean);
|
|
12782
|
+
resolve(dirtyFiles);
|
|
12783
|
+
});
|
|
12784
|
+
});
|
|
12785
|
+
}
|
|
12786
|
+
async function resolveCometResumeProbe(projectRoot2, rawInput) {
|
|
12787
|
+
const input = normalizeInput(rawInput);
|
|
12788
|
+
const utterance = input.utterance.trim();
|
|
12789
|
+
const lower = utterance.toLowerCase();
|
|
12790
|
+
if (input.agent_context.already_in_comet_flow) {
|
|
12791
|
+
return result3("out_of_scope", null, "low", "already in Comet flow");
|
|
12792
|
+
}
|
|
12793
|
+
if (includesAny(lower, OPT_OUT_WORDS)) {
|
|
12794
|
+
return result3("out_of_scope", null, "low", "user opted out of Comet resume", [
|
|
12795
|
+
{ source: "user", quote: utterance }
|
|
12796
|
+
]);
|
|
12797
|
+
}
|
|
12798
|
+
const changes = await discoverActiveChanges(projectRoot2);
|
|
12799
|
+
if (changes.length === 0) {
|
|
12800
|
+
return result3("none", null, "none", "no active Comet changes");
|
|
12801
|
+
}
|
|
12802
|
+
const dirtyFiles = await gitDirtyFiles(projectRoot2);
|
|
12803
|
+
if (changes.length > 1) {
|
|
12804
|
+
const named = changes.find((change2) => lower.includes(change2.name.toLowerCase()));
|
|
12805
|
+
if (!named) {
|
|
12806
|
+
return result3("ask_user", null, "low", "multiple active changes require a change name");
|
|
12807
|
+
}
|
|
12808
|
+
if (dirtyFiles.length > 0) {
|
|
12809
|
+
return result3("ask_user", named, "low", "uncommitted worktree changes require attribution", [
|
|
12810
|
+
{ source: "repo", quote: `${dirtyFiles.length} dirty file(s)` }
|
|
12811
|
+
]);
|
|
12812
|
+
}
|
|
12813
|
+
return hasDecisionPoint(named) ? result3("ask_user", named, "low", "active change is at a decision point") : result3("auto_resume", named, "high", "request names an active change", [
|
|
12814
|
+
{ source: "user", quote: named.name }
|
|
12815
|
+
]);
|
|
12816
|
+
}
|
|
12817
|
+
const [change] = changes;
|
|
12818
|
+
if (dirtyFiles.length > 0) {
|
|
12819
|
+
return result3("ask_user", change, "low", "uncommitted worktree changes require attribution", [
|
|
12820
|
+
{ source: "repo", quote: `${dirtyFiles.length} dirty file(s)` }
|
|
12821
|
+
]);
|
|
12822
|
+
}
|
|
12823
|
+
if (hasDecisionPoint(change)) {
|
|
12824
|
+
if (change.missingCometState) {
|
|
12825
|
+
return result3("ask_user", change, "low", "active OpenSpec change is missing Comet state");
|
|
12826
|
+
}
|
|
12827
|
+
return result3("ask_user", change, "low", "active change is at a decision point", [
|
|
12828
|
+
{ source: "state", quote: `phase: ${change.phase}` }
|
|
12829
|
+
]);
|
|
12830
|
+
}
|
|
12831
|
+
const resumeLike = includesAny(lower, RESUME_WORDS);
|
|
12832
|
+
const questionLike = !input.agent_context.non_trivial_work && includesAny(lower, QUESTION_WORDS);
|
|
12833
|
+
if (questionLike && !resumeLike) {
|
|
12834
|
+
return result3("out_of_scope", change, "low", "user asked a question without workflow work");
|
|
12835
|
+
}
|
|
12836
|
+
const evidence = relatedEvidence(utterance, change);
|
|
12837
|
+
if (resumeLike || evidence.length > 0) {
|
|
12838
|
+
return result3("auto_resume", change, "high", "single active change and request is related", [
|
|
12839
|
+
{ source: "state", quote: `phase: ${change.phase}` },
|
|
12840
|
+
...evidence
|
|
12841
|
+
]);
|
|
12842
|
+
}
|
|
12843
|
+
if (input.agent_context.non_trivial_work) {
|
|
12844
|
+
return result3(
|
|
12845
|
+
"ask_user",
|
|
12846
|
+
change,
|
|
12847
|
+
"low",
|
|
12848
|
+
"single active change exists but request looks unrelated"
|
|
12849
|
+
);
|
|
12850
|
+
}
|
|
12851
|
+
return result3("out_of_scope", change, "low", "request is not workflow work");
|
|
12852
|
+
}
|
|
12853
|
+
|
|
12854
|
+
// domains/comet-classic/classic-resume-probe-command.ts
|
|
12855
|
+
function result4(exitCode, stdout, stderr) {
|
|
12856
|
+
return {
|
|
12857
|
+
exitCode,
|
|
12858
|
+
...stdout === void 0 ? {} : { stdout },
|
|
12859
|
+
...stderr === void 0 ? {} : { stderr }
|
|
12860
|
+
};
|
|
12861
|
+
}
|
|
12862
|
+
function usage2() {
|
|
12863
|
+
return result4(
|
|
12864
|
+
64,
|
|
12865
|
+
void 0,
|
|
12866
|
+
"Usage: comet-resume-probe.mjs probe <input-json>\nUsage: comet-resume-probe.mjs probe --stdin"
|
|
12867
|
+
);
|
|
12868
|
+
}
|
|
12869
|
+
async function readStdin2() {
|
|
12870
|
+
const chunks = [];
|
|
12871
|
+
for await (const chunk of process.stdin) {
|
|
12872
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
|
|
12873
|
+
}
|
|
12874
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
12875
|
+
}
|
|
12876
|
+
function rawUtteranceInput(utterance) {
|
|
12877
|
+
return {
|
|
12878
|
+
schema_version: COMET_RESUME_PROBE_SCHEMA_VERSION,
|
|
12879
|
+
utterance,
|
|
12880
|
+
locale: "unknown",
|
|
12881
|
+
agent_context: {
|
|
12882
|
+
non_trivial_work: true,
|
|
12883
|
+
already_in_comet_flow: false
|
|
12884
|
+
}
|
|
12885
|
+
};
|
|
12886
|
+
}
|
|
12887
|
+
function parseStdinInput(source) {
|
|
12888
|
+
try {
|
|
12889
|
+
return JSON.parse(source);
|
|
12890
|
+
} catch {
|
|
12891
|
+
return rawUtteranceInput(source);
|
|
12892
|
+
}
|
|
12893
|
+
}
|
|
12894
|
+
var classicResumeProbeCommand = async (args) => {
|
|
12895
|
+
const [subcommand, input] = args;
|
|
12896
|
+
if (subcommand !== "probe") return usage2();
|
|
12897
|
+
const fromStdin = input === "--stdin";
|
|
12898
|
+
const source = fromStdin ? await readStdin2() : input;
|
|
12899
|
+
if (!source) return usage2();
|
|
12900
|
+
try {
|
|
12901
|
+
const parsedInput = fromStdin ? parseStdinInput(source) : JSON.parse(source);
|
|
12902
|
+
const resolution = await resolveCometResumeProbe(process.cwd(), parsedInput);
|
|
12903
|
+
return result4(0, `${JSON.stringify(resolution, null, 2)}
|
|
12904
|
+
`);
|
|
12905
|
+
} catch (error) {
|
|
12906
|
+
if (error instanceof SyntaxError) {
|
|
12907
|
+
return result4(1, void 0, `Invalid JSON: ${error.message}`);
|
|
12908
|
+
}
|
|
12909
|
+
return result4(1, void 0, error instanceof Error ? error.message : String(error));
|
|
12910
|
+
}
|
|
12911
|
+
};
|
|
12912
|
+
|
|
12072
12913
|
// domains/comet-classic/classic-state-command.ts
|
|
12073
|
-
var
|
|
12914
|
+
var import_yaml7 = __toESM(require_dist(), 1);
|
|
12074
12915
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
12075
|
-
import { randomUUID as
|
|
12076
|
-
import { existsSync as existsSync3, promises as
|
|
12077
|
-
import
|
|
12916
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
12917
|
+
import { existsSync as existsSync3, promises as fs20 } from "fs";
|
|
12918
|
+
import path21 from "path";
|
|
12078
12919
|
init_state();
|
|
12079
12920
|
var GREEN5 = "\x1B[32m";
|
|
12080
12921
|
var RED5 = "\x1B[31m";
|
|
@@ -12086,6 +12927,7 @@ var ARTIFACT_LANGUAGES2 = ["en", "zh-CN"];
|
|
|
12086
12927
|
var EVENTS = CLASSIC_TRANSITION_EVENTS;
|
|
12087
12928
|
var MACHINE_OWNED_FIELDS = /* @__PURE__ */ new Set([
|
|
12088
12929
|
...RUN_WIRE_KEYS,
|
|
12930
|
+
"archive_confirmation",
|
|
12089
12931
|
"classic_profile",
|
|
12090
12932
|
"classic_migration"
|
|
12091
12933
|
]);
|
|
@@ -12106,6 +12948,7 @@ var FIELD_ENUMS = {
|
|
|
12106
12948
|
auto_transition: ["true", "false"],
|
|
12107
12949
|
verify_result: ["pending", "pass", "fail"],
|
|
12108
12950
|
branch_status: ["pending", "handled"],
|
|
12951
|
+
archive_confirmation: ["pending", "confirmed"],
|
|
12109
12952
|
archived: ["true", "false"],
|
|
12110
12953
|
direct_override: ["true", "false"],
|
|
12111
12954
|
classic_profile: PROFILES,
|
|
@@ -12121,6 +12964,7 @@ var CLASSIC_FIELD_WIRE_NAMES2 = {
|
|
|
12121
12964
|
phase: "phase",
|
|
12122
12965
|
verificationReport: "verification_report",
|
|
12123
12966
|
verifiedAt: "verified_at",
|
|
12967
|
+
archiveConfirmation: "archive_confirmation",
|
|
12124
12968
|
verifyResult: "verify_result",
|
|
12125
12969
|
workflow: "workflow"
|
|
12126
12970
|
};
|
|
@@ -12182,7 +13026,7 @@ function validateRelativePath(value, field2) {
|
|
|
12182
13026
|
}
|
|
12183
13027
|
async function exists6(file) {
|
|
12184
13028
|
try {
|
|
12185
|
-
await
|
|
13029
|
+
await fs20.access(file);
|
|
12186
13030
|
return true;
|
|
12187
13031
|
} catch (error) {
|
|
12188
13032
|
if (error.code === "ENOENT") return false;
|
|
@@ -12191,39 +13035,39 @@ async function exists6(file) {
|
|
|
12191
13035
|
}
|
|
12192
13036
|
async function nonempty3(file) {
|
|
12193
13037
|
try {
|
|
12194
|
-
return (await
|
|
13038
|
+
return (await fs20.stat(file)).size > 0;
|
|
12195
13039
|
} catch (error) {
|
|
12196
13040
|
if (error.code === "ENOENT") return false;
|
|
12197
13041
|
throw error;
|
|
12198
13042
|
}
|
|
12199
13043
|
}
|
|
12200
|
-
async function
|
|
13044
|
+
async function changeDirectory2(name) {
|
|
12201
13045
|
return resolveClassicChangeDirectory(name);
|
|
12202
13046
|
}
|
|
12203
13047
|
async function readDocument2(file) {
|
|
12204
13048
|
let source;
|
|
12205
13049
|
try {
|
|
12206
|
-
source = await
|
|
13050
|
+
source = await fs20.readFile(file, "utf8");
|
|
12207
13051
|
} catch (error) {
|
|
12208
13052
|
if (error.code === "ENOENT") {
|
|
12209
13053
|
fail2(
|
|
12210
|
-
`ERROR: .comet.yaml not found at ${
|
|
13054
|
+
`ERROR: .comet.yaml not found at ${path21.relative(process.cwd(), file).replaceAll("\\", "/")}`
|
|
12211
13055
|
);
|
|
12212
13056
|
}
|
|
12213
13057
|
throw error;
|
|
12214
13058
|
}
|
|
12215
|
-
const document = (0,
|
|
13059
|
+
const document = (0, import_yaml7.parseDocument)(source, { uniqueKeys: false });
|
|
12216
13060
|
if (document.errors.length > 0) fail2(`ERROR: Invalid .comet.yaml: ${document.errors[0].message}`);
|
|
12217
13061
|
return document;
|
|
12218
13062
|
}
|
|
12219
13063
|
async function atomicWrite2(file, content) {
|
|
12220
|
-
await
|
|
12221
|
-
const temporary = `${file}.${
|
|
13064
|
+
await fs20.mkdir(path21.dirname(file), { recursive: true });
|
|
13065
|
+
const temporary = `${file}.${randomUUID7()}.tmp`;
|
|
12222
13066
|
try {
|
|
12223
|
-
await
|
|
12224
|
-
await
|
|
13067
|
+
await fs20.writeFile(temporary, content, "utf8");
|
|
13068
|
+
await fs20.rename(temporary, file);
|
|
12225
13069
|
} catch (error) {
|
|
12226
|
-
await
|
|
13070
|
+
await fs20.rm(temporary, { force: true });
|
|
12227
13071
|
throw error;
|
|
12228
13072
|
}
|
|
12229
13073
|
}
|
|
@@ -12299,6 +13143,12 @@ function sparseClassicState(record) {
|
|
|
12299
13143
|
branchStatus: enumRecordValue(record, "branch_status", ["pending", "handled"], null),
|
|
12300
13144
|
createdAt: nullableRecordString(record, "created_at"),
|
|
12301
13145
|
verifiedAt: nullableRecordString(record, "verified_at"),
|
|
13146
|
+
archiveConfirmation: enumRecordValue(
|
|
13147
|
+
record,
|
|
13148
|
+
"archive_confirmation",
|
|
13149
|
+
["pending", "confirmed"],
|
|
13150
|
+
null
|
|
13151
|
+
),
|
|
12302
13152
|
archived: nullableRecordBoolean(record, "archived") ?? false,
|
|
12303
13153
|
directOverride: nullableRecordBoolean(record, "direct_override"),
|
|
12304
13154
|
handoffContext: nullableRecordString(record, "handoff_context"),
|
|
@@ -12308,17 +13158,13 @@ function sparseClassicState(record) {
|
|
|
12308
13158
|
};
|
|
12309
13159
|
}
|
|
12310
13160
|
async function projectConfigValue2(field2) {
|
|
12311
|
-
|
|
12312
|
-
if (!await exists6(file)) return null;
|
|
12313
|
-
const document = await readDocument2(file);
|
|
12314
|
-
const value = document.get(field2);
|
|
12315
|
-
return value === null || value === void 0 ? null : scalar(value);
|
|
13161
|
+
return (await readClassicConfigValue(field2))?.value ?? null;
|
|
12316
13162
|
}
|
|
12317
13163
|
async function projectLanguageDefault() {
|
|
12318
13164
|
if (process.env.COMET_LANGUAGE)
|
|
12319
13165
|
return validateLanguage(process.env.COMET_LANGUAGE, "COMET_LANGUAGE");
|
|
12320
|
-
const
|
|
12321
|
-
if (
|
|
13166
|
+
const configured = await readClassicConfigValue("language");
|
|
13167
|
+
if (configured) return validateLanguage(configured.value, configured.source);
|
|
12322
13168
|
return "en";
|
|
12323
13169
|
}
|
|
12324
13170
|
async function contextCompression() {
|
|
@@ -12346,14 +13192,14 @@ Valid values: off, standard, thorough`);
|
|
|
12346
13192
|
return value === "null" ? null : value;
|
|
12347
13193
|
}
|
|
12348
13194
|
function gitOutput(args) {
|
|
12349
|
-
const
|
|
12350
|
-
return
|
|
13195
|
+
const result5 = spawnSync3("git", args, { encoding: "utf8" });
|
|
13196
|
+
return result5.status === 0 ? result5.stdout.trim() : null;
|
|
12351
13197
|
}
|
|
12352
13198
|
async function stateFile(name) {
|
|
12353
|
-
const change = await
|
|
13199
|
+
const change = await changeDirectory2(name);
|
|
12354
13200
|
return {
|
|
12355
13201
|
...change,
|
|
12356
|
-
file:
|
|
13202
|
+
file: path21.join(change.directory, ".comet.yaml")
|
|
12357
13203
|
};
|
|
12358
13204
|
}
|
|
12359
13205
|
async function readField3(name, field2) {
|
|
@@ -12371,7 +13217,7 @@ async function readField3(name, field2) {
|
|
|
12371
13217
|
return scalar(value);
|
|
12372
13218
|
}
|
|
12373
13219
|
function parsedValue(field2, value) {
|
|
12374
|
-
const document = (0,
|
|
13220
|
+
const document = (0, import_yaml7.parseDocument)(`${field2}: ${value}
|
|
12375
13221
|
`);
|
|
12376
13222
|
if (document.errors.length > 0) fail2(`ERROR: Invalid value: '${value}'`);
|
|
12377
13223
|
return document.get(field2);
|
|
@@ -12393,7 +13239,7 @@ function validateSetValue(field2, value) {
|
|
|
12393
13239
|
}
|
|
12394
13240
|
async function setField2(output, name, field2, value, options = {}) {
|
|
12395
13241
|
if (MACHINE_OWNED_FIELDS.has(field2) && !options.machineOwned) {
|
|
12396
|
-
fail2(`ERROR: '${field2}' is a machine-owned
|
|
13242
|
+
fail2(`ERROR: '${field2}' is a machine-owned field and cannot be set directly`);
|
|
12397
13243
|
}
|
|
12398
13244
|
if (!SETTABLE_FIELDS.has(field2) && !MACHINE_OWNED_FIELDS.has(field2)) {
|
|
12399
13245
|
fail2(`ERROR: Unknown field: '${field2}'`);
|
|
@@ -12456,10 +13302,10 @@ async function init(output, name, workflow) {
|
|
|
12456
13302
|
validateEnum(workflow, PROFILES);
|
|
12457
13303
|
const { file, label, directory } = await stateFile(name);
|
|
12458
13304
|
if (await exists6(file)) fail2(`ERROR: .comet.yaml already exists at ${label}/.comet.yaml`);
|
|
12459
|
-
await
|
|
13305
|
+
await fs20.mkdir(directory, { recursive: true });
|
|
12460
13306
|
const preset = workflow !== "full";
|
|
12461
13307
|
const reviewMode = preset ? "off" : await reviewModeDefault();
|
|
12462
|
-
const document = new
|
|
13308
|
+
const document = new import_yaml7.Document({
|
|
12463
13309
|
workflow,
|
|
12464
13310
|
language: await projectLanguageDefault(),
|
|
12465
13311
|
phase: "open",
|
|
@@ -12480,6 +13326,7 @@ async function init(output, name, workflow) {
|
|
|
12480
13326
|
branch_status: "pending",
|
|
12481
13327
|
created_at: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
|
|
12482
13328
|
verified_at: null,
|
|
13329
|
+
archive_confirmation: null,
|
|
12483
13330
|
archived: false
|
|
12484
13331
|
});
|
|
12485
13332
|
await atomicWrite2(file, document.toString());
|
|
@@ -12534,13 +13381,13 @@ async function requireOpenArtifacts(name) {
|
|
|
12534
13381
|
const { directory } = await stateFile(name);
|
|
12535
13382
|
const workflow = await readField3(name, "workflow");
|
|
12536
13383
|
for (const artifact of ["proposal.md", "tasks.md"]) {
|
|
12537
|
-
if (!await nonempty3(
|
|
13384
|
+
if (!await nonempty3(path21.join(directory, artifact))) {
|
|
12538
13385
|
fail2(
|
|
12539
13386
|
`ERROR: Cannot transition '${name}': ${artifact} must exist and be non-empty before leaving open`
|
|
12540
13387
|
);
|
|
12541
13388
|
}
|
|
12542
13389
|
}
|
|
12543
|
-
if (workflow === "full" && !await nonempty3(
|
|
13390
|
+
if (workflow === "full" && !await nonempty3(path21.join(directory, "design.md"))) {
|
|
12544
13391
|
fail2(
|
|
12545
13392
|
`ERROR: Cannot transition '${name}': design.md must exist and be non-empty before leaving open`
|
|
12546
13393
|
);
|
|
@@ -12548,14 +13395,14 @@ async function requireOpenArtifacts(name) {
|
|
|
12548
13395
|
}
|
|
12549
13396
|
async function requireDesignEvidence(name) {
|
|
12550
13397
|
const designDoc = await readField3(name, "design_doc");
|
|
12551
|
-
if (!designDoc || designDoc === "null" || !await nonempty3(
|
|
13398
|
+
if (!designDoc || designDoc === "null" || !await nonempty3(path21.resolve(designDoc))) {
|
|
12552
13399
|
fail2(
|
|
12553
13400
|
`ERROR: Cannot transition '${name}': design_doc must point to an existing Design Doc before leaving design`
|
|
12554
13401
|
);
|
|
12555
13402
|
}
|
|
12556
13403
|
}
|
|
12557
13404
|
async function writeSparseTransitionEffects(directory, effects) {
|
|
12558
|
-
const file =
|
|
13405
|
+
const file = path21.join(directory, ".comet.yaml");
|
|
12559
13406
|
const document = await readDocument2(file);
|
|
12560
13407
|
for (const effect of effects) {
|
|
12561
13408
|
const field2 = wireField2(effect.field);
|
|
@@ -12570,21 +13417,21 @@ async function applyTransitionEvent(output, name, event) {
|
|
|
12570
13417
|
let sparse = false;
|
|
12571
13418
|
if (!classic) {
|
|
12572
13419
|
if (projection.run) fail2("ERROR: Classic state projection is missing");
|
|
12573
|
-
const document = await readDocument2(
|
|
13420
|
+
const document = await readDocument2(path21.join(directory, ".comet.yaml"));
|
|
12574
13421
|
classic = sparseClassicState(document.toJS());
|
|
12575
13422
|
sparse = true;
|
|
12576
13423
|
}
|
|
12577
|
-
const
|
|
13424
|
+
const result5 = applyClassicTransition(classic, event);
|
|
12578
13425
|
if (projection.run) {
|
|
12579
|
-
await transitionClassicRuntimeRun(directory,
|
|
13426
|
+
await transitionClassicRuntimeRun(directory, result5.classic, projection.run, {
|
|
12580
13427
|
event,
|
|
12581
13428
|
source: "comet-state"
|
|
12582
13429
|
});
|
|
12583
13430
|
} else if (sparse) {
|
|
12584
|
-
await writeSparseTransitionEffects(directory,
|
|
13431
|
+
await writeSparseTransitionEffects(directory, result5.effects);
|
|
12585
13432
|
} else {
|
|
12586
13433
|
await writeClassicState(directory, {
|
|
12587
|
-
classic:
|
|
13434
|
+
classic: result5.classic,
|
|
12588
13435
|
run: null,
|
|
12589
13436
|
unknownKeys: projection.unknownKeys
|
|
12590
13437
|
});
|
|
@@ -12594,10 +13441,10 @@ async function applyTransitionEvent(output, name, event) {
|
|
|
12594
13441
|
event,
|
|
12595
13442
|
source: "comet-state",
|
|
12596
13443
|
from: classic,
|
|
12597
|
-
to:
|
|
12598
|
-
effects:
|
|
13444
|
+
to: result5.classic,
|
|
13445
|
+
effects: result5.effects
|
|
12599
13446
|
});
|
|
12600
|
-
for (const effect of
|
|
13447
|
+
for (const effect of result5.effects) {
|
|
12601
13448
|
output.stderr.push(green4(`[SET] ${wireField2(effect.field)}=${wireValue2(effect.to)}`));
|
|
12602
13449
|
}
|
|
12603
13450
|
output.stderr.push(green4(`[TRANSITION] ${event}`));
|
|
@@ -12617,7 +13464,7 @@ async function transition(output, name, event) {
|
|
|
12617
13464
|
} else if (event === "verify-pass") {
|
|
12618
13465
|
await requirePhase(name, "verify");
|
|
12619
13466
|
const report = await readField3(name, "verification_report");
|
|
12620
|
-
if (!report || !await exists6(
|
|
13467
|
+
if (!report || !await exists6(path21.resolve(report))) {
|
|
12621
13468
|
fail2(
|
|
12622
13469
|
`ERROR: Cannot transition '${name}': verification_report must point to an existing report file`
|
|
12623
13470
|
);
|
|
@@ -12627,6 +13474,14 @@ async function transition(output, name, event) {
|
|
|
12627
13474
|
}
|
|
12628
13475
|
} else if (event === "verify-fail") {
|
|
12629
13476
|
await requirePhase(name, "verify");
|
|
13477
|
+
} else if (event === "archive-confirm") {
|
|
13478
|
+
await requirePhase(name, "archive");
|
|
13479
|
+
if (await readField3(name, "verify_result") !== "pass") {
|
|
13480
|
+
fail2(`ERROR: Cannot transition '${name}': verify_result must be pass before archiving`);
|
|
13481
|
+
}
|
|
13482
|
+
if (await readField3(name, "archived") === "true") {
|
|
13483
|
+
fail2(`ERROR: Cannot transition '${name}': already archived`);
|
|
13484
|
+
}
|
|
12630
13485
|
} else if (event === "preset-escalate") {
|
|
12631
13486
|
await requirePhase(name, "build");
|
|
12632
13487
|
const workflow = await readField3(name, "workflow");
|
|
@@ -12645,6 +13500,11 @@ async function transition(output, name, event) {
|
|
|
12645
13500
|
if (await readField3(name, "verify_result") !== "pass") {
|
|
12646
13501
|
fail2(`ERROR: Cannot transition '${name}': verify_result must be pass before archiving`);
|
|
12647
13502
|
}
|
|
13503
|
+
if (await readField3(name, "archive_confirmation") !== "confirmed") {
|
|
13504
|
+
fail2(
|
|
13505
|
+
`ERROR: Cannot transition '${name}': archive_confirmation must be confirmed before archiving`
|
|
13506
|
+
);
|
|
13507
|
+
}
|
|
12648
13508
|
}
|
|
12649
13509
|
await applyTransitionEvent(output, name, event);
|
|
12650
13510
|
}
|
|
@@ -12671,9 +13531,9 @@ async function next(output, name) {
|
|
|
12671
13531
|
async function taskCheckoff(output, taskFile, taskText) {
|
|
12672
13532
|
validateRelativePath(taskFile, "task file");
|
|
12673
13533
|
if (!taskText) fail2("ERROR: Task text cannot be empty");
|
|
12674
|
-
const file =
|
|
13534
|
+
const file = path21.resolve(taskFile);
|
|
12675
13535
|
if (!await exists6(file)) fail2(`ERROR: Task file not found: ${taskFile}`);
|
|
12676
|
-
const lines = (await
|
|
13536
|
+
const lines = (await fs20.readFile(file, "utf8")).split(/\r?\n/u);
|
|
12677
13537
|
const matches = lines.filter(
|
|
12678
13538
|
(line) => [`- [ ] ${taskText}`, `- [x] ${taskText}`, `- [X] ${taskText}`].includes(line)
|
|
12679
13539
|
);
|
|
@@ -12711,21 +13571,21 @@ async function check2(output, name, phase) {
|
|
|
12711
13571
|
designDoc ? `design_doc=${designDoc} (expected: empty/null)` : "design_doc is empty/null"
|
|
12712
13572
|
);
|
|
12713
13573
|
for (const artifact of ["proposal.md", "design.md", "tasks.md"]) {
|
|
12714
|
-
(await nonempty3(
|
|
12715
|
-
`${artifact} ${await nonempty3(
|
|
13574
|
+
(await nonempty3(path21.join(directory, artifact)) ? pass2 : reject)(
|
|
13575
|
+
`${artifact} ${await nonempty3(path21.join(directory, artifact)) ? "non-empty" : "missing or empty"}`
|
|
12716
13576
|
);
|
|
12717
13577
|
}
|
|
12718
13578
|
} else if (phase === "build") {
|
|
12719
13579
|
const workflow = await readField3(name, "workflow");
|
|
12720
13580
|
const designDoc = await readField3(name, "design_doc");
|
|
12721
13581
|
if (workflow === "full") {
|
|
12722
|
-
(designDoc && designDoc !== "null" && await exists6(
|
|
13582
|
+
(designDoc && designDoc !== "null" && await exists6(path21.resolve(designDoc)) ? pass2 : reject)(`design_doc=${designDoc} (expected: non-null and file exists)`);
|
|
12723
13583
|
} else {
|
|
12724
13584
|
pass2(`workflow=${workflow} (design_doc not required)`);
|
|
12725
13585
|
}
|
|
12726
13586
|
for (const artifact of ["proposal.md", "tasks.md"]) {
|
|
12727
|
-
(await nonempty3(
|
|
12728
|
-
`${artifact} ${await nonempty3(
|
|
13587
|
+
(await nonempty3(path21.join(directory, artifact)) ? pass2 : reject)(
|
|
13588
|
+
`${artifact} ${await nonempty3(path21.join(directory, artifact)) ? "non-empty" : "missing or empty"}`
|
|
12729
13589
|
);
|
|
12730
13590
|
}
|
|
12731
13591
|
} else if (phase === "verify") {
|
|
@@ -12747,7 +13607,7 @@ async function check2(output, name, phase) {
|
|
|
12747
13607
|
}
|
|
12748
13608
|
function fieldStatus(field2, value, file) {
|
|
12749
13609
|
if (!value || value === "null") return ` - ${field2}: PENDING`;
|
|
12750
|
-
if (file && !existsSync3(
|
|
13610
|
+
if (file && !existsSync3(path21.resolve(file))) {
|
|
12751
13611
|
return ` - ${field2}: BROKEN (path ${value} does not exist)`;
|
|
12752
13612
|
}
|
|
12753
13613
|
return ` - ${field2}: DONE (${value})`;
|
|
@@ -12756,7 +13616,7 @@ async function recoverOpen(output, directory) {
|
|
|
12756
13616
|
output.stdout.push(" Artifacts:");
|
|
12757
13617
|
let complete = 0;
|
|
12758
13618
|
for (const artifact of ["proposal.md", "design.md", "tasks.md"]) {
|
|
12759
|
-
const done = await nonempty3(
|
|
13619
|
+
const done = await nonempty3(path21.join(directory, artifact));
|
|
12760
13620
|
if (done) complete += 1;
|
|
12761
13621
|
output.stdout.push(` - ${artifact}: ${done ? "DONE" : "PENDING"}`);
|
|
12762
13622
|
}
|
|
@@ -12769,7 +13629,7 @@ async function recoverDesign(output, name, directory) {
|
|
|
12769
13629
|
output.stdout.push(" Artifacts:");
|
|
12770
13630
|
for (const artifact of ["proposal.md", "design.md", "tasks.md"]) {
|
|
12771
13631
|
output.stdout.push(
|
|
12772
|
-
` - ${artifact}: ${await nonempty3(
|
|
13632
|
+
` - ${artifact}: ${await nonempty3(path21.join(directory, artifact)) ? "DONE" : "MISSING (unexpected in design phase)"}`
|
|
12773
13633
|
);
|
|
12774
13634
|
}
|
|
12775
13635
|
const handoff = await readField3(name, "handoff_context");
|
|
@@ -12783,11 +13643,11 @@ async function recoverDesign(output, name, directory) {
|
|
|
12783
13643
|
fieldStatus("design_doc", design, design),
|
|
12784
13644
|
""
|
|
12785
13645
|
);
|
|
12786
|
-
if (design && design !== "null" && await exists6(
|
|
13646
|
+
if (design && design !== "null" && await exists6(path21.resolve(design))) {
|
|
12787
13647
|
output.stdout.push(
|
|
12788
13648
|
"Recovery action: Design Doc already created and linked. Run guard to transition to build."
|
|
12789
13649
|
);
|
|
12790
|
-
} else if (handoff && handoff !== "null" && await exists6(
|
|
13650
|
+
} else if (handoff && handoff !== "null" && await exists6(path21.resolve(handoff))) {
|
|
12791
13651
|
output.stdout.push(
|
|
12792
13652
|
"Recovery action: Handoff generated but Design Doc not yet created. Resume from brainstorming confirmation (Step 1c)."
|
|
12793
13653
|
);
|
|
@@ -12817,7 +13677,7 @@ async function recoverBuild(output, name, directory, workflow) {
|
|
|
12817
13677
|
decisions.push(fieldStatus("subagent_dispatch", subagentDispatch));
|
|
12818
13678
|
}
|
|
12819
13679
|
output.stdout.push(...decisions, "", " Plan:", fieldStatus("plan", plan, plan), "");
|
|
12820
|
-
const tasks =
|
|
13680
|
+
const tasks = path21.join(directory, "tasks.md");
|
|
12821
13681
|
if (!await exists6(tasks)) {
|
|
12822
13682
|
output.stdout.push(
|
|
12823
13683
|
" Tasks: tasks.md MISSING",
|
|
@@ -12826,14 +13686,14 @@ async function recoverBuild(output, name, directory, workflow) {
|
|
|
12826
13686
|
);
|
|
12827
13687
|
return;
|
|
12828
13688
|
}
|
|
12829
|
-
const lines = (await
|
|
13689
|
+
const lines = (await fs20.readFile(tasks, "utf8")).split(/\r?\n/u);
|
|
12830
13690
|
const total = lines.filter((line) => /^\s*- \[[ xX]\] /u.test(line)).length;
|
|
12831
13691
|
const done = lines.filter((line) => /^\s*- \[[xX]\] /u.test(line)).length;
|
|
12832
13692
|
const pending = total - done;
|
|
12833
13693
|
let planTotal = 0;
|
|
12834
13694
|
let planDone = 0;
|
|
12835
|
-
if (plan && plan !== "null" && await exists6(
|
|
12836
|
-
const planLines = (await
|
|
13695
|
+
if (plan && plan !== "null" && await exists6(path21.resolve(plan))) {
|
|
13696
|
+
const planLines = (await fs20.readFile(path21.resolve(plan), "utf8")).split(/\r?\n/u);
|
|
12837
13697
|
planTotal = planLines.filter((line) => /^\s*- \[[ xX]\] /u.test(line)).length;
|
|
12838
13698
|
planDone = planLines.filter((line) => /^\s*- \[[xX]\] /u.test(line)).length;
|
|
12839
13699
|
}
|
|
@@ -12905,27 +13765,29 @@ function resolveBuildRecoveryAction(workflow, isolation, buildMode, pause, subag
|
|
|
12905
13765
|
return "Recovery action: All tasks done. Run guard to transition to verify.";
|
|
12906
13766
|
}
|
|
12907
13767
|
async function recoverVerify(output, name) {
|
|
12908
|
-
const
|
|
13768
|
+
const result5 = await readField3(name, "verify_result");
|
|
12909
13769
|
const mode = await readField3(name, "verify_mode");
|
|
12910
13770
|
const report = await readField3(name, "verification_report");
|
|
12911
13771
|
const branch = await readField3(name, "branch_status");
|
|
12912
13772
|
output.stdout.push(
|
|
12913
13773
|
" Verification:",
|
|
12914
|
-
fieldStatus("verify_result",
|
|
13774
|
+
fieldStatus("verify_result", result5),
|
|
12915
13775
|
fieldStatus("verify_mode", mode),
|
|
12916
13776
|
fieldStatus("verification_report", report, report),
|
|
12917
13777
|
fieldStatus("branch_status", branch),
|
|
12918
13778
|
"",
|
|
12919
|
-
|
|
13779
|
+
result5 === "pass" && branch === "handled" ? "Recovery action: Verification complete. Run guard to transition to archive." : result5 === "fail" ? "Recovery action: Verification failed and rolled back to build. Resume from /comet-build." : "Recovery action: Verification not yet started or in progress. Run scale assessment then verify."
|
|
12920
13780
|
);
|
|
12921
13781
|
}
|
|
12922
13782
|
async function recoverArchive(output, name) {
|
|
13783
|
+
const archiveConfirmation = await readField3(name, "archive_confirmation");
|
|
12923
13784
|
output.stdout.push(
|
|
12924
13785
|
" Archive:",
|
|
12925
13786
|
fieldStatus("verify_result", await readField3(name, "verify_result")),
|
|
13787
|
+
fieldStatus("archive_confirmation", archiveConfirmation),
|
|
12926
13788
|
fieldStatus("archived", await readField3(name, "archived")),
|
|
12927
13789
|
"",
|
|
12928
|
-
"Recovery action: Run /comet-archive to complete archiving."
|
|
13790
|
+
archiveConfirmation === "confirmed" ? "Recovery action: Archive is confirmed. Run /comet-archive to complete archiving." : "Recovery action: Ask for final archive confirmation in /comet-archive before running the archive command."
|
|
12929
13791
|
);
|
|
12930
13792
|
}
|
|
12931
13793
|
async function recover(output, name) {
|
|
@@ -12960,19 +13822,19 @@ async function scale(output, name) {
|
|
|
12960
13822
|
validateChangeName4(name);
|
|
12961
13823
|
const { file, directory, label } = await stateFile(name);
|
|
12962
13824
|
if (!await exists6(file)) fail2(`ERROR: .comet.yaml not found at ${label}/.comet.yaml`);
|
|
12963
|
-
const tasksFile =
|
|
12964
|
-
const taskCount = await exists6(tasksFile) ? (await
|
|
12965
|
-
const specs =
|
|
13825
|
+
const tasksFile = path21.join(directory, "tasks.md");
|
|
13826
|
+
const taskCount = await exists6(tasksFile) ? (await fs20.readFile(tasksFile, "utf8")).split(/\r?\n/u).filter((line) => /^- \[/u.test(line)).length : 0;
|
|
13827
|
+
const specs = path21.join(directory, "specs");
|
|
12966
13828
|
let deltaSpecs = 0;
|
|
12967
13829
|
if (await exists6(specs)) {
|
|
12968
|
-
for (const entry2 of await
|
|
12969
|
-
if (await exists6(
|
|
13830
|
+
for (const entry2 of await fs20.readdir(specs)) {
|
|
13831
|
+
if (await exists6(path21.join(specs, entry2, "spec.md"))) deltaSpecs += 1;
|
|
12970
13832
|
}
|
|
12971
13833
|
}
|
|
12972
13834
|
const plan = await readField3(name, "plan");
|
|
12973
13835
|
let baseRef = "";
|
|
12974
|
-
if (plan && plan !== "null" && await exists6(
|
|
12975
|
-
const match = (await
|
|
13836
|
+
if (plan && plan !== "null" && await exists6(path21.resolve(plan))) {
|
|
13837
|
+
const match = (await fs20.readFile(path21.resolve(plan), "utf8")).match(/^base-ref:\s*(.+)$/mu);
|
|
12976
13838
|
baseRef = match?.[1].trim() ?? "";
|
|
12977
13839
|
}
|
|
12978
13840
|
if (!baseRef) baseRef = await readField3(name, "base_ref");
|
|
@@ -12982,19 +13844,102 @@ async function scale(output, name) {
|
|
|
12982
13844
|
...baseRef && baseRef !== "null" ? [`${baseRef}...HEAD`] : ["HEAD"]
|
|
12983
13845
|
]);
|
|
12984
13846
|
const changedFiles = changed ? changed.split(/\r?\n/u).filter(Boolean).length : 0;
|
|
12985
|
-
const
|
|
12986
|
-
await setField2(new CommandOutput(), name, "verify_mode",
|
|
13847
|
+
const result5 = taskCount > 3 || deltaSpecs > 1 || changedFiles > 8 ? "full" : "light";
|
|
13848
|
+
await setField2(new CommandOutput(), name, "verify_mode", result5);
|
|
12987
13849
|
output.stderr.push(
|
|
12988
13850
|
`=== Scale Assessment: ${name} ===`,
|
|
12989
13851
|
` Tasks: ${taskCount} (threshold: 3)`,
|
|
12990
13852
|
` Delta specs: ${deltaSpecs} capabilities (threshold: 1)`,
|
|
12991
13853
|
` Changed files: ${changedFiles} (threshold: 8)`,
|
|
12992
|
-
` → Result: ${
|
|
12993
|
-
green4(`[SCALE] verify_mode=${
|
|
13854
|
+
` → Result: ${result5}`,
|
|
13855
|
+
green4(`[SCALE] verify_mode=${result5}`)
|
|
13856
|
+
);
|
|
13857
|
+
}
|
|
13858
|
+
function parseRecordCheckOptions(args) {
|
|
13859
|
+
let command;
|
|
13860
|
+
let exitCodeText;
|
|
13861
|
+
let cwd;
|
|
13862
|
+
for (let index = 0; index < args.length; index += 2) {
|
|
13863
|
+
const option = args[index];
|
|
13864
|
+
if (!["--command", "--exit-code", "--cwd"].includes(option)) {
|
|
13865
|
+
fail2(`ERROR: Unknown option: ${option}`);
|
|
13866
|
+
}
|
|
13867
|
+
const value = args[index + 1];
|
|
13868
|
+
if (value === void 0) fail2(`ERROR: Missing value for option: ${option}`);
|
|
13869
|
+
if (option === "--command") command = value;
|
|
13870
|
+
else if (option === "--exit-code") exitCodeText = value;
|
|
13871
|
+
else cwd = value;
|
|
13872
|
+
}
|
|
13873
|
+
if (command === void 0) fail2("ERROR: Missing option: --command");
|
|
13874
|
+
if (exitCodeText === void 0) fail2("ERROR: Missing option: --exit-code");
|
|
13875
|
+
if (!/^-?\d+$/u.test(exitCodeText)) fail2("ERROR: --exit-code must be an integer");
|
|
13876
|
+
return { command, exitCode: Number(exitCodeText), ...cwd === void 0 ? {} : { cwd } };
|
|
13877
|
+
}
|
|
13878
|
+
async function recordCheck(output, name, scopeText, args) {
|
|
13879
|
+
validateChangeName4(name);
|
|
13880
|
+
if (scopeText !== "build" && scopeText !== "verify") {
|
|
13881
|
+
fail2(`ERROR: Invalid command check scope: '${scopeText}'`);
|
|
13882
|
+
}
|
|
13883
|
+
const options = parseRecordCheckOptions(args);
|
|
13884
|
+
const { label, directory, file } = await stateFile(name);
|
|
13885
|
+
if (label !== `openspec/changes/${name}` || !await exists6(file)) {
|
|
13886
|
+
fail2(`ERROR: command checks require an active change: ${name}`);
|
|
13887
|
+
}
|
|
13888
|
+
try {
|
|
13889
|
+
const projection = await readClassicState(directory, { migrate: false });
|
|
13890
|
+
if (!projection.classic || !projection.run) {
|
|
13891
|
+
throw new Error("command checks require an existing synchronized Classic Run");
|
|
13892
|
+
}
|
|
13893
|
+
const { run } = await validateClassicRuntimeRun(directory, projection);
|
|
13894
|
+
const recorded = await recordCommandCheck(directory, run, {
|
|
13895
|
+
scope: scopeText,
|
|
13896
|
+
...options
|
|
13897
|
+
});
|
|
13898
|
+
output.stderr.push(
|
|
13899
|
+
green4(
|
|
13900
|
+
`[RECORDED] ${recorded.scope} exit=${recorded.exitCode} cwd=${recorded.cwd} command=${recorded.command}`
|
|
13901
|
+
)
|
|
13902
|
+
);
|
|
13903
|
+
} catch (error) {
|
|
13904
|
+
fail2(`ERROR: ${error.message}`);
|
|
13905
|
+
}
|
|
13906
|
+
}
|
|
13907
|
+
function required(args, count, usage3) {
|
|
13908
|
+
if (args.length < count) fail2(usage3);
|
|
13909
|
+
}
|
|
13910
|
+
function requiredExact(args, count, usage3) {
|
|
13911
|
+
if (args.length !== count) fail2(usage3);
|
|
13912
|
+
}
|
|
13913
|
+
async function selectChange(output, name) {
|
|
13914
|
+
validateChangeName4(name);
|
|
13915
|
+
try {
|
|
13916
|
+
const selection = await selectCurrentChange(process.cwd(), name);
|
|
13917
|
+
output.stderr.push(
|
|
13918
|
+
green4(
|
|
13919
|
+
`[SELECTED] current change: ${selection.change}${selection.branch ? ` (branch: ${selection.branch})` : ""}`
|
|
13920
|
+
)
|
|
13921
|
+
);
|
|
13922
|
+
} catch (error) {
|
|
13923
|
+
fail2(`ERROR: ${error instanceof Error ? error.message : String(error)}`);
|
|
13924
|
+
}
|
|
13925
|
+
}
|
|
13926
|
+
async function currentChange(output) {
|
|
13927
|
+
const resolution = await resolveCurrentChange(process.cwd());
|
|
13928
|
+
if (resolution.status === "selected") {
|
|
13929
|
+
output.stdout.push(resolution.selection.change);
|
|
13930
|
+
return;
|
|
13931
|
+
}
|
|
13932
|
+
if (resolution.status === "missing") {
|
|
13933
|
+
fail2("ERROR: no current change selected\nUse: comet-state.mjs select <change-name>");
|
|
13934
|
+
}
|
|
13935
|
+
fail2(
|
|
13936
|
+
`ERROR: current change selection is stale: ${resolution.reason}
|
|
13937
|
+
Use: comet-state.mjs select <change-name>`
|
|
12994
13938
|
);
|
|
12995
13939
|
}
|
|
12996
|
-
function
|
|
12997
|
-
|
|
13940
|
+
async function clearSelection(output) {
|
|
13941
|
+
await clearCurrentChange(process.cwd());
|
|
13942
|
+
output.stderr.push(green4("[CLEARED] current change selection"));
|
|
12998
13943
|
}
|
|
12999
13944
|
var classicStateCommand = async (args) => {
|
|
13000
13945
|
const output = new CommandOutput();
|
|
@@ -13021,9 +13966,25 @@ var classicStateCommand = async (args) => {
|
|
|
13021
13966
|
} else if (subcommand === "scale") {
|
|
13022
13967
|
required(rest, 1, "Usage: comet-state.mjs scale <change-name>");
|
|
13023
13968
|
await scale(output, rest[0]);
|
|
13969
|
+
} else if (subcommand === "record-check") {
|
|
13970
|
+
required(
|
|
13971
|
+
rest,
|
|
13972
|
+
2,
|
|
13973
|
+
"Usage: comet state record-check <change> <build|verify> --command <text> --exit-code <int> [--cwd <path>]"
|
|
13974
|
+
);
|
|
13975
|
+
await recordCheck(output, rest[0], rest[1], rest.slice(2));
|
|
13024
13976
|
} else if (subcommand === "task-checkoff") {
|
|
13025
13977
|
required(rest, 2, "Usage: comet-state.mjs task-checkoff <file> <task-text>");
|
|
13026
13978
|
await taskCheckoff(output, rest[0], rest[1]);
|
|
13979
|
+
} else if (subcommand === "select") {
|
|
13980
|
+
requiredExact(rest, 1, "Usage: comet-state.mjs select <change-name>");
|
|
13981
|
+
await selectChange(output, rest[0]);
|
|
13982
|
+
} else if (subcommand === "current") {
|
|
13983
|
+
requiredExact(rest, 0, "Usage: comet-state.mjs current");
|
|
13984
|
+
await currentChange(output);
|
|
13985
|
+
} else if (subcommand === "clear-selection") {
|
|
13986
|
+
requiredExact(rest, 0, "Usage: comet-state.mjs clear-selection");
|
|
13987
|
+
await clearSelection(output);
|
|
13027
13988
|
} else if (subcommand === "next") {
|
|
13028
13989
|
required(rest, 1, "Usage: comet-state.mjs next <change-name>");
|
|
13029
13990
|
await next(output, rest[0]);
|
|
@@ -13048,7 +14009,8 @@ var CLASSIC_COMMANDS = [
|
|
|
13048
14009
|
"handoff",
|
|
13049
14010
|
"archive",
|
|
13050
14011
|
"hook-guard",
|
|
13051
|
-
"intent"
|
|
14012
|
+
"intent",
|
|
14013
|
+
"resume-probe"
|
|
13052
14014
|
];
|
|
13053
14015
|
var DEFAULT_HANDLERS = {
|
|
13054
14016
|
state: classicStateCommand,
|
|
@@ -13057,7 +14019,8 @@ var DEFAULT_HANDLERS = {
|
|
|
13057
14019
|
handoff: classicHandoffCommand,
|
|
13058
14020
|
archive: classicArchiveCommand,
|
|
13059
14021
|
"hook-guard": classicHookGuardCommand,
|
|
13060
|
-
intent: classicIntentCommand
|
|
14022
|
+
intent: classicIntentCommand,
|
|
14023
|
+
"resume-probe": classicResumeProbeCommand
|
|
13061
14024
|
};
|
|
13062
14025
|
function isClassicCommand(value) {
|
|
13063
14026
|
return CLASSIC_COMMANDS.includes(value);
|
|
@@ -13092,14 +14055,14 @@ async function dispatch(command, args, options, handlers) {
|
|
|
13092
14055
|
};
|
|
13093
14056
|
}
|
|
13094
14057
|
}
|
|
13095
|
-
function jsonResult(command,
|
|
14058
|
+
function jsonResult(command, result5) {
|
|
13096
14059
|
return {
|
|
13097
|
-
exitCode:
|
|
14060
|
+
exitCode: result5.exitCode,
|
|
13098
14061
|
stdout: JSON.stringify({
|
|
13099
14062
|
command: command ?? null,
|
|
13100
|
-
exitCode:
|
|
13101
|
-
...
|
|
13102
|
-
...
|
|
14063
|
+
exitCode: result5.exitCode,
|
|
14064
|
+
...result5.stdout === void 0 ? {} : { stdout: result5.stdout },
|
|
14065
|
+
...result5.stderr === void 0 ? {} : { stderr: result5.stderr }
|
|
13103
14066
|
}) + "\n"
|
|
13104
14067
|
};
|
|
13105
14068
|
}
|
|
@@ -13107,15 +14070,15 @@ async function runClassicCli(argv, handlers = DEFAULT_HANDLERS) {
|
|
|
13107
14070
|
const json = argv.includes("--json");
|
|
13108
14071
|
const args = argv.filter((argument) => argument !== "--json");
|
|
13109
14072
|
const command = args.shift();
|
|
13110
|
-
const
|
|
13111
|
-
return json ? jsonResult(command,
|
|
14073
|
+
const result5 = await dispatch(command, args, { json }, handlers);
|
|
14074
|
+
return json ? jsonResult(command, result5) : result5;
|
|
13112
14075
|
}
|
|
13113
14076
|
async function main(argv = process.argv.slice(2)) {
|
|
13114
|
-
const
|
|
13115
|
-
if (
|
|
13116
|
-
if (
|
|
13117
|
-
process.stderr.write(
|
|
13118
|
-
return
|
|
14077
|
+
const result5 = await runClassicCli(argv);
|
|
14078
|
+
if (result5.stdout) process.stdout.write(result5.stdout);
|
|
14079
|
+
if (result5.stderr)
|
|
14080
|
+
process.stderr.write(result5.stderr + (result5.stderr.endsWith("\n") ? "" : "\n"));
|
|
14081
|
+
return result5.exitCode;
|
|
13119
14082
|
}
|
|
13120
14083
|
var entry = process.argv[1];
|
|
13121
14084
|
if (entry && import.meta.url === pathToFileURL(entry).href) {
|