@rpamis/comet 0.4.0-beta.3 → 0.4.0-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +15 -1
- package/assets/manifest.json +2 -1
- package/assets/skills/comet/SKILL.md +54 -28
- package/assets/skills/comet/reference/auto-transition.md +1 -1
- package/assets/skills/comet/reference/comet-yaml-fields.md +11 -6
- package/assets/skills/comet/reference/context-recovery.md +12 -2
- package/assets/skills/comet/reference/decision-point.md +11 -0
- package/assets/skills/comet/reference/scripts.md +47 -15
- package/assets/skills/comet/reference/subagent-dispatch.md +6 -5
- package/assets/skills/comet/rules/comet-phase-guard.en.md +31 -28
- package/assets/skills/comet/rules/comet-phase-guard.md +31 -29
- package/assets/skills/comet/scripts/comet-resume-probe.mjs +3 -0
- package/assets/skills/comet/scripts/comet-runtime.mjs +1568 -476
- package/assets/skills/comet-any/SKILL.md +1 -1
- package/assets/skills/comet-any/reference/authored-zone-example.md +10 -5
- package/assets/skills/comet-any/reference/subagents/pause-points-author.md +14 -11
- package/assets/skills/comet-any/reference/subagents/skill-reviewer.md +2 -0
- package/assets/skills/comet-any/reference/subagents/workflow-entry-author.md +5 -4
- package/assets/skills/comet-archive/SKILL.md +52 -18
- package/assets/skills/comet-build/SKILL.md +64 -45
- package/assets/skills/comet-design/SKILL.md +24 -22
- package/assets/skills/comet-hotfix/SKILL.md +37 -34
- package/assets/skills/comet-open/SKILL.md +106 -57
- package/assets/skills/comet-tweak/SKILL.md +23 -18
- package/assets/skills/comet-verify/SKILL.md +57 -72
- package/assets/skills-zh/comet/SKILL.md +54 -28
- package/assets/skills-zh/comet/reference/auto-transition.md +1 -1
- package/assets/skills-zh/comet/reference/comet-yaml-fields.md +11 -6
- package/assets/skills-zh/comet/reference/context-recovery.md +12 -2
- package/assets/skills-zh/comet/reference/decision-point.md +11 -0
- package/assets/skills-zh/comet/reference/scripts.md +47 -15
- package/assets/skills-zh/comet/reference/subagent-dispatch.md +6 -5
- package/assets/skills-zh/comet-any/SKILL.md +1 -1
- package/assets/skills-zh/comet-any/reference/authored-zone-example.md +10 -5
- package/assets/skills-zh/comet-any/reference/subagents/pause-points-author.md +14 -12
- package/assets/skills-zh/comet-any/reference/subagents/skill-reviewer.md +2 -0
- package/assets/skills-zh/comet-any/reference/subagents/workflow-entry-author.md +5 -4
- package/assets/skills-zh/comet-archive/SKILL.md +52 -18
- package/assets/skills-zh/comet-build/SKILL.md +64 -45
- package/assets/skills-zh/comet-design/SKILL.md +24 -22
- package/assets/skills-zh/comet-hotfix/SKILL.md +36 -33
- package/assets/skills-zh/comet-open/SKILL.md +105 -56
- package/assets/skills-zh/comet-tweak/SKILL.md +22 -17
- package/assets/skills-zh/comet-verify/SKILL.md +57 -72
- package/dist/app/cli/comet-banner.d.ts +25 -0
- package/dist/app/cli/comet-banner.d.ts.map +1 -0
- package/dist/app/cli/comet-banner.js +265 -0
- package/dist/app/cli/comet-banner.js.map +1 -0
- package/dist/app/cli/index.js +36 -2
- package/dist/app/cli/index.js.map +1 -1
- package/dist/app/commands/classic.d.ts +4 -0
- package/dist/app/commands/classic.d.ts.map +1 -0
- package/dist/app/commands/classic.js +11 -0
- package/dist/app/commands/classic.js.map +1 -0
- package/dist/app/commands/doctor.d.ts.map +1 -1
- package/dist/app/commands/doctor.js +99 -37
- 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 +1 -1
- package/dist/app/commands/init.d.ts.map +1 -1
- package/dist/app/commands/init.js +72 -25
- 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 +5 -0
- package/dist/app/commands/uninstall.d.ts.map +1 -1
- package/dist/app/commands/uninstall.js +264 -53
- 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 +558 -86
- 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 +1 -0
- package/dist/domains/bundle/bundle-platform.d.ts.map +1 -1
- package/dist/domains/bundle/bundle-platform.js +9 -3
- 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 +83 -80
- 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 +179 -42
- 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 +4 -4
- package/dist/domains/comet-classic/classic-resolver.js.map +1 -1
- package/dist/domains/comet-classic/classic-resume-probe-command.d.ts +3 -0
- package/dist/domains/comet-classic/classic-resume-probe-command.d.ts.map +1 -0
- package/dist/domains/comet-classic/classic-resume-probe-command.js +58 -0
- package/dist/domains/comet-classic/classic-resume-probe-command.js.map +1 -0
- package/dist/domains/comet-classic/classic-resume-probe-entry.d.ts +2 -0
- package/dist/domains/comet-classic/classic-resume-probe-entry.d.ts.map +1 -0
- package/dist/domains/comet-classic/classic-resume-probe-entry.js +4 -0
- package/dist/domains/comet-classic/classic-resume-probe-entry.js.map +1 -0
- package/dist/domains/comet-classic/classic-resume-probe.d.ts +29 -0
- package/dist/domains/comet-classic/classic-resume-probe.d.ts.map +1 -0
- package/dist/domains/comet-classic/classic-resume-probe.js +386 -0
- package/dist/domains/comet-classic/classic-resume-probe.js.map +1 -0
- package/dist/domains/comet-classic/classic-runtime-run.d.ts +2 -1
- package/dist/domains/comet-classic/classic-runtime-run.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-runtime-run.js +38 -0
- package/dist/domains/comet-classic/classic-runtime-run.js.map +1 -1
- package/dist/domains/comet-classic/classic-state-command.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-state-command.js +150 -25
- package/dist/domains/comet-classic/classic-state-command.js.map +1 -1
- package/dist/domains/comet-classic/classic-state.d.ts +5 -2
- package/dist/domains/comet-classic/classic-state.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-state.js +17 -1
- package/dist/domains/comet-classic/classic-state.js.map +1 -1
- package/dist/domains/comet-classic/classic-store.d.ts +4 -1
- package/dist/domains/comet-classic/classic-store.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-store.js +4 -3
- package/dist/domains/comet-classic/classic-store.js.map +1 -1
- package/dist/domains/comet-classic/classic-transitions.d.ts +1 -1
- package/dist/domains/comet-classic/classic-transitions.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-transitions.js +36 -3
- 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 +8 -1
- 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/_commonjsHelpers-CqkleIqs.js +1 -0
- package/dist/domains/dashboard/web/assets/arc-TGVUG2WN.js +1 -0
- package/dist/domains/dashboard/web/assets/architectureDiagram-3BPJPVTR-ASHmiHoP.js +36 -0
- package/dist/domains/dashboard/web/assets/blockDiagram-GPEHLZMM-CwASxBKe.js +132 -0
- package/dist/domains/dashboard/web/assets/c4Diagram-AAUBKEIU-BU8z_WNk.js +10 -0
- package/dist/domains/dashboard/web/assets/channel-kp6Ng2JJ.js +1 -0
- package/dist/domains/dashboard/web/assets/chunk-2J33WTMH-CBGlUx4_.js +1 -0
- package/dist/domains/dashboard/web/assets/chunk-4BX2VUAB-CS-iGcFz.js +1 -0
- package/dist/domains/dashboard/web/assets/chunk-55IACEB6-BUtCVJat.js +1 -0
- package/dist/domains/dashboard/web/assets/chunk-727SXJPM-C94tj_pf.js +206 -0
- package/dist/domains/dashboard/web/assets/chunk-AQP2D5EJ-BFOKcpdG.js +231 -0
- package/dist/domains/dashboard/web/assets/chunk-FMBD7UC4-Dmx8B-st.js +15 -0
- package/dist/domains/dashboard/web/assets/chunk-ND2GUHAM-DYV00CNn.js +1 -0
- package/dist/domains/dashboard/web/assets/chunk-QZHKN3VN-DMJJyQnw.js +1 -0
- package/dist/domains/dashboard/web/assets/classDiagram-4FO5ZUOK-yBDjgMY_.js +1 -0
- package/dist/domains/dashboard/web/assets/classDiagram-v2-Q7XG4LA2-yBDjgMY_.js +1 -0
- package/dist/domains/dashboard/web/assets/cose-bilkent-S5V4N54A-P4nE2rea.js +1 -0
- package/dist/domains/dashboard/web/assets/cynefin-VYW2F7L2-C-R1HXDv.js +166 -0
- package/dist/domains/dashboard/web/assets/cytoscape.esm-D3_iZ_3b.js +321 -0
- package/dist/domains/dashboard/web/assets/dagre-BM42HDAG-BH8d-C9z.js +4 -0
- package/dist/domains/dashboard/web/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/dist/domains/dashboard/web/assets/diagram-2AECGRRQ-DpXTgQIH.js +43 -0
- package/dist/domains/dashboard/web/assets/diagram-5GNKFQAL-DSiFJzSw.js +10 -0
- package/dist/domains/dashboard/web/assets/diagram-KO2AKTUF-DULMkben.js +3 -0
- package/dist/domains/dashboard/web/assets/diagram-LMA3HP47-Pi8m_NO9.js +24 -0
- package/dist/domains/dashboard/web/assets/diagram-OG6HWLK6-BSe6bCWm.js +24 -0
- package/dist/domains/dashboard/web/assets/erDiagram-TEJ5UH35-zb-FEcbq.js +85 -0
- package/dist/domains/dashboard/web/assets/flowDiagram-I6XJVG4X-Dr-Gk0z2.js +162 -0
- package/dist/domains/dashboard/web/assets/ganttDiagram-6RSMTGT7-qs6Zb2A6.js +292 -0
- package/dist/domains/dashboard/web/assets/gitGraphDiagram-PVQCEYII-BQ314orT.js +106 -0
- package/dist/domains/dashboard/web/assets/graph--OzhPTMs.js +1 -0
- package/dist/domains/dashboard/web/assets/index-BLAfkNTu.css +1 -0
- package/dist/domains/dashboard/web/assets/index-DlSYK56b.js +139 -0
- package/dist/domains/dashboard/web/assets/index-DtLUOcAW.js +24 -0
- package/dist/domains/dashboard/web/assets/index-QO0z6F4w.js +10 -0
- package/dist/domains/dashboard/web/assets/infoDiagram-5YYISTIA-O0bQp98A.js +2 -0
- package/dist/domains/dashboard/web/assets/init-Gi6I4Gst.js +1 -0
- package/dist/domains/dashboard/web/assets/ishikawaDiagram-YF4QCWOH-BPC7wbKh.js +70 -0
- package/dist/domains/dashboard/web/assets/journeyDiagram-JHISSGLW-ohViOd4b.js +139 -0
- package/dist/domains/dashboard/web/assets/kanban-definition-UN3LZRKU-CHleNdbG.js +89 -0
- package/dist/domains/dashboard/web/assets/katex-HP8lGamR.js +257 -0
- package/dist/domains/dashboard/web/assets/layout-SsrduOYp.js +1 -0
- package/dist/domains/dashboard/web/assets/linear-CyqiW--Q.js +1 -0
- package/dist/domains/dashboard/web/assets/marked.esm-DECB3bxB.js +64 -0
- package/dist/domains/dashboard/web/assets/mermaid.core-qS9XvtaK.js +301 -0
- package/dist/domains/dashboard/web/assets/mindmap-definition-RKZ34NQL-oaGkqO9l.js +96 -0
- package/dist/domains/dashboard/web/assets/ordinal-Cboi1Yqb.js +1 -0
- package/dist/domains/dashboard/web/assets/pieDiagram-4H26LBE5-CPcB-AFh.js +30 -0
- package/dist/domains/dashboard/web/assets/purify.es-VaSPOPhr.js +3 -0
- package/dist/domains/dashboard/web/assets/quadrantDiagram-W4KKPZXB-LFy52Qtj.js +7 -0
- package/dist/domains/dashboard/web/assets/requirementDiagram-4Y6WPE33-BdjeaYP_.js +84 -0
- package/dist/domains/dashboard/web/assets/sankeyDiagram-5OEKKPKP-DDJ4styL.js +40 -0
- package/dist/domains/dashboard/web/assets/sequenceDiagram-3UESZ5HK-BL5bFNoz.js +162 -0
- package/dist/domains/dashboard/web/assets/stateDiagram-AJRCARHV-BNUfikui.js +1 -0
- package/dist/domains/dashboard/web/assets/stateDiagram-v2-BHNVJYJU-DGrsmWts.js +1 -0
- package/dist/domains/dashboard/web/assets/timeline-definition-PNZ67QCA--ps2uu6L.js +120 -0
- package/dist/domains/dashboard/web/assets/vennDiagram-CIIHVFJN-C2q8q26c.js +34 -0
- package/dist/domains/dashboard/web/assets/wardleyDiagram-YWT4CUSO-BUH9gXqd.js +78 -0
- package/dist/domains/dashboard/web/assets/xychartDiagram-2RQKCTM6-B-DielEy.js +7 -0
- 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/factory/package.d.ts.map +1 -1
- package/dist/domains/factory/package.js +20 -6
- package/dist/domains/factory/package.js.map +1 -1
- package/dist/domains/integrations/openspec.d.ts +4 -1
- package/dist/domains/integrations/openspec.d.ts.map +1 -1
- package/dist/domains/integrations/openspec.js +62 -10
- 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/json-object.d.ts +13 -0
- package/dist/domains/skill/json-object.d.ts.map +1 -0
- package/dist/domains/skill/json-object.js +24 -0
- package/dist/domains/skill/json-object.js.map +1 -0
- 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-inspect.d.ts +9 -0
- package/dist/domains/skill/platform-inspect.d.ts.map +1 -0
- package/dist/domains/skill/platform-inspect.js +149 -0
- package/dist/domains/skill/platform-inspect.js.map +1 -0
- package/dist/domains/skill/platform-install.d.ts +20 -9
- package/dist/domains/skill/platform-install.d.ts.map +1 -1
- package/dist/domains/skill/platform-install.js +356 -153
- 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 +259 -149
- package/dist/domains/skill/uninstall.js.map +1 -1
- package/dist/platform/fs/file-system.d.ts +3 -3
- package/dist/platform/fs/file-system.d.ts.map +1 -1
- package/dist/platform/fs/file-system.js +21 -11
- package/dist/platform/fs/file-system.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 +9 -1
- package/dist/platform/install/platforms.d.ts.map +1 -1
- package/dist/platform/install/platforms.js +23 -4
- package/dist/platform/install/platforms.js.map +1 -1
- package/dist/platform/install/project-registry.d.ts +38 -0
- package/dist/platform/install/project-registry.d.ts.map +1 -0
- package/dist/platform/install/project-registry.js +213 -0
- package/dist/platform/install/project-registry.js.map +1 -0
- package/dist/platform/install/skill-root-owner.d.ts +15 -0
- package/dist/platform/install/skill-root-owner.d.ts.map +1 -0
- package/dist/platform/install/skill-root-owner.js +68 -0
- package/dist/platform/install/skill-root-owner.js.map +1 -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 +29 -2
- package/dist/domains/dashboard/web/assets/index-BcO3_QlT.css +0 -1
- package/dist/domains/dashboard/web/assets/index-CjU3VXVu.js +0 -22
|
@@ -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
|
|
|
@@ -7731,7 +7739,7 @@ function fullBuildConfigured(classic) {
|
|
|
7731
7739
|
}
|
|
7732
7740
|
function presetBuildConfigured(classic) {
|
|
7733
7741
|
return Boolean(
|
|
7734
|
-
classic.buildMode === "direct" && classic.tddMode === "direct" && classic.isolation
|
|
7742
|
+
classic.buildMode === "direct" && classic.tddMode === "direct" && classic.isolation !== null && classic.verifyMode === "light"
|
|
7735
7743
|
);
|
|
7736
7744
|
}
|
|
7737
7745
|
function resolveBuild(profile, classic, evidence) {
|
|
@@ -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
|
|
|
@@ -7800,10 +7808,11 @@ var BUILD_PAUSES = ["plan-ready"];
|
|
|
7800
7808
|
var SUBAGENT_DISPATCH = ["confirmed"];
|
|
7801
7809
|
var TDD_MODES = ["tdd", "direct"];
|
|
7802
7810
|
var REVIEW_MODES = ["off", "standard", "thorough"];
|
|
7803
|
-
var ISOLATIONS = ["branch", "worktree"];
|
|
7811
|
+
var ISOLATIONS = ["current", "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",
|
|
@@ -7821,10 +7830,12 @@ var CLASSIC_WIRE_KEYS = [
|
|
|
7821
7830
|
"design_doc",
|
|
7822
7831
|
"plan",
|
|
7823
7832
|
"verify_result",
|
|
7833
|
+
"verify_failures",
|
|
7824
7834
|
"verification_report",
|
|
7825
7835
|
"branch_status",
|
|
7826
7836
|
"created_at",
|
|
7827
7837
|
"verified_at",
|
|
7838
|
+
"archive_confirmation",
|
|
7828
7839
|
"archived",
|
|
7829
7840
|
"direct_override",
|
|
7830
7841
|
"handoff_context",
|
|
@@ -7881,6 +7892,14 @@ function booleanValue(doc, key, nullable = true) {
|
|
|
7881
7892
|
}
|
|
7882
7893
|
return value;
|
|
7883
7894
|
}
|
|
7895
|
+
function nonNegativeInteger(doc, key, fallback = 0) {
|
|
7896
|
+
const value = doc[key];
|
|
7897
|
+
if (value === null || value === void 0 || value === "") return fallback;
|
|
7898
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
|
|
7899
|
+
throw new Error(`Invalid Classic state: ${key} must be a non-negative integer`);
|
|
7900
|
+
}
|
|
7901
|
+
return value;
|
|
7902
|
+
}
|
|
7884
7903
|
function relativePath(doc, key) {
|
|
7885
7904
|
const value = nullableString(doc, key);
|
|
7886
7905
|
if (value === null) return null;
|
|
@@ -7929,10 +7948,12 @@ function classicStateFromDocument(doc) {
|
|
|
7929
7948
|
designDoc: relativePath(doc, "design_doc"),
|
|
7930
7949
|
plan: relativePath(doc, "plan"),
|
|
7931
7950
|
verifyResult: enumValue(doc, "verify_result", VERIFY_RESULTS, false),
|
|
7951
|
+
verifyFailures: nonNegativeInteger(doc, "verify_failures"),
|
|
7932
7952
|
verificationReport: relativePath(doc, "verification_report"),
|
|
7933
7953
|
branchStatus: enumValue(doc, "branch_status", BRANCH_STATUSES),
|
|
7934
7954
|
createdAt: nullableString(doc, "created_at"),
|
|
7935
7955
|
verifiedAt: nullableString(doc, "verified_at"),
|
|
7956
|
+
archiveConfirmation: enumValue(doc, "archive_confirmation", ARCHIVE_CONFIRMATIONS),
|
|
7936
7957
|
archived: booleanValue(doc, "archived", false),
|
|
7937
7958
|
directOverride: booleanValue(doc, "direct_override"),
|
|
7938
7959
|
handoffContext: relativePath(doc, "handoff_context"),
|
|
@@ -7992,10 +8013,12 @@ function classicStateToDocument(state) {
|
|
|
7992
8013
|
design_doc: state.designDoc,
|
|
7993
8014
|
plan: state.plan,
|
|
7994
8015
|
verify_result: state.verifyResult,
|
|
8016
|
+
verify_failures: state.verifyFailures,
|
|
7995
8017
|
verification_report: state.verificationReport,
|
|
7996
8018
|
branch_status: state.branchStatus,
|
|
7997
8019
|
created_at: state.createdAt,
|
|
7998
8020
|
verified_at: state.verifiedAt,
|
|
8021
|
+
archive_confirmation: state.archiveConfirmation,
|
|
7999
8022
|
archived: state.archived,
|
|
8000
8023
|
direct_override: state.directOverride,
|
|
8001
8024
|
handoff_context: state.handoffContext,
|
|
@@ -8077,7 +8100,8 @@ async function readDocument(file) {
|
|
|
8077
8100
|
documentRecord(document);
|
|
8078
8101
|
return document;
|
|
8079
8102
|
}
|
|
8080
|
-
async function readClassicState(changeDir) {
|
|
8103
|
+
async function readClassicState(changeDir, options = {}) {
|
|
8104
|
+
const shouldMigrate = options.migrate !== false;
|
|
8081
8105
|
const file = path4.join(changeDir, ".comet.yaml");
|
|
8082
8106
|
const document = await readDocument(file);
|
|
8083
8107
|
let doc = documentRecord(document);
|
|
@@ -8087,13 +8111,13 @@ async function readClassicState(changeDir) {
|
|
|
8087
8111
|
if (!run && doc.run_id && doc.skill) {
|
|
8088
8112
|
const { runStateFromDocument: runStateFromDocument2 } = await Promise.resolve().then(() => (init_state(), state_exports));
|
|
8089
8113
|
run = runStateFromDocument2(doc);
|
|
8090
|
-
if (run) {
|
|
8114
|
+
if (run && shouldMigrate) {
|
|
8091
8115
|
await writeRunState(changeDir, run);
|
|
8092
8116
|
stripLegacyRunFields(document);
|
|
8093
8117
|
migrated = true;
|
|
8094
8118
|
}
|
|
8095
8119
|
}
|
|
8096
|
-
if (migrated) {
|
|
8120
|
+
if (migrated && shouldMigrate) {
|
|
8097
8121
|
const temporary = path4.join(changeDir, `.comet.yaml.${randomUUID2()}.tmp`);
|
|
8098
8122
|
await fs4.writeFile(temporary, document.toString(), "utf8");
|
|
8099
8123
|
await fs4.rename(temporary, file);
|
|
@@ -8830,13 +8854,13 @@ function artifactKey(code) {
|
|
|
8830
8854
|
return code.replaceAll(".", "_").replaceAll("-", "_");
|
|
8831
8855
|
}
|
|
8832
8856
|
async function migrationArtifacts(changeDir, evidence) {
|
|
8833
|
-
const
|
|
8857
|
+
const projectRoot2 = projectRootFor2(changeDir);
|
|
8834
8858
|
const artifacts = Object.fromEntries(
|
|
8835
8859
|
evidence.filter((item) => item.satisfied && item.source).map((item) => [artifactKey(item.code), item.source])
|
|
8836
8860
|
);
|
|
8837
8861
|
const progress = path9.join(changeDir, "subagent-progress.md");
|
|
8838
8862
|
if (await pathExists2(progress)) {
|
|
8839
|
-
artifacts.subagent_progress = path9.relative(
|
|
8863
|
+
artifacts.subagent_progress = path9.relative(projectRoot2, progress).split(path9.sep).join("/");
|
|
8840
8864
|
}
|
|
8841
8865
|
const handoff = evidence.find((item) => item.code === "design.handoff" && item.satisfied);
|
|
8842
8866
|
if (handoff?.source) artifacts.handoff_context = handoff.source;
|
|
@@ -8928,11 +8952,11 @@ async function ensureClassicRun(changeDir, options) {
|
|
|
8928
8952
|
classicMigration: CLASSIC_MIGRATION_VERSION
|
|
8929
8953
|
};
|
|
8930
8954
|
const artifacts = await migrationArtifacts(changeDir, evidence);
|
|
8931
|
-
const
|
|
8955
|
+
const projectRoot2 = projectRootFor2(changeDir);
|
|
8932
8956
|
const handoff = evidence.find((item) => item.code === "design.handoff" && item.satisfied);
|
|
8933
8957
|
let context = null;
|
|
8934
8958
|
if (handoff?.source) {
|
|
8935
|
-
context = await fs8.readFile(path9.resolve(
|
|
8959
|
+
context = await fs8.readFile(path9.resolve(projectRoot2, handoff.source), "utf8");
|
|
8936
8960
|
await writeContext(changeDir, run.contextRef, context);
|
|
8937
8961
|
createdFiles.push(path9.resolve(changeDir, run.contextRef));
|
|
8938
8962
|
}
|
|
@@ -9251,6 +9275,46 @@ async function ensureStrictClassicRuntimeRun(changeDir) {
|
|
|
9251
9275
|
}
|
|
9252
9276
|
return ensureClassicRuntimeRun(changeDir);
|
|
9253
9277
|
}
|
|
9278
|
+
async function validateClassicRuntimeRun(changeDir, existingProjection) {
|
|
9279
|
+
const projection = existingProjection ?? await readClassicState(changeDir, { migrate: false });
|
|
9280
|
+
const unknownKeys = Array.from(new Set(projection.unknownKeys)).sort();
|
|
9281
|
+
if (unknownKeys.length > 0) {
|
|
9282
|
+
throw new Error(`Invalid Classic state: unknown field(s): ${unknownKeys.join(", ")}`);
|
|
9283
|
+
}
|
|
9284
|
+
if (!projection.classic || !projection.run) {
|
|
9285
|
+
throw new Error("Classic runtime validation requires synchronized Classic and Run projections");
|
|
9286
|
+
}
|
|
9287
|
+
if (projection.classic.classicMigration !== CLASSIC_MIGRATION_VERSION) {
|
|
9288
|
+
throw new Error("Classic Run exists without a supported classic_migration marker");
|
|
9289
|
+
}
|
|
9290
|
+
const root = await classicRuntimeRoot();
|
|
9291
|
+
const skillPackage = root ? await loadClassicRuntimePackage(root) : embeddedClassicRuntimePackage(path10.dirname(fileURLToPath(import.meta.url)));
|
|
9292
|
+
if (projection.run.skill !== skillPackage.definition.metadata.name) {
|
|
9293
|
+
throw new Error(
|
|
9294
|
+
`Classic Run skill mismatch: expected ${skillPackage.definition.metadata.name}, got ${projection.run.skill}`
|
|
9295
|
+
);
|
|
9296
|
+
}
|
|
9297
|
+
const snapshot = await readSkillSnapshot(changeDir, projection.run.skillHash);
|
|
9298
|
+
if (snapshot.definition.metadata.name !== projection.run.skill) {
|
|
9299
|
+
throw new Error(
|
|
9300
|
+
`Classic Run snapshot skill mismatch: expected ${projection.run.skill}, got ${snapshot.definition.metadata.name}`
|
|
9301
|
+
);
|
|
9302
|
+
}
|
|
9303
|
+
const evidence = await collectClassicEvidence(changeDir, projection);
|
|
9304
|
+
const currentStep = resolveClassicStepId(projection.classic, evidence);
|
|
9305
|
+
if (projection.run.currentStep !== currentStep) {
|
|
9306
|
+
throw new Error(
|
|
9307
|
+
`Classic Run step mismatch: expected ${currentStep}, got ${projection.run.currentStep}`
|
|
9308
|
+
);
|
|
9309
|
+
}
|
|
9310
|
+
return {
|
|
9311
|
+
classic: projection.classic,
|
|
9312
|
+
run: projection.run,
|
|
9313
|
+
evidence,
|
|
9314
|
+
migrated: false,
|
|
9315
|
+
snapshotDir: path10.join(changeDir, ".comet", "skill-snapshots", projection.run.skillHash)
|
|
9316
|
+
};
|
|
9317
|
+
}
|
|
9254
9318
|
async function transitionClassicRuntimeRun(changeDir, classic, run, data) {
|
|
9255
9319
|
const projection = await readClassicState(changeDir);
|
|
9256
9320
|
if (!projection.classic || !projection.run) {
|
|
@@ -9313,6 +9377,7 @@ var CLASSIC_TRANSITION_EVENTS = [
|
|
|
9313
9377
|
"build-complete",
|
|
9314
9378
|
"verify-pass",
|
|
9315
9379
|
"verify-fail",
|
|
9380
|
+
"archive-confirm",
|
|
9316
9381
|
"archive-reopen",
|
|
9317
9382
|
"archived",
|
|
9318
9383
|
"preset-escalate"
|
|
@@ -9336,13 +9401,18 @@ var CLASSIC_TRANSITION_TABLE = {
|
|
|
9336
9401
|
"verify-pass": {
|
|
9337
9402
|
event: "verify-pass",
|
|
9338
9403
|
from: "verify",
|
|
9339
|
-
guardRefs: ["verification-report-present"
|
|
9404
|
+
guardRefs: ["verification-report-present"]
|
|
9340
9405
|
},
|
|
9341
9406
|
"verify-fail": {
|
|
9342
9407
|
event: "verify-fail",
|
|
9343
9408
|
from: "verify",
|
|
9344
9409
|
guardRefs: ["verification-failed"]
|
|
9345
9410
|
},
|
|
9411
|
+
"archive-confirm": {
|
|
9412
|
+
event: "archive-confirm",
|
|
9413
|
+
from: "archive",
|
|
9414
|
+
guardRefs: ["archive-final-confirmation"]
|
|
9415
|
+
},
|
|
9346
9416
|
"archive-reopen": {
|
|
9347
9417
|
event: "archive-reopen",
|
|
9348
9418
|
from: "archive",
|
|
@@ -9351,7 +9421,7 @@ var CLASSIC_TRANSITION_TABLE = {
|
|
|
9351
9421
|
archived: {
|
|
9352
9422
|
event: "archived",
|
|
9353
9423
|
from: "archive",
|
|
9354
|
-
guardRefs: ["verify-result-pass"]
|
|
9424
|
+
guardRefs: ["verify-result-pass", "archive-confirmed"]
|
|
9355
9425
|
},
|
|
9356
9426
|
"preset-escalate": {
|
|
9357
9427
|
event: "preset-escalate",
|
|
@@ -9391,17 +9461,22 @@ function applyClassicTransition(current, event, options = {}) {
|
|
|
9391
9461
|
const preserveEvidence = classic.verifyResult === "fail";
|
|
9392
9462
|
setField(classic, effects, "phase", "verify");
|
|
9393
9463
|
setField(classic, effects, "verifyResult", "pending");
|
|
9464
|
+
setField(classic, effects, "branchStatus", "pending");
|
|
9394
9465
|
if (!preserveEvidence) {
|
|
9395
9466
|
setField(classic, effects, "verificationReport", null);
|
|
9396
|
-
setField(classic, effects, "branchStatus", "pending");
|
|
9397
9467
|
}
|
|
9398
9468
|
} else if (event === "verify-pass") {
|
|
9399
9469
|
setField(classic, effects, "verifyResult", "pass");
|
|
9470
|
+
setField(classic, effects, "verifyFailures", 0);
|
|
9400
9471
|
setField(classic, effects, "phase", "archive");
|
|
9401
9472
|
setField(classic, effects, "verifiedAt", dateOnly(now));
|
|
9473
|
+
setField(classic, effects, "archiveConfirmation", "pending");
|
|
9474
|
+
setField(classic, effects, "branchStatus", "pending");
|
|
9402
9475
|
} else if (event === "verify-fail") {
|
|
9403
9476
|
setField(classic, effects, "verifyResult", "fail");
|
|
9477
|
+
setField(classic, effects, "verifyFailures", classic.verifyFailures + 1);
|
|
9404
9478
|
setField(classic, effects, "phase", "build");
|
|
9479
|
+
setField(classic, effects, "branchStatus", "pending");
|
|
9405
9480
|
} else if (event === "preset-escalate") {
|
|
9406
9481
|
if (classic.workflow !== "hotfix" && classic.workflow !== "tweak") {
|
|
9407
9482
|
throw new Error(
|
|
@@ -9412,15 +9487,35 @@ function applyClassicTransition(current, event, options = {}) {
|
|
|
9412
9487
|
setField(classic, effects, "classicProfile", "full");
|
|
9413
9488
|
setField(classic, effects, "phase", "design");
|
|
9414
9489
|
setField(classic, effects, "designDoc", null);
|
|
9490
|
+
setField(classic, effects, "buildPause", null);
|
|
9491
|
+
setField(classic, effects, "buildMode", null);
|
|
9492
|
+
setField(classic, effects, "subagentDispatch", null);
|
|
9493
|
+
setField(classic, effects, "tddMode", null);
|
|
9494
|
+
setField(classic, effects, "reviewMode", null);
|
|
9495
|
+
setField(classic, effects, "isolation", null);
|
|
9496
|
+
setField(classic, effects, "verifyMode", null);
|
|
9497
|
+
setField(classic, effects, "directOverride", null);
|
|
9498
|
+
} else if (event === "archive-confirm") {
|
|
9499
|
+
if (classic.verifyResult !== "pass") {
|
|
9500
|
+
throw new Error(`Cannot apply ${event}: verifyResult must be pass`);
|
|
9501
|
+
}
|
|
9502
|
+
if (classic.archived) throw new Error(`Cannot apply ${event}: already archived`);
|
|
9503
|
+
setField(classic, effects, "archiveConfirmation", "confirmed");
|
|
9415
9504
|
} else if (event === "archive-reopen") {
|
|
9416
9505
|
if (classic.archived) throw new Error(`Cannot apply ${event}: already archived`);
|
|
9417
9506
|
setField(classic, effects, "verifyResult", "pending");
|
|
9507
|
+
setField(classic, effects, "verifyFailures", 0);
|
|
9418
9508
|
setField(classic, effects, "phase", "verify");
|
|
9419
9509
|
setField(classic, effects, "verifiedAt", null);
|
|
9510
|
+
setField(classic, effects, "archiveConfirmation", null);
|
|
9511
|
+
setField(classic, effects, "branchStatus", "pending");
|
|
9420
9512
|
} else {
|
|
9421
9513
|
if (classic.verifyResult !== "pass") {
|
|
9422
9514
|
throw new Error(`Cannot apply ${event}: verifyResult must be pass`);
|
|
9423
9515
|
}
|
|
9516
|
+
if (classic.archiveConfirmation !== "confirmed") {
|
|
9517
|
+
throw new Error(`Cannot apply ${event}: archiveConfirmation must be confirmed`);
|
|
9518
|
+
}
|
|
9424
9519
|
setField(classic, effects, "archived", true);
|
|
9425
9520
|
}
|
|
9426
9521
|
return { classic, effects, definition };
|
|
@@ -9483,6 +9578,33 @@ function artifactsHash(artifacts) {
|
|
|
9483
9578
|
)
|
|
9484
9579
|
);
|
|
9485
9580
|
}
|
|
9581
|
+
function exactlyOneFinalNewline(markdown) {
|
|
9582
|
+
return `${markdown.replace(/\n+$/u, "")}
|
|
9583
|
+
`;
|
|
9584
|
+
}
|
|
9585
|
+
function annotatedMarkdown(original, archiveName, extraFields) {
|
|
9586
|
+
const normalized2 = original.replace(/\r\n/gu, "\n");
|
|
9587
|
+
const lines = normalized2.split("\n");
|
|
9588
|
+
const closingDelimiter = lines[0] === "---" ? lines.indexOf("---", 1) : -1;
|
|
9589
|
+
const extraFieldName = extraFields.match(/^([^:\n]+):/u)?.[1]?.trim();
|
|
9590
|
+
if (closingDelimiter !== -1) {
|
|
9591
|
+
const frontmatter = lines.slice(1, closingDelimiter).filter((line) => {
|
|
9592
|
+
const fieldName = line.match(/^([^:\n]+):/u)?.[1]?.trim();
|
|
9593
|
+
if (fieldName === void 0) return true;
|
|
9594
|
+
return fieldName !== "archived-with" && fieldName !== extraFieldName;
|
|
9595
|
+
});
|
|
9596
|
+
frontmatter.push(`archived-with: ${archiveName}`);
|
|
9597
|
+
if (extraFields) frontmatter.push(extraFields);
|
|
9598
|
+
return exactlyOneFinalNewline(
|
|
9599
|
+
["---", ...frontmatter, "---", ...lines.slice(closingDelimiter + 1)].join("\n")
|
|
9600
|
+
);
|
|
9601
|
+
}
|
|
9602
|
+
const header = ["---", `archived-with: ${archiveName}`];
|
|
9603
|
+
if (extraFields) header.push(extraFields);
|
|
9604
|
+
if (extraFieldName !== "status") header.push("status: final");
|
|
9605
|
+
header.push("---");
|
|
9606
|
+
return exactlyOneFinalNewline([...header, normalized2].join("\n"));
|
|
9607
|
+
}
|
|
9486
9608
|
async function findArchiveDir(change, preferred) {
|
|
9487
9609
|
if (await exists2(preferred)) return preferred;
|
|
9488
9610
|
const archiveRoot = "openspec/changes/archive";
|
|
@@ -9521,37 +9643,7 @@ async function annotateFrontmatter(output, file, archiveName, extraFields, dryRu
|
|
|
9521
9643
|
return;
|
|
9522
9644
|
}
|
|
9523
9645
|
const original = await fs11.readFile(file, "utf8");
|
|
9524
|
-
const
|
|
9525
|
-
let updated;
|
|
9526
|
-
if (firstLine === "---") {
|
|
9527
|
-
const lines = original.split(/\r?\n/u);
|
|
9528
|
-
const out = [];
|
|
9529
|
-
let sawFirst = false;
|
|
9530
|
-
for (const line of lines) {
|
|
9531
|
-
if (/^archived-with:/u.test(line)) continue;
|
|
9532
|
-
if (line === "---") {
|
|
9533
|
-
if (!sawFirst) {
|
|
9534
|
-
out.push(line);
|
|
9535
|
-
sawFirst = true;
|
|
9536
|
-
continue;
|
|
9537
|
-
}
|
|
9538
|
-
out.push(`archived-with: ${archiveName}`);
|
|
9539
|
-
if (extraFields) out.push(extraFields);
|
|
9540
|
-
out.push(line);
|
|
9541
|
-
} else {
|
|
9542
|
-
out.push(line);
|
|
9543
|
-
}
|
|
9544
|
-
}
|
|
9545
|
-
updated = `${out.join("\n")}
|
|
9546
|
-
`;
|
|
9547
|
-
} else {
|
|
9548
|
-
const header = ["---", `archived-with: ${archiveName}`];
|
|
9549
|
-
if (extraFields) header.push(extraFields);
|
|
9550
|
-
header.push("status: final", "---");
|
|
9551
|
-
updated = `${header.join("\n")}
|
|
9552
|
-
${original}`;
|
|
9553
|
-
if (!updated.endsWith("\n")) updated += "\n";
|
|
9554
|
-
}
|
|
9646
|
+
const updated = annotatedMarkdown(original, archiveName, extraFields);
|
|
9555
9647
|
await fs11.writeFile(file, updated);
|
|
9556
9648
|
output.stderr.push(green(` [OK] Annotated: ${file}`));
|
|
9557
9649
|
output.stepsOk += 1;
|
|
@@ -9636,6 +9728,13 @@ var classicArchiveCommand = async (args) => {
|
|
|
9636
9728
|
if (runtime.run.pending && runtime.run.pending !== actionId) {
|
|
9637
9729
|
throw new ArchiveFailure(red(`FATAL: another action is pending: ${runtime.run.pending}`));
|
|
9638
9730
|
}
|
|
9731
|
+
if (!recovering && !classic.archived && classic.archiveConfirmation !== "confirmed") {
|
|
9732
|
+
throw new ArchiveFailure(
|
|
9733
|
+
red(
|
|
9734
|
+
`FATAL: archive_confirmation is '${classic.archiveConfirmation ?? "null"}', expected 'confirmed'. Run final archive confirmation first.`
|
|
9735
|
+
)
|
|
9736
|
+
);
|
|
9737
|
+
}
|
|
9639
9738
|
if (!recovering) {
|
|
9640
9739
|
const action = {
|
|
9641
9740
|
id: actionId,
|
|
@@ -9707,7 +9806,10 @@ var classicArchiveCommand = async (args) => {
|
|
|
9707
9806
|
archive_directory: archiveDir
|
|
9708
9807
|
};
|
|
9709
9808
|
await writeArtifacts(archiveDir, archivedProjection.run.artifactsRef, artifacts);
|
|
9710
|
-
const archiveTransition = applyClassicTransition(
|
|
9809
|
+
const archiveTransition = applyClassicTransition(
|
|
9810
|
+
recovering && archivedProjection.classic.archiveConfirmation !== "confirmed" ? { ...archivedProjection.classic, archiveConfirmation: "confirmed" } : archivedProjection.classic,
|
|
9811
|
+
"archived"
|
|
9812
|
+
);
|
|
9711
9813
|
const archivedClassic = archiveTransition.classic;
|
|
9712
9814
|
let transitionedRun = archivedProjection.run;
|
|
9713
9815
|
if (archivedProjection.run.currentStep !== "completed" || archivedProjection.run.status !== "completed") {
|
|
@@ -9805,11 +9907,98 @@ var classicArchiveCommand = async (args) => {
|
|
|
9805
9907
|
};
|
|
9806
9908
|
|
|
9807
9909
|
// domains/comet-classic/classic-guard.ts
|
|
9808
|
-
var
|
|
9910
|
+
var import_yaml5 = __toESM(require_dist(), 1);
|
|
9809
9911
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
9810
9912
|
import { createHash as createHash4 } from "crypto";
|
|
9811
|
-
import { existsSync, promises as
|
|
9812
|
-
import
|
|
9913
|
+
import { existsSync, promises as fs15, readFileSync } from "fs";
|
|
9914
|
+
import path16 from "path";
|
|
9915
|
+
|
|
9916
|
+
// domains/comet-classic/classic-command-checks.ts
|
|
9917
|
+
import path13 from "path";
|
|
9918
|
+
function validateScope(scope) {
|
|
9919
|
+
if (scope !== "build" && scope !== "verify") {
|
|
9920
|
+
throw new Error(`Invalid command check scope: '${String(scope)}'`);
|
|
9921
|
+
}
|
|
9922
|
+
}
|
|
9923
|
+
function projectRoot(changeDir) {
|
|
9924
|
+
return path13.resolve(changeDir, "..", "..", "..");
|
|
9925
|
+
}
|
|
9926
|
+
function normalizedCwd(changeDir, cwd = ".") {
|
|
9927
|
+
if (cwd.trim().length === 0) throw new Error("Command check cwd cannot be blank");
|
|
9928
|
+
const root = projectRoot(changeDir);
|
|
9929
|
+
const target = path13.resolve(root, cwd);
|
|
9930
|
+
if (target !== root && !target.startsWith(root + path13.sep)) {
|
|
9931
|
+
throw new Error(`Command check cwd must resolve within the project root: '${cwd}'`);
|
|
9932
|
+
}
|
|
9933
|
+
return path13.relative(root, target).replaceAll("\\", "/") || ".";
|
|
9934
|
+
}
|
|
9935
|
+
function validRecord(changeDir, event) {
|
|
9936
|
+
if (event.type !== "command_check_recorded") return null;
|
|
9937
|
+
const data = event.data;
|
|
9938
|
+
if (typeof data !== "object" || data === null || Array.isArray(data)) return null;
|
|
9939
|
+
const { scope, command, exitCode, cwd } = data;
|
|
9940
|
+
if (scope !== "build" && scope !== "verify" || typeof command !== "string" || command.trim().length === 0 || !Number.isInteger(exitCode) || typeof cwd !== "string") {
|
|
9941
|
+
return null;
|
|
9942
|
+
}
|
|
9943
|
+
let normalized2;
|
|
9944
|
+
try {
|
|
9945
|
+
normalized2 = normalizedCwd(changeDir, cwd);
|
|
9946
|
+
} catch {
|
|
9947
|
+
return null;
|
|
9948
|
+
}
|
|
9949
|
+
return {
|
|
9950
|
+
sequence: event.sequence,
|
|
9951
|
+
timestamp: event.timestamp,
|
|
9952
|
+
runId: event.runId,
|
|
9953
|
+
scope,
|
|
9954
|
+
command,
|
|
9955
|
+
exitCode,
|
|
9956
|
+
cwd: normalized2
|
|
9957
|
+
};
|
|
9958
|
+
}
|
|
9959
|
+
async function recordCommandCheck(changeDir, run, input) {
|
|
9960
|
+
validateScope(input.scope);
|
|
9961
|
+
if (typeof input.command !== "string" || input.command.trim().length === 0) {
|
|
9962
|
+
throw new Error("Command check command cannot be blank");
|
|
9963
|
+
}
|
|
9964
|
+
if (!Number.isInteger(input.exitCode)) {
|
|
9965
|
+
throw new Error("Command check exitCode must be an integer");
|
|
9966
|
+
}
|
|
9967
|
+
const trajectory = await readTrajectory(changeDir, run.trajectoryRef);
|
|
9968
|
+
const recorded = {
|
|
9969
|
+
sequence: trajectory.reduce((maximum, event) => Math.max(maximum, event.sequence), 0) + 1,
|
|
9970
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9971
|
+
runId: run.runId,
|
|
9972
|
+
scope: input.scope,
|
|
9973
|
+
command: input.command,
|
|
9974
|
+
exitCode: input.exitCode,
|
|
9975
|
+
cwd: normalizedCwd(changeDir, input.cwd)
|
|
9976
|
+
};
|
|
9977
|
+
await appendTrajectory(changeDir, run.trajectoryRef, {
|
|
9978
|
+
sequence: recorded.sequence,
|
|
9979
|
+
timestamp: recorded.timestamp,
|
|
9980
|
+
type: "command_check_recorded",
|
|
9981
|
+
runId: recorded.runId,
|
|
9982
|
+
data: {
|
|
9983
|
+
scope: recorded.scope,
|
|
9984
|
+
command: recorded.command,
|
|
9985
|
+
exitCode: recorded.exitCode,
|
|
9986
|
+
cwd: recorded.cwd
|
|
9987
|
+
}
|
|
9988
|
+
});
|
|
9989
|
+
return recorded;
|
|
9990
|
+
}
|
|
9991
|
+
async function latestCommandCheck(changeDir, run, scope) {
|
|
9992
|
+
validateScope(scope);
|
|
9993
|
+
const trajectory = await readTrajectory(changeDir, run.trajectoryRef);
|
|
9994
|
+
for (let index = trajectory.length - 1; index >= 0; index -= 1) {
|
|
9995
|
+
const event = trajectory[index];
|
|
9996
|
+
if (event.runId !== run.runId) continue;
|
|
9997
|
+
const record = validRecord(changeDir, event);
|
|
9998
|
+
if (record?.scope === scope) return record;
|
|
9999
|
+
}
|
|
10000
|
+
return null;
|
|
10001
|
+
}
|
|
9813
10002
|
|
|
9814
10003
|
// domains/comet-classic/classic-runtime-evals.ts
|
|
9815
10004
|
var STEP_EVIDENCE = {
|
|
@@ -9904,7 +10093,7 @@ async function inspectClassicChange(changeDir, name) {
|
|
|
9904
10093
|
// domains/comet-classic/classic-validate-command.ts
|
|
9905
10094
|
var import_yaml3 = __toESM(require_dist(), 1);
|
|
9906
10095
|
import { promises as fs12 } from "fs";
|
|
9907
|
-
import
|
|
10096
|
+
import path14 from "path";
|
|
9908
10097
|
var GREEN2 = "\x1B[32m";
|
|
9909
10098
|
var RED2 = "\x1B[31m";
|
|
9910
10099
|
var YELLOW2 = "\x1B[33m";
|
|
@@ -9931,11 +10120,12 @@ var ENUMS = {
|
|
|
9931
10120
|
subagent_dispatch: ["confirmed"],
|
|
9932
10121
|
tdd_mode: ["tdd", "direct"],
|
|
9933
10122
|
review_mode: ["off", "standard", "thorough"],
|
|
9934
|
-
isolation: ["branch", "worktree"],
|
|
10123
|
+
isolation: ["current", "branch", "worktree"],
|
|
9935
10124
|
verify_mode: ["light", "full"],
|
|
9936
10125
|
auto_transition: ["true", "false"],
|
|
9937
10126
|
verify_result: ["pending", "pass", "fail"],
|
|
9938
10127
|
branch_status: ["pending", "handled"],
|
|
10128
|
+
archive_confirmation: ["pending", "confirmed"],
|
|
9939
10129
|
archived: ["true", "false"],
|
|
9940
10130
|
direct_override: ["true", "false"],
|
|
9941
10131
|
classic_profile: ["full", "hotfix", "tweak"],
|
|
@@ -9974,7 +10164,7 @@ var classicValidateCommand = async (args) => {
|
|
|
9974
10164
|
};
|
|
9975
10165
|
}
|
|
9976
10166
|
const { directory, label } = await resolveClassicChangeDirectory(name);
|
|
9977
|
-
const yamlFile =
|
|
10167
|
+
const yamlFile = path14.join(directory, ".comet.yaml");
|
|
9978
10168
|
const lines = [`[VALIDATE] ${label}/.comet.yaml`];
|
|
9979
10169
|
let errors = 0;
|
|
9980
10170
|
let warnings = 0;
|
|
@@ -10023,9 +10213,15 @@ var classicValidateCommand = async (args) => {
|
|
|
10023
10213
|
fail3(`${field2}='${value}' is not valid. Expected: ${values.join(" ")}`);
|
|
10024
10214
|
}
|
|
10025
10215
|
}
|
|
10216
|
+
if (Object.prototype.hasOwnProperty.call(record, "verify_failures")) {
|
|
10217
|
+
const value = record.verify_failures;
|
|
10218
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
|
|
10219
|
+
fail3(`verify_failures='${text(value)}' is not a non-negative integer`);
|
|
10220
|
+
}
|
|
10221
|
+
}
|
|
10026
10222
|
for (const field2 of ["design_doc", "plan", "handoff_context"]) {
|
|
10027
10223
|
const value = text(record[field2]);
|
|
10028
|
-
if (value && !await exists3(
|
|
10224
|
+
if (value && !await exists3(path14.resolve(value))) {
|
|
10029
10225
|
fail3(`${field2}='${value}' does not exist on disk`);
|
|
10030
10226
|
}
|
|
10031
10227
|
}
|
|
@@ -10047,6 +10243,66 @@ var classicValidateCommand = async (args) => {
|
|
|
10047
10243
|
return { exitCode: 0, stderr: lines.join("\n") };
|
|
10048
10244
|
};
|
|
10049
10245
|
|
|
10246
|
+
// domains/comet-classic/classic-project-config.ts
|
|
10247
|
+
var import_yaml4 = __toESM(require_dist(), 1);
|
|
10248
|
+
import os from "os";
|
|
10249
|
+
import { promises as fs14 } from "fs";
|
|
10250
|
+
import path15 from "path";
|
|
10251
|
+
|
|
10252
|
+
// platform/fs/file-system.ts
|
|
10253
|
+
import { promises as fs13 } from "fs";
|
|
10254
|
+
async function fileExists3(filePath) {
|
|
10255
|
+
try {
|
|
10256
|
+
await fs13.access(filePath);
|
|
10257
|
+
return true;
|
|
10258
|
+
} catch (error) {
|
|
10259
|
+
if (isNotFoundError(error)) return false;
|
|
10260
|
+
throw error;
|
|
10261
|
+
}
|
|
10262
|
+
}
|
|
10263
|
+
async function readDir(dirPath) {
|
|
10264
|
+
try {
|
|
10265
|
+
return await fs13.readdir(dirPath);
|
|
10266
|
+
} catch (error) {
|
|
10267
|
+
const code = error?.code;
|
|
10268
|
+
if (code === "ENOENT" || code === "ENOTDIR") {
|
|
10269
|
+
return [];
|
|
10270
|
+
}
|
|
10271
|
+
throw error;
|
|
10272
|
+
}
|
|
10273
|
+
}
|
|
10274
|
+
function isNotFoundError(error) {
|
|
10275
|
+
return error?.code === "ENOENT";
|
|
10276
|
+
}
|
|
10277
|
+
|
|
10278
|
+
// domains/comet-classic/classic-project-config.ts
|
|
10279
|
+
function configCandidates(options = {}) {
|
|
10280
|
+
const cwd = options.cwd ?? process.cwd();
|
|
10281
|
+
const homeDir = options.homeDir ?? os.homedir();
|
|
10282
|
+
const candidates = [
|
|
10283
|
+
{ file: path15.resolve(cwd, ".comet", "config.yaml"), source: ".comet/config.yaml" },
|
|
10284
|
+
{
|
|
10285
|
+
file: path15.resolve(homeDir, ".comet", "config.yaml"),
|
|
10286
|
+
source: "~/.comet/config.yaml"
|
|
10287
|
+
}
|
|
10288
|
+
];
|
|
10289
|
+
return candidates.filter(
|
|
10290
|
+
(candidate, index) => candidates.findIndex((entry2) => entry2.file === candidate.file) === index
|
|
10291
|
+
);
|
|
10292
|
+
}
|
|
10293
|
+
async function readClassicConfigValue(field2, options = {}) {
|
|
10294
|
+
for (const candidate of configCandidates(options)) {
|
|
10295
|
+
if (!await fileExists3(candidate.file)) continue;
|
|
10296
|
+
const document = (0, import_yaml4.parseDocument)(await fs14.readFile(candidate.file, "utf8"), {
|
|
10297
|
+
uniqueKeys: false
|
|
10298
|
+
});
|
|
10299
|
+
const value = document.get(field2);
|
|
10300
|
+
if (value === null || value === void 0) continue;
|
|
10301
|
+
return { value: String(value), source: candidate.source };
|
|
10302
|
+
}
|
|
10303
|
+
return null;
|
|
10304
|
+
}
|
|
10305
|
+
|
|
10050
10306
|
// domains/comet-classic/classic-guard.ts
|
|
10051
10307
|
var GREEN3 = "\x1B[32m";
|
|
10052
10308
|
var RED3 = "\x1B[31m";
|
|
@@ -10111,7 +10367,7 @@ var GuardOutput = class {
|
|
|
10111
10367
|
};
|
|
10112
10368
|
async function exists4(file) {
|
|
10113
10369
|
try {
|
|
10114
|
-
await
|
|
10370
|
+
await fs15.access(file);
|
|
10115
10371
|
return true;
|
|
10116
10372
|
} catch (error) {
|
|
10117
10373
|
if (error.code === "ENOENT") return false;
|
|
@@ -10120,7 +10376,7 @@ async function exists4(file) {
|
|
|
10120
10376
|
}
|
|
10121
10377
|
async function nonempty(file) {
|
|
10122
10378
|
try {
|
|
10123
|
-
return (await
|
|
10379
|
+
return (await fs15.stat(file)).size > 0;
|
|
10124
10380
|
} catch (error) {
|
|
10125
10381
|
if (error.code === "ENOENT") return false;
|
|
10126
10382
|
throw error;
|
|
@@ -10133,32 +10389,9 @@ function validateChangeName2(name) {
|
|
|
10133
10389
|
async function resolveChangeDir(name) {
|
|
10134
10390
|
return (await resolveClassicChangeDirectory(name)).label;
|
|
10135
10391
|
}
|
|
10136
|
-
function stripInlineComment(value) {
|
|
10137
|
-
let out = "";
|
|
10138
|
-
let quote = "";
|
|
10139
|
-
for (let i = 0; i < value.length; i += 1) {
|
|
10140
|
-
const c = value[i];
|
|
10141
|
-
if (quote === "") {
|
|
10142
|
-
if (c === '"' || c === "'") {
|
|
10143
|
-
quote = c;
|
|
10144
|
-
} else if (c === "#" && (i === 0 || /\s/u.test(value[i - 1]))) {
|
|
10145
|
-
return out.replace(/\s+$/u, "");
|
|
10146
|
-
}
|
|
10147
|
-
} else if (c === quote) {
|
|
10148
|
-
quote = "";
|
|
10149
|
-
}
|
|
10150
|
-
out += c;
|
|
10151
|
-
}
|
|
10152
|
-
return out;
|
|
10153
|
-
}
|
|
10154
|
-
function stripWrappingQuotes(value) {
|
|
10155
|
-
if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) return value.slice(1, -1);
|
|
10156
|
-
if (value.length >= 2 && value.startsWith("'") && value.endsWith("'")) return value.slice(1, -1);
|
|
10157
|
-
return value;
|
|
10158
|
-
}
|
|
10159
10392
|
async function readField(changeDir, field2) {
|
|
10160
|
-
const file =
|
|
10161
|
-
const document = (0,
|
|
10393
|
+
const file = path16.join(changeDir, ".comet.yaml");
|
|
10394
|
+
const document = (0, import_yaml5.parseDocument)(await fs15.readFile(file, "utf8"), { uniqueKeys: false });
|
|
10162
10395
|
if (document.errors.length > 0) {
|
|
10163
10396
|
throw new GuardFailure(`ERROR: Invalid .comet.yaml: ${document.errors[0].message}`);
|
|
10164
10397
|
}
|
|
@@ -10171,18 +10404,7 @@ async function readField(changeDir, field2) {
|
|
|
10171
10404
|
async function projectConfigValue(field2, changeDir) {
|
|
10172
10405
|
const changeValue = await readField(changeDir, field2);
|
|
10173
10406
|
if (changeValue && changeValue !== "null") return changeValue;
|
|
10174
|
-
|
|
10175
|
-
if (!await exists4(config)) continue;
|
|
10176
|
-
for (const line of (await fs13.readFile(config, "utf8")).split(/\r?\n/u)) {
|
|
10177
|
-
if (new RegExp(`^${field2}:`, "u").test(line)) {
|
|
10178
|
-
const value = stripWrappingQuotes(
|
|
10179
|
-
stripInlineComment(line.replace(new RegExp(`^${field2}:\\s*`, "u"), ""))
|
|
10180
|
-
);
|
|
10181
|
-
if (value && value !== "null") return value;
|
|
10182
|
-
}
|
|
10183
|
-
}
|
|
10184
|
-
}
|
|
10185
|
-
return "";
|
|
10407
|
+
return (await readClassicConfigValue(field2))?.value ?? "";
|
|
10186
10408
|
}
|
|
10187
10409
|
async function configuredLanguage(changeDir) {
|
|
10188
10410
|
const language = await projectConfigValue("language", changeDir);
|
|
@@ -10210,7 +10432,7 @@ function countEnglishWords(source) {
|
|
|
10210
10432
|
}
|
|
10211
10433
|
async function documentLanguageMatchesConfigured(changeDir, file) {
|
|
10212
10434
|
const language = await configuredLanguage(changeDir);
|
|
10213
|
-
const source = stripFencedCodeBlocks(await
|
|
10435
|
+
const source = stripFencedCodeBlocks(await fs15.readFile(file, "utf8"));
|
|
10214
10436
|
const cjk = countCjkChars(source);
|
|
10215
10437
|
const englishWords = countEnglishWords(source);
|
|
10216
10438
|
if (language === "zh-CN" && cjk < 20 && englishWords >= 20) {
|
|
@@ -10234,7 +10456,7 @@ async function handoffSourceFiles(changeDir) {
|
|
|
10234
10456
|
const files = [`${changeDir}/proposal.md`, `${changeDir}/design.md`, `${changeDir}/tasks.md`];
|
|
10235
10457
|
const specs = `${changeDir}/specs`;
|
|
10236
10458
|
if (await exists4(specs)) {
|
|
10237
|
-
for (const entry2 of (await
|
|
10459
|
+
for (const entry2 of (await fs15.readdir(specs)).sort()) {
|
|
10238
10460
|
const spec = `${specs}/${entry2}/spec.md`;
|
|
10239
10461
|
if (await exists4(spec)) files.push(spec);
|
|
10240
10462
|
}
|
|
@@ -10254,13 +10476,13 @@ async function preflight(changeDir, name) {
|
|
|
10254
10476
|
if (!await exists4(changeDir)) {
|
|
10255
10477
|
throw new GuardFailure(red2(`FATAL: change directory not found: ${changeDir}`));
|
|
10256
10478
|
}
|
|
10257
|
-
if (!await exists4(
|
|
10479
|
+
if (!await exists4(path16.join(changeDir, ".comet.yaml"))) {
|
|
10258
10480
|
throw new GuardFailure(red2(`FATAL: .comet.yaml not found in ${changeDir}`));
|
|
10259
10481
|
}
|
|
10260
|
-
const
|
|
10261
|
-
if (
|
|
10262
|
-
if (
|
|
10263
|
-
process.stderr.write(
|
|
10482
|
+
const result5 = await classicValidateCommand([name], { json: false });
|
|
10483
|
+
if (result5.exitCode !== 0) {
|
|
10484
|
+
if (result5.stderr)
|
|
10485
|
+
process.stderr.write(result5.stderr.endsWith("\n") ? result5.stderr : `${result5.stderr}
|
|
10264
10486
|
`);
|
|
10265
10487
|
throw new GuardFailure(red2("FATAL: .comet.yaml schema validation failed"));
|
|
10266
10488
|
}
|
|
@@ -10275,6 +10497,9 @@ async function preflight(changeDir, name) {
|
|
|
10275
10497
|
function pushCheck(output, outcome) {
|
|
10276
10498
|
if (outcome.passed) {
|
|
10277
10499
|
output.stderr.push(green2(` [PASS] ${outcome.description}`));
|
|
10500
|
+
if (outcome.detail) {
|
|
10501
|
+
for (const line of outcome.detail.split("\n")) output.stderr.push(green2(` ${line}`));
|
|
10502
|
+
}
|
|
10278
10503
|
} else {
|
|
10279
10504
|
output.stderr.push(red2(` [FAIL] ${outcome.description}`));
|
|
10280
10505
|
if (outcome.detail) {
|
|
@@ -10285,11 +10510,11 @@ function pushCheck(output, outcome) {
|
|
|
10285
10510
|
function check(description, run) {
|
|
10286
10511
|
return async () => {
|
|
10287
10512
|
try {
|
|
10288
|
-
const
|
|
10513
|
+
const result5 = await run();
|
|
10289
10514
|
return {
|
|
10290
10515
|
description,
|
|
10291
|
-
passed:
|
|
10292
|
-
detail: ("detail" in
|
|
10516
|
+
passed: result5.passed,
|
|
10517
|
+
detail: ("detail" in result5 ? result5.detail : "") ?? ""
|
|
10293
10518
|
};
|
|
10294
10519
|
} catch (error) {
|
|
10295
10520
|
return {
|
|
@@ -10300,8 +10525,8 @@ function check(description, run) {
|
|
|
10300
10525
|
}
|
|
10301
10526
|
};
|
|
10302
10527
|
}
|
|
10303
|
-
function pass() {
|
|
10304
|
-
return { passed: true };
|
|
10528
|
+
function pass(detail) {
|
|
10529
|
+
return { passed: true, ...detail ? { detail } : {} };
|
|
10305
10530
|
}
|
|
10306
10531
|
function fail(detail) {
|
|
10307
10532
|
return { passed: false, detail };
|
|
@@ -10315,10 +10540,15 @@ async function runChecks(output, builders) {
|
|
|
10315
10540
|
}
|
|
10316
10541
|
return blocked2;
|
|
10317
10542
|
}
|
|
10543
|
+
var INFERRED_COMMAND_SOURCES = [
|
|
10544
|
+
"package.json with a build script",
|
|
10545
|
+
"pom.xml",
|
|
10546
|
+
"Cargo.toml"
|
|
10547
|
+
];
|
|
10318
10548
|
async function removedProjectCommandField(field2) {
|
|
10319
|
-
const config =
|
|
10549
|
+
const config = path16.join(".comet", "config.yaml");
|
|
10320
10550
|
if (!await exists4(config)) return false;
|
|
10321
|
-
const document = (0,
|
|
10551
|
+
const document = (0, import_yaml5.parseDocument)(await fs15.readFile(config, "utf8"));
|
|
10322
10552
|
if (document.errors.length > 0) {
|
|
10323
10553
|
throw new Error(
|
|
10324
10554
|
`.comet/config.yaml is invalid YAML (${document.errors[0].message}); cannot check for removed "${field2}" field. Fix the config and retry.`
|
|
@@ -10334,47 +10564,79 @@ function removedProjectCommandRun(field2) {
|
|
|
10334
10564
|
};
|
|
10335
10565
|
}
|
|
10336
10566
|
function runInferred(command) {
|
|
10337
|
-
const
|
|
10567
|
+
const result5 = spawnSync2(command, { shell: true, encoding: "utf8", timeout: 3e5 });
|
|
10338
10568
|
return {
|
|
10339
|
-
status:
|
|
10340
|
-
output: `${
|
|
10569
|
+
status: result5.status ?? 1,
|
|
10570
|
+
output: `${result5.stdout ?? ""}${result5.stderr ?? ""}`.replace(/\n+$/u, "")
|
|
10341
10571
|
};
|
|
10342
10572
|
}
|
|
10343
|
-
async function
|
|
10344
|
-
if (
|
|
10345
|
-
|
|
10346
|
-
return
|
|
10347
|
-
}
|
|
10348
|
-
|
|
10349
|
-
return runInferred("npm run build");
|
|
10573
|
+
async function inferredBuildCommand() {
|
|
10574
|
+
if (await exists4("package.json") && (() => {
|
|
10575
|
+
const parsed = JSON.parse(readFileSync("package.json", "utf8"));
|
|
10576
|
+
return typeof parsed.scripts?.build === "string";
|
|
10577
|
+
})()) {
|
|
10578
|
+
return "npm run build";
|
|
10350
10579
|
}
|
|
10351
10580
|
if (await exists4("pom.xml")) {
|
|
10352
10581
|
if (process.platform === "win32") {
|
|
10353
|
-
if (existsSync("mvnw.cmd")) return
|
|
10354
|
-
return
|
|
10582
|
+
if (existsSync("mvnw.cmd")) return "mvnw.cmd compile -q";
|
|
10583
|
+
return "mvn.cmd compile -q";
|
|
10355
10584
|
}
|
|
10356
|
-
if (existsSync("mvnw")) return
|
|
10357
|
-
return
|
|
10585
|
+
if (existsSync("mvnw")) return "./mvnw compile -q";
|
|
10586
|
+
return "mvn compile -q";
|
|
10358
10587
|
}
|
|
10359
|
-
if (await exists4("Cargo.toml")) return
|
|
10360
|
-
return
|
|
10588
|
+
if (await exists4("Cargo.toml")) return "cargo build";
|
|
10589
|
+
return null;
|
|
10590
|
+
}
|
|
10591
|
+
function evidenceDetail(record) {
|
|
10592
|
+
return `Evidence: recorded command-check at ${record.timestamp}; command: ${record.command}; cwd: ${record.cwd}`;
|
|
10593
|
+
}
|
|
10594
|
+
function recoveryCommand(change, scope, command) {
|
|
10595
|
+
return `comet state record-check ${change} ${scope} --command "${command}" --exit-code 0`;
|
|
10361
10596
|
}
|
|
10362
|
-
async function
|
|
10363
|
-
if (process.env.COMET_SKIP_BUILD === "1")
|
|
10364
|
-
|
|
10365
|
-
|
|
10597
|
+
async function commandCheckPasses(changeDir, change, run, scope) {
|
|
10598
|
+
if (process.env.COMET_SKIP_BUILD === "1") {
|
|
10599
|
+
return { status: 0, output: "SKIPPED via COMET_SKIP_BUILD=1" };
|
|
10600
|
+
}
|
|
10601
|
+
const removedFields = scope === "build" ? ["build_command"] : ["verify_command", "build_command"];
|
|
10602
|
+
for (const removedField of removedFields) {
|
|
10603
|
+
if (await removedProjectCommandField(removedField)) {
|
|
10604
|
+
return removedProjectCommandRun(removedField);
|
|
10605
|
+
}
|
|
10606
|
+
}
|
|
10607
|
+
const inferred = scope === "build" ? await inferredBuildCommand() : null;
|
|
10608
|
+
if (inferred) return runInferred(inferred);
|
|
10609
|
+
const recorded = await latestCommandCheck(changeDir, run, scope);
|
|
10610
|
+
if (!recorded) {
|
|
10611
|
+
return {
|
|
10612
|
+
status: 1,
|
|
10613
|
+
output: scope === "build" ? `No inferred build command or recorded build check. Detection searched: ${INFERRED_COMMAND_SOURCES.join(", ")}.
|
|
10614
|
+
Next: run the required command, then record it with:
|
|
10615
|
+
${recoveryCommand(change, scope, "<command>")}` : `No recorded verify check.
|
|
10616
|
+
Next: run the required verification command, then record it with:
|
|
10617
|
+
${recoveryCommand(change, scope, "<command>")}`
|
|
10618
|
+
};
|
|
10619
|
+
}
|
|
10620
|
+
if (recorded.exitCode !== 0) {
|
|
10621
|
+
return {
|
|
10622
|
+
status: recorded.exitCode,
|
|
10623
|
+
output: `Latest recorded ${scope} check failed with exit code ${recorded.exitCode}.
|
|
10624
|
+
${evidenceDetail(recorded)}
|
|
10625
|
+
Next: rerun the command successfully, then record it with:
|
|
10626
|
+
${recoveryCommand(change, scope, recorded.command)}`
|
|
10627
|
+
};
|
|
10366
10628
|
}
|
|
10367
|
-
return
|
|
10629
|
+
return { status: 0, output: evidenceDetail(recorded) };
|
|
10368
10630
|
}
|
|
10369
10631
|
async function tasksAllDone(changeDir) {
|
|
10370
|
-
const tasks =
|
|
10632
|
+
const tasks = path16.join(changeDir, "tasks.md");
|
|
10371
10633
|
if (!await exists4(tasks)) {
|
|
10372
10634
|
return fail(
|
|
10373
10635
|
`tasks.md is missing at ${tasks}
|
|
10374
10636
|
Next: restore or create tasks.md for this change before leaving build.`
|
|
10375
10637
|
);
|
|
10376
10638
|
}
|
|
10377
|
-
const source = await
|
|
10639
|
+
const source = await fs15.readFile(tasks, "utf8");
|
|
10378
10640
|
if (!/- \[x\]/u.test(source)) {
|
|
10379
10641
|
return fail(
|
|
10380
10642
|
"tasks.md has no completed tasks.\nNext: complete implementation tasks and mark them with '- [x]'."
|
|
@@ -10391,9 +10653,9 @@ Next: complete or explicitly remove unfinished tasks, then mark tasks.md with '-
|
|
|
10391
10653
|
return pass();
|
|
10392
10654
|
}
|
|
10393
10655
|
async function tasksHasAny(changeDir) {
|
|
10394
|
-
const tasks =
|
|
10656
|
+
const tasks = path16.join(changeDir, "tasks.md");
|
|
10395
10657
|
if (!await exists4(tasks)) return false;
|
|
10396
|
-
return /- \[/u.test(await
|
|
10658
|
+
return /- \[/u.test(await fs15.readFile(tasks, "utf8"));
|
|
10397
10659
|
}
|
|
10398
10660
|
async function planTasksAllDone(changeDir) {
|
|
10399
10661
|
const plan = await readField(changeDir, "plan");
|
|
@@ -10404,7 +10666,7 @@ async function planTasksAllDone(changeDir) {
|
|
|
10404
10666
|
Next: restore the Superpowers plan file or update .comet.yaml plan before leaving build.`
|
|
10405
10667
|
);
|
|
10406
10668
|
}
|
|
10407
|
-
const source = await
|
|
10669
|
+
const source = await fs15.readFile(plan, "utf8");
|
|
10408
10670
|
const unfinished = source.split(/\r?\n/u).map((line, index) => ({ line, number: index + 1 })).filter((entry2) => /^\s*- \[ \]/u.test(entry2.line));
|
|
10409
10671
|
if (unfinished.length > 0) {
|
|
10410
10672
|
return fail(
|
|
@@ -10417,11 +10679,14 @@ Next: check off corresponding completed plan tasks, then commit the plan update.
|
|
|
10417
10679
|
}
|
|
10418
10680
|
async function isolationSelected(changeDir, change) {
|
|
10419
10681
|
const isolation = await readField(changeDir, "isolation");
|
|
10682
|
+
const workflow = await readField(changeDir, "workflow");
|
|
10420
10683
|
if (isolation === "branch" || isolation === "worktree") return pass();
|
|
10684
|
+
if (isolation === "current" && (workflow === "hotfix" || workflow === "tweak")) return pass();
|
|
10685
|
+
const allowedValues = workflow === "full" ? "<branch|worktree>" : "<current|branch|worktree>";
|
|
10421
10686
|
return fail(
|
|
10422
|
-
`isolation must be branch or worktree, got '${isolation || "null"}'
|
|
10423
|
-
Next:
|
|
10424
|
-
|
|
10687
|
+
`isolation must be ${workflow === "full" ? "branch or worktree" : "current, branch, or worktree"}, got '${isolation || "null"}'
|
|
10688
|
+
Next: choose a valid workspace mode, prepare it when needed, then run:
|
|
10689
|
+
comet state set ${change} isolation ${allowedValues}`
|
|
10425
10690
|
);
|
|
10426
10691
|
}
|
|
10427
10692
|
async function buildModeSelected(changeDir, change) {
|
|
@@ -10431,7 +10696,7 @@ async function buildModeSelected(changeDir, change) {
|
|
|
10431
10696
|
return fail(
|
|
10432
10697
|
`build_mode must be selected before leaving build, got '${buildMode || "null"}'
|
|
10433
10698
|
Next: ask the user to choose an execution mode, then run:
|
|
10434
|
-
|
|
10699
|
+
comet state set ${change} build_mode <subagent-driven-development|executing-plans>`
|
|
10435
10700
|
);
|
|
10436
10701
|
}
|
|
10437
10702
|
async function buildModeAllowedForWorkflow(changeDir) {
|
|
@@ -10453,9 +10718,9 @@ async function subagentDispatchConfirmed(changeDir, change) {
|
|
|
10453
10718
|
return fail(
|
|
10454
10719
|
`subagent_dispatch must be confirmed before using build_mode=subagent-driven-development
|
|
10455
10720
|
Next: confirm the current platform has a real background subagent/Task/multi-agent dispatcher, then run:
|
|
10456
|
-
|
|
10457
|
-
|
|
10458
|
-
|
|
10721
|
+
comet state set ${change} subagent_dispatch confirmed
|
|
10722
|
+
If dispatch is unavailable, return to /comet-build Step 2 with subagent-driven-development removed. When executing-plans is the only valid mode, run:
|
|
10723
|
+
comet state set ${change} build_mode executing-plans`
|
|
10459
10724
|
);
|
|
10460
10725
|
}
|
|
10461
10726
|
async function tddModeSelected(changeDir, change) {
|
|
@@ -10466,7 +10731,7 @@ async function tddModeSelected(changeDir, change) {
|
|
|
10466
10731
|
return fail(
|
|
10467
10732
|
`tdd_mode must be tdd or direct for full workflow, got '${tddMode || "null"}'
|
|
10468
10733
|
Next: ask the user to choose TDD enforcement level, then run:
|
|
10469
|
-
|
|
10734
|
+
comet state set ${change} tdd_mode <tdd|direct>`
|
|
10470
10735
|
);
|
|
10471
10736
|
}
|
|
10472
10737
|
async function reviewModeSelected(changeDir, change) {
|
|
@@ -10479,7 +10744,7 @@ async function reviewModeSelected(changeDir, change) {
|
|
|
10479
10744
|
return fail(
|
|
10480
10745
|
`review_mode must be off, standard, or thorough before leaving build, got '${reviewMode || "null"}'
|
|
10481
10746
|
Next: ask the user to choose review strength, then run:
|
|
10482
|
-
|
|
10747
|
+
comet state set ${change} review_mode <off|standard|thorough>`
|
|
10483
10748
|
);
|
|
10484
10749
|
}
|
|
10485
10750
|
async function verificationReportExists(changeDir) {
|
|
@@ -10493,7 +10758,7 @@ async function archivedIsTrue(changeDir) {
|
|
|
10493
10758
|
return await readField(changeDir, "archived") === "true";
|
|
10494
10759
|
}
|
|
10495
10760
|
async function designDocFrontmatterHas(designDoc, field2, expected) {
|
|
10496
|
-
const source = (await
|
|
10761
|
+
const source = (await fs15.readFile(designDoc, "utf8")).replace(/^\uFEFF/u, "");
|
|
10497
10762
|
let inFrontmatter = false;
|
|
10498
10763
|
for (const line of source.split(/\r?\n/u)) {
|
|
10499
10764
|
if (!inFrontmatter) {
|
|
@@ -10510,7 +10775,7 @@ async function designDocRecorded(changeDir, change) {
|
|
|
10510
10775
|
if (designDoc && designDoc !== "null" && existsSync(designDoc)) return pass();
|
|
10511
10776
|
return fail(
|
|
10512
10777
|
`design_doc must point to an existing Superpowers Design Doc for full workflow before leaving design.
|
|
10513
|
-
Next: create the Design Doc and run:
|
|
10778
|
+
Next: create the Design Doc and run: comet state set ${change} design_doc <path>`
|
|
10514
10779
|
);
|
|
10515
10780
|
}
|
|
10516
10781
|
async function designHandoffContextValid(changeDir, change) {
|
|
@@ -10525,13 +10790,13 @@ Next: run node "$COMET_HANDOFF" ${change} design --write before invoking Superpo
|
|
|
10525
10790
|
if (!await nonempty(context)) {
|
|
10526
10791
|
return fail(
|
|
10527
10792
|
`handoff_context does not point to a non-empty file: ${context}
|
|
10528
|
-
Next: regenerate the design handoff with comet
|
|
10793
|
+
Next: regenerate the design handoff with comet handoff ${change} design --write.`
|
|
10529
10794
|
);
|
|
10530
10795
|
}
|
|
10531
10796
|
if (!/^[a-f0-9]{64}$/u.test(recordedHash)) {
|
|
10532
10797
|
return fail(
|
|
10533
10798
|
`handoff_hash is missing or invalid: ${recordedHash || "null"}
|
|
10534
|
-
Next: regenerate the design handoff with comet
|
|
10799
|
+
Next: regenerate the design handoff with comet handoff ${change} design --write.`
|
|
10535
10800
|
);
|
|
10536
10801
|
}
|
|
10537
10802
|
const actualHash = await computeHandoffHash(changeDir);
|
|
@@ -10540,14 +10805,14 @@ Next: regenerate the design handoff with comet-handoff.mjs.`
|
|
|
10540
10805
|
`OpenSpec artifacts changed after handoff was generated.
|
|
10541
10806
|
Expected handoff_hash: ${recordedHash}
|
|
10542
10807
|
Actual handoff_hash: ${actualHash}
|
|
10543
|
-
Next:
|
|
10808
|
+
Next: run comet handoff ${change} design --write so Superpowers receives the current OpenSpec context.`
|
|
10544
10809
|
);
|
|
10545
10810
|
}
|
|
10546
10811
|
const markdown = `${context.replace(/\.json$/u, "")}.md`;
|
|
10547
10812
|
if (!await nonempty(markdown)) {
|
|
10548
10813
|
return fail(
|
|
10549
10814
|
`design handoff markdown is missing or empty: ${markdown}
|
|
10550
|
-
Next: regenerate the design handoff with comet
|
|
10815
|
+
Next: regenerate the design handoff with comet handoff ${change} design --write.`
|
|
10551
10816
|
);
|
|
10552
10817
|
}
|
|
10553
10818
|
return pass();
|
|
@@ -10558,7 +10823,7 @@ async function designHandoffMarkdownTraceable(changeDir) {
|
|
|
10558
10823
|
const markdown = `${context.replace(/\.json$/u, "")}.md`;
|
|
10559
10824
|
if (!await nonempty(markdown))
|
|
10560
10825
|
return fail(`design handoff markdown is missing or empty: ${markdown}`);
|
|
10561
|
-
const source = await
|
|
10826
|
+
const source = await fs15.readFile(markdown, "utf8");
|
|
10562
10827
|
const problems = [];
|
|
10563
10828
|
if (!/^Generated-by: comet-handoff\.sh$/mu.test(source)) {
|
|
10564
10829
|
problems.push("handoff markdown is missing Generated-by marker");
|
|
@@ -10585,7 +10850,7 @@ async function betaSpecJsonStructurallyValid(changeDir) {
|
|
|
10585
10850
|
const context = await readField(changeDir, "handoff_context");
|
|
10586
10851
|
if (!context || context === "null") return fail("handoff_context is missing from .comet.yaml");
|
|
10587
10852
|
if (!await nonempty(context)) return fail(`spec-context.json is missing or empty: ${context}`);
|
|
10588
|
-
const source = await
|
|
10853
|
+
const source = await fs15.readFile(context, "utf8");
|
|
10589
10854
|
const problems = [];
|
|
10590
10855
|
let parsed;
|
|
10591
10856
|
try {
|
|
@@ -10622,19 +10887,19 @@ async function guardOpenChecks(output, changeDir) {
|
|
|
10622
10887
|
const checks = [
|
|
10623
10888
|
check(
|
|
10624
10889
|
"proposal.md exists and non-empty",
|
|
10625
|
-
async () => await nonempty(
|
|
10890
|
+
async () => await nonempty(path16.join(changeDir, "proposal.md")) ? pass() : fail("")
|
|
10626
10891
|
),
|
|
10627
10892
|
check(
|
|
10628
10893
|
"proposal.md matches configured language",
|
|
10629
|
-
() => documentLanguageMatchesConfigured(changeDir,
|
|
10894
|
+
() => documentLanguageMatchesConfigured(changeDir, path16.join(changeDir, "proposal.md"))
|
|
10630
10895
|
),
|
|
10631
10896
|
check(
|
|
10632
10897
|
"tasks.md exists and non-empty",
|
|
10633
|
-
async () => await nonempty(
|
|
10898
|
+
async () => await nonempty(path16.join(changeDir, "tasks.md")) ? pass() : fail("")
|
|
10634
10899
|
),
|
|
10635
10900
|
check(
|
|
10636
10901
|
"tasks.md matches configured language",
|
|
10637
|
-
() => documentLanguageMatchesConfigured(changeDir,
|
|
10902
|
+
() => documentLanguageMatchesConfigured(changeDir, path16.join(changeDir, "tasks.md"))
|
|
10638
10903
|
),
|
|
10639
10904
|
check(
|
|
10640
10905
|
"tasks.md has at least one task",
|
|
@@ -10647,11 +10912,11 @@ async function guardOpenChecks(output, changeDir) {
|
|
|
10647
10912
|
0,
|
|
10648
10913
|
check(
|
|
10649
10914
|
"design.md exists and non-empty",
|
|
10650
|
-
async () => await nonempty(
|
|
10915
|
+
async () => await nonempty(path16.join(changeDir, "design.md")) ? pass() : fail("")
|
|
10651
10916
|
),
|
|
10652
10917
|
check(
|
|
10653
10918
|
"design.md matches configured language",
|
|
10654
|
-
() => documentLanguageMatchesConfigured(changeDir,
|
|
10919
|
+
() => documentLanguageMatchesConfigured(changeDir, path16.join(changeDir, "design.md"))
|
|
10655
10920
|
)
|
|
10656
10921
|
);
|
|
10657
10922
|
}
|
|
@@ -10663,27 +10928,27 @@ async function guardDesignChecks(output, changeDir, change) {
|
|
|
10663
10928
|
const builders = [
|
|
10664
10929
|
check(
|
|
10665
10930
|
"proposal.md exists",
|
|
10666
|
-
async () => await nonempty(
|
|
10931
|
+
async () => await nonempty(path16.join(changeDir, "proposal.md")) ? pass() : fail("")
|
|
10667
10932
|
),
|
|
10668
10933
|
check(
|
|
10669
10934
|
"proposal.md matches configured language",
|
|
10670
|
-
() => documentLanguageMatchesConfigured(changeDir,
|
|
10935
|
+
() => documentLanguageMatchesConfigured(changeDir, path16.join(changeDir, "proposal.md"))
|
|
10671
10936
|
),
|
|
10672
10937
|
check(
|
|
10673
10938
|
"design.md exists",
|
|
10674
|
-
async () => await nonempty(
|
|
10939
|
+
async () => await nonempty(path16.join(changeDir, "design.md")) ? pass() : fail("")
|
|
10675
10940
|
),
|
|
10676
10941
|
check(
|
|
10677
10942
|
"design.md matches configured language",
|
|
10678
|
-
() => documentLanguageMatchesConfigured(changeDir,
|
|
10943
|
+
() => documentLanguageMatchesConfigured(changeDir, path16.join(changeDir, "design.md"))
|
|
10679
10944
|
),
|
|
10680
10945
|
check(
|
|
10681
10946
|
"tasks.md exists",
|
|
10682
|
-
async () => await nonempty(
|
|
10947
|
+
async () => await nonempty(path16.join(changeDir, "tasks.md")) ? pass() : fail("")
|
|
10683
10948
|
),
|
|
10684
10949
|
check(
|
|
10685
10950
|
"tasks.md matches configured language",
|
|
10686
|
-
() => documentLanguageMatchesConfigured(changeDir,
|
|
10951
|
+
() => documentLanguageMatchesConfigured(changeDir, path16.join(changeDir, "tasks.md"))
|
|
10687
10952
|
),
|
|
10688
10953
|
check("design handoff context exists", () => designHandoffContextValid(changeDir, change)),
|
|
10689
10954
|
check("design handoff markdown is traceable", () => designHandoffMarkdownTraceable(changeDir))
|
|
@@ -10732,7 +10997,7 @@ async function guardDesignChecks(output, changeDir, change) {
|
|
|
10732
10997
|
}
|
|
10733
10998
|
return blocked2;
|
|
10734
10999
|
}
|
|
10735
|
-
async function guardBuildChecks(output, changeDir, change) {
|
|
11000
|
+
async function guardBuildChecks(output, changeDir, change, run) {
|
|
10736
11001
|
return runChecks(output, [
|
|
10737
11002
|
check("isolation selected", () => isolationSelected(changeDir, change)),
|
|
10738
11003
|
check("build_mode selected", () => buildModeSelected(changeDir, change)),
|
|
@@ -10744,11 +11009,11 @@ async function guardBuildChecks(output, changeDir, change) {
|
|
|
10744
11009
|
check("Superpowers plan all tasks checked", () => planTasksAllDone(changeDir)),
|
|
10745
11010
|
check(
|
|
10746
11011
|
"proposal.md exists",
|
|
10747
|
-
async () => await nonempty(
|
|
11012
|
+
async () => await nonempty(path16.join(changeDir, "proposal.md")) ? pass() : fail("")
|
|
10748
11013
|
),
|
|
10749
11014
|
check(
|
|
10750
11015
|
"proposal.md matches configured language",
|
|
10751
|
-
() => documentLanguageMatchesConfigured(changeDir,
|
|
11016
|
+
() => documentLanguageMatchesConfigured(changeDir, path16.join(changeDir, "proposal.md"))
|
|
10752
11017
|
),
|
|
10753
11018
|
check("Superpowers plan matches configured language", async () => {
|
|
10754
11019
|
const plan = await readField(changeDir, "plan");
|
|
@@ -10758,19 +11023,19 @@ async function guardBuildChecks(output, changeDir, change) {
|
|
|
10758
11023
|
// Build check runs last — only after all config checks pass — to avoid
|
|
10759
11024
|
// wasting time on a build that would be rejected by a config failure.
|
|
10760
11025
|
check("Build passes", async () => {
|
|
10761
|
-
const buildResult = await
|
|
10762
|
-
return buildResult.status === 0 ? pass() : fail(buildResult.output);
|
|
11026
|
+
const buildResult = await commandCheckPasses(changeDir, change, run, "build");
|
|
11027
|
+
return buildResult.status === 0 ? pass(buildResult.output) : fail(buildResult.output);
|
|
10763
11028
|
})
|
|
10764
11029
|
]);
|
|
10765
11030
|
}
|
|
10766
|
-
async function guardVerifyChecks(output, changeDir) {
|
|
11031
|
+
async function guardVerifyChecks(output, changeDir, change, run) {
|
|
10767
11032
|
return runChecks(output, [
|
|
10768
11033
|
check("tasks.md all tasks checked", () => tasksAllDone(changeDir)),
|
|
10769
11034
|
// Verification command runs after tasks check — no point running tests
|
|
10770
11035
|
// if tasks.md is incomplete.
|
|
10771
11036
|
check("Verification passes", async () => {
|
|
10772
|
-
const verifyResult = await
|
|
10773
|
-
return verifyResult.status === 0 ? pass() : fail(verifyResult.output);
|
|
11037
|
+
const verifyResult = await commandCheckPasses(changeDir, change, run, "verify");
|
|
11038
|
+
return verifyResult.status === 0 ? pass(verifyResult.output) : fail(verifyResult.output);
|
|
10774
11039
|
}),
|
|
10775
11040
|
check(
|
|
10776
11041
|
"verification_report exists",
|
|
@@ -10780,11 +11045,7 @@ async function guardVerifyChecks(output, changeDir) {
|
|
|
10780
11045
|
const report = await readField(changeDir, "verification_report");
|
|
10781
11046
|
if (!report || report === "null" || !await exists4(report)) return pass();
|
|
10782
11047
|
return documentLanguageMatchesConfigured(changeDir, report);
|
|
10783
|
-
})
|
|
10784
|
-
check(
|
|
10785
|
-
"branch_status=handled",
|
|
10786
|
-
async () => await branchStatusHandled(changeDir) ? pass() : fail("")
|
|
10787
|
-
)
|
|
11048
|
+
})
|
|
10788
11049
|
]);
|
|
10789
11050
|
}
|
|
10790
11051
|
async function guardArchiveChecks(output, changeDir) {
|
|
@@ -10792,20 +11053,24 @@ async function guardArchiveChecks(output, changeDir) {
|
|
|
10792
11053
|
check("archived is true", async () => await archivedIsTrue(changeDir) ? pass() : fail("")),
|
|
10793
11054
|
check(
|
|
10794
11055
|
"proposal.md exists",
|
|
10795
|
-
async () => await nonempty(
|
|
11056
|
+
async () => await nonempty(path16.join(changeDir, "proposal.md")) ? pass() : fail("")
|
|
10796
11057
|
),
|
|
10797
11058
|
check(
|
|
10798
11059
|
"design.md exists",
|
|
10799
|
-
async () => await nonempty(
|
|
11060
|
+
async () => await nonempty(path16.join(changeDir, "design.md")) ? pass() : fail("")
|
|
10800
11061
|
),
|
|
10801
|
-
check("tasks.md all tasks checked", () => tasksAllDone(changeDir))
|
|
11062
|
+
check("tasks.md all tasks checked", () => tasksAllDone(changeDir)),
|
|
11063
|
+
check(
|
|
11064
|
+
"branch_status=handled",
|
|
11065
|
+
async () => await branchStatusHandled(changeDir) ? pass() : fail("")
|
|
11066
|
+
)
|
|
10802
11067
|
]);
|
|
10803
11068
|
}
|
|
10804
11069
|
async function applyStateUpdate(output, change, changeDir, phase, context) {
|
|
10805
11070
|
const event = CLASSIC_GUARD_TRANSITION_EVENT[phase];
|
|
10806
11071
|
if (!event) return;
|
|
10807
|
-
const
|
|
10808
|
-
await transitionClassicRuntimeRun(changeDir,
|
|
11072
|
+
const result5 = applyClassicTransition(context.classic, event);
|
|
11073
|
+
await transitionClassicRuntimeRun(changeDir, result5.classic, context.run, {
|
|
10809
11074
|
event,
|
|
10810
11075
|
phase,
|
|
10811
11076
|
source: "comet-guard"
|
|
@@ -10815,15 +11080,15 @@ async function applyStateUpdate(output, change, changeDir, phase, context) {
|
|
|
10815
11080
|
event,
|
|
10816
11081
|
source: "comet-guard",
|
|
10817
11082
|
from: context.classic,
|
|
10818
|
-
to:
|
|
10819
|
-
effects:
|
|
11083
|
+
to: result5.classic,
|
|
11084
|
+
effects: result5.effects
|
|
10820
11085
|
});
|
|
10821
|
-
for (const effect of
|
|
11086
|
+
for (const effect of result5.effects) {
|
|
10822
11087
|
output.stderr.push(green2(`[SET] ${wireField(effect.field)}=${wireValue(effect.to)}`));
|
|
10823
11088
|
}
|
|
10824
11089
|
output.stderr.push(green2(`[TRANSITION] ${event}`));
|
|
10825
11090
|
const template = APPLY_MESSAGE[phase];
|
|
10826
|
-
const message = phase === "open" ? template.replace("PLACEHOLDER",
|
|
11091
|
+
const message = phase === "open" ? template.replace("PLACEHOLDER", result5.classic.phase) : template;
|
|
10827
11092
|
output.stderr.push(green2(message));
|
|
10828
11093
|
}
|
|
10829
11094
|
var classicGuardCommand = async (args, options) => {
|
|
@@ -10853,8 +11118,10 @@ Valid phases: open, design, build, verify, archive`
|
|
|
10853
11118
|
let blocked2;
|
|
10854
11119
|
if (phase === "open") blocked2 = await guardOpenChecks(output, changeDir);
|
|
10855
11120
|
else if (phase === "design") blocked2 = await guardDesignChecks(output, changeDir, change);
|
|
10856
|
-
else if (phase === "build")
|
|
10857
|
-
|
|
11121
|
+
else if (phase === "build")
|
|
11122
|
+
blocked2 = await guardBuildChecks(output, changeDir, change, runContext.run);
|
|
11123
|
+
else if (phase === "verify")
|
|
11124
|
+
blocked2 = await guardVerifyChecks(output, changeDir, change, runContext.run);
|
|
10858
11125
|
else blocked2 = await guardArchiveChecks(output, changeDir);
|
|
10859
11126
|
if (blocked2) {
|
|
10860
11127
|
output.stderr.push("");
|
|
@@ -10877,10 +11144,10 @@ Valid phases: open, design, build, verify, archive`
|
|
|
10877
11144
|
};
|
|
10878
11145
|
|
|
10879
11146
|
// domains/comet-classic/classic-handoff.ts
|
|
10880
|
-
var
|
|
11147
|
+
var import_yaml6 = __toESM(require_dist(), 1);
|
|
10881
11148
|
import { createHash as createHash5 } from "crypto";
|
|
10882
|
-
import { promises as
|
|
10883
|
-
import
|
|
11149
|
+
import { promises as fs16, readFileSync as readFileSync2 } from "fs";
|
|
11150
|
+
import path17 from "path";
|
|
10884
11151
|
var GREEN4 = "\x1B[32m";
|
|
10885
11152
|
var RED4 = "\x1B[31m";
|
|
10886
11153
|
var YELLOW4 = "\x1B[33m";
|
|
@@ -10914,7 +11181,7 @@ var HandoffOutput = class {
|
|
|
10914
11181
|
};
|
|
10915
11182
|
async function exists5(file) {
|
|
10916
11183
|
try {
|
|
10917
|
-
await
|
|
11184
|
+
await fs16.access(file);
|
|
10918
11185
|
return true;
|
|
10919
11186
|
} catch (error) {
|
|
10920
11187
|
if (error.code === "ENOENT") return false;
|
|
@@ -10923,7 +11190,7 @@ async function exists5(file) {
|
|
|
10923
11190
|
}
|
|
10924
11191
|
async function nonempty2(file) {
|
|
10925
11192
|
try {
|
|
10926
|
-
return (await
|
|
11193
|
+
return (await fs16.stat(file)).size > 0;
|
|
10927
11194
|
} catch (error) {
|
|
10928
11195
|
if (error.code === "ENOENT") return false;
|
|
10929
11196
|
throw error;
|
|
@@ -10952,7 +11219,7 @@ async function handoffSourceFiles2(changeDir) {
|
|
|
10952
11219
|
const files = [`${changeDir}/proposal.md`, `${changeDir}/design.md`, `${changeDir}/tasks.md`];
|
|
10953
11220
|
const specs = `${changeDir}/specs`;
|
|
10954
11221
|
if (await exists5(specs)) {
|
|
10955
|
-
for (const entry2 of (await
|
|
11222
|
+
for (const entry2 of (await fs16.readdir(specs)).sort()) {
|
|
10956
11223
|
const spec = `${specs}/${entry2}/spec.md`;
|
|
10957
11224
|
if (await exists5(spec)) files.push(spec);
|
|
10958
11225
|
}
|
|
@@ -11000,7 +11267,7 @@ async function writeMarkdownContext(changeDir, change, mode, contextHash, output
|
|
|
11000
11267
|
];
|
|
11001
11268
|
for (const file of await handoffSourceFiles2(changeDir)) {
|
|
11002
11269
|
if (!await exists5(file)) continue;
|
|
11003
|
-
const content = await
|
|
11270
|
+
const content = await fs16.readFile(file, "utf8");
|
|
11004
11271
|
const total = lineCount(content);
|
|
11005
11272
|
lines.push(
|
|
11006
11273
|
`## ${file}`,
|
|
@@ -11025,7 +11292,7 @@ async function writeMarkdownContext(changeDir, change, mode, contextHash, output
|
|
|
11025
11292
|
}
|
|
11026
11293
|
lines.push("");
|
|
11027
11294
|
}
|
|
11028
|
-
await
|
|
11295
|
+
await fs16.writeFile(output, lines.join("\n"));
|
|
11029
11296
|
}
|
|
11030
11297
|
async function writeJsonContext(changeDir, change, mode, contextHash, output) {
|
|
11031
11298
|
const entries = [];
|
|
@@ -11048,7 +11315,7 @@ async function writeJsonContext(changeDir, change, mode, contextHash, output) {
|
|
|
11048
11315
|
"}",
|
|
11049
11316
|
""
|
|
11050
11317
|
].join("\n");
|
|
11051
|
-
await
|
|
11318
|
+
await fs16.writeFile(output, document);
|
|
11052
11319
|
}
|
|
11053
11320
|
async function writeSpecProjectionForFile(file, content) {
|
|
11054
11321
|
return [
|
|
@@ -11088,11 +11355,11 @@ async function writeSpecMarkdownContext(changeDir, change, contextHash, output)
|
|
|
11088
11355
|
const specs = `${changeDir}/specs`;
|
|
11089
11356
|
let projected = false;
|
|
11090
11357
|
if (await exists5(specs)) {
|
|
11091
|
-
for (const entry2 of (await
|
|
11358
|
+
for (const entry2 of (await fs16.readdir(specs)).sort()) {
|
|
11092
11359
|
const spec = `${specs}/${entry2}/spec.md`;
|
|
11093
11360
|
if (!await exists5(spec)) continue;
|
|
11094
11361
|
projected = true;
|
|
11095
|
-
lines.push(...await writeSpecProjectionForFile(spec, await
|
|
11362
|
+
lines.push(...await writeSpecProjectionForFile(spec, await fs16.readFile(spec, "utf8")));
|
|
11096
11363
|
}
|
|
11097
11364
|
}
|
|
11098
11365
|
if (!projected) {
|
|
@@ -11101,7 +11368,7 @@ async function writeSpecMarkdownContext(changeDir, change, contextHash, output)
|
|
|
11101
11368
|
lines.push(
|
|
11102
11369
|
"Full source files remain canonical. If a required heading or scenario is missing here, regenerate the handoff or read the source spec directly. Supporting files (proposal, design, tasks) are referenced by hash only."
|
|
11103
11370
|
);
|
|
11104
|
-
await
|
|
11371
|
+
await fs16.writeFile(output, lines.join("\n"));
|
|
11105
11372
|
}
|
|
11106
11373
|
async function writeSpecJsonContext(changeDir, change, contextHash, output) {
|
|
11107
11374
|
const entries = [];
|
|
@@ -11110,7 +11377,7 @@ async function writeSpecJsonContext(changeDir, change, contextHash, output) {
|
|
|
11110
11377
|
const role = /\/specs\/[^/]+\/spec\.md$/u.test(file) ? "spec" : "supporting";
|
|
11111
11378
|
entries.push({ path: file, sha256: hashFile2(file), role });
|
|
11112
11379
|
}
|
|
11113
|
-
await
|
|
11380
|
+
await fs16.writeFile(
|
|
11114
11381
|
output,
|
|
11115
11382
|
`${JSON.stringify(
|
|
11116
11383
|
{
|
|
@@ -11129,8 +11396,8 @@ async function writeSpecJsonContext(changeDir, change, contextHash, output) {
|
|
|
11129
11396
|
);
|
|
11130
11397
|
}
|
|
11131
11398
|
async function readField2(changeDir, field2) {
|
|
11132
|
-
const file =
|
|
11133
|
-
const document = (0,
|
|
11399
|
+
const file = path17.join(changeDir, ".comet.yaml");
|
|
11400
|
+
const document = (0, import_yaml6.parseDocument)(await fs16.readFile(file, "utf8"), { uniqueKeys: false });
|
|
11134
11401
|
if (document.errors.length > 0) {
|
|
11135
11402
|
throw new HandoffFailure(`ERROR: Invalid .comet.yaml: ${document.errors[0].message}`);
|
|
11136
11403
|
}
|
|
@@ -11164,7 +11431,7 @@ async function completedHandoffIsCurrent(changeDir, run, contextHash, contextJso
|
|
|
11164
11431
|
readCheckpoint(changeDir, run.checkpointRef)
|
|
11165
11432
|
]);
|
|
11166
11433
|
if (!await exists5(contextJson) || !await exists5(contextMd)) return false;
|
|
11167
|
-
if (context !== await
|
|
11434
|
+
if (context !== await fs16.readFile(contextMd, "utf8")) return false;
|
|
11168
11435
|
if (artifacts.handoff_context !== contextJson || artifacts.handoff_markdown !== contextMd) {
|
|
11169
11436
|
return false;
|
|
11170
11437
|
}
|
|
@@ -11287,7 +11554,7 @@ var classicHandoffCommand = async (args) => {
|
|
|
11287
11554
|
run: pendingRun,
|
|
11288
11555
|
unknownKeys: (await readClassicState(changeDir)).unknownKeys
|
|
11289
11556
|
});
|
|
11290
|
-
await
|
|
11557
|
+
await fs16.mkdir(handoffDir, { recursive: true });
|
|
11291
11558
|
if (handoffMode === "beta") {
|
|
11292
11559
|
await writeSpecMarkdownContext(changeDir, change, contextHash, contextMd);
|
|
11293
11560
|
await writeSpecJsonContext(changeDir, change, contextHash, contextJson);
|
|
@@ -11295,7 +11562,7 @@ var classicHandoffCommand = async (args) => {
|
|
|
11295
11562
|
await writeMarkdownContext(changeDir, change, handoffMode, contextHash, contextMd);
|
|
11296
11563
|
await writeJsonContext(changeDir, change, handoffMode, contextHash, contextJson);
|
|
11297
11564
|
}
|
|
11298
|
-
const context = await
|
|
11565
|
+
const context = await fs16.readFile(contextMd, "utf8");
|
|
11299
11566
|
await writeContext(changeDir, pendingRun.contextRef, context);
|
|
11300
11567
|
const artifacts = {
|
|
11301
11568
|
...await readArtifacts(changeDir, pendingRun.artifactsRef),
|
|
@@ -11352,8 +11619,140 @@ var classicHandoffCommand = async (args) => {
|
|
|
11352
11619
|
};
|
|
11353
11620
|
|
|
11354
11621
|
// domains/comet-classic/classic-hook-guard.ts
|
|
11355
|
-
import { existsSync as existsSync2, promises as
|
|
11356
|
-
import
|
|
11622
|
+
import { existsSync as existsSync2, promises as fs18, readFileSync as readFileSync3 } from "fs";
|
|
11623
|
+
import path19 from "path";
|
|
11624
|
+
|
|
11625
|
+
// domains/comet-classic/classic-current-change.ts
|
|
11626
|
+
import { execFileSync } from "child_process";
|
|
11627
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
11628
|
+
import { promises as fs17 } from "fs";
|
|
11629
|
+
import path18 from "path";
|
|
11630
|
+
function currentChangeFile(projectRoot2) {
|
|
11631
|
+
return path18.join(projectRoot2, ".comet", "current-change.json");
|
|
11632
|
+
}
|
|
11633
|
+
function currentBranch(projectRoot2) {
|
|
11634
|
+
try {
|
|
11635
|
+
const branch = execFileSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
11636
|
+
cwd: projectRoot2,
|
|
11637
|
+
encoding: "utf8",
|
|
11638
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
11639
|
+
}).trim();
|
|
11640
|
+
return branch && branch !== "HEAD" ? branch : null;
|
|
11641
|
+
} catch {
|
|
11642
|
+
return null;
|
|
11643
|
+
}
|
|
11644
|
+
}
|
|
11645
|
+
function changeDirectory(projectRoot2, changeName) {
|
|
11646
|
+
return path18.join(projectRoot2, "openspec", "changes", changeName);
|
|
11647
|
+
}
|
|
11648
|
+
async function validateActiveChange(projectRoot2, changeName) {
|
|
11649
|
+
assertOpenSpecChangeName(changeName);
|
|
11650
|
+
const changeDir = changeDirectory(projectRoot2, changeName);
|
|
11651
|
+
try {
|
|
11652
|
+
await fs17.access(path18.join(changeDir, ".comet.yaml"));
|
|
11653
|
+
} catch (error) {
|
|
11654
|
+
if (error.code === "ENOENT") {
|
|
11655
|
+
throw new Error(
|
|
11656
|
+
`Cannot select current change '${changeName}': active change state not found`,
|
|
11657
|
+
{
|
|
11658
|
+
cause: error
|
|
11659
|
+
}
|
|
11660
|
+
);
|
|
11661
|
+
}
|
|
11662
|
+
throw error;
|
|
11663
|
+
}
|
|
11664
|
+
const projection = await readClassicState(changeDir, { migrate: false });
|
|
11665
|
+
if (!projection.classic) {
|
|
11666
|
+
throw new Error(`Cannot select current change '${changeName}': Classic state is incomplete`);
|
|
11667
|
+
}
|
|
11668
|
+
if (projection.classic.archived) {
|
|
11669
|
+
throw new Error(`Cannot select current change '${changeName}': change is archived`);
|
|
11670
|
+
}
|
|
11671
|
+
}
|
|
11672
|
+
function parseSelection(source) {
|
|
11673
|
+
let value;
|
|
11674
|
+
try {
|
|
11675
|
+
value = JSON.parse(source);
|
|
11676
|
+
} catch (error) {
|
|
11677
|
+
throw new Error(
|
|
11678
|
+
`current change selection contains invalid JSON: ${error instanceof Error ? error.message : String(error)}`,
|
|
11679
|
+
{ cause: error }
|
|
11680
|
+
);
|
|
11681
|
+
}
|
|
11682
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
11683
|
+
throw new Error("current change selection must be a JSON object");
|
|
11684
|
+
}
|
|
11685
|
+
const record = value;
|
|
11686
|
+
if (record.version !== 1) {
|
|
11687
|
+
throw new Error("current change selection version must be 1");
|
|
11688
|
+
}
|
|
11689
|
+
if (typeof record.change !== "string") {
|
|
11690
|
+
throw new Error("current change selection change must be a string");
|
|
11691
|
+
}
|
|
11692
|
+
assertOpenSpecChangeName(record.change);
|
|
11693
|
+
if (record.branch !== null && typeof record.branch !== "string") {
|
|
11694
|
+
throw new Error("current change selection branch must be a string or null");
|
|
11695
|
+
}
|
|
11696
|
+
return {
|
|
11697
|
+
version: 1,
|
|
11698
|
+
change: record.change,
|
|
11699
|
+
branch: record.branch
|
|
11700
|
+
};
|
|
11701
|
+
}
|
|
11702
|
+
async function selectCurrentChange(projectRoot2, changeName) {
|
|
11703
|
+
await validateActiveChange(projectRoot2, changeName);
|
|
11704
|
+
const selection = {
|
|
11705
|
+
version: 1,
|
|
11706
|
+
change: changeName,
|
|
11707
|
+
branch: currentBranch(projectRoot2)
|
|
11708
|
+
};
|
|
11709
|
+
const file = currentChangeFile(projectRoot2);
|
|
11710
|
+
const temporary = `${file}.${randomUUID6()}.tmp`;
|
|
11711
|
+
await fs17.mkdir(path18.dirname(file), { recursive: true });
|
|
11712
|
+
try {
|
|
11713
|
+
await fs17.writeFile(temporary, JSON.stringify(selection, null, 2) + "\n", "utf8");
|
|
11714
|
+
await fs17.rename(temporary, file);
|
|
11715
|
+
} catch (error) {
|
|
11716
|
+
await fs17.rm(temporary, { force: true });
|
|
11717
|
+
throw error;
|
|
11718
|
+
}
|
|
11719
|
+
return selection;
|
|
11720
|
+
}
|
|
11721
|
+
async function resolveCurrentChange(projectRoot2) {
|
|
11722
|
+
let source;
|
|
11723
|
+
try {
|
|
11724
|
+
source = await fs17.readFile(currentChangeFile(projectRoot2), "utf8");
|
|
11725
|
+
} catch (error) {
|
|
11726
|
+
if (error.code === "ENOENT") return { status: "missing" };
|
|
11727
|
+
return {
|
|
11728
|
+
status: "stale",
|
|
11729
|
+
reason: `cannot read current change selection: ${error instanceof Error ? error.message : String(error)}`
|
|
11730
|
+
};
|
|
11731
|
+
}
|
|
11732
|
+
let selection;
|
|
11733
|
+
try {
|
|
11734
|
+
selection = parseSelection(source);
|
|
11735
|
+
await validateActiveChange(projectRoot2, selection.change);
|
|
11736
|
+
} catch (error) {
|
|
11737
|
+
return {
|
|
11738
|
+
status: "stale",
|
|
11739
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
11740
|
+
};
|
|
11741
|
+
}
|
|
11742
|
+
const branch = currentBranch(projectRoot2);
|
|
11743
|
+
if (selection.branch !== null && branch !== selection.branch) {
|
|
11744
|
+
return {
|
|
11745
|
+
status: "stale",
|
|
11746
|
+
reason: `current change '${selection.change}' was selected on branch '${selection.branch}', current branch is '${branch ?? "detached HEAD"}'`
|
|
11747
|
+
};
|
|
11748
|
+
}
|
|
11749
|
+
return { status: "selected", selection };
|
|
11750
|
+
}
|
|
11751
|
+
async function clearCurrentChange(projectRoot2) {
|
|
11752
|
+
await fs17.rm(currentChangeFile(projectRoot2), { force: true });
|
|
11753
|
+
}
|
|
11754
|
+
|
|
11755
|
+
// domains/comet-classic/classic-hook-guard.ts
|
|
11357
11756
|
function result(exitCode, message) {
|
|
11358
11757
|
return { exitCode, stderr: message + "\n" };
|
|
11359
11758
|
}
|
|
@@ -11375,66 +11774,75 @@ function inputTarget() {
|
|
|
11375
11774
|
function normalized(value) {
|
|
11376
11775
|
return value.replaceAll("\\", "/").replace(/\/+/gu, "/");
|
|
11377
11776
|
}
|
|
11777
|
+
function comparisonKey(value) {
|
|
11778
|
+
const normalizedValue = normalized(value);
|
|
11779
|
+
return process.platform === "win32" ? normalizedValue.toLowerCase() : normalizedValue;
|
|
11780
|
+
}
|
|
11378
11781
|
function parseProjectRoot(args) {
|
|
11379
11782
|
const index = args.indexOf("--project-root");
|
|
11380
11783
|
const value = index >= 0 ? args[index + 1] : void 0;
|
|
11381
|
-
return
|
|
11784
|
+
return path19.resolve(value && !value.startsWith("--") ? value : process.cwd());
|
|
11382
11785
|
}
|
|
11383
|
-
function relativeToProjectRoot(target,
|
|
11384
|
-
const relative = normalized(
|
|
11786
|
+
function relativeToProjectRoot(target, projectRoot2) {
|
|
11787
|
+
const relative = normalized(path19.relative(projectRoot2, target));
|
|
11385
11788
|
if (relative === "") return "";
|
|
11386
|
-
if (relative.startsWith("../") || relative === ".." ||
|
|
11789
|
+
if (relative.startsWith("../") || relative === ".." || path19.isAbsolute(relative)) return null;
|
|
11387
11790
|
return relative;
|
|
11388
11791
|
}
|
|
11389
11792
|
async function physicalPathForPossiblyMissingTarget(target) {
|
|
11390
|
-
const resolved =
|
|
11391
|
-
const root =
|
|
11793
|
+
const resolved = path19.resolve(target);
|
|
11794
|
+
const root = path19.parse(resolved).root;
|
|
11392
11795
|
const missingSegments = [];
|
|
11393
11796
|
let cursor = resolved;
|
|
11394
11797
|
while (cursor && cursor !== root) {
|
|
11395
11798
|
try {
|
|
11396
|
-
const physicalBase = await
|
|
11397
|
-
return
|
|
11799
|
+
const physicalBase = await fs18.realpath(cursor);
|
|
11800
|
+
return path19.join(physicalBase, ...missingSegments.reverse());
|
|
11398
11801
|
} catch (error) {
|
|
11399
11802
|
const code = error.code;
|
|
11400
11803
|
if (code !== "ENOENT" && code !== "ENOTDIR") throw error;
|
|
11401
|
-
missingSegments.push(
|
|
11402
|
-
cursor =
|
|
11804
|
+
missingSegments.push(path19.basename(cursor));
|
|
11805
|
+
cursor = path19.dirname(cursor);
|
|
11403
11806
|
}
|
|
11404
11807
|
}
|
|
11405
11808
|
try {
|
|
11406
|
-
const physicalRoot = await
|
|
11407
|
-
return
|
|
11809
|
+
const physicalRoot = await fs18.realpath(root);
|
|
11810
|
+
return path19.join(physicalRoot, ...missingSegments.reverse());
|
|
11408
11811
|
} catch {
|
|
11409
11812
|
return null;
|
|
11410
11813
|
}
|
|
11411
11814
|
}
|
|
11412
|
-
async function projectRelative(target,
|
|
11413
|
-
const rawCandidate =
|
|
11815
|
+
async function projectRelative(target, projectRoot2) {
|
|
11816
|
+
const rawCandidate = path19.isAbsolute(target) ? target : path19.resolve(process.cwd(), target);
|
|
11414
11817
|
let candidate = normalized(rawCandidate);
|
|
11415
|
-
const rootRelative = relativeToProjectRoot(rawCandidate,
|
|
11818
|
+
const rootRelative = relativeToProjectRoot(rawCandidate, projectRoot2);
|
|
11416
11819
|
if (rootRelative !== null) return rootRelative;
|
|
11417
11820
|
try {
|
|
11418
11821
|
const physicalCandidate = await physicalPathForPossiblyMissingTarget(rawCandidate);
|
|
11419
|
-
const physicalRoot = await
|
|
11822
|
+
const physicalRoot = await fs18.realpath(projectRoot2);
|
|
11420
11823
|
if (physicalCandidate) {
|
|
11421
11824
|
const physicalRootRelative = relativeToProjectRoot(physicalCandidate, physicalRoot);
|
|
11422
11825
|
if (physicalRootRelative !== null) return physicalRootRelative;
|
|
11423
11826
|
candidate = normalized(physicalCandidate);
|
|
11424
11827
|
}
|
|
11425
11828
|
} catch {
|
|
11426
|
-
if (!
|
|
11829
|
+
if (!path19.isAbsolute(target)) return normalized(target).replace(/^\.\//u, "");
|
|
11427
11830
|
}
|
|
11428
11831
|
return candidate.replace(/^\.\//u, "");
|
|
11429
11832
|
}
|
|
11430
11833
|
async function loadGoverningChange(changeDir) {
|
|
11431
11834
|
try {
|
|
11432
|
-
const
|
|
11835
|
+
const projection = await readClassicState(changeDir, { migrate: false });
|
|
11836
|
+
const unknownKeys = Array.from(new Set(projection.unknownKeys)).sort();
|
|
11837
|
+
if (unknownKeys.length > 0) {
|
|
11838
|
+
throw new Error(`Invalid Classic state: unknown field(s): ${unknownKeys.join(", ")}`);
|
|
11839
|
+
}
|
|
11840
|
+
if (!projection.classic) throw new Error("Classic state projection is unavailable");
|
|
11433
11841
|
return {
|
|
11434
11842
|
changeDir,
|
|
11435
|
-
phase:
|
|
11436
|
-
classic:
|
|
11437
|
-
archived:
|
|
11843
|
+
phase: projection.classic.phase,
|
|
11844
|
+
classic: projection.classic,
|
|
11845
|
+
archived: projection.classic.archived
|
|
11438
11846
|
};
|
|
11439
11847
|
} catch {
|
|
11440
11848
|
const legacy = await readLegacyState(changeDir);
|
|
@@ -11447,36 +11855,64 @@ async function loadGoverningChange(changeDir) {
|
|
|
11447
11855
|
};
|
|
11448
11856
|
}
|
|
11449
11857
|
}
|
|
11450
|
-
async function activeChanges(
|
|
11451
|
-
const changesDir =
|
|
11858
|
+
async function activeChanges(projectRoot2) {
|
|
11859
|
+
const changesDir = path19.join(projectRoot2, "openspec", "changes");
|
|
11452
11860
|
const governingChanges = [];
|
|
11453
11861
|
if (!existsSync2(changesDir)) return governingChanges;
|
|
11454
|
-
for (const entry2 of (await
|
|
11862
|
+
for (const entry2 of (await fs18.readdir(changesDir, { withFileTypes: true })).sort(
|
|
11455
11863
|
(left, right) => left.name.localeCompare(right.name)
|
|
11456
11864
|
)) {
|
|
11457
11865
|
if (!entry2.isDirectory() || entry2.name === "archive") continue;
|
|
11458
|
-
const changeDir =
|
|
11459
|
-
if (!existsSync2(
|
|
11866
|
+
const changeDir = path19.join(changesDir, entry2.name);
|
|
11867
|
+
if (!existsSync2(path19.join(changeDir, ".comet.yaml"))) continue;
|
|
11460
11868
|
const governing = await loadGoverningChange(changeDir);
|
|
11461
11869
|
if (!governing || governing.archived) continue;
|
|
11462
11870
|
governingChanges.push(governing);
|
|
11463
11871
|
}
|
|
11464
11872
|
return governingChanges;
|
|
11465
11873
|
}
|
|
11466
|
-
function
|
|
11467
|
-
|
|
11468
|
-
|
|
11874
|
+
function isSuperpowersArtifactPath(relativePath2) {
|
|
11875
|
+
return comparisonKey(relativePath2).startsWith("docs/superpowers/");
|
|
11876
|
+
}
|
|
11877
|
+
var SUPERPOWERS_ARTIFACT_SLOTS = [
|
|
11878
|
+
{
|
|
11879
|
+
prefix: "docs/superpowers/specs/",
|
|
11880
|
+
field: "designDoc",
|
|
11881
|
+
wireField: "design_doc",
|
|
11882
|
+
phase: "design"
|
|
11883
|
+
},
|
|
11884
|
+
{
|
|
11885
|
+
prefix: "docs/superpowers/plans/",
|
|
11886
|
+
field: "plan",
|
|
11887
|
+
wireField: "plan",
|
|
11888
|
+
phase: "build"
|
|
11889
|
+
},
|
|
11890
|
+
{
|
|
11891
|
+
prefix: "docs/superpowers/reports/",
|
|
11892
|
+
field: "verificationReport",
|
|
11893
|
+
wireField: "verification_report",
|
|
11894
|
+
phase: "verify"
|
|
11469
11895
|
}
|
|
11470
|
-
|
|
11896
|
+
];
|
|
11897
|
+
function standardSuperpowersArtifactSlot(relativePath2) {
|
|
11898
|
+
const key = comparisonKey(relativePath2);
|
|
11899
|
+
const slot = SUPERPOWERS_ARTIFACT_SLOTS.find((candidate) => key.startsWith(candidate.prefix));
|
|
11900
|
+
if (!slot) return null;
|
|
11901
|
+
const fileName = key.slice(slot.prefix.length);
|
|
11902
|
+
if (!fileName || fileName.includes("/") || !fileName.endsWith(".md")) return null;
|
|
11903
|
+
return slot;
|
|
11471
11904
|
}
|
|
11472
|
-
function
|
|
11473
|
-
return
|
|
11905
|
+
function superpowersArtifactValue(governing, slot) {
|
|
11906
|
+
return governing.classic?.[slot.field] ?? null;
|
|
11907
|
+
}
|
|
11908
|
+
function allowsFirstSuperpowersArtifactWrite(governing, slot) {
|
|
11909
|
+
return governing.classic !== null && governing.phase === slot.phase && !superpowersArtifactValue(governing, slot);
|
|
11474
11910
|
}
|
|
11475
11911
|
function allowsSuperpowersArtifacts(governing) {
|
|
11476
11912
|
return governing.phase === "design" || governing.phase === "build" || governing.phase === "verify";
|
|
11477
11913
|
}
|
|
11478
11914
|
function governingChangeName(governing) {
|
|
11479
|
-
return governing.changeDir ?
|
|
11915
|
+
return governing.changeDir ? path19.basename(governing.changeDir) : null;
|
|
11480
11916
|
}
|
|
11481
11917
|
var SUPERPOWERS_ARTIFACT_SUFFIXES = /* @__PURE__ */ new Set([
|
|
11482
11918
|
"design",
|
|
@@ -11496,7 +11932,7 @@ function matchesRecordedSuperpowersArtifact(relativePath2, governing) {
|
|
|
11496
11932
|
governing.classic?.verificationReport
|
|
11497
11933
|
];
|
|
11498
11934
|
return artifactPaths.some(
|
|
11499
|
-
(artifactPath) => artifactPath &&
|
|
11935
|
+
(artifactPath) => artifactPath && comparisonKey(artifactPath) === comparisonKey(relativePath2)
|
|
11500
11936
|
);
|
|
11501
11937
|
}
|
|
11502
11938
|
function matchesSuperpowersArtifactName(relativePath2, changeName) {
|
|
@@ -11507,12 +11943,12 @@ function matchesSuperpowersArtifactName(relativePath2, changeName) {
|
|
|
11507
11943
|
const pattern = new RegExp(`(^|[-_.])${escapeRegex(changeName)}[-_.](${suffixes})$`, "u");
|
|
11508
11944
|
return pattern.test(stem);
|
|
11509
11945
|
}
|
|
11510
|
-
async function superpowersArtifactGoverningChange(relativePath2,
|
|
11511
|
-
const active = await activeChanges(
|
|
11946
|
+
async function superpowersArtifactGoverningChange(relativePath2, projectRoot2) {
|
|
11947
|
+
const active = await activeChanges(projectRoot2);
|
|
11512
11948
|
const recorded = active.find(
|
|
11513
11949
|
(governing) => matchesRecordedSuperpowersArtifact(relativePath2, governing)
|
|
11514
11950
|
);
|
|
11515
|
-
if (recorded) return recorded;
|
|
11951
|
+
if (recorded) return { governing: recorded, match: "recorded" };
|
|
11516
11952
|
const eligible = active.filter(allowsSuperpowersArtifacts);
|
|
11517
11953
|
const named = eligible.filter((governing) => {
|
|
11518
11954
|
const name = governingChangeName(governing);
|
|
@@ -11520,21 +11956,44 @@ async function superpowersArtifactGoverningChange(relativePath2, projectRoot) {
|
|
|
11520
11956
|
}).sort(
|
|
11521
11957
|
(a, b) => (governingChangeName(b)?.length ?? 0) - (governingChangeName(a)?.length ?? 0)
|
|
11522
11958
|
)[0];
|
|
11523
|
-
if (named) return named;
|
|
11959
|
+
if (named) return { governing: named, match: "named" };
|
|
11524
11960
|
return null;
|
|
11525
11961
|
}
|
|
11526
|
-
async function repoSourceGoverningChange(
|
|
11527
|
-
const active = await activeChanges(
|
|
11528
|
-
|
|
11962
|
+
async function repoSourceGoverningChange(projectRoot2, relativePath2) {
|
|
11963
|
+
const active = await activeChanges(projectRoot2);
|
|
11964
|
+
if (active.length === 0) return null;
|
|
11965
|
+
const current = await resolveCurrentChange(projectRoot2);
|
|
11966
|
+
if (current.status === "stale") {
|
|
11967
|
+
return { blockedResult: blockedStaleSelection(relativePath2, current.reason) };
|
|
11968
|
+
}
|
|
11969
|
+
if (current.status === "selected") {
|
|
11970
|
+
const selected = active.find(
|
|
11971
|
+
(governing) => governingChangeName(governing) === current.selection.change
|
|
11972
|
+
);
|
|
11973
|
+
if (selected) return selected;
|
|
11974
|
+
return {
|
|
11975
|
+
blockedResult: blockedStaleSelection(
|
|
11976
|
+
relativePath2,
|
|
11977
|
+
`selected change '${current.selection.change}' is no longer active`
|
|
11978
|
+
)
|
|
11979
|
+
};
|
|
11980
|
+
}
|
|
11981
|
+
if (active.length === 1) return active[0];
|
|
11982
|
+
return {
|
|
11983
|
+
blockedResult: blockedMultipleChanges(
|
|
11984
|
+
relativePath2,
|
|
11985
|
+
active.map((governing) => governingChangeName(governing)).filter(Boolean)
|
|
11986
|
+
)
|
|
11987
|
+
};
|
|
11529
11988
|
}
|
|
11530
|
-
async function governingChange(relativePath2,
|
|
11989
|
+
async function governingChange(relativePath2, projectRoot2) {
|
|
11531
11990
|
const prefix = "openspec/changes/";
|
|
11532
11991
|
if (relativePath2.startsWith(prefix)) {
|
|
11533
11992
|
const rest = relativePath2.slice(prefix.length);
|
|
11534
11993
|
const [name] = rest.split("/");
|
|
11535
11994
|
if (name && name !== "archive") {
|
|
11536
|
-
const changeDir =
|
|
11537
|
-
const stateFile2 =
|
|
11995
|
+
const changeDir = path19.join(projectRoot2, "openspec", "changes", name);
|
|
11996
|
+
const stateFile2 = path19.join(changeDir, ".comet.yaml");
|
|
11538
11997
|
if (existsSync2(stateFile2)) {
|
|
11539
11998
|
const governing = await loadGoverningChange(changeDir);
|
|
11540
11999
|
if (governing) return governing;
|
|
@@ -11544,12 +12003,31 @@ async function governingChange(relativePath2, projectRoot) {
|
|
|
11544
12003
|
}
|
|
11545
12004
|
}
|
|
11546
12005
|
if (isSuperpowersArtifactPath(relativePath2)) {
|
|
11547
|
-
const superpowers = await superpowersArtifactGoverningChange(relativePath2,
|
|
11548
|
-
if (superpowers
|
|
11549
|
-
|
|
12006
|
+
const superpowers = await superpowersArtifactGoverningChange(relativePath2, projectRoot2);
|
|
12007
|
+
if (superpowers?.match === "recorded") {
|
|
12008
|
+
return { ...superpowers.governing, superpowersArtifact: "matched" };
|
|
12009
|
+
}
|
|
12010
|
+
const slot = standardSuperpowersArtifactSlot(relativePath2);
|
|
12011
|
+
if (superpowers) {
|
|
12012
|
+
return slot ? {
|
|
12013
|
+
...superpowers.governing,
|
|
12014
|
+
superpowersArtifact: allowsFirstSuperpowersArtifactWrite(superpowers.governing, slot) ? "matched" : "unmatched",
|
|
12015
|
+
superpowersSlot: slot
|
|
12016
|
+
} : { ...superpowers.governing, superpowersArtifact: "matched" };
|
|
12017
|
+
}
|
|
12018
|
+
if (slot) {
|
|
12019
|
+
const candidate = await repoSourceGoverningChange(projectRoot2, relativePath2);
|
|
12020
|
+
if (!candidate || "blockedResult" in candidate) return candidate;
|
|
12021
|
+
return {
|
|
12022
|
+
...candidate,
|
|
12023
|
+
superpowersArtifact: allowsFirstSuperpowersArtifactWrite(candidate, slot) ? "matched" : "unmatched",
|
|
12024
|
+
superpowersSlot: slot
|
|
12025
|
+
};
|
|
12026
|
+
}
|
|
12027
|
+
const fallback = (await activeChanges(projectRoot2))[0] ?? null;
|
|
11550
12028
|
return fallback ? { ...fallback, superpowersArtifact: "unmatched" } : null;
|
|
11551
12029
|
}
|
|
11552
|
-
return repoSourceGoverningChange(
|
|
12030
|
+
return repoSourceGoverningChange(projectRoot2, relativePath2);
|
|
11553
12031
|
}
|
|
11554
12032
|
function isRootMarkdown(relativePath2) {
|
|
11555
12033
|
return !relativePath2.includes("/") && relativePath2.endsWith(".md");
|
|
@@ -11593,7 +12071,7 @@ function blocked(relativePath2, phase) {
|
|
|
11593
12071
|
" BLOCKED: source writes are not allowed during design",
|
|
11594
12072
|
" This phase does not allow source writes",
|
|
11595
12073
|
" ALLOWED: run brainstorming, create the Design Doc, and run guard",
|
|
11596
|
-
" NEXT: finish the Design Doc, then run comet
|
|
12074
|
+
" NEXT: finish the Design Doc, then run comet guard <change-name> design --apply to enter build"
|
|
11597
12075
|
] : [
|
|
11598
12076
|
" BLOCKED: source writes are not allowed during archive",
|
|
11599
12077
|
" This phase does not allow source writes",
|
|
@@ -11634,7 +12112,25 @@ function blockedMissingDesignDoc(relativePath2) {
|
|
|
11634
12112
|
].join("\n")
|
|
11635
12113
|
);
|
|
11636
12114
|
}
|
|
11637
|
-
function blockedUnmatchedSuperpowersArtifact(relativePath2,
|
|
12115
|
+
function blockedUnmatchedSuperpowersArtifact(relativePath2, governing) {
|
|
12116
|
+
const slot = governing.superpowersSlot;
|
|
12117
|
+
const recorded = slot ? superpowersArtifactValue(governing, slot) : null;
|
|
12118
|
+
const details = slot ? governing.phase !== slot.phase ? [
|
|
12119
|
+
` BLOCKED: ${slot.wireField} cannot be first-written in phase ${governing.phase}`,
|
|
12120
|
+
` Expected phase: ${slot.phase}`,
|
|
12121
|
+
" NEXT: resume the matching Comet phase or use an already recorded artifact path"
|
|
12122
|
+
] : recorded ? [
|
|
12123
|
+
` BLOCKED: ${slot.wireField} is already recorded for this change`,
|
|
12124
|
+
` Recorded path: ${recorded}`,
|
|
12125
|
+
" NEXT: write the recorded artifact or explicitly correct the state path"
|
|
12126
|
+
] : [
|
|
12127
|
+
" BLOCKED: standard Superpowers artifact state is incomplete",
|
|
12128
|
+
" NEXT: validate the active change state, then retry the matching phase"
|
|
12129
|
+
] : [
|
|
12130
|
+
" BLOCKED: unmatched Superpowers artifact",
|
|
12131
|
+
" This docs/superpowers/ path does not match any active change artifact",
|
|
12132
|
+
" NEXT: use a recorded artifact path or a standard phase artifact directory"
|
|
12133
|
+
];
|
|
11638
12134
|
return result(
|
|
11639
12135
|
2,
|
|
11640
12136
|
[
|
|
@@ -11643,32 +12139,55 @@ function blockedUnmatchedSuperpowersArtifact(relativePath2, phase) {
|
|
|
11643
12139
|
"║ COMET PHASE GUARD — WRITE BLOCKED ║",
|
|
11644
12140
|
"╚══════════════════════════════════════════╝",
|
|
11645
12141
|
"",
|
|
11646
|
-
` Current phase: ${phase}`,
|
|
12142
|
+
` Current phase: ${governing.phase}`,
|
|
12143
|
+
` Target file: ${relativePath2}`,
|
|
12144
|
+
"",
|
|
12145
|
+
...details,
|
|
12146
|
+
""
|
|
12147
|
+
].join("\n")
|
|
12148
|
+
);
|
|
12149
|
+
}
|
|
12150
|
+
function blockedMultipleChanges(relativePath2, changeNames) {
|
|
12151
|
+
return result(
|
|
12152
|
+
2,
|
|
12153
|
+
[
|
|
12154
|
+
"",
|
|
12155
|
+
"╔══════════════════════════════════════════╗",
|
|
12156
|
+
"║ COMET PHASE GUARD — WRITE BLOCKED ║",
|
|
12157
|
+
"╚══════════════════════════════════════════╝",
|
|
12158
|
+
"",
|
|
12159
|
+
" BLOCKED: multiple active changes require a current change",
|
|
12160
|
+
` Target file: ${relativePath2}`,
|
|
12161
|
+
` Active changes: ${changeNames.join(", ")}`,
|
|
12162
|
+
"",
|
|
12163
|
+
" NEXT: run comet state select <change-name>, then retry the source write",
|
|
12164
|
+
""
|
|
12165
|
+
].join("\n")
|
|
12166
|
+
);
|
|
12167
|
+
}
|
|
12168
|
+
function blockedStaleSelection(relativePath2, reason) {
|
|
12169
|
+
return result(
|
|
12170
|
+
2,
|
|
12171
|
+
[
|
|
12172
|
+
"",
|
|
12173
|
+
"╔══════════════════════════════════════════╗",
|
|
12174
|
+
"║ COMET PHASE GUARD — WRITE BLOCKED ║",
|
|
12175
|
+
"╚══════════════════════════════════════════╝",
|
|
12176
|
+
"",
|
|
12177
|
+
" BLOCKED: current change selection is stale or invalid",
|
|
11647
12178
|
` Target file: ${relativePath2}`,
|
|
12179
|
+
` Reason: ${reason}`,
|
|
11648
12180
|
"",
|
|
11649
|
-
"
|
|
11650
|
-
" This docs/superpowers/ path does not match any active change artifact",
|
|
11651
|
-
" NEXT: record the artifact path in .comet.yaml or include the change name in the artifact filename",
|
|
12181
|
+
" NEXT: run comet state select <change-name>, then retry the source write",
|
|
11652
12182
|
""
|
|
11653
12183
|
].join("\n")
|
|
11654
12184
|
);
|
|
11655
12185
|
}
|
|
11656
12186
|
var classicHookGuardCommand = async (args) => {
|
|
11657
|
-
const
|
|
12187
|
+
const projectRoot2 = parseProjectRoot(args);
|
|
11658
12188
|
const target = inputTarget();
|
|
11659
12189
|
if (!target) return allowed("no file path in tool input");
|
|
11660
|
-
const relativePath2 = await projectRelative(target,
|
|
11661
|
-
let governing;
|
|
11662
|
-
try {
|
|
11663
|
-
governing = await governingChange(relativePath2, projectRoot);
|
|
11664
|
-
} catch (error) {
|
|
11665
|
-
return result(
|
|
11666
|
-
2,
|
|
11667
|
-
`[COMET-HOOK] blocked: ${error instanceof Error ? error.message : String(error)}`
|
|
11668
|
-
);
|
|
11669
|
-
}
|
|
11670
|
-
if (!governing) return allowed("no active comet change");
|
|
11671
|
-
if (governing.archived) return allowed(`${relativePath2} (own change archived)`);
|
|
12190
|
+
const relativePath2 = await projectRelative(target, projectRoot2);
|
|
11672
12191
|
if (isCometConfig(relativePath2)) {
|
|
11673
12192
|
return allowed(`${relativePath2} (whitelist: comet config)`);
|
|
11674
12193
|
}
|
|
@@ -11681,6 +12200,18 @@ var classicHookGuardCommand = async (args) => {
|
|
|
11681
12200
|
if (relativePath2 === "CLAUDE.md" || relativePath2 === "CHANGELOG.md" || relativePath2 === "README.md" || isRootMarkdown(relativePath2)) {
|
|
11682
12201
|
return allowed(`${relativePath2} (whitelist: root markdown)`);
|
|
11683
12202
|
}
|
|
12203
|
+
let governing;
|
|
12204
|
+
try {
|
|
12205
|
+
governing = await governingChange(relativePath2, projectRoot2);
|
|
12206
|
+
} catch (error) {
|
|
12207
|
+
return result(
|
|
12208
|
+
2,
|
|
12209
|
+
`[COMET-HOOK] blocked: ${error instanceof Error ? error.message : String(error)}`
|
|
12210
|
+
);
|
|
12211
|
+
}
|
|
12212
|
+
if (!governing) return allowed("no active comet change");
|
|
12213
|
+
if ("blockedResult" in governing) return governing.blockedResult;
|
|
12214
|
+
if (governing.archived) return allowed(`${relativePath2} (own change archived)`);
|
|
11684
12215
|
const phase = governing.phase;
|
|
11685
12216
|
const openSpec = openSpecAllowed(relativePath2, phase);
|
|
11686
12217
|
if (openSpec) return allowed(openSpec);
|
|
@@ -11689,7 +12220,7 @@ var classicHookGuardCommand = async (args) => {
|
|
|
11689
12220
|
return allowed(`${relativePath2} (phase: ${phase}, superpowers)`);
|
|
11690
12221
|
}
|
|
11691
12222
|
if (governing.superpowersArtifact === "unmatched") {
|
|
11692
|
-
return blockedUnmatchedSuperpowersArtifact(relativePath2,
|
|
12223
|
+
return blockedUnmatchedSuperpowersArtifact(relativePath2, governing);
|
|
11693
12224
|
}
|
|
11694
12225
|
}
|
|
11695
12226
|
if (phase === "build" && governing.classic?.workflow === "full" && !governing.classic.designDoc) {
|
|
@@ -12069,12 +12600,438 @@ var classicIntentCommand = async (args, _options) => {
|
|
|
12069
12600
|
}
|
|
12070
12601
|
};
|
|
12071
12602
|
|
|
12603
|
+
// domains/comet-classic/classic-resume-probe.ts
|
|
12604
|
+
import path20 from "path";
|
|
12605
|
+
import { promises as fs19 } from "fs";
|
|
12606
|
+
import { spawn } from "child_process";
|
|
12607
|
+
var COMET_RESUME_PROBE_SCHEMA_VERSION = "comet.resume_probe.v1";
|
|
12608
|
+
function isRecord2(value) {
|
|
12609
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12610
|
+
}
|
|
12611
|
+
function normalizeInput(input) {
|
|
12612
|
+
if (!isRecord2(input)) {
|
|
12613
|
+
throw new Error("Invalid CometResumeProbeInput: input must be an object");
|
|
12614
|
+
}
|
|
12615
|
+
if (input.schema_version !== COMET_RESUME_PROBE_SCHEMA_VERSION) {
|
|
12616
|
+
throw new Error(
|
|
12617
|
+
`Invalid CometResumeProbeInput: schema_version must be ${COMET_RESUME_PROBE_SCHEMA_VERSION}`
|
|
12618
|
+
);
|
|
12619
|
+
}
|
|
12620
|
+
if (typeof input.utterance !== "string") {
|
|
12621
|
+
throw new Error("Invalid CometResumeProbeInput: utterance must be a string");
|
|
12622
|
+
}
|
|
12623
|
+
const context = isRecord2(input.agent_context) ? input.agent_context : {};
|
|
12624
|
+
return {
|
|
12625
|
+
schema_version: COMET_RESUME_PROBE_SCHEMA_VERSION,
|
|
12626
|
+
utterance: input.utterance,
|
|
12627
|
+
locale: typeof input.locale === "string" ? input.locale : "unknown",
|
|
12628
|
+
agent_context: {
|
|
12629
|
+
non_trivial_work: context.non_trivial_work === true,
|
|
12630
|
+
already_in_comet_flow: context.already_in_comet_flow === true
|
|
12631
|
+
}
|
|
12632
|
+
};
|
|
12633
|
+
}
|
|
12634
|
+
function result3(action, change, confidence, reason, evidence = []) {
|
|
12635
|
+
return {
|
|
12636
|
+
schema_version: COMET_RESUME_PROBE_SCHEMA_VERSION,
|
|
12637
|
+
action,
|
|
12638
|
+
changeName: change?.name ?? null,
|
|
12639
|
+
phase: change?.phase ?? null,
|
|
12640
|
+
nextCommand: action === "auto_resume" || action === "ask_user" ? change?.nextCommand ?? null : null,
|
|
12641
|
+
confidence,
|
|
12642
|
+
reason,
|
|
12643
|
+
evidence
|
|
12644
|
+
};
|
|
12645
|
+
}
|
|
12646
|
+
async function readIfExists(filePath) {
|
|
12647
|
+
if (!await fileExists3(filePath)) return "";
|
|
12648
|
+
return fs19.readFile(filePath, "utf8");
|
|
12649
|
+
}
|
|
12650
|
+
async function changeSearchText(changeDir, classic) {
|
|
12651
|
+
const files = ["proposal.md", "design.md", "tasks.md"];
|
|
12652
|
+
const parts = [classic.name, classic.workflow, classic.phase];
|
|
12653
|
+
for (const file of files) {
|
|
12654
|
+
parts.push(await readIfExists(path20.join(changeDir, file)));
|
|
12655
|
+
}
|
|
12656
|
+
return parts.join("\n").toLowerCase();
|
|
12657
|
+
}
|
|
12658
|
+
function nextCommandForPhase2(phase) {
|
|
12659
|
+
switch (phase) {
|
|
12660
|
+
case "open":
|
|
12661
|
+
return "/comet-open";
|
|
12662
|
+
case "design":
|
|
12663
|
+
return "/comet-design";
|
|
12664
|
+
case "build":
|
|
12665
|
+
return "/comet-build";
|
|
12666
|
+
case "verify":
|
|
12667
|
+
return "/comet-verify";
|
|
12668
|
+
case "archive":
|
|
12669
|
+
return "/comet-archive";
|
|
12670
|
+
default:
|
|
12671
|
+
return null;
|
|
12672
|
+
}
|
|
12673
|
+
}
|
|
12674
|
+
function diagnosticFromProjection(changeDir, name, projection) {
|
|
12675
|
+
const classic = projection.classic;
|
|
12676
|
+
const unknownKeys = projection.unknownKeys.filter((key) => key !== "run_id");
|
|
12677
|
+
if (!classic) {
|
|
12678
|
+
return {
|
|
12679
|
+
name,
|
|
12680
|
+
valid: false,
|
|
12681
|
+
workflow: "unknown",
|
|
12682
|
+
phase: "invalid",
|
|
12683
|
+
currentStep: null,
|
|
12684
|
+
nextCommand: null,
|
|
12685
|
+
runtimeMode: "invalid",
|
|
12686
|
+
runtimeEval: null,
|
|
12687
|
+
evidence: [],
|
|
12688
|
+
error: `${changeDir} does not contain valid Comet state`
|
|
12689
|
+
};
|
|
12690
|
+
}
|
|
12691
|
+
if (unknownKeys.length > 0) {
|
|
12692
|
+
return {
|
|
12693
|
+
name,
|
|
12694
|
+
valid: false,
|
|
12695
|
+
workflow: classic.workflow,
|
|
12696
|
+
phase: classic.phase,
|
|
12697
|
+
currentStep: null,
|
|
12698
|
+
nextCommand: null,
|
|
12699
|
+
runtimeMode: "invalid",
|
|
12700
|
+
runtimeEval: null,
|
|
12701
|
+
evidence: [],
|
|
12702
|
+
error: `unknown field(s): ${unknownKeys.join(", ")}`
|
|
12703
|
+
};
|
|
12704
|
+
}
|
|
12705
|
+
return {
|
|
12706
|
+
name,
|
|
12707
|
+
valid: true,
|
|
12708
|
+
workflow: classic.workflow,
|
|
12709
|
+
phase: classic.phase,
|
|
12710
|
+
currentStep: null,
|
|
12711
|
+
nextCommand: nextCommandForPhase2(classic.phase),
|
|
12712
|
+
runtimeMode: "engine-projection",
|
|
12713
|
+
runtimeEval: null,
|
|
12714
|
+
evidence: []
|
|
12715
|
+
};
|
|
12716
|
+
}
|
|
12717
|
+
async function hasOpenSpecChangeFiles(changeDir) {
|
|
12718
|
+
return await fileExists3(path20.join(changeDir, "proposal.md")) || await fileExists3(path20.join(changeDir, "design.md")) || await fileExists3(path20.join(changeDir, "tasks.md"));
|
|
12719
|
+
}
|
|
12720
|
+
async function discoverActiveChanges(projectRoot2) {
|
|
12721
|
+
const changesDir = path20.join(projectRoot2, "openspec", "changes");
|
|
12722
|
+
if (!await fileExists3(changesDir)) return [];
|
|
12723
|
+
const entries = await readDir(changesDir);
|
|
12724
|
+
const changes = [];
|
|
12725
|
+
for (const entry2 of entries) {
|
|
12726
|
+
if (entry2 === "archive") continue;
|
|
12727
|
+
const changeDir = path20.join(changesDir, entry2);
|
|
12728
|
+
const stat = await fs19.stat(changeDir).catch(() => null);
|
|
12729
|
+
if (!stat?.isDirectory()) continue;
|
|
12730
|
+
const hasCometState = await fileExists3(path20.join(changeDir, ".comet.yaml"));
|
|
12731
|
+
if (!hasCometState) {
|
|
12732
|
+
if (!await hasOpenSpecChangeFiles(changeDir)) continue;
|
|
12733
|
+
const missingStateChange = {
|
|
12734
|
+
name: entry2,
|
|
12735
|
+
workflow: "unknown",
|
|
12736
|
+
phase: "invalid",
|
|
12737
|
+
nextCommand: null,
|
|
12738
|
+
diagnostic: {
|
|
12739
|
+
name: entry2,
|
|
12740
|
+
valid: false,
|
|
12741
|
+
workflow: "unknown",
|
|
12742
|
+
phase: "invalid",
|
|
12743
|
+
currentStep: null,
|
|
12744
|
+
nextCommand: null,
|
|
12745
|
+
runtimeMode: "invalid",
|
|
12746
|
+
runtimeEval: null,
|
|
12747
|
+
evidence: [],
|
|
12748
|
+
error: "missing Comet state"
|
|
12749
|
+
},
|
|
12750
|
+
buildPause: null,
|
|
12751
|
+
hasClassicProjection: false,
|
|
12752
|
+
verifyResult: null,
|
|
12753
|
+
text: "",
|
|
12754
|
+
missingCometState: true
|
|
12755
|
+
};
|
|
12756
|
+
missingStateChange.text = await changeSearchText(changeDir, missingStateChange);
|
|
12757
|
+
changes.push(missingStateChange);
|
|
12758
|
+
continue;
|
|
12759
|
+
}
|
|
12760
|
+
const projection = await readClassicState(changeDir, { migrate: false });
|
|
12761
|
+
const classic = projection.classic;
|
|
12762
|
+
const diagnostic = diagnosticFromProjection(changeDir, entry2, projection);
|
|
12763
|
+
const hasClassicProjection = Boolean(classic);
|
|
12764
|
+
const phase = classic?.phase ?? diagnostic.phase;
|
|
12765
|
+
const workflow = classic?.workflow ?? diagnostic.workflow;
|
|
12766
|
+
if (phase === "archive" || classic?.archived) continue;
|
|
12767
|
+
const change = {
|
|
12768
|
+
name: entry2,
|
|
12769
|
+
workflow,
|
|
12770
|
+
phase,
|
|
12771
|
+
nextCommand: diagnostic.nextCommand,
|
|
12772
|
+
diagnostic,
|
|
12773
|
+
buildPause: classic?.buildPause ?? null,
|
|
12774
|
+
hasClassicProjection,
|
|
12775
|
+
verifyResult: classic?.verifyResult ?? null,
|
|
12776
|
+
text: "",
|
|
12777
|
+
missingCometState: false
|
|
12778
|
+
};
|
|
12779
|
+
change.text = await changeSearchText(changeDir, change);
|
|
12780
|
+
changes.push(change);
|
|
12781
|
+
}
|
|
12782
|
+
return changes;
|
|
12783
|
+
}
|
|
12784
|
+
var RESUME_WORDS = [
|
|
12785
|
+
"continue",
|
|
12786
|
+
"resume",
|
|
12787
|
+
"carry on",
|
|
12788
|
+
"finish",
|
|
12789
|
+
"run it",
|
|
12790
|
+
"commit",
|
|
12791
|
+
"verify",
|
|
12792
|
+
"archive",
|
|
12793
|
+
"继续",
|
|
12794
|
+
"接着",
|
|
12795
|
+
"恢复",
|
|
12796
|
+
"跑完",
|
|
12797
|
+
"提交",
|
|
12798
|
+
"验证",
|
|
12799
|
+
"归档",
|
|
12800
|
+
"修刚才"
|
|
12801
|
+
];
|
|
12802
|
+
var QUESTION_WORDS = [
|
|
12803
|
+
"what",
|
|
12804
|
+
"why",
|
|
12805
|
+
"how",
|
|
12806
|
+
"explain",
|
|
12807
|
+
"summarize",
|
|
12808
|
+
"reliable",
|
|
12809
|
+
"靠谱吗",
|
|
12810
|
+
"是什么",
|
|
12811
|
+
"为什么",
|
|
12812
|
+
"解释",
|
|
12813
|
+
"总结",
|
|
12814
|
+
"取名",
|
|
12815
|
+
"命名"
|
|
12816
|
+
];
|
|
12817
|
+
var GENERIC_RELATED_TOKENS = /* @__PURE__ */ new Set([
|
|
12818
|
+
"add",
|
|
12819
|
+
"build",
|
|
12820
|
+
"cache",
|
|
12821
|
+
"change",
|
|
12822
|
+
"code",
|
|
12823
|
+
"design",
|
|
12824
|
+
"docs",
|
|
12825
|
+
"file",
|
|
12826
|
+
"fix",
|
|
12827
|
+
"implement",
|
|
12828
|
+
"plan",
|
|
12829
|
+
"readme",
|
|
12830
|
+
"task",
|
|
12831
|
+
"test",
|
|
12832
|
+
"update",
|
|
12833
|
+
"修改",
|
|
12834
|
+
"更新",
|
|
12835
|
+
"修复",
|
|
12836
|
+
"添加",
|
|
12837
|
+
"文档",
|
|
12838
|
+
"任务",
|
|
12839
|
+
"计划",
|
|
12840
|
+
"实现"
|
|
12841
|
+
]);
|
|
12842
|
+
var OPT_OUT_WORDS = [
|
|
12843
|
+
"do not resume",
|
|
12844
|
+
"don't resume",
|
|
12845
|
+
"without comet",
|
|
12846
|
+
"skip comet",
|
|
12847
|
+
"不要恢复",
|
|
12848
|
+
"不走 comet",
|
|
12849
|
+
"不要走 comet",
|
|
12850
|
+
"直接解释",
|
|
12851
|
+
"只回答"
|
|
12852
|
+
];
|
|
12853
|
+
function includesAny(text2, words) {
|
|
12854
|
+
return words.some((word) => text2.includes(word));
|
|
12855
|
+
}
|
|
12856
|
+
function hasDecisionPoint(change) {
|
|
12857
|
+
if (change.missingCometState) return true;
|
|
12858
|
+
if (!change.hasClassicProjection) return true;
|
|
12859
|
+
if (!change.diagnostic.valid) return true;
|
|
12860
|
+
if (change.phase === "archive") return true;
|
|
12861
|
+
if (change.verifyResult === "fail") return true;
|
|
12862
|
+
if (change.diagnostic.runtimeEval && !change.diagnostic.runtimeEval.passed) return true;
|
|
12863
|
+
if (change.phase !== "build") return false;
|
|
12864
|
+
if (change.buildPause === "plan-ready") return true;
|
|
12865
|
+
return false;
|
|
12866
|
+
}
|
|
12867
|
+
function relatedEvidence(utterance, change) {
|
|
12868
|
+
const text2 = utterance.toLowerCase();
|
|
12869
|
+
const evidence = [];
|
|
12870
|
+
if (text2.includes(change.name.toLowerCase())) {
|
|
12871
|
+
evidence.push({ source: "user", quote: change.name });
|
|
12872
|
+
}
|
|
12873
|
+
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));
|
|
12874
|
+
const matched = [...new Set(tokens.filter((token) => text2.includes(token)))].slice(0, 3);
|
|
12875
|
+
for (const token of matched) {
|
|
12876
|
+
evidence.push({ source: "repo", quote: token });
|
|
12877
|
+
}
|
|
12878
|
+
return evidence;
|
|
12879
|
+
}
|
|
12880
|
+
async function gitDirtyFiles(projectRoot2) {
|
|
12881
|
+
return new Promise((resolve) => {
|
|
12882
|
+
const child = spawn("git", ["status", "--short", "--untracked-files=all"], {
|
|
12883
|
+
cwd: projectRoot2,
|
|
12884
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
12885
|
+
shell: false
|
|
12886
|
+
});
|
|
12887
|
+
const chunks = [];
|
|
12888
|
+
child.stdout.on("data", (chunk) => {
|
|
12889
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
|
|
12890
|
+
});
|
|
12891
|
+
child.on("error", () => resolve([]));
|
|
12892
|
+
child.on("exit", (code) => {
|
|
12893
|
+
if (code !== 0) {
|
|
12894
|
+
resolve([]);
|
|
12895
|
+
return;
|
|
12896
|
+
}
|
|
12897
|
+
const dirtyFiles = Buffer.concat(chunks).toString("utf8").split(/\r?\n/u).map((line) => line.trim()).filter(Boolean);
|
|
12898
|
+
resolve(dirtyFiles);
|
|
12899
|
+
});
|
|
12900
|
+
});
|
|
12901
|
+
}
|
|
12902
|
+
async function resolveCometResumeProbe(projectRoot2, rawInput) {
|
|
12903
|
+
const input = normalizeInput(rawInput);
|
|
12904
|
+
const utterance = input.utterance.trim();
|
|
12905
|
+
const lower = utterance.toLowerCase();
|
|
12906
|
+
if (input.agent_context.already_in_comet_flow) {
|
|
12907
|
+
return result3("out_of_scope", null, "low", "already in Comet flow");
|
|
12908
|
+
}
|
|
12909
|
+
if (includesAny(lower, OPT_OUT_WORDS)) {
|
|
12910
|
+
return result3("out_of_scope", null, "low", "user opted out of Comet resume", [
|
|
12911
|
+
{ source: "user", quote: utterance }
|
|
12912
|
+
]);
|
|
12913
|
+
}
|
|
12914
|
+
const changes = await discoverActiveChanges(projectRoot2);
|
|
12915
|
+
if (changes.length === 0) {
|
|
12916
|
+
return result3("none", null, "none", "no active Comet changes");
|
|
12917
|
+
}
|
|
12918
|
+
const dirtyFiles = await gitDirtyFiles(projectRoot2);
|
|
12919
|
+
if (changes.length > 1) {
|
|
12920
|
+
const named = changes.find((change2) => lower.includes(change2.name.toLowerCase()));
|
|
12921
|
+
if (!named) {
|
|
12922
|
+
return result3("ask_user", null, "low", "multiple active changes require a change name");
|
|
12923
|
+
}
|
|
12924
|
+
if (dirtyFiles.length > 0) {
|
|
12925
|
+
return result3("ask_user", named, "low", "uncommitted worktree changes require attribution", [
|
|
12926
|
+
{ source: "repo", quote: `${dirtyFiles.length} dirty file(s)` }
|
|
12927
|
+
]);
|
|
12928
|
+
}
|
|
12929
|
+
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", [
|
|
12930
|
+
{ source: "user", quote: named.name }
|
|
12931
|
+
]);
|
|
12932
|
+
}
|
|
12933
|
+
const [change] = changes;
|
|
12934
|
+
if (dirtyFiles.length > 0) {
|
|
12935
|
+
return result3("ask_user", change, "low", "uncommitted worktree changes require attribution", [
|
|
12936
|
+
{ source: "repo", quote: `${dirtyFiles.length} dirty file(s)` }
|
|
12937
|
+
]);
|
|
12938
|
+
}
|
|
12939
|
+
if (hasDecisionPoint(change)) {
|
|
12940
|
+
if (change.missingCometState) {
|
|
12941
|
+
return result3("ask_user", change, "low", "active OpenSpec change is missing Comet state");
|
|
12942
|
+
}
|
|
12943
|
+
return result3("ask_user", change, "low", "active change is at a decision point", [
|
|
12944
|
+
{ source: "state", quote: `phase: ${change.phase}` }
|
|
12945
|
+
]);
|
|
12946
|
+
}
|
|
12947
|
+
const resumeLike = includesAny(lower, RESUME_WORDS);
|
|
12948
|
+
const questionLike = !input.agent_context.non_trivial_work && includesAny(lower, QUESTION_WORDS);
|
|
12949
|
+
if (questionLike && !resumeLike) {
|
|
12950
|
+
return result3("out_of_scope", change, "low", "user asked a question without workflow work");
|
|
12951
|
+
}
|
|
12952
|
+
const evidence = relatedEvidence(utterance, change);
|
|
12953
|
+
if (resumeLike || evidence.length > 0) {
|
|
12954
|
+
return result3("auto_resume", change, "high", "single active change and request is related", [
|
|
12955
|
+
{ source: "state", quote: `phase: ${change.phase}` },
|
|
12956
|
+
...evidence
|
|
12957
|
+
]);
|
|
12958
|
+
}
|
|
12959
|
+
if (input.agent_context.non_trivial_work) {
|
|
12960
|
+
return result3(
|
|
12961
|
+
"ask_user",
|
|
12962
|
+
change,
|
|
12963
|
+
"low",
|
|
12964
|
+
"single active change exists but request looks unrelated"
|
|
12965
|
+
);
|
|
12966
|
+
}
|
|
12967
|
+
return result3("out_of_scope", change, "low", "request is not workflow work");
|
|
12968
|
+
}
|
|
12969
|
+
|
|
12970
|
+
// domains/comet-classic/classic-resume-probe-command.ts
|
|
12971
|
+
function result4(exitCode, stdout, stderr) {
|
|
12972
|
+
return {
|
|
12973
|
+
exitCode,
|
|
12974
|
+
...stdout === void 0 ? {} : { stdout },
|
|
12975
|
+
...stderr === void 0 ? {} : { stderr }
|
|
12976
|
+
};
|
|
12977
|
+
}
|
|
12978
|
+
function usage2() {
|
|
12979
|
+
return result4(
|
|
12980
|
+
64,
|
|
12981
|
+
void 0,
|
|
12982
|
+
"Usage: comet-resume-probe.mjs probe <input-json>\nUsage: comet-resume-probe.mjs probe --stdin"
|
|
12983
|
+
);
|
|
12984
|
+
}
|
|
12985
|
+
async function readStdin2() {
|
|
12986
|
+
const chunks = [];
|
|
12987
|
+
for await (const chunk of process.stdin) {
|
|
12988
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
|
|
12989
|
+
}
|
|
12990
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
12991
|
+
}
|
|
12992
|
+
function rawUtteranceInput(utterance) {
|
|
12993
|
+
return {
|
|
12994
|
+
schema_version: COMET_RESUME_PROBE_SCHEMA_VERSION,
|
|
12995
|
+
utterance,
|
|
12996
|
+
locale: "unknown",
|
|
12997
|
+
agent_context: {
|
|
12998
|
+
non_trivial_work: true,
|
|
12999
|
+
already_in_comet_flow: false
|
|
13000
|
+
}
|
|
13001
|
+
};
|
|
13002
|
+
}
|
|
13003
|
+
function parseStdinInput(source) {
|
|
13004
|
+
try {
|
|
13005
|
+
return JSON.parse(source);
|
|
13006
|
+
} catch {
|
|
13007
|
+
return rawUtteranceInput(source);
|
|
13008
|
+
}
|
|
13009
|
+
}
|
|
13010
|
+
var classicResumeProbeCommand = async (args) => {
|
|
13011
|
+
const [subcommand, input] = args;
|
|
13012
|
+
if (subcommand !== "probe") return usage2();
|
|
13013
|
+
const fromStdin = input === "--stdin";
|
|
13014
|
+
const source = fromStdin ? await readStdin2() : input;
|
|
13015
|
+
if (!source) return usage2();
|
|
13016
|
+
try {
|
|
13017
|
+
const parsedInput = fromStdin ? parseStdinInput(source) : JSON.parse(source);
|
|
13018
|
+
const resolution = await resolveCometResumeProbe(process.cwd(), parsedInput);
|
|
13019
|
+
return result4(0, `${JSON.stringify(resolution, null, 2)}
|
|
13020
|
+
`);
|
|
13021
|
+
} catch (error) {
|
|
13022
|
+
if (error instanceof SyntaxError) {
|
|
13023
|
+
return result4(1, void 0, `Invalid JSON: ${error.message}`);
|
|
13024
|
+
}
|
|
13025
|
+
return result4(1, void 0, error instanceof Error ? error.message : String(error));
|
|
13026
|
+
}
|
|
13027
|
+
};
|
|
13028
|
+
|
|
12072
13029
|
// domains/comet-classic/classic-state-command.ts
|
|
12073
|
-
var
|
|
13030
|
+
var import_yaml7 = __toESM(require_dist(), 1);
|
|
12074
13031
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
12075
|
-
import { randomUUID as
|
|
12076
|
-
import { existsSync as existsSync3, promises as
|
|
12077
|
-
import
|
|
13032
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
13033
|
+
import { existsSync as existsSync3, promises as fs20 } from "fs";
|
|
13034
|
+
import path21 from "path";
|
|
12078
13035
|
init_state();
|
|
12079
13036
|
var GREEN5 = "\x1B[32m";
|
|
12080
13037
|
var RED5 = "\x1B[31m";
|
|
@@ -12086,6 +13043,8 @@ var ARTIFACT_LANGUAGES2 = ["en", "zh-CN"];
|
|
|
12086
13043
|
var EVENTS = CLASSIC_TRANSITION_EVENTS;
|
|
12087
13044
|
var MACHINE_OWNED_FIELDS = /* @__PURE__ */ new Set([
|
|
12088
13045
|
...RUN_WIRE_KEYS,
|
|
13046
|
+
"archive_confirmation",
|
|
13047
|
+
"verify_failures",
|
|
12089
13048
|
"classic_profile",
|
|
12090
13049
|
"classic_migration"
|
|
12091
13050
|
]);
|
|
@@ -12101,11 +13060,12 @@ var FIELD_ENUMS = {
|
|
|
12101
13060
|
subagent_dispatch: ["null", "confirmed"],
|
|
12102
13061
|
tdd_mode: ["tdd", "direct"],
|
|
12103
13062
|
review_mode: ["off", "standard", "thorough"],
|
|
12104
|
-
isolation: ["branch", "worktree"],
|
|
13063
|
+
isolation: ["current", "branch", "worktree"],
|
|
12105
13064
|
verify_mode: ["light", "full"],
|
|
12106
13065
|
auto_transition: ["true", "false"],
|
|
12107
13066
|
verify_result: ["pending", "pass", "fail"],
|
|
12108
13067
|
branch_status: ["pending", "handled"],
|
|
13068
|
+
archive_confirmation: ["pending", "confirmed"],
|
|
12109
13069
|
archived: ["true", "false"],
|
|
12110
13070
|
direct_override: ["true", "false"],
|
|
12111
13071
|
classic_profile: PROFILES,
|
|
@@ -12121,7 +13081,9 @@ var CLASSIC_FIELD_WIRE_NAMES2 = {
|
|
|
12121
13081
|
phase: "phase",
|
|
12122
13082
|
verificationReport: "verification_report",
|
|
12123
13083
|
verifiedAt: "verified_at",
|
|
13084
|
+
archiveConfirmation: "archive_confirmation",
|
|
12124
13085
|
verifyResult: "verify_result",
|
|
13086
|
+
verifyFailures: "verify_failures",
|
|
12125
13087
|
workflow: "workflow"
|
|
12126
13088
|
};
|
|
12127
13089
|
var CommandFailure = class extends Error {
|
|
@@ -12182,7 +13144,7 @@ function validateRelativePath(value, field2) {
|
|
|
12182
13144
|
}
|
|
12183
13145
|
async function exists6(file) {
|
|
12184
13146
|
try {
|
|
12185
|
-
await
|
|
13147
|
+
await fs20.access(file);
|
|
12186
13148
|
return true;
|
|
12187
13149
|
} catch (error) {
|
|
12188
13150
|
if (error.code === "ENOENT") return false;
|
|
@@ -12191,39 +13153,39 @@ async function exists6(file) {
|
|
|
12191
13153
|
}
|
|
12192
13154
|
async function nonempty3(file) {
|
|
12193
13155
|
try {
|
|
12194
|
-
return (await
|
|
13156
|
+
return (await fs20.stat(file)).size > 0;
|
|
12195
13157
|
} catch (error) {
|
|
12196
13158
|
if (error.code === "ENOENT") return false;
|
|
12197
13159
|
throw error;
|
|
12198
13160
|
}
|
|
12199
13161
|
}
|
|
12200
|
-
async function
|
|
13162
|
+
async function changeDirectory2(name) {
|
|
12201
13163
|
return resolveClassicChangeDirectory(name);
|
|
12202
13164
|
}
|
|
12203
13165
|
async function readDocument2(file) {
|
|
12204
13166
|
let source;
|
|
12205
13167
|
try {
|
|
12206
|
-
source = await
|
|
13168
|
+
source = await fs20.readFile(file, "utf8");
|
|
12207
13169
|
} catch (error) {
|
|
12208
13170
|
if (error.code === "ENOENT") {
|
|
12209
13171
|
fail2(
|
|
12210
|
-
`ERROR: .comet.yaml not found at ${
|
|
13172
|
+
`ERROR: .comet.yaml not found at ${path21.relative(process.cwd(), file).replaceAll("\\", "/")}`
|
|
12211
13173
|
);
|
|
12212
13174
|
}
|
|
12213
13175
|
throw error;
|
|
12214
13176
|
}
|
|
12215
|
-
const document = (0,
|
|
13177
|
+
const document = (0, import_yaml7.parseDocument)(source, { uniqueKeys: false });
|
|
12216
13178
|
if (document.errors.length > 0) fail2(`ERROR: Invalid .comet.yaml: ${document.errors[0].message}`);
|
|
12217
13179
|
return document;
|
|
12218
13180
|
}
|
|
12219
13181
|
async function atomicWrite2(file, content) {
|
|
12220
|
-
await
|
|
12221
|
-
const temporary = `${file}.${
|
|
13182
|
+
await fs20.mkdir(path21.dirname(file), { recursive: true });
|
|
13183
|
+
const temporary = `${file}.${randomUUID7()}.tmp`;
|
|
12222
13184
|
try {
|
|
12223
|
-
await
|
|
12224
|
-
await
|
|
13185
|
+
await fs20.writeFile(temporary, content, "utf8");
|
|
13186
|
+
await fs20.rename(temporary, file);
|
|
12225
13187
|
} catch (error) {
|
|
12226
|
-
await
|
|
13188
|
+
await fs20.rm(temporary, { force: true });
|
|
12227
13189
|
throw error;
|
|
12228
13190
|
}
|
|
12229
13191
|
}
|
|
@@ -12256,6 +13218,10 @@ function nullableRecordBoolean(record, field2) {
|
|
|
12256
13218
|
if (value === "false") return false;
|
|
12257
13219
|
return null;
|
|
12258
13220
|
}
|
|
13221
|
+
function nonNegativeRecordInteger(record, field2, fallback = 0) {
|
|
13222
|
+
const value = record[field2];
|
|
13223
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0 ? value : fallback;
|
|
13224
|
+
}
|
|
12259
13225
|
function sparseClassicState(record) {
|
|
12260
13226
|
const workflow = enumRecordValue(record, "workflow", PROFILES, "full");
|
|
12261
13227
|
return {
|
|
@@ -12283,7 +13249,12 @@ function sparseClassicState(record) {
|
|
|
12283
13249
|
["off", "standard", "thorough"],
|
|
12284
13250
|
null
|
|
12285
13251
|
),
|
|
12286
|
-
isolation: enumRecordValue(
|
|
13252
|
+
isolation: enumRecordValue(
|
|
13253
|
+
record,
|
|
13254
|
+
"isolation",
|
|
13255
|
+
["current", "branch", "worktree"],
|
|
13256
|
+
null
|
|
13257
|
+
),
|
|
12287
13258
|
verifyMode: enumRecordValue(record, "verify_mode", ["light", "full"], null),
|
|
12288
13259
|
autoTransition: nullableRecordBoolean(record, "auto_transition"),
|
|
12289
13260
|
baseRef: nullableRecordString(record, "base_ref"),
|
|
@@ -12295,10 +13266,17 @@ function sparseClassicState(record) {
|
|
|
12295
13266
|
["pending", "pass", "fail"],
|
|
12296
13267
|
"pending"
|
|
12297
13268
|
),
|
|
13269
|
+
verifyFailures: nonNegativeRecordInteger(record, "verify_failures"),
|
|
12298
13270
|
verificationReport: nullableRecordString(record, "verification_report"),
|
|
12299
13271
|
branchStatus: enumRecordValue(record, "branch_status", ["pending", "handled"], null),
|
|
12300
13272
|
createdAt: nullableRecordString(record, "created_at"),
|
|
12301
13273
|
verifiedAt: nullableRecordString(record, "verified_at"),
|
|
13274
|
+
archiveConfirmation: enumRecordValue(
|
|
13275
|
+
record,
|
|
13276
|
+
"archive_confirmation",
|
|
13277
|
+
["pending", "confirmed"],
|
|
13278
|
+
null
|
|
13279
|
+
),
|
|
12302
13280
|
archived: nullableRecordBoolean(record, "archived") ?? false,
|
|
12303
13281
|
directOverride: nullableRecordBoolean(record, "direct_override"),
|
|
12304
13282
|
handoffContext: nullableRecordString(record, "handoff_context"),
|
|
@@ -12308,17 +13286,13 @@ function sparseClassicState(record) {
|
|
|
12308
13286
|
};
|
|
12309
13287
|
}
|
|
12310
13288
|
async function projectConfigValue2(field2) {
|
|
12311
|
-
|
|
12312
|
-
if (!await exists6(file)) return null;
|
|
12313
|
-
const document = await readDocument2(file);
|
|
12314
|
-
const value = document.get(field2);
|
|
12315
|
-
return value === null || value === void 0 ? null : scalar(value);
|
|
13289
|
+
return (await readClassicConfigValue(field2))?.value ?? null;
|
|
12316
13290
|
}
|
|
12317
13291
|
async function projectLanguageDefault() {
|
|
12318
13292
|
if (process.env.COMET_LANGUAGE)
|
|
12319
13293
|
return validateLanguage(process.env.COMET_LANGUAGE, "COMET_LANGUAGE");
|
|
12320
|
-
const
|
|
12321
|
-
if (
|
|
13294
|
+
const configured = await readClassicConfigValue("language");
|
|
13295
|
+
if (configured) return validateLanguage(configured.value, configured.source);
|
|
12322
13296
|
return "en";
|
|
12323
13297
|
}
|
|
12324
13298
|
async function contextCompression() {
|
|
@@ -12346,14 +13320,14 @@ Valid values: off, standard, thorough`);
|
|
|
12346
13320
|
return value === "null" ? null : value;
|
|
12347
13321
|
}
|
|
12348
13322
|
function gitOutput(args) {
|
|
12349
|
-
const
|
|
12350
|
-
return
|
|
13323
|
+
const result5 = spawnSync3("git", args, { encoding: "utf8" });
|
|
13324
|
+
return result5.status === 0 ? result5.stdout.trim() : null;
|
|
12351
13325
|
}
|
|
12352
13326
|
async function stateFile(name) {
|
|
12353
|
-
const change = await
|
|
13327
|
+
const change = await changeDirectory2(name);
|
|
12354
13328
|
return {
|
|
12355
13329
|
...change,
|
|
12356
|
-
file:
|
|
13330
|
+
file: path21.join(change.directory, ".comet.yaml")
|
|
12357
13331
|
};
|
|
12358
13332
|
}
|
|
12359
13333
|
async function readField3(name, field2) {
|
|
@@ -12371,7 +13345,7 @@ async function readField3(name, field2) {
|
|
|
12371
13345
|
return scalar(value);
|
|
12372
13346
|
}
|
|
12373
13347
|
function parsedValue(field2, value) {
|
|
12374
|
-
const document = (0,
|
|
13348
|
+
const document = (0, import_yaml7.parseDocument)(`${field2}: ${value}
|
|
12375
13349
|
`);
|
|
12376
13350
|
if (document.errors.length > 0) fail2(`ERROR: Invalid value: '${value}'`);
|
|
12377
13351
|
return document.get(field2);
|
|
@@ -12393,7 +13367,7 @@ function validateSetValue(field2, value) {
|
|
|
12393
13367
|
}
|
|
12394
13368
|
async function setField2(output, name, field2, value, options = {}) {
|
|
12395
13369
|
if (MACHINE_OWNED_FIELDS.has(field2) && !options.machineOwned) {
|
|
12396
|
-
fail2(`ERROR: '${field2}' is a machine-owned
|
|
13370
|
+
fail2(`ERROR: '${field2}' is a machine-owned field and cannot be set directly`);
|
|
12397
13371
|
}
|
|
12398
13372
|
if (!SETTABLE_FIELDS.has(field2) && !MACHINE_OWNED_FIELDS.has(field2)) {
|
|
12399
13373
|
fail2(`ERROR: Unknown field: '${field2}'`);
|
|
@@ -12456,10 +13430,10 @@ async function init(output, name, workflow) {
|
|
|
12456
13430
|
validateEnum(workflow, PROFILES);
|
|
12457
13431
|
const { file, label, directory } = await stateFile(name);
|
|
12458
13432
|
if (await exists6(file)) fail2(`ERROR: .comet.yaml already exists at ${label}/.comet.yaml`);
|
|
12459
|
-
await
|
|
13433
|
+
await fs20.mkdir(directory, { recursive: true });
|
|
12460
13434
|
const preset = workflow !== "full";
|
|
12461
13435
|
const reviewMode = preset ? "off" : await reviewModeDefault();
|
|
12462
|
-
const document = new
|
|
13436
|
+
const document = new import_yaml7.Document({
|
|
12463
13437
|
workflow,
|
|
12464
13438
|
language: await projectLanguageDefault(),
|
|
12465
13439
|
phase: "open",
|
|
@@ -12469,17 +13443,19 @@ async function init(output, name, workflow) {
|
|
|
12469
13443
|
subagent_dispatch: null,
|
|
12470
13444
|
tdd_mode: preset ? "direct" : null,
|
|
12471
13445
|
review_mode: reviewMode,
|
|
12472
|
-
isolation: preset ? "
|
|
13446
|
+
isolation: preset ? "current" : null,
|
|
12473
13447
|
verify_mode: preset ? "light" : null,
|
|
12474
13448
|
auto_transition: await autoTransition() === "true",
|
|
12475
13449
|
base_ref: gitOutput(["rev-parse", "--verify", "HEAD"]),
|
|
12476
13450
|
design_doc: null,
|
|
12477
13451
|
plan: null,
|
|
12478
13452
|
verify_result: "pending",
|
|
13453
|
+
verify_failures: 0,
|
|
12479
13454
|
verification_report: null,
|
|
12480
13455
|
branch_status: "pending",
|
|
12481
13456
|
created_at: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
|
|
12482
13457
|
verified_at: null,
|
|
13458
|
+
archive_confirmation: null,
|
|
12483
13459
|
archived: false
|
|
12484
13460
|
});
|
|
12485
13461
|
await atomicWrite2(file, document.toString());
|
|
@@ -12499,9 +13475,10 @@ async function requireBuildDecisions(name) {
|
|
|
12499
13475
|
const subagentDispatch = await readField3(name, "subagent_dispatch");
|
|
12500
13476
|
const tddMode = await readField3(name, "tdd_mode");
|
|
12501
13477
|
const reviewMode = await readField3(name, "review_mode");
|
|
12502
|
-
|
|
13478
|
+
const allowedIsolation = workflow === "full" ? ["branch", "worktree"] : ["current", "branch", "worktree"];
|
|
13479
|
+
if (!allowedIsolation.includes(isolation)) {
|
|
12503
13480
|
fail2(
|
|
12504
|
-
`ERROR: Cannot transition '${name}': isolation must be branch or worktree, got '${isolation || "null"}'`
|
|
13481
|
+
`ERROR: Cannot transition '${name}': isolation must be ${workflow === "full" ? "branch or worktree" : "current, branch, or worktree"}, got '${isolation || "null"}'`
|
|
12505
13482
|
);
|
|
12506
13483
|
}
|
|
12507
13484
|
if (!["subagent-driven-development", "executing-plans", "direct"].includes(buildMode)) {
|
|
@@ -12534,13 +13511,13 @@ async function requireOpenArtifacts(name) {
|
|
|
12534
13511
|
const { directory } = await stateFile(name);
|
|
12535
13512
|
const workflow = await readField3(name, "workflow");
|
|
12536
13513
|
for (const artifact of ["proposal.md", "tasks.md"]) {
|
|
12537
|
-
if (!await nonempty3(
|
|
13514
|
+
if (!await nonempty3(path21.join(directory, artifact))) {
|
|
12538
13515
|
fail2(
|
|
12539
13516
|
`ERROR: Cannot transition '${name}': ${artifact} must exist and be non-empty before leaving open`
|
|
12540
13517
|
);
|
|
12541
13518
|
}
|
|
12542
13519
|
}
|
|
12543
|
-
if (workflow === "full" && !await nonempty3(
|
|
13520
|
+
if (workflow === "full" && !await nonempty3(path21.join(directory, "design.md"))) {
|
|
12544
13521
|
fail2(
|
|
12545
13522
|
`ERROR: Cannot transition '${name}': design.md must exist and be non-empty before leaving open`
|
|
12546
13523
|
);
|
|
@@ -12548,14 +13525,14 @@ async function requireOpenArtifacts(name) {
|
|
|
12548
13525
|
}
|
|
12549
13526
|
async function requireDesignEvidence(name) {
|
|
12550
13527
|
const designDoc = await readField3(name, "design_doc");
|
|
12551
|
-
if (!designDoc || designDoc === "null" || !await nonempty3(
|
|
13528
|
+
if (!designDoc || designDoc === "null" || !await nonempty3(path21.resolve(designDoc))) {
|
|
12552
13529
|
fail2(
|
|
12553
13530
|
`ERROR: Cannot transition '${name}': design_doc must point to an existing Design Doc before leaving design`
|
|
12554
13531
|
);
|
|
12555
13532
|
}
|
|
12556
13533
|
}
|
|
12557
13534
|
async function writeSparseTransitionEffects(directory, effects) {
|
|
12558
|
-
const file =
|
|
13535
|
+
const file = path21.join(directory, ".comet.yaml");
|
|
12559
13536
|
const document = await readDocument2(file);
|
|
12560
13537
|
for (const effect of effects) {
|
|
12561
13538
|
const field2 = wireField2(effect.field);
|
|
@@ -12570,21 +13547,21 @@ async function applyTransitionEvent(output, name, event) {
|
|
|
12570
13547
|
let sparse = false;
|
|
12571
13548
|
if (!classic) {
|
|
12572
13549
|
if (projection.run) fail2("ERROR: Classic state projection is missing");
|
|
12573
|
-
const document = await readDocument2(
|
|
13550
|
+
const document = await readDocument2(path21.join(directory, ".comet.yaml"));
|
|
12574
13551
|
classic = sparseClassicState(document.toJS());
|
|
12575
13552
|
sparse = true;
|
|
12576
13553
|
}
|
|
12577
|
-
const
|
|
13554
|
+
const result5 = applyClassicTransition(classic, event);
|
|
12578
13555
|
if (projection.run) {
|
|
12579
|
-
await transitionClassicRuntimeRun(directory,
|
|
13556
|
+
await transitionClassicRuntimeRun(directory, result5.classic, projection.run, {
|
|
12580
13557
|
event,
|
|
12581
13558
|
source: "comet-state"
|
|
12582
13559
|
});
|
|
12583
13560
|
} else if (sparse) {
|
|
12584
|
-
await writeSparseTransitionEffects(directory,
|
|
13561
|
+
await writeSparseTransitionEffects(directory, result5.effects);
|
|
12585
13562
|
} else {
|
|
12586
13563
|
await writeClassicState(directory, {
|
|
12587
|
-
classic:
|
|
13564
|
+
classic: result5.classic,
|
|
12588
13565
|
run: null,
|
|
12589
13566
|
unknownKeys: projection.unknownKeys
|
|
12590
13567
|
});
|
|
@@ -12594,10 +13571,10 @@ async function applyTransitionEvent(output, name, event) {
|
|
|
12594
13571
|
event,
|
|
12595
13572
|
source: "comet-state",
|
|
12596
13573
|
from: classic,
|
|
12597
|
-
to:
|
|
12598
|
-
effects:
|
|
13574
|
+
to: result5.classic,
|
|
13575
|
+
effects: result5.effects
|
|
12599
13576
|
});
|
|
12600
|
-
for (const effect of
|
|
13577
|
+
for (const effect of result5.effects) {
|
|
12601
13578
|
output.stderr.push(green4(`[SET] ${wireField2(effect.field)}=${wireValue2(effect.to)}`));
|
|
12602
13579
|
}
|
|
12603
13580
|
output.stderr.push(green4(`[TRANSITION] ${event}`));
|
|
@@ -12617,16 +13594,21 @@ async function transition(output, name, event) {
|
|
|
12617
13594
|
} else if (event === "verify-pass") {
|
|
12618
13595
|
await requirePhase(name, "verify");
|
|
12619
13596
|
const report = await readField3(name, "verification_report");
|
|
12620
|
-
if (!report || !await exists6(
|
|
13597
|
+
if (!report || !await exists6(path21.resolve(report))) {
|
|
12621
13598
|
fail2(
|
|
12622
13599
|
`ERROR: Cannot transition '${name}': verification_report must point to an existing report file`
|
|
12623
13600
|
);
|
|
12624
13601
|
}
|
|
12625
|
-
if (await readField3(name, "branch_status") !== "handled") {
|
|
12626
|
-
fail2(`ERROR: Cannot transition '${name}': branch_status must be handled`);
|
|
12627
|
-
}
|
|
12628
13602
|
} else if (event === "verify-fail") {
|
|
12629
13603
|
await requirePhase(name, "verify");
|
|
13604
|
+
} else if (event === "archive-confirm") {
|
|
13605
|
+
await requirePhase(name, "archive");
|
|
13606
|
+
if (await readField3(name, "verify_result") !== "pass") {
|
|
13607
|
+
fail2(`ERROR: Cannot transition '${name}': verify_result must be pass before archiving`);
|
|
13608
|
+
}
|
|
13609
|
+
if (await readField3(name, "archived") === "true") {
|
|
13610
|
+
fail2(`ERROR: Cannot transition '${name}': already archived`);
|
|
13611
|
+
}
|
|
12630
13612
|
} else if (event === "preset-escalate") {
|
|
12631
13613
|
await requirePhase(name, "build");
|
|
12632
13614
|
const workflow = await readField3(name, "workflow");
|
|
@@ -12645,6 +13627,11 @@ async function transition(output, name, event) {
|
|
|
12645
13627
|
if (await readField3(name, "verify_result") !== "pass") {
|
|
12646
13628
|
fail2(`ERROR: Cannot transition '${name}': verify_result must be pass before archiving`);
|
|
12647
13629
|
}
|
|
13630
|
+
if (await readField3(name, "archive_confirmation") !== "confirmed") {
|
|
13631
|
+
fail2(
|
|
13632
|
+
`ERROR: Cannot transition '${name}': archive_confirmation must be confirmed before archiving`
|
|
13633
|
+
);
|
|
13634
|
+
}
|
|
12648
13635
|
}
|
|
12649
13636
|
await applyTransitionEvent(output, name, event);
|
|
12650
13637
|
}
|
|
@@ -12671,9 +13658,9 @@ async function next(output, name) {
|
|
|
12671
13658
|
async function taskCheckoff(output, taskFile, taskText) {
|
|
12672
13659
|
validateRelativePath(taskFile, "task file");
|
|
12673
13660
|
if (!taskText) fail2("ERROR: Task text cannot be empty");
|
|
12674
|
-
const file =
|
|
13661
|
+
const file = path21.resolve(taskFile);
|
|
12675
13662
|
if (!await exists6(file)) fail2(`ERROR: Task file not found: ${taskFile}`);
|
|
12676
|
-
const lines = (await
|
|
13663
|
+
const lines = (await fs20.readFile(file, "utf8")).split(/\r?\n/u);
|
|
12677
13664
|
const matches = lines.filter(
|
|
12678
13665
|
(line) => [`- [ ] ${taskText}`, `- [x] ${taskText}`, `- [X] ${taskText}`].includes(line)
|
|
12679
13666
|
);
|
|
@@ -12711,21 +13698,21 @@ async function check2(output, name, phase) {
|
|
|
12711
13698
|
designDoc ? `design_doc=${designDoc} (expected: empty/null)` : "design_doc is empty/null"
|
|
12712
13699
|
);
|
|
12713
13700
|
for (const artifact of ["proposal.md", "design.md", "tasks.md"]) {
|
|
12714
|
-
(await nonempty3(
|
|
12715
|
-
`${artifact} ${await nonempty3(
|
|
13701
|
+
(await nonempty3(path21.join(directory, artifact)) ? pass2 : reject)(
|
|
13702
|
+
`${artifact} ${await nonempty3(path21.join(directory, artifact)) ? "non-empty" : "missing or empty"}`
|
|
12716
13703
|
);
|
|
12717
13704
|
}
|
|
12718
13705
|
} else if (phase === "build") {
|
|
12719
13706
|
const workflow = await readField3(name, "workflow");
|
|
12720
13707
|
const designDoc = await readField3(name, "design_doc");
|
|
12721
13708
|
if (workflow === "full") {
|
|
12722
|
-
(designDoc && designDoc !== "null" && await exists6(
|
|
13709
|
+
(designDoc && designDoc !== "null" && await exists6(path21.resolve(designDoc)) ? pass2 : reject)(`design_doc=${designDoc} (expected: non-null and file exists)`);
|
|
12723
13710
|
} else {
|
|
12724
13711
|
pass2(`workflow=${workflow} (design_doc not required)`);
|
|
12725
13712
|
}
|
|
12726
13713
|
for (const artifact of ["proposal.md", "tasks.md"]) {
|
|
12727
|
-
(await nonempty3(
|
|
12728
|
-
`${artifact} ${await nonempty3(
|
|
13714
|
+
(await nonempty3(path21.join(directory, artifact)) ? pass2 : reject)(
|
|
13715
|
+
`${artifact} ${await nonempty3(path21.join(directory, artifact)) ? "non-empty" : "missing or empty"}`
|
|
12729
13716
|
);
|
|
12730
13717
|
}
|
|
12731
13718
|
} else if (phase === "verify") {
|
|
@@ -12747,7 +13734,7 @@ async function check2(output, name, phase) {
|
|
|
12747
13734
|
}
|
|
12748
13735
|
function fieldStatus(field2, value, file) {
|
|
12749
13736
|
if (!value || value === "null") return ` - ${field2}: PENDING`;
|
|
12750
|
-
if (file && !existsSync3(
|
|
13737
|
+
if (file && !existsSync3(path21.resolve(file))) {
|
|
12751
13738
|
return ` - ${field2}: BROKEN (path ${value} does not exist)`;
|
|
12752
13739
|
}
|
|
12753
13740
|
return ` - ${field2}: DONE (${value})`;
|
|
@@ -12756,7 +13743,7 @@ async function recoverOpen(output, directory) {
|
|
|
12756
13743
|
output.stdout.push(" Artifacts:");
|
|
12757
13744
|
let complete = 0;
|
|
12758
13745
|
for (const artifact of ["proposal.md", "design.md", "tasks.md"]) {
|
|
12759
|
-
const done = await nonempty3(
|
|
13746
|
+
const done = await nonempty3(path21.join(directory, artifact));
|
|
12760
13747
|
if (done) complete += 1;
|
|
12761
13748
|
output.stdout.push(` - ${artifact}: ${done ? "DONE" : "PENDING"}`);
|
|
12762
13749
|
}
|
|
@@ -12769,7 +13756,7 @@ async function recoverDesign(output, name, directory) {
|
|
|
12769
13756
|
output.stdout.push(" Artifacts:");
|
|
12770
13757
|
for (const artifact of ["proposal.md", "design.md", "tasks.md"]) {
|
|
12771
13758
|
output.stdout.push(
|
|
12772
|
-
` - ${artifact}: ${await nonempty3(
|
|
13759
|
+
` - ${artifact}: ${await nonempty3(path21.join(directory, artifact)) ? "DONE" : "MISSING (unexpected in design phase)"}`
|
|
12773
13760
|
);
|
|
12774
13761
|
}
|
|
12775
13762
|
const handoff = await readField3(name, "handoff_context");
|
|
@@ -12783,11 +13770,11 @@ async function recoverDesign(output, name, directory) {
|
|
|
12783
13770
|
fieldStatus("design_doc", design, design),
|
|
12784
13771
|
""
|
|
12785
13772
|
);
|
|
12786
|
-
if (design && design !== "null" && await exists6(
|
|
13773
|
+
if (design && design !== "null" && await exists6(path21.resolve(design))) {
|
|
12787
13774
|
output.stdout.push(
|
|
12788
13775
|
"Recovery action: Design Doc already created and linked. Run guard to transition to build."
|
|
12789
13776
|
);
|
|
12790
|
-
} else if (handoff && handoff !== "null" && await exists6(
|
|
13777
|
+
} else if (handoff && handoff !== "null" && await exists6(path21.resolve(handoff))) {
|
|
12791
13778
|
output.stdout.push(
|
|
12792
13779
|
"Recovery action: Handoff generated but Design Doc not yet created. Resume from brainstorming confirmation (Step 1c)."
|
|
12793
13780
|
);
|
|
@@ -12817,7 +13804,7 @@ async function recoverBuild(output, name, directory, workflow) {
|
|
|
12817
13804
|
decisions.push(fieldStatus("subagent_dispatch", subagentDispatch));
|
|
12818
13805
|
}
|
|
12819
13806
|
output.stdout.push(...decisions, "", " Plan:", fieldStatus("plan", plan, plan), "");
|
|
12820
|
-
const tasks =
|
|
13807
|
+
const tasks = path21.join(directory, "tasks.md");
|
|
12821
13808
|
if (!await exists6(tasks)) {
|
|
12822
13809
|
output.stdout.push(
|
|
12823
13810
|
" Tasks: tasks.md MISSING",
|
|
@@ -12826,14 +13813,14 @@ async function recoverBuild(output, name, directory, workflow) {
|
|
|
12826
13813
|
);
|
|
12827
13814
|
return;
|
|
12828
13815
|
}
|
|
12829
|
-
const lines = (await
|
|
13816
|
+
const lines = (await fs20.readFile(tasks, "utf8")).split(/\r?\n/u);
|
|
12830
13817
|
const total = lines.filter((line) => /^\s*- \[[ xX]\] /u.test(line)).length;
|
|
12831
13818
|
const done = lines.filter((line) => /^\s*- \[[xX]\] /u.test(line)).length;
|
|
12832
13819
|
const pending = total - done;
|
|
12833
13820
|
let planTotal = 0;
|
|
12834
13821
|
let planDone = 0;
|
|
12835
|
-
if (plan && plan !== "null" && await exists6(
|
|
12836
|
-
const planLines = (await
|
|
13822
|
+
if (plan && plan !== "null" && await exists6(path21.resolve(plan))) {
|
|
13823
|
+
const planLines = (await fs20.readFile(path21.resolve(plan), "utf8")).split(/\r?\n/u);
|
|
12837
13824
|
planTotal = planLines.filter((line) => /^\s*- \[[ xX]\] /u.test(line)).length;
|
|
12838
13825
|
planDone = planLines.filter((line) => /^\s*- \[[xX]\] /u.test(line)).length;
|
|
12839
13826
|
}
|
|
@@ -12871,7 +13858,7 @@ function resolveBuildRecoveryAction(workflow, isolation, buildMode, pause, subag
|
|
|
12871
13858
|
}
|
|
12872
13859
|
if (pause === "plan-ready") {
|
|
12873
13860
|
if (buildMode === "subagent-driven-development" && (pending > 0 || planPending > 0)) {
|
|
12874
|
-
return subagentDispatch === "confirmed" ? "Recovery action: Plan-ready pause is stale because build decisions are already selected. Clear build_pause to null, then inspect the first unchecked task (OpenSpec or plan additions) against recent git history/diff. If implemented, check it off; otherwise dispatch a real background subagent. Do not execute the pending task directly in the main window." : "Recovery action: Plan-ready pause is stale and subagent dispatch is not confirmed. Confirm a real background subagent/Task/multi-agent dispatcher and set subagent_dispatch to confirmed, or set build_mode to executing-plans before continuing.";
|
|
13861
|
+
return subagentDispatch === "confirmed" ? "Recovery action: Plan-ready pause is stale because build decisions are already selected. Clear build_pause to null, then inspect the first unchecked task (OpenSpec or plan additions) against recent git history/diff. If implemented, check it off; otherwise dispatch a real background subagent. Do not execute the pending task directly in the main window." : "Recovery action: Plan-ready pause is stale and subagent dispatch is not confirmed. Return to /comet-build Step 2 capability preflight. Confirm a real background subagent/Task/multi-agent dispatcher and set subagent_dispatch to confirmed, or remove the unavailable mode and set build_mode to executing-plans before continuing.";
|
|
12875
13862
|
}
|
|
12876
13863
|
if (pending > 0 || planPending > 0) {
|
|
12877
13864
|
return "Recovery action: Plan-ready pause is stale because build decisions are already selected. Clear build_pause to null, then continue from the first unchecked task.";
|
|
@@ -12892,40 +13879,44 @@ function resolveBuildRecoveryAction(workflow, isolation, buildMode, pause, subag
|
|
|
12892
13879
|
}
|
|
12893
13880
|
if (pending > 0) {
|
|
12894
13881
|
if (buildMode === "subagent-driven-development") {
|
|
12895
|
-
return subagentDispatch === "confirmed" ? "Recovery action: Read tasks.md and the Superpowers plan (which may include additions beyond OpenSpec), then inspect the first unchecked task against recent git history/diff. If implemented, check it off; otherwise dispatch a real background subagent. Do not execute the pending task directly in the main window." : "Recovery action: Subagent dispatch is not confirmed. Confirm a real background subagent/Task/multi-agent dispatcher and set subagent_dispatch to confirmed, or set build_mode to executing-plans before continuing.";
|
|
13882
|
+
return subagentDispatch === "confirmed" ? "Recovery action: Read tasks.md and the Superpowers plan (which may include additions beyond OpenSpec), then inspect the first unchecked task against recent git history/diff. If implemented, check it off; otherwise dispatch a real background subagent. Do not execute the pending task directly in the main window." : "Recovery action: Subagent dispatch is not confirmed. Return to /comet-build Step 2 capability preflight. Confirm a real background subagent/Task/multi-agent dispatcher and set subagent_dispatch to confirmed, or remove the unavailable mode and set build_mode to executing-plans before continuing.";
|
|
12896
13883
|
}
|
|
12897
13884
|
return "Recovery action: Read tasks.md and continue from first unchecked task.";
|
|
12898
13885
|
}
|
|
12899
13886
|
if (planPending > 0) {
|
|
12900
13887
|
if (buildMode === "subagent-driven-development") {
|
|
12901
|
-
return subagentDispatch === "confirmed" ? "Recovery action: Read the Superpowers plan, then inspect the first unchecked Superpowers plan task against recent git history/diff. If implemented, check it off; otherwise dispatch a real background subagent. Do not execute the pending task directly in the main window." : "Recovery action: Subagent dispatch is not confirmed. Confirm a real background subagent/Task/multi-agent dispatcher and set subagent_dispatch to confirmed, or set build_mode to executing-plans before continuing.";
|
|
13888
|
+
return subagentDispatch === "confirmed" ? "Recovery action: Read the Superpowers plan, then inspect the first unchecked Superpowers plan task against recent git history/diff. If implemented, check it off; otherwise dispatch a real background subagent. Do not execute the pending task directly in the main window." : "Recovery action: Subagent dispatch is not confirmed. Return to /comet-build Step 2 capability preflight. Confirm a real background subagent/Task/multi-agent dispatcher and set subagent_dispatch to confirmed, or remove the unavailable mode and set build_mode to executing-plans before continuing.";
|
|
12902
13889
|
}
|
|
12903
13890
|
return "Recovery action: Read the Superpowers plan and continue from the first unchecked plan task.";
|
|
12904
13891
|
}
|
|
12905
13892
|
return "Recovery action: All tasks done. Run guard to transition to verify.";
|
|
12906
13893
|
}
|
|
12907
13894
|
async function recoverVerify(output, name) {
|
|
12908
|
-
const
|
|
13895
|
+
const result5 = await readField3(name, "verify_result");
|
|
13896
|
+
const failures = await readField3(name, "verify_failures");
|
|
12909
13897
|
const mode = await readField3(name, "verify_mode");
|
|
12910
13898
|
const report = await readField3(name, "verification_report");
|
|
12911
13899
|
const branch = await readField3(name, "branch_status");
|
|
12912
13900
|
output.stdout.push(
|
|
12913
13901
|
" Verification:",
|
|
12914
|
-
fieldStatus("verify_result",
|
|
13902
|
+
fieldStatus("verify_result", result5),
|
|
13903
|
+
` - verify_failures: ${failures || "0"}`,
|
|
12915
13904
|
fieldStatus("verify_mode", mode),
|
|
12916
13905
|
fieldStatus("verification_report", report, report),
|
|
12917
|
-
|
|
13906
|
+
branch === "handled" ? " - branch_status: LEGACY (handled before archive; archive still owns final closure)" : " - branch_status: DEFERRED (handled after the archive commit)",
|
|
12918
13907
|
"",
|
|
12919
|
-
|
|
13908
|
+
result5 === "pass" ? "Recovery action: Verification complete. Continue to archive; branch handling happens after archive changes are committed." : result5 === "fail" ? "Recovery action: Verification failed and rolled back to build. Resume from /comet-build." : "Recovery action: Verification not yet started or in progress. Run scale assessment then verify."
|
|
12920
13909
|
);
|
|
12921
13910
|
}
|
|
12922
13911
|
async function recoverArchive(output, name) {
|
|
13912
|
+
const archiveConfirmation = await readField3(name, "archive_confirmation");
|
|
12923
13913
|
output.stdout.push(
|
|
12924
13914
|
" Archive:",
|
|
12925
13915
|
fieldStatus("verify_result", await readField3(name, "verify_result")),
|
|
13916
|
+
fieldStatus("archive_confirmation", archiveConfirmation),
|
|
12926
13917
|
fieldStatus("archived", await readField3(name, "archived")),
|
|
12927
13918
|
"",
|
|
12928
|
-
"Recovery action: Run /comet-archive to complete archiving."
|
|
13919
|
+
archiveConfirmation === "confirmed" ? "Recovery action: Archive is confirmed. Run /comet-archive to complete archiving." : "Recovery action: Ask for final archive confirmation in /comet-archive before running the archive command."
|
|
12929
13920
|
);
|
|
12930
13921
|
}
|
|
12931
13922
|
async function recover(output, name) {
|
|
@@ -12960,19 +13951,19 @@ async function scale(output, name) {
|
|
|
12960
13951
|
validateChangeName4(name);
|
|
12961
13952
|
const { file, directory, label } = await stateFile(name);
|
|
12962
13953
|
if (!await exists6(file)) fail2(`ERROR: .comet.yaml not found at ${label}/.comet.yaml`);
|
|
12963
|
-
const tasksFile =
|
|
12964
|
-
const taskCount = await exists6(tasksFile) ? (await
|
|
12965
|
-
const specs =
|
|
13954
|
+
const tasksFile = path21.join(directory, "tasks.md");
|
|
13955
|
+
const taskCount = await exists6(tasksFile) ? (await fs20.readFile(tasksFile, "utf8")).split(/\r?\n/u).filter((line) => /^- \[/u.test(line)).length : 0;
|
|
13956
|
+
const specs = path21.join(directory, "specs");
|
|
12966
13957
|
let deltaSpecs = 0;
|
|
12967
13958
|
if (await exists6(specs)) {
|
|
12968
|
-
for (const entry2 of await
|
|
12969
|
-
if (await exists6(
|
|
13959
|
+
for (const entry2 of await fs20.readdir(specs)) {
|
|
13960
|
+
if (await exists6(path21.join(specs, entry2, "spec.md"))) deltaSpecs += 1;
|
|
12970
13961
|
}
|
|
12971
13962
|
}
|
|
12972
13963
|
const plan = await readField3(name, "plan");
|
|
12973
13964
|
let baseRef = "";
|
|
12974
|
-
if (plan && plan !== "null" && await exists6(
|
|
12975
|
-
const match = (await
|
|
13965
|
+
if (plan && plan !== "null" && await exists6(path21.resolve(plan))) {
|
|
13966
|
+
const match = (await fs20.readFile(path21.resolve(plan), "utf8")).match(/^base-ref:\s*(.+)$/mu);
|
|
12976
13967
|
baseRef = match?.[1].trim() ?? "";
|
|
12977
13968
|
}
|
|
12978
13969
|
if (!baseRef) baseRef = await readField3(name, "base_ref");
|
|
@@ -12982,19 +13973,102 @@ async function scale(output, name) {
|
|
|
12982
13973
|
...baseRef && baseRef !== "null" ? [`${baseRef}...HEAD`] : ["HEAD"]
|
|
12983
13974
|
]);
|
|
12984
13975
|
const changedFiles = changed ? changed.split(/\r?\n/u).filter(Boolean).length : 0;
|
|
12985
|
-
const
|
|
12986
|
-
await setField2(new CommandOutput(), name, "verify_mode",
|
|
13976
|
+
const result5 = taskCount > 3 || deltaSpecs > 1 || changedFiles > 8 ? "full" : "light";
|
|
13977
|
+
await setField2(new CommandOutput(), name, "verify_mode", result5);
|
|
12987
13978
|
output.stderr.push(
|
|
12988
13979
|
`=== Scale Assessment: ${name} ===`,
|
|
12989
13980
|
` Tasks: ${taskCount} (threshold: 3)`,
|
|
12990
13981
|
` Delta specs: ${deltaSpecs} capabilities (threshold: 1)`,
|
|
12991
13982
|
` Changed files: ${changedFiles} (threshold: 8)`,
|
|
12992
|
-
` → Result: ${
|
|
12993
|
-
green4(`[SCALE] verify_mode=${
|
|
13983
|
+
` → Result: ${result5}`,
|
|
13984
|
+
green4(`[SCALE] verify_mode=${result5}`)
|
|
12994
13985
|
);
|
|
12995
13986
|
}
|
|
12996
|
-
function
|
|
12997
|
-
|
|
13987
|
+
function parseRecordCheckOptions(args) {
|
|
13988
|
+
let command;
|
|
13989
|
+
let exitCodeText;
|
|
13990
|
+
let cwd;
|
|
13991
|
+
for (let index = 0; index < args.length; index += 2) {
|
|
13992
|
+
const option = args[index];
|
|
13993
|
+
if (!["--command", "--exit-code", "--cwd"].includes(option)) {
|
|
13994
|
+
fail2(`ERROR: Unknown option: ${option}`);
|
|
13995
|
+
}
|
|
13996
|
+
const value = args[index + 1];
|
|
13997
|
+
if (value === void 0) fail2(`ERROR: Missing value for option: ${option}`);
|
|
13998
|
+
if (option === "--command") command = value;
|
|
13999
|
+
else if (option === "--exit-code") exitCodeText = value;
|
|
14000
|
+
else cwd = value;
|
|
14001
|
+
}
|
|
14002
|
+
if (command === void 0) fail2("ERROR: Missing option: --command");
|
|
14003
|
+
if (exitCodeText === void 0) fail2("ERROR: Missing option: --exit-code");
|
|
14004
|
+
if (!/^-?\d+$/u.test(exitCodeText)) fail2("ERROR: --exit-code must be an integer");
|
|
14005
|
+
return { command, exitCode: Number(exitCodeText), ...cwd === void 0 ? {} : { cwd } };
|
|
14006
|
+
}
|
|
14007
|
+
async function recordCheck(output, name, scopeText, args) {
|
|
14008
|
+
validateChangeName4(name);
|
|
14009
|
+
if (scopeText !== "build" && scopeText !== "verify") {
|
|
14010
|
+
fail2(`ERROR: Invalid command check scope: '${scopeText}'`);
|
|
14011
|
+
}
|
|
14012
|
+
const options = parseRecordCheckOptions(args);
|
|
14013
|
+
const { label, directory, file } = await stateFile(name);
|
|
14014
|
+
if (label !== `openspec/changes/${name}` || !await exists6(file)) {
|
|
14015
|
+
fail2(`ERROR: command checks require an active change: ${name}`);
|
|
14016
|
+
}
|
|
14017
|
+
try {
|
|
14018
|
+
const projection = await readClassicState(directory, { migrate: false });
|
|
14019
|
+
if (!projection.classic || !projection.run) {
|
|
14020
|
+
throw new Error("command checks require an existing synchronized Classic Run");
|
|
14021
|
+
}
|
|
14022
|
+
const { run } = await validateClassicRuntimeRun(directory, projection);
|
|
14023
|
+
const recorded = await recordCommandCheck(directory, run, {
|
|
14024
|
+
scope: scopeText,
|
|
14025
|
+
...options
|
|
14026
|
+
});
|
|
14027
|
+
output.stderr.push(
|
|
14028
|
+
green4(
|
|
14029
|
+
`[RECORDED] ${recorded.scope} exit=${recorded.exitCode} cwd=${recorded.cwd} command=${recorded.command}`
|
|
14030
|
+
)
|
|
14031
|
+
);
|
|
14032
|
+
} catch (error) {
|
|
14033
|
+
fail2(`ERROR: ${error.message}`);
|
|
14034
|
+
}
|
|
14035
|
+
}
|
|
14036
|
+
function required(args, count, usage3) {
|
|
14037
|
+
if (args.length < count) fail2(usage3);
|
|
14038
|
+
}
|
|
14039
|
+
function requiredExact(args, count, usage3) {
|
|
14040
|
+
if (args.length !== count) fail2(usage3);
|
|
14041
|
+
}
|
|
14042
|
+
async function selectChange(output, name) {
|
|
14043
|
+
validateChangeName4(name);
|
|
14044
|
+
try {
|
|
14045
|
+
const selection = await selectCurrentChange(process.cwd(), name);
|
|
14046
|
+
output.stderr.push(
|
|
14047
|
+
green4(
|
|
14048
|
+
`[SELECTED] current change: ${selection.change}${selection.branch ? ` (branch: ${selection.branch})` : ""}`
|
|
14049
|
+
)
|
|
14050
|
+
);
|
|
14051
|
+
} catch (error) {
|
|
14052
|
+
fail2(`ERROR: ${error instanceof Error ? error.message : String(error)}`);
|
|
14053
|
+
}
|
|
14054
|
+
}
|
|
14055
|
+
async function currentChange(output) {
|
|
14056
|
+
const resolution = await resolveCurrentChange(process.cwd());
|
|
14057
|
+
if (resolution.status === "selected") {
|
|
14058
|
+
output.stdout.push(resolution.selection.change);
|
|
14059
|
+
return;
|
|
14060
|
+
}
|
|
14061
|
+
if (resolution.status === "missing") {
|
|
14062
|
+
fail2("ERROR: no current change selected\nUse: comet-state.mjs select <change-name>");
|
|
14063
|
+
}
|
|
14064
|
+
fail2(
|
|
14065
|
+
`ERROR: current change selection is stale: ${resolution.reason}
|
|
14066
|
+
Use: comet-state.mjs select <change-name>`
|
|
14067
|
+
);
|
|
14068
|
+
}
|
|
14069
|
+
async function clearSelection(output) {
|
|
14070
|
+
await clearCurrentChange(process.cwd());
|
|
14071
|
+
output.stderr.push(green4("[CLEARED] current change selection"));
|
|
12998
14072
|
}
|
|
12999
14073
|
var classicStateCommand = async (args) => {
|
|
13000
14074
|
const output = new CommandOutput();
|
|
@@ -13021,9 +14095,25 @@ var classicStateCommand = async (args) => {
|
|
|
13021
14095
|
} else if (subcommand === "scale") {
|
|
13022
14096
|
required(rest, 1, "Usage: comet-state.mjs scale <change-name>");
|
|
13023
14097
|
await scale(output, rest[0]);
|
|
14098
|
+
} else if (subcommand === "record-check") {
|
|
14099
|
+
required(
|
|
14100
|
+
rest,
|
|
14101
|
+
2,
|
|
14102
|
+
"Usage: comet state record-check <change> <build|verify> --command <text> --exit-code <int> [--cwd <path>]"
|
|
14103
|
+
);
|
|
14104
|
+
await recordCheck(output, rest[0], rest[1], rest.slice(2));
|
|
13024
14105
|
} else if (subcommand === "task-checkoff") {
|
|
13025
14106
|
required(rest, 2, "Usage: comet-state.mjs task-checkoff <file> <task-text>");
|
|
13026
14107
|
await taskCheckoff(output, rest[0], rest[1]);
|
|
14108
|
+
} else if (subcommand === "select") {
|
|
14109
|
+
requiredExact(rest, 1, "Usage: comet-state.mjs select <change-name>");
|
|
14110
|
+
await selectChange(output, rest[0]);
|
|
14111
|
+
} else if (subcommand === "current") {
|
|
14112
|
+
requiredExact(rest, 0, "Usage: comet-state.mjs current");
|
|
14113
|
+
await currentChange(output);
|
|
14114
|
+
} else if (subcommand === "clear-selection") {
|
|
14115
|
+
requiredExact(rest, 0, "Usage: comet-state.mjs clear-selection");
|
|
14116
|
+
await clearSelection(output);
|
|
13027
14117
|
} else if (subcommand === "next") {
|
|
13028
14118
|
required(rest, 1, "Usage: comet-state.mjs next <change-name>");
|
|
13029
14119
|
await next(output, rest[0]);
|
|
@@ -13048,7 +14138,8 @@ var CLASSIC_COMMANDS = [
|
|
|
13048
14138
|
"handoff",
|
|
13049
14139
|
"archive",
|
|
13050
14140
|
"hook-guard",
|
|
13051
|
-
"intent"
|
|
14141
|
+
"intent",
|
|
14142
|
+
"resume-probe"
|
|
13052
14143
|
];
|
|
13053
14144
|
var DEFAULT_HANDLERS = {
|
|
13054
14145
|
state: classicStateCommand,
|
|
@@ -13057,7 +14148,8 @@ var DEFAULT_HANDLERS = {
|
|
|
13057
14148
|
handoff: classicHandoffCommand,
|
|
13058
14149
|
archive: classicArchiveCommand,
|
|
13059
14150
|
"hook-guard": classicHookGuardCommand,
|
|
13060
|
-
intent: classicIntentCommand
|
|
14151
|
+
intent: classicIntentCommand,
|
|
14152
|
+
"resume-probe": classicResumeProbeCommand
|
|
13061
14153
|
};
|
|
13062
14154
|
function isClassicCommand(value) {
|
|
13063
14155
|
return CLASSIC_COMMANDS.includes(value);
|
|
@@ -13092,14 +14184,14 @@ async function dispatch(command, args, options, handlers) {
|
|
|
13092
14184
|
};
|
|
13093
14185
|
}
|
|
13094
14186
|
}
|
|
13095
|
-
function jsonResult(command,
|
|
14187
|
+
function jsonResult(command, result5) {
|
|
13096
14188
|
return {
|
|
13097
|
-
exitCode:
|
|
14189
|
+
exitCode: result5.exitCode,
|
|
13098
14190
|
stdout: JSON.stringify({
|
|
13099
14191
|
command: command ?? null,
|
|
13100
|
-
exitCode:
|
|
13101
|
-
...
|
|
13102
|
-
...
|
|
14192
|
+
exitCode: result5.exitCode,
|
|
14193
|
+
...result5.stdout === void 0 ? {} : { stdout: result5.stdout },
|
|
14194
|
+
...result5.stderr === void 0 ? {} : { stderr: result5.stderr }
|
|
13103
14195
|
}) + "\n"
|
|
13104
14196
|
};
|
|
13105
14197
|
}
|
|
@@ -13107,15 +14199,15 @@ async function runClassicCli(argv, handlers = DEFAULT_HANDLERS) {
|
|
|
13107
14199
|
const json = argv.includes("--json");
|
|
13108
14200
|
const args = argv.filter((argument) => argument !== "--json");
|
|
13109
14201
|
const command = args.shift();
|
|
13110
|
-
const
|
|
13111
|
-
return json ? jsonResult(command,
|
|
14202
|
+
const result5 = await dispatch(command, args, { json }, handlers);
|
|
14203
|
+
return json ? jsonResult(command, result5) : result5;
|
|
13112
14204
|
}
|
|
13113
14205
|
async function main(argv = process.argv.slice(2)) {
|
|
13114
|
-
const
|
|
13115
|
-
if (
|
|
13116
|
-
if (
|
|
13117
|
-
process.stderr.write(
|
|
13118
|
-
return
|
|
14206
|
+
const result5 = await runClassicCli(argv);
|
|
14207
|
+
if (result5.stdout) process.stdout.write(result5.stdout);
|
|
14208
|
+
if (result5.stderr)
|
|
14209
|
+
process.stderr.write(result5.stderr + (result5.stderr.endsWith("\n") ? "" : "\n"));
|
|
14210
|
+
return result5.exitCode;
|
|
13119
14211
|
}
|
|
13120
14212
|
var entry = process.argv[1];
|
|
13121
14213
|
if (entry && import.meta.url === pathToFileURL(entry).href) {
|