cohorte 3.0.0-dev.5 → 3.0.0-dev.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/assets/manifest.json +16 -6
  2. package/assets/prompts/agents/brainstormer.md +3 -0
  3. package/assets/prompts/agents/spec-author.md +3 -0
  4. package/assets/schemas/commands.schema.json +7 -0
  5. package/assets/schemas/config.schema.json +22 -0
  6. package/dist/bundle-manifest.json +72 -48
  7. package/dist/chunks/align-ds-r_X_1aQ_.mjs +93 -0
  8. package/dist/chunks/align-ds-r_X_1aQ_.mjs.map +1 -0
  9. package/dist/chunks/audit-B4ZWQ_DI.mjs +179 -0
  10. package/dist/chunks/audit-B4ZWQ_DI.mjs.map +1 -0
  11. package/dist/chunks/brainstorm-DasVwI1D.mjs +123 -0
  12. package/dist/chunks/brainstorm-DasVwI1D.mjs.map +1 -0
  13. package/dist/chunks/build-crOld8gE.mjs +19 -0
  14. package/dist/chunks/build-crOld8gE.mjs.map +1 -0
  15. package/dist/chunks/{catalogue-B5uFqfzJ.mjs → catalogue-IaCJdfB8.mjs} +4 -4
  16. package/dist/chunks/{catalogue-B5uFqfzJ.mjs.map → catalogue-IaCJdfB8.mjs.map} +1 -1
  17. package/dist/chunks/{compose-CKo_q39e.mjs → compose-DRKjQcn0.mjs} +42 -23
  18. package/dist/chunks/compose-DRKjQcn0.mjs.map +1 -0
  19. package/dist/chunks/{config-DefHA2Lw.mjs → config-Cqu5XQoP.mjs} +2 -2
  20. package/dist/chunks/{config-DefHA2Lw.mjs.map → config-Cqu5XQoP.mjs.map} +1 -1
  21. package/dist/chunks/{contract-BkMjU1kR.mjs → contract-DjssDPpS.mjs} +66 -26
  22. package/dist/chunks/contract-DjssDPpS.mjs.map +1 -0
  23. package/dist/chunks/{diff-D_0hBwHl.mjs → diff-Qw0II0sj.mjs} +2 -2
  24. package/dist/chunks/{diff-D_0hBwHl.mjs.map → diff-Qw0II0sj.mjs.map} +1 -1
  25. package/dist/chunks/{discover-BJMwXyGL.mjs → discover-BZPolSZG.mjs} +2 -2
  26. package/dist/chunks/{discover-BJMwXyGL.mjs.map → discover-BZPolSZG.mjs.map} +1 -1
  27. package/dist/chunks/{doctor-CginW8y8.mjs → doctor-DcDFPvQM.mjs} +6 -6
  28. package/dist/chunks/{doctor-CginW8y8.mjs.map → doctor-DcDFPvQM.mjs.map} +1 -1
  29. package/dist/chunks/fix-B_oGzGVn.mjs +82 -0
  30. package/dist/chunks/fix-B_oGzGVn.mjs.map +1 -0
  31. package/dist/chunks/fleet-yWHQY2ya.mjs +297 -0
  32. package/dist/chunks/fleet-yWHQY2ya.mjs.map +1 -0
  33. package/dist/chunks/{init-CP6puNbE.mjs → init--FxxkrcT.mjs} +2 -2
  34. package/dist/chunks/{init-CP6puNbE.mjs.map → init--FxxkrcT.mjs.map} +1 -1
  35. package/dist/chunks/intake-Dd1KVXnO.mjs +12 -0
  36. package/dist/chunks/intake-Dd1KVXnO.mjs.map +1 -0
  37. package/dist/chunks/loop-BWsUpyus.mjs +212 -0
  38. package/dist/chunks/loop-BWsUpyus.mjs.map +1 -0
  39. package/dist/chunks/{migrate-DybihKzZ.mjs → migrate-DuYCAbE3.mjs} +2 -2
  40. package/dist/chunks/{migrate-DybihKzZ.mjs.map → migrate-DuYCAbE3.mjs.map} +1 -1
  41. package/dist/chunks/obsidian-BHLkXx_H.mjs +235 -0
  42. package/dist/chunks/obsidian-BHLkXx_H.mjs.map +1 -0
  43. package/dist/chunks/patch-sAgVqjpx.mjs +50 -0
  44. package/dist/chunks/patch-sAgVqjpx.mjs.map +1 -0
  45. package/dist/chunks/{policy-Clypo20s.mjs → policy-Du7WVBFX.mjs} +4 -4
  46. package/dist/chunks/{policy-Clypo20s.mjs.map → policy-Du7WVBFX.mjs.map} +1 -1
  47. package/dist/chunks/{reconcile-czNaLOP6.mjs → reconcile-BHn66X3C.mjs} +3 -3
  48. package/dist/chunks/{reconcile-czNaLOP6.mjs.map → reconcile-BHn66X3C.mjs.map} +1 -1
  49. package/dist/chunks/refactor-CoNoqLZr.mjs +153 -0
  50. package/dist/chunks/refactor-CoNoqLZr.mjs.map +1 -0
  51. package/dist/chunks/retro-Dr-2RTNN.mjs +78 -0
  52. package/dist/chunks/retro-Dr-2RTNN.mjs.map +1 -0
  53. package/dist/chunks/review-ZsBqp3vZ.mjs +35 -0
  54. package/dist/chunks/review-ZsBqp3vZ.mjs.map +1 -0
  55. package/dist/chunks/{run-CgTWYlcd.mjs → run-NGAxdsIX.mjs} +28 -5
  56. package/dist/chunks/run-NGAxdsIX.mjs.map +1 -0
  57. package/dist/chunks/{schema-C9QbUW4H.mjs → schema-Bq9qtgsd.mjs} +9 -4
  58. package/dist/chunks/schema-Bq9qtgsd.mjs.map +1 -0
  59. package/dist/chunks/ship-BizfUe62.mjs +140 -0
  60. package/dist/chunks/ship-BizfUe62.mjs.map +1 -0
  61. package/dist/chunks/spec-C2h3U64g.mjs +65 -0
  62. package/dist/chunks/spec-C2h3U64g.mjs.map +1 -0
  63. package/dist/chunks/spec-path-tptqR7W8.mjs +10 -0
  64. package/dist/chunks/spec-path-tptqR7W8.mjs.map +1 -0
  65. package/dist/chunks/{src-r0ECB1UJ.mjs → src-Bfr1yzsn.mjs} +3 -3
  66. package/dist/chunks/{src-r0ECB1UJ.mjs.map → src-Bfr1yzsn.mjs.map} +1 -1
  67. package/dist/chunks/{src-DZazJCp1.mjs → src-CPW2_Ons.mjs} +101 -17
  68. package/dist/chunks/src-CPW2_Ons.mjs.map +1 -0
  69. package/dist/chunks/{src-CmFjh4M0.mjs → src-DNsLSxgm.mjs} +2 -2
  70. package/dist/chunks/{src-CmFjh4M0.mjs.map → src-DNsLSxgm.mjs.map} +1 -1
  71. package/dist/chunks/{src-DcU4IXMe.mjs → src-DbS9gkJy.mjs} +2 -2
  72. package/dist/chunks/{src-DcU4IXMe.mjs.map → src-DbS9gkJy.mjs.map} +1 -1
  73. package/dist/chunks/{src-s7YRvk0t.mjs → src-yi5whv3n.mjs} +4 -2
  74. package/dist/chunks/src-yi5whv3n.mjs.map +1 -0
  75. package/dist/chunks/update-pipeline-Du-Gu1Pl.mjs +56 -0
  76. package/dist/chunks/update-pipeline-Du-Gu1Pl.mjs.map +1 -0
  77. package/dist/cli.mjs +29 -18
  78. package/dist/cli.mjs.map +1 -1
  79. package/package.json +1 -1
  80. package/dist/chunks/brainstorm-BwKA6yZP.mjs +0 -7
  81. package/dist/chunks/brainstorm-BwKA6yZP.mjs.map +0 -1
  82. package/dist/chunks/compose-CKo_q39e.mjs.map +0 -1
  83. package/dist/chunks/contract-BkMjU1kR.mjs.map +0 -1
  84. package/dist/chunks/fix-D4Tj8RVA.mjs +0 -18
  85. package/dist/chunks/fix-D4Tj8RVA.mjs.map +0 -1
  86. package/dist/chunks/review-BDdjb6kB.mjs +0 -17
  87. package/dist/chunks/review-BDdjb6kB.mjs.map +0 -1
  88. package/dist/chunks/run-CgTWYlcd.mjs.map +0 -1
  89. package/dist/chunks/schema-C9QbUW4H.mjs.map +0 -1
  90. package/dist/chunks/ship-DTuLGoaJ.mjs +0 -19
  91. package/dist/chunks/ship-DTuLGoaJ.mjs.map +0 -1
  92. package/dist/chunks/spec-Bq0P3jVh.mjs +0 -20
  93. package/dist/chunks/spec-Bq0P3jVh.mjs.map +0 -1
  94. package/dist/chunks/src-DZazJCp1.mjs.map +0 -1
  95. package/dist/chunks/src-s7YRvk0t.mjs.map +0 -1
@@ -0,0 +1,297 @@
1
+ import { access, mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import { join, resolve } from "node:path";
3
+ import { parse } from "yaml";
4
+ import { execFile } from "node:child_process";
5
+ import { promisify } from "node:util";
6
+ //#region src/commands/fleet/index.ts
7
+ const exec = promisify(execFile);
8
+ const exists = async (path) => {
9
+ try {
10
+ await access(path);
11
+ return true;
12
+ } catch {
13
+ return false;
14
+ }
15
+ };
16
+ const specText = async (cwd, id) => {
17
+ for (const path of [join(cwd, "specs", `${id}.md`), join(cwd, ".cohorte", "specs", `${id}.yaml`)]) try {
18
+ return await readFile(path, "utf8");
19
+ } catch {}
20
+ };
21
+ const frozen = async (cwd, id) => {
22
+ const text = await specText(cwd, id);
23
+ return text !== void 0 && (/^status:\s*frozen\s*$/mu.test(text.slice(0, 1500)) || /status:\s*frozen/u.test(text));
24
+ };
25
+ const specSurfaces = async (cwd, id, known) => {
26
+ const text = await specText(cwd, id);
27
+ if (!text) return {
28
+ keys: [...known],
29
+ inferred: true
30
+ };
31
+ const listed = known.filter((key) => new RegExp(`(?:^|[\\s"'])${key.replaceAll("-", "[^A-Za-z0-9]?")}([\\s"']|$)`, "mu").test(text));
32
+ return {
33
+ keys: listed.length ? listed : [...known],
34
+ inferred: listed.length === 0
35
+ };
36
+ };
37
+ const specDependencies = async (cwd, id, candidates) => {
38
+ const text = await specText(cwd, id);
39
+ if (!text) return [];
40
+ const declared = /* @__PURE__ */ new Set();
41
+ const matches = [...text.matchAll(/(?:^|\n)\s*(?:dependsOn|depends-on|depends on)\s*:\s*([^\n]+)/giu), ...text.matchAll(/(?:dependsOn|depends-on|depends on)\s+([^\n]+)/giu)];
42
+ for (const match of matches) for (const value of (match[1] ?? "").split(/[,\s[\]"']/u)) if (candidates.includes(value) && value !== id) declared.add(value);
43
+ return [...declared].sort();
44
+ };
45
+ function dependencyOrder(ids, dependencies) {
46
+ const result = [];
47
+ const visiting = /* @__PURE__ */ new Set();
48
+ const visited = /* @__PURE__ */ new Set();
49
+ const visit = (id) => {
50
+ if (visited.has(id)) return;
51
+ if (visiting.has(id)) return;
52
+ visiting.add(id);
53
+ for (const dependency of dependencies.get(id) ?? []) visit(dependency);
54
+ visiting.delete(id);
55
+ visited.add(id);
56
+ result.push(id);
57
+ };
58
+ for (const id of ids) visit(id);
59
+ return result;
60
+ }
61
+ const statusOf = async (cwd, id) => {
62
+ try {
63
+ const text = await readFile(join(cwd, "specs", `${id}.md`), "utf8");
64
+ return /^status:\s*(\S+)/mu.exec(text.slice(0, 1500))?.[1] ?? "unknown";
65
+ } catch {
66
+ return "missing";
67
+ }
68
+ };
69
+ const surfaces = async (cwd) => {
70
+ try {
71
+ const text = await readFile(join(cwd, "PIPELINE.md"), "utf8");
72
+ const match = /```yaml pipeline-profile\s*\n([\s\S]*?)\n```/u.exec(text);
73
+ const profile = match?.[1] ? parse(match[1]) : {};
74
+ return (Array.isArray(profile.surfaces) ? profile.surfaces : []).flatMap((x) => x && typeof x === "object" && "key" in x && typeof x.key === "string" ? [x.key] : []);
75
+ } catch {
76
+ return [];
77
+ }
78
+ };
79
+ const fleet = {
80
+ verb: "fleet",
81
+ async run(ctx, args) {
82
+ const mode = args.subVerb ?? args.positionals[0];
83
+ const ids = args.positionals.filter((value) => !value.startsWith("--") && value !== mode);
84
+ const file = join(ctx.cwd, "specs", "reports", "fleet.json");
85
+ if (mode === "plan") {
86
+ if (ids.length < 2) return 2;
87
+ const invalid = [];
88
+ for (const id of ids) if (!await frozen(ctx.cwd, id)) invalid.push(id);
89
+ if (invalid.length) {
90
+ ctx.stdio.stderr.write(`fleet requires frozen specs: ${invalid.join(", ")}\n`);
91
+ return 11;
92
+ }
93
+ const keys = await surfaces(ctx.cwd);
94
+ const dependencyMap = /* @__PURE__ */ new Map();
95
+ const featureData = Object.fromEntries(await Promise.all(ids.map(async (id) => {
96
+ const ownership = await specSurfaces(ctx.cwd, id, keys);
97
+ const dependsOn = await specDependencies(ctx.cwd, id, ids);
98
+ dependencyMap.set(id, dependsOn);
99
+ return [id, {
100
+ dependsOn,
101
+ overlap: ownership.keys,
102
+ overlapInferred: ownership.inferred,
103
+ worktree: null
104
+ }];
105
+ })));
106
+ const order = dependencyOrder(ids, dependencyMap);
107
+ const plan = {
108
+ documentVersion: 1,
109
+ generatedAt: ctx.clock.now(),
110
+ order,
111
+ surfaces: keys,
112
+ features: featureData,
113
+ status: "planned"
114
+ };
115
+ if (args.positionals.includes("--apply")) {
116
+ await mkdir(join(ctx.cwd, "specs", "reports"), { recursive: true });
117
+ if (await exists(join(ctx.cwd, ".git"))) {
118
+ const worktreeRoot = resolve(ctx.cwd, ".cohorte", "worktrees");
119
+ await mkdir(worktreeRoot, { recursive: true });
120
+ for (const id of ids) {
121
+ const worktree = resolve(worktreeRoot, id);
122
+ if (!await exists(worktree)) await exec("git", [
123
+ "worktree",
124
+ "add",
125
+ "-b",
126
+ `cohorte/${id}`,
127
+ worktree,
128
+ "HEAD"
129
+ ], { cwd: ctx.cwd });
130
+ plan.features[id].worktree = worktree;
131
+ plan.features[id].branch = `cohorte/${id}`;
132
+ }
133
+ }
134
+ await writeFile(file, `${JSON.stringify(plan, null, 2)}\n`, "utf8");
135
+ }
136
+ ctx.stdio.stdout.write(`${args.json ? JSON.stringify(plan) : `${ids.length} feature(s) planned in order: ${ids.join(" → ")}${args.positionals.includes("--apply") ? `\nwritten ${file}` : "\nre-run with --apply to persist the plan"}`}\n`);
137
+ return 0;
138
+ }
139
+ if (mode === "status") try {
140
+ const plan = JSON.parse(await readFile(file, "utf8"));
141
+ const defaultBranch = "main";
142
+ const rows = [];
143
+ for (const id of plan.order ?? []) {
144
+ const worktree = plan.features?.[id]?.worktree ?? null;
145
+ const root = worktree ?? ctx.cwd;
146
+ const status = await statusOf(root, id);
147
+ let loop = {};
148
+ let verdict = {};
149
+ try {
150
+ loop = JSON.parse(await readFile(join(root, "specs", "reports", `${id}.loop.json`), "utf8"));
151
+ } catch {}
152
+ try {
153
+ verdict = JSON.parse(await readFile(join(root, "specs", "reports", `${id}.verdict.json`), "utf8"));
154
+ } catch {}
155
+ let ahead = 0;
156
+ let behind = 0;
157
+ try {
158
+ await exec("git", [
159
+ "-C",
160
+ root,
161
+ "fetch",
162
+ "--quiet",
163
+ "origin",
164
+ defaultBranch
165
+ ]);
166
+ const { stdout } = await exec("git", [
167
+ "-C",
168
+ root,
169
+ "rev-list",
170
+ "--left-right",
171
+ "--count",
172
+ `origin/${defaultBranch}...HEAD`
173
+ ]);
174
+ const counts = stdout.trim().split(/\s+/u).map(Number);
175
+ behind = counts[0] ?? 0;
176
+ ahead = counts[1] ?? 0;
177
+ } catch {}
178
+ rows.push({
179
+ id,
180
+ status,
181
+ worktree,
182
+ loop: {
183
+ phase: loop.phase ?? null,
184
+ round: loop.round ?? null,
185
+ outcome: loop.outcome ?? null
186
+ },
187
+ blocking: verdict.blocking ?? null,
188
+ ahead,
189
+ behind
190
+ });
191
+ }
192
+ ctx.stdio.stdout.write(`${args.json ? JSON.stringify({ rows }) : rows.map((x) => `${x.id} · ${x.status} · ${x.worktree ?? "no worktree"}`).join("\n")}\n`);
193
+ return 0;
194
+ } catch {
195
+ ctx.stdio.stderr.write("fleet plan not found; run cohorte fleet plan <id> <id> --apply\n");
196
+ return 1;
197
+ }
198
+ if (mode === "sync") try {
199
+ const plan = JSON.parse(await readFile(file, "utf8"));
200
+ const defaultBranch = "main";
201
+ const requestedShipped = args.positionals.find((value) => value !== mode && !value.startsWith("--"));
202
+ const mergedBranches = await exec("git", [
203
+ "branch",
204
+ "--merged",
205
+ defaultBranch
206
+ ], { cwd: ctx.cwd }).then(({ stdout }) => stdout).catch(() => "");
207
+ const detectedShipped = (requestedShipped && (plan.order ?? []).includes(requestedShipped) ? requestedShipped : void 0) ?? (plan.order ?? []).find((id) => {
208
+ const branch = plan.features?.[id]?.branch;
209
+ return branch ? mergedBranches.split(/\r?\n/u).some((line) => line.replace(/^\*?\s*/u, "") === branch) : false;
210
+ });
211
+ if (detectedShipped) {
212
+ plan.order = (plan.order ?? []).filter((id) => id !== detectedShipped);
213
+ if (plan.features) delete plan.features[detectedShipped];
214
+ await writeFile(file, `${JSON.stringify(plan, null, 2)}\n`, "utf8");
215
+ }
216
+ const rows = detectedShipped ? [{
217
+ id: detectedShipped,
218
+ action: "shipped: removed from fleet plan"
219
+ }] : [];
220
+ for (const id of plan.order ?? []) {
221
+ const worktree = plan.features?.[id]?.worktree;
222
+ if (!worktree) {
223
+ rows.push({
224
+ id,
225
+ action: "no worktree"
226
+ });
227
+ continue;
228
+ }
229
+ try {
230
+ await exec("git", [
231
+ "-C",
232
+ worktree,
233
+ "fetch",
234
+ "--quiet",
235
+ "origin",
236
+ defaultBranch
237
+ ]);
238
+ const loopPath = join(worktree, "specs", "reports", `${id}.loop.json`);
239
+ let loop = {};
240
+ try {
241
+ loop = JSON.parse(await readFile(loopPath, "utf8"));
242
+ } catch {}
243
+ const { stdout } = await exec("git", [
244
+ "-C",
245
+ worktree,
246
+ "status",
247
+ "--porcelain"
248
+ ]);
249
+ if (stdout.trim()) rows.push({
250
+ id,
251
+ action: "rebase needed: dirty worktree",
252
+ worktree
253
+ });
254
+ else if (!loop.outcome) rows.push({
255
+ id,
256
+ action: "rebase needed: loop in flight",
257
+ worktree
258
+ });
259
+ else try {
260
+ await exec("git", [
261
+ "-C",
262
+ worktree,
263
+ "rebase",
264
+ `origin/${defaultBranch}`
265
+ ]);
266
+ rows.push({
267
+ id,
268
+ action: "rebased; fresh review required",
269
+ worktree
270
+ });
271
+ } catch (error) {
272
+ rows.push({
273
+ id,
274
+ action: `rebase conflict: ${error instanceof Error ? error.message : String(error)}`,
275
+ worktree
276
+ });
277
+ }
278
+ } catch {
279
+ rows.push({
280
+ id,
281
+ action: "worktree missing",
282
+ worktree
283
+ });
284
+ }
285
+ }
286
+ ctx.stdio.stdout.write(`${args.json ? JSON.stringify({ rows }) : rows.map((x) => `${x.id} · ${x.action}`).join("\n")}\n`);
287
+ return 0;
288
+ } catch {
289
+ return 1;
290
+ }
291
+ return 2;
292
+ }
293
+ };
294
+ //#endregion
295
+ export { fleet as default };
296
+
297
+ //# sourceMappingURL=fleet-yWHQY2ya.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fleet-yWHQY2ya.mjs","names":[],"sources":["../../../../apps/cli/src/commands/fleet/index.ts"],"sourcesContent":["import { execFile } from 'node:child_process';\nimport { access, mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { join, resolve } from 'node:path';\nimport { promisify } from 'node:util';\nimport { parse } from 'yaml';\nimport type { CommandModule } from '../../contract/index.ts';\n\nconst exec = promisify(execFile);\nconst exists = async (path: string) => {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n};\nconst specText = async (cwd: string, id: string): Promise<string | undefined> => {\n for (const path of [join(cwd, 'specs', `${id}.md`), join(cwd, '.cohorte', 'specs', `${id}.yaml`)]) {\n try {\n return await readFile(path, 'utf8');\n } catch {}\n }\n return undefined;\n};\nconst frozen = async (cwd: string, id: string) => {\n const text = await specText(cwd, id);\n return text !== undefined && (/^status:\\s*frozen\\s*$/mu.test(text.slice(0, 1500)) || /status:\\s*frozen/u.test(text));\n};\nconst specSurfaces = async (cwd: string, id: string, known: readonly string[]) => {\n const text = await specText(cwd, id);\n if (!text) return { keys: [...known], inferred: true };\n const listed = known.filter((key) =>\n new RegExp(`(?:^|[\\\\s\"'])${key.replaceAll('-', '[^A-Za-z0-9]?')}([\\\\s\"']|$)`, 'mu').test(text),\n );\n return { keys: listed.length ? listed : [...known], inferred: listed.length === 0 };\n};\nconst specDependencies = async (cwd: string, id: string, candidates: readonly string[]): Promise<string[]> => {\n const text = await specText(cwd, id);\n if (!text) return [];\n const declared = new Set<string>();\n const matches = [\n ...text.matchAll(/(?:^|\\n)\\s*(?:dependsOn|depends-on|depends on)\\s*:\\s*([^\\n]+)/giu),\n ...text.matchAll(/(?:dependsOn|depends-on|depends on)\\s+([^\\n]+)/giu),\n ];\n for (const match of matches) {\n for (const value of (match[1] ?? '').split(/[,\\s[\\]\"']/u)) {\n if (candidates.includes(value) && value !== id) declared.add(value);\n }\n }\n return [...declared].sort();\n};\nfunction dependencyOrder(ids: readonly string[], dependencies: ReadonlyMap<string, readonly string[]>): string[] {\n const result: string[] = [];\n const visiting = new Set<string>();\n const visited = new Set<string>();\n const visit = (id: string): void => {\n if (visited.has(id)) return;\n if (visiting.has(id)) return;\n visiting.add(id);\n for (const dependency of dependencies.get(id) ?? []) visit(dependency);\n visiting.delete(id);\n visited.add(id);\n result.push(id);\n };\n for (const id of ids) visit(id);\n return result;\n}\nconst statusOf = async (cwd: string, id: string) => {\n try {\n const text = await readFile(join(cwd, 'specs', `${id}.md`), 'utf8');\n return /^status:\\s*(\\S+)/mu.exec(text.slice(0, 1500))?.[1] ?? 'unknown';\n } catch {\n return 'missing';\n }\n};\nconst surfaces = async (cwd: string): Promise<string[]> => {\n try {\n const text = await readFile(join(cwd, 'PIPELINE.md'), 'utf8');\n const match = /```yaml pipeline-profile\\s*\\n([\\s\\S]*?)\\n```/u.exec(text);\n const profile = match?.[1] ? (parse(match[1]) as Record<string, unknown>) : {};\n return (Array.isArray(profile.surfaces) ? profile.surfaces : []).flatMap((x) =>\n x && typeof x === 'object' && 'key' in x && typeof x.key === 'string' ? [x.key] : [],\n );\n } catch {\n return [];\n }\n};\nconst fleet: CommandModule = {\n verb: 'fleet',\n async run(ctx, args) {\n const mode = args.subVerb ?? args.positionals[0];\n const ids = args.positionals.filter((value) => !value.startsWith('--') && value !== mode);\n const file = join(ctx.cwd, 'specs', 'reports', 'fleet.json');\n if (mode === 'plan') {\n if (ids.length < 2) return 2;\n const invalid = [];\n for (const id of ids) if (!(await frozen(ctx.cwd, id))) invalid.push(id);\n if (invalid.length) {\n ctx.stdio.stderr.write(`fleet requires frozen specs: ${invalid.join(', ')}\\n`);\n return 11;\n }\n const keys = await surfaces(ctx.cwd);\n const dependencyMap = new Map<string, string[]>();\n const featureData = Object.fromEntries(\n await Promise.all(\n ids.map(async (id) => {\n const ownership = await specSurfaces(ctx.cwd, id, keys);\n const dependsOn = await specDependencies(ctx.cwd, id, ids);\n dependencyMap.set(id, dependsOn);\n return [id, { dependsOn, overlap: ownership.keys, overlapInferred: ownership.inferred, worktree: null }];\n }),\n ),\n );\n const order = dependencyOrder(ids, dependencyMap);\n const plan = {\n documentVersion: 1,\n generatedAt: ctx.clock.now(),\n order,\n surfaces: keys,\n features: featureData,\n status: 'planned',\n };\n if (args.positionals.includes('--apply')) {\n await mkdir(join(ctx.cwd, 'specs', 'reports'), { recursive: true });\n if (await exists(join(ctx.cwd, '.git'))) {\n const worktreeRoot = resolve(ctx.cwd, '.cohorte', 'worktrees');\n await mkdir(worktreeRoot, { recursive: true });\n for (const id of ids) {\n const worktree = resolve(worktreeRoot, id);\n if (!(await exists(worktree))) {\n await exec('git', ['worktree', 'add', '-b', `cohorte/${id}`, worktree, 'HEAD'], { cwd: ctx.cwd });\n }\n (plan.features[id] as { worktree: string; branch: string }).worktree = worktree;\n (plan.features[id] as { branch: string }).branch = `cohorte/${id}`;\n }\n }\n await writeFile(file, `${JSON.stringify(plan, null, 2)}\\n`, 'utf8');\n }\n ctx.stdio.stdout.write(\n `${args.json ? JSON.stringify(plan) : `${ids.length} feature(s) planned in order: ${ids.join(' → ')}${args.positionals.includes('--apply') ? `\\nwritten ${file}` : '\\nre-run with --apply to persist the plan'}`}\\n`,\n );\n return 0;\n }\n if (mode === 'status') {\n try {\n const plan = JSON.parse(await readFile(file, 'utf8')) as {\n order?: string[];\n features?: Record<string, { worktree?: string }>;\n };\n const defaultBranch = 'main';\n const rows = [];\n for (const id of plan.order ?? []) {\n const worktree = plan.features?.[id]?.worktree ?? null;\n const root = worktree ?? ctx.cwd;\n const status = await statusOf(root, id);\n let loop: Record<string, unknown> = {};\n let verdict: Record<string, unknown> = {};\n try {\n loop = JSON.parse(await readFile(join(root, 'specs', 'reports', `${id}.loop.json`), 'utf8'));\n } catch {}\n try {\n verdict = JSON.parse(await readFile(join(root, 'specs', 'reports', `${id}.verdict.json`), 'utf8'));\n } catch {}\n let ahead = 0;\n let behind = 0;\n try {\n await exec('git', ['-C', root, 'fetch', '--quiet', 'origin', defaultBranch]);\n const { stdout } = await exec('git', [\n '-C',\n root,\n 'rev-list',\n '--left-right',\n '--count',\n `origin/${defaultBranch}...HEAD`,\n ]);\n const counts = stdout.trim().split(/\\s+/u).map(Number);\n behind = counts[0] ?? 0;\n ahead = counts[1] ?? 0;\n } catch {}\n rows.push({\n id,\n status,\n worktree,\n loop: { phase: loop.phase ?? null, round: loop.round ?? null, outcome: loop.outcome ?? null },\n blocking: verdict.blocking ?? null,\n ahead,\n behind,\n });\n }\n ctx.stdio.stdout.write(\n `${args.json ? JSON.stringify({ rows }) : rows.map((x) => `${x.id} · ${x.status} · ${x.worktree ?? 'no worktree'}`).join('\\n')}\\n`,\n );\n return 0;\n } catch {\n ctx.stdio.stderr.write('fleet plan not found; run cohorte fleet plan <id> <id> --apply\\n');\n return 1;\n }\n }\n if (mode === 'sync') {\n try {\n const plan = JSON.parse(await readFile(file, 'utf8')) as {\n order?: string[];\n features?: Record<string, { worktree?: string; branch?: string }>;\n };\n const defaultBranch = 'main';\n const requestedShipped = args.positionals.find((value) => value !== mode && !value.startsWith('--'));\n const mergedBranches = await exec('git', ['branch', '--merged', defaultBranch], { cwd: ctx.cwd })\n .then(({ stdout }) => stdout)\n .catch(() => '');\n const requested =\n requestedShipped && (plan.order ?? []).includes(requestedShipped) ? requestedShipped : undefined;\n const detectedShipped =\n requested ??\n (plan.order ?? []).find((id) => {\n const branch = plan.features?.[id]?.branch;\n return branch\n ? mergedBranches.split(/\\r?\\n/u).some((line) => line.replace(/^\\*?\\s*/u, '') === branch)\n : false;\n });\n if (detectedShipped) {\n plan.order = (plan.order ?? []).filter((id) => id !== detectedShipped);\n if (plan.features) delete plan.features[detectedShipped];\n await writeFile(file, `${JSON.stringify(plan, null, 2)}\\n`, 'utf8');\n }\n const rows: Array<{ id: string; action: string; worktree?: string }> = detectedShipped\n ? [{ id: detectedShipped, action: 'shipped: removed from fleet plan' }]\n : [];\n for (const id of plan.order ?? []) {\n const worktree = plan.features?.[id]?.worktree;\n if (!worktree) {\n rows.push({ id, action: 'no worktree' });\n continue;\n }\n try {\n await exec('git', ['-C', worktree, 'fetch', '--quiet', 'origin', defaultBranch]);\n const loopPath = join(worktree, 'specs', 'reports', `${id}.loop.json`);\n let loop: { outcome?: string } = {};\n try {\n loop = JSON.parse(await readFile(loopPath, 'utf8')) as { outcome?: string };\n } catch {}\n const { stdout } = await exec('git', ['-C', worktree, 'status', '--porcelain']);\n if (stdout.trim()) {\n rows.push({ id, action: 'rebase needed: dirty worktree', worktree });\n } else if (!loop.outcome) {\n rows.push({ id, action: 'rebase needed: loop in flight', worktree });\n } else {\n try {\n await exec('git', ['-C', worktree, 'rebase', `origin/${defaultBranch}`]);\n rows.push({ id, action: 'rebased; fresh review required', worktree });\n } catch (error) {\n rows.push({\n id,\n action: `rebase conflict: ${error instanceof Error ? error.message : String(error)}`,\n worktree,\n });\n }\n }\n } catch {\n rows.push({ id, action: 'worktree missing', worktree });\n }\n }\n ctx.stdio.stdout.write(\n `${args.json ? JSON.stringify({ rows }) : rows.map((x) => `${x.id} · ${x.action}`).join('\\n')}\\n`,\n );\n return 0;\n } catch {\n return 1;\n }\n }\n return 2;\n },\n};\nexport default fleet;\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { a as applyV2Import, i as planInit, o as exportV2, r as applyInit, s as planV2Import } from "./src-CmFjh4M0.mjs";
1
+ import { a as applyV2Import, i as planInit, o as exportV2, r as applyInit, s as planV2Import } from "./src-DNsLSxgm.mjs";
2
2
  import { t as scanRepository } from "./scan-C5uDr7Yq.mjs";
3
3
  import { resolve } from "node:path";
4
4
  //#region src/commands/init/index.ts
@@ -60,4 +60,4 @@ const init = {
60
60
  //#endregion
61
61
  export { init as default };
62
62
 
63
- //# sourceMappingURL=init-CP6puNbE.mjs.map
63
+ //# sourceMappingURL=init--FxxkrcT.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"init-CP6puNbE.mjs","names":[],"sources":["../../../../apps/cli/src/commands/init/index.ts"],"sourcesContent":["// apps/cli/src/commands/init/index.ts — DESIGN §9 verb `init` (PLAN §3 rule 3 \"later units fill\n// stub files that lie inside their owned paths; they never edit ... the registry\"). Wave-0 stub: exits with the\n// documented not-available error (spec 24 `configuration/phase-not-available`); filled by the Wave-4/5\n// unit that owns `apps/cli/src/commands/init/**`.\nimport { resolve } from 'node:path';\nimport { applyInit, applyV2Import, exportV2, planInit, planV2Import, scanRepository } from '@cohorte/project-model';\nimport type { CommandModule } from '../../contract/index.ts';\n\nconst init: CommandModule = {\n verb: 'init',\n async run(ctx, args) {\n const fromV2 = args.positionals.indexOf('--from-v2');\n const exportV2At = args.positionals.indexOf('--export-v2');\n const optionValues = new Set(\n [fromV2, exportV2At]\n .filter((index) => index !== -1)\n .map((index) => args.positionals[index + 1])\n .filter((value): value is string => value !== undefined),\n );\n const requestedRoot = args.positionals.find((value) => !value.startsWith('--') && !optionValues.has(value));\n const root = requestedRoot === undefined ? ctx.cwd : resolve(ctx.cwd, requestedRoot);\n if (fromV2 !== -1) {\n const bundle = args.positionals[fromV2 + 1];\n if (!bundle) throw new Error('configuration/import-invalid: init --from-v2 requires a bundle directory');\n const model = await scanRepository(root, { clock: ctx.clock, toolVersion: '3.0.0-v2-import' });\n const plan = await planV2Import(resolve(ctx.cwd, bundle), root, { model });\n if (!args.positionals.includes('--yes')) {\n ctx.stdio.stdout.write(`${JSON.stringify(plan, null, 2)}\\n`);\n return plan.conflicts.length ? 3 : 0;\n }\n const result = await applyV2Import(plan, {\n confirm: args.positionals.includes('--yes'),\n backupRoot: resolve(root, '..', '.cohorte-migration-backups'),\n });\n ctx.stdio.stdout.write(`${JSON.stringify(result)}\\n`);\n return 0;\n }\n if (exportV2At !== -1) {\n const destination = args.positionals[exportV2At + 1];\n if (!destination) throw new Error('configuration/import-invalid: init --export-v2 requires a destination');\n const result = await exportV2({ root, destination: resolve(ctx.cwd, destination) });\n ctx.stdio.stdout.write(`${JSON.stringify(result)}\\n`);\n return result.manifest.warnings.some((item) => item.blocking) ? 3 : 0;\n }\n const model = await scanRepository(root, { clock: ctx.clock, toolVersion: '3.0.0' });\n const plan = await planInit({ root, model, cohorteVersion: '3.0.0' });\n if (args.positionals.includes('--plan') || args.json) {\n ctx.stdio.stdout.write(`${JSON.stringify(plan)}\\n`);\n return 0;\n }\n const result = await applyInit(plan);\n ctx.stdio.stdout.write(`written: ${result.written.join(', ') || 'none'}\\n`);\n return 0;\n },\n};\n\nexport default init;\n"],"mappings":";;;;AAQA,MAAM,OAAsB;CAC1B,MAAM;CACN,MAAM,IAAI,KAAK,MAAM;EACnB,MAAM,SAAS,KAAK,YAAY,QAAQ,WAAW;EACnD,MAAM,aAAa,KAAK,YAAY,QAAQ,aAAa;EACzD,MAAM,eAAe,IAAI,IACvB,CAAC,QAAQ,UAAU,CAAC,CACjB,QAAQ,UAAU,UAAU,EAAE,CAAC,CAC/B,KAAK,UAAU,KAAK,YAAY,QAAQ,EAAE,CAAC,CAC3C,QAAQ,UAA2B,UAAU,KAAA,CAAS,CAC3D;EACA,MAAM,gBAAgB,KAAK,YAAY,MAAM,UAAU,CAAC,MAAM,WAAW,IAAI,KAAK,CAAC,aAAa,IAAI,KAAK,CAAC;EAC1G,MAAM,OAAO,kBAAkB,KAAA,IAAY,IAAI,MAAM,QAAQ,IAAI,KAAK,aAAa;EACnF,IAAI,WAAW,IAAI;GACjB,MAAM,SAAS,KAAK,YAAY,SAAS;GACzC,IAAI,CAAC,QAAQ,MAAM,IAAI,MAAM,0EAA0E;GACvG,MAAM,QAAQ,MAAM,eAAe,MAAM;IAAE,OAAO,IAAI;IAAO,aAAa;GAAkB,CAAC;GAC7F,MAAM,OAAO,MAAM,aAAa,QAAQ,IAAI,KAAK,MAAM,GAAG,MAAM,EAAE,MAAM,CAAC;GACzE,IAAI,CAAC,KAAK,YAAY,SAAS,OAAO,GAAG;IACvC,IAAI,MAAM,OAAO,MAAM,GAAG,KAAK,UAAU,MAAM,MAAM,CAAC,EAAE,GAAG;IAC3D,OAAO,KAAK,UAAU,SAAS,IAAI;GACrC;GACA,MAAM,SAAS,MAAM,cAAc,MAAM;IACvC,SAAS,KAAK,YAAY,SAAS,OAAO;IAC1C,YAAY,QAAQ,MAAM,MAAM,4BAA4B;GAC9D,CAAC;GACD,IAAI,MAAM,OAAO,MAAM,GAAG,KAAK,UAAU,MAAM,EAAE,GAAG;GACpD,OAAO;EACT;EACA,IAAI,eAAe,IAAI;GACrB,MAAM,cAAc,KAAK,YAAY,aAAa;GAClD,IAAI,CAAC,aAAa,MAAM,IAAI,MAAM,uEAAuE;GACzG,MAAM,SAAS,MAAM,SAAS;IAAE;IAAM,aAAa,QAAQ,IAAI,KAAK,WAAW;GAAE,CAAC;GAClF,IAAI,MAAM,OAAO,MAAM,GAAG,KAAK,UAAU,MAAM,EAAE,GAAG;GACpD,OAAO,OAAO,SAAS,SAAS,MAAM,SAAS,KAAK,QAAQ,IAAI,IAAI;EACtE;EACA,MAAM,QAAQ,MAAM,eAAe,MAAM;GAAE,OAAO,IAAI;GAAO,aAAa;EAAQ,CAAC;EACnF,MAAM,OAAO,MAAM,SAAS;GAAE;GAAM;GAAO,gBAAgB;EAAQ,CAAC;EACpE,IAAI,KAAK,YAAY,SAAS,QAAQ,KAAK,KAAK,MAAM;GACpD,IAAI,MAAM,OAAO,MAAM,GAAG,KAAK,UAAU,IAAI,EAAE,GAAG;GAClD,OAAO;EACT;EACA,MAAM,SAAS,MAAM,UAAU,IAAI;EACnC,IAAI,MAAM,OAAO,MAAM,YAAY,OAAO,QAAQ,KAAK,IAAI,KAAK,OAAO,GAAG;EAC1E,OAAO;CACT;AACF"}
1
+ {"version":3,"file":"init--FxxkrcT.mjs","names":[],"sources":["../../../../apps/cli/src/commands/init/index.ts"],"sourcesContent":["// apps/cli/src/commands/init/index.ts — DESIGN §9 verb `init` (PLAN §3 rule 3 \"later units fill\n// stub files that lie inside their owned paths; they never edit ... the registry\"). Wave-0 stub: exits with the\n// documented not-available error (spec 24 `configuration/phase-not-available`); filled by the Wave-4/5\n// unit that owns `apps/cli/src/commands/init/**`.\nimport { resolve } from 'node:path';\nimport { applyInit, applyV2Import, exportV2, planInit, planV2Import, scanRepository } from '@cohorte/project-model';\nimport type { CommandModule } from '../../contract/index.ts';\n\nconst init: CommandModule = {\n verb: 'init',\n async run(ctx, args) {\n const fromV2 = args.positionals.indexOf('--from-v2');\n const exportV2At = args.positionals.indexOf('--export-v2');\n const optionValues = new Set(\n [fromV2, exportV2At]\n .filter((index) => index !== -1)\n .map((index) => args.positionals[index + 1])\n .filter((value): value is string => value !== undefined),\n );\n const requestedRoot = args.positionals.find((value) => !value.startsWith('--') && !optionValues.has(value));\n const root = requestedRoot === undefined ? ctx.cwd : resolve(ctx.cwd, requestedRoot);\n if (fromV2 !== -1) {\n const bundle = args.positionals[fromV2 + 1];\n if (!bundle) throw new Error('configuration/import-invalid: init --from-v2 requires a bundle directory');\n const model = await scanRepository(root, { clock: ctx.clock, toolVersion: '3.0.0-v2-import' });\n const plan = await planV2Import(resolve(ctx.cwd, bundle), root, { model });\n if (!args.positionals.includes('--yes')) {\n ctx.stdio.stdout.write(`${JSON.stringify(plan, null, 2)}\\n`);\n return plan.conflicts.length ? 3 : 0;\n }\n const result = await applyV2Import(plan, {\n confirm: args.positionals.includes('--yes'),\n backupRoot: resolve(root, '..', '.cohorte-migration-backups'),\n });\n ctx.stdio.stdout.write(`${JSON.stringify(result)}\\n`);\n return 0;\n }\n if (exportV2At !== -1) {\n const destination = args.positionals[exportV2At + 1];\n if (!destination) throw new Error('configuration/import-invalid: init --export-v2 requires a destination');\n const result = await exportV2({ root, destination: resolve(ctx.cwd, destination) });\n ctx.stdio.stdout.write(`${JSON.stringify(result)}\\n`);\n return result.manifest.warnings.some((item) => item.blocking) ? 3 : 0;\n }\n const model = await scanRepository(root, { clock: ctx.clock, toolVersion: '3.0.0' });\n const plan = await planInit({ root, model, cohorteVersion: '3.0.0' });\n if (args.positionals.includes('--plan') || args.json) {\n ctx.stdio.stdout.write(`${JSON.stringify(plan)}\\n`);\n return 0;\n }\n const result = await applyInit(plan);\n ctx.stdio.stdout.write(`written: ${result.written.join(', ') || 'none'}\\n`);\n return 0;\n },\n};\n\nexport default init;\n"],"mappings":";;;;AAQA,MAAM,OAAsB;CAC1B,MAAM;CACN,MAAM,IAAI,KAAK,MAAM;EACnB,MAAM,SAAS,KAAK,YAAY,QAAQ,WAAW;EACnD,MAAM,aAAa,KAAK,YAAY,QAAQ,aAAa;EACzD,MAAM,eAAe,IAAI,IACvB,CAAC,QAAQ,UAAU,CAAC,CACjB,QAAQ,UAAU,UAAU,EAAE,CAAC,CAC/B,KAAK,UAAU,KAAK,YAAY,QAAQ,EAAE,CAAC,CAC3C,QAAQ,UAA2B,UAAU,KAAA,CAAS,CAC3D;EACA,MAAM,gBAAgB,KAAK,YAAY,MAAM,UAAU,CAAC,MAAM,WAAW,IAAI,KAAK,CAAC,aAAa,IAAI,KAAK,CAAC;EAC1G,MAAM,OAAO,kBAAkB,KAAA,IAAY,IAAI,MAAM,QAAQ,IAAI,KAAK,aAAa;EACnF,IAAI,WAAW,IAAI;GACjB,MAAM,SAAS,KAAK,YAAY,SAAS;GACzC,IAAI,CAAC,QAAQ,MAAM,IAAI,MAAM,0EAA0E;GACvG,MAAM,QAAQ,MAAM,eAAe,MAAM;IAAE,OAAO,IAAI;IAAO,aAAa;GAAkB,CAAC;GAC7F,MAAM,OAAO,MAAM,aAAa,QAAQ,IAAI,KAAK,MAAM,GAAG,MAAM,EAAE,MAAM,CAAC;GACzE,IAAI,CAAC,KAAK,YAAY,SAAS,OAAO,GAAG;IACvC,IAAI,MAAM,OAAO,MAAM,GAAG,KAAK,UAAU,MAAM,MAAM,CAAC,EAAE,GAAG;IAC3D,OAAO,KAAK,UAAU,SAAS,IAAI;GACrC;GACA,MAAM,SAAS,MAAM,cAAc,MAAM;IACvC,SAAS,KAAK,YAAY,SAAS,OAAO;IAC1C,YAAY,QAAQ,MAAM,MAAM,4BAA4B;GAC9D,CAAC;GACD,IAAI,MAAM,OAAO,MAAM,GAAG,KAAK,UAAU,MAAM,EAAE,GAAG;GACpD,OAAO;EACT;EACA,IAAI,eAAe,IAAI;GACrB,MAAM,cAAc,KAAK,YAAY,aAAa;GAClD,IAAI,CAAC,aAAa,MAAM,IAAI,MAAM,uEAAuE;GACzG,MAAM,SAAS,MAAM,SAAS;IAAE;IAAM,aAAa,QAAQ,IAAI,KAAK,WAAW;GAAE,CAAC;GAClF,IAAI,MAAM,OAAO,MAAM,GAAG,KAAK,UAAU,MAAM,EAAE,GAAG;GACpD,OAAO,OAAO,SAAS,SAAS,MAAM,SAAS,KAAK,QAAQ,IAAI,IAAI;EACtE;EACA,MAAM,QAAQ,MAAM,eAAe,MAAM;GAAE,OAAO,IAAI;GAAO,aAAa;EAAQ,CAAC;EACnF,MAAM,OAAO,MAAM,SAAS;GAAE;GAAM;GAAO,gBAAgB;EAAQ,CAAC;EACpE,IAAI,KAAK,YAAY,SAAS,QAAQ,KAAK,KAAK,MAAM;GACpD,IAAI,MAAM,OAAO,MAAM,GAAG,KAAK,UAAU,IAAI,EAAE,GAAG;GAClD,OAAO;EACT;EACA,MAAM,SAAS,MAAM,UAAU,IAAI;EACnC,IAAI,MAAM,OAAO,MAAM,YAAY,OAAO,QAAQ,KAAK,IAAI,KAAK,OAAO,GAAG;EAC1E,OAAO;CACT;AACF"}
@@ -0,0 +1,12 @@
1
+ import brainstorm from "./brainstorm-DasVwI1D.mjs";
2
+ //#region src/commands/intake/index.ts
3
+ const intake = {
4
+ verb: "intake",
5
+ async run(ctx, args) {
6
+ return brainstorm.run(ctx, args);
7
+ }
8
+ };
9
+ //#endregion
10
+ export { intake as default };
11
+
12
+ //# sourceMappingURL=intake-Dd1KVXnO.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"intake-Dd1KVXnO.mjs","names":[],"sources":["../../../../apps/cli/src/commands/intake/index.ts"],"sourcesContent":["import type { CommandModule } from '../../contract/index.ts';\nimport brainstorm from '../brainstorm/index.ts';\n\nconst intake: CommandModule = {\n verb: 'intake',\n async run(ctx, args) {\n return brainstorm.run(ctx, args);\n },\n};\n\nexport default intake;\n"],"mappings":";;AAGA,MAAM,SAAwB;CAC5B,MAAM;CACN,MAAM,IAAI,KAAK,MAAM;EACnB,OAAO,WAAW,IAAI,KAAK,IAAI;CACjC;AACF"}
@@ -0,0 +1,212 @@
1
+ import { t as resolveSpecPath } from "./spec-path-tptqR7W8.mjs";
2
+ import { mkdir, readFile, stat, writeFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ //#region src/commands/loop/reducer.ts
5
+ /**
6
+ * The V2 loop reducer, kept pure so a CLI invocation and a resumed run apply
7
+ * exactly the same safety rules. A missing/invalid review never counts as a
8
+ * clean review.
9
+ */
10
+ function decideLoop(review, lastKey, round, maxRounds) {
11
+ if (!review) return {
12
+ outcome: "abort",
13
+ reason: "review-died"
14
+ };
15
+ if (review.verdict === "needs-human" && review.unreviewed.length > 0) return {
16
+ outcome: "abort",
17
+ reason: "unreviewed"
18
+ };
19
+ if (review.unreviewed.length > 0) return {
20
+ outcome: "abort",
21
+ reason: "unreviewed"
22
+ };
23
+ if (review.verdict === "needs-human" || review.deferred.length > 0) return {
24
+ outcome: "abort",
25
+ reason: "approval-required"
26
+ };
27
+ if (review.clean && review.blocking === 0) return { outcome: "ship" };
28
+ if (!Number.isInteger(review.blocking) || review.blocking < 0) return {
29
+ outcome: "abort",
30
+ reason: "no-verdict"
31
+ };
32
+ if (review.blocking === 0) return { outcome: "ship" };
33
+ const key = review.blockingItems.join("\n");
34
+ if (!key) return {
35
+ outcome: "abort",
36
+ reason: "no-verdict"
37
+ };
38
+ if (lastKey !== void 0 && key === lastKey) return {
39
+ outcome: "abort",
40
+ reason: "treading-water"
41
+ };
42
+ if (round >= maxRounds) return {
43
+ outcome: "abort",
44
+ reason: "max-rounds"
45
+ };
46
+ return {
47
+ outcome: "continue",
48
+ key
49
+ };
50
+ }
51
+ /** Extract only a closed ReviewResult from a controller/report payload. */
52
+ function readReview(value) {
53
+ if (!value || typeof value !== "object") return null;
54
+ const candidate = value;
55
+ if (candidate.verdict !== "approved" && candidate.verdict !== "findings" && candidate.verdict !== "needs-human" || !Array.isArray(candidate.unreviewed) || !Array.isArray(candidate.deferred) || !Array.isArray(candidate.blockingItems) || typeof candidate.blocking !== "number" || typeof candidate.clean !== "boolean") return null;
56
+ return candidate;
57
+ }
58
+ //#endregion
59
+ //#region src/commands/loop/index.ts
60
+ const LOOP_PHASES = [
61
+ "PREFLIGHT",
62
+ "BUILD",
63
+ "TEST",
64
+ "REVIEW",
65
+ "FIX",
66
+ "TEST",
67
+ "REVIEW",
68
+ "SHIP"
69
+ ];
70
+ const ACTIVE_STATES = /* @__PURE__ */ new Set([
71
+ "PREFLIGHT",
72
+ "BRAINSTORM",
73
+ "SPEC",
74
+ "BUILD",
75
+ "TEST",
76
+ "REVIEW",
77
+ "FIX",
78
+ "SHIP",
79
+ "SUSPENDED"
80
+ ]);
81
+ function valueAfter(values, flag) {
82
+ const index = values.indexOf(flag);
83
+ return index < 0 ? void 0 : values[index + 1];
84
+ }
85
+ async function isFreshReview(cwd, feature) {
86
+ try {
87
+ const [spec, verdict] = await Promise.all([stat(join(cwd, "specs", `${feature}.md`)), stat(join(cwd, "specs", "reports", `${feature}.verdict.json`))]);
88
+ return verdict.mtimeMs >= spec.mtimeMs;
89
+ } catch {
90
+ return false;
91
+ }
92
+ }
93
+ const loop = {
94
+ verb: "loop",
95
+ async run(ctx, args) {
96
+ const feature = args.positionals.find((value, index) => !value.startsWith("--") && args.positionals[index - 1] !== "--max-rounds" && args.positionals[index - 1] !== "--wait");
97
+ if (!feature) return 2;
98
+ const parsedMax = Number(valueAfter(args.positionals, "--max-rounds") ?? 5);
99
+ const maxRounds = Number.isInteger(parsedMax) ? Math.min(10, Math.max(1, parsedMax)) : 5;
100
+ const surfaces = (valueAfter(args.positionals, "--surfaces") ?? "").split(",").map((value) => value.trim()).filter(Boolean);
101
+ const reportPath = join(ctx.cwd, "specs", "reports", `${feature}.loop.json`);
102
+ let previous;
103
+ try {
104
+ previous = JSON.parse(await readFile(reportPath, "utf8"));
105
+ } catch {}
106
+ const round = previous?.status === "completed" && !previous.outcome ? Math.max(1, Number(previous.round) || 1) + 1 : previous?.outcome ? 1 : Math.max(1, Number(previous?.round) || 1);
107
+ const now = ctx.clock.now();
108
+ const before = {
109
+ id: feature,
110
+ round,
111
+ maxRounds,
112
+ phase: round === 1 ? "build" : "fix",
113
+ status: "running",
114
+ ...surfaces.length > 0 ? { surfaces } : previous?.surfaces ? { surfaces: previous.surfaces } : {},
115
+ ...previous?.history ? { history: previous.history } : {},
116
+ startedAt: previous?.startedAt ?? now,
117
+ updatedAt: now
118
+ };
119
+ await mkdir(join(ctx.cwd, "specs", "reports"), { recursive: true });
120
+ await writeFile(reportPath, `${JSON.stringify(before, null, 2)}\n`, "utf8");
121
+ let result;
122
+ let controllerResult = before;
123
+ if (previous?.status === "pending" && previous.runId) {
124
+ controllerResult = {
125
+ ...before,
126
+ runId: previous.runId
127
+ };
128
+ try {
129
+ const state = (await ctx.openStore().then(async (store) => {
130
+ try {
131
+ return await store.readRunTree(previous?.runId);
132
+ } finally {
133
+ await store.close();
134
+ }
135
+ })).run.state;
136
+ if (ACTIVE_STATES.has(state)) {
137
+ controllerResult = {
138
+ ...controllerResult,
139
+ phase: previous.phase ?? "build",
140
+ status: "pending"
141
+ };
142
+ await writeFile(reportPath, `${JSON.stringify(controllerResult, null, 2)}\n`, "utf8");
143
+ if (args.json) ctx.stdio.stdout.write(`${JSON.stringify({
144
+ ...controllerResult,
145
+ reportPath
146
+ })}\n`);
147
+ else ctx.stdio.stdout.write(`loop pending: ${reportPath}\n`);
148
+ return 4;
149
+ }
150
+ result = state === "COMPLETED" ? 0 : state === "CANCELLED" ? 16 : 3;
151
+ } catch {
152
+ result = 4;
153
+ }
154
+ } else {
155
+ const started = await ctx.controller.send("start", {
156
+ profile: "feature",
157
+ spec: { path: resolveSpecPath(ctx.cwd, feature) },
158
+ phases: [...LOOP_PHASES],
159
+ ...surfaces.length > 0 ? { surfaces } : previous?.surfaces ? { surfaces: previous.surfaces } : {},
160
+ withFix: true,
161
+ unattended: true
162
+ });
163
+ const runId = started.result && typeof started.result === "object" && "runId" in started.result ? String(started.result.runId) : void 0;
164
+ if (runId && started.status === "pending") await ctx.hostSpawner.spawnDetached(runId);
165
+ controllerResult = {
166
+ ...before,
167
+ ...runId ? { runId } : {}
168
+ };
169
+ result = started.status === "rejected" ? 3 : started.status === "pending" ? 4 : 0;
170
+ }
171
+ let review = readReview(result && typeof result === "object" && "result" in result ? result.result : void 0);
172
+ if (!review && result === 0 && await isFreshReview(ctx.cwd, feature)) try {
173
+ review = readReview(JSON.parse(await readFile(join(ctx.cwd, "specs", "reports", `${feature}.verdict.json`), "utf8")));
174
+ } catch {}
175
+ else if (!review && result === 0) review = null;
176
+ const decision = result === 4 ? void 0 : decideLoop(review, previous?.blockingKey, round, maxRounds);
177
+ const output = {
178
+ ...controllerResult,
179
+ status: result === 4 ? "pending" : result === 0 ? "completed" : "rejected",
180
+ phase: decision?.outcome === "ship" || decision?.outcome === "abort" ? "done" : controllerResult.phase,
181
+ ...decision?.outcome === "ship" ? { outcome: "ship" } : decision?.outcome === "abort" ? {
182
+ outcome: "abort",
183
+ reason: decision.reason
184
+ } : result !== 4 ? {
185
+ outcome: "abort",
186
+ reason: "run-rejected"
187
+ } : {},
188
+ ...decision?.outcome === "continue" ? { blockingKey: decision.key } : {},
189
+ updatedAt: ctx.clock.now(),
190
+ ...result === 4 ? {} : { history: [...controllerResult.history ?? [], {
191
+ round,
192
+ ...controllerResult.runId ? { runId: controllerResult.runId } : {},
193
+ status: result === 0 ? "completed" : "rejected",
194
+ ...decision?.outcome ? { outcome: decision.outcome } : { outcome: "abort" },
195
+ ...decision?.outcome === "abort" ? { reason: decision.reason } : result !== 0 ? { reason: "run-rejected" } : {},
196
+ ...decision?.outcome === "continue" ? { blockingKey: decision.key } : {},
197
+ updatedAt: ctx.clock.now()
198
+ }] }
199
+ };
200
+ await writeFile(reportPath, `${JSON.stringify(output, null, 2)}\n`, "utf8");
201
+ if (args.json) ctx.stdio.stdout.write(`${JSON.stringify({
202
+ ...output,
203
+ reportPath
204
+ })}\n`);
205
+ else ctx.stdio.stdout.write(`loop ${output.status}: ${reportPath}\n`);
206
+ return result;
207
+ }
208
+ };
209
+ //#endregion
210
+ export { loop as default };
211
+
212
+ //# sourceMappingURL=loop-BWsUpyus.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loop-BWsUpyus.mjs","names":[],"sources":["../../../../apps/cli/src/commands/loop/reducer.ts","../../../../apps/cli/src/commands/loop/index.ts"],"sourcesContent":["import type { ReviewResult } from '@cohorte/protocol';\n\nexport type LoopReducerDecision =\n | { outcome: 'ship' }\n | { outcome: 'continue'; key: string }\n | { outcome: 'abort'; reason: string };\n\n/**\n * The V2 loop reducer, kept pure so a CLI invocation and a resumed run apply\n * exactly the same safety rules. A missing/invalid review never counts as a\n * clean review.\n */\nexport function decideLoop(\n review: ReviewResult | null,\n lastKey: string | undefined,\n round: number,\n maxRounds: number,\n): LoopReducerDecision {\n if (!review) return { outcome: 'abort', reason: 'review-died' };\n if (review.verdict === 'needs-human' && review.unreviewed.length > 0) {\n return { outcome: 'abort', reason: 'unreviewed' };\n }\n if (review.unreviewed.length > 0) return { outcome: 'abort', reason: 'unreviewed' };\n if (review.verdict === 'needs-human' || review.deferred.length > 0) {\n return { outcome: 'abort', reason: 'approval-required' };\n }\n if (review.clean && review.blocking === 0) return { outcome: 'ship' };\n if (!Number.isInteger(review.blocking) || review.blocking < 0) {\n return { outcome: 'abort', reason: 'no-verdict' };\n }\n if (review.blocking === 0) return { outcome: 'ship' };\n const key = review.blockingItems.join('\\n');\n if (!key) return { outcome: 'abort', reason: 'no-verdict' };\n if (lastKey !== undefined && key === lastKey) return { outcome: 'abort', reason: 'treading-water' };\n if (round >= maxRounds) return { outcome: 'abort', reason: 'max-rounds' };\n return { outcome: 'continue', key };\n}\n\n/** Extract only a closed ReviewResult from a controller/report payload. */\nexport function readReview(value: unknown): ReviewResult | null {\n if (!value || typeof value !== 'object') return null;\n const candidate = value as Partial<ReviewResult>;\n if (\n (candidate.verdict !== 'approved' && candidate.verdict !== 'findings' && candidate.verdict !== 'needs-human') ||\n !Array.isArray(candidate.unreviewed) ||\n !Array.isArray(candidate.deferred) ||\n !Array.isArray(candidate.blockingItems) ||\n typeof candidate.blocking !== 'number' ||\n typeof candidate.clean !== 'boolean'\n ) {\n return null;\n }\n return candidate as ReviewResult;\n}\n","import { mkdir, readFile, stat, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { ActivePipelineState } from '@cohorte/protocol';\nimport type { CommandModule } from '../../contract/index.ts';\nimport { resolveSpecPath } from '../../project/spec-path.ts';\nimport { decideLoop, readReview } from './reducer.ts';\n\ntype LoopSnapshot = {\n id: string;\n round: number;\n maxRounds: number;\n phase: 'build' | 'review' | 'fix' | 'done';\n status: 'running' | 'pending' | 'completed' | 'rejected';\n runId?: string;\n outcome?: 'ship' | 'abort';\n reason?: string;\n blockingKey?: string;\n surfaces?: string[];\n history?: Array<{\n round: number;\n runId?: string;\n status: 'completed' | 'rejected';\n outcome?: 'ship' | 'abort' | 'continue';\n reason?: string;\n blockingKey?: string;\n updatedAt: string;\n }>;\n startedAt: string;\n updatedAt: string;\n};\n\nconst LOOP_PHASES = [\n 'PREFLIGHT',\n 'BUILD',\n 'TEST',\n 'REVIEW',\n 'FIX',\n 'TEST',\n 'REVIEW',\n 'SHIP',\n] as const satisfies readonly ActivePipelineState[];\n\nconst ACTIVE_STATES = new Set([\n 'PREFLIGHT',\n 'BRAINSTORM',\n 'SPEC',\n 'BUILD',\n 'TEST',\n 'REVIEW',\n 'FIX',\n 'SHIP',\n 'SUSPENDED',\n]);\n\nfunction valueAfter(values: readonly string[], flag: string): string | undefined {\n const index = values.indexOf(flag);\n return index < 0 ? undefined : values[index + 1];\n}\n\nasync function isFreshReview(cwd: string, feature: string): Promise<boolean> {\n try {\n const [spec, verdict] = await Promise.all([\n stat(join(cwd, 'specs', `${feature}.md`)),\n stat(join(cwd, 'specs', 'reports', `${feature}.verdict.json`)),\n ]);\n return verdict.mtimeMs >= spec.mtimeMs;\n } catch {\n return false;\n }\n}\n\nconst loop: CommandModule = {\n verb: 'loop',\n async run(ctx, args) {\n const feature = args.positionals.find(\n (value, index) =>\n !value.startsWith('--') &&\n args.positionals[index - 1] !== '--max-rounds' &&\n args.positionals[index - 1] !== '--wait',\n );\n if (!feature) return 2;\n const parsedMax = Number(valueAfter(args.positionals, '--max-rounds') ?? 5);\n const maxRounds = Number.isInteger(parsedMax) ? Math.min(10, Math.max(1, parsedMax)) : 5;\n const surfaces = (valueAfter(args.positionals, '--surfaces') ?? '')\n .split(',')\n .map((value) => value.trim())\n .filter(Boolean);\n const reportPath = join(ctx.cwd, 'specs', 'reports', `${feature}.loop.json`);\n let previous: Partial<LoopSnapshot> | undefined;\n try {\n previous = JSON.parse(await readFile(reportPath, 'utf8')) as Partial<LoopSnapshot>;\n } catch {}\n const round =\n previous?.status === 'completed' && !previous.outcome\n ? Math.max(1, Number(previous.round) || 1) + 1\n : previous?.outcome\n ? 1\n : Math.max(1, Number(previous?.round) || 1);\n const now = ctx.clock.now();\n const before: LoopSnapshot = {\n id: feature,\n round,\n maxRounds,\n phase: round === 1 ? 'build' : 'fix',\n status: 'running',\n ...(surfaces.length > 0 ? { surfaces } : previous?.surfaces ? { surfaces: previous.surfaces } : {}),\n ...(previous?.history ? { history: previous.history } : {}),\n startedAt: previous?.startedAt ?? now,\n updatedAt: now,\n };\n await mkdir(join(ctx.cwd, 'specs', 'reports'), { recursive: true });\n await writeFile(reportPath, `${JSON.stringify(before, null, 2)}\\n`, 'utf8');\n\n let result: number;\n let controllerResult: LoopSnapshot = before;\n if (previous?.status === 'pending' && previous.runId) {\n controllerResult = { ...before, runId: previous.runId };\n try {\n const tree = await ctx.openStore().then(async (store) => {\n try {\n return await store.readRunTree(previous?.runId as never);\n } finally {\n await store.close();\n }\n });\n const state = tree.run.state;\n if (ACTIVE_STATES.has(state)) {\n controllerResult = { ...controllerResult, phase: previous.phase ?? 'build', status: 'pending' };\n await writeFile(reportPath, `${JSON.stringify(controllerResult, null, 2)}\\n`, 'utf8');\n if (args.json) ctx.stdio.stdout.write(`${JSON.stringify({ ...controllerResult, reportPath })}\\n`);\n else ctx.stdio.stdout.write(`loop pending: ${reportPath}\\n`);\n return 4;\n }\n result = state === 'COMPLETED' ? 0 : state === 'CANCELLED' ? 16 : 3;\n } catch {\n // If the state store cannot be read, do not claim that a pending run is resumable.\n result = 4;\n }\n } else {\n const started = await ctx.controller.send('start', {\n profile: 'feature',\n spec: { path: resolveSpecPath(ctx.cwd, feature) },\n phases: [...LOOP_PHASES],\n ...(surfaces.length > 0\n ? { surfaces: surfaces as never }\n : previous?.surfaces\n ? { surfaces: previous.surfaces as never }\n : {}),\n withFix: true,\n unattended: true,\n });\n const runId =\n started.result && typeof started.result === 'object' && 'runId' in started.result\n ? String((started.result as { runId: string }).runId)\n : undefined;\n if (runId && started.status === 'pending') await ctx.hostSpawner.spawnDetached(runId as never);\n controllerResult = { ...before, ...(runId ? { runId } : {}) };\n result = started.status === 'rejected' ? 3 : started.status === 'pending' ? 4 : 0;\n }\n let review = readReview(\n result && typeof result === 'object' && 'result' in result ? (result as { result?: unknown }).result : undefined,\n );\n if (!review && result === 0 && (await isFreshReview(ctx.cwd, feature))) {\n try {\n review = readReview(\n JSON.parse(await readFile(join(ctx.cwd, 'specs', 'reports', `${feature}.verdict.json`), 'utf8')),\n );\n } catch {\n // A completed Pi command without a durable verdict is not a successful loop.\n }\n } else if (!review && result === 0) {\n // A stale verdict is deliberately ignored: V2 treats it as uncovered review,\n // never as evidence that the new build is clean.\n review = null;\n }\n const decision = result === 4 ? undefined : decideLoop(review, previous?.blockingKey, round, maxRounds);\n const output = {\n ...controllerResult,\n status: result === 4 ? 'pending' : result === 0 ? 'completed' : 'rejected',\n phase: decision?.outcome === 'ship' || decision?.outcome === 'abort' ? 'done' : controllerResult.phase,\n ...(decision?.outcome === 'ship'\n ? { outcome: 'ship' as const }\n : decision?.outcome === 'abort'\n ? { outcome: 'abort' as const, reason: decision.reason }\n : result !== 4\n ? { outcome: 'abort' as const, reason: 'run-rejected' }\n : {}),\n ...(decision?.outcome === 'continue' ? { blockingKey: decision.key } : {}),\n updatedAt: ctx.clock.now(),\n ...(result === 4\n ? {}\n : {\n history: [\n ...(controllerResult.history ?? []),\n {\n round,\n ...(controllerResult.runId ? { runId: controllerResult.runId } : {}),\n status: result === 0 ? ('completed' as const) : ('rejected' as const),\n ...(decision?.outcome ? { outcome: decision.outcome } : { outcome: 'abort' as const }),\n ...(decision?.outcome === 'abort'\n ? { reason: decision.reason }\n : result !== 0\n ? { reason: 'run-rejected' }\n : {}),\n ...(decision?.outcome === 'continue' ? { blockingKey: decision.key } : {}),\n updatedAt: ctx.clock.now(),\n },\n ],\n }),\n } satisfies LoopSnapshot;\n await writeFile(reportPath, `${JSON.stringify(output, null, 2)}\\n`, 'utf8');\n if (args.json) ctx.stdio.stdout.write(`${JSON.stringify({ ...output, reportPath })}\\n`);\n else ctx.stdio.stdout.write(`loop ${output.status}: ${reportPath}\\n`);\n return result;\n },\n};\n\nexport default loop;\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { c as rollbackV2Import } from "./src-CmFjh4M0.mjs";
1
+ import { c as rollbackV2Import } from "./src-DNsLSxgm.mjs";
2
2
  import { readFile, writeFile } from "node:fs/promises";
3
3
  import { join, resolve } from "node:path";
4
4
  //#region src/commands/migrate/index.ts
@@ -30,4 +30,4 @@ const migrate = {
30
30
  //#endregion
31
31
  export { migrate as default };
32
32
 
33
- //# sourceMappingURL=migrate-DybihKzZ.mjs.map
33
+ //# sourceMappingURL=migrate-DuYCAbE3.mjs.map