@rpamis/comet 0.4.0-beta.4 → 0.4.0-beta.6
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 +11 -4
- package/assets/manifest.json +1 -1
- package/assets/skills/comet/SKILL.md +30 -28
- package/assets/skills/comet/reference/auto-transition.md +1 -1
- package/assets/skills/comet/reference/comet-yaml-fields.md +8 -4
- package/assets/skills/comet/reference/context-recovery.md +2 -2
- package/assets/skills/comet/reference/decision-point.md +11 -0
- package/assets/skills/comet/reference/subagent-dispatch.md +6 -5
- package/assets/skills/comet/rules/comet-phase-guard.en.md +23 -28
- package/assets/skills/comet/rules/comet-phase-guard.md +22 -28
- package/assets/skills/comet/scripts/comet-runtime.mjs +682 -309
- 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 +33 -13
- package/assets/skills/comet-build/SKILL.md +37 -29
- package/assets/skills/comet-design/SKILL.md +23 -22
- package/assets/skills/comet-hotfix/SKILL.md +46 -31
- package/assets/skills/comet-open/SKILL.md +97 -58
- package/assets/skills/comet-tweak/SKILL.md +34 -19
- package/assets/skills/comet-verify/SKILL.md +37 -59
- package/assets/skills-zh/comet/SKILL.md +30 -28
- package/assets/skills-zh/comet/reference/auto-transition.md +1 -1
- package/assets/skills-zh/comet/reference/comet-yaml-fields.md +8 -4
- package/assets/skills-zh/comet/reference/context-recovery.md +2 -2
- package/assets/skills-zh/comet/reference/decision-point.md +11 -0
- 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 +33 -13
- package/assets/skills-zh/comet-build/SKILL.md +37 -29
- package/assets/skills-zh/comet-design/SKILL.md +23 -22
- package/assets/skills-zh/comet-hotfix/SKILL.md +45 -30
- package/assets/skills-zh/comet-open/SKILL.md +96 -57
- package/assets/skills-zh/comet-tweak/SKILL.md +33 -18
- package/assets/skills-zh/comet-verify/SKILL.md +37 -59
- package/dist/app/commands/doctor.d.ts.map +1 -1
- package/dist/app/commands/doctor.js +62 -15
- package/dist/app/commands/doctor.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 +58 -14
- package/dist/app/commands/init.js.map +1 -1
- package/dist/app/commands/status.d.ts +1 -0
- package/dist/app/commands/status.d.ts.map +1 -1
- package/dist/app/commands/status.js +10 -0
- package/dist/app/commands/status.js.map +1 -1
- package/dist/app/commands/uninstall.d.ts +3 -0
- package/dist/app/commands/uninstall.d.ts.map +1 -1
- package/dist/app/commands/uninstall.js +70 -36
- package/dist/app/commands/uninstall.js.map +1 -1
- package/dist/app/commands/update.d.ts.map +1 -1
- package/dist/app/commands/update.js +213 -34
- package/dist/app/commands/update.js.map +1 -1
- 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 +6 -2
- package/dist/domains/bundle/bundle-platform.js.map +1 -1
- package/dist/domains/comet-classic/classic-branch-binding.d.ts +51 -0
- package/dist/domains/comet-classic/classic-branch-binding.d.ts.map +1 -0
- package/dist/domains/comet-classic/classic-branch-binding.js +107 -0
- package/dist/domains/comet-classic/classic-branch-binding.js.map +1 -0
- package/dist/domains/comet-classic/classic-current-change.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-current-change.js +37 -20
- package/dist/domains/comet-classic/classic-current-change.js.map +1 -1
- package/dist/domains/comet-classic/classic-guard.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-guard.js +50 -16
- 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 +138 -21
- package/dist/domains/comet-classic/classic-hook-guard.js.map +1 -1
- package/dist/domains/comet-classic/classic-resolver.js +1 -1
- package/dist/domains/comet-classic/classic-resolver.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 +113 -14
- package/dist/domains/comet-classic/classic-state-command.js.map +1 -1
- package/dist/domains/comet-classic/classic-state-events.d.ts +1 -1
- package/dist/domains/comet-classic/classic-state-events.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-state.d.ts +4 -2
- package/dist/domains/comet-classic/classic-state.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-state.js +16 -1
- package/dist/domains/comet-classic/classic-state.js.map +1 -1
- package/dist/domains/comet-classic/classic-transitions.d.ts.map +1 -1
- package/dist/domains/comet-classic/classic-transitions.js +17 -2
- 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 +13 -1
- package/dist/domains/comet-classic/classic-validate-command.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/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 +56 -4
- 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 +7 -1
- 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/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 +16 -6
- package/dist/domains/skill/platform-install.d.ts.map +1 -1
- package/dist/domains/skill/platform-install.js +277 -144
- package/dist/domains/skill/platform-install.js.map +1 -1
- package/dist/domains/skill/uninstall.d.ts.map +1 -1
- package/dist/domains/skill/uninstall.js +204 -141
- 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/platforms.d.ts +4 -0
- package/dist/platform/install/platforms.d.ts.map +1 -1
- package/dist/platform/install/platforms.js +2 -0
- package/dist/platform/install/platforms.js.map +1 -1
- package/dist/platform/install/project-registry.d.ts +1 -0
- package/dist/platform/install/project-registry.d.ts.map +1 -1
- package/dist/platform/install/project-registry.js +9 -1
- package/dist/platform/install/project-registry.js.map +1 -1
- 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/package.json +6 -1
- package/dist/domains/dashboard/web/assets/index-CIvN-PL8.js +0 -22
- package/dist/domains/dashboard/web/assets/index-DcyRPBnT.css +0 -1
- package/eval/.env +0 -28
|
@@ -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, path23) {
|
|
130
|
+
const ctrl = callVisitor(key, node, visitor, path23);
|
|
131
131
|
if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
|
|
132
|
-
replaceNode(key,
|
|
133
|
-
return visit_(key, ctrl, visitor,
|
|
132
|
+
replaceNode(key, path23, ctrl);
|
|
133
|
+
return visit_(key, ctrl, visitor, path23);
|
|
134
134
|
}
|
|
135
135
|
if (typeof ctrl !== "symbol") {
|
|
136
136
|
if (identity.isCollection(node)) {
|
|
137
|
-
|
|
137
|
+
path23 = Object.freeze(path23.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, path23);
|
|
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
|
+
path23 = Object.freeze(path23.concat(node));
|
|
151
|
+
const ck = visit_("key", node.key, visitor, path23);
|
|
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, path23);
|
|
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, path23) {
|
|
178
|
+
const ctrl = await callVisitor(key, node, visitor, path23);
|
|
179
179
|
if (identity.isNode(ctrl) || identity.isPair(ctrl)) {
|
|
180
|
-
replaceNode(key,
|
|
181
|
-
return visitAsync_(key, ctrl, visitor,
|
|
180
|
+
replaceNode(key, path23, ctrl);
|
|
181
|
+
return visitAsync_(key, ctrl, visitor, path23);
|
|
182
182
|
}
|
|
183
183
|
if (typeof ctrl !== "symbol") {
|
|
184
184
|
if (identity.isCollection(node)) {
|
|
185
|
-
|
|
185
|
+
path23 = Object.freeze(path23.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, path23);
|
|
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
|
+
path23 = Object.freeze(path23.concat(node));
|
|
199
|
+
const ck = await visitAsync_("key", node.key, visitor, path23);
|
|
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, path23);
|
|
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, path23) {
|
|
232
232
|
if (typeof visitor === "function")
|
|
233
|
-
return visitor(key, node,
|
|
233
|
+
return visitor(key, node, path23);
|
|
234
234
|
if (identity.isMap(node))
|
|
235
|
-
return visitor.Map?.(key, node,
|
|
235
|
+
return visitor.Map?.(key, node, path23);
|
|
236
236
|
if (identity.isSeq(node))
|
|
237
|
-
return visitor.Seq?.(key, node,
|
|
237
|
+
return visitor.Seq?.(key, node, path23);
|
|
238
238
|
if (identity.isPair(node))
|
|
239
|
-
return visitor.Pair?.(key, node,
|
|
239
|
+
return visitor.Pair?.(key, node, path23);
|
|
240
240
|
if (identity.isScalar(node))
|
|
241
|
-
return visitor.Scalar?.(key, node,
|
|
241
|
+
return visitor.Scalar?.(key, node, path23);
|
|
242
242
|
if (identity.isAlias(node))
|
|
243
|
-
return visitor.Alias?.(key, node,
|
|
243
|
+
return visitor.Alias?.(key, node, path23);
|
|
244
244
|
return void 0;
|
|
245
245
|
}
|
|
246
|
-
function replaceNode(key,
|
|
247
|
-
const parent =
|
|
246
|
+
function replaceNode(key, path23, node) {
|
|
247
|
+
const parent = path23[path23.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, path23, value) {
|
|
858
858
|
let v = value;
|
|
859
|
-
for (let i =
|
|
860
|
-
const k =
|
|
859
|
+
for (let i = path23.length - 1; i >= 0; --i) {
|
|
860
|
+
const k = path23[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 = (path23) => path23 == null || typeof path23 === "object" && !!path23[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(path23, value) {
|
|
910
|
+
if (isEmptyPath(path23))
|
|
911
911
|
this.add(value);
|
|
912
912
|
else {
|
|
913
|
-
const [key, ...rest] =
|
|
913
|
+
const [key, ...rest] = path23;
|
|
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(path23) {
|
|
928
|
+
const [key, ...rest] = path23;
|
|
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(path23, keepScalar) {
|
|
943
|
+
const [key, ...rest] = path23;
|
|
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(path23) {
|
|
962
|
+
const [key, ...rest] = path23;
|
|
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(path23, value) {
|
|
973
|
+
const [key, ...rest] = path23;
|
|
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(path23, value) {
|
|
3489
3489
|
if (assertCollection(this.contents))
|
|
3490
|
-
this.contents.addIn(
|
|
3490
|
+
this.contents.addIn(path23, 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(path23) {
|
|
3566
|
+
if (Collection.isEmptyPath(path23)) {
|
|
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(path23) : 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(path23, keepScalar) {
|
|
3588
|
+
if (Collection.isEmptyPath(path23))
|
|
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(path23, 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(path23) {
|
|
3602
|
+
if (Collection.isEmptyPath(path23))
|
|
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(path23) : 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(path23, value) {
|
|
3622
|
+
if (Collection.isEmptyPath(path23)) {
|
|
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(path23), value);
|
|
3626
3626
|
} else if (assertCollection(this.contents)) {
|
|
3627
|
-
this.contents.setIn(
|
|
3627
|
+
this.contents.setIn(path23, 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, path23) => {
|
|
5588
5588
|
let item = cst;
|
|
5589
|
-
for (const [field2, index] of
|
|
5589
|
+
for (const [field2, index] of path23) {
|
|
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, path23) => {
|
|
5599
|
+
const parent = visit.itemAtPath(cst, path23.slice(0, -1));
|
|
5600
|
+
const field2 = path23[path23.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(path23, item, visitor) {
|
|
5607
|
+
let ctrl = visitor(item, path23);
|
|
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(path23.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, path23);
|
|
5626
5626
|
}
|
|
5627
5627
|
}
|
|
5628
|
-
return typeof ctrl === "function" ? ctrl(item,
|
|
5628
|
+
return typeof ctrl === "function" ? ctrl(item, path23) : 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 fs22 = this.flowScalar(this.type);
|
|
6931
6931
|
if (atNextItem || it.value) {
|
|
6932
|
-
map.items.push({ start, key:
|
|
6932
|
+
map.items.push({ start, key: fs22, sep: [] });
|
|
6933
6933
|
this.onKeyLine = true;
|
|
6934
6934
|
} else if (it.sep) {
|
|
6935
|
-
this.stack.push(
|
|
6935
|
+
this.stack.push(fs22);
|
|
6936
6936
|
} else {
|
|
6937
|
-
Object.assign(it, { key:
|
|
6937
|
+
Object.assign(it, { key: fs22, 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 fs22 = this.flowScalar(this.type);
|
|
7066
7066
|
if (!it || it.value)
|
|
7067
|
-
fc.items.push({ start: [], key:
|
|
7067
|
+
fc.items.push({ start: [], key: fs22, sep: [] });
|
|
7068
7068
|
else if (it.sep)
|
|
7069
|
-
this.stack.push(
|
|
7069
|
+
this.stack.push(fs22);
|
|
7070
7070
|
else
|
|
7071
|
-
Object.assign(it, { key:
|
|
7071
|
+
Object.assign(it, { key: fs22, 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 parseDocument8(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 = parseDocument8(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 = parseDocument8;
|
|
7323
7323
|
exports.stringify = stringify;
|
|
7324
7324
|
}
|
|
7325
7325
|
});
|
|
@@ -7739,7 +7739,7 @@ function fullBuildConfigured(classic) {
|
|
|
7739
7739
|
}
|
|
7740
7740
|
function presetBuildConfigured(classic) {
|
|
7741
7741
|
return Boolean(
|
|
7742
|
-
classic.buildMode === "direct" && classic.tddMode === "direct" && classic.isolation
|
|
7742
|
+
classic.buildMode === "direct" && classic.tddMode === "direct" && classic.isolation !== null && classic.verifyMode === "light"
|
|
7743
7743
|
);
|
|
7744
7744
|
}
|
|
7745
7745
|
function resolveBuild(profile, classic, evidence) {
|
|
@@ -7808,7 +7808,7 @@ var BUILD_PAUSES = ["plan-ready"];
|
|
|
7808
7808
|
var SUBAGENT_DISPATCH = ["confirmed"];
|
|
7809
7809
|
var TDD_MODES = ["tdd", "direct"];
|
|
7810
7810
|
var REVIEW_MODES = ["off", "standard", "thorough"];
|
|
7811
|
-
var ISOLATIONS = ["branch", "worktree"];
|
|
7811
|
+
var ISOLATIONS = ["current", "branch", "worktree"];
|
|
7812
7812
|
var VERIFY_MODES = ["light", "full"];
|
|
7813
7813
|
var VERIFY_RESULTS = ["pending", "pass", "fail"];
|
|
7814
7814
|
var BRANCH_STATUSES = ["pending", "handled"];
|
|
@@ -7824,12 +7824,14 @@ var CLASSIC_WIRE_KEYS = [
|
|
|
7824
7824
|
"tdd_mode",
|
|
7825
7825
|
"review_mode",
|
|
7826
7826
|
"isolation",
|
|
7827
|
+
"bound_branch",
|
|
7827
7828
|
"verify_mode",
|
|
7828
7829
|
"auto_transition",
|
|
7829
7830
|
"base_ref",
|
|
7830
7831
|
"design_doc",
|
|
7831
7832
|
"plan",
|
|
7832
7833
|
"verify_result",
|
|
7834
|
+
"verify_failures",
|
|
7833
7835
|
"verification_report",
|
|
7834
7836
|
"branch_status",
|
|
7835
7837
|
"created_at",
|
|
@@ -7891,6 +7893,14 @@ function booleanValue(doc, key, nullable = true) {
|
|
|
7891
7893
|
}
|
|
7892
7894
|
return value;
|
|
7893
7895
|
}
|
|
7896
|
+
function nonNegativeInteger(doc, key, fallback = 0) {
|
|
7897
|
+
const value = doc[key];
|
|
7898
|
+
if (value === null || value === void 0 || value === "") return fallback;
|
|
7899
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
|
|
7900
|
+
throw new Error(`Invalid Classic state: ${key} must be a non-negative integer`);
|
|
7901
|
+
}
|
|
7902
|
+
return value;
|
|
7903
|
+
}
|
|
7894
7904
|
function relativePath(doc, key) {
|
|
7895
7905
|
const value = nullableString(doc, key);
|
|
7896
7906
|
if (value === null) return null;
|
|
@@ -7933,12 +7943,14 @@ function classicStateFromDocument(doc) {
|
|
|
7933
7943
|
tddMode: enumValue(doc, "tdd_mode", TDD_MODES),
|
|
7934
7944
|
reviewMode: enumValue(doc, "review_mode", REVIEW_MODES),
|
|
7935
7945
|
isolation: enumValue(doc, "isolation", ISOLATIONS),
|
|
7946
|
+
boundBranch: nullableString(doc, "bound_branch"),
|
|
7936
7947
|
verifyMode: enumValue(doc, "verify_mode", VERIFY_MODES),
|
|
7937
7948
|
autoTransition: booleanValue(doc, "auto_transition"),
|
|
7938
7949
|
baseRef: nullableString(doc, "base_ref"),
|
|
7939
7950
|
designDoc: relativePath(doc, "design_doc"),
|
|
7940
7951
|
plan: relativePath(doc, "plan"),
|
|
7941
7952
|
verifyResult: enumValue(doc, "verify_result", VERIFY_RESULTS, false),
|
|
7953
|
+
verifyFailures: nonNegativeInteger(doc, "verify_failures"),
|
|
7942
7954
|
verificationReport: relativePath(doc, "verification_report"),
|
|
7943
7955
|
branchStatus: enumValue(doc, "branch_status", BRANCH_STATUSES),
|
|
7944
7956
|
createdAt: nullableString(doc, "created_at"),
|
|
@@ -7997,12 +8009,14 @@ function classicStateToDocument(state) {
|
|
|
7997
8009
|
tdd_mode: state.tddMode,
|
|
7998
8010
|
review_mode: state.reviewMode,
|
|
7999
8011
|
isolation: state.isolation,
|
|
8012
|
+
bound_branch: state.boundBranch,
|
|
8000
8013
|
verify_mode: state.verifyMode,
|
|
8001
8014
|
auto_transition: state.autoTransition,
|
|
8002
8015
|
base_ref: state.baseRef,
|
|
8003
8016
|
design_doc: state.designDoc,
|
|
8004
8017
|
plan: state.plan,
|
|
8005
8018
|
verify_result: state.verifyResult,
|
|
8019
|
+
verify_failures: state.verifyFailures,
|
|
8006
8020
|
verification_report: state.verificationReport,
|
|
8007
8021
|
branch_status: state.branchStatus,
|
|
8008
8022
|
created_at: state.createdAt,
|
|
@@ -9390,7 +9404,7 @@ var CLASSIC_TRANSITION_TABLE = {
|
|
|
9390
9404
|
"verify-pass": {
|
|
9391
9405
|
event: "verify-pass",
|
|
9392
9406
|
from: "verify",
|
|
9393
|
-
guardRefs: ["verification-report-present"
|
|
9407
|
+
guardRefs: ["verification-report-present"]
|
|
9394
9408
|
},
|
|
9395
9409
|
"verify-fail": {
|
|
9396
9410
|
event: "verify-fail",
|
|
@@ -9450,18 +9464,22 @@ function applyClassicTransition(current, event, options = {}) {
|
|
|
9450
9464
|
const preserveEvidence = classic.verifyResult === "fail";
|
|
9451
9465
|
setField(classic, effects, "phase", "verify");
|
|
9452
9466
|
setField(classic, effects, "verifyResult", "pending");
|
|
9467
|
+
setField(classic, effects, "branchStatus", "pending");
|
|
9453
9468
|
if (!preserveEvidence) {
|
|
9454
9469
|
setField(classic, effects, "verificationReport", null);
|
|
9455
|
-
setField(classic, effects, "branchStatus", "pending");
|
|
9456
9470
|
}
|
|
9457
9471
|
} else if (event === "verify-pass") {
|
|
9458
9472
|
setField(classic, effects, "verifyResult", "pass");
|
|
9473
|
+
setField(classic, effects, "verifyFailures", 0);
|
|
9459
9474
|
setField(classic, effects, "phase", "archive");
|
|
9460
9475
|
setField(classic, effects, "verifiedAt", dateOnly(now));
|
|
9461
9476
|
setField(classic, effects, "archiveConfirmation", "pending");
|
|
9477
|
+
setField(classic, effects, "branchStatus", "pending");
|
|
9462
9478
|
} else if (event === "verify-fail") {
|
|
9463
9479
|
setField(classic, effects, "verifyResult", "fail");
|
|
9480
|
+
setField(classic, effects, "verifyFailures", classic.verifyFailures + 1);
|
|
9464
9481
|
setField(classic, effects, "phase", "build");
|
|
9482
|
+
setField(classic, effects, "branchStatus", "pending");
|
|
9465
9483
|
} else if (event === "preset-escalate") {
|
|
9466
9484
|
if (classic.workflow !== "hotfix" && classic.workflow !== "tweak") {
|
|
9467
9485
|
throw new Error(
|
|
@@ -9472,6 +9490,15 @@ function applyClassicTransition(current, event, options = {}) {
|
|
|
9472
9490
|
setField(classic, effects, "classicProfile", "full");
|
|
9473
9491
|
setField(classic, effects, "phase", "design");
|
|
9474
9492
|
setField(classic, effects, "designDoc", null);
|
|
9493
|
+
setField(classic, effects, "buildPause", null);
|
|
9494
|
+
setField(classic, effects, "buildMode", null);
|
|
9495
|
+
setField(classic, effects, "subagentDispatch", null);
|
|
9496
|
+
setField(classic, effects, "tddMode", null);
|
|
9497
|
+
setField(classic, effects, "reviewMode", null);
|
|
9498
|
+
setField(classic, effects, "isolation", null);
|
|
9499
|
+
setField(classic, effects, "boundBranch", null);
|
|
9500
|
+
setField(classic, effects, "verifyMode", null);
|
|
9501
|
+
setField(classic, effects, "directOverride", null);
|
|
9475
9502
|
} else if (event === "archive-confirm") {
|
|
9476
9503
|
if (classic.verifyResult !== "pass") {
|
|
9477
9504
|
throw new Error(`Cannot apply ${event}: verifyResult must be pass`);
|
|
@@ -9481,9 +9508,11 @@ function applyClassicTransition(current, event, options = {}) {
|
|
|
9481
9508
|
} else if (event === "archive-reopen") {
|
|
9482
9509
|
if (classic.archived) throw new Error(`Cannot apply ${event}: already archived`);
|
|
9483
9510
|
setField(classic, effects, "verifyResult", "pending");
|
|
9511
|
+
setField(classic, effects, "verifyFailures", 0);
|
|
9484
9512
|
setField(classic, effects, "phase", "verify");
|
|
9485
9513
|
setField(classic, effects, "verifiedAt", null);
|
|
9486
9514
|
setField(classic, effects, "archiveConfirmation", null);
|
|
9515
|
+
setField(classic, effects, "branchStatus", "pending");
|
|
9487
9516
|
} else {
|
|
9488
9517
|
if (classic.verifyResult !== "pass") {
|
|
9489
9518
|
throw new Error(`Cannot apply ${event}: verifyResult must be pass`);
|
|
@@ -9882,11 +9911,11 @@ var classicArchiveCommand = async (args) => {
|
|
|
9882
9911
|
};
|
|
9883
9912
|
|
|
9884
9913
|
// domains/comet-classic/classic-guard.ts
|
|
9885
|
-
var
|
|
9914
|
+
var import_yaml6 = __toESM(require_dist(), 1);
|
|
9886
9915
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
9887
9916
|
import { createHash as createHash4 } from "crypto";
|
|
9888
|
-
import { existsSync, promises as
|
|
9889
|
-
import
|
|
9917
|
+
import { existsSync, promises as fs16, readFileSync } from "fs";
|
|
9918
|
+
import path17 from "path";
|
|
9890
9919
|
|
|
9891
9920
|
// domains/comet-classic/classic-command-checks.ts
|
|
9892
9921
|
import path13 from "path";
|
|
@@ -10095,7 +10124,7 @@ var ENUMS = {
|
|
|
10095
10124
|
subagent_dispatch: ["confirmed"],
|
|
10096
10125
|
tdd_mode: ["tdd", "direct"],
|
|
10097
10126
|
review_mode: ["off", "standard", "thorough"],
|
|
10098
|
-
isolation: ["branch", "worktree"],
|
|
10127
|
+
isolation: ["current", "branch", "worktree"],
|
|
10099
10128
|
verify_mode: ["light", "full"],
|
|
10100
10129
|
auto_transition: ["true", "false"],
|
|
10101
10130
|
verify_result: ["pending", "pass", "fail"],
|
|
@@ -10188,6 +10217,18 @@ var classicValidateCommand = async (args) => {
|
|
|
10188
10217
|
fail3(`${field2}='${value}' is not valid. Expected: ${values.join(" ")}`);
|
|
10189
10218
|
}
|
|
10190
10219
|
}
|
|
10220
|
+
if (Object.prototype.hasOwnProperty.call(record, "bound_branch")) {
|
|
10221
|
+
const value = record.bound_branch;
|
|
10222
|
+
if (value !== null && typeof value !== "string") {
|
|
10223
|
+
fail3(`bound_branch='${text(value)}' is not a string or null`);
|
|
10224
|
+
}
|
|
10225
|
+
}
|
|
10226
|
+
if (Object.prototype.hasOwnProperty.call(record, "verify_failures")) {
|
|
10227
|
+
const value = record.verify_failures;
|
|
10228
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
|
|
10229
|
+
fail3(`verify_failures='${text(value)}' is not a non-negative integer`);
|
|
10230
|
+
}
|
|
10231
|
+
}
|
|
10191
10232
|
for (const field2 of ["design_doc", "plan", "handoff_context"]) {
|
|
10192
10233
|
const value = text(record[field2]);
|
|
10193
10234
|
if (value && !await exists3(path14.resolve(value))) {
|
|
@@ -10224,8 +10265,9 @@ async function fileExists3(filePath) {
|
|
|
10224
10265
|
try {
|
|
10225
10266
|
await fs13.access(filePath);
|
|
10226
10267
|
return true;
|
|
10227
|
-
} catch {
|
|
10228
|
-
return false;
|
|
10268
|
+
} catch (error) {
|
|
10269
|
+
if (isNotFoundError(error)) return false;
|
|
10270
|
+
throw error;
|
|
10229
10271
|
}
|
|
10230
10272
|
}
|
|
10231
10273
|
async function readDir(dirPath) {
|
|
@@ -10239,6 +10281,9 @@ async function readDir(dirPath) {
|
|
|
10239
10281
|
throw error;
|
|
10240
10282
|
}
|
|
10241
10283
|
}
|
|
10284
|
+
function isNotFoundError(error) {
|
|
10285
|
+
return error?.code === "ENOENT";
|
|
10286
|
+
}
|
|
10242
10287
|
|
|
10243
10288
|
// domains/comet-classic/classic-project-config.ts
|
|
10244
10289
|
function configCandidates(options = {}) {
|
|
@@ -10268,6 +10313,96 @@ async function readClassicConfigValue(field2, options = {}) {
|
|
|
10268
10313
|
return null;
|
|
10269
10314
|
}
|
|
10270
10315
|
|
|
10316
|
+
// domains/comet-classic/classic-branch-binding.ts
|
|
10317
|
+
var import_yaml5 = __toESM(require_dist(), 1);
|
|
10318
|
+
import { execFileSync } from "child_process";
|
|
10319
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
10320
|
+
import { promises as fs15 } from "fs";
|
|
10321
|
+
import path16 from "path";
|
|
10322
|
+
function liveGitBranch(cwd) {
|
|
10323
|
+
try {
|
|
10324
|
+
const branch = execFileSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
10325
|
+
cwd,
|
|
10326
|
+
encoding: "utf8",
|
|
10327
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
10328
|
+
}).trim();
|
|
10329
|
+
return branch && branch !== "HEAD" ? branch : null;
|
|
10330
|
+
} catch {
|
|
10331
|
+
return null;
|
|
10332
|
+
}
|
|
10333
|
+
}
|
|
10334
|
+
function isGitWorkTree(cwd) {
|
|
10335
|
+
try {
|
|
10336
|
+
return execFileSync("git", ["rev-parse", "--is-inside-work-tree"], {
|
|
10337
|
+
cwd,
|
|
10338
|
+
encoding: "utf8",
|
|
10339
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
10340
|
+
}).trim() === "true";
|
|
10341
|
+
} catch {
|
|
10342
|
+
return false;
|
|
10343
|
+
}
|
|
10344
|
+
}
|
|
10345
|
+
var BOUND_BRANCH_ISOLATIONS = ["current", "branch", "worktree"];
|
|
10346
|
+
function requiresBranchBinding(isolation) {
|
|
10347
|
+
return BOUND_BRANCH_ISOLATIONS.includes(isolation);
|
|
10348
|
+
}
|
|
10349
|
+
function evaluateBranchBinding(input) {
|
|
10350
|
+
if (!requiresBranchBinding(input.isolation)) return { status: "not-applicable" };
|
|
10351
|
+
if (input.boundBranch === null && input.currentBranch === null && input.gitWorkTree === false) {
|
|
10352
|
+
return { status: "not-applicable" };
|
|
10353
|
+
}
|
|
10354
|
+
if (input.boundBranch === null) {
|
|
10355
|
+
return input.currentBranch === null ? { status: "unbound-detached" } : { status: "needs-heal", branch: input.currentBranch };
|
|
10356
|
+
}
|
|
10357
|
+
if (input.currentBranch === input.boundBranch) return { status: "ok" };
|
|
10358
|
+
return { status: "drift", boundBranch: input.boundBranch, currentBranch: input.currentBranch };
|
|
10359
|
+
}
|
|
10360
|
+
async function resolveBranchBinding(changeDir, options) {
|
|
10361
|
+
const file = path16.join(changeDir, ".comet.yaml");
|
|
10362
|
+
const document = (0, import_yaml5.parseDocument)(await fs15.readFile(file, "utf8"), { uniqueKeys: false });
|
|
10363
|
+
if (document.errors.length > 0) {
|
|
10364
|
+
throw new Error(`Invalid .comet.yaml: ${document.errors[0].message}`);
|
|
10365
|
+
}
|
|
10366
|
+
const record = document.toJS() ?? {};
|
|
10367
|
+
const isolation = typeof record.isolation === "string" ? record.isolation : null;
|
|
10368
|
+
const boundBranch = typeof record.bound_branch === "string" && record.bound_branch !== "" ? record.bound_branch : null;
|
|
10369
|
+
const bindingRequired = requiresBranchBinding(isolation);
|
|
10370
|
+
const currentBranch = liveGitBranch(options.cwd);
|
|
10371
|
+
const gitWorkTree = bindingRequired && boundBranch === null && currentBranch === null ? isGitWorkTree(options.cwd) : true;
|
|
10372
|
+
const verdict = evaluateBranchBinding({ isolation, boundBranch, currentBranch, gitWorkTree });
|
|
10373
|
+
if (verdict.status === "needs-heal" && options.heal) {
|
|
10374
|
+
await healBoundBranch(changeDir, verdict.branch);
|
|
10375
|
+
return { status: "healed", branch: verdict.branch, bindingRequired, currentBranch };
|
|
10376
|
+
}
|
|
10377
|
+
return { ...verdict, bindingRequired, currentBranch };
|
|
10378
|
+
}
|
|
10379
|
+
async function healBoundBranch(changeDir, branch) {
|
|
10380
|
+
const file = path16.join(changeDir, ".comet.yaml");
|
|
10381
|
+
const document = (0, import_yaml5.parseDocument)(await fs15.readFile(file, "utf8"), { uniqueKeys: false });
|
|
10382
|
+
document.set("bound_branch", branch);
|
|
10383
|
+
const temporary = `${file}.${randomUUID6()}.tmp`;
|
|
10384
|
+
try {
|
|
10385
|
+
await fs15.writeFile(temporary, document.toString(), "utf8");
|
|
10386
|
+
await fs15.rename(temporary, file);
|
|
10387
|
+
} catch (error) {
|
|
10388
|
+
await fs15.rm(temporary, { force: true });
|
|
10389
|
+
throw error;
|
|
10390
|
+
}
|
|
10391
|
+
}
|
|
10392
|
+
function branchLabel(currentBranch) {
|
|
10393
|
+
return currentBranch ?? "detached HEAD";
|
|
10394
|
+
}
|
|
10395
|
+
function driftBlockedMessage(change, boundBranch, currentBranch) {
|
|
10396
|
+
return `change '${change}' is bound to branch '${boundBranch}', but current branch is '${branchLabel(currentBranch)}'.
|
|
10397
|
+
Next: ask the user to confirm — switch back to '${boundBranch}', or run \`comet state rebind ${change}\` after explicit confirmation.`;
|
|
10398
|
+
}
|
|
10399
|
+
function driftStaleReason(change, boundBranch, currentBranch) {
|
|
10400
|
+
return `change '${change}' is bound to branch '${boundBranch}', but current branch is '${branchLabel(currentBranch)}'`;
|
|
10401
|
+
}
|
|
10402
|
+
function unboundDetachedMessage(change) {
|
|
10403
|
+
return `change '${change}' uses a branch-bound workspace mode but has no bound branch and HEAD is detached; checkout a branch first before continuing.`;
|
|
10404
|
+
}
|
|
10405
|
+
|
|
10271
10406
|
// domains/comet-classic/classic-guard.ts
|
|
10272
10407
|
var GREEN3 = "\x1B[32m";
|
|
10273
10408
|
var RED3 = "\x1B[31m";
|
|
@@ -10332,7 +10467,7 @@ var GuardOutput = class {
|
|
|
10332
10467
|
};
|
|
10333
10468
|
async function exists4(file) {
|
|
10334
10469
|
try {
|
|
10335
|
-
await
|
|
10470
|
+
await fs16.access(file);
|
|
10336
10471
|
return true;
|
|
10337
10472
|
} catch (error) {
|
|
10338
10473
|
if (error.code === "ENOENT") return false;
|
|
@@ -10341,7 +10476,7 @@ async function exists4(file) {
|
|
|
10341
10476
|
}
|
|
10342
10477
|
async function nonempty(file) {
|
|
10343
10478
|
try {
|
|
10344
|
-
return (await
|
|
10479
|
+
return (await fs16.stat(file)).size > 0;
|
|
10345
10480
|
} catch (error) {
|
|
10346
10481
|
if (error.code === "ENOENT") return false;
|
|
10347
10482
|
throw error;
|
|
@@ -10355,8 +10490,8 @@ async function resolveChangeDir(name) {
|
|
|
10355
10490
|
return (await resolveClassicChangeDirectory(name)).label;
|
|
10356
10491
|
}
|
|
10357
10492
|
async function readField(changeDir, field2) {
|
|
10358
|
-
const file =
|
|
10359
|
-
const document = (0,
|
|
10493
|
+
const file = path17.join(changeDir, ".comet.yaml");
|
|
10494
|
+
const document = (0, import_yaml6.parseDocument)(await fs16.readFile(file, "utf8"), { uniqueKeys: false });
|
|
10360
10495
|
if (document.errors.length > 0) {
|
|
10361
10496
|
throw new GuardFailure(`ERROR: Invalid .comet.yaml: ${document.errors[0].message}`);
|
|
10362
10497
|
}
|
|
@@ -10397,7 +10532,7 @@ function countEnglishWords(source) {
|
|
|
10397
10532
|
}
|
|
10398
10533
|
async function documentLanguageMatchesConfigured(changeDir, file) {
|
|
10399
10534
|
const language = await configuredLanguage(changeDir);
|
|
10400
|
-
const source = stripFencedCodeBlocks(await
|
|
10535
|
+
const source = stripFencedCodeBlocks(await fs16.readFile(file, "utf8"));
|
|
10401
10536
|
const cjk = countCjkChars(source);
|
|
10402
10537
|
const englishWords = countEnglishWords(source);
|
|
10403
10538
|
if (language === "zh-CN" && cjk < 20 && englishWords >= 20) {
|
|
@@ -10421,7 +10556,7 @@ async function handoffSourceFiles(changeDir) {
|
|
|
10421
10556
|
const files = [`${changeDir}/proposal.md`, `${changeDir}/design.md`, `${changeDir}/tasks.md`];
|
|
10422
10557
|
const specs = `${changeDir}/specs`;
|
|
10423
10558
|
if (await exists4(specs)) {
|
|
10424
|
-
for (const entry2 of (await
|
|
10559
|
+
for (const entry2 of (await fs16.readdir(specs)).sort()) {
|
|
10425
10560
|
const spec = `${specs}/${entry2}/spec.md`;
|
|
10426
10561
|
if (await exists4(spec)) files.push(spec);
|
|
10427
10562
|
}
|
|
@@ -10441,7 +10576,7 @@ async function preflight(changeDir, name) {
|
|
|
10441
10576
|
if (!await exists4(changeDir)) {
|
|
10442
10577
|
throw new GuardFailure(red2(`FATAL: change directory not found: ${changeDir}`));
|
|
10443
10578
|
}
|
|
10444
|
-
if (!await exists4(
|
|
10579
|
+
if (!await exists4(path17.join(changeDir, ".comet.yaml"))) {
|
|
10445
10580
|
throw new GuardFailure(red2(`FATAL: .comet.yaml not found in ${changeDir}`));
|
|
10446
10581
|
}
|
|
10447
10582
|
const result5 = await classicValidateCommand([name], { json: false });
|
|
@@ -10511,9 +10646,9 @@ var INFERRED_COMMAND_SOURCES = [
|
|
|
10511
10646
|
"Cargo.toml"
|
|
10512
10647
|
];
|
|
10513
10648
|
async function removedProjectCommandField(field2) {
|
|
10514
|
-
const config =
|
|
10649
|
+
const config = path17.join(".comet", "config.yaml");
|
|
10515
10650
|
if (!await exists4(config)) return false;
|
|
10516
|
-
const document = (0,
|
|
10651
|
+
const document = (0, import_yaml6.parseDocument)(await fs16.readFile(config, "utf8"));
|
|
10517
10652
|
if (document.errors.length > 0) {
|
|
10518
10653
|
throw new Error(
|
|
10519
10654
|
`.comet/config.yaml is invalid YAML (${document.errors[0].message}); cannot check for removed "${field2}" field. Fix the config and retry.`
|
|
@@ -10594,14 +10729,14 @@ ${recoveryCommand(change, scope, recorded.command)}`
|
|
|
10594
10729
|
return { status: 0, output: evidenceDetail(recorded) };
|
|
10595
10730
|
}
|
|
10596
10731
|
async function tasksAllDone(changeDir) {
|
|
10597
|
-
const tasks =
|
|
10732
|
+
const tasks = path17.join(changeDir, "tasks.md");
|
|
10598
10733
|
if (!await exists4(tasks)) {
|
|
10599
10734
|
return fail(
|
|
10600
10735
|
`tasks.md is missing at ${tasks}
|
|
10601
10736
|
Next: restore or create tasks.md for this change before leaving build.`
|
|
10602
10737
|
);
|
|
10603
10738
|
}
|
|
10604
|
-
const source = await
|
|
10739
|
+
const source = await fs16.readFile(tasks, "utf8");
|
|
10605
10740
|
if (!/- \[x\]/u.test(source)) {
|
|
10606
10741
|
return fail(
|
|
10607
10742
|
"tasks.md has no completed tasks.\nNext: complete implementation tasks and mark them with '- [x]'."
|
|
@@ -10618,9 +10753,9 @@ Next: complete or explicitly remove unfinished tasks, then mark tasks.md with '-
|
|
|
10618
10753
|
return pass();
|
|
10619
10754
|
}
|
|
10620
10755
|
async function tasksHasAny(changeDir) {
|
|
10621
|
-
const tasks =
|
|
10756
|
+
const tasks = path17.join(changeDir, "tasks.md");
|
|
10622
10757
|
if (!await exists4(tasks)) return false;
|
|
10623
|
-
return /- \[/u.test(await
|
|
10758
|
+
return /- \[/u.test(await fs16.readFile(tasks, "utf8"));
|
|
10624
10759
|
}
|
|
10625
10760
|
async function planTasksAllDone(changeDir) {
|
|
10626
10761
|
const plan = await readField(changeDir, "plan");
|
|
@@ -10631,7 +10766,7 @@ async function planTasksAllDone(changeDir) {
|
|
|
10631
10766
|
Next: restore the Superpowers plan file or update .comet.yaml plan before leaving build.`
|
|
10632
10767
|
);
|
|
10633
10768
|
}
|
|
10634
|
-
const source = await
|
|
10769
|
+
const source = await fs16.readFile(plan, "utf8");
|
|
10635
10770
|
const unfinished = source.split(/\r?\n/u).map((line, index) => ({ line, number: index + 1 })).filter((entry2) => /^\s*- \[ \]/u.test(entry2.line));
|
|
10636
10771
|
if (unfinished.length > 0) {
|
|
10637
10772
|
return fail(
|
|
@@ -10642,13 +10777,38 @@ Next: check off corresponding completed plan tasks, then commit the plan update.
|
|
|
10642
10777
|
}
|
|
10643
10778
|
return pass();
|
|
10644
10779
|
}
|
|
10780
|
+
async function boundBranchMatches(changeDir, change) {
|
|
10781
|
+
let outcome;
|
|
10782
|
+
try {
|
|
10783
|
+
outcome = await resolveBranchBinding(changeDir, { heal: true, cwd: process.cwd() });
|
|
10784
|
+
} catch (error) {
|
|
10785
|
+
throw new GuardFailure(`ERROR: ${error instanceof Error ? error.message : String(error)}`);
|
|
10786
|
+
}
|
|
10787
|
+
switch (outcome.status) {
|
|
10788
|
+
case "drift":
|
|
10789
|
+
return fail(driftBlockedMessage(change, outcome.boundBranch, outcome.currentBranch));
|
|
10790
|
+
case "unbound-detached":
|
|
10791
|
+
return fail(unboundDetachedMessage(change));
|
|
10792
|
+
case "healed":
|
|
10793
|
+
return pass(`bound_branch lazily set to ${outcome.branch}`);
|
|
10794
|
+
case "needs-heal":
|
|
10795
|
+
case "ok":
|
|
10796
|
+
case "not-applicable":
|
|
10797
|
+
return pass();
|
|
10798
|
+
default: {
|
|
10799
|
+
const exhaustive = outcome;
|
|
10800
|
+
throw new Error(`unhandled branch binding status: ${JSON.stringify(exhaustive)}`);
|
|
10801
|
+
}
|
|
10802
|
+
}
|
|
10803
|
+
}
|
|
10645
10804
|
async function isolationSelected(changeDir, change) {
|
|
10646
10805
|
const isolation = await readField(changeDir, "isolation");
|
|
10647
|
-
if (isolation === "branch" || isolation === "worktree") return pass();
|
|
10806
|
+
if (isolation === "current" || isolation === "branch" || isolation === "worktree") return pass();
|
|
10807
|
+
const allowedValues = "<current|branch|worktree>";
|
|
10648
10808
|
return fail(
|
|
10649
|
-
`isolation must be branch or worktree, got '${isolation || "null"}'
|
|
10650
|
-
Next:
|
|
10651
|
-
|
|
10809
|
+
`isolation must be current, branch, or worktree, got '${isolation || "null"}'
|
|
10810
|
+
Next: choose a valid workspace mode, prepare it when needed, then run:
|
|
10811
|
+
comet state set ${change} isolation ${allowedValues}`
|
|
10652
10812
|
);
|
|
10653
10813
|
}
|
|
10654
10814
|
async function buildModeSelected(changeDir, change) {
|
|
@@ -10658,7 +10818,7 @@ async function buildModeSelected(changeDir, change) {
|
|
|
10658
10818
|
return fail(
|
|
10659
10819
|
`build_mode must be selected before leaving build, got '${buildMode || "null"}'
|
|
10660
10820
|
Next: ask the user to choose an execution mode, then run:
|
|
10661
|
-
|
|
10821
|
+
comet state set ${change} build_mode <subagent-driven-development|executing-plans>`
|
|
10662
10822
|
);
|
|
10663
10823
|
}
|
|
10664
10824
|
async function buildModeAllowedForWorkflow(changeDir) {
|
|
@@ -10680,9 +10840,9 @@ async function subagentDispatchConfirmed(changeDir, change) {
|
|
|
10680
10840
|
return fail(
|
|
10681
10841
|
`subagent_dispatch must be confirmed before using build_mode=subagent-driven-development
|
|
10682
10842
|
Next: confirm the current platform has a real background subagent/Task/multi-agent dispatcher, then run:
|
|
10683
|
-
|
|
10684
|
-
|
|
10685
|
-
|
|
10843
|
+
comet state set ${change} subagent_dispatch confirmed
|
|
10844
|
+
If dispatch is unavailable, return to /comet-build Step 2 with subagent-driven-development removed. When executing-plans is the only valid mode, run:
|
|
10845
|
+
comet state set ${change} build_mode executing-plans`
|
|
10686
10846
|
);
|
|
10687
10847
|
}
|
|
10688
10848
|
async function tddModeSelected(changeDir, change) {
|
|
@@ -10693,7 +10853,7 @@ async function tddModeSelected(changeDir, change) {
|
|
|
10693
10853
|
return fail(
|
|
10694
10854
|
`tdd_mode must be tdd or direct for full workflow, got '${tddMode || "null"}'
|
|
10695
10855
|
Next: ask the user to choose TDD enforcement level, then run:
|
|
10696
|
-
|
|
10856
|
+
comet state set ${change} tdd_mode <tdd|direct>`
|
|
10697
10857
|
);
|
|
10698
10858
|
}
|
|
10699
10859
|
async function reviewModeSelected(changeDir, change) {
|
|
@@ -10706,7 +10866,7 @@ async function reviewModeSelected(changeDir, change) {
|
|
|
10706
10866
|
return fail(
|
|
10707
10867
|
`review_mode must be off, standard, or thorough before leaving build, got '${reviewMode || "null"}'
|
|
10708
10868
|
Next: ask the user to choose review strength, then run:
|
|
10709
|
-
|
|
10869
|
+
comet state set ${change} review_mode <off|standard|thorough>`
|
|
10710
10870
|
);
|
|
10711
10871
|
}
|
|
10712
10872
|
async function verificationReportExists(changeDir) {
|
|
@@ -10720,7 +10880,7 @@ async function archivedIsTrue(changeDir) {
|
|
|
10720
10880
|
return await readField(changeDir, "archived") === "true";
|
|
10721
10881
|
}
|
|
10722
10882
|
async function designDocFrontmatterHas(designDoc, field2, expected) {
|
|
10723
|
-
const source = (await
|
|
10883
|
+
const source = (await fs16.readFile(designDoc, "utf8")).replace(/^\uFEFF/u, "");
|
|
10724
10884
|
let inFrontmatter = false;
|
|
10725
10885
|
for (const line of source.split(/\r?\n/u)) {
|
|
10726
10886
|
if (!inFrontmatter) {
|
|
@@ -10737,7 +10897,7 @@ async function designDocRecorded(changeDir, change) {
|
|
|
10737
10897
|
if (designDoc && designDoc !== "null" && existsSync(designDoc)) return pass();
|
|
10738
10898
|
return fail(
|
|
10739
10899
|
`design_doc must point to an existing Superpowers Design Doc for full workflow before leaving design.
|
|
10740
|
-
Next: create the Design Doc and run:
|
|
10900
|
+
Next: create the Design Doc and run: comet state set ${change} design_doc <path>`
|
|
10741
10901
|
);
|
|
10742
10902
|
}
|
|
10743
10903
|
async function designHandoffContextValid(changeDir, change) {
|
|
@@ -10752,13 +10912,13 @@ Next: run node "$COMET_HANDOFF" ${change} design --write before invoking Superpo
|
|
|
10752
10912
|
if (!await nonempty(context)) {
|
|
10753
10913
|
return fail(
|
|
10754
10914
|
`handoff_context does not point to a non-empty file: ${context}
|
|
10755
|
-
Next: regenerate the design handoff with comet
|
|
10915
|
+
Next: regenerate the design handoff with comet handoff ${change} design --write.`
|
|
10756
10916
|
);
|
|
10757
10917
|
}
|
|
10758
10918
|
if (!/^[a-f0-9]{64}$/u.test(recordedHash)) {
|
|
10759
10919
|
return fail(
|
|
10760
10920
|
`handoff_hash is missing or invalid: ${recordedHash || "null"}
|
|
10761
|
-
Next: regenerate the design handoff with comet
|
|
10921
|
+
Next: regenerate the design handoff with comet handoff ${change} design --write.`
|
|
10762
10922
|
);
|
|
10763
10923
|
}
|
|
10764
10924
|
const actualHash = await computeHandoffHash(changeDir);
|
|
@@ -10767,14 +10927,14 @@ Next: regenerate the design handoff with comet-handoff.mjs.`
|
|
|
10767
10927
|
`OpenSpec artifacts changed after handoff was generated.
|
|
10768
10928
|
Expected handoff_hash: ${recordedHash}
|
|
10769
10929
|
Actual handoff_hash: ${actualHash}
|
|
10770
|
-
Next:
|
|
10930
|
+
Next: run comet handoff ${change} design --write so Superpowers receives the current OpenSpec context.`
|
|
10771
10931
|
);
|
|
10772
10932
|
}
|
|
10773
10933
|
const markdown = `${context.replace(/\.json$/u, "")}.md`;
|
|
10774
10934
|
if (!await nonempty(markdown)) {
|
|
10775
10935
|
return fail(
|
|
10776
10936
|
`design handoff markdown is missing or empty: ${markdown}
|
|
10777
|
-
Next: regenerate the design handoff with comet
|
|
10937
|
+
Next: regenerate the design handoff with comet handoff ${change} design --write.`
|
|
10778
10938
|
);
|
|
10779
10939
|
}
|
|
10780
10940
|
return pass();
|
|
@@ -10785,7 +10945,7 @@ async function designHandoffMarkdownTraceable(changeDir) {
|
|
|
10785
10945
|
const markdown = `${context.replace(/\.json$/u, "")}.md`;
|
|
10786
10946
|
if (!await nonempty(markdown))
|
|
10787
10947
|
return fail(`design handoff markdown is missing or empty: ${markdown}`);
|
|
10788
|
-
const source = await
|
|
10948
|
+
const source = await fs16.readFile(markdown, "utf8");
|
|
10789
10949
|
const problems = [];
|
|
10790
10950
|
if (!/^Generated-by: comet-handoff\.sh$/mu.test(source)) {
|
|
10791
10951
|
problems.push("handoff markdown is missing Generated-by marker");
|
|
@@ -10812,7 +10972,7 @@ async function betaSpecJsonStructurallyValid(changeDir) {
|
|
|
10812
10972
|
const context = await readField(changeDir, "handoff_context");
|
|
10813
10973
|
if (!context || context === "null") return fail("handoff_context is missing from .comet.yaml");
|
|
10814
10974
|
if (!await nonempty(context)) return fail(`spec-context.json is missing or empty: ${context}`);
|
|
10815
|
-
const source = await
|
|
10975
|
+
const source = await fs16.readFile(context, "utf8");
|
|
10816
10976
|
const problems = [];
|
|
10817
10977
|
let parsed;
|
|
10818
10978
|
try {
|
|
@@ -10849,19 +11009,19 @@ async function guardOpenChecks(output, changeDir) {
|
|
|
10849
11009
|
const checks = [
|
|
10850
11010
|
check(
|
|
10851
11011
|
"proposal.md exists and non-empty",
|
|
10852
|
-
async () => await nonempty(
|
|
11012
|
+
async () => await nonempty(path17.join(changeDir, "proposal.md")) ? pass() : fail("")
|
|
10853
11013
|
),
|
|
10854
11014
|
check(
|
|
10855
11015
|
"proposal.md matches configured language",
|
|
10856
|
-
() => documentLanguageMatchesConfigured(changeDir,
|
|
11016
|
+
() => documentLanguageMatchesConfigured(changeDir, path17.join(changeDir, "proposal.md"))
|
|
10857
11017
|
),
|
|
10858
11018
|
check(
|
|
10859
11019
|
"tasks.md exists and non-empty",
|
|
10860
|
-
async () => await nonempty(
|
|
11020
|
+
async () => await nonempty(path17.join(changeDir, "tasks.md")) ? pass() : fail("")
|
|
10861
11021
|
),
|
|
10862
11022
|
check(
|
|
10863
11023
|
"tasks.md matches configured language",
|
|
10864
|
-
() => documentLanguageMatchesConfigured(changeDir,
|
|
11024
|
+
() => documentLanguageMatchesConfigured(changeDir, path17.join(changeDir, "tasks.md"))
|
|
10865
11025
|
),
|
|
10866
11026
|
check(
|
|
10867
11027
|
"tasks.md has at least one task",
|
|
@@ -10874,11 +11034,11 @@ async function guardOpenChecks(output, changeDir) {
|
|
|
10874
11034
|
0,
|
|
10875
11035
|
check(
|
|
10876
11036
|
"design.md exists and non-empty",
|
|
10877
|
-
async () => await nonempty(
|
|
11037
|
+
async () => await nonempty(path17.join(changeDir, "design.md")) ? pass() : fail("")
|
|
10878
11038
|
),
|
|
10879
11039
|
check(
|
|
10880
11040
|
"design.md matches configured language",
|
|
10881
|
-
() => documentLanguageMatchesConfigured(changeDir,
|
|
11041
|
+
() => documentLanguageMatchesConfigured(changeDir, path17.join(changeDir, "design.md"))
|
|
10882
11042
|
)
|
|
10883
11043
|
);
|
|
10884
11044
|
}
|
|
@@ -10890,27 +11050,27 @@ async function guardDesignChecks(output, changeDir, change) {
|
|
|
10890
11050
|
const builders = [
|
|
10891
11051
|
check(
|
|
10892
11052
|
"proposal.md exists",
|
|
10893
|
-
async () => await nonempty(
|
|
11053
|
+
async () => await nonempty(path17.join(changeDir, "proposal.md")) ? pass() : fail("")
|
|
10894
11054
|
),
|
|
10895
11055
|
check(
|
|
10896
11056
|
"proposal.md matches configured language",
|
|
10897
|
-
() => documentLanguageMatchesConfigured(changeDir,
|
|
11057
|
+
() => documentLanguageMatchesConfigured(changeDir, path17.join(changeDir, "proposal.md"))
|
|
10898
11058
|
),
|
|
10899
11059
|
check(
|
|
10900
11060
|
"design.md exists",
|
|
10901
|
-
async () => await nonempty(
|
|
11061
|
+
async () => await nonempty(path17.join(changeDir, "design.md")) ? pass() : fail("")
|
|
10902
11062
|
),
|
|
10903
11063
|
check(
|
|
10904
11064
|
"design.md matches configured language",
|
|
10905
|
-
() => documentLanguageMatchesConfigured(changeDir,
|
|
11065
|
+
() => documentLanguageMatchesConfigured(changeDir, path17.join(changeDir, "design.md"))
|
|
10906
11066
|
),
|
|
10907
11067
|
check(
|
|
10908
11068
|
"tasks.md exists",
|
|
10909
|
-
async () => await nonempty(
|
|
11069
|
+
async () => await nonempty(path17.join(changeDir, "tasks.md")) ? pass() : fail("")
|
|
10910
11070
|
),
|
|
10911
11071
|
check(
|
|
10912
11072
|
"tasks.md matches configured language",
|
|
10913
|
-
() => documentLanguageMatchesConfigured(changeDir,
|
|
11073
|
+
() => documentLanguageMatchesConfigured(changeDir, path17.join(changeDir, "tasks.md"))
|
|
10914
11074
|
),
|
|
10915
11075
|
check("design handoff context exists", () => designHandoffContextValid(changeDir, change)),
|
|
10916
11076
|
check("design handoff markdown is traceable", () => designHandoffMarkdownTraceable(changeDir))
|
|
@@ -10961,6 +11121,7 @@ async function guardDesignChecks(output, changeDir, change) {
|
|
|
10961
11121
|
}
|
|
10962
11122
|
async function guardBuildChecks(output, changeDir, change, run) {
|
|
10963
11123
|
return runChecks(output, [
|
|
11124
|
+
check("bound branch matches workspace mode", () => boundBranchMatches(changeDir, change)),
|
|
10964
11125
|
check("isolation selected", () => isolationSelected(changeDir, change)),
|
|
10965
11126
|
check("build_mode selected", () => buildModeSelected(changeDir, change)),
|
|
10966
11127
|
check("build_mode allowed for workflow", () => buildModeAllowedForWorkflow(changeDir)),
|
|
@@ -10971,11 +11132,11 @@ async function guardBuildChecks(output, changeDir, change, run) {
|
|
|
10971
11132
|
check("Superpowers plan all tasks checked", () => planTasksAllDone(changeDir)),
|
|
10972
11133
|
check(
|
|
10973
11134
|
"proposal.md exists",
|
|
10974
|
-
async () => await nonempty(
|
|
11135
|
+
async () => await nonempty(path17.join(changeDir, "proposal.md")) ? pass() : fail("")
|
|
10975
11136
|
),
|
|
10976
11137
|
check(
|
|
10977
11138
|
"proposal.md matches configured language",
|
|
10978
|
-
() => documentLanguageMatchesConfigured(changeDir,
|
|
11139
|
+
() => documentLanguageMatchesConfigured(changeDir, path17.join(changeDir, "proposal.md"))
|
|
10979
11140
|
),
|
|
10980
11141
|
check("Superpowers plan matches configured language", async () => {
|
|
10981
11142
|
const plan = await readField(changeDir, "plan");
|
|
@@ -10992,6 +11153,7 @@ async function guardBuildChecks(output, changeDir, change, run) {
|
|
|
10992
11153
|
}
|
|
10993
11154
|
async function guardVerifyChecks(output, changeDir, change, run) {
|
|
10994
11155
|
return runChecks(output, [
|
|
11156
|
+
check("bound branch matches workspace mode", () => boundBranchMatches(changeDir, change)),
|
|
10995
11157
|
check("tasks.md all tasks checked", () => tasksAllDone(changeDir)),
|
|
10996
11158
|
// Verification command runs after tasks check — no point running tests
|
|
10997
11159
|
// if tasks.md is incomplete.
|
|
@@ -11007,30 +11169,32 @@ async function guardVerifyChecks(output, changeDir, change, run) {
|
|
|
11007
11169
|
const report = await readField(changeDir, "verification_report");
|
|
11008
11170
|
if (!report || report === "null" || !await exists4(report)) return pass();
|
|
11009
11171
|
return documentLanguageMatchesConfigured(changeDir, report);
|
|
11010
|
-
})
|
|
11011
|
-
check(
|
|
11012
|
-
"branch_status=handled",
|
|
11013
|
-
async () => await branchStatusHandled(changeDir) ? pass() : fail("")
|
|
11014
|
-
)
|
|
11172
|
+
})
|
|
11015
11173
|
]);
|
|
11016
11174
|
}
|
|
11017
|
-
async function guardArchiveChecks(output, changeDir) {
|
|
11175
|
+
async function guardArchiveChecks(output, changeDir, change) {
|
|
11018
11176
|
return runChecks(output, [
|
|
11177
|
+
check("bound branch matches workspace mode", () => boundBranchMatches(changeDir, change)),
|
|
11019
11178
|
check("archived is true", async () => await archivedIsTrue(changeDir) ? pass() : fail("")),
|
|
11020
11179
|
check(
|
|
11021
11180
|
"proposal.md exists",
|
|
11022
|
-
async () => await nonempty(
|
|
11181
|
+
async () => await nonempty(path17.join(changeDir, "proposal.md")) ? pass() : fail("")
|
|
11023
11182
|
),
|
|
11024
11183
|
check(
|
|
11025
11184
|
"design.md exists",
|
|
11026
|
-
async () => await nonempty(
|
|
11185
|
+
async () => await nonempty(path17.join(changeDir, "design.md")) ? pass() : fail("")
|
|
11027
11186
|
),
|
|
11028
|
-
check("tasks.md all tasks checked", () => tasksAllDone(changeDir))
|
|
11187
|
+
check("tasks.md all tasks checked", () => tasksAllDone(changeDir)),
|
|
11188
|
+
check(
|
|
11189
|
+
"branch_status=handled",
|
|
11190
|
+
async () => await branchStatusHandled(changeDir) ? pass() : fail("")
|
|
11191
|
+
)
|
|
11029
11192
|
]);
|
|
11030
11193
|
}
|
|
11031
|
-
async function applyStateUpdate(output, change, changeDir, phase
|
|
11194
|
+
async function applyStateUpdate(output, change, changeDir, phase) {
|
|
11032
11195
|
const event = CLASSIC_GUARD_TRANSITION_EVENT[phase];
|
|
11033
11196
|
if (!event) return;
|
|
11197
|
+
const context = await ensureClassicRuntimeRun(changeDir);
|
|
11034
11198
|
const result5 = applyClassicTransition(context.classic, event);
|
|
11035
11199
|
await transitionClassicRuntimeRun(changeDir, result5.classic, context.run, {
|
|
11036
11200
|
event,
|
|
@@ -11084,7 +11248,7 @@ Valid phases: open, design, build, verify, archive`
|
|
|
11084
11248
|
blocked2 = await guardBuildChecks(output, changeDir, change, runContext.run);
|
|
11085
11249
|
else if (phase === "verify")
|
|
11086
11250
|
blocked2 = await guardVerifyChecks(output, changeDir, change, runContext.run);
|
|
11087
|
-
else blocked2 = await guardArchiveChecks(output, changeDir);
|
|
11251
|
+
else blocked2 = await guardArchiveChecks(output, changeDir, change);
|
|
11088
11252
|
if (blocked2) {
|
|
11089
11253
|
output.stderr.push("");
|
|
11090
11254
|
output.stderr.push(red2("BLOCKED — fix failing checks before proceeding to next phase"));
|
|
@@ -11093,7 +11257,7 @@ Valid phases: open, design, build, verify, archive`
|
|
|
11093
11257
|
output.stderr.push("");
|
|
11094
11258
|
output.stderr.push(green2("ALL CHECKS PASSED — ready for next phase"));
|
|
11095
11259
|
if (flag === "--apply") {
|
|
11096
|
-
await applyStateUpdate(output, change, changeDir, phase
|
|
11260
|
+
await applyStateUpdate(output, change, changeDir, phase);
|
|
11097
11261
|
}
|
|
11098
11262
|
return output.toResult(0);
|
|
11099
11263
|
} catch (error) {
|
|
@@ -11106,10 +11270,10 @@ Valid phases: open, design, build, verify, archive`
|
|
|
11106
11270
|
};
|
|
11107
11271
|
|
|
11108
11272
|
// domains/comet-classic/classic-handoff.ts
|
|
11109
|
-
var
|
|
11273
|
+
var import_yaml7 = __toESM(require_dist(), 1);
|
|
11110
11274
|
import { createHash as createHash5 } from "crypto";
|
|
11111
|
-
import { promises as
|
|
11112
|
-
import
|
|
11275
|
+
import { promises as fs17, readFileSync as readFileSync2 } from "fs";
|
|
11276
|
+
import path18 from "path";
|
|
11113
11277
|
var GREEN4 = "\x1B[32m";
|
|
11114
11278
|
var RED4 = "\x1B[31m";
|
|
11115
11279
|
var YELLOW4 = "\x1B[33m";
|
|
@@ -11143,7 +11307,7 @@ var HandoffOutput = class {
|
|
|
11143
11307
|
};
|
|
11144
11308
|
async function exists5(file) {
|
|
11145
11309
|
try {
|
|
11146
|
-
await
|
|
11310
|
+
await fs17.access(file);
|
|
11147
11311
|
return true;
|
|
11148
11312
|
} catch (error) {
|
|
11149
11313
|
if (error.code === "ENOENT") return false;
|
|
@@ -11152,7 +11316,7 @@ async function exists5(file) {
|
|
|
11152
11316
|
}
|
|
11153
11317
|
async function nonempty2(file) {
|
|
11154
11318
|
try {
|
|
11155
|
-
return (await
|
|
11319
|
+
return (await fs17.stat(file)).size > 0;
|
|
11156
11320
|
} catch (error) {
|
|
11157
11321
|
if (error.code === "ENOENT") return false;
|
|
11158
11322
|
throw error;
|
|
@@ -11181,7 +11345,7 @@ async function handoffSourceFiles2(changeDir) {
|
|
|
11181
11345
|
const files = [`${changeDir}/proposal.md`, `${changeDir}/design.md`, `${changeDir}/tasks.md`];
|
|
11182
11346
|
const specs = `${changeDir}/specs`;
|
|
11183
11347
|
if (await exists5(specs)) {
|
|
11184
|
-
for (const entry2 of (await
|
|
11348
|
+
for (const entry2 of (await fs17.readdir(specs)).sort()) {
|
|
11185
11349
|
const spec = `${specs}/${entry2}/spec.md`;
|
|
11186
11350
|
if (await exists5(spec)) files.push(spec);
|
|
11187
11351
|
}
|
|
@@ -11229,7 +11393,7 @@ async function writeMarkdownContext(changeDir, change, mode, contextHash, output
|
|
|
11229
11393
|
];
|
|
11230
11394
|
for (const file of await handoffSourceFiles2(changeDir)) {
|
|
11231
11395
|
if (!await exists5(file)) continue;
|
|
11232
|
-
const content = await
|
|
11396
|
+
const content = await fs17.readFile(file, "utf8");
|
|
11233
11397
|
const total = lineCount(content);
|
|
11234
11398
|
lines.push(
|
|
11235
11399
|
`## ${file}`,
|
|
@@ -11254,7 +11418,7 @@ async function writeMarkdownContext(changeDir, change, mode, contextHash, output
|
|
|
11254
11418
|
}
|
|
11255
11419
|
lines.push("");
|
|
11256
11420
|
}
|
|
11257
|
-
await
|
|
11421
|
+
await fs17.writeFile(output, lines.join("\n"));
|
|
11258
11422
|
}
|
|
11259
11423
|
async function writeJsonContext(changeDir, change, mode, contextHash, output) {
|
|
11260
11424
|
const entries = [];
|
|
@@ -11277,7 +11441,7 @@ async function writeJsonContext(changeDir, change, mode, contextHash, output) {
|
|
|
11277
11441
|
"}",
|
|
11278
11442
|
""
|
|
11279
11443
|
].join("\n");
|
|
11280
|
-
await
|
|
11444
|
+
await fs17.writeFile(output, document);
|
|
11281
11445
|
}
|
|
11282
11446
|
async function writeSpecProjectionForFile(file, content) {
|
|
11283
11447
|
return [
|
|
@@ -11317,11 +11481,11 @@ async function writeSpecMarkdownContext(changeDir, change, contextHash, output)
|
|
|
11317
11481
|
const specs = `${changeDir}/specs`;
|
|
11318
11482
|
let projected = false;
|
|
11319
11483
|
if (await exists5(specs)) {
|
|
11320
|
-
for (const entry2 of (await
|
|
11484
|
+
for (const entry2 of (await fs17.readdir(specs)).sort()) {
|
|
11321
11485
|
const spec = `${specs}/${entry2}/spec.md`;
|
|
11322
11486
|
if (!await exists5(spec)) continue;
|
|
11323
11487
|
projected = true;
|
|
11324
|
-
lines.push(...await writeSpecProjectionForFile(spec, await
|
|
11488
|
+
lines.push(...await writeSpecProjectionForFile(spec, await fs17.readFile(spec, "utf8")));
|
|
11325
11489
|
}
|
|
11326
11490
|
}
|
|
11327
11491
|
if (!projected) {
|
|
@@ -11330,7 +11494,7 @@ async function writeSpecMarkdownContext(changeDir, change, contextHash, output)
|
|
|
11330
11494
|
lines.push(
|
|
11331
11495
|
"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."
|
|
11332
11496
|
);
|
|
11333
|
-
await
|
|
11497
|
+
await fs17.writeFile(output, lines.join("\n"));
|
|
11334
11498
|
}
|
|
11335
11499
|
async function writeSpecJsonContext(changeDir, change, contextHash, output) {
|
|
11336
11500
|
const entries = [];
|
|
@@ -11339,7 +11503,7 @@ async function writeSpecJsonContext(changeDir, change, contextHash, output) {
|
|
|
11339
11503
|
const role = /\/specs\/[^/]+\/spec\.md$/u.test(file) ? "spec" : "supporting";
|
|
11340
11504
|
entries.push({ path: file, sha256: hashFile2(file), role });
|
|
11341
11505
|
}
|
|
11342
|
-
await
|
|
11506
|
+
await fs17.writeFile(
|
|
11343
11507
|
output,
|
|
11344
11508
|
`${JSON.stringify(
|
|
11345
11509
|
{
|
|
@@ -11358,8 +11522,8 @@ async function writeSpecJsonContext(changeDir, change, contextHash, output) {
|
|
|
11358
11522
|
);
|
|
11359
11523
|
}
|
|
11360
11524
|
async function readField2(changeDir, field2) {
|
|
11361
|
-
const file =
|
|
11362
|
-
const document = (0,
|
|
11525
|
+
const file = path18.join(changeDir, ".comet.yaml");
|
|
11526
|
+
const document = (0, import_yaml7.parseDocument)(await fs17.readFile(file, "utf8"), { uniqueKeys: false });
|
|
11363
11527
|
if (document.errors.length > 0) {
|
|
11364
11528
|
throw new HandoffFailure(`ERROR: Invalid .comet.yaml: ${document.errors[0].message}`);
|
|
11365
11529
|
}
|
|
@@ -11393,7 +11557,7 @@ async function completedHandoffIsCurrent(changeDir, run, contextHash, contextJso
|
|
|
11393
11557
|
readCheckpoint(changeDir, run.checkpointRef)
|
|
11394
11558
|
]);
|
|
11395
11559
|
if (!await exists5(contextJson) || !await exists5(contextMd)) return false;
|
|
11396
|
-
if (context !== await
|
|
11560
|
+
if (context !== await fs17.readFile(contextMd, "utf8")) return false;
|
|
11397
11561
|
if (artifacts.handoff_context !== contextJson || artifacts.handoff_markdown !== contextMd) {
|
|
11398
11562
|
return false;
|
|
11399
11563
|
}
|
|
@@ -11516,7 +11680,7 @@ var classicHandoffCommand = async (args) => {
|
|
|
11516
11680
|
run: pendingRun,
|
|
11517
11681
|
unknownKeys: (await readClassicState(changeDir)).unknownKeys
|
|
11518
11682
|
});
|
|
11519
|
-
await
|
|
11683
|
+
await fs17.mkdir(handoffDir, { recursive: true });
|
|
11520
11684
|
if (handoffMode === "beta") {
|
|
11521
11685
|
await writeSpecMarkdownContext(changeDir, change, contextHash, contextMd);
|
|
11522
11686
|
await writeSpecJsonContext(changeDir, change, contextHash, contextJson);
|
|
@@ -11524,7 +11688,7 @@ var classicHandoffCommand = async (args) => {
|
|
|
11524
11688
|
await writeMarkdownContext(changeDir, change, handoffMode, contextHash, contextMd);
|
|
11525
11689
|
await writeJsonContext(changeDir, change, handoffMode, contextHash, contextJson);
|
|
11526
11690
|
}
|
|
11527
|
-
const context = await
|
|
11691
|
+
const context = await fs17.readFile(contextMd, "utf8");
|
|
11528
11692
|
await writeContext(changeDir, pendingRun.contextRef, context);
|
|
11529
11693
|
const artifacts = {
|
|
11530
11694
|
...await readArtifacts(changeDir, pendingRun.artifactsRef),
|
|
@@ -11581,37 +11745,24 @@ var classicHandoffCommand = async (args) => {
|
|
|
11581
11745
|
};
|
|
11582
11746
|
|
|
11583
11747
|
// domains/comet-classic/classic-hook-guard.ts
|
|
11584
|
-
import { existsSync as existsSync2, promises as
|
|
11585
|
-
import
|
|
11748
|
+
import { existsSync as existsSync2, promises as fs19, readFileSync as readFileSync3 } from "fs";
|
|
11749
|
+
import path20 from "path";
|
|
11586
11750
|
|
|
11587
11751
|
// domains/comet-classic/classic-current-change.ts
|
|
11588
|
-
import {
|
|
11589
|
-
import {
|
|
11590
|
-
import
|
|
11591
|
-
import path18 from "path";
|
|
11752
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
11753
|
+
import { promises as fs18 } from "fs";
|
|
11754
|
+
import path19 from "path";
|
|
11592
11755
|
function currentChangeFile(projectRoot2) {
|
|
11593
|
-
return
|
|
11594
|
-
}
|
|
11595
|
-
function currentBranch(projectRoot2) {
|
|
11596
|
-
try {
|
|
11597
|
-
const branch = execFileSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
11598
|
-
cwd: projectRoot2,
|
|
11599
|
-
encoding: "utf8",
|
|
11600
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
11601
|
-
}).trim();
|
|
11602
|
-
return branch && branch !== "HEAD" ? branch : null;
|
|
11603
|
-
} catch {
|
|
11604
|
-
return null;
|
|
11605
|
-
}
|
|
11756
|
+
return path19.join(projectRoot2, ".comet", "current-change.json");
|
|
11606
11757
|
}
|
|
11607
11758
|
function changeDirectory(projectRoot2, changeName) {
|
|
11608
|
-
return
|
|
11759
|
+
return path19.join(projectRoot2, "openspec", "changes", changeName);
|
|
11609
11760
|
}
|
|
11610
11761
|
async function validateActiveChange(projectRoot2, changeName) {
|
|
11611
11762
|
assertOpenSpecChangeName(changeName);
|
|
11612
11763
|
const changeDir = changeDirectory(projectRoot2, changeName);
|
|
11613
11764
|
try {
|
|
11614
|
-
await
|
|
11765
|
+
await fs18.access(path19.join(changeDir, ".comet.yaml"));
|
|
11615
11766
|
} catch (error) {
|
|
11616
11767
|
if (error.code === "ENOENT") {
|
|
11617
11768
|
throw new Error(
|
|
@@ -11652,30 +11803,40 @@ function parseSelection(source) {
|
|
|
11652
11803
|
throw new Error("current change selection change must be a string");
|
|
11653
11804
|
}
|
|
11654
11805
|
assertOpenSpecChangeName(record.change);
|
|
11655
|
-
if (record.branch !== null && typeof record.branch !== "string") {
|
|
11806
|
+
if (record.branch !== void 0 && record.branch !== null && typeof record.branch !== "string") {
|
|
11656
11807
|
throw new Error("current change selection branch must be a string or null");
|
|
11657
11808
|
}
|
|
11658
11809
|
return {
|
|
11659
11810
|
version: 1,
|
|
11660
11811
|
change: record.change,
|
|
11661
|
-
branch: record.branch
|
|
11812
|
+
branch: record.branch ?? null
|
|
11662
11813
|
};
|
|
11663
11814
|
}
|
|
11664
11815
|
async function selectCurrentChange(projectRoot2, changeName) {
|
|
11665
11816
|
await validateActiveChange(projectRoot2, changeName);
|
|
11817
|
+
const outcome = await resolveBranchBinding(changeDirectory(projectRoot2, changeName), {
|
|
11818
|
+
heal: true,
|
|
11819
|
+
cwd: projectRoot2
|
|
11820
|
+
});
|
|
11821
|
+
if (outcome.status === "drift") {
|
|
11822
|
+
throw new Error(driftStaleReason(changeName, outcome.boundBranch, outcome.currentBranch));
|
|
11823
|
+
}
|
|
11824
|
+
if (outcome.status === "unbound-detached") {
|
|
11825
|
+
throw new Error(unboundDetachedMessage(changeName));
|
|
11826
|
+
}
|
|
11666
11827
|
const selection = {
|
|
11667
11828
|
version: 1,
|
|
11668
11829
|
change: changeName,
|
|
11669
|
-
branch: currentBranch
|
|
11830
|
+
branch: outcome.currentBranch
|
|
11670
11831
|
};
|
|
11671
11832
|
const file = currentChangeFile(projectRoot2);
|
|
11672
|
-
const temporary = `${file}.${
|
|
11673
|
-
await
|
|
11833
|
+
const temporary = `${file}.${randomUUID7()}.tmp`;
|
|
11834
|
+
await fs18.mkdir(path19.dirname(file), { recursive: true });
|
|
11674
11835
|
try {
|
|
11675
|
-
await
|
|
11676
|
-
await
|
|
11836
|
+
await fs18.writeFile(temporary, JSON.stringify(selection, null, 2) + "\n", "utf8");
|
|
11837
|
+
await fs18.rename(temporary, file);
|
|
11677
11838
|
} catch (error) {
|
|
11678
|
-
await
|
|
11839
|
+
await fs18.rm(temporary, { force: true });
|
|
11679
11840
|
throw error;
|
|
11680
11841
|
}
|
|
11681
11842
|
return selection;
|
|
@@ -11683,7 +11844,7 @@ async function selectCurrentChange(projectRoot2, changeName) {
|
|
|
11683
11844
|
async function resolveCurrentChange(projectRoot2) {
|
|
11684
11845
|
let source;
|
|
11685
11846
|
try {
|
|
11686
|
-
source = await
|
|
11847
|
+
source = await fs18.readFile(currentChangeFile(projectRoot2), "utf8");
|
|
11687
11848
|
} catch (error) {
|
|
11688
11849
|
if (error.code === "ENOENT") return { status: "missing" };
|
|
11689
11850
|
return {
|
|
@@ -11701,17 +11862,32 @@ async function resolveCurrentChange(projectRoot2) {
|
|
|
11701
11862
|
reason: error instanceof Error ? error.message : String(error)
|
|
11702
11863
|
};
|
|
11703
11864
|
}
|
|
11704
|
-
const
|
|
11705
|
-
|
|
11865
|
+
const outcome = await resolveBranchBinding(changeDirectory(projectRoot2, selection.change), {
|
|
11866
|
+
heal: false,
|
|
11867
|
+
cwd: projectRoot2
|
|
11868
|
+
});
|
|
11869
|
+
if (outcome.status === "drift") {
|
|
11870
|
+
return {
|
|
11871
|
+
status: "stale",
|
|
11872
|
+
reason: driftStaleReason(selection.change, outcome.boundBranch, outcome.currentBranch)
|
|
11873
|
+
};
|
|
11874
|
+
}
|
|
11875
|
+
if (outcome.status === "unbound-detached") {
|
|
11876
|
+
return { status: "stale", reason: unboundDetachedMessage(selection.change) };
|
|
11877
|
+
}
|
|
11878
|
+
if (outcome.status === "ok") {
|
|
11879
|
+
return { status: "selected", selection };
|
|
11880
|
+
}
|
|
11881
|
+
if (selection.branch !== null && outcome.currentBranch !== selection.branch) {
|
|
11706
11882
|
return {
|
|
11707
11883
|
status: "stale",
|
|
11708
|
-
reason: `current change '${selection.change}' was selected on branch '${selection.branch}', current branch is '${
|
|
11884
|
+
reason: `current change '${selection.change}' was selected on branch '${selection.branch}', current branch is '${outcome.currentBranch ?? "detached HEAD"}'`
|
|
11709
11885
|
};
|
|
11710
11886
|
}
|
|
11711
11887
|
return { status: "selected", selection };
|
|
11712
11888
|
}
|
|
11713
11889
|
async function clearCurrentChange(projectRoot2) {
|
|
11714
|
-
await
|
|
11890
|
+
await fs18.rm(currentChangeFile(projectRoot2), { force: true });
|
|
11715
11891
|
}
|
|
11716
11892
|
|
|
11717
11893
|
// domains/comet-classic/classic-hook-guard.ts
|
|
@@ -11736,66 +11912,75 @@ function inputTarget() {
|
|
|
11736
11912
|
function normalized(value) {
|
|
11737
11913
|
return value.replaceAll("\\", "/").replace(/\/+/gu, "/");
|
|
11738
11914
|
}
|
|
11915
|
+
function comparisonKey(value) {
|
|
11916
|
+
const normalizedValue = normalized(value);
|
|
11917
|
+
return process.platform === "win32" ? normalizedValue.toLowerCase() : normalizedValue;
|
|
11918
|
+
}
|
|
11739
11919
|
function parseProjectRoot(args) {
|
|
11740
11920
|
const index = args.indexOf("--project-root");
|
|
11741
11921
|
const value = index >= 0 ? args[index + 1] : void 0;
|
|
11742
|
-
return
|
|
11922
|
+
return path20.resolve(value && !value.startsWith("--") ? value : process.cwd());
|
|
11743
11923
|
}
|
|
11744
11924
|
function relativeToProjectRoot(target, projectRoot2) {
|
|
11745
|
-
const relative = normalized(
|
|
11925
|
+
const relative = normalized(path20.relative(projectRoot2, target));
|
|
11746
11926
|
if (relative === "") return "";
|
|
11747
|
-
if (relative.startsWith("../") || relative === ".." ||
|
|
11927
|
+
if (relative.startsWith("../") || relative === ".." || path20.isAbsolute(relative)) return null;
|
|
11748
11928
|
return relative;
|
|
11749
11929
|
}
|
|
11750
11930
|
async function physicalPathForPossiblyMissingTarget(target) {
|
|
11751
|
-
const resolved =
|
|
11752
|
-
const root =
|
|
11931
|
+
const resolved = path20.resolve(target);
|
|
11932
|
+
const root = path20.parse(resolved).root;
|
|
11753
11933
|
const missingSegments = [];
|
|
11754
11934
|
let cursor = resolved;
|
|
11755
11935
|
while (cursor && cursor !== root) {
|
|
11756
11936
|
try {
|
|
11757
|
-
const physicalBase = await
|
|
11758
|
-
return
|
|
11937
|
+
const physicalBase = await fs19.realpath(cursor);
|
|
11938
|
+
return path20.join(physicalBase, ...missingSegments.reverse());
|
|
11759
11939
|
} catch (error) {
|
|
11760
11940
|
const code = error.code;
|
|
11761
11941
|
if (code !== "ENOENT" && code !== "ENOTDIR") throw error;
|
|
11762
|
-
missingSegments.push(
|
|
11763
|
-
cursor =
|
|
11942
|
+
missingSegments.push(path20.basename(cursor));
|
|
11943
|
+
cursor = path20.dirname(cursor);
|
|
11764
11944
|
}
|
|
11765
11945
|
}
|
|
11766
11946
|
try {
|
|
11767
|
-
const physicalRoot = await
|
|
11768
|
-
return
|
|
11947
|
+
const physicalRoot = await fs19.realpath(root);
|
|
11948
|
+
return path20.join(physicalRoot, ...missingSegments.reverse());
|
|
11769
11949
|
} catch {
|
|
11770
11950
|
return null;
|
|
11771
11951
|
}
|
|
11772
11952
|
}
|
|
11773
11953
|
async function projectRelative(target, projectRoot2) {
|
|
11774
|
-
const rawCandidate =
|
|
11954
|
+
const rawCandidate = path20.isAbsolute(target) ? target : path20.resolve(process.cwd(), target);
|
|
11775
11955
|
let candidate = normalized(rawCandidate);
|
|
11776
11956
|
const rootRelative = relativeToProjectRoot(rawCandidate, projectRoot2);
|
|
11777
11957
|
if (rootRelative !== null) return rootRelative;
|
|
11778
11958
|
try {
|
|
11779
11959
|
const physicalCandidate = await physicalPathForPossiblyMissingTarget(rawCandidate);
|
|
11780
|
-
const physicalRoot = await
|
|
11960
|
+
const physicalRoot = await fs19.realpath(projectRoot2);
|
|
11781
11961
|
if (physicalCandidate) {
|
|
11782
11962
|
const physicalRootRelative = relativeToProjectRoot(physicalCandidate, physicalRoot);
|
|
11783
11963
|
if (physicalRootRelative !== null) return physicalRootRelative;
|
|
11784
11964
|
candidate = normalized(physicalCandidate);
|
|
11785
11965
|
}
|
|
11786
11966
|
} catch {
|
|
11787
|
-
if (!
|
|
11967
|
+
if (!path20.isAbsolute(target)) return normalized(target).replace(/^\.\//u, "");
|
|
11788
11968
|
}
|
|
11789
11969
|
return candidate.replace(/^\.\//u, "");
|
|
11790
11970
|
}
|
|
11791
11971
|
async function loadGoverningChange(changeDir) {
|
|
11792
11972
|
try {
|
|
11793
|
-
const
|
|
11973
|
+
const projection = await readClassicState(changeDir, { migrate: false });
|
|
11974
|
+
const unknownKeys = Array.from(new Set(projection.unknownKeys)).sort();
|
|
11975
|
+
if (unknownKeys.length > 0) {
|
|
11976
|
+
throw new Error(`Invalid Classic state: unknown field(s): ${unknownKeys.join(", ")}`);
|
|
11977
|
+
}
|
|
11978
|
+
if (!projection.classic) throw new Error("Classic state projection is unavailable");
|
|
11794
11979
|
return {
|
|
11795
11980
|
changeDir,
|
|
11796
|
-
phase:
|
|
11797
|
-
classic:
|
|
11798
|
-
archived:
|
|
11981
|
+
phase: projection.classic.phase,
|
|
11982
|
+
classic: projection.classic,
|
|
11983
|
+
archived: projection.classic.archived
|
|
11799
11984
|
};
|
|
11800
11985
|
} catch {
|
|
11801
11986
|
const legacy = await readLegacyState(changeDir);
|
|
@@ -11809,15 +11994,15 @@ async function loadGoverningChange(changeDir) {
|
|
|
11809
11994
|
}
|
|
11810
11995
|
}
|
|
11811
11996
|
async function activeChanges(projectRoot2) {
|
|
11812
|
-
const changesDir =
|
|
11997
|
+
const changesDir = path20.join(projectRoot2, "openspec", "changes");
|
|
11813
11998
|
const governingChanges = [];
|
|
11814
11999
|
if (!existsSync2(changesDir)) return governingChanges;
|
|
11815
|
-
for (const entry2 of (await
|
|
12000
|
+
for (const entry2 of (await fs19.readdir(changesDir, { withFileTypes: true })).sort(
|
|
11816
12001
|
(left, right) => left.name.localeCompare(right.name)
|
|
11817
12002
|
)) {
|
|
11818
12003
|
if (!entry2.isDirectory() || entry2.name === "archive") continue;
|
|
11819
|
-
const changeDir =
|
|
11820
|
-
if (!existsSync2(
|
|
12004
|
+
const changeDir = path20.join(changesDir, entry2.name);
|
|
12005
|
+
if (!existsSync2(path20.join(changeDir, ".comet.yaml"))) continue;
|
|
11821
12006
|
const governing = await loadGoverningChange(changeDir);
|
|
11822
12007
|
if (!governing || governing.archived) continue;
|
|
11823
12008
|
governingChanges.push(governing);
|
|
@@ -11825,13 +12010,47 @@ async function activeChanges(projectRoot2) {
|
|
|
11825
12010
|
return governingChanges;
|
|
11826
12011
|
}
|
|
11827
12012
|
function isSuperpowersArtifactPath(relativePath2) {
|
|
11828
|
-
return relativePath2.startsWith("docs/superpowers/");
|
|
12013
|
+
return comparisonKey(relativePath2).startsWith("docs/superpowers/");
|
|
12014
|
+
}
|
|
12015
|
+
var SUPERPOWERS_ARTIFACT_SLOTS = [
|
|
12016
|
+
{
|
|
12017
|
+
prefix: "docs/superpowers/specs/",
|
|
12018
|
+
field: "designDoc",
|
|
12019
|
+
wireField: "design_doc",
|
|
12020
|
+
phase: "design"
|
|
12021
|
+
},
|
|
12022
|
+
{
|
|
12023
|
+
prefix: "docs/superpowers/plans/",
|
|
12024
|
+
field: "plan",
|
|
12025
|
+
wireField: "plan",
|
|
12026
|
+
phase: "build"
|
|
12027
|
+
},
|
|
12028
|
+
{
|
|
12029
|
+
prefix: "docs/superpowers/reports/",
|
|
12030
|
+
field: "verificationReport",
|
|
12031
|
+
wireField: "verification_report",
|
|
12032
|
+
phase: "verify"
|
|
12033
|
+
}
|
|
12034
|
+
];
|
|
12035
|
+
function standardSuperpowersArtifactSlot(relativePath2) {
|
|
12036
|
+
const key = comparisonKey(relativePath2);
|
|
12037
|
+
const slot = SUPERPOWERS_ARTIFACT_SLOTS.find((candidate) => key.startsWith(candidate.prefix));
|
|
12038
|
+
if (!slot) return null;
|
|
12039
|
+
const fileName = key.slice(slot.prefix.length);
|
|
12040
|
+
if (!fileName || fileName.includes("/") || !fileName.endsWith(".md")) return null;
|
|
12041
|
+
return slot;
|
|
12042
|
+
}
|
|
12043
|
+
function superpowersArtifactValue(governing, slot) {
|
|
12044
|
+
return governing.classic?.[slot.field] ?? null;
|
|
12045
|
+
}
|
|
12046
|
+
function allowsFirstSuperpowersArtifactWrite(governing, slot) {
|
|
12047
|
+
return governing.classic !== null && governing.phase === slot.phase && !superpowersArtifactValue(governing, slot);
|
|
11829
12048
|
}
|
|
11830
12049
|
function allowsSuperpowersArtifacts(governing) {
|
|
11831
12050
|
return governing.phase === "design" || governing.phase === "build" || governing.phase === "verify";
|
|
11832
12051
|
}
|
|
11833
12052
|
function governingChangeName(governing) {
|
|
11834
|
-
return governing.changeDir ?
|
|
12053
|
+
return governing.changeDir ? path20.basename(governing.changeDir) : null;
|
|
11835
12054
|
}
|
|
11836
12055
|
var SUPERPOWERS_ARTIFACT_SUFFIXES = /* @__PURE__ */ new Set([
|
|
11837
12056
|
"design",
|
|
@@ -11851,7 +12070,7 @@ function matchesRecordedSuperpowersArtifact(relativePath2, governing) {
|
|
|
11851
12070
|
governing.classic?.verificationReport
|
|
11852
12071
|
];
|
|
11853
12072
|
return artifactPaths.some(
|
|
11854
|
-
(artifactPath) => artifactPath &&
|
|
12073
|
+
(artifactPath) => artifactPath && comparisonKey(artifactPath) === comparisonKey(relativePath2)
|
|
11855
12074
|
);
|
|
11856
12075
|
}
|
|
11857
12076
|
function matchesSuperpowersArtifactName(relativePath2, changeName) {
|
|
@@ -11867,7 +12086,7 @@ async function superpowersArtifactGoverningChange(relativePath2, projectRoot2) {
|
|
|
11867
12086
|
const recorded = active.find(
|
|
11868
12087
|
(governing) => matchesRecordedSuperpowersArtifact(relativePath2, governing)
|
|
11869
12088
|
);
|
|
11870
|
-
if (recorded) return recorded;
|
|
12089
|
+
if (recorded) return { governing: recorded, match: "recorded" };
|
|
11871
12090
|
const eligible = active.filter(allowsSuperpowersArtifacts);
|
|
11872
12091
|
const named = eligible.filter((governing) => {
|
|
11873
12092
|
const name = governingChangeName(governing);
|
|
@@ -11875,7 +12094,7 @@ async function superpowersArtifactGoverningChange(relativePath2, projectRoot2) {
|
|
|
11875
12094
|
}).sort(
|
|
11876
12095
|
(a, b) => (governingChangeName(b)?.length ?? 0) - (governingChangeName(a)?.length ?? 0)
|
|
11877
12096
|
)[0];
|
|
11878
|
-
if (named) return named;
|
|
12097
|
+
if (named) return { governing: named, match: "named" };
|
|
11879
12098
|
return null;
|
|
11880
12099
|
}
|
|
11881
12100
|
async function repoSourceGoverningChange(projectRoot2, relativePath2) {
|
|
@@ -11897,7 +12116,30 @@ async function repoSourceGoverningChange(projectRoot2, relativePath2) {
|
|
|
11897
12116
|
)
|
|
11898
12117
|
};
|
|
11899
12118
|
}
|
|
11900
|
-
if (active.length === 1)
|
|
12119
|
+
if (active.length === 1) {
|
|
12120
|
+
const sole = active[0];
|
|
12121
|
+
if (sole.changeDir !== null) {
|
|
12122
|
+
const outcome = await resolveBranchBinding(sole.changeDir, {
|
|
12123
|
+
heal: false,
|
|
12124
|
+
cwd: projectRoot2
|
|
12125
|
+
});
|
|
12126
|
+
const name = governingChangeName(sole) ?? "unknown";
|
|
12127
|
+
if (outcome.status === "drift") {
|
|
12128
|
+
return {
|
|
12129
|
+
blockedResult: blockedStaleSelection(
|
|
12130
|
+
relativePath2,
|
|
12131
|
+
driftStaleReason(name, outcome.boundBranch, outcome.currentBranch)
|
|
12132
|
+
)
|
|
12133
|
+
};
|
|
12134
|
+
}
|
|
12135
|
+
if (outcome.status === "unbound-detached") {
|
|
12136
|
+
return {
|
|
12137
|
+
blockedResult: blockedStaleSelection(relativePath2, unboundDetachedMessage(name))
|
|
12138
|
+
};
|
|
12139
|
+
}
|
|
12140
|
+
}
|
|
12141
|
+
return sole;
|
|
12142
|
+
}
|
|
11901
12143
|
return {
|
|
11902
12144
|
blockedResult: blockedMultipleChanges(
|
|
11903
12145
|
relativePath2,
|
|
@@ -11911,8 +12153,8 @@ async function governingChange(relativePath2, projectRoot2) {
|
|
|
11911
12153
|
const rest = relativePath2.slice(prefix.length);
|
|
11912
12154
|
const [name] = rest.split("/");
|
|
11913
12155
|
if (name && name !== "archive") {
|
|
11914
|
-
const changeDir =
|
|
11915
|
-
const stateFile2 =
|
|
12156
|
+
const changeDir = path20.join(projectRoot2, "openspec", "changes", name);
|
|
12157
|
+
const stateFile2 = path20.join(changeDir, ".comet.yaml");
|
|
11916
12158
|
if (existsSync2(stateFile2)) {
|
|
11917
12159
|
const governing = await loadGoverningChange(changeDir);
|
|
11918
12160
|
if (governing) return governing;
|
|
@@ -11923,7 +12165,26 @@ async function governingChange(relativePath2, projectRoot2) {
|
|
|
11923
12165
|
}
|
|
11924
12166
|
if (isSuperpowersArtifactPath(relativePath2)) {
|
|
11925
12167
|
const superpowers = await superpowersArtifactGoverningChange(relativePath2, projectRoot2);
|
|
11926
|
-
if (superpowers
|
|
12168
|
+
if (superpowers?.match === "recorded") {
|
|
12169
|
+
return { ...superpowers.governing, superpowersArtifact: "matched" };
|
|
12170
|
+
}
|
|
12171
|
+
const slot = standardSuperpowersArtifactSlot(relativePath2);
|
|
12172
|
+
if (superpowers) {
|
|
12173
|
+
return slot ? {
|
|
12174
|
+
...superpowers.governing,
|
|
12175
|
+
superpowersArtifact: allowsFirstSuperpowersArtifactWrite(superpowers.governing, slot) ? "matched" : "unmatched",
|
|
12176
|
+
superpowersSlot: slot
|
|
12177
|
+
} : { ...superpowers.governing, superpowersArtifact: "matched" };
|
|
12178
|
+
}
|
|
12179
|
+
if (slot) {
|
|
12180
|
+
const candidate = await repoSourceGoverningChange(projectRoot2, relativePath2);
|
|
12181
|
+
if (!candidate || "blockedResult" in candidate) return candidate;
|
|
12182
|
+
return {
|
|
12183
|
+
...candidate,
|
|
12184
|
+
superpowersArtifact: allowsFirstSuperpowersArtifactWrite(candidate, slot) ? "matched" : "unmatched",
|
|
12185
|
+
superpowersSlot: slot
|
|
12186
|
+
};
|
|
12187
|
+
}
|
|
11927
12188
|
const fallback = (await activeChanges(projectRoot2))[0] ?? null;
|
|
11928
12189
|
return fallback ? { ...fallback, superpowersArtifact: "unmatched" } : null;
|
|
11929
12190
|
}
|
|
@@ -11971,7 +12232,7 @@ function blocked(relativePath2, phase) {
|
|
|
11971
12232
|
" BLOCKED: source writes are not allowed during design",
|
|
11972
12233
|
" This phase does not allow source writes",
|
|
11973
12234
|
" ALLOWED: run brainstorming, create the Design Doc, and run guard",
|
|
11974
|
-
" NEXT: finish the Design Doc, then run comet
|
|
12235
|
+
" NEXT: finish the Design Doc, then run comet guard <change-name> design --apply to enter build"
|
|
11975
12236
|
] : [
|
|
11976
12237
|
" BLOCKED: source writes are not allowed during archive",
|
|
11977
12238
|
" This phase does not allow source writes",
|
|
@@ -12012,7 +12273,25 @@ function blockedMissingDesignDoc(relativePath2) {
|
|
|
12012
12273
|
].join("\n")
|
|
12013
12274
|
);
|
|
12014
12275
|
}
|
|
12015
|
-
function blockedUnmatchedSuperpowersArtifact(relativePath2,
|
|
12276
|
+
function blockedUnmatchedSuperpowersArtifact(relativePath2, governing) {
|
|
12277
|
+
const slot = governing.superpowersSlot;
|
|
12278
|
+
const recorded = slot ? superpowersArtifactValue(governing, slot) : null;
|
|
12279
|
+
const details = slot ? governing.phase !== slot.phase ? [
|
|
12280
|
+
` BLOCKED: ${slot.wireField} cannot be first-written in phase ${governing.phase}`,
|
|
12281
|
+
` Expected phase: ${slot.phase}`,
|
|
12282
|
+
" NEXT: resume the matching Comet phase or use an already recorded artifact path"
|
|
12283
|
+
] : recorded ? [
|
|
12284
|
+
` BLOCKED: ${slot.wireField} is already recorded for this change`,
|
|
12285
|
+
` Recorded path: ${recorded}`,
|
|
12286
|
+
" NEXT: write the recorded artifact or explicitly correct the state path"
|
|
12287
|
+
] : [
|
|
12288
|
+
" BLOCKED: standard Superpowers artifact state is incomplete",
|
|
12289
|
+
" NEXT: validate the active change state, then retry the matching phase"
|
|
12290
|
+
] : [
|
|
12291
|
+
" BLOCKED: unmatched Superpowers artifact",
|
|
12292
|
+
" This docs/superpowers/ path does not match any active change artifact",
|
|
12293
|
+
" NEXT: use a recorded artifact path or a standard phase artifact directory"
|
|
12294
|
+
];
|
|
12016
12295
|
return result(
|
|
12017
12296
|
2,
|
|
12018
12297
|
[
|
|
@@ -12021,12 +12300,10 @@ function blockedUnmatchedSuperpowersArtifact(relativePath2, phase) {
|
|
|
12021
12300
|
"║ COMET PHASE GUARD — WRITE BLOCKED ║",
|
|
12022
12301
|
"╚══════════════════════════════════════════╝",
|
|
12023
12302
|
"",
|
|
12024
|
-
` Current phase: ${phase}`,
|
|
12303
|
+
` Current phase: ${governing.phase}`,
|
|
12025
12304
|
` Target file: ${relativePath2}`,
|
|
12026
12305
|
"",
|
|
12027
|
-
|
|
12028
|
-
" This docs/superpowers/ path does not match any active change artifact",
|
|
12029
|
-
" NEXT: record the artifact path in .comet.yaml or include the change name in the artifact filename",
|
|
12306
|
+
...details,
|
|
12030
12307
|
""
|
|
12031
12308
|
].join("\n")
|
|
12032
12309
|
);
|
|
@@ -12104,7 +12381,7 @@ var classicHookGuardCommand = async (args) => {
|
|
|
12104
12381
|
return allowed(`${relativePath2} (phase: ${phase}, superpowers)`);
|
|
12105
12382
|
}
|
|
12106
12383
|
if (governing.superpowersArtifact === "unmatched") {
|
|
12107
|
-
return blockedUnmatchedSuperpowersArtifact(relativePath2,
|
|
12384
|
+
return blockedUnmatchedSuperpowersArtifact(relativePath2, governing);
|
|
12108
12385
|
}
|
|
12109
12386
|
}
|
|
12110
12387
|
if (phase === "build" && governing.classic?.workflow === "full" && !governing.classic.designDoc) {
|
|
@@ -12485,8 +12762,8 @@ var classicIntentCommand = async (args, _options) => {
|
|
|
12485
12762
|
};
|
|
12486
12763
|
|
|
12487
12764
|
// domains/comet-classic/classic-resume-probe.ts
|
|
12488
|
-
import
|
|
12489
|
-
import { promises as
|
|
12765
|
+
import path21 from "path";
|
|
12766
|
+
import { promises as fs20 } from "fs";
|
|
12490
12767
|
import { spawn } from "child_process";
|
|
12491
12768
|
var COMET_RESUME_PROBE_SCHEMA_VERSION = "comet.resume_probe.v1";
|
|
12492
12769
|
function isRecord2(value) {
|
|
@@ -12529,13 +12806,13 @@ function result3(action, change, confidence, reason, evidence = []) {
|
|
|
12529
12806
|
}
|
|
12530
12807
|
async function readIfExists(filePath) {
|
|
12531
12808
|
if (!await fileExists3(filePath)) return "";
|
|
12532
|
-
return
|
|
12809
|
+
return fs20.readFile(filePath, "utf8");
|
|
12533
12810
|
}
|
|
12534
12811
|
async function changeSearchText(changeDir, classic) {
|
|
12535
12812
|
const files = ["proposal.md", "design.md", "tasks.md"];
|
|
12536
12813
|
const parts = [classic.name, classic.workflow, classic.phase];
|
|
12537
12814
|
for (const file of files) {
|
|
12538
|
-
parts.push(await readIfExists(
|
|
12815
|
+
parts.push(await readIfExists(path21.join(changeDir, file)));
|
|
12539
12816
|
}
|
|
12540
12817
|
return parts.join("\n").toLowerCase();
|
|
12541
12818
|
}
|
|
@@ -12599,19 +12876,19 @@ function diagnosticFromProjection(changeDir, name, projection) {
|
|
|
12599
12876
|
};
|
|
12600
12877
|
}
|
|
12601
12878
|
async function hasOpenSpecChangeFiles(changeDir) {
|
|
12602
|
-
return await fileExists3(
|
|
12879
|
+
return await fileExists3(path21.join(changeDir, "proposal.md")) || await fileExists3(path21.join(changeDir, "design.md")) || await fileExists3(path21.join(changeDir, "tasks.md"));
|
|
12603
12880
|
}
|
|
12604
12881
|
async function discoverActiveChanges(projectRoot2) {
|
|
12605
|
-
const changesDir =
|
|
12882
|
+
const changesDir = path21.join(projectRoot2, "openspec", "changes");
|
|
12606
12883
|
if (!await fileExists3(changesDir)) return [];
|
|
12607
12884
|
const entries = await readDir(changesDir);
|
|
12608
12885
|
const changes = [];
|
|
12609
12886
|
for (const entry2 of entries) {
|
|
12610
12887
|
if (entry2 === "archive") continue;
|
|
12611
|
-
const changeDir =
|
|
12612
|
-
const stat = await
|
|
12888
|
+
const changeDir = path21.join(changesDir, entry2);
|
|
12889
|
+
const stat = await fs20.stat(changeDir).catch(() => null);
|
|
12613
12890
|
if (!stat?.isDirectory()) continue;
|
|
12614
|
-
const hasCometState = await fileExists3(
|
|
12891
|
+
const hasCometState = await fileExists3(path21.join(changeDir, ".comet.yaml"));
|
|
12615
12892
|
if (!hasCometState) {
|
|
12616
12893
|
if (!await hasOpenSpecChangeFiles(changeDir)) continue;
|
|
12617
12894
|
const missingStateChange = {
|
|
@@ -12911,11 +13188,11 @@ var classicResumeProbeCommand = async (args) => {
|
|
|
12911
13188
|
};
|
|
12912
13189
|
|
|
12913
13190
|
// domains/comet-classic/classic-state-command.ts
|
|
12914
|
-
var
|
|
13191
|
+
var import_yaml8 = __toESM(require_dist(), 1);
|
|
12915
13192
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
12916
|
-
import { randomUUID as
|
|
12917
|
-
import { existsSync as existsSync3, promises as
|
|
12918
|
-
import
|
|
13193
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
13194
|
+
import { existsSync as existsSync3, promises as fs21 } from "fs";
|
|
13195
|
+
import path22 from "path";
|
|
12919
13196
|
init_state();
|
|
12920
13197
|
var GREEN5 = "\x1B[32m";
|
|
12921
13198
|
var RED5 = "\x1B[31m";
|
|
@@ -12928,8 +13205,10 @@ var EVENTS = CLASSIC_TRANSITION_EVENTS;
|
|
|
12928
13205
|
var MACHINE_OWNED_FIELDS = /* @__PURE__ */ new Set([
|
|
12929
13206
|
...RUN_WIRE_KEYS,
|
|
12930
13207
|
"archive_confirmation",
|
|
13208
|
+
"verify_failures",
|
|
12931
13209
|
"classic_profile",
|
|
12932
|
-
"classic_migration"
|
|
13210
|
+
"classic_migration",
|
|
13211
|
+
"bound_branch"
|
|
12933
13212
|
]);
|
|
12934
13213
|
var SETTABLE_FIELDS = new Set(
|
|
12935
13214
|
CLASSIC_WIRE_KEYS.filter((field2) => !MACHINE_OWNED_FIELDS.has(field2))
|
|
@@ -12943,7 +13222,7 @@ var FIELD_ENUMS = {
|
|
|
12943
13222
|
subagent_dispatch: ["null", "confirmed"],
|
|
12944
13223
|
tdd_mode: ["tdd", "direct"],
|
|
12945
13224
|
review_mode: ["off", "standard", "thorough"],
|
|
12946
|
-
isolation: ["branch", "worktree"],
|
|
13225
|
+
isolation: ["current", "branch", "worktree"],
|
|
12947
13226
|
verify_mode: ["light", "full"],
|
|
12948
13227
|
auto_transition: ["true", "false"],
|
|
12949
13228
|
verify_result: ["pending", "pass", "fail"],
|
|
@@ -12966,6 +13245,7 @@ var CLASSIC_FIELD_WIRE_NAMES2 = {
|
|
|
12966
13245
|
verifiedAt: "verified_at",
|
|
12967
13246
|
archiveConfirmation: "archive_confirmation",
|
|
12968
13247
|
verifyResult: "verify_result",
|
|
13248
|
+
verifyFailures: "verify_failures",
|
|
12969
13249
|
workflow: "workflow"
|
|
12970
13250
|
};
|
|
12971
13251
|
var CommandFailure = class extends Error {
|
|
@@ -13026,7 +13306,7 @@ function validateRelativePath(value, field2) {
|
|
|
13026
13306
|
}
|
|
13027
13307
|
async function exists6(file) {
|
|
13028
13308
|
try {
|
|
13029
|
-
await
|
|
13309
|
+
await fs21.access(file);
|
|
13030
13310
|
return true;
|
|
13031
13311
|
} catch (error) {
|
|
13032
13312
|
if (error.code === "ENOENT") return false;
|
|
@@ -13035,7 +13315,7 @@ async function exists6(file) {
|
|
|
13035
13315
|
}
|
|
13036
13316
|
async function nonempty3(file) {
|
|
13037
13317
|
try {
|
|
13038
|
-
return (await
|
|
13318
|
+
return (await fs21.stat(file)).size > 0;
|
|
13039
13319
|
} catch (error) {
|
|
13040
13320
|
if (error.code === "ENOENT") return false;
|
|
13041
13321
|
throw error;
|
|
@@ -13047,27 +13327,27 @@ async function changeDirectory2(name) {
|
|
|
13047
13327
|
async function readDocument2(file) {
|
|
13048
13328
|
let source;
|
|
13049
13329
|
try {
|
|
13050
|
-
source = await
|
|
13330
|
+
source = await fs21.readFile(file, "utf8");
|
|
13051
13331
|
} catch (error) {
|
|
13052
13332
|
if (error.code === "ENOENT") {
|
|
13053
13333
|
fail2(
|
|
13054
|
-
`ERROR: .comet.yaml not found at ${
|
|
13334
|
+
`ERROR: .comet.yaml not found at ${path22.relative(process.cwd(), file).replaceAll("\\", "/")}`
|
|
13055
13335
|
);
|
|
13056
13336
|
}
|
|
13057
13337
|
throw error;
|
|
13058
13338
|
}
|
|
13059
|
-
const document = (0,
|
|
13339
|
+
const document = (0, import_yaml8.parseDocument)(source, { uniqueKeys: false });
|
|
13060
13340
|
if (document.errors.length > 0) fail2(`ERROR: Invalid .comet.yaml: ${document.errors[0].message}`);
|
|
13061
13341
|
return document;
|
|
13062
13342
|
}
|
|
13063
13343
|
async function atomicWrite2(file, content) {
|
|
13064
|
-
await
|
|
13065
|
-
const temporary = `${file}.${
|
|
13344
|
+
await fs21.mkdir(path22.dirname(file), { recursive: true });
|
|
13345
|
+
const temporary = `${file}.${randomUUID8()}.tmp`;
|
|
13066
13346
|
try {
|
|
13067
|
-
await
|
|
13068
|
-
await
|
|
13347
|
+
await fs21.writeFile(temporary, content, "utf8");
|
|
13348
|
+
await fs21.rename(temporary, file);
|
|
13069
13349
|
} catch (error) {
|
|
13070
|
-
await
|
|
13350
|
+
await fs21.rm(temporary, { force: true });
|
|
13071
13351
|
throw error;
|
|
13072
13352
|
}
|
|
13073
13353
|
}
|
|
@@ -13100,6 +13380,10 @@ function nullableRecordBoolean(record, field2) {
|
|
|
13100
13380
|
if (value === "false") return false;
|
|
13101
13381
|
return null;
|
|
13102
13382
|
}
|
|
13383
|
+
function nonNegativeRecordInteger(record, field2, fallback = 0) {
|
|
13384
|
+
const value = record[field2];
|
|
13385
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0 ? value : fallback;
|
|
13386
|
+
}
|
|
13103
13387
|
function sparseClassicState(record) {
|
|
13104
13388
|
const workflow = enumRecordValue(record, "workflow", PROFILES, "full");
|
|
13105
13389
|
return {
|
|
@@ -13127,7 +13411,13 @@ function sparseClassicState(record) {
|
|
|
13127
13411
|
["off", "standard", "thorough"],
|
|
13128
13412
|
null
|
|
13129
13413
|
),
|
|
13130
|
-
isolation: enumRecordValue(
|
|
13414
|
+
isolation: enumRecordValue(
|
|
13415
|
+
record,
|
|
13416
|
+
"isolation",
|
|
13417
|
+
["current", "branch", "worktree"],
|
|
13418
|
+
null
|
|
13419
|
+
),
|
|
13420
|
+
boundBranch: nullableRecordString(record, "bound_branch"),
|
|
13131
13421
|
verifyMode: enumRecordValue(record, "verify_mode", ["light", "full"], null),
|
|
13132
13422
|
autoTransition: nullableRecordBoolean(record, "auto_transition"),
|
|
13133
13423
|
baseRef: nullableRecordString(record, "base_ref"),
|
|
@@ -13139,6 +13429,7 @@ function sparseClassicState(record) {
|
|
|
13139
13429
|
["pending", "pass", "fail"],
|
|
13140
13430
|
"pending"
|
|
13141
13431
|
),
|
|
13432
|
+
verifyFailures: nonNegativeRecordInteger(record, "verify_failures"),
|
|
13142
13433
|
verificationReport: nullableRecordString(record, "verification_report"),
|
|
13143
13434
|
branchStatus: enumRecordValue(record, "branch_status", ["pending", "handled"], null),
|
|
13144
13435
|
createdAt: nullableRecordString(record, "created_at"),
|
|
@@ -13199,7 +13490,7 @@ async function stateFile(name) {
|
|
|
13199
13490
|
const change = await changeDirectory2(name);
|
|
13200
13491
|
return {
|
|
13201
13492
|
...change,
|
|
13202
|
-
file:
|
|
13493
|
+
file: path22.join(change.directory, ".comet.yaml")
|
|
13203
13494
|
};
|
|
13204
13495
|
}
|
|
13205
13496
|
async function readField3(name, field2) {
|
|
@@ -13217,7 +13508,7 @@ async function readField3(name, field2) {
|
|
|
13217
13508
|
return scalar(value);
|
|
13218
13509
|
}
|
|
13219
13510
|
function parsedValue(field2, value) {
|
|
13220
|
-
const document = (0,
|
|
13511
|
+
const document = (0, import_yaml8.parseDocument)(`${field2}: ${value}
|
|
13221
13512
|
`);
|
|
13222
13513
|
if (document.errors.length > 0) fail2(`ERROR: Invalid value: '${value}'`);
|
|
13223
13514
|
return document.get(field2);
|
|
@@ -13252,7 +13543,35 @@ async function setField2(output, name, field2, value, options = {}) {
|
|
|
13252
13543
|
validateSetValue(field2, value);
|
|
13253
13544
|
const { file, directory } = await stateFile(name);
|
|
13254
13545
|
const document = await readDocument2(file);
|
|
13546
|
+
const previousRecord = document.toJS() ?? {};
|
|
13255
13547
|
document.set(field2, parsedValue(field2, value));
|
|
13548
|
+
if (field2 === "isolation") {
|
|
13549
|
+
if (requiresBranchBinding(value)) {
|
|
13550
|
+
const previousIsolation = typeof previousRecord.isolation === "string" ? previousRecord.isolation : null;
|
|
13551
|
+
const existing = previousRecord.bound_branch;
|
|
13552
|
+
const alreadyBound = typeof existing === "string" && existing !== "";
|
|
13553
|
+
if (!alreadyBound || previousIsolation !== value) {
|
|
13554
|
+
const currentBranch = liveGitBranch(process.cwd());
|
|
13555
|
+
const verdict = evaluateBranchBinding({
|
|
13556
|
+
isolation: value,
|
|
13557
|
+
boundBranch: null,
|
|
13558
|
+
currentBranch,
|
|
13559
|
+
gitWorkTree: currentBranch === null ? isGitWorkTree(process.cwd()) : true
|
|
13560
|
+
});
|
|
13561
|
+
if (verdict.status === "needs-heal") {
|
|
13562
|
+
document.set("bound_branch", verdict.branch);
|
|
13563
|
+
} else if (verdict.status === "unbound-detached") {
|
|
13564
|
+
fail2(
|
|
13565
|
+
`ERROR: cannot bind isolation=${value} while HEAD is detached; checkout a branch first`
|
|
13566
|
+
);
|
|
13567
|
+
} else {
|
|
13568
|
+
document.set("bound_branch", null);
|
|
13569
|
+
}
|
|
13570
|
+
}
|
|
13571
|
+
} else {
|
|
13572
|
+
document.set("bound_branch", null);
|
|
13573
|
+
}
|
|
13574
|
+
}
|
|
13256
13575
|
const run = await readRunState(directory);
|
|
13257
13576
|
const projection = parseClassicStateDocument(document.toJS(), run);
|
|
13258
13577
|
if (projection.run) {
|
|
@@ -13302,10 +13621,10 @@ async function init(output, name, workflow) {
|
|
|
13302
13621
|
validateEnum(workflow, PROFILES);
|
|
13303
13622
|
const { file, label, directory } = await stateFile(name);
|
|
13304
13623
|
if (await exists6(file)) fail2(`ERROR: .comet.yaml already exists at ${label}/.comet.yaml`);
|
|
13305
|
-
await
|
|
13624
|
+
await fs21.mkdir(directory, { recursive: true });
|
|
13306
13625
|
const preset = workflow !== "full";
|
|
13307
13626
|
const reviewMode = preset ? "off" : await reviewModeDefault();
|
|
13308
|
-
const document = new
|
|
13627
|
+
const document = new import_yaml8.Document({
|
|
13309
13628
|
workflow,
|
|
13310
13629
|
language: await projectLanguageDefault(),
|
|
13311
13630
|
phase: "open",
|
|
@@ -13315,13 +13634,14 @@ async function init(output, name, workflow) {
|
|
|
13315
13634
|
subagent_dispatch: null,
|
|
13316
13635
|
tdd_mode: preset ? "direct" : null,
|
|
13317
13636
|
review_mode: reviewMode,
|
|
13318
|
-
isolation:
|
|
13637
|
+
isolation: null,
|
|
13319
13638
|
verify_mode: preset ? "light" : null,
|
|
13320
13639
|
auto_transition: await autoTransition() === "true",
|
|
13321
13640
|
base_ref: gitOutput(["rev-parse", "--verify", "HEAD"]),
|
|
13322
13641
|
design_doc: null,
|
|
13323
13642
|
plan: null,
|
|
13324
13643
|
verify_result: "pending",
|
|
13644
|
+
verify_failures: 0,
|
|
13325
13645
|
verification_report: null,
|
|
13326
13646
|
branch_status: "pending",
|
|
13327
13647
|
created_at: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
|
|
@@ -13346,9 +13666,10 @@ async function requireBuildDecisions(name) {
|
|
|
13346
13666
|
const subagentDispatch = await readField3(name, "subagent_dispatch");
|
|
13347
13667
|
const tddMode = await readField3(name, "tdd_mode");
|
|
13348
13668
|
const reviewMode = await readField3(name, "review_mode");
|
|
13349
|
-
|
|
13669
|
+
const allowedIsolation = ["current", "branch", "worktree"];
|
|
13670
|
+
if (!allowedIsolation.includes(isolation)) {
|
|
13350
13671
|
fail2(
|
|
13351
|
-
`ERROR: Cannot transition '${name}': isolation must be branch or worktree, got '${isolation || "null"}'`
|
|
13672
|
+
`ERROR: Cannot transition '${name}': isolation must be current, branch, or worktree, got '${isolation || "null"}'`
|
|
13352
13673
|
);
|
|
13353
13674
|
}
|
|
13354
13675
|
if (!["subagent-driven-development", "executing-plans", "direct"].includes(buildMode)) {
|
|
@@ -13381,13 +13702,13 @@ async function requireOpenArtifacts(name) {
|
|
|
13381
13702
|
const { directory } = await stateFile(name);
|
|
13382
13703
|
const workflow = await readField3(name, "workflow");
|
|
13383
13704
|
for (const artifact of ["proposal.md", "tasks.md"]) {
|
|
13384
|
-
if (!await nonempty3(
|
|
13705
|
+
if (!await nonempty3(path22.join(directory, artifact))) {
|
|
13385
13706
|
fail2(
|
|
13386
13707
|
`ERROR: Cannot transition '${name}': ${artifact} must exist and be non-empty before leaving open`
|
|
13387
13708
|
);
|
|
13388
13709
|
}
|
|
13389
13710
|
}
|
|
13390
|
-
if (workflow === "full" && !await nonempty3(
|
|
13711
|
+
if (workflow === "full" && !await nonempty3(path22.join(directory, "design.md"))) {
|
|
13391
13712
|
fail2(
|
|
13392
13713
|
`ERROR: Cannot transition '${name}': design.md must exist and be non-empty before leaving open`
|
|
13393
13714
|
);
|
|
@@ -13395,14 +13716,14 @@ async function requireOpenArtifacts(name) {
|
|
|
13395
13716
|
}
|
|
13396
13717
|
async function requireDesignEvidence(name) {
|
|
13397
13718
|
const designDoc = await readField3(name, "design_doc");
|
|
13398
|
-
if (!designDoc || designDoc === "null" || !await nonempty3(
|
|
13719
|
+
if (!designDoc || designDoc === "null" || !await nonempty3(path22.resolve(designDoc))) {
|
|
13399
13720
|
fail2(
|
|
13400
13721
|
`ERROR: Cannot transition '${name}': design_doc must point to an existing Design Doc before leaving design`
|
|
13401
13722
|
);
|
|
13402
13723
|
}
|
|
13403
13724
|
}
|
|
13404
13725
|
async function writeSparseTransitionEffects(directory, effects) {
|
|
13405
|
-
const file =
|
|
13726
|
+
const file = path22.join(directory, ".comet.yaml");
|
|
13406
13727
|
const document = await readDocument2(file);
|
|
13407
13728
|
for (const effect of effects) {
|
|
13408
13729
|
const field2 = wireField2(effect.field);
|
|
@@ -13417,7 +13738,7 @@ async function applyTransitionEvent(output, name, event) {
|
|
|
13417
13738
|
let sparse = false;
|
|
13418
13739
|
if (!classic) {
|
|
13419
13740
|
if (projection.run) fail2("ERROR: Classic state projection is missing");
|
|
13420
|
-
const document = await readDocument2(
|
|
13741
|
+
const document = await readDocument2(path22.join(directory, ".comet.yaml"));
|
|
13421
13742
|
classic = sparseClassicState(document.toJS());
|
|
13422
13743
|
sparse = true;
|
|
13423
13744
|
}
|
|
@@ -13464,14 +13785,11 @@ async function transition(output, name, event) {
|
|
|
13464
13785
|
} else if (event === "verify-pass") {
|
|
13465
13786
|
await requirePhase(name, "verify");
|
|
13466
13787
|
const report = await readField3(name, "verification_report");
|
|
13467
|
-
if (!report || !await exists6(
|
|
13788
|
+
if (!report || !await exists6(path22.resolve(report))) {
|
|
13468
13789
|
fail2(
|
|
13469
13790
|
`ERROR: Cannot transition '${name}': verification_report must point to an existing report file`
|
|
13470
13791
|
);
|
|
13471
13792
|
}
|
|
13472
|
-
if (await readField3(name, "branch_status") !== "handled") {
|
|
13473
|
-
fail2(`ERROR: Cannot transition '${name}': branch_status must be handled`);
|
|
13474
|
-
}
|
|
13475
13793
|
} else if (event === "verify-fail") {
|
|
13476
13794
|
await requirePhase(name, "verify");
|
|
13477
13795
|
} else if (event === "archive-confirm") {
|
|
@@ -13531,9 +13849,9 @@ async function next(output, name) {
|
|
|
13531
13849
|
async function taskCheckoff(output, taskFile, taskText) {
|
|
13532
13850
|
validateRelativePath(taskFile, "task file");
|
|
13533
13851
|
if (!taskText) fail2("ERROR: Task text cannot be empty");
|
|
13534
|
-
const file =
|
|
13852
|
+
const file = path22.resolve(taskFile);
|
|
13535
13853
|
if (!await exists6(file)) fail2(`ERROR: Task file not found: ${taskFile}`);
|
|
13536
|
-
const lines = (await
|
|
13854
|
+
const lines = (await fs21.readFile(file, "utf8")).split(/\r?\n/u);
|
|
13537
13855
|
const matches = lines.filter(
|
|
13538
13856
|
(line) => [`- [ ] ${taskText}`, `- [x] ${taskText}`, `- [X] ${taskText}`].includes(line)
|
|
13539
13857
|
);
|
|
@@ -13571,21 +13889,21 @@ async function check2(output, name, phase) {
|
|
|
13571
13889
|
designDoc ? `design_doc=${designDoc} (expected: empty/null)` : "design_doc is empty/null"
|
|
13572
13890
|
);
|
|
13573
13891
|
for (const artifact of ["proposal.md", "design.md", "tasks.md"]) {
|
|
13574
|
-
(await nonempty3(
|
|
13575
|
-
`${artifact} ${await nonempty3(
|
|
13892
|
+
(await nonempty3(path22.join(directory, artifact)) ? pass2 : reject)(
|
|
13893
|
+
`${artifact} ${await nonempty3(path22.join(directory, artifact)) ? "non-empty" : "missing or empty"}`
|
|
13576
13894
|
);
|
|
13577
13895
|
}
|
|
13578
13896
|
} else if (phase === "build") {
|
|
13579
13897
|
const workflow = await readField3(name, "workflow");
|
|
13580
13898
|
const designDoc = await readField3(name, "design_doc");
|
|
13581
13899
|
if (workflow === "full") {
|
|
13582
|
-
(designDoc && designDoc !== "null" && await exists6(
|
|
13900
|
+
(designDoc && designDoc !== "null" && await exists6(path22.resolve(designDoc)) ? pass2 : reject)(`design_doc=${designDoc} (expected: non-null and file exists)`);
|
|
13583
13901
|
} else {
|
|
13584
13902
|
pass2(`workflow=${workflow} (design_doc not required)`);
|
|
13585
13903
|
}
|
|
13586
13904
|
for (const artifact of ["proposal.md", "tasks.md"]) {
|
|
13587
|
-
(await nonempty3(
|
|
13588
|
-
`${artifact} ${await nonempty3(
|
|
13905
|
+
(await nonempty3(path22.join(directory, artifact)) ? pass2 : reject)(
|
|
13906
|
+
`${artifact} ${await nonempty3(path22.join(directory, artifact)) ? "non-empty" : "missing or empty"}`
|
|
13589
13907
|
);
|
|
13590
13908
|
}
|
|
13591
13909
|
} else if (phase === "verify") {
|
|
@@ -13598,6 +13916,29 @@ async function check2(output, name, phase) {
|
|
|
13598
13916
|
const archived = await readField3(name, "archived");
|
|
13599
13917
|
(archived !== "true" ? pass2 : reject)(`archived=${archived} (expected: not true)`);
|
|
13600
13918
|
}
|
|
13919
|
+
const binding = await resolveBranchBinding(directory, { heal: true, cwd: process.cwd() });
|
|
13920
|
+
if (binding.bindingRequired) {
|
|
13921
|
+
switch (binding.status) {
|
|
13922
|
+
case "drift":
|
|
13923
|
+
reject(driftBlockedMessage(name, binding.boundBranch, binding.currentBranch));
|
|
13924
|
+
break;
|
|
13925
|
+
case "unbound-detached":
|
|
13926
|
+
reject(unboundDetachedMessage(name));
|
|
13927
|
+
break;
|
|
13928
|
+
case "healed":
|
|
13929
|
+
pass2(`bound_branch lazily set to ${binding.branch}`);
|
|
13930
|
+
break;
|
|
13931
|
+
case "needs-heal":
|
|
13932
|
+
case "ok":
|
|
13933
|
+
case "not-applicable":
|
|
13934
|
+
pass2("bound_branch matches current branch");
|
|
13935
|
+
break;
|
|
13936
|
+
default: {
|
|
13937
|
+
const exhaustive = binding;
|
|
13938
|
+
throw new Error(`unhandled branch binding status: ${JSON.stringify(exhaustive)}`);
|
|
13939
|
+
}
|
|
13940
|
+
}
|
|
13941
|
+
}
|
|
13601
13942
|
output.stdout.push("");
|
|
13602
13943
|
if (blocked2) {
|
|
13603
13944
|
output.stderr.push(red4("BLOCKED — fix failing checks before proceeding"));
|
|
@@ -13607,7 +13948,7 @@ async function check2(output, name, phase) {
|
|
|
13607
13948
|
}
|
|
13608
13949
|
function fieldStatus(field2, value, file) {
|
|
13609
13950
|
if (!value || value === "null") return ` - ${field2}: PENDING`;
|
|
13610
|
-
if (file && !existsSync3(
|
|
13951
|
+
if (file && !existsSync3(path22.resolve(file))) {
|
|
13611
13952
|
return ` - ${field2}: BROKEN (path ${value} does not exist)`;
|
|
13612
13953
|
}
|
|
13613
13954
|
return ` - ${field2}: DONE (${value})`;
|
|
@@ -13616,7 +13957,7 @@ async function recoverOpen(output, directory) {
|
|
|
13616
13957
|
output.stdout.push(" Artifacts:");
|
|
13617
13958
|
let complete = 0;
|
|
13618
13959
|
for (const artifact of ["proposal.md", "design.md", "tasks.md"]) {
|
|
13619
|
-
const done = await nonempty3(
|
|
13960
|
+
const done = await nonempty3(path22.join(directory, artifact));
|
|
13620
13961
|
if (done) complete += 1;
|
|
13621
13962
|
output.stdout.push(` - ${artifact}: ${done ? "DONE" : "PENDING"}`);
|
|
13622
13963
|
}
|
|
@@ -13629,7 +13970,7 @@ async function recoverDesign(output, name, directory) {
|
|
|
13629
13970
|
output.stdout.push(" Artifacts:");
|
|
13630
13971
|
for (const artifact of ["proposal.md", "design.md", "tasks.md"]) {
|
|
13631
13972
|
output.stdout.push(
|
|
13632
|
-
` - ${artifact}: ${await nonempty3(
|
|
13973
|
+
` - ${artifact}: ${await nonempty3(path22.join(directory, artifact)) ? "DONE" : "MISSING (unexpected in design phase)"}`
|
|
13633
13974
|
);
|
|
13634
13975
|
}
|
|
13635
13976
|
const handoff = await readField3(name, "handoff_context");
|
|
@@ -13643,11 +13984,11 @@ async function recoverDesign(output, name, directory) {
|
|
|
13643
13984
|
fieldStatus("design_doc", design, design),
|
|
13644
13985
|
""
|
|
13645
13986
|
);
|
|
13646
|
-
if (design && design !== "null" && await exists6(
|
|
13987
|
+
if (design && design !== "null" && await exists6(path22.resolve(design))) {
|
|
13647
13988
|
output.stdout.push(
|
|
13648
13989
|
"Recovery action: Design Doc already created and linked. Run guard to transition to build."
|
|
13649
13990
|
);
|
|
13650
|
-
} else if (handoff && handoff !== "null" && await exists6(
|
|
13991
|
+
} else if (handoff && handoff !== "null" && await exists6(path22.resolve(handoff))) {
|
|
13651
13992
|
output.stdout.push(
|
|
13652
13993
|
"Recovery action: Handoff generated but Design Doc not yet created. Resume from brainstorming confirmation (Step 1c)."
|
|
13653
13994
|
);
|
|
@@ -13677,7 +14018,7 @@ async function recoverBuild(output, name, directory, workflow) {
|
|
|
13677
14018
|
decisions.push(fieldStatus("subagent_dispatch", subagentDispatch));
|
|
13678
14019
|
}
|
|
13679
14020
|
output.stdout.push(...decisions, "", " Plan:", fieldStatus("plan", plan, plan), "");
|
|
13680
|
-
const tasks =
|
|
14021
|
+
const tasks = path22.join(directory, "tasks.md");
|
|
13681
14022
|
if (!await exists6(tasks)) {
|
|
13682
14023
|
output.stdout.push(
|
|
13683
14024
|
" Tasks: tasks.md MISSING",
|
|
@@ -13686,14 +14027,14 @@ async function recoverBuild(output, name, directory, workflow) {
|
|
|
13686
14027
|
);
|
|
13687
14028
|
return;
|
|
13688
14029
|
}
|
|
13689
|
-
const lines = (await
|
|
14030
|
+
const lines = (await fs21.readFile(tasks, "utf8")).split(/\r?\n/u);
|
|
13690
14031
|
const total = lines.filter((line) => /^\s*- \[[ xX]\] /u.test(line)).length;
|
|
13691
14032
|
const done = lines.filter((line) => /^\s*- \[[xX]\] /u.test(line)).length;
|
|
13692
14033
|
const pending = total - done;
|
|
13693
14034
|
let planTotal = 0;
|
|
13694
14035
|
let planDone = 0;
|
|
13695
|
-
if (plan && plan !== "null" && await exists6(
|
|
13696
|
-
const planLines = (await
|
|
14036
|
+
if (plan && plan !== "null" && await exists6(path22.resolve(plan))) {
|
|
14037
|
+
const planLines = (await fs21.readFile(path22.resolve(plan), "utf8")).split(/\r?\n/u);
|
|
13697
14038
|
planTotal = planLines.filter((line) => /^\s*- \[[ xX]\] /u.test(line)).length;
|
|
13698
14039
|
planDone = planLines.filter((line) => /^\s*- \[[xX]\] /u.test(line)).length;
|
|
13699
14040
|
}
|
|
@@ -13731,7 +14072,7 @@ function resolveBuildRecoveryAction(workflow, isolation, buildMode, pause, subag
|
|
|
13731
14072
|
}
|
|
13732
14073
|
if (pause === "plan-ready") {
|
|
13733
14074
|
if (buildMode === "subagent-driven-development" && (pending > 0 || planPending > 0)) {
|
|
13734
|
-
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.";
|
|
14075
|
+
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.";
|
|
13735
14076
|
}
|
|
13736
14077
|
if (pending > 0 || planPending > 0) {
|
|
13737
14078
|
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.";
|
|
@@ -13752,13 +14093,13 @@ function resolveBuildRecoveryAction(workflow, isolation, buildMode, pause, subag
|
|
|
13752
14093
|
}
|
|
13753
14094
|
if (pending > 0) {
|
|
13754
14095
|
if (buildMode === "subagent-driven-development") {
|
|
13755
|
-
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.";
|
|
14096
|
+
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.";
|
|
13756
14097
|
}
|
|
13757
14098
|
return "Recovery action: Read tasks.md and continue from first unchecked task.";
|
|
13758
14099
|
}
|
|
13759
14100
|
if (planPending > 0) {
|
|
13760
14101
|
if (buildMode === "subagent-driven-development") {
|
|
13761
|
-
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.";
|
|
14102
|
+
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.";
|
|
13762
14103
|
}
|
|
13763
14104
|
return "Recovery action: Read the Superpowers plan and continue from the first unchecked plan task.";
|
|
13764
14105
|
}
|
|
@@ -13766,17 +14107,19 @@ function resolveBuildRecoveryAction(workflow, isolation, buildMode, pause, subag
|
|
|
13766
14107
|
}
|
|
13767
14108
|
async function recoverVerify(output, name) {
|
|
13768
14109
|
const result5 = await readField3(name, "verify_result");
|
|
14110
|
+
const failures = await readField3(name, "verify_failures");
|
|
13769
14111
|
const mode = await readField3(name, "verify_mode");
|
|
13770
14112
|
const report = await readField3(name, "verification_report");
|
|
13771
14113
|
const branch = await readField3(name, "branch_status");
|
|
13772
14114
|
output.stdout.push(
|
|
13773
14115
|
" Verification:",
|
|
13774
14116
|
fieldStatus("verify_result", result5),
|
|
14117
|
+
` - verify_failures: ${failures || "0"}`,
|
|
13775
14118
|
fieldStatus("verify_mode", mode),
|
|
13776
14119
|
fieldStatus("verification_report", report, report),
|
|
13777
|
-
|
|
14120
|
+
branch === "handled" ? " - branch_status: LEGACY (handled before archive; archive still owns final closure)" : " - branch_status: DEFERRED (handled after the archive commit)",
|
|
13778
14121
|
"",
|
|
13779
|
-
result5 === "pass"
|
|
14122
|
+
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."
|
|
13780
14123
|
);
|
|
13781
14124
|
}
|
|
13782
14125
|
async function recoverArchive(output, name) {
|
|
@@ -13822,19 +14165,19 @@ async function scale(output, name) {
|
|
|
13822
14165
|
validateChangeName4(name);
|
|
13823
14166
|
const { file, directory, label } = await stateFile(name);
|
|
13824
14167
|
if (!await exists6(file)) fail2(`ERROR: .comet.yaml not found at ${label}/.comet.yaml`);
|
|
13825
|
-
const tasksFile =
|
|
13826
|
-
const taskCount = await exists6(tasksFile) ? (await
|
|
13827
|
-
const specs =
|
|
14168
|
+
const tasksFile = path22.join(directory, "tasks.md");
|
|
14169
|
+
const taskCount = await exists6(tasksFile) ? (await fs21.readFile(tasksFile, "utf8")).split(/\r?\n/u).filter((line) => /^- \[/u.test(line)).length : 0;
|
|
14170
|
+
const specs = path22.join(directory, "specs");
|
|
13828
14171
|
let deltaSpecs = 0;
|
|
13829
14172
|
if (await exists6(specs)) {
|
|
13830
|
-
for (const entry2 of await
|
|
13831
|
-
if (await exists6(
|
|
14173
|
+
for (const entry2 of await fs21.readdir(specs)) {
|
|
14174
|
+
if (await exists6(path22.join(specs, entry2, "spec.md"))) deltaSpecs += 1;
|
|
13832
14175
|
}
|
|
13833
14176
|
}
|
|
13834
14177
|
const plan = await readField3(name, "plan");
|
|
13835
14178
|
let baseRef = "";
|
|
13836
|
-
if (plan && plan !== "null" && await exists6(
|
|
13837
|
-
const match = (await
|
|
14179
|
+
if (plan && plan !== "null" && await exists6(path22.resolve(plan))) {
|
|
14180
|
+
const match = (await fs21.readFile(path22.resolve(plan), "utf8")).match(/^base-ref:\s*(.+)$/mu);
|
|
13838
14181
|
baseRef = match?.[1].trim() ?? "";
|
|
13839
14182
|
}
|
|
13840
14183
|
if (!baseRef) baseRef = await readField3(name, "base_ref");
|
|
@@ -13914,15 +14257,42 @@ async function selectChange(output, name) {
|
|
|
13914
14257
|
validateChangeName4(name);
|
|
13915
14258
|
try {
|
|
13916
14259
|
const selection = await selectCurrentChange(process.cwd(), name);
|
|
14260
|
+
const boundBranch = await readField3(name, "bound_branch");
|
|
14261
|
+
const bound = boundBranch && boundBranch !== "null" ? boundBranch : null;
|
|
13917
14262
|
output.stderr.push(
|
|
13918
|
-
green4(
|
|
13919
|
-
`[SELECTED] current change: ${selection.change}${selection.branch ? ` (branch: ${selection.branch})` : ""}`
|
|
13920
|
-
)
|
|
14263
|
+
green4(`[SELECTED] current change: ${selection.change}${bound ? ` (branch: ${bound})` : ""}`)
|
|
13921
14264
|
);
|
|
13922
14265
|
} catch (error) {
|
|
13923
14266
|
fail2(`ERROR: ${error instanceof Error ? error.message : String(error)}`);
|
|
13924
14267
|
}
|
|
13925
14268
|
}
|
|
14269
|
+
async function rebind(output, name) {
|
|
14270
|
+
validateChangeName4(name);
|
|
14271
|
+
const { directory } = await stateFile(name);
|
|
14272
|
+
const boundBranch = await readField3(name, "bound_branch");
|
|
14273
|
+
if (!boundBranch || boundBranch === "null") {
|
|
14274
|
+
fail2(
|
|
14275
|
+
`ERROR: '${name}' is not yet bound; use 'comet state set ${name} isolation <current|branch|worktree>' to establish the first binding`
|
|
14276
|
+
);
|
|
14277
|
+
}
|
|
14278
|
+
const branch = liveGitBranch(process.cwd());
|
|
14279
|
+
if (branch === null) {
|
|
14280
|
+
fail2("ERROR: cannot rebind while HEAD is detached; checkout a branch first");
|
|
14281
|
+
}
|
|
14282
|
+
const before = await readClassicState(directory);
|
|
14283
|
+
if (!before.classic) fail2("ERROR: Classic state projection is missing");
|
|
14284
|
+
await healBoundBranch(directory, branch);
|
|
14285
|
+
const after = { ...before.classic, boundBranch: branch };
|
|
14286
|
+
await appendClassicStateEvent(directory, {
|
|
14287
|
+
change: name,
|
|
14288
|
+
event: "rebind",
|
|
14289
|
+
source: "comet-state",
|
|
14290
|
+
from: before.classic,
|
|
14291
|
+
to: after,
|
|
14292
|
+
effects: [{ field: "boundBranch", from: boundBranch, to: branch }]
|
|
14293
|
+
});
|
|
14294
|
+
output.stderr.push(green4(`[REBIND] bound_branch: ${boundBranch} → ${branch}`));
|
|
14295
|
+
}
|
|
13926
14296
|
async function currentChange(output) {
|
|
13927
14297
|
const resolution = await resolveCurrentChange(process.cwd());
|
|
13928
14298
|
if (resolution.status === "selected") {
|
|
@@ -13976,6 +14346,9 @@ var classicStateCommand = async (args) => {
|
|
|
13976
14346
|
} else if (subcommand === "task-checkoff") {
|
|
13977
14347
|
required(rest, 2, "Usage: comet-state.mjs task-checkoff <file> <task-text>");
|
|
13978
14348
|
await taskCheckoff(output, rest[0], rest[1]);
|
|
14349
|
+
} else if (subcommand === "rebind") {
|
|
14350
|
+
requiredExact(rest, 1, "Usage: comet-state.mjs rebind <change-name>");
|
|
14351
|
+
await rebind(output, rest[0]);
|
|
13979
14352
|
} else if (subcommand === "select") {
|
|
13980
14353
|
requiredExact(rest, 1, "Usage: comet-state.mjs select <change-name>");
|
|
13981
14354
|
await selectChange(output, rest[0]);
|