@rpamis/comet 0.4.0-beta.2 → 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 +27 -18
- package/assets/manifest.json +2 -1
- package/assets/skills/comet/SKILL.md +25 -1
- package/assets/skills/comet/reference/comet-yaml-fields.md +6 -4
- 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 +1664 -512
- package/assets/skills/comet-archive/SKILL.md +24 -11
- package/assets/skills/comet-build/SKILL.md +40 -31
- 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 -4
- 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 +40 -31
- 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 +37 -3
- 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 +1 -0
- package/dist/app/commands/doctor.d.ts.map +1 -1
- package/dist/app/commands/doctor.js +100 -35
- 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 +101 -141
- 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 +67 -29
- 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 +269 -4
- 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 +129 -15
- package/dist/domains/comet-classic/classic-state-command.js.map +1 -1
- package/dist/domains/comet-classic/classic-state.d.ts +3 -3
- package/dist/domains/comet-classic/classic-state.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-state.js +4 -6
- 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 +27 -9
- 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 +16 -16
- 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,10 +7834,9 @@ 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
|
-
"build_command",
|
|
7831
|
-
"verify_command",
|
|
7832
7840
|
"handoff_context",
|
|
7833
7841
|
"handoff_hash",
|
|
7834
7842
|
"classic_profile",
|
|
@@ -7935,10 +7943,9 @@ function classicStateFromDocument(doc) {
|
|
|
7935
7943
|
branchStatus: enumValue(doc, "branch_status", BRANCH_STATUSES),
|
|
7936
7944
|
createdAt: nullableString(doc, "created_at"),
|
|
7937
7945
|
verifiedAt: nullableString(doc, "verified_at"),
|
|
7946
|
+
archiveConfirmation: enumValue(doc, "archive_confirmation", ARCHIVE_CONFIRMATIONS),
|
|
7938
7947
|
archived: booleanValue(doc, "archived", false),
|
|
7939
7948
|
directOverride: booleanValue(doc, "direct_override"),
|
|
7940
|
-
buildCommand: nullableString(doc, "build_command"),
|
|
7941
|
-
verifyCommand: nullableString(doc, "verify_command"),
|
|
7942
7949
|
handoffContext: relativePath(doc, "handoff_context"),
|
|
7943
7950
|
handoffHash: sha256(doc, "handoff_hash"),
|
|
7944
7951
|
classicProfile: enumValue(doc, "classic_profile", CLASSIC_PROFILES),
|
|
@@ -8000,10 +8007,9 @@ function classicStateToDocument(state) {
|
|
|
8000
8007
|
branch_status: state.branchStatus,
|
|
8001
8008
|
created_at: state.createdAt,
|
|
8002
8009
|
verified_at: state.verifiedAt,
|
|
8010
|
+
archive_confirmation: state.archiveConfirmation,
|
|
8003
8011
|
archived: state.archived,
|
|
8004
8012
|
direct_override: state.directOverride,
|
|
8005
|
-
build_command: state.buildCommand,
|
|
8006
|
-
verify_command: state.verifyCommand,
|
|
8007
8013
|
handoff_context: state.handoffContext,
|
|
8008
8014
|
handoff_hash: state.handoffHash,
|
|
8009
8015
|
classic_profile: state.classicProfile,
|
|
@@ -8058,6 +8064,16 @@ function stripLegacyRunFields(document) {
|
|
|
8058
8064
|
];
|
|
8059
8065
|
for (const key of LEGACY_RUN_KEYS) document.delete(key);
|
|
8060
8066
|
}
|
|
8067
|
+
function stripLegacyCommandFields(document) {
|
|
8068
|
+
let changed = false;
|
|
8069
|
+
for (const key of ["build_command", "verify_command"]) {
|
|
8070
|
+
if (document.has(key)) {
|
|
8071
|
+
document.delete(key);
|
|
8072
|
+
changed = true;
|
|
8073
|
+
}
|
|
8074
|
+
}
|
|
8075
|
+
return changed;
|
|
8076
|
+
}
|
|
8061
8077
|
async function readDocument(file) {
|
|
8062
8078
|
let source;
|
|
8063
8079
|
try {
|
|
@@ -8073,23 +8089,29 @@ async function readDocument(file) {
|
|
|
8073
8089
|
documentRecord(document);
|
|
8074
8090
|
return document;
|
|
8075
8091
|
}
|
|
8076
|
-
async function readClassicState(changeDir) {
|
|
8077
|
-
const
|
|
8078
|
-
const
|
|
8092
|
+
async function readClassicState(changeDir, options = {}) {
|
|
8093
|
+
const shouldMigrate = options.migrate !== false;
|
|
8094
|
+
const file = path4.join(changeDir, ".comet.yaml");
|
|
8095
|
+
const document = await readDocument(file);
|
|
8096
|
+
let doc = documentRecord(document);
|
|
8097
|
+
let migrated = stripLegacyCommandFields(document);
|
|
8098
|
+
if (migrated) doc = documentRecord(document);
|
|
8079
8099
|
let run = await readRunState(changeDir);
|
|
8080
8100
|
if (!run && doc.run_id && doc.skill) {
|
|
8081
8101
|
const { runStateFromDocument: runStateFromDocument2 } = await Promise.resolve().then(() => (init_state(), state_exports));
|
|
8082
8102
|
run = runStateFromDocument2(doc);
|
|
8083
|
-
if (run) {
|
|
8103
|
+
if (run && shouldMigrate) {
|
|
8084
8104
|
await writeRunState(changeDir, run);
|
|
8085
8105
|
stripLegacyRunFields(document);
|
|
8086
|
-
|
|
8087
|
-
const temporary = path4.join(changeDir, `.comet.yaml.${randomUUID2()}.tmp`);
|
|
8088
|
-
await fs4.writeFile(temporary, document.toString(), "utf8");
|
|
8089
|
-
await fs4.rename(temporary, file);
|
|
8106
|
+
migrated = true;
|
|
8090
8107
|
}
|
|
8091
8108
|
}
|
|
8092
|
-
|
|
8109
|
+
if (migrated && shouldMigrate) {
|
|
8110
|
+
const temporary = path4.join(changeDir, `.comet.yaml.${randomUUID2()}.tmp`);
|
|
8111
|
+
await fs4.writeFile(temporary, document.toString(), "utf8");
|
|
8112
|
+
await fs4.rename(temporary, file);
|
|
8113
|
+
}
|
|
8114
|
+
return parseClassicStateDocument(documentRecord(document), run);
|
|
8093
8115
|
}
|
|
8094
8116
|
async function readLegacyState(changeDir) {
|
|
8095
8117
|
const document = await readDocument(path4.join(changeDir, ".comet.yaml"));
|
|
@@ -8821,13 +8843,13 @@ function artifactKey(code) {
|
|
|
8821
8843
|
return code.replaceAll(".", "_").replaceAll("-", "_");
|
|
8822
8844
|
}
|
|
8823
8845
|
async function migrationArtifacts(changeDir, evidence) {
|
|
8824
|
-
const
|
|
8846
|
+
const projectRoot2 = projectRootFor2(changeDir);
|
|
8825
8847
|
const artifacts = Object.fromEntries(
|
|
8826
8848
|
evidence.filter((item) => item.satisfied && item.source).map((item) => [artifactKey(item.code), item.source])
|
|
8827
8849
|
);
|
|
8828
8850
|
const progress = path9.join(changeDir, "subagent-progress.md");
|
|
8829
8851
|
if (await pathExists2(progress)) {
|
|
8830
|
-
artifacts.subagent_progress = path9.relative(
|
|
8852
|
+
artifacts.subagent_progress = path9.relative(projectRoot2, progress).split(path9.sep).join("/");
|
|
8831
8853
|
}
|
|
8832
8854
|
const handoff = evidence.find((item) => item.code === "design.handoff" && item.satisfied);
|
|
8833
8855
|
if (handoff?.source) artifacts.handoff_context = handoff.source;
|
|
@@ -8919,11 +8941,11 @@ async function ensureClassicRun(changeDir, options) {
|
|
|
8919
8941
|
classicMigration: CLASSIC_MIGRATION_VERSION
|
|
8920
8942
|
};
|
|
8921
8943
|
const artifacts = await migrationArtifacts(changeDir, evidence);
|
|
8922
|
-
const
|
|
8944
|
+
const projectRoot2 = projectRootFor2(changeDir);
|
|
8923
8945
|
const handoff = evidence.find((item) => item.code === "design.handoff" && item.satisfied);
|
|
8924
8946
|
let context = null;
|
|
8925
8947
|
if (handoff?.source) {
|
|
8926
|
-
context = await fs8.readFile(path9.resolve(
|
|
8948
|
+
context = await fs8.readFile(path9.resolve(projectRoot2, handoff.source), "utf8");
|
|
8927
8949
|
await writeContext(changeDir, run.contextRef, context);
|
|
8928
8950
|
createdFiles.push(path9.resolve(changeDir, run.contextRef));
|
|
8929
8951
|
}
|
|
@@ -8980,6 +9002,230 @@ async function fileExists2(file) {
|
|
|
8980
9002
|
throw error;
|
|
8981
9003
|
}
|
|
8982
9004
|
}
|
|
9005
|
+
function embeddedClassicRuntimePackage(root) {
|
|
9006
|
+
return {
|
|
9007
|
+
root,
|
|
9008
|
+
packageKind: "runtime",
|
|
9009
|
+
definition: {
|
|
9010
|
+
apiVersion: "comet/v1alpha1",
|
|
9011
|
+
kind: "Skill",
|
|
9012
|
+
metadata: {
|
|
9013
|
+
name: "comet-classic",
|
|
9014
|
+
version: "1",
|
|
9015
|
+
description: "Internal compatibility orchestration for classic Comet full, hotfix, and tweak workflows"
|
|
9016
|
+
},
|
|
9017
|
+
goal: {
|
|
9018
|
+
statement: "Advance or restore a classic Comet Run without changing the user command surface",
|
|
9019
|
+
inputs: [
|
|
9020
|
+
{
|
|
9021
|
+
name: "classic-state",
|
|
9022
|
+
description: "Validated ClassicState consistent with the Run projection",
|
|
9023
|
+
required: true
|
|
9024
|
+
},
|
|
9025
|
+
{
|
|
9026
|
+
name: "evidence",
|
|
9027
|
+
description: "Structured evidence produced by the Classic Evidence collector",
|
|
9028
|
+
required: true
|
|
9029
|
+
}
|
|
9030
|
+
],
|
|
9031
|
+
outputs: [
|
|
9032
|
+
{
|
|
9033
|
+
name: "run-state",
|
|
9034
|
+
description: "Atomically synchronized Classic and Run state",
|
|
9035
|
+
required: true
|
|
9036
|
+
}
|
|
9037
|
+
],
|
|
9038
|
+
success: [
|
|
9039
|
+
"Legacy fields and Run fields remain consistent",
|
|
9040
|
+
"Every step invokes only a declared public Comet Skill",
|
|
9041
|
+
"The completed state passes its completion eval"
|
|
9042
|
+
]
|
|
9043
|
+
},
|
|
9044
|
+
orchestration: {
|
|
9045
|
+
mode: "deterministic",
|
|
9046
|
+
entry: "full.open",
|
|
9047
|
+
steps: [
|
|
9048
|
+
{
|
|
9049
|
+
id: "full.open",
|
|
9050
|
+
action: { type: "invoke_skill", ref: "comet-open" },
|
|
9051
|
+
next: "full.design.handoff"
|
|
9052
|
+
},
|
|
9053
|
+
{
|
|
9054
|
+
id: "full.design.handoff",
|
|
9055
|
+
action: { type: "invoke_skill", ref: "comet-design" },
|
|
9056
|
+
next: "full.design.document"
|
|
9057
|
+
},
|
|
9058
|
+
{
|
|
9059
|
+
id: "full.design.document",
|
|
9060
|
+
action: { type: "invoke_skill", ref: "comet-design" },
|
|
9061
|
+
next: "full.build.plan"
|
|
9062
|
+
},
|
|
9063
|
+
{
|
|
9064
|
+
id: "full.build.plan",
|
|
9065
|
+
action: { type: "invoke_skill", ref: "comet-build" },
|
|
9066
|
+
next: "full.build.plan-ready"
|
|
9067
|
+
},
|
|
9068
|
+
{
|
|
9069
|
+
id: "full.build.plan-ready",
|
|
9070
|
+
action: { type: "invoke_skill", ref: "comet-build" },
|
|
9071
|
+
next: "full.build.configure"
|
|
9072
|
+
},
|
|
9073
|
+
{
|
|
9074
|
+
id: "full.build.configure",
|
|
9075
|
+
action: { type: "invoke_skill", ref: "comet-build" },
|
|
9076
|
+
next: "full.build.execute"
|
|
9077
|
+
},
|
|
9078
|
+
{
|
|
9079
|
+
id: "full.build.execute",
|
|
9080
|
+
action: { type: "invoke_skill", ref: "comet-build" },
|
|
9081
|
+
next: "full.build.complete"
|
|
9082
|
+
},
|
|
9083
|
+
{
|
|
9084
|
+
id: "full.build.complete",
|
|
9085
|
+
action: { type: "invoke_skill", ref: "comet-build" },
|
|
9086
|
+
next: "full.verify.run"
|
|
9087
|
+
},
|
|
9088
|
+
{
|
|
9089
|
+
id: "full.build.fix",
|
|
9090
|
+
action: { type: "invoke_skill", ref: "comet-build" },
|
|
9091
|
+
next: "full.build.execute"
|
|
9092
|
+
},
|
|
9093
|
+
{
|
|
9094
|
+
id: "full.verify.run",
|
|
9095
|
+
action: { type: "invoke_skill", ref: "comet-verify" },
|
|
9096
|
+
next: "full.verify.branch"
|
|
9097
|
+
},
|
|
9098
|
+
{
|
|
9099
|
+
id: "full.verify.branch",
|
|
9100
|
+
action: { type: "invoke_skill", ref: "comet-verify" },
|
|
9101
|
+
next: "full.archive.confirm"
|
|
9102
|
+
},
|
|
9103
|
+
{
|
|
9104
|
+
id: "full.archive.confirm",
|
|
9105
|
+
action: { type: "invoke_skill", ref: "comet-archive" },
|
|
9106
|
+
next: "full.archive.execute"
|
|
9107
|
+
},
|
|
9108
|
+
{
|
|
9109
|
+
id: "full.archive.execute",
|
|
9110
|
+
action: { type: "invoke_skill", ref: "comet-archive" },
|
|
9111
|
+
next: "completed"
|
|
9112
|
+
},
|
|
9113
|
+
{
|
|
9114
|
+
id: "hotfix.open",
|
|
9115
|
+
action: { type: "invoke_skill", ref: "comet-hotfix" },
|
|
9116
|
+
next: "hotfix.build.execute"
|
|
9117
|
+
},
|
|
9118
|
+
{
|
|
9119
|
+
id: "hotfix.build.execute",
|
|
9120
|
+
action: { type: "invoke_skill", ref: "comet-build" },
|
|
9121
|
+
next: "hotfix.build.complete"
|
|
9122
|
+
},
|
|
9123
|
+
{
|
|
9124
|
+
id: "hotfix.build.complete",
|
|
9125
|
+
action: { type: "invoke_skill", ref: "comet-build" },
|
|
9126
|
+
next: "hotfix.verify.run"
|
|
9127
|
+
},
|
|
9128
|
+
{
|
|
9129
|
+
id: "hotfix.verify.run",
|
|
9130
|
+
action: { type: "invoke_skill", ref: "comet-verify" },
|
|
9131
|
+
next: "hotfix.verify.branch"
|
|
9132
|
+
},
|
|
9133
|
+
{
|
|
9134
|
+
id: "hotfix.verify.branch",
|
|
9135
|
+
action: { type: "invoke_skill", ref: "comet-verify" },
|
|
9136
|
+
next: "hotfix.archive.confirm"
|
|
9137
|
+
},
|
|
9138
|
+
{
|
|
9139
|
+
id: "hotfix.archive.confirm",
|
|
9140
|
+
action: { type: "invoke_skill", ref: "comet-archive" },
|
|
9141
|
+
next: "hotfix.archive.execute"
|
|
9142
|
+
},
|
|
9143
|
+
{
|
|
9144
|
+
id: "hotfix.archive.execute",
|
|
9145
|
+
action: { type: "invoke_skill", ref: "comet-archive" },
|
|
9146
|
+
next: "completed"
|
|
9147
|
+
},
|
|
9148
|
+
{
|
|
9149
|
+
id: "tweak.open",
|
|
9150
|
+
action: { type: "invoke_skill", ref: "comet-tweak" },
|
|
9151
|
+
next: "tweak.build.execute"
|
|
9152
|
+
},
|
|
9153
|
+
{
|
|
9154
|
+
id: "tweak.build.execute",
|
|
9155
|
+
action: { type: "invoke_skill", ref: "comet-build" },
|
|
9156
|
+
next: "tweak.build.complete"
|
|
9157
|
+
},
|
|
9158
|
+
{
|
|
9159
|
+
id: "tweak.build.complete",
|
|
9160
|
+
action: { type: "invoke_skill", ref: "comet-build" },
|
|
9161
|
+
next: "tweak.verify.run"
|
|
9162
|
+
},
|
|
9163
|
+
{
|
|
9164
|
+
id: "tweak.verify.run",
|
|
9165
|
+
action: { type: "invoke_skill", ref: "comet-verify" },
|
|
9166
|
+
next: "tweak.verify.branch"
|
|
9167
|
+
},
|
|
9168
|
+
{
|
|
9169
|
+
id: "tweak.verify.branch",
|
|
9170
|
+
action: { type: "invoke_skill", ref: "comet-verify" },
|
|
9171
|
+
next: "tweak.archive.confirm"
|
|
9172
|
+
},
|
|
9173
|
+
{
|
|
9174
|
+
id: "tweak.archive.confirm",
|
|
9175
|
+
action: { type: "invoke_skill", ref: "comet-archive" },
|
|
9176
|
+
next: "tweak.archive.execute"
|
|
9177
|
+
},
|
|
9178
|
+
{
|
|
9179
|
+
id: "tweak.archive.execute",
|
|
9180
|
+
action: { type: "invoke_skill", ref: "comet-archive" },
|
|
9181
|
+
next: "completed"
|
|
9182
|
+
},
|
|
9183
|
+
{
|
|
9184
|
+
id: "completed",
|
|
9185
|
+
action: { type: "checkpoint" },
|
|
9186
|
+
completionEvals: ["classic-completed"]
|
|
9187
|
+
}
|
|
9188
|
+
]
|
|
9189
|
+
},
|
|
9190
|
+
skills: [
|
|
9191
|
+
{ id: "comet-open" },
|
|
9192
|
+
{ id: "comet-design" },
|
|
9193
|
+
{ id: "comet-build" },
|
|
9194
|
+
{ id: "comet-verify" },
|
|
9195
|
+
{ id: "comet-archive" },
|
|
9196
|
+
{ id: "comet-hotfix" },
|
|
9197
|
+
{ id: "comet-tweak" }
|
|
9198
|
+
],
|
|
9199
|
+
agents: [],
|
|
9200
|
+
tools: []
|
|
9201
|
+
},
|
|
9202
|
+
guardrails: {
|
|
9203
|
+
allowedSkills: [
|
|
9204
|
+
"comet-open",
|
|
9205
|
+
"comet-design",
|
|
9206
|
+
"comet-build",
|
|
9207
|
+
"comet-verify",
|
|
9208
|
+
"comet-archive",
|
|
9209
|
+
"comet-hotfix",
|
|
9210
|
+
"comet-tweak"
|
|
9211
|
+
],
|
|
9212
|
+
allowedAgents: [],
|
|
9213
|
+
allowedTools: [],
|
|
9214
|
+
maxIterations: 500,
|
|
9215
|
+
maxRetriesPerAction: 3,
|
|
9216
|
+
confirmationRequiredFor: []
|
|
9217
|
+
},
|
|
9218
|
+
evals: [
|
|
9219
|
+
{
|
|
9220
|
+
id: "classic-completed",
|
|
9221
|
+
scope: "completion",
|
|
9222
|
+
type: "state_equals",
|
|
9223
|
+
field: "status",
|
|
9224
|
+
equals: "completed"
|
|
9225
|
+
}
|
|
9226
|
+
]
|
|
9227
|
+
};
|
|
9228
|
+
}
|
|
8983
9229
|
async function classicRuntimeRoot() {
|
|
8984
9230
|
const runtimeDirectory = path10.dirname(fileURLToPath(import.meta.url));
|
|
8985
9231
|
const candidates = [
|
|
@@ -8996,7 +9242,7 @@ async function classicRuntimeRoot() {
|
|
|
8996
9242
|
for (const candidate of candidates) {
|
|
8997
9243
|
if (await directoryExists(candidate)) return candidate;
|
|
8998
9244
|
}
|
|
8999
|
-
|
|
9245
|
+
return null;
|
|
9000
9246
|
}
|
|
9001
9247
|
async function loadClassicRuntimePackage(root) {
|
|
9002
9248
|
if (await fileExists2(path10.join(root, "skill.yaml"))) {
|
|
@@ -9007,17 +9253,56 @@ async function loadClassicRuntimePackage(root) {
|
|
|
9007
9253
|
async function ensureClassicRuntimeRun(changeDir) {
|
|
9008
9254
|
const root = await classicRuntimeRoot();
|
|
9009
9255
|
return ensureClassicRun(changeDir, {
|
|
9010
|
-
skillPackage: await loadClassicRuntimePackage(root)
|
|
9256
|
+
skillPackage: root ? await loadClassicRuntimePackage(root) : embeddedClassicRuntimePackage(path10.dirname(fileURLToPath(import.meta.url)))
|
|
9011
9257
|
});
|
|
9012
9258
|
}
|
|
9013
9259
|
async function ensureStrictClassicRuntimeRun(changeDir) {
|
|
9014
9260
|
const projection = await readClassicState(changeDir);
|
|
9015
|
-
|
|
9261
|
+
const unknownKeys = Array.from(new Set(projection.unknownKeys)).sort();
|
|
9262
|
+
if (unknownKeys.length > 0) {
|
|
9263
|
+
throw new Error(`Invalid Classic state: unknown field(s): ${unknownKeys.join(", ")}`);
|
|
9264
|
+
}
|
|
9265
|
+
return ensureClassicRuntimeRun(changeDir);
|
|
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) {
|
|
9016
9282
|
throw new Error(
|
|
9017
|
-
`
|
|
9283
|
+
`Classic Run skill mismatch: expected ${skillPackage.definition.metadata.name}, got ${projection.run.skill}`
|
|
9018
9284
|
);
|
|
9019
9285
|
}
|
|
9020
|
-
|
|
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
|
+
};
|
|
9021
9306
|
}
|
|
9022
9307
|
async function transitionClassicRuntimeRun(changeDir, classic, run, data) {
|
|
9023
9308
|
const projection = await readClassicState(changeDir);
|
|
@@ -9081,6 +9366,7 @@ var CLASSIC_TRANSITION_EVENTS = [
|
|
|
9081
9366
|
"build-complete",
|
|
9082
9367
|
"verify-pass",
|
|
9083
9368
|
"verify-fail",
|
|
9369
|
+
"archive-confirm",
|
|
9084
9370
|
"archive-reopen",
|
|
9085
9371
|
"archived",
|
|
9086
9372
|
"preset-escalate"
|
|
@@ -9111,6 +9397,11 @@ var CLASSIC_TRANSITION_TABLE = {
|
|
|
9111
9397
|
from: "verify",
|
|
9112
9398
|
guardRefs: ["verification-failed"]
|
|
9113
9399
|
},
|
|
9400
|
+
"archive-confirm": {
|
|
9401
|
+
event: "archive-confirm",
|
|
9402
|
+
from: "archive",
|
|
9403
|
+
guardRefs: ["archive-final-confirmation"]
|
|
9404
|
+
},
|
|
9114
9405
|
"archive-reopen": {
|
|
9115
9406
|
event: "archive-reopen",
|
|
9116
9407
|
from: "archive",
|
|
@@ -9119,7 +9410,7 @@ var CLASSIC_TRANSITION_TABLE = {
|
|
|
9119
9410
|
archived: {
|
|
9120
9411
|
event: "archived",
|
|
9121
9412
|
from: "archive",
|
|
9122
|
-
guardRefs: ["verify-result-pass"]
|
|
9413
|
+
guardRefs: ["verify-result-pass", "archive-confirmed"]
|
|
9123
9414
|
},
|
|
9124
9415
|
"preset-escalate": {
|
|
9125
9416
|
event: "preset-escalate",
|
|
@@ -9167,6 +9458,7 @@ function applyClassicTransition(current, event, options = {}) {
|
|
|
9167
9458
|
setField(classic, effects, "verifyResult", "pass");
|
|
9168
9459
|
setField(classic, effects, "phase", "archive");
|
|
9169
9460
|
setField(classic, effects, "verifiedAt", dateOnly(now));
|
|
9461
|
+
setField(classic, effects, "archiveConfirmation", "pending");
|
|
9170
9462
|
} else if (event === "verify-fail") {
|
|
9171
9463
|
setField(classic, effects, "verifyResult", "fail");
|
|
9172
9464
|
setField(classic, effects, "phase", "build");
|
|
@@ -9180,15 +9472,25 @@ function applyClassicTransition(current, event, options = {}) {
|
|
|
9180
9472
|
setField(classic, effects, "classicProfile", "full");
|
|
9181
9473
|
setField(classic, effects, "phase", "design");
|
|
9182
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");
|
|
9183
9481
|
} else if (event === "archive-reopen") {
|
|
9184
9482
|
if (classic.archived) throw new Error(`Cannot apply ${event}: already archived`);
|
|
9185
9483
|
setField(classic, effects, "verifyResult", "pending");
|
|
9186
9484
|
setField(classic, effects, "phase", "verify");
|
|
9187
9485
|
setField(classic, effects, "verifiedAt", null);
|
|
9486
|
+
setField(classic, effects, "archiveConfirmation", null);
|
|
9188
9487
|
} else {
|
|
9189
9488
|
if (classic.verifyResult !== "pass") {
|
|
9190
9489
|
throw new Error(`Cannot apply ${event}: verifyResult must be pass`);
|
|
9191
9490
|
}
|
|
9491
|
+
if (classic.archiveConfirmation !== "confirmed") {
|
|
9492
|
+
throw new Error(`Cannot apply ${event}: archiveConfirmation must be confirmed`);
|
|
9493
|
+
}
|
|
9192
9494
|
setField(classic, effects, "archived", true);
|
|
9193
9495
|
}
|
|
9194
9496
|
return { classic, effects, definition };
|
|
@@ -9251,6 +9553,33 @@ function artifactsHash(artifacts) {
|
|
|
9251
9553
|
)
|
|
9252
9554
|
);
|
|
9253
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
|
+
}
|
|
9254
9583
|
async function findArchiveDir(change, preferred) {
|
|
9255
9584
|
if (await exists2(preferred)) return preferred;
|
|
9256
9585
|
const archiveRoot = "openspec/changes/archive";
|
|
@@ -9289,37 +9618,7 @@ async function annotateFrontmatter(output, file, archiveName, extraFields, dryRu
|
|
|
9289
9618
|
return;
|
|
9290
9619
|
}
|
|
9291
9620
|
const original = await fs11.readFile(file, "utf8");
|
|
9292
|
-
const
|
|
9293
|
-
let updated;
|
|
9294
|
-
if (firstLine === "---") {
|
|
9295
|
-
const lines = original.split(/\r?\n/u);
|
|
9296
|
-
const out = [];
|
|
9297
|
-
let sawFirst = false;
|
|
9298
|
-
for (const line of lines) {
|
|
9299
|
-
if (/^archived-with:/u.test(line)) continue;
|
|
9300
|
-
if (line === "---") {
|
|
9301
|
-
if (!sawFirst) {
|
|
9302
|
-
out.push(line);
|
|
9303
|
-
sawFirst = true;
|
|
9304
|
-
continue;
|
|
9305
|
-
}
|
|
9306
|
-
out.push(`archived-with: ${archiveName}`);
|
|
9307
|
-
if (extraFields) out.push(extraFields);
|
|
9308
|
-
out.push(line);
|
|
9309
|
-
} else {
|
|
9310
|
-
out.push(line);
|
|
9311
|
-
}
|
|
9312
|
-
}
|
|
9313
|
-
updated = `${out.join("\n")}
|
|
9314
|
-
`;
|
|
9315
|
-
} else {
|
|
9316
|
-
const header = ["---", `archived-with: ${archiveName}`];
|
|
9317
|
-
if (extraFields) header.push(extraFields);
|
|
9318
|
-
header.push("status: final", "---");
|
|
9319
|
-
updated = `${header.join("\n")}
|
|
9320
|
-
${original}`;
|
|
9321
|
-
if (!updated.endsWith("\n")) updated += "\n";
|
|
9322
|
-
}
|
|
9621
|
+
const updated = annotatedMarkdown(original, archiveName, extraFields);
|
|
9323
9622
|
await fs11.writeFile(file, updated);
|
|
9324
9623
|
output.stderr.push(green(` [OK] Annotated: ${file}`));
|
|
9325
9624
|
output.stepsOk += 1;
|
|
@@ -9404,6 +9703,13 @@ var classicArchiveCommand = async (args) => {
|
|
|
9404
9703
|
if (runtime.run.pending && runtime.run.pending !== actionId) {
|
|
9405
9704
|
throw new ArchiveFailure(red(`FATAL: another action is pending: ${runtime.run.pending}`));
|
|
9406
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
|
+
}
|
|
9407
9713
|
if (!recovering) {
|
|
9408
9714
|
const action = {
|
|
9409
9715
|
id: actionId,
|
|
@@ -9475,7 +9781,10 @@ var classicArchiveCommand = async (args) => {
|
|
|
9475
9781
|
archive_directory: archiveDir
|
|
9476
9782
|
};
|
|
9477
9783
|
await writeArtifacts(archiveDir, archivedProjection.run.artifactsRef, artifacts);
|
|
9478
|
-
const archiveTransition = applyClassicTransition(
|
|
9784
|
+
const archiveTransition = applyClassicTransition(
|
|
9785
|
+
recovering && archivedProjection.classic.archiveConfirmation !== "confirmed" ? { ...archivedProjection.classic, archiveConfirmation: "confirmed" } : archivedProjection.classic,
|
|
9786
|
+
"archived"
|
|
9787
|
+
);
|
|
9479
9788
|
const archivedClassic = archiveTransition.classic;
|
|
9480
9789
|
let transitionedRun = archivedProjection.run;
|
|
9481
9790
|
if (archivedProjection.run.currentStep !== "completed" || archivedProjection.run.status !== "completed") {
|
|
@@ -9573,11 +9882,98 @@ var classicArchiveCommand = async (args) => {
|
|
|
9573
9882
|
};
|
|
9574
9883
|
|
|
9575
9884
|
// domains/comet-classic/classic-guard.ts
|
|
9576
|
-
var
|
|
9885
|
+
var import_yaml5 = __toESM(require_dist(), 1);
|
|
9577
9886
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
9578
9887
|
import { createHash as createHash4 } from "crypto";
|
|
9579
|
-
import { existsSync, promises as
|
|
9580
|
-
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
|
+
}
|
|
9581
9977
|
|
|
9582
9978
|
// domains/comet-classic/classic-runtime-evals.ts
|
|
9583
9979
|
var STEP_EVIDENCE = {
|
|
@@ -9672,7 +10068,7 @@ async function inspectClassicChange(changeDir, name) {
|
|
|
9672
10068
|
// domains/comet-classic/classic-validate-command.ts
|
|
9673
10069
|
var import_yaml3 = __toESM(require_dist(), 1);
|
|
9674
10070
|
import { promises as fs12 } from "fs";
|
|
9675
|
-
import
|
|
10071
|
+
import path14 from "path";
|
|
9676
10072
|
var GREEN2 = "\x1B[32m";
|
|
9677
10073
|
var RED2 = "\x1B[31m";
|
|
9678
10074
|
var YELLOW2 = "\x1B[33m";
|
|
@@ -9704,6 +10100,7 @@ var ENUMS = {
|
|
|
9704
10100
|
auto_transition: ["true", "false"],
|
|
9705
10101
|
verify_result: ["pending", "pass", "fail"],
|
|
9706
10102
|
branch_status: ["pending", "handled"],
|
|
10103
|
+
archive_confirmation: ["pending", "confirmed"],
|
|
9707
10104
|
archived: ["true", "false"],
|
|
9708
10105
|
direct_override: ["true", "false"],
|
|
9709
10106
|
classic_profile: ["full", "hotfix", "tweak"],
|
|
@@ -9742,7 +10139,7 @@ var classicValidateCommand = async (args) => {
|
|
|
9742
10139
|
};
|
|
9743
10140
|
}
|
|
9744
10141
|
const { directory, label } = await resolveClassicChangeDirectory(name);
|
|
9745
|
-
const yamlFile =
|
|
10142
|
+
const yamlFile = path14.join(directory, ".comet.yaml");
|
|
9746
10143
|
const lines = [`[VALIDATE] ${label}/.comet.yaml`];
|
|
9747
10144
|
let errors = 0;
|
|
9748
10145
|
let warnings = 0;
|
|
@@ -9793,7 +10190,7 @@ var classicValidateCommand = async (args) => {
|
|
|
9793
10190
|
}
|
|
9794
10191
|
for (const field2 of ["design_doc", "plan", "handoff_context"]) {
|
|
9795
10192
|
const value = text(record[field2]);
|
|
9796
|
-
if (value && !await exists3(
|
|
10193
|
+
if (value && !await exists3(path14.resolve(value))) {
|
|
9797
10194
|
fail3(`${field2}='${value}' does not exist on disk`);
|
|
9798
10195
|
}
|
|
9799
10196
|
}
|
|
@@ -9815,6 +10212,62 @@ var classicValidateCommand = async (args) => {
|
|
|
9815
10212
|
return { exitCode: 0, stderr: lines.join("\n") };
|
|
9816
10213
|
};
|
|
9817
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
|
+
|
|
9818
10271
|
// domains/comet-classic/classic-guard.ts
|
|
9819
10272
|
var GREEN3 = "\x1B[32m";
|
|
9820
10273
|
var RED3 = "\x1B[31m";
|
|
@@ -9879,7 +10332,7 @@ var GuardOutput = class {
|
|
|
9879
10332
|
};
|
|
9880
10333
|
async function exists4(file) {
|
|
9881
10334
|
try {
|
|
9882
|
-
await
|
|
10335
|
+
await fs15.access(file);
|
|
9883
10336
|
return true;
|
|
9884
10337
|
} catch (error) {
|
|
9885
10338
|
if (error.code === "ENOENT") return false;
|
|
@@ -9888,7 +10341,7 @@ async function exists4(file) {
|
|
|
9888
10341
|
}
|
|
9889
10342
|
async function nonempty(file) {
|
|
9890
10343
|
try {
|
|
9891
|
-
return (await
|
|
10344
|
+
return (await fs15.stat(file)).size > 0;
|
|
9892
10345
|
} catch (error) {
|
|
9893
10346
|
if (error.code === "ENOENT") return false;
|
|
9894
10347
|
throw error;
|
|
@@ -9901,32 +10354,9 @@ function validateChangeName2(name) {
|
|
|
9901
10354
|
async function resolveChangeDir(name) {
|
|
9902
10355
|
return (await resolveClassicChangeDirectory(name)).label;
|
|
9903
10356
|
}
|
|
9904
|
-
function stripInlineComment(value) {
|
|
9905
|
-
let out = "";
|
|
9906
|
-
let quote = "";
|
|
9907
|
-
for (let i = 0; i < value.length; i += 1) {
|
|
9908
|
-
const c = value[i];
|
|
9909
|
-
if (quote === "") {
|
|
9910
|
-
if (c === '"' || c === "'") {
|
|
9911
|
-
quote = c;
|
|
9912
|
-
} else if (c === "#" && (i === 0 || /\s/u.test(value[i - 1]))) {
|
|
9913
|
-
return out.replace(/\s+$/u, "");
|
|
9914
|
-
}
|
|
9915
|
-
} else if (c === quote) {
|
|
9916
|
-
quote = "";
|
|
9917
|
-
}
|
|
9918
|
-
out += c;
|
|
9919
|
-
}
|
|
9920
|
-
return out;
|
|
9921
|
-
}
|
|
9922
|
-
function stripWrappingQuotes(value) {
|
|
9923
|
-
if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) return value.slice(1, -1);
|
|
9924
|
-
if (value.length >= 2 && value.startsWith("'") && value.endsWith("'")) return value.slice(1, -1);
|
|
9925
|
-
return value;
|
|
9926
|
-
}
|
|
9927
10357
|
async function readField(changeDir, field2) {
|
|
9928
|
-
const file =
|
|
9929
|
-
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 });
|
|
9930
10360
|
if (document.errors.length > 0) {
|
|
9931
10361
|
throw new GuardFailure(`ERROR: Invalid .comet.yaml: ${document.errors[0].message}`);
|
|
9932
10362
|
}
|
|
@@ -9939,24 +10369,7 @@ async function readField(changeDir, field2) {
|
|
|
9939
10369
|
async function projectConfigValue(field2, changeDir) {
|
|
9940
10370
|
const changeValue = await readField(changeDir, field2);
|
|
9941
10371
|
if (changeValue && changeValue !== "null") return changeValue;
|
|
9942
|
-
|
|
9943
|
-
".comet/config.yaml",
|
|
9944
|
-
".comet.yaml",
|
|
9945
|
-
"comet.yaml",
|
|
9946
|
-
".comet.yml",
|
|
9947
|
-
"comet.yml"
|
|
9948
|
-
]) {
|
|
9949
|
-
if (!await exists4(config)) continue;
|
|
9950
|
-
for (const line of (await fs13.readFile(config, "utf8")).split(/\r?\n/u)) {
|
|
9951
|
-
if (new RegExp(`^${field2}:`, "u").test(line)) {
|
|
9952
|
-
const value = stripWrappingQuotes(
|
|
9953
|
-
stripInlineComment(line.replace(new RegExp(`^${field2}:\\s*`, "u"), ""))
|
|
9954
|
-
);
|
|
9955
|
-
if (value && value !== "null") return value;
|
|
9956
|
-
}
|
|
9957
|
-
}
|
|
9958
|
-
}
|
|
9959
|
-
return "";
|
|
10372
|
+
return (await readClassicConfigValue(field2))?.value ?? "";
|
|
9960
10373
|
}
|
|
9961
10374
|
async function configuredLanguage(changeDir) {
|
|
9962
10375
|
const language = await projectConfigValue("language", changeDir);
|
|
@@ -9984,7 +10397,7 @@ function countEnglishWords(source) {
|
|
|
9984
10397
|
}
|
|
9985
10398
|
async function documentLanguageMatchesConfigured(changeDir, file) {
|
|
9986
10399
|
const language = await configuredLanguage(changeDir);
|
|
9987
|
-
const source = stripFencedCodeBlocks(await
|
|
10400
|
+
const source = stripFencedCodeBlocks(await fs15.readFile(file, "utf8"));
|
|
9988
10401
|
const cjk = countCjkChars(source);
|
|
9989
10402
|
const englishWords = countEnglishWords(source);
|
|
9990
10403
|
if (language === "zh-CN" && cjk < 20 && englishWords >= 20) {
|
|
@@ -10001,68 +10414,6 @@ Next: regenerate or rewrite this artifact in English while preserving necessary
|
|
|
10001
10414
|
}
|
|
10002
10415
|
return pass();
|
|
10003
10416
|
}
|
|
10004
|
-
function runCommandString(command) {
|
|
10005
|
-
if (!command) return { status: 1, output: red2("ERROR: build/verify command is empty") };
|
|
10006
|
-
const split = splitCommandChain(command);
|
|
10007
|
-
if (typeof split === "string") {
|
|
10008
|
-
return {
|
|
10009
|
-
status: 1,
|
|
10010
|
-
output: `${red2(`ERROR: build/verify command contains shell metacharacters: ${command}`)}
|
|
10011
|
-
${red2(
|
|
10012
|
-
split
|
|
10013
|
-
)}`
|
|
10014
|
-
};
|
|
10015
|
-
}
|
|
10016
|
-
const output = [];
|
|
10017
|
-
for (const part of split) {
|
|
10018
|
-
const segment = part.trim();
|
|
10019
|
-
if (!segment) {
|
|
10020
|
-
return { status: 1, output: red2("ERROR: build/verify command contains an empty && step") };
|
|
10021
|
-
}
|
|
10022
|
-
const result3 = spawnSync2(segment, { shell: true, encoding: "utf8", timeout: 3e5 });
|
|
10023
|
-
const combined = `${result3.stdout ?? ""}${result3.stderr ?? ""}`.replace(/\n+$/u, "");
|
|
10024
|
-
output.push(`${red2(`+ ${segment}`)}${combined ? `
|
|
10025
|
-
${combined}` : ""}`);
|
|
10026
|
-
if (result3.status !== 0) {
|
|
10027
|
-
return { status: result3.status ?? 1, output: output.join("\n") };
|
|
10028
|
-
}
|
|
10029
|
-
}
|
|
10030
|
-
return { status: 0, output: output.join("\n") };
|
|
10031
|
-
}
|
|
10032
|
-
function splitCommandChain(command) {
|
|
10033
|
-
const parts = [];
|
|
10034
|
-
let current = "";
|
|
10035
|
-
let quote = "";
|
|
10036
|
-
for (let i = 0; i < command.length; i += 1) {
|
|
10037
|
-
const c = command[i];
|
|
10038
|
-
if (c === "$" || c === "`") {
|
|
10039
|
-
return "Allowed: command words, quotes, paths, and && between sequential commands";
|
|
10040
|
-
}
|
|
10041
|
-
if (quote) {
|
|
10042
|
-
current += c;
|
|
10043
|
-
if (c === quote) quote = "";
|
|
10044
|
-
continue;
|
|
10045
|
-
}
|
|
10046
|
-
if (c === '"' || c === "'") {
|
|
10047
|
-
quote = c;
|
|
10048
|
-
current += c;
|
|
10049
|
-
continue;
|
|
10050
|
-
}
|
|
10051
|
-
if (c === "&" && command[i + 1] === "&") {
|
|
10052
|
-
parts.push(current);
|
|
10053
|
-
current = "";
|
|
10054
|
-
i += 1;
|
|
10055
|
-
continue;
|
|
10056
|
-
}
|
|
10057
|
-
if (c === ";" || c === "|" || c === "&") {
|
|
10058
|
-
return "Allowed: command words, quotes, paths, and && between sequential commands";
|
|
10059
|
-
}
|
|
10060
|
-
current += c;
|
|
10061
|
-
}
|
|
10062
|
-
if (quote) return "Command has an unmatched quote";
|
|
10063
|
-
parts.push(current);
|
|
10064
|
-
return parts;
|
|
10065
|
-
}
|
|
10066
10417
|
function hashFile(file) {
|
|
10067
10418
|
return createHash4("sha256").update(readFileSync(file)).digest("hex");
|
|
10068
10419
|
}
|
|
@@ -10070,7 +10421,7 @@ async function handoffSourceFiles(changeDir) {
|
|
|
10070
10421
|
const files = [`${changeDir}/proposal.md`, `${changeDir}/design.md`, `${changeDir}/tasks.md`];
|
|
10071
10422
|
const specs = `${changeDir}/specs`;
|
|
10072
10423
|
if (await exists4(specs)) {
|
|
10073
|
-
for (const entry2 of (await
|
|
10424
|
+
for (const entry2 of (await fs15.readdir(specs)).sort()) {
|
|
10074
10425
|
const spec = `${specs}/${entry2}/spec.md`;
|
|
10075
10426
|
if (await exists4(spec)) files.push(spec);
|
|
10076
10427
|
}
|
|
@@ -10090,20 +10441,30 @@ async function preflight(changeDir, name) {
|
|
|
10090
10441
|
if (!await exists4(changeDir)) {
|
|
10091
10442
|
throw new GuardFailure(red2(`FATAL: change directory not found: ${changeDir}`));
|
|
10092
10443
|
}
|
|
10093
|
-
if (!await exists4(
|
|
10444
|
+
if (!await exists4(path16.join(changeDir, ".comet.yaml"))) {
|
|
10094
10445
|
throw new GuardFailure(red2(`FATAL: .comet.yaml not found in ${changeDir}`));
|
|
10095
10446
|
}
|
|
10096
|
-
const
|
|
10097
|
-
if (
|
|
10098
|
-
if (
|
|
10099
|
-
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}
|
|
10100
10451
|
`);
|
|
10101
10452
|
throw new GuardFailure(red2("FATAL: .comet.yaml schema validation failed"));
|
|
10102
10453
|
}
|
|
10454
|
+
const projection = await readClassicState(changeDir);
|
|
10455
|
+
const unknownKeys = Array.from(new Set(projection.unknownKeys)).sort();
|
|
10456
|
+
if (unknownKeys.length > 0) {
|
|
10457
|
+
throw new GuardFailure(
|
|
10458
|
+
red2(`FATAL: .comet.yaml has unknown field(s): ${unknownKeys.join(", ")}`)
|
|
10459
|
+
);
|
|
10460
|
+
}
|
|
10103
10461
|
}
|
|
10104
10462
|
function pushCheck(output, outcome) {
|
|
10105
10463
|
if (outcome.passed) {
|
|
10106
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
|
+
}
|
|
10107
10468
|
} else {
|
|
10108
10469
|
output.stderr.push(red2(` [FAIL] ${outcome.description}`));
|
|
10109
10470
|
if (outcome.detail) {
|
|
@@ -10114,11 +10475,11 @@ function pushCheck(output, outcome) {
|
|
|
10114
10475
|
function check(description, run) {
|
|
10115
10476
|
return async () => {
|
|
10116
10477
|
try {
|
|
10117
|
-
const
|
|
10478
|
+
const result5 = await run();
|
|
10118
10479
|
return {
|
|
10119
10480
|
description,
|
|
10120
|
-
passed:
|
|
10121
|
-
detail: ("detail" in
|
|
10481
|
+
passed: result5.passed,
|
|
10482
|
+
detail: ("detail" in result5 ? result5.detail : "") ?? ""
|
|
10122
10483
|
};
|
|
10123
10484
|
} catch (error) {
|
|
10124
10485
|
return {
|
|
@@ -10129,8 +10490,8 @@ function check(description, run) {
|
|
|
10129
10490
|
}
|
|
10130
10491
|
};
|
|
10131
10492
|
}
|
|
10132
|
-
function pass() {
|
|
10133
|
-
return { passed: true };
|
|
10493
|
+
function pass(detail) {
|
|
10494
|
+
return { passed: true, ...detail ? { detail } : {} };
|
|
10134
10495
|
}
|
|
10135
10496
|
function fail(detail) {
|
|
10136
10497
|
return { passed: false, detail };
|
|
@@ -10144,46 +10505,103 @@ async function runChecks(output, builders) {
|
|
|
10144
10505
|
}
|
|
10145
10506
|
return blocked2;
|
|
10146
10507
|
}
|
|
10508
|
+
var INFERRED_COMMAND_SOURCES = [
|
|
10509
|
+
"package.json with a build script",
|
|
10510
|
+
"pom.xml",
|
|
10511
|
+
"Cargo.toml"
|
|
10512
|
+
];
|
|
10513
|
+
async function removedProjectCommandField(field2) {
|
|
10514
|
+
const config = path16.join(".comet", "config.yaml");
|
|
10515
|
+
if (!await exists4(config)) return false;
|
|
10516
|
+
const document = (0, import_yaml5.parseDocument)(await fs15.readFile(config, "utf8"));
|
|
10517
|
+
if (document.errors.length > 0) {
|
|
10518
|
+
throw new Error(
|
|
10519
|
+
`.comet/config.yaml is invalid YAML (${document.errors[0].message}); cannot check for removed "${field2}" field. Fix the config and retry.`
|
|
10520
|
+
);
|
|
10521
|
+
}
|
|
10522
|
+
const value = document.toJS();
|
|
10523
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value) && Object.prototype.hasOwnProperty.call(value, field2);
|
|
10524
|
+
}
|
|
10525
|
+
function removedProjectCommandRun(field2) {
|
|
10526
|
+
return {
|
|
10527
|
+
status: 1,
|
|
10528
|
+
output: `${field2} has been removed from .comet/config.yaml. Delete this field and run any required ${field2 === "build_command" ? "build" : "verification"} command manually before retrying.`
|
|
10529
|
+
};
|
|
10530
|
+
}
|
|
10147
10531
|
function runInferred(command) {
|
|
10148
|
-
const
|
|
10532
|
+
const result5 = spawnSync2(command, { shell: true, encoding: "utf8", timeout: 3e5 });
|
|
10149
10533
|
return {
|
|
10150
|
-
status:
|
|
10151
|
-
output: `${
|
|
10534
|
+
status: result5.status ?? 1,
|
|
10535
|
+
output: `${result5.stdout ?? ""}${result5.stderr ?? ""}`.replace(/\n+$/u, "")
|
|
10152
10536
|
};
|
|
10153
10537
|
}
|
|
10154
|
-
async function
|
|
10155
|
-
if (
|
|
10156
|
-
|
|
10157
|
-
|
|
10158
|
-
|
|
10159
|
-
return
|
|
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";
|
|
10160
10544
|
}
|
|
10161
10545
|
if (await exists4("pom.xml")) {
|
|
10162
10546
|
if (process.platform === "win32") {
|
|
10163
|
-
if (existsSync("mvnw.cmd")) return
|
|
10164
|
-
return
|
|
10547
|
+
if (existsSync("mvnw.cmd")) return "mvnw.cmd compile -q";
|
|
10548
|
+
return "mvn.cmd compile -q";
|
|
10165
10549
|
}
|
|
10166
|
-
if (existsSync("mvnw")) return
|
|
10167
|
-
return
|
|
10550
|
+
if (existsSync("mvnw")) return "./mvnw compile -q";
|
|
10551
|
+
return "mvn compile -q";
|
|
10168
10552
|
}
|
|
10169
|
-
if (await exists4("Cargo.toml")) return
|
|
10170
|
-
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}`;
|
|
10171
10558
|
}
|
|
10172
|
-
|
|
10173
|
-
|
|
10174
|
-
|
|
10175
|
-
|
|
10176
|
-
|
|
10559
|
+
function recoveryCommand(change, scope, command) {
|
|
10560
|
+
return `comet state record-check ${change} ${scope} --command "${command}" --exit-code 0`;
|
|
10561
|
+
}
|
|
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" };
|
|
10565
|
+
}
|
|
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) };
|
|
10177
10595
|
}
|
|
10178
10596
|
async function tasksAllDone(changeDir) {
|
|
10179
|
-
const tasks =
|
|
10597
|
+
const tasks = path16.join(changeDir, "tasks.md");
|
|
10180
10598
|
if (!await exists4(tasks)) {
|
|
10181
10599
|
return fail(
|
|
10182
10600
|
`tasks.md is missing at ${tasks}
|
|
10183
10601
|
Next: restore or create tasks.md for this change before leaving build.`
|
|
10184
10602
|
);
|
|
10185
10603
|
}
|
|
10186
|
-
const source = await
|
|
10604
|
+
const source = await fs15.readFile(tasks, "utf8");
|
|
10187
10605
|
if (!/- \[x\]/u.test(source)) {
|
|
10188
10606
|
return fail(
|
|
10189
10607
|
"tasks.md has no completed tasks.\nNext: complete implementation tasks and mark them with '- [x]'."
|
|
@@ -10200,9 +10618,9 @@ Next: complete or explicitly remove unfinished tasks, then mark tasks.md with '-
|
|
|
10200
10618
|
return pass();
|
|
10201
10619
|
}
|
|
10202
10620
|
async function tasksHasAny(changeDir) {
|
|
10203
|
-
const tasks =
|
|
10621
|
+
const tasks = path16.join(changeDir, "tasks.md");
|
|
10204
10622
|
if (!await exists4(tasks)) return false;
|
|
10205
|
-
return /- \[/u.test(await
|
|
10623
|
+
return /- \[/u.test(await fs15.readFile(tasks, "utf8"));
|
|
10206
10624
|
}
|
|
10207
10625
|
async function planTasksAllDone(changeDir) {
|
|
10208
10626
|
const plan = await readField(changeDir, "plan");
|
|
@@ -10213,7 +10631,7 @@ async function planTasksAllDone(changeDir) {
|
|
|
10213
10631
|
Next: restore the Superpowers plan file or update .comet.yaml plan before leaving build.`
|
|
10214
10632
|
);
|
|
10215
10633
|
}
|
|
10216
|
-
const source = await
|
|
10634
|
+
const source = await fs15.readFile(plan, "utf8");
|
|
10217
10635
|
const unfinished = source.split(/\r?\n/u).map((line, index) => ({ line, number: index + 1 })).filter((entry2) => /^\s*- \[ \]/u.test(entry2.line));
|
|
10218
10636
|
if (unfinished.length > 0) {
|
|
10219
10637
|
return fail(
|
|
@@ -10302,7 +10720,7 @@ async function archivedIsTrue(changeDir) {
|
|
|
10302
10720
|
return await readField(changeDir, "archived") === "true";
|
|
10303
10721
|
}
|
|
10304
10722
|
async function designDocFrontmatterHas(designDoc, field2, expected) {
|
|
10305
|
-
const source = (await
|
|
10723
|
+
const source = (await fs15.readFile(designDoc, "utf8")).replace(/^\uFEFF/u, "");
|
|
10306
10724
|
let inFrontmatter = false;
|
|
10307
10725
|
for (const line of source.split(/\r?\n/u)) {
|
|
10308
10726
|
if (!inFrontmatter) {
|
|
@@ -10367,7 +10785,7 @@ async function designHandoffMarkdownTraceable(changeDir) {
|
|
|
10367
10785
|
const markdown = `${context.replace(/\.json$/u, "")}.md`;
|
|
10368
10786
|
if (!await nonempty(markdown))
|
|
10369
10787
|
return fail(`design handoff markdown is missing or empty: ${markdown}`);
|
|
10370
|
-
const source = await
|
|
10788
|
+
const source = await fs15.readFile(markdown, "utf8");
|
|
10371
10789
|
const problems = [];
|
|
10372
10790
|
if (!/^Generated-by: comet-handoff\.sh$/mu.test(source)) {
|
|
10373
10791
|
problems.push("handoff markdown is missing Generated-by marker");
|
|
@@ -10394,7 +10812,7 @@ async function betaSpecJsonStructurallyValid(changeDir) {
|
|
|
10394
10812
|
const context = await readField(changeDir, "handoff_context");
|
|
10395
10813
|
if (!context || context === "null") return fail("handoff_context is missing from .comet.yaml");
|
|
10396
10814
|
if (!await nonempty(context)) return fail(`spec-context.json is missing or empty: ${context}`);
|
|
10397
|
-
const source = await
|
|
10815
|
+
const source = await fs15.readFile(context, "utf8");
|
|
10398
10816
|
const problems = [];
|
|
10399
10817
|
let parsed;
|
|
10400
10818
|
try {
|
|
@@ -10431,19 +10849,19 @@ async function guardOpenChecks(output, changeDir) {
|
|
|
10431
10849
|
const checks = [
|
|
10432
10850
|
check(
|
|
10433
10851
|
"proposal.md exists and non-empty",
|
|
10434
|
-
async () => await nonempty(
|
|
10852
|
+
async () => await nonempty(path16.join(changeDir, "proposal.md")) ? pass() : fail("")
|
|
10435
10853
|
),
|
|
10436
10854
|
check(
|
|
10437
10855
|
"proposal.md matches configured language",
|
|
10438
|
-
() => documentLanguageMatchesConfigured(changeDir,
|
|
10856
|
+
() => documentLanguageMatchesConfigured(changeDir, path16.join(changeDir, "proposal.md"))
|
|
10439
10857
|
),
|
|
10440
10858
|
check(
|
|
10441
10859
|
"tasks.md exists and non-empty",
|
|
10442
|
-
async () => await nonempty(
|
|
10860
|
+
async () => await nonempty(path16.join(changeDir, "tasks.md")) ? pass() : fail("")
|
|
10443
10861
|
),
|
|
10444
10862
|
check(
|
|
10445
10863
|
"tasks.md matches configured language",
|
|
10446
|
-
() => documentLanguageMatchesConfigured(changeDir,
|
|
10864
|
+
() => documentLanguageMatchesConfigured(changeDir, path16.join(changeDir, "tasks.md"))
|
|
10447
10865
|
),
|
|
10448
10866
|
check(
|
|
10449
10867
|
"tasks.md has at least one task",
|
|
@@ -10456,11 +10874,11 @@ async function guardOpenChecks(output, changeDir) {
|
|
|
10456
10874
|
0,
|
|
10457
10875
|
check(
|
|
10458
10876
|
"design.md exists and non-empty",
|
|
10459
|
-
async () => await nonempty(
|
|
10877
|
+
async () => await nonempty(path16.join(changeDir, "design.md")) ? pass() : fail("")
|
|
10460
10878
|
),
|
|
10461
10879
|
check(
|
|
10462
10880
|
"design.md matches configured language",
|
|
10463
|
-
() => documentLanguageMatchesConfigured(changeDir,
|
|
10881
|
+
() => documentLanguageMatchesConfigured(changeDir, path16.join(changeDir, "design.md"))
|
|
10464
10882
|
)
|
|
10465
10883
|
);
|
|
10466
10884
|
}
|
|
@@ -10472,27 +10890,27 @@ async function guardDesignChecks(output, changeDir, change) {
|
|
|
10472
10890
|
const builders = [
|
|
10473
10891
|
check(
|
|
10474
10892
|
"proposal.md exists",
|
|
10475
|
-
async () => await nonempty(
|
|
10893
|
+
async () => await nonempty(path16.join(changeDir, "proposal.md")) ? pass() : fail("")
|
|
10476
10894
|
),
|
|
10477
10895
|
check(
|
|
10478
10896
|
"proposal.md matches configured language",
|
|
10479
|
-
() => documentLanguageMatchesConfigured(changeDir,
|
|
10897
|
+
() => documentLanguageMatchesConfigured(changeDir, path16.join(changeDir, "proposal.md"))
|
|
10480
10898
|
),
|
|
10481
10899
|
check(
|
|
10482
10900
|
"design.md exists",
|
|
10483
|
-
async () => await nonempty(
|
|
10901
|
+
async () => await nonempty(path16.join(changeDir, "design.md")) ? pass() : fail("")
|
|
10484
10902
|
),
|
|
10485
10903
|
check(
|
|
10486
10904
|
"design.md matches configured language",
|
|
10487
|
-
() => documentLanguageMatchesConfigured(changeDir,
|
|
10905
|
+
() => documentLanguageMatchesConfigured(changeDir, path16.join(changeDir, "design.md"))
|
|
10488
10906
|
),
|
|
10489
10907
|
check(
|
|
10490
10908
|
"tasks.md exists",
|
|
10491
|
-
async () => await nonempty(
|
|
10909
|
+
async () => await nonempty(path16.join(changeDir, "tasks.md")) ? pass() : fail("")
|
|
10492
10910
|
),
|
|
10493
10911
|
check(
|
|
10494
10912
|
"tasks.md matches configured language",
|
|
10495
|
-
() => documentLanguageMatchesConfigured(changeDir,
|
|
10913
|
+
() => documentLanguageMatchesConfigured(changeDir, path16.join(changeDir, "tasks.md"))
|
|
10496
10914
|
),
|
|
10497
10915
|
check("design handoff context exists", () => designHandoffContextValid(changeDir, change)),
|
|
10498
10916
|
check("design handoff markdown is traceable", () => designHandoffMarkdownTraceable(changeDir))
|
|
@@ -10541,7 +10959,7 @@ async function guardDesignChecks(output, changeDir, change) {
|
|
|
10541
10959
|
}
|
|
10542
10960
|
return blocked2;
|
|
10543
10961
|
}
|
|
10544
|
-
async function guardBuildChecks(output, changeDir, change) {
|
|
10962
|
+
async function guardBuildChecks(output, changeDir, change, run) {
|
|
10545
10963
|
return runChecks(output, [
|
|
10546
10964
|
check("isolation selected", () => isolationSelected(changeDir, change)),
|
|
10547
10965
|
check("build_mode selected", () => buildModeSelected(changeDir, change)),
|
|
@@ -10553,11 +10971,11 @@ async function guardBuildChecks(output, changeDir, change) {
|
|
|
10553
10971
|
check("Superpowers plan all tasks checked", () => planTasksAllDone(changeDir)),
|
|
10554
10972
|
check(
|
|
10555
10973
|
"proposal.md exists",
|
|
10556
|
-
async () => await nonempty(
|
|
10974
|
+
async () => await nonempty(path16.join(changeDir, "proposal.md")) ? pass() : fail("")
|
|
10557
10975
|
),
|
|
10558
10976
|
check(
|
|
10559
10977
|
"proposal.md matches configured language",
|
|
10560
|
-
() => documentLanguageMatchesConfigured(changeDir,
|
|
10978
|
+
() => documentLanguageMatchesConfigured(changeDir, path16.join(changeDir, "proposal.md"))
|
|
10561
10979
|
),
|
|
10562
10980
|
check("Superpowers plan matches configured language", async () => {
|
|
10563
10981
|
const plan = await readField(changeDir, "plan");
|
|
@@ -10567,19 +10985,19 @@ async function guardBuildChecks(output, changeDir, change) {
|
|
|
10567
10985
|
// Build check runs last — only after all config checks pass — to avoid
|
|
10568
10986
|
// wasting time on a build that would be rejected by a config failure.
|
|
10569
10987
|
check("Build passes", async () => {
|
|
10570
|
-
const buildResult = await
|
|
10571
|
-
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);
|
|
10572
10990
|
})
|
|
10573
10991
|
]);
|
|
10574
10992
|
}
|
|
10575
|
-
async function guardVerifyChecks(output, changeDir) {
|
|
10993
|
+
async function guardVerifyChecks(output, changeDir, change, run) {
|
|
10576
10994
|
return runChecks(output, [
|
|
10577
10995
|
check("tasks.md all tasks checked", () => tasksAllDone(changeDir)),
|
|
10578
10996
|
// Verification command runs after tasks check — no point running tests
|
|
10579
10997
|
// if tasks.md is incomplete.
|
|
10580
10998
|
check("Verification passes", async () => {
|
|
10581
|
-
const verifyResult = await
|
|
10582
|
-
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);
|
|
10583
11001
|
}),
|
|
10584
11002
|
check(
|
|
10585
11003
|
"verification_report exists",
|
|
@@ -10601,11 +11019,11 @@ async function guardArchiveChecks(output, changeDir) {
|
|
|
10601
11019
|
check("archived is true", async () => await archivedIsTrue(changeDir) ? pass() : fail("")),
|
|
10602
11020
|
check(
|
|
10603
11021
|
"proposal.md exists",
|
|
10604
|
-
async () => await nonempty(
|
|
11022
|
+
async () => await nonempty(path16.join(changeDir, "proposal.md")) ? pass() : fail("")
|
|
10605
11023
|
),
|
|
10606
11024
|
check(
|
|
10607
11025
|
"design.md exists",
|
|
10608
|
-
async () => await nonempty(
|
|
11026
|
+
async () => await nonempty(path16.join(changeDir, "design.md")) ? pass() : fail("")
|
|
10609
11027
|
),
|
|
10610
11028
|
check("tasks.md all tasks checked", () => tasksAllDone(changeDir))
|
|
10611
11029
|
]);
|
|
@@ -10613,8 +11031,8 @@ async function guardArchiveChecks(output, changeDir) {
|
|
|
10613
11031
|
async function applyStateUpdate(output, change, changeDir, phase, context) {
|
|
10614
11032
|
const event = CLASSIC_GUARD_TRANSITION_EVENT[phase];
|
|
10615
11033
|
if (!event) return;
|
|
10616
|
-
const
|
|
10617
|
-
await transitionClassicRuntimeRun(changeDir,
|
|
11034
|
+
const result5 = applyClassicTransition(context.classic, event);
|
|
11035
|
+
await transitionClassicRuntimeRun(changeDir, result5.classic, context.run, {
|
|
10618
11036
|
event,
|
|
10619
11037
|
phase,
|
|
10620
11038
|
source: "comet-guard"
|
|
@@ -10624,15 +11042,15 @@ async function applyStateUpdate(output, change, changeDir, phase, context) {
|
|
|
10624
11042
|
event,
|
|
10625
11043
|
source: "comet-guard",
|
|
10626
11044
|
from: context.classic,
|
|
10627
|
-
to:
|
|
10628
|
-
effects:
|
|
11045
|
+
to: result5.classic,
|
|
11046
|
+
effects: result5.effects
|
|
10629
11047
|
});
|
|
10630
|
-
for (const effect of
|
|
11048
|
+
for (const effect of result5.effects) {
|
|
10631
11049
|
output.stderr.push(green2(`[SET] ${wireField(effect.field)}=${wireValue(effect.to)}`));
|
|
10632
11050
|
}
|
|
10633
11051
|
output.stderr.push(green2(`[TRANSITION] ${event}`));
|
|
10634
11052
|
const template = APPLY_MESSAGE[phase];
|
|
10635
|
-
const message = phase === "open" ? template.replace("PLACEHOLDER",
|
|
11053
|
+
const message = phase === "open" ? template.replace("PLACEHOLDER", result5.classic.phase) : template;
|
|
10636
11054
|
output.stderr.push(green2(message));
|
|
10637
11055
|
}
|
|
10638
11056
|
var classicGuardCommand = async (args, options) => {
|
|
@@ -10662,8 +11080,10 @@ Valid phases: open, design, build, verify, archive`
|
|
|
10662
11080
|
let blocked2;
|
|
10663
11081
|
if (phase === "open") blocked2 = await guardOpenChecks(output, changeDir);
|
|
10664
11082
|
else if (phase === "design") blocked2 = await guardDesignChecks(output, changeDir, change);
|
|
10665
|
-
else if (phase === "build")
|
|
10666
|
-
|
|
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);
|
|
10667
11087
|
else blocked2 = await guardArchiveChecks(output, changeDir);
|
|
10668
11088
|
if (blocked2) {
|
|
10669
11089
|
output.stderr.push("");
|
|
@@ -10686,10 +11106,10 @@ Valid phases: open, design, build, verify, archive`
|
|
|
10686
11106
|
};
|
|
10687
11107
|
|
|
10688
11108
|
// domains/comet-classic/classic-handoff.ts
|
|
10689
|
-
var
|
|
11109
|
+
var import_yaml6 = __toESM(require_dist(), 1);
|
|
10690
11110
|
import { createHash as createHash5 } from "crypto";
|
|
10691
|
-
import { promises as
|
|
10692
|
-
import
|
|
11111
|
+
import { promises as fs16, readFileSync as readFileSync2 } from "fs";
|
|
11112
|
+
import path17 from "path";
|
|
10693
11113
|
var GREEN4 = "\x1B[32m";
|
|
10694
11114
|
var RED4 = "\x1B[31m";
|
|
10695
11115
|
var YELLOW4 = "\x1B[33m";
|
|
@@ -10723,7 +11143,7 @@ var HandoffOutput = class {
|
|
|
10723
11143
|
};
|
|
10724
11144
|
async function exists5(file) {
|
|
10725
11145
|
try {
|
|
10726
|
-
await
|
|
11146
|
+
await fs16.access(file);
|
|
10727
11147
|
return true;
|
|
10728
11148
|
} catch (error) {
|
|
10729
11149
|
if (error.code === "ENOENT") return false;
|
|
@@ -10732,7 +11152,7 @@ async function exists5(file) {
|
|
|
10732
11152
|
}
|
|
10733
11153
|
async function nonempty2(file) {
|
|
10734
11154
|
try {
|
|
10735
|
-
return (await
|
|
11155
|
+
return (await fs16.stat(file)).size > 0;
|
|
10736
11156
|
} catch (error) {
|
|
10737
11157
|
if (error.code === "ENOENT") return false;
|
|
10738
11158
|
throw error;
|
|
@@ -10761,7 +11181,7 @@ async function handoffSourceFiles2(changeDir) {
|
|
|
10761
11181
|
const files = [`${changeDir}/proposal.md`, `${changeDir}/design.md`, `${changeDir}/tasks.md`];
|
|
10762
11182
|
const specs = `${changeDir}/specs`;
|
|
10763
11183
|
if (await exists5(specs)) {
|
|
10764
|
-
for (const entry2 of (await
|
|
11184
|
+
for (const entry2 of (await fs16.readdir(specs)).sort()) {
|
|
10765
11185
|
const spec = `${specs}/${entry2}/spec.md`;
|
|
10766
11186
|
if (await exists5(spec)) files.push(spec);
|
|
10767
11187
|
}
|
|
@@ -10809,7 +11229,7 @@ async function writeMarkdownContext(changeDir, change, mode, contextHash, output
|
|
|
10809
11229
|
];
|
|
10810
11230
|
for (const file of await handoffSourceFiles2(changeDir)) {
|
|
10811
11231
|
if (!await exists5(file)) continue;
|
|
10812
|
-
const content = await
|
|
11232
|
+
const content = await fs16.readFile(file, "utf8");
|
|
10813
11233
|
const total = lineCount(content);
|
|
10814
11234
|
lines.push(
|
|
10815
11235
|
`## ${file}`,
|
|
@@ -10834,7 +11254,7 @@ async function writeMarkdownContext(changeDir, change, mode, contextHash, output
|
|
|
10834
11254
|
}
|
|
10835
11255
|
lines.push("");
|
|
10836
11256
|
}
|
|
10837
|
-
await
|
|
11257
|
+
await fs16.writeFile(output, lines.join("\n"));
|
|
10838
11258
|
}
|
|
10839
11259
|
async function writeJsonContext(changeDir, change, mode, contextHash, output) {
|
|
10840
11260
|
const entries = [];
|
|
@@ -10857,7 +11277,7 @@ async function writeJsonContext(changeDir, change, mode, contextHash, output) {
|
|
|
10857
11277
|
"}",
|
|
10858
11278
|
""
|
|
10859
11279
|
].join("\n");
|
|
10860
|
-
await
|
|
11280
|
+
await fs16.writeFile(output, document);
|
|
10861
11281
|
}
|
|
10862
11282
|
async function writeSpecProjectionForFile(file, content) {
|
|
10863
11283
|
return [
|
|
@@ -10897,11 +11317,11 @@ async function writeSpecMarkdownContext(changeDir, change, contextHash, output)
|
|
|
10897
11317
|
const specs = `${changeDir}/specs`;
|
|
10898
11318
|
let projected = false;
|
|
10899
11319
|
if (await exists5(specs)) {
|
|
10900
|
-
for (const entry2 of (await
|
|
11320
|
+
for (const entry2 of (await fs16.readdir(specs)).sort()) {
|
|
10901
11321
|
const spec = `${specs}/${entry2}/spec.md`;
|
|
10902
11322
|
if (!await exists5(spec)) continue;
|
|
10903
11323
|
projected = true;
|
|
10904
|
-
lines.push(...await writeSpecProjectionForFile(spec, await
|
|
11324
|
+
lines.push(...await writeSpecProjectionForFile(spec, await fs16.readFile(spec, "utf8")));
|
|
10905
11325
|
}
|
|
10906
11326
|
}
|
|
10907
11327
|
if (!projected) {
|
|
@@ -10910,7 +11330,7 @@ async function writeSpecMarkdownContext(changeDir, change, contextHash, output)
|
|
|
10910
11330
|
lines.push(
|
|
10911
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."
|
|
10912
11332
|
);
|
|
10913
|
-
await
|
|
11333
|
+
await fs16.writeFile(output, lines.join("\n"));
|
|
10914
11334
|
}
|
|
10915
11335
|
async function writeSpecJsonContext(changeDir, change, contextHash, output) {
|
|
10916
11336
|
const entries = [];
|
|
@@ -10919,7 +11339,7 @@ async function writeSpecJsonContext(changeDir, change, contextHash, output) {
|
|
|
10919
11339
|
const role = /\/specs\/[^/]+\/spec\.md$/u.test(file) ? "spec" : "supporting";
|
|
10920
11340
|
entries.push({ path: file, sha256: hashFile2(file), role });
|
|
10921
11341
|
}
|
|
10922
|
-
await
|
|
11342
|
+
await fs16.writeFile(
|
|
10923
11343
|
output,
|
|
10924
11344
|
`${JSON.stringify(
|
|
10925
11345
|
{
|
|
@@ -10938,8 +11358,8 @@ async function writeSpecJsonContext(changeDir, change, contextHash, output) {
|
|
|
10938
11358
|
);
|
|
10939
11359
|
}
|
|
10940
11360
|
async function readField2(changeDir, field2) {
|
|
10941
|
-
const file =
|
|
10942
|
-
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 });
|
|
10943
11363
|
if (document.errors.length > 0) {
|
|
10944
11364
|
throw new HandoffFailure(`ERROR: Invalid .comet.yaml: ${document.errors[0].message}`);
|
|
10945
11365
|
}
|
|
@@ -10973,7 +11393,7 @@ async function completedHandoffIsCurrent(changeDir, run, contextHash, contextJso
|
|
|
10973
11393
|
readCheckpoint(changeDir, run.checkpointRef)
|
|
10974
11394
|
]);
|
|
10975
11395
|
if (!await exists5(contextJson) || !await exists5(contextMd)) return false;
|
|
10976
|
-
if (context !== await
|
|
11396
|
+
if (context !== await fs16.readFile(contextMd, "utf8")) return false;
|
|
10977
11397
|
if (artifacts.handoff_context !== contextJson || artifacts.handoff_markdown !== contextMd) {
|
|
10978
11398
|
return false;
|
|
10979
11399
|
}
|
|
@@ -11096,7 +11516,7 @@ var classicHandoffCommand = async (args) => {
|
|
|
11096
11516
|
run: pendingRun,
|
|
11097
11517
|
unknownKeys: (await readClassicState(changeDir)).unknownKeys
|
|
11098
11518
|
});
|
|
11099
|
-
await
|
|
11519
|
+
await fs16.mkdir(handoffDir, { recursive: true });
|
|
11100
11520
|
if (handoffMode === "beta") {
|
|
11101
11521
|
await writeSpecMarkdownContext(changeDir, change, contextHash, contextMd);
|
|
11102
11522
|
await writeSpecJsonContext(changeDir, change, contextHash, contextJson);
|
|
@@ -11104,7 +11524,7 @@ var classicHandoffCommand = async (args) => {
|
|
|
11104
11524
|
await writeMarkdownContext(changeDir, change, handoffMode, contextHash, contextMd);
|
|
11105
11525
|
await writeJsonContext(changeDir, change, handoffMode, contextHash, contextJson);
|
|
11106
11526
|
}
|
|
11107
|
-
const context = await
|
|
11527
|
+
const context = await fs16.readFile(contextMd, "utf8");
|
|
11108
11528
|
await writeContext(changeDir, pendingRun.contextRef, context);
|
|
11109
11529
|
const artifacts = {
|
|
11110
11530
|
...await readArtifacts(changeDir, pendingRun.artifactsRef),
|
|
@@ -11161,10 +11581,142 @@ var classicHandoffCommand = async (args) => {
|
|
|
11161
11581
|
};
|
|
11162
11582
|
|
|
11163
11583
|
// domains/comet-classic/classic-hook-guard.ts
|
|
11164
|
-
import { existsSync as existsSync2, promises as
|
|
11165
|
-
import
|
|
11166
|
-
|
|
11167
|
-
|
|
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
|
|
11718
|
+
function result(exitCode, message) {
|
|
11719
|
+
return { exitCode, stderr: message + "\n" };
|
|
11168
11720
|
}
|
|
11169
11721
|
function allowed(message) {
|
|
11170
11722
|
return result(0, `[COMET-HOOK] allowed: ${message}`);
|
|
@@ -11187,52 +11739,52 @@ function normalized(value) {
|
|
|
11187
11739
|
function parseProjectRoot(args) {
|
|
11188
11740
|
const index = args.indexOf("--project-root");
|
|
11189
11741
|
const value = index >= 0 ? args[index + 1] : void 0;
|
|
11190
|
-
return
|
|
11742
|
+
return path19.resolve(value && !value.startsWith("--") ? value : process.cwd());
|
|
11191
11743
|
}
|
|
11192
|
-
function relativeToProjectRoot(target,
|
|
11193
|
-
const relative = normalized(
|
|
11744
|
+
function relativeToProjectRoot(target, projectRoot2) {
|
|
11745
|
+
const relative = normalized(path19.relative(projectRoot2, target));
|
|
11194
11746
|
if (relative === "") return "";
|
|
11195
|
-
if (relative.startsWith("../") || relative === ".." ||
|
|
11747
|
+
if (relative.startsWith("../") || relative === ".." || path19.isAbsolute(relative)) return null;
|
|
11196
11748
|
return relative;
|
|
11197
11749
|
}
|
|
11198
11750
|
async function physicalPathForPossiblyMissingTarget(target) {
|
|
11199
|
-
const resolved =
|
|
11200
|
-
const root =
|
|
11751
|
+
const resolved = path19.resolve(target);
|
|
11752
|
+
const root = path19.parse(resolved).root;
|
|
11201
11753
|
const missingSegments = [];
|
|
11202
11754
|
let cursor = resolved;
|
|
11203
11755
|
while (cursor && cursor !== root) {
|
|
11204
11756
|
try {
|
|
11205
|
-
const physicalBase = await
|
|
11206
|
-
return
|
|
11757
|
+
const physicalBase = await fs18.realpath(cursor);
|
|
11758
|
+
return path19.join(physicalBase, ...missingSegments.reverse());
|
|
11207
11759
|
} catch (error) {
|
|
11208
11760
|
const code = error.code;
|
|
11209
11761
|
if (code !== "ENOENT" && code !== "ENOTDIR") throw error;
|
|
11210
|
-
missingSegments.push(
|
|
11211
|
-
cursor =
|
|
11762
|
+
missingSegments.push(path19.basename(cursor));
|
|
11763
|
+
cursor = path19.dirname(cursor);
|
|
11212
11764
|
}
|
|
11213
11765
|
}
|
|
11214
11766
|
try {
|
|
11215
|
-
const physicalRoot = await
|
|
11216
|
-
return
|
|
11767
|
+
const physicalRoot = await fs18.realpath(root);
|
|
11768
|
+
return path19.join(physicalRoot, ...missingSegments.reverse());
|
|
11217
11769
|
} catch {
|
|
11218
11770
|
return null;
|
|
11219
11771
|
}
|
|
11220
11772
|
}
|
|
11221
|
-
async function projectRelative(target,
|
|
11222
|
-
const rawCandidate =
|
|
11773
|
+
async function projectRelative(target, projectRoot2) {
|
|
11774
|
+
const rawCandidate = path19.isAbsolute(target) ? target : path19.resolve(process.cwd(), target);
|
|
11223
11775
|
let candidate = normalized(rawCandidate);
|
|
11224
|
-
const rootRelative = relativeToProjectRoot(rawCandidate,
|
|
11776
|
+
const rootRelative = relativeToProjectRoot(rawCandidate, projectRoot2);
|
|
11225
11777
|
if (rootRelative !== null) return rootRelative;
|
|
11226
11778
|
try {
|
|
11227
11779
|
const physicalCandidate = await physicalPathForPossiblyMissingTarget(rawCandidate);
|
|
11228
|
-
const physicalRoot = await
|
|
11780
|
+
const physicalRoot = await fs18.realpath(projectRoot2);
|
|
11229
11781
|
if (physicalCandidate) {
|
|
11230
11782
|
const physicalRootRelative = relativeToProjectRoot(physicalCandidate, physicalRoot);
|
|
11231
11783
|
if (physicalRootRelative !== null) return physicalRootRelative;
|
|
11232
11784
|
candidate = normalized(physicalCandidate);
|
|
11233
11785
|
}
|
|
11234
11786
|
} catch {
|
|
11235
|
-
if (!
|
|
11787
|
+
if (!path19.isAbsolute(target)) return normalized(target).replace(/^\.\//u, "");
|
|
11236
11788
|
}
|
|
11237
11789
|
return candidate.replace(/^\.\//u, "");
|
|
11238
11790
|
}
|
|
@@ -11256,28 +11808,22 @@ async function loadGoverningChange(changeDir) {
|
|
|
11256
11808
|
};
|
|
11257
11809
|
}
|
|
11258
11810
|
}
|
|
11259
|
-
async function activeChanges(
|
|
11260
|
-
const changesDir =
|
|
11811
|
+
async function activeChanges(projectRoot2) {
|
|
11812
|
+
const changesDir = path19.join(projectRoot2, "openspec", "changes");
|
|
11261
11813
|
const governingChanges = [];
|
|
11262
11814
|
if (!existsSync2(changesDir)) return governingChanges;
|
|
11263
|
-
for (const entry2 of (await
|
|
11815
|
+
for (const entry2 of (await fs18.readdir(changesDir, { withFileTypes: true })).sort(
|
|
11264
11816
|
(left, right) => left.name.localeCompare(right.name)
|
|
11265
11817
|
)) {
|
|
11266
11818
|
if (!entry2.isDirectory() || entry2.name === "archive") continue;
|
|
11267
|
-
const changeDir =
|
|
11268
|
-
if (!existsSync2(
|
|
11819
|
+
const changeDir = path19.join(changesDir, entry2.name);
|
|
11820
|
+
if (!existsSync2(path19.join(changeDir, ".comet.yaml"))) continue;
|
|
11269
11821
|
const governing = await loadGoverningChange(changeDir);
|
|
11270
11822
|
if (!governing || governing.archived) continue;
|
|
11271
11823
|
governingChanges.push(governing);
|
|
11272
11824
|
}
|
|
11273
11825
|
return governingChanges;
|
|
11274
11826
|
}
|
|
11275
|
-
function blocksSourceWrites(governing) {
|
|
11276
|
-
if (governing.phase === "open" || governing.phase === "design" || governing.phase === "archive") {
|
|
11277
|
-
return true;
|
|
11278
|
-
}
|
|
11279
|
-
return governing.phase === "build" && governing.classic?.workflow === "full" && !governing.classic.designDoc;
|
|
11280
|
-
}
|
|
11281
11827
|
function isSuperpowersArtifactPath(relativePath2) {
|
|
11282
11828
|
return relativePath2.startsWith("docs/superpowers/");
|
|
11283
11829
|
}
|
|
@@ -11285,7 +11831,7 @@ function allowsSuperpowersArtifacts(governing) {
|
|
|
11285
11831
|
return governing.phase === "design" || governing.phase === "build" || governing.phase === "verify";
|
|
11286
11832
|
}
|
|
11287
11833
|
function governingChangeName(governing) {
|
|
11288
|
-
return governing.changeDir ?
|
|
11834
|
+
return governing.changeDir ? path19.basename(governing.changeDir) : null;
|
|
11289
11835
|
}
|
|
11290
11836
|
var SUPERPOWERS_ARTIFACT_SUFFIXES = /* @__PURE__ */ new Set([
|
|
11291
11837
|
"design",
|
|
@@ -11316,8 +11862,8 @@ function matchesSuperpowersArtifactName(relativePath2, changeName) {
|
|
|
11316
11862
|
const pattern = new RegExp(`(^|[-_.])${escapeRegex(changeName)}[-_.](${suffixes})$`, "u");
|
|
11317
11863
|
return pattern.test(stem);
|
|
11318
11864
|
}
|
|
11319
|
-
async function superpowersArtifactGoverningChange(relativePath2,
|
|
11320
|
-
const active = await activeChanges(
|
|
11865
|
+
async function superpowersArtifactGoverningChange(relativePath2, projectRoot2) {
|
|
11866
|
+
const active = await activeChanges(projectRoot2);
|
|
11321
11867
|
const recorded = active.find(
|
|
11322
11868
|
(governing) => matchesRecordedSuperpowersArtifact(relativePath2, governing)
|
|
11323
11869
|
);
|
|
@@ -11332,18 +11878,41 @@ async function superpowersArtifactGoverningChange(relativePath2, projectRoot) {
|
|
|
11332
11878
|
if (named) return named;
|
|
11333
11879
|
return null;
|
|
11334
11880
|
}
|
|
11335
|
-
async function repoSourceGoverningChange(
|
|
11336
|
-
const active = await activeChanges(
|
|
11337
|
-
|
|
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
|
+
};
|
|
11338
11907
|
}
|
|
11339
|
-
async function governingChange(relativePath2,
|
|
11908
|
+
async function governingChange(relativePath2, projectRoot2) {
|
|
11340
11909
|
const prefix = "openspec/changes/";
|
|
11341
11910
|
if (relativePath2.startsWith(prefix)) {
|
|
11342
11911
|
const rest = relativePath2.slice(prefix.length);
|
|
11343
11912
|
const [name] = rest.split("/");
|
|
11344
11913
|
if (name && name !== "archive") {
|
|
11345
|
-
const changeDir =
|
|
11346
|
-
const stateFile2 =
|
|
11914
|
+
const changeDir = path19.join(projectRoot2, "openspec", "changes", name);
|
|
11915
|
+
const stateFile2 = path19.join(changeDir, ".comet.yaml");
|
|
11347
11916
|
if (existsSync2(stateFile2)) {
|
|
11348
11917
|
const governing = await loadGoverningChange(changeDir);
|
|
11349
11918
|
if (governing) return governing;
|
|
@@ -11353,18 +11922,18 @@ async function governingChange(relativePath2, projectRoot) {
|
|
|
11353
11922
|
}
|
|
11354
11923
|
}
|
|
11355
11924
|
if (isSuperpowersArtifactPath(relativePath2)) {
|
|
11356
|
-
const superpowers = await superpowersArtifactGoverningChange(relativePath2,
|
|
11925
|
+
const superpowers = await superpowersArtifactGoverningChange(relativePath2, projectRoot2);
|
|
11357
11926
|
if (superpowers) return { ...superpowers, superpowersArtifact: "matched" };
|
|
11358
|
-
const fallback = await
|
|
11927
|
+
const fallback = (await activeChanges(projectRoot2))[0] ?? null;
|
|
11359
11928
|
return fallback ? { ...fallback, superpowersArtifact: "unmatched" } : null;
|
|
11360
11929
|
}
|
|
11361
|
-
return repoSourceGoverningChange(
|
|
11930
|
+
return repoSourceGoverningChange(projectRoot2, relativePath2);
|
|
11362
11931
|
}
|
|
11363
11932
|
function isRootMarkdown(relativePath2) {
|
|
11364
11933
|
return !relativePath2.includes("/") && relativePath2.endsWith(".md");
|
|
11365
11934
|
}
|
|
11366
11935
|
function isCometConfig(relativePath2) {
|
|
11367
|
-
return relativePath2
|
|
11936
|
+
return relativePath2.startsWith(".comet/") || relativePath2.includes("/.comet/");
|
|
11368
11937
|
}
|
|
11369
11938
|
function isSuperpowersWorkspace(relativePath2) {
|
|
11370
11939
|
return relativePath2 === ".superpowers" || relativePath2.startsWith(".superpowers/");
|
|
@@ -11462,22 +12031,47 @@ function blockedUnmatchedSuperpowersArtifact(relativePath2, phase) {
|
|
|
11462
12031
|
].join("\n")
|
|
11463
12032
|
);
|
|
11464
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
|
+
}
|
|
11465
12070
|
var classicHookGuardCommand = async (args) => {
|
|
11466
|
-
const
|
|
12071
|
+
const projectRoot2 = parseProjectRoot(args);
|
|
11467
12072
|
const target = inputTarget();
|
|
11468
12073
|
if (!target) return allowed("no file path in tool input");
|
|
11469
|
-
const relativePath2 = await projectRelative(target,
|
|
11470
|
-
let governing;
|
|
11471
|
-
try {
|
|
11472
|
-
governing = await governingChange(relativePath2, projectRoot);
|
|
11473
|
-
} catch (error) {
|
|
11474
|
-
return result(
|
|
11475
|
-
2,
|
|
11476
|
-
`[COMET-HOOK] blocked: ${error instanceof Error ? error.message : String(error)}`
|
|
11477
|
-
);
|
|
11478
|
-
}
|
|
11479
|
-
if (!governing) return allowed("no active comet change");
|
|
11480
|
-
if (governing.archived) return allowed(`${relativePath2} (own change archived)`);
|
|
12074
|
+
const relativePath2 = await projectRelative(target, projectRoot2);
|
|
11481
12075
|
if (isCometConfig(relativePath2)) {
|
|
11482
12076
|
return allowed(`${relativePath2} (whitelist: comet config)`);
|
|
11483
12077
|
}
|
|
@@ -11490,6 +12084,18 @@ var classicHookGuardCommand = async (args) => {
|
|
|
11490
12084
|
if (relativePath2 === "CLAUDE.md" || relativePath2 === "CHANGELOG.md" || relativePath2 === "README.md" || isRootMarkdown(relativePath2)) {
|
|
11491
12085
|
return allowed(`${relativePath2} (whitelist: root markdown)`);
|
|
11492
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)`);
|
|
11493
12099
|
const phase = governing.phase;
|
|
11494
12100
|
const openSpec = openSpecAllowed(relativePath2, phase);
|
|
11495
12101
|
if (openSpec) return allowed(openSpec);
|
|
@@ -11878,12 +12484,438 @@ var classicIntentCommand = async (args, _options) => {
|
|
|
11878
12484
|
}
|
|
11879
12485
|
};
|
|
11880
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
|
+
|
|
11881
12913
|
// domains/comet-classic/classic-state-command.ts
|
|
11882
|
-
var
|
|
12914
|
+
var import_yaml7 = __toESM(require_dist(), 1);
|
|
11883
12915
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
11884
|
-
import { randomUUID as
|
|
11885
|
-
import { existsSync as existsSync3, promises as
|
|
11886
|
-
import
|
|
12916
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
12917
|
+
import { existsSync as existsSync3, promises as fs20 } from "fs";
|
|
12918
|
+
import path21 from "path";
|
|
11887
12919
|
init_state();
|
|
11888
12920
|
var GREEN5 = "\x1B[32m";
|
|
11889
12921
|
var RED5 = "\x1B[31m";
|
|
@@ -11895,6 +12927,7 @@ var ARTIFACT_LANGUAGES2 = ["en", "zh-CN"];
|
|
|
11895
12927
|
var EVENTS = CLASSIC_TRANSITION_EVENTS;
|
|
11896
12928
|
var MACHINE_OWNED_FIELDS = /* @__PURE__ */ new Set([
|
|
11897
12929
|
...RUN_WIRE_KEYS,
|
|
12930
|
+
"archive_confirmation",
|
|
11898
12931
|
"classic_profile",
|
|
11899
12932
|
"classic_migration"
|
|
11900
12933
|
]);
|
|
@@ -11915,6 +12948,7 @@ var FIELD_ENUMS = {
|
|
|
11915
12948
|
auto_transition: ["true", "false"],
|
|
11916
12949
|
verify_result: ["pending", "pass", "fail"],
|
|
11917
12950
|
branch_status: ["pending", "handled"],
|
|
12951
|
+
archive_confirmation: ["pending", "confirmed"],
|
|
11918
12952
|
archived: ["true", "false"],
|
|
11919
12953
|
direct_override: ["true", "false"],
|
|
11920
12954
|
classic_profile: PROFILES,
|
|
@@ -11930,6 +12964,7 @@ var CLASSIC_FIELD_WIRE_NAMES2 = {
|
|
|
11930
12964
|
phase: "phase",
|
|
11931
12965
|
verificationReport: "verification_report",
|
|
11932
12966
|
verifiedAt: "verified_at",
|
|
12967
|
+
archiveConfirmation: "archive_confirmation",
|
|
11933
12968
|
verifyResult: "verify_result",
|
|
11934
12969
|
workflow: "workflow"
|
|
11935
12970
|
};
|
|
@@ -11991,7 +13026,7 @@ function validateRelativePath(value, field2) {
|
|
|
11991
13026
|
}
|
|
11992
13027
|
async function exists6(file) {
|
|
11993
13028
|
try {
|
|
11994
|
-
await
|
|
13029
|
+
await fs20.access(file);
|
|
11995
13030
|
return true;
|
|
11996
13031
|
} catch (error) {
|
|
11997
13032
|
if (error.code === "ENOENT") return false;
|
|
@@ -12000,39 +13035,39 @@ async function exists6(file) {
|
|
|
12000
13035
|
}
|
|
12001
13036
|
async function nonempty3(file) {
|
|
12002
13037
|
try {
|
|
12003
|
-
return (await
|
|
13038
|
+
return (await fs20.stat(file)).size > 0;
|
|
12004
13039
|
} catch (error) {
|
|
12005
13040
|
if (error.code === "ENOENT") return false;
|
|
12006
13041
|
throw error;
|
|
12007
13042
|
}
|
|
12008
13043
|
}
|
|
12009
|
-
async function
|
|
13044
|
+
async function changeDirectory2(name) {
|
|
12010
13045
|
return resolveClassicChangeDirectory(name);
|
|
12011
13046
|
}
|
|
12012
13047
|
async function readDocument2(file) {
|
|
12013
13048
|
let source;
|
|
12014
13049
|
try {
|
|
12015
|
-
source = await
|
|
13050
|
+
source = await fs20.readFile(file, "utf8");
|
|
12016
13051
|
} catch (error) {
|
|
12017
13052
|
if (error.code === "ENOENT") {
|
|
12018
13053
|
fail2(
|
|
12019
|
-
`ERROR: .comet.yaml not found at ${
|
|
13054
|
+
`ERROR: .comet.yaml not found at ${path21.relative(process.cwd(), file).replaceAll("\\", "/")}`
|
|
12020
13055
|
);
|
|
12021
13056
|
}
|
|
12022
13057
|
throw error;
|
|
12023
13058
|
}
|
|
12024
|
-
const document = (0,
|
|
13059
|
+
const document = (0, import_yaml7.parseDocument)(source, { uniqueKeys: false });
|
|
12025
13060
|
if (document.errors.length > 0) fail2(`ERROR: Invalid .comet.yaml: ${document.errors[0].message}`);
|
|
12026
13061
|
return document;
|
|
12027
13062
|
}
|
|
12028
13063
|
async function atomicWrite2(file, content) {
|
|
12029
|
-
await
|
|
12030
|
-
const temporary = `${file}.${
|
|
13064
|
+
await fs20.mkdir(path21.dirname(file), { recursive: true });
|
|
13065
|
+
const temporary = `${file}.${randomUUID7()}.tmp`;
|
|
12031
13066
|
try {
|
|
12032
|
-
await
|
|
12033
|
-
await
|
|
13067
|
+
await fs20.writeFile(temporary, content, "utf8");
|
|
13068
|
+
await fs20.rename(temporary, file);
|
|
12034
13069
|
} catch (error) {
|
|
12035
|
-
await
|
|
13070
|
+
await fs20.rm(temporary, { force: true });
|
|
12036
13071
|
throw error;
|
|
12037
13072
|
}
|
|
12038
13073
|
}
|
|
@@ -12108,10 +13143,14 @@ function sparseClassicState(record) {
|
|
|
12108
13143
|
branchStatus: enumRecordValue(record, "branch_status", ["pending", "handled"], null),
|
|
12109
13144
|
createdAt: nullableRecordString(record, "created_at"),
|
|
12110
13145
|
verifiedAt: nullableRecordString(record, "verified_at"),
|
|
13146
|
+
archiveConfirmation: enumRecordValue(
|
|
13147
|
+
record,
|
|
13148
|
+
"archive_confirmation",
|
|
13149
|
+
["pending", "confirmed"],
|
|
13150
|
+
null
|
|
13151
|
+
),
|
|
12111
13152
|
archived: nullableRecordBoolean(record, "archived") ?? false,
|
|
12112
13153
|
directOverride: nullableRecordBoolean(record, "direct_override"),
|
|
12113
|
-
buildCommand: nullableRecordString(record, "build_command"),
|
|
12114
|
-
verifyCommand: nullableRecordString(record, "verify_command"),
|
|
12115
13154
|
handoffContext: nullableRecordString(record, "handoff_context"),
|
|
12116
13155
|
handoffHash: nullableRecordString(record, "handoff_hash"),
|
|
12117
13156
|
classicProfile: enumRecordValue(record, "classic_profile", PROFILES, workflow),
|
|
@@ -12119,17 +13158,13 @@ function sparseClassicState(record) {
|
|
|
12119
13158
|
};
|
|
12120
13159
|
}
|
|
12121
13160
|
async function projectConfigValue2(field2) {
|
|
12122
|
-
|
|
12123
|
-
if (!await exists6(file)) return null;
|
|
12124
|
-
const document = await readDocument2(file);
|
|
12125
|
-
const value = document.get(field2);
|
|
12126
|
-
return value === null || value === void 0 ? null : scalar(value);
|
|
13161
|
+
return (await readClassicConfigValue(field2))?.value ?? null;
|
|
12127
13162
|
}
|
|
12128
13163
|
async function projectLanguageDefault() {
|
|
12129
13164
|
if (process.env.COMET_LANGUAGE)
|
|
12130
13165
|
return validateLanguage(process.env.COMET_LANGUAGE, "COMET_LANGUAGE");
|
|
12131
|
-
const
|
|
12132
|
-
if (
|
|
13166
|
+
const configured = await readClassicConfigValue("language");
|
|
13167
|
+
if (configured) return validateLanguage(configured.value, configured.source);
|
|
12133
13168
|
return "en";
|
|
12134
13169
|
}
|
|
12135
13170
|
async function contextCompression() {
|
|
@@ -12157,14 +13192,14 @@ Valid values: off, standard, thorough`);
|
|
|
12157
13192
|
return value === "null" ? null : value;
|
|
12158
13193
|
}
|
|
12159
13194
|
function gitOutput(args) {
|
|
12160
|
-
const
|
|
12161
|
-
return
|
|
13195
|
+
const result5 = spawnSync3("git", args, { encoding: "utf8" });
|
|
13196
|
+
return result5.status === 0 ? result5.stdout.trim() : null;
|
|
12162
13197
|
}
|
|
12163
13198
|
async function stateFile(name) {
|
|
12164
|
-
const change = await
|
|
13199
|
+
const change = await changeDirectory2(name);
|
|
12165
13200
|
return {
|
|
12166
13201
|
...change,
|
|
12167
|
-
file:
|
|
13202
|
+
file: path21.join(change.directory, ".comet.yaml")
|
|
12168
13203
|
};
|
|
12169
13204
|
}
|
|
12170
13205
|
async function readField3(name, field2) {
|
|
@@ -12182,7 +13217,7 @@ async function readField3(name, field2) {
|
|
|
12182
13217
|
return scalar(value);
|
|
12183
13218
|
}
|
|
12184
13219
|
function parsedValue(field2, value) {
|
|
12185
|
-
const document = (0,
|
|
13220
|
+
const document = (0, import_yaml7.parseDocument)(`${field2}: ${value}
|
|
12186
13221
|
`);
|
|
12187
13222
|
if (document.errors.length > 0) fail2(`ERROR: Invalid value: '${value}'`);
|
|
12188
13223
|
return document.get(field2);
|
|
@@ -12204,7 +13239,7 @@ function validateSetValue(field2, value) {
|
|
|
12204
13239
|
}
|
|
12205
13240
|
async function setField2(output, name, field2, value, options = {}) {
|
|
12206
13241
|
if (MACHINE_OWNED_FIELDS.has(field2) && !options.machineOwned) {
|
|
12207
|
-
fail2(`ERROR: '${field2}' is a machine-owned
|
|
13242
|
+
fail2(`ERROR: '${field2}' is a machine-owned field and cannot be set directly`);
|
|
12208
13243
|
}
|
|
12209
13244
|
if (!SETTABLE_FIELDS.has(field2) && !MACHINE_OWNED_FIELDS.has(field2)) {
|
|
12210
13245
|
fail2(`ERROR: Unknown field: '${field2}'`);
|
|
@@ -12267,10 +13302,10 @@ async function init(output, name, workflow) {
|
|
|
12267
13302
|
validateEnum(workflow, PROFILES);
|
|
12268
13303
|
const { file, label, directory } = await stateFile(name);
|
|
12269
13304
|
if (await exists6(file)) fail2(`ERROR: .comet.yaml already exists at ${label}/.comet.yaml`);
|
|
12270
|
-
await
|
|
13305
|
+
await fs20.mkdir(directory, { recursive: true });
|
|
12271
13306
|
const preset = workflow !== "full";
|
|
12272
13307
|
const reviewMode = preset ? "off" : await reviewModeDefault();
|
|
12273
|
-
const document = new
|
|
13308
|
+
const document = new import_yaml7.Document({
|
|
12274
13309
|
workflow,
|
|
12275
13310
|
language: await projectLanguageDefault(),
|
|
12276
13311
|
phase: "open",
|
|
@@ -12291,10 +13326,11 @@ async function init(output, name, workflow) {
|
|
|
12291
13326
|
branch_status: "pending",
|
|
12292
13327
|
created_at: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
|
|
12293
13328
|
verified_at: null,
|
|
13329
|
+
archive_confirmation: null,
|
|
12294
13330
|
archived: false
|
|
12295
13331
|
});
|
|
12296
13332
|
await atomicWrite2(file, document.toString());
|
|
12297
|
-
output.
|
|
13333
|
+
output.stdout.push(green4(`Initialized: ${label}/.comet.yaml (workflow=${workflow})`));
|
|
12298
13334
|
}
|
|
12299
13335
|
async function requirePhase(name, expected) {
|
|
12300
13336
|
const actual = await readField3(name, "phase");
|
|
@@ -12345,13 +13381,13 @@ async function requireOpenArtifacts(name) {
|
|
|
12345
13381
|
const { directory } = await stateFile(name);
|
|
12346
13382
|
const workflow = await readField3(name, "workflow");
|
|
12347
13383
|
for (const artifact of ["proposal.md", "tasks.md"]) {
|
|
12348
|
-
if (!await nonempty3(
|
|
13384
|
+
if (!await nonempty3(path21.join(directory, artifact))) {
|
|
12349
13385
|
fail2(
|
|
12350
13386
|
`ERROR: Cannot transition '${name}': ${artifact} must exist and be non-empty before leaving open`
|
|
12351
13387
|
);
|
|
12352
13388
|
}
|
|
12353
13389
|
}
|
|
12354
|
-
if (workflow === "full" && !await nonempty3(
|
|
13390
|
+
if (workflow === "full" && !await nonempty3(path21.join(directory, "design.md"))) {
|
|
12355
13391
|
fail2(
|
|
12356
13392
|
`ERROR: Cannot transition '${name}': design.md must exist and be non-empty before leaving open`
|
|
12357
13393
|
);
|
|
@@ -12359,14 +13395,14 @@ async function requireOpenArtifacts(name) {
|
|
|
12359
13395
|
}
|
|
12360
13396
|
async function requireDesignEvidence(name) {
|
|
12361
13397
|
const designDoc = await readField3(name, "design_doc");
|
|
12362
|
-
if (!designDoc || designDoc === "null" || !await nonempty3(
|
|
13398
|
+
if (!designDoc || designDoc === "null" || !await nonempty3(path21.resolve(designDoc))) {
|
|
12363
13399
|
fail2(
|
|
12364
13400
|
`ERROR: Cannot transition '${name}': design_doc must point to an existing Design Doc before leaving design`
|
|
12365
13401
|
);
|
|
12366
13402
|
}
|
|
12367
13403
|
}
|
|
12368
13404
|
async function writeSparseTransitionEffects(directory, effects) {
|
|
12369
|
-
const file =
|
|
13405
|
+
const file = path21.join(directory, ".comet.yaml");
|
|
12370
13406
|
const document = await readDocument2(file);
|
|
12371
13407
|
for (const effect of effects) {
|
|
12372
13408
|
const field2 = wireField2(effect.field);
|
|
@@ -12381,21 +13417,21 @@ async function applyTransitionEvent(output, name, event) {
|
|
|
12381
13417
|
let sparse = false;
|
|
12382
13418
|
if (!classic) {
|
|
12383
13419
|
if (projection.run) fail2("ERROR: Classic state projection is missing");
|
|
12384
|
-
const document = await readDocument2(
|
|
13420
|
+
const document = await readDocument2(path21.join(directory, ".comet.yaml"));
|
|
12385
13421
|
classic = sparseClassicState(document.toJS());
|
|
12386
13422
|
sparse = true;
|
|
12387
13423
|
}
|
|
12388
|
-
const
|
|
13424
|
+
const result5 = applyClassicTransition(classic, event);
|
|
12389
13425
|
if (projection.run) {
|
|
12390
|
-
await transitionClassicRuntimeRun(directory,
|
|
13426
|
+
await transitionClassicRuntimeRun(directory, result5.classic, projection.run, {
|
|
12391
13427
|
event,
|
|
12392
13428
|
source: "comet-state"
|
|
12393
13429
|
});
|
|
12394
13430
|
} else if (sparse) {
|
|
12395
|
-
await writeSparseTransitionEffects(directory,
|
|
13431
|
+
await writeSparseTransitionEffects(directory, result5.effects);
|
|
12396
13432
|
} else {
|
|
12397
13433
|
await writeClassicState(directory, {
|
|
12398
|
-
classic:
|
|
13434
|
+
classic: result5.classic,
|
|
12399
13435
|
run: null,
|
|
12400
13436
|
unknownKeys: projection.unknownKeys
|
|
12401
13437
|
});
|
|
@@ -12405,10 +13441,10 @@ async function applyTransitionEvent(output, name, event) {
|
|
|
12405
13441
|
event,
|
|
12406
13442
|
source: "comet-state",
|
|
12407
13443
|
from: classic,
|
|
12408
|
-
to:
|
|
12409
|
-
effects:
|
|
13444
|
+
to: result5.classic,
|
|
13445
|
+
effects: result5.effects
|
|
12410
13446
|
});
|
|
12411
|
-
for (const effect of
|
|
13447
|
+
for (const effect of result5.effects) {
|
|
12412
13448
|
output.stderr.push(green4(`[SET] ${wireField2(effect.field)}=${wireValue2(effect.to)}`));
|
|
12413
13449
|
}
|
|
12414
13450
|
output.stderr.push(green4(`[TRANSITION] ${event}`));
|
|
@@ -12428,7 +13464,7 @@ async function transition(output, name, event) {
|
|
|
12428
13464
|
} else if (event === "verify-pass") {
|
|
12429
13465
|
await requirePhase(name, "verify");
|
|
12430
13466
|
const report = await readField3(name, "verification_report");
|
|
12431
|
-
if (!report || !await exists6(
|
|
13467
|
+
if (!report || !await exists6(path21.resolve(report))) {
|
|
12432
13468
|
fail2(
|
|
12433
13469
|
`ERROR: Cannot transition '${name}': verification_report must point to an existing report file`
|
|
12434
13470
|
);
|
|
@@ -12438,6 +13474,14 @@ async function transition(output, name, event) {
|
|
|
12438
13474
|
}
|
|
12439
13475
|
} else if (event === "verify-fail") {
|
|
12440
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
|
+
}
|
|
12441
13485
|
} else if (event === "preset-escalate") {
|
|
12442
13486
|
await requirePhase(name, "build");
|
|
12443
13487
|
const workflow = await readField3(name, "workflow");
|
|
@@ -12456,6 +13500,11 @@ async function transition(output, name, event) {
|
|
|
12456
13500
|
if (await readField3(name, "verify_result") !== "pass") {
|
|
12457
13501
|
fail2(`ERROR: Cannot transition '${name}': verify_result must be pass before archiving`);
|
|
12458
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
|
+
}
|
|
12459
13508
|
}
|
|
12460
13509
|
await applyTransitionEvent(output, name, event);
|
|
12461
13510
|
}
|
|
@@ -12482,9 +13531,9 @@ async function next(output, name) {
|
|
|
12482
13531
|
async function taskCheckoff(output, taskFile, taskText) {
|
|
12483
13532
|
validateRelativePath(taskFile, "task file");
|
|
12484
13533
|
if (!taskText) fail2("ERROR: Task text cannot be empty");
|
|
12485
|
-
const file =
|
|
13534
|
+
const file = path21.resolve(taskFile);
|
|
12486
13535
|
if (!await exists6(file)) fail2(`ERROR: Task file not found: ${taskFile}`);
|
|
12487
|
-
const lines = (await
|
|
13536
|
+
const lines = (await fs20.readFile(file, "utf8")).split(/\r?\n/u);
|
|
12488
13537
|
const matches = lines.filter(
|
|
12489
13538
|
(line) => [`- [ ] ${taskText}`, `- [x] ${taskText}`, `- [X] ${taskText}`].includes(line)
|
|
12490
13539
|
);
|
|
@@ -12522,21 +13571,21 @@ async function check2(output, name, phase) {
|
|
|
12522
13571
|
designDoc ? `design_doc=${designDoc} (expected: empty/null)` : "design_doc is empty/null"
|
|
12523
13572
|
);
|
|
12524
13573
|
for (const artifact of ["proposal.md", "design.md", "tasks.md"]) {
|
|
12525
|
-
(await nonempty3(
|
|
12526
|
-
`${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"}`
|
|
12527
13576
|
);
|
|
12528
13577
|
}
|
|
12529
13578
|
} else if (phase === "build") {
|
|
12530
13579
|
const workflow = await readField3(name, "workflow");
|
|
12531
13580
|
const designDoc = await readField3(name, "design_doc");
|
|
12532
13581
|
if (workflow === "full") {
|
|
12533
|
-
(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)`);
|
|
12534
13583
|
} else {
|
|
12535
13584
|
pass2(`workflow=${workflow} (design_doc not required)`);
|
|
12536
13585
|
}
|
|
12537
13586
|
for (const artifact of ["proposal.md", "tasks.md"]) {
|
|
12538
|
-
(await nonempty3(
|
|
12539
|
-
`${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"}`
|
|
12540
13589
|
);
|
|
12541
13590
|
}
|
|
12542
13591
|
} else if (phase === "verify") {
|
|
@@ -12558,7 +13607,7 @@ async function check2(output, name, phase) {
|
|
|
12558
13607
|
}
|
|
12559
13608
|
function fieldStatus(field2, value, file) {
|
|
12560
13609
|
if (!value || value === "null") return ` - ${field2}: PENDING`;
|
|
12561
|
-
if (file && !existsSync3(
|
|
13610
|
+
if (file && !existsSync3(path21.resolve(file))) {
|
|
12562
13611
|
return ` - ${field2}: BROKEN (path ${value} does not exist)`;
|
|
12563
13612
|
}
|
|
12564
13613
|
return ` - ${field2}: DONE (${value})`;
|
|
@@ -12567,7 +13616,7 @@ async function recoverOpen(output, directory) {
|
|
|
12567
13616
|
output.stdout.push(" Artifacts:");
|
|
12568
13617
|
let complete = 0;
|
|
12569
13618
|
for (const artifact of ["proposal.md", "design.md", "tasks.md"]) {
|
|
12570
|
-
const done = await nonempty3(
|
|
13619
|
+
const done = await nonempty3(path21.join(directory, artifact));
|
|
12571
13620
|
if (done) complete += 1;
|
|
12572
13621
|
output.stdout.push(` - ${artifact}: ${done ? "DONE" : "PENDING"}`);
|
|
12573
13622
|
}
|
|
@@ -12580,7 +13629,7 @@ async function recoverDesign(output, name, directory) {
|
|
|
12580
13629
|
output.stdout.push(" Artifacts:");
|
|
12581
13630
|
for (const artifact of ["proposal.md", "design.md", "tasks.md"]) {
|
|
12582
13631
|
output.stdout.push(
|
|
12583
|
-
` - ${artifact}: ${await nonempty3(
|
|
13632
|
+
` - ${artifact}: ${await nonempty3(path21.join(directory, artifact)) ? "DONE" : "MISSING (unexpected in design phase)"}`
|
|
12584
13633
|
);
|
|
12585
13634
|
}
|
|
12586
13635
|
const handoff = await readField3(name, "handoff_context");
|
|
@@ -12594,11 +13643,11 @@ async function recoverDesign(output, name, directory) {
|
|
|
12594
13643
|
fieldStatus("design_doc", design, design),
|
|
12595
13644
|
""
|
|
12596
13645
|
);
|
|
12597
|
-
if (design && design !== "null" && await exists6(
|
|
13646
|
+
if (design && design !== "null" && await exists6(path21.resolve(design))) {
|
|
12598
13647
|
output.stdout.push(
|
|
12599
13648
|
"Recovery action: Design Doc already created and linked. Run guard to transition to build."
|
|
12600
13649
|
);
|
|
12601
|
-
} else if (handoff && handoff !== "null" && await exists6(
|
|
13650
|
+
} else if (handoff && handoff !== "null" && await exists6(path21.resolve(handoff))) {
|
|
12602
13651
|
output.stdout.push(
|
|
12603
13652
|
"Recovery action: Handoff generated but Design Doc not yet created. Resume from brainstorming confirmation (Step 1c)."
|
|
12604
13653
|
);
|
|
@@ -12628,7 +13677,7 @@ async function recoverBuild(output, name, directory, workflow) {
|
|
|
12628
13677
|
decisions.push(fieldStatus("subagent_dispatch", subagentDispatch));
|
|
12629
13678
|
}
|
|
12630
13679
|
output.stdout.push(...decisions, "", " Plan:", fieldStatus("plan", plan, plan), "");
|
|
12631
|
-
const tasks =
|
|
13680
|
+
const tasks = path21.join(directory, "tasks.md");
|
|
12632
13681
|
if (!await exists6(tasks)) {
|
|
12633
13682
|
output.stdout.push(
|
|
12634
13683
|
" Tasks: tasks.md MISSING",
|
|
@@ -12637,14 +13686,14 @@ async function recoverBuild(output, name, directory, workflow) {
|
|
|
12637
13686
|
);
|
|
12638
13687
|
return;
|
|
12639
13688
|
}
|
|
12640
|
-
const lines = (await
|
|
13689
|
+
const lines = (await fs20.readFile(tasks, "utf8")).split(/\r?\n/u);
|
|
12641
13690
|
const total = lines.filter((line) => /^\s*- \[[ xX]\] /u.test(line)).length;
|
|
12642
13691
|
const done = lines.filter((line) => /^\s*- \[[xX]\] /u.test(line)).length;
|
|
12643
13692
|
const pending = total - done;
|
|
12644
13693
|
let planTotal = 0;
|
|
12645
13694
|
let planDone = 0;
|
|
12646
|
-
if (plan && plan !== "null" && await exists6(
|
|
12647
|
-
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);
|
|
12648
13697
|
planTotal = planLines.filter((line) => /^\s*- \[[ xX]\] /u.test(line)).length;
|
|
12649
13698
|
planDone = planLines.filter((line) => /^\s*- \[[xX]\] /u.test(line)).length;
|
|
12650
13699
|
}
|
|
@@ -12716,27 +13765,29 @@ function resolveBuildRecoveryAction(workflow, isolation, buildMode, pause, subag
|
|
|
12716
13765
|
return "Recovery action: All tasks done. Run guard to transition to verify.";
|
|
12717
13766
|
}
|
|
12718
13767
|
async function recoverVerify(output, name) {
|
|
12719
|
-
const
|
|
13768
|
+
const result5 = await readField3(name, "verify_result");
|
|
12720
13769
|
const mode = await readField3(name, "verify_mode");
|
|
12721
13770
|
const report = await readField3(name, "verification_report");
|
|
12722
13771
|
const branch = await readField3(name, "branch_status");
|
|
12723
13772
|
output.stdout.push(
|
|
12724
13773
|
" Verification:",
|
|
12725
|
-
fieldStatus("verify_result",
|
|
13774
|
+
fieldStatus("verify_result", result5),
|
|
12726
13775
|
fieldStatus("verify_mode", mode),
|
|
12727
13776
|
fieldStatus("verification_report", report, report),
|
|
12728
13777
|
fieldStatus("branch_status", branch),
|
|
12729
13778
|
"",
|
|
12730
|
-
|
|
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."
|
|
12731
13780
|
);
|
|
12732
13781
|
}
|
|
12733
13782
|
async function recoverArchive(output, name) {
|
|
13783
|
+
const archiveConfirmation = await readField3(name, "archive_confirmation");
|
|
12734
13784
|
output.stdout.push(
|
|
12735
13785
|
" Archive:",
|
|
12736
13786
|
fieldStatus("verify_result", await readField3(name, "verify_result")),
|
|
13787
|
+
fieldStatus("archive_confirmation", archiveConfirmation),
|
|
12737
13788
|
fieldStatus("archived", await readField3(name, "archived")),
|
|
12738
13789
|
"",
|
|
12739
|
-
"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."
|
|
12740
13791
|
);
|
|
12741
13792
|
}
|
|
12742
13793
|
async function recover(output, name) {
|
|
@@ -12771,19 +13822,19 @@ async function scale(output, name) {
|
|
|
12771
13822
|
validateChangeName4(name);
|
|
12772
13823
|
const { file, directory, label } = await stateFile(name);
|
|
12773
13824
|
if (!await exists6(file)) fail2(`ERROR: .comet.yaml not found at ${label}/.comet.yaml`);
|
|
12774
|
-
const tasksFile =
|
|
12775
|
-
const taskCount = await exists6(tasksFile) ? (await
|
|
12776
|
-
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");
|
|
12777
13828
|
let deltaSpecs = 0;
|
|
12778
13829
|
if (await exists6(specs)) {
|
|
12779
|
-
for (const entry2 of await
|
|
12780
|
-
if (await exists6(
|
|
13830
|
+
for (const entry2 of await fs20.readdir(specs)) {
|
|
13831
|
+
if (await exists6(path21.join(specs, entry2, "spec.md"))) deltaSpecs += 1;
|
|
12781
13832
|
}
|
|
12782
13833
|
}
|
|
12783
13834
|
const plan = await readField3(name, "plan");
|
|
12784
13835
|
let baseRef = "";
|
|
12785
|
-
if (plan && plan !== "null" && await exists6(
|
|
12786
|
-
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);
|
|
12787
13838
|
baseRef = match?.[1].trim() ?? "";
|
|
12788
13839
|
}
|
|
12789
13840
|
if (!baseRef) baseRef = await readField3(name, "base_ref");
|
|
@@ -12793,19 +13844,102 @@ async function scale(output, name) {
|
|
|
12793
13844
|
...baseRef && baseRef !== "null" ? [`${baseRef}...HEAD`] : ["HEAD"]
|
|
12794
13845
|
]);
|
|
12795
13846
|
const changedFiles = changed ? changed.split(/\r?\n/u).filter(Boolean).length : 0;
|
|
12796
|
-
const
|
|
12797
|
-
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);
|
|
12798
13849
|
output.stderr.push(
|
|
12799
13850
|
`=== Scale Assessment: ${name} ===`,
|
|
12800
13851
|
` Tasks: ${taskCount} (threshold: 3)`,
|
|
12801
13852
|
` Delta specs: ${deltaSpecs} capabilities (threshold: 1)`,
|
|
12802
13853
|
` Changed files: ${changedFiles} (threshold: 8)`,
|
|
12803
|
-
` → Result: ${
|
|
12804
|
-
green4(`[SCALE] verify_mode=${
|
|
13854
|
+
` → Result: ${result5}`,
|
|
13855
|
+
green4(`[SCALE] verify_mode=${result5}`)
|
|
12805
13856
|
);
|
|
12806
13857
|
}
|
|
12807
|
-
function
|
|
12808
|
-
|
|
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>`
|
|
13938
|
+
);
|
|
13939
|
+
}
|
|
13940
|
+
async function clearSelection(output) {
|
|
13941
|
+
await clearCurrentChange(process.cwd());
|
|
13942
|
+
output.stderr.push(green4("[CLEARED] current change selection"));
|
|
12809
13943
|
}
|
|
12810
13944
|
var classicStateCommand = async (args) => {
|
|
12811
13945
|
const output = new CommandOutput();
|
|
@@ -12832,9 +13966,25 @@ var classicStateCommand = async (args) => {
|
|
|
12832
13966
|
} else if (subcommand === "scale") {
|
|
12833
13967
|
required(rest, 1, "Usage: comet-state.mjs scale <change-name>");
|
|
12834
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));
|
|
12835
13976
|
} else if (subcommand === "task-checkoff") {
|
|
12836
13977
|
required(rest, 2, "Usage: comet-state.mjs task-checkoff <file> <task-text>");
|
|
12837
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);
|
|
12838
13988
|
} else if (subcommand === "next") {
|
|
12839
13989
|
required(rest, 1, "Usage: comet-state.mjs next <change-name>");
|
|
12840
13990
|
await next(output, rest[0]);
|
|
@@ -12859,7 +14009,8 @@ var CLASSIC_COMMANDS = [
|
|
|
12859
14009
|
"handoff",
|
|
12860
14010
|
"archive",
|
|
12861
14011
|
"hook-guard",
|
|
12862
|
-
"intent"
|
|
14012
|
+
"intent",
|
|
14013
|
+
"resume-probe"
|
|
12863
14014
|
];
|
|
12864
14015
|
var DEFAULT_HANDLERS = {
|
|
12865
14016
|
state: classicStateCommand,
|
|
@@ -12868,7 +14019,8 @@ var DEFAULT_HANDLERS = {
|
|
|
12868
14019
|
handoff: classicHandoffCommand,
|
|
12869
14020
|
archive: classicArchiveCommand,
|
|
12870
14021
|
"hook-guard": classicHookGuardCommand,
|
|
12871
|
-
intent: classicIntentCommand
|
|
14022
|
+
intent: classicIntentCommand,
|
|
14023
|
+
"resume-probe": classicResumeProbeCommand
|
|
12872
14024
|
};
|
|
12873
14025
|
function isClassicCommand(value) {
|
|
12874
14026
|
return CLASSIC_COMMANDS.includes(value);
|
|
@@ -12903,14 +14055,14 @@ async function dispatch(command, args, options, handlers) {
|
|
|
12903
14055
|
};
|
|
12904
14056
|
}
|
|
12905
14057
|
}
|
|
12906
|
-
function jsonResult(command,
|
|
14058
|
+
function jsonResult(command, result5) {
|
|
12907
14059
|
return {
|
|
12908
|
-
exitCode:
|
|
14060
|
+
exitCode: result5.exitCode,
|
|
12909
14061
|
stdout: JSON.stringify({
|
|
12910
14062
|
command: command ?? null,
|
|
12911
|
-
exitCode:
|
|
12912
|
-
...
|
|
12913
|
-
...
|
|
14063
|
+
exitCode: result5.exitCode,
|
|
14064
|
+
...result5.stdout === void 0 ? {} : { stdout: result5.stdout },
|
|
14065
|
+
...result5.stderr === void 0 ? {} : { stderr: result5.stderr }
|
|
12914
14066
|
}) + "\n"
|
|
12915
14067
|
};
|
|
12916
14068
|
}
|
|
@@ -12918,15 +14070,15 @@ async function runClassicCli(argv, handlers = DEFAULT_HANDLERS) {
|
|
|
12918
14070
|
const json = argv.includes("--json");
|
|
12919
14071
|
const args = argv.filter((argument) => argument !== "--json");
|
|
12920
14072
|
const command = args.shift();
|
|
12921
|
-
const
|
|
12922
|
-
return json ? jsonResult(command,
|
|
14073
|
+
const result5 = await dispatch(command, args, { json }, handlers);
|
|
14074
|
+
return json ? jsonResult(command, result5) : result5;
|
|
12923
14075
|
}
|
|
12924
14076
|
async function main(argv = process.argv.slice(2)) {
|
|
12925
|
-
const
|
|
12926
|
-
if (
|
|
12927
|
-
if (
|
|
12928
|
-
process.stderr.write(
|
|
12929
|
-
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;
|
|
12930
14082
|
}
|
|
12931
14083
|
var entry = process.argv[1];
|
|
12932
14084
|
if (entry && import.meta.url === pathToFileURL(entry).href) {
|