cohorte 2.10.1 → 3.0.0-dev.1

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 (213) hide show
  1. package/README.md +7 -0
  2. package/assets/manifest.json +193 -0
  3. package/assets/migrations/config/README.md +12 -0
  4. package/assets/migrations/state/0001_init.sql +154 -0
  5. package/assets/prompts/README.md +11 -0
  6. package/assets/prompts/agents/fixer.md +7 -0
  7. package/assets/prompts/agents/implementer.md +12 -0
  8. package/assets/prompts/agents/reviewer.md +8 -0
  9. package/assets/prompts/agents/security-reviewer.md +7 -0
  10. package/assets/prompts/phases/build.md +5 -0
  11. package/assets/prompts/phases/review.md +5 -0
  12. package/assets/prompts/phases/test.md +5 -0
  13. package/assets/prompts/system.md +11 -0
  14. package/assets/schemas/agent-output.schema.json +225 -0
  15. package/assets/schemas/auth-status.schema.json +87 -0
  16. package/assets/schemas/command-result.schema.json +192 -0
  17. package/assets/schemas/commands.schema.json +952 -0
  18. package/assets/schemas/config.schema.json +2259 -0
  19. package/assets/schemas/doctor-report.schema.json +104 -0
  20. package/assets/schemas/events.schema.json +8514 -0
  21. package/assets/schemas/fake-script.schema.json +549 -0
  22. package/assets/schemas/inspect.schema.json +3133 -0
  23. package/assets/schemas/manifest.schema.json +79 -0
  24. package/assets/schemas/ownership.schema.json +52 -0
  25. package/assets/schemas/policy-verdict.schema.json +282 -0
  26. package/assets/schemas/project-model.schema.json +783 -0
  27. package/assets/schemas/project-status.schema.json +217 -0
  28. package/assets/schemas/reconcile-plan.schema.json +154 -0
  29. package/assets/schemas/run-diff.schema.json +177 -0
  30. package/assets/schemas/run-snapshot-manifest.schema.json +2104 -0
  31. package/assets/schemas/run-state.schema.json +1755 -0
  32. package/assets/schemas/runtime-capabilities.schema.json +1545 -0
  33. package/assets/schemas/sandbox-capabilities.schema.json +116 -0
  34. package/assets/schemas/skill.schema.json +87 -0
  35. package/assets/schemas/spec.schema.json +202 -0
  36. package/assets/schemas/tool-catalogue.schema.json +655 -0
  37. package/assets/schemas/trust-record.schema.json +38 -0
  38. package/assets/skills/README.md +9 -0
  39. package/assets/skills/inspect-surface.md +4 -0
  40. package/dist/agent-host.mjs +2187 -0
  41. package/dist/agent-host.mjs.map +1 -0
  42. package/dist/bundle-manifest.json +104 -0
  43. package/dist/chunks/__host-DgGPyco9.mjs +18 -0
  44. package/dist/chunks/__host-DgGPyco9.mjs.map +1 -0
  45. package/dist/chunks/approve-Cv135UqI.mjs +19 -0
  46. package/dist/chunks/approve-Cv135UqI.mjs.map +1 -0
  47. package/dist/chunks/auth-BT5oVp76.mjs +132 -0
  48. package/dist/chunks/auth-BT5oVp76.mjs.map +1 -0
  49. package/dist/chunks/auth-CYOh60KG.mjs +93 -0
  50. package/dist/chunks/auth-CYOh60KG.mjs.map +1 -0
  51. package/dist/chunks/brainstorm-BwKA6yZP.mjs +7 -0
  52. package/dist/chunks/brainstorm-BwKA6yZP.mjs.map +1 -0
  53. package/dist/chunks/cancel-KV_cpsqS.mjs +15 -0
  54. package/dist/chunks/cancel-KV_cpsqS.mjs.map +1 -0
  55. package/dist/chunks/canonical-C8UxeEmB.mjs +57 -0
  56. package/dist/chunks/canonical-C8UxeEmB.mjs.map +1 -0
  57. package/dist/chunks/catalogue-BvwSrM_j.mjs +1549 -0
  58. package/dist/chunks/catalogue-BvwSrM_j.mjs.map +1 -0
  59. package/dist/chunks/compose-Dvxunpto.mjs +7307 -0
  60. package/dist/chunks/compose-Dvxunpto.mjs.map +1 -0
  61. package/dist/chunks/config-DefHA2Lw.mjs +96 -0
  62. package/dist/chunks/config-DefHA2Lw.mjs.map +1 -0
  63. package/dist/chunks/contract-BkMjU1kR.mjs +295 -0
  64. package/dist/chunks/contract-BkMjU1kR.mjs.map +1 -0
  65. package/dist/chunks/contract-bQ9FJl4N.mjs +123 -0
  66. package/dist/chunks/contract-bQ9FJl4N.mjs.map +1 -0
  67. package/dist/chunks/deny-ClTxSvHJ.mjs +15 -0
  68. package/dist/chunks/deny-ClTxSvHJ.mjs.map +1 -0
  69. package/dist/chunks/diff-Qw0II0sj.mjs +101 -0
  70. package/dist/chunks/diff-Qw0II0sj.mjs.map +1 -0
  71. package/dist/chunks/discover-eS01-me1.mjs +21 -0
  72. package/dist/chunks/discover-eS01-me1.mjs.map +1 -0
  73. package/dist/chunks/doctor-B-H2UK5e.mjs +222 -0
  74. package/dist/chunks/doctor-B-H2UK5e.mjs.map +1 -0
  75. package/dist/chunks/fix-D4Tj8RVA.mjs +18 -0
  76. package/dist/chunks/fix-D4Tj8RVA.mjs.map +1 -0
  77. package/dist/chunks/gc-BBXlBF5w.mjs +161 -0
  78. package/dist/chunks/gc-BBXlBF5w.mjs.map +1 -0
  79. package/dist/chunks/init-BUnzIkeE.mjs +63 -0
  80. package/dist/chunks/init-BUnzIkeE.mjs.map +1 -0
  81. package/dist/chunks/inspect-Buby4cQD.mjs +22 -0
  82. package/dist/chunks/inspect-Buby4cQD.mjs.map +1 -0
  83. package/dist/chunks/logs-Giq5sUau.mjs +25 -0
  84. package/dist/chunks/logs-Giq5sUau.mjs.map +1 -0
  85. package/dist/chunks/migrate-DybihKzZ.mjs +33 -0
  86. package/dist/chunks/migrate-DybihKzZ.mjs.map +1 -0
  87. package/dist/chunks/models-Btp6X2YZ.mjs +24 -0
  88. package/dist/chunks/models-Btp6X2YZ.mjs.map +1 -0
  89. package/dist/chunks/pause-BASBfKLO.mjs +16 -0
  90. package/dist/chunks/pause-BASBfKLO.mjs.map +1 -0
  91. package/dist/chunks/policy-DUIgzF3W.mjs +139 -0
  92. package/dist/chunks/policy-DUIgzF3W.mjs.map +1 -0
  93. package/dist/chunks/providers-Dx78imKj.mjs +19 -0
  94. package/dist/chunks/providers-Dx78imKj.mjs.map +1 -0
  95. package/dist/chunks/reconcile-Bhy3IIYo.mjs +50 -0
  96. package/dist/chunks/reconcile-Bhy3IIYo.mjs.map +1 -0
  97. package/dist/chunks/render-DyZr5Lgi.mjs +53 -0
  98. package/dist/chunks/render-DyZr5Lgi.mjs.map +1 -0
  99. package/dist/chunks/resume-GJQRb2Eb.mjs +15 -0
  100. package/dist/chunks/resume-GJQRb2Eb.mjs.map +1 -0
  101. package/dist/chunks/retry-CLrUem1H.mjs +15 -0
  102. package/dist/chunks/retry-CLrUem1H.mjs.map +1 -0
  103. package/dist/chunks/review-BDdjb6kB.mjs +17 -0
  104. package/dist/chunks/review-BDdjb6kB.mjs.map +1 -0
  105. package/dist/chunks/run-CgTWYlcd.mjs +82 -0
  106. package/dist/chunks/run-CgTWYlcd.mjs.map +1 -0
  107. package/dist/chunks/run-tool-C3rAv4XF.mjs +26 -0
  108. package/dist/chunks/run-tool-C3rAv4XF.mjs.map +1 -0
  109. package/dist/chunks/scan-8Gy_B6kI.mjs +122 -0
  110. package/dist/chunks/scan-8Gy_B6kI.mjs.map +1 -0
  111. package/dist/chunks/schema-C9QbUW4H.mjs +578 -0
  112. package/dist/chunks/schema-C9QbUW4H.mjs.map +1 -0
  113. package/dist/chunks/send-BdnJ85Nz.mjs +19 -0
  114. package/dist/chunks/send-BdnJ85Nz.mjs.map +1 -0
  115. package/dist/chunks/ship-DTuLGoaJ.mjs +19 -0
  116. package/dist/chunks/ship-DTuLGoaJ.mjs.map +1 -0
  117. package/dist/chunks/shutdown-BM92Iyv2.mjs +15 -0
  118. package/dist/chunks/shutdown-BM92Iyv2.mjs.map +1 -0
  119. package/dist/chunks/skip-DZG5kpcH.mjs +18 -0
  120. package/dist/chunks/skip-DZG5kpcH.mjs.map +1 -0
  121. package/dist/chunks/spec-Bq0P3jVh.mjs +20 -0
  122. package/dist/chunks/spec-Bq0P3jVh.mjs.map +1 -0
  123. package/dist/chunks/src-CId6kMsY.mjs +1689 -0
  124. package/dist/chunks/src-CId6kMsY.mjs.map +1 -0
  125. package/dist/chunks/src-CfAOde7C.mjs +5403 -0
  126. package/dist/chunks/src-CfAOde7C.mjs.map +1 -0
  127. package/dist/chunks/src-CmFjh4M0.mjs +928 -0
  128. package/dist/chunks/src-CmFjh4M0.mjs.map +1 -0
  129. package/dist/chunks/src-Cu3TxNob.mjs +783 -0
  130. package/dist/chunks/src-Cu3TxNob.mjs.map +1 -0
  131. package/dist/chunks/src-DcU4IXMe.mjs +117 -0
  132. package/dist/chunks/src-DcU4IXMe.mjs.map +1 -0
  133. package/dist/chunks/src-s7YRvk0t.mjs +2116 -0
  134. package/dist/chunks/src-s7YRvk0t.mjs.map +1 -0
  135. package/dist/chunks/status-SMjiOMYy.mjs +29 -0
  136. package/dist/chunks/status-SMjiOMYy.mjs.map +1 -0
  137. package/dist/chunks/tail-BuhT-2PH.mjs +25 -0
  138. package/dist/chunks/tail-BuhT-2PH.mjs.map +1 -0
  139. package/dist/chunks/update-BXIppsbi.mjs +16 -0
  140. package/dist/chunks/update-BXIppsbi.mjs.map +1 -0
  141. package/dist/cli.mjs +175 -0
  142. package/dist/cli.mjs.map +1 -0
  143. package/package.json +17 -32
  144. package/CHANGELOG.md +0 -1461
  145. package/bin/cli.js +0 -817
  146. package/bin/report.js +0 -141
  147. package/core/adapter/render.js +0 -415
  148. package/core/agents/implementer.template.md +0 -142
  149. package/core/agents/profile-reader.md +0 -50
  150. package/core/agents/release.md +0 -58
  151. package/core/agents/review.md +0 -179
  152. package/core/commands/cohorte-align-ds.md +0 -33
  153. package/core/commands/cohorte-audit.md +0 -58
  154. package/core/commands/cohorte-brainstorm.md +0 -70
  155. package/core/commands/cohorte-build.md +0 -219
  156. package/core/commands/cohorte-doctor.md +0 -194
  157. package/core/commands/cohorte-fix.md +0 -80
  158. package/core/commands/cohorte-fleet.md +0 -103
  159. package/core/commands/cohorte-init-pipeline.md +0 -31
  160. package/core/commands/cohorte-intake.md +0 -92
  161. package/core/commands/cohorte-patch.md +0 -118
  162. package/core/commands/cohorte-refactor.md +0 -59
  163. package/core/commands/cohorte-retro.md +0 -85
  164. package/core/commands/cohorte-review.md +0 -270
  165. package/core/commands/cohorte-ship.md +0 -122
  166. package/core/commands/cohorte-spec.md +0 -101
  167. package/core/commands/cohorte-update-pipeline.md +0 -165
  168. package/core/hooks/gate.py +0 -588
  169. package/core/runtimes/claude.json +0 -74
  170. package/core/runtimes/codex.json +0 -85
  171. package/core/runtimes/cursor.json +0 -76
  172. package/core/runtimes/gemini.json +0 -76
  173. package/core/runtimes/opencode.json +0 -73
  174. package/core/templates/agent-handoff.md +0 -34
  175. package/core/templates/brainstorm-return.md +0 -36
  176. package/core/templates/decisions.template.md +0 -42
  177. package/core/templates/design-brief.md +0 -44
  178. package/core/templates/patch.template.md +0 -86
  179. package/core/templates/pr-body.md +0 -25
  180. package/core/templates/review-feedback.md +0 -38
  181. package/core/templates/spec.template.md +0 -87
  182. package/core/templates/steps/init-pipeline/01-detect-stack.md +0 -44
  183. package/core/templates/steps/init-pipeline/02-interview-gaps.md +0 -55
  184. package/core/templates/steps/init-pipeline/03-draft-profile.md +0 -10
  185. package/core/templates/steps/init-pipeline/04-write-render.md +0 -145
  186. package/core/templates/steps/init-pipeline/05-report.md +0 -12
  187. package/core/workflows/audit.js +0 -240
  188. package/core/workflows/loop.js +0 -641
  189. package/core/workflows/refactor.js +0 -276
  190. package/core/workflows/review.js +0 -456
  191. package/install.ps1 +0 -103
  192. package/install.sh +0 -114
  193. package/lib/doctor.js +0 -534
  194. package/lib/runtime.js +0 -139
  195. package/lib/versions.js +0 -149
  196. package/lib/yaml.js +0 -129
  197. package/profile/PIPELINE.template.md +0 -259
  198. package/profile/SCHEMA.md +0 -735
  199. package/profile/cohorte.config.template.yaml +0 -43
  200. package/scripts/assert-gate-hook.mjs +0 -75
  201. package/scripts/kanban-move.sh +0 -313
  202. package/scripts/metrics/collect.mjs +0 -531
  203. package/scripts/metrics/prices.json +0 -42
  204. package/scripts/new-feature.sh.template +0 -98
  205. package/scripts/preflight.sh +0 -124
  206. package/scripts/remove-feature.sh.template +0 -56
  207. package/scripts/test-adapter.mjs +0 -437
  208. package/scripts/test-gate.mjs +0 -443
  209. package/scripts/test-kanban.mjs +0 -304
  210. package/scripts/test-lib.mjs +0 -356
  211. package/scripts/test-metrics.mjs +0 -156
  212. package/scripts/test-workflows.mjs +0 -758
  213. package/scripts/validate-core.mjs +0 -325
@@ -0,0 +1,101 @@
1
+ import { n as sha256Hex } from "./canonical-C8UxeEmB.mjs";
2
+ import "./src-Cu3TxNob.mjs";
3
+ import { mkdir, writeFile } from "node:fs/promises";
4
+ import { execFile } from "node:child_process";
5
+ import { promisify } from "node:util";
6
+ //#region src/commands/diff/index.ts
7
+ const exec = promisify(execFile);
8
+ async function git(cwd, args) {
9
+ return (await exec("git", args, {
10
+ cwd,
11
+ maxBuffer: 4194304
12
+ })).stdout;
13
+ }
14
+ function parseNumstat(text) {
15
+ return text.split("\n").filter(Boolean).map((line) => {
16
+ const [addedText, removedText, ...pathParts] = line.split(" ");
17
+ const path = pathParts.join(" ");
18
+ const added = addedText === "-" ? 0 : Number(addedText);
19
+ const removed = removedText === "-" ? 0 : Number(removedText);
20
+ return {
21
+ path,
22
+ op: added === 0 && removed > 0 ? "delete" : added > 0 && removed === 0 ? "create" : "modify",
23
+ added,
24
+ removed
25
+ };
26
+ });
27
+ }
28
+ const diff = {
29
+ verb: "diff",
30
+ async run(ctx, args) {
31
+ const runId = args.positionals[0];
32
+ if (!runId) return 2;
33
+ const store = await ctx.openStore();
34
+ try {
35
+ const tree = await store.readRunTree(runId);
36
+ const run = tree.run;
37
+ const worktree = tree.worktrees.find((item) => item.slot === (args.positionals[1] ?? "_integration")) ?? tree.worktrees[0];
38
+ if (!worktree || !run.baseSha) return 3;
39
+ const base = run.baseSha;
40
+ const head = run.integrationHead ?? (await git(worktree.path, ["rev-parse", "HEAD"])).trim();
41
+ const rows = parseNumstat(await git(worktree.path, [
42
+ "diff",
43
+ "--numstat",
44
+ `${base}..${head}`
45
+ ]));
46
+ const patch = await git(worktree.path, [
47
+ "diff",
48
+ "--binary",
49
+ `${base}..${head}`
50
+ ]);
51
+ const artifactId = ctx.ids.next("art");
52
+ const artifactDir = `${ctx.cwd}/.cohorte/runs/${runId}/artifacts`;
53
+ const relativePatch = `.cohorte/runs/${runId}/artifacts/${artifactId}.patch`;
54
+ await mkdir(artifactDir, {
55
+ recursive: true,
56
+ mode: 448
57
+ });
58
+ await writeFile(`${ctx.cwd}/${relativePatch}`, patch, "utf8");
59
+ const document = {
60
+ documentVersion: 1,
61
+ runId,
62
+ base: {
63
+ branch: run.baseBranch,
64
+ sha: base
65
+ },
66
+ head: {
67
+ branch: run.integrationBranch ?? worktree.branch ?? "HEAD",
68
+ sha: head,
69
+ treeDigest: (await git(worktree.path, ["rev-parse", `${head}^{tree}`])).trim()
70
+ },
71
+ surfaces: [{
72
+ surface: args.positionals[1] ?? "shared",
73
+ files: rows.map((row) => ({
74
+ path: row.path,
75
+ op: row.op
76
+ })),
77
+ stat: {
78
+ added: rows.reduce((sum, row) => sum + row.added, 0),
79
+ removed: rows.reduce((sum, row) => sum + row.removed, 0)
80
+ },
81
+ patch: {
82
+ artifactId,
83
+ kind: "patch",
84
+ path: relativePatch,
85
+ sha256: sha256Hex(patch),
86
+ bytes: Buffer.byteLength(patch)
87
+ }
88
+ }]
89
+ };
90
+ if (args.json) ctx.stdio.stdout.write(`${JSON.stringify(document)}\n`);
91
+ else ctx.stdio.stdout.write(patch);
92
+ return 0;
93
+ } finally {
94
+ await store.close();
95
+ }
96
+ }
97
+ };
98
+ //#endregion
99
+ export { diff as default };
100
+
101
+ //# sourceMappingURL=diff-Qw0II0sj.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff-Qw0II0sj.mjs","names":[],"sources":["../../../../apps/cli/src/commands/diff/index.ts"],"sourcesContent":["import { execFile } from 'node:child_process';\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { promisify } from 'node:util';\nimport { sha256Hex } from '@cohorte/base';\nimport type { CommandModule } from '../../contract/index.ts';\n\nconst exec = promisify(execFile);\ntype GitRow = { path: string; op: 'create' | 'modify' | 'delete'; added: number; removed: number };\n\nasync function git(cwd: string, args: string[]): Promise<string> {\n const result = await exec('git', args, { cwd, maxBuffer: 4 * 1024 * 1024 });\n return result.stdout;\n}\n\nfunction parseNumstat(text: string): GitRow[] {\n return text\n .split('\\n')\n .filter(Boolean)\n .map((line) => {\n const [addedText, removedText, ...pathParts] = line.split('\\t');\n const path = pathParts.join('\\t');\n const added = addedText === '-' ? 0 : Number(addedText);\n const removed = removedText === '-' ? 0 : Number(removedText);\n return {\n path,\n op: added === 0 && removed > 0 ? 'delete' : added > 0 && removed === 0 ? 'create' : 'modify',\n added,\n removed,\n };\n });\n}\n\nconst diff: CommandModule = {\n verb: 'diff',\n async run(ctx, args) {\n const runId = args.positionals[0];\n if (!runId) return 2;\n const store = await ctx.openStore();\n try {\n const tree = await store.readRunTree(runId as never);\n const run = tree.run;\n const worktree =\n tree.worktrees.find((item) => item.slot === (args.positionals[1] ?? '_integration')) ?? tree.worktrees[0];\n if (!worktree || !run.baseSha) return 3;\n const base = run.baseSha;\n const head = run.integrationHead ?? (await git(worktree.path, ['rev-parse', 'HEAD'])).trim();\n const rows = parseNumstat(await git(worktree.path, ['diff', '--numstat', `${base}..${head}`]));\n const patch = await git(worktree.path, ['diff', '--binary', `${base}..${head}`]);\n const artifactId = ctx.ids.next<'ArtifactId'>('art');\n const artifactDir = `${ctx.cwd}/.cohorte/runs/${runId}/artifacts`;\n const relativePatch = `.cohorte/runs/${runId}/artifacts/${artifactId}.patch`;\n await mkdir(artifactDir, { recursive: true, mode: 0o700 });\n await writeFile(`${ctx.cwd}/${relativePatch}`, patch, 'utf8');\n const document = {\n documentVersion: 1,\n runId,\n base: { branch: run.baseBranch, sha: base },\n head: {\n branch: run.integrationBranch ?? worktree.branch ?? 'HEAD',\n sha: head,\n treeDigest: (await git(worktree.path, ['rev-parse', `${head}^{tree}`])).trim(),\n },\n surfaces: [\n {\n surface: args.positionals[1] ?? 'shared',\n files: rows.map((row) => ({ path: row.path, op: row.op })),\n stat: {\n added: rows.reduce((sum, row) => sum + row.added, 0),\n removed: rows.reduce((sum, row) => sum + row.removed, 0),\n },\n patch: {\n artifactId,\n kind: 'patch',\n path: relativePatch,\n sha256: sha256Hex(patch),\n bytes: Buffer.byteLength(patch),\n },\n },\n ],\n };\n if (args.json) ctx.stdio.stdout.write(`${JSON.stringify(document)}\\n`);\n else ctx.stdio.stdout.write(patch);\n return 0;\n } finally {\n await store.close();\n }\n },\n};\n\nexport default diff;\n"],"mappings":";;;;;;AAMA,MAAM,OAAO,UAAU,QAAQ;AAG/B,eAAe,IAAI,KAAa,MAAiC;CAE/D,QAAO,MADc,KAAK,OAAO,MAAM;EAAE;EAAK,WAAW;CAAgB,CAAC,EAAA,CAC5D;AAChB;AAEA,SAAS,aAAa,MAAwB;CAC5C,OAAO,KACJ,MAAM,IAAI,CAAC,CACX,OAAO,OAAO,CAAC,CACf,KAAK,SAAS;EACb,MAAM,CAAC,WAAW,aAAa,GAAG,aAAa,KAAK,MAAM,GAAI;EAC9D,MAAM,OAAO,UAAU,KAAK,GAAI;EAChC,MAAM,QAAQ,cAAc,MAAM,IAAI,OAAO,SAAS;EACtD,MAAM,UAAU,gBAAgB,MAAM,IAAI,OAAO,WAAW;EAC5D,OAAO;GACL;GACA,IAAI,UAAU,KAAK,UAAU,IAAI,WAAW,QAAQ,KAAK,YAAY,IAAI,WAAW;GACpF;GACA;EACF;CACF,CAAC;AACL;AAEA,MAAM,OAAsB;CAC1B,MAAM;CACN,MAAM,IAAI,KAAK,MAAM;EACnB,MAAM,QAAQ,KAAK,YAAY;EAC/B,IAAI,CAAC,OAAO,OAAO;EACnB,MAAM,QAAQ,MAAM,IAAI,UAAU;EAClC,IAAI;GACF,MAAM,OAAO,MAAM,MAAM,YAAY,KAAc;GACnD,MAAM,MAAM,KAAK;GACjB,MAAM,WACJ,KAAK,UAAU,MAAM,SAAS,KAAK,UAAU,KAAK,YAAY,MAAM,eAAe,KAAK,KAAK,UAAU;GACzG,IAAI,CAAC,YAAY,CAAC,IAAI,SAAS,OAAO;GACtC,MAAM,OAAO,IAAI;GACjB,MAAM,OAAO,IAAI,oBAAoB,MAAM,IAAI,SAAS,MAAM,CAAC,aAAa,MAAM,CAAC,EAAA,CAAG,KAAK;GAC3F,MAAM,OAAO,aAAa,MAAM,IAAI,SAAS,MAAM;IAAC;IAAQ;IAAa,GAAG,KAAK,IAAI;GAAM,CAAC,CAAC;GAC7F,MAAM,QAAQ,MAAM,IAAI,SAAS,MAAM;IAAC;IAAQ;IAAY,GAAG,KAAK,IAAI;GAAM,CAAC;GAC/E,MAAM,aAAa,IAAI,IAAI,KAAmB,KAAK;GACnD,MAAM,cAAc,GAAG,IAAI,IAAI,iBAAiB,MAAM;GACtD,MAAM,gBAAgB,iBAAiB,MAAM,aAAa,WAAW;GACrE,MAAM,MAAM,aAAa;IAAE,WAAW;IAAM,MAAM;GAAM,CAAC;GACzD,MAAM,UAAU,GAAG,IAAI,IAAI,GAAG,iBAAiB,OAAO,MAAM;GAC5D,MAAM,WAAW;IACf,iBAAiB;IACjB;IACA,MAAM;KAAE,QAAQ,IAAI;KAAY,KAAK;IAAK;IAC1C,MAAM;KACJ,QAAQ,IAAI,qBAAqB,SAAS,UAAU;KACpD,KAAK;KACL,aAAa,MAAM,IAAI,SAAS,MAAM,CAAC,aAAa,GAAG,KAAK,QAAQ,CAAC,EAAA,CAAG,KAAK;IAC/E;IACA,UAAU,CACR;KACE,SAAS,KAAK,YAAY,MAAM;KAChC,OAAO,KAAK,KAAK,SAAS;MAAE,MAAM,IAAI;MAAM,IAAI,IAAI;KAAG,EAAE;KACzD,MAAM;MACJ,OAAO,KAAK,QAAQ,KAAK,QAAQ,MAAM,IAAI,OAAO,CAAC;MACnD,SAAS,KAAK,QAAQ,KAAK,QAAQ,MAAM,IAAI,SAAS,CAAC;KACzD;KACA,OAAO;MACL;MACA,MAAM;MACN,MAAM;MACN,QAAQ,UAAU,KAAK;MACvB,OAAO,OAAO,WAAW,KAAK;KAChC;IACF,CACF;GACF;GACA,IAAI,KAAK,MAAM,IAAI,MAAM,OAAO,MAAM,GAAG,KAAK,UAAU,QAAQ,EAAE,GAAG;QAChE,IAAI,MAAM,OAAO,MAAM,KAAK;GACjC,OAAO;EACT,UAAU;GACR,MAAM,MAAM,MAAM;EACpB;CACF;AACF"}
@@ -0,0 +1,21 @@
1
+ import { N as errorOf, j as CohorteError } from "./src-Cu3TxNob.mjs";
2
+ import "./src-CmFjh4M0.mjs";
3
+ import { t as scanRepository } from "./scan-8Gy_B6kI.mjs";
4
+ //#region src/commands/discover/index.ts
5
+ const discover = {
6
+ verb: "discover",
7
+ async run(ctx, args) {
8
+ if (args.positionals.includes("--semantic")) throw new CohorteError(errorOf("configuration/phase-not-available", "semantic discovery is not available in V3.0"));
9
+ const model = await scanRepository(ctx.cwd, {
10
+ clock: ctx.clock,
11
+ toolVersion: "3.0.0"
12
+ });
13
+ if (args.json) ctx.stdio.stdout.write(`${JSON.stringify(model)}\n`);
14
+ else ctx.stdio.stdout.write(`${JSON.stringify(model, null, 2)}\n`);
15
+ return 0;
16
+ }
17
+ };
18
+ //#endregion
19
+ export { discover as default };
20
+
21
+ //# sourceMappingURL=discover-eS01-me1.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discover-eS01-me1.mjs","names":[],"sources":["../../../../apps/cli/src/commands/discover/index.ts"],"sourcesContent":["import { CohorteError, errorOf } from '@cohorte/base';\nimport { scanRepository } from '@cohorte/project-model';\nimport type { CommandModule } from '../../contract/index.ts';\n\nconst discover: CommandModule = {\n verb: 'discover',\n async run(ctx, args) {\n if (args.positionals.includes('--semantic')) {\n throw new CohorteError(\n errorOf('configuration/phase-not-available', 'semantic discovery is not available in V3.0'),\n );\n }\n const model = await scanRepository(ctx.cwd, { clock: ctx.clock, toolVersion: '3.0.0' });\n if (args.json) ctx.stdio.stdout.write(`${JSON.stringify(model)}\\n`);\n else ctx.stdio.stdout.write(`${JSON.stringify(model, null, 2)}\\n`);\n return 0;\n },\n};\n\nexport default discover;\n"],"mappings":";;;;AAIA,MAAM,WAA0B;CAC9B,MAAM;CACN,MAAM,IAAI,KAAK,MAAM;EACnB,IAAI,KAAK,YAAY,SAAS,YAAY,GACxC,MAAM,IAAI,aACR,QAAQ,qCAAqC,6CAA6C,CAC5F;EAEF,MAAM,QAAQ,MAAM,eAAe,IAAI,KAAK;GAAE,OAAO,IAAI;GAAO,aAAa;EAAQ,CAAC;EACtF,IAAI,KAAK,MAAM,IAAI,MAAM,OAAO,MAAM,GAAG,KAAK,UAAU,KAAK,EAAE,GAAG;OAC7D,IAAI,MAAM,OAAO,MAAM,GAAG,KAAK,UAAU,OAAO,MAAM,CAAC,EAAE,GAAG;EACjE,OAAO;CACT;AACF"}
@@ -0,0 +1,222 @@
1
+ import { n as sha256Hex } from "./canonical-C8UxeEmB.mjs";
2
+ import "./src-Cu3TxNob.mjs";
3
+ import { n as createKeyStore, r as createTrustStore } from "./auth-BT5oVp76.mjs";
4
+ import "./contract-BkMjU1kR.mjs";
5
+ import { i as resolveConfig, n as loadConfig } from "./src-DcU4IXMe.mjs";
6
+ import { _ as probeSandbox } from "./src-CId6kMsY.mjs";
7
+ import { l as verifyProjectionAgainstEvents } from "./src-CfAOde7C.mjs";
8
+ import { join } from "node:path";
9
+ import { execFile } from "node:child_process";
10
+ import { promisify } from "node:util";
11
+ //#region src/contract/doctor.ts
12
+ /** DESIGN §9 doctor capabilities + F-1 (ripgrep backend) + F-3 (sandbox availability) + 6.3 (protected roots). */
13
+ const DOCTOR_CHECK_IDS = [
14
+ "node-version",
15
+ "git",
16
+ "sqlite",
17
+ "search-backend",
18
+ "sandbox",
19
+ "auth",
20
+ "install",
21
+ "config",
22
+ "worktree-root"
23
+ ];
24
+ //#endregion
25
+ //#region src/doctor/checks/auth/index.ts
26
+ const authDoctorCheck = {
27
+ id: "auth",
28
+ description: "Checks that the configured provider's authentication is usable, without reading the token.",
29
+ async run(ctx) {
30
+ try {
31
+ const statuses = await ctx.runtime.resolve().authStatus([]);
32
+ const usable = statuses.every((status) => status.state !== "absent" && status.state !== "unknown-transient");
33
+ const accountNote = statuses.some((status) => status.accountLabel === void 0) ? "; account: not exposed by the engine" : "";
34
+ return {
35
+ id: "auth",
36
+ status: usable ? "ok" : "warning",
37
+ summary: `${usable ? "runtime authentication is available" : "runtime authentication needs attention"}${accountNote}`
38
+ };
39
+ } catch (error) {
40
+ return {
41
+ id: "auth",
42
+ status: "error",
43
+ summary: `authentication status unavailable: ${String(error)}`
44
+ };
45
+ }
46
+ }
47
+ };
48
+ //#endregion
49
+ //#region src/doctor/index.ts
50
+ const exec = promisify(execFile);
51
+ const result = (id, summary, status = "ok") => ({
52
+ id,
53
+ summary,
54
+ status
55
+ });
56
+ function versionAtLeast(value, required) {
57
+ const [major = 0, minor = 0] = value.split(".").map((part) => Number.parseInt(part, 10));
58
+ return major > required[0] || major === required[0] && minor >= required[1];
59
+ }
60
+ async function runDoctor(ctx, options = {}) {
61
+ const checks = [
62
+ {
63
+ id: "node-version",
64
+ description: "Node",
65
+ run: async () => versionAtLeast(process.versions.node, [24, 16]) ? result("node-version", `Node ${process.versions.node}`) : result("node-version", `Node ${process.versions.node} is below 24.16`, "error")
66
+ },
67
+ {
68
+ id: "git",
69
+ description: "Git",
70
+ run: async () => {
71
+ try {
72
+ const { stdout } = await exec("git", ["--version"]);
73
+ const version = stdout.match(/(\d+\.\d+(?:\.\d+)?)/)?.[1] ?? "0.0.0";
74
+ return versionAtLeast(version, [2, 38]) ? result("git", `Git ${version}`) : result("git", `Git ${version} is below 2.38`, "error");
75
+ } catch (error) {
76
+ return result("git", `git unavailable: ${String(error)}`, "error");
77
+ }
78
+ }
79
+ },
80
+ {
81
+ id: "sqlite",
82
+ description: "SQLite",
83
+ run: async () => {
84
+ try {
85
+ const store = await ctx.openStore();
86
+ const info = await store.open();
87
+ const migration = await store.migrate("check");
88
+ await store.close();
89
+ return migration.pending.length === 0 ? result("sqlite", `${info.kind} schema ${info.schemaVersion}`) : result("sqlite", `${migration.pending.length} migration(s) pending`, "warning");
90
+ } catch (error) {
91
+ return result("sqlite", `state store unavailable: ${String(error)}`, "error");
92
+ }
93
+ }
94
+ },
95
+ {
96
+ id: "search-backend",
97
+ description: "Search",
98
+ run: async () => {
99
+ try {
100
+ const { stdout } = await exec("rg", ["--version"]);
101
+ return result("search-backend", `ripgrep ${stdout.split(/\s+/u)[1] ?? "available"}`);
102
+ } catch {
103
+ try {
104
+ await exec("git", ["grep", "--version"]);
105
+ return result("search-backend", "git grep fallback");
106
+ } catch {
107
+ return result("search-backend", "neither rg nor git grep is available", "error");
108
+ }
109
+ }
110
+ }
111
+ },
112
+ {
113
+ id: "sandbox",
114
+ description: "Sandbox",
115
+ run: async () => {
116
+ const capabilities = await probeSandbox();
117
+ return result("sandbox", `level=${capabilities.level}, backend=${capabilities.backend}, filesystem=${capabilities.filesystem}, network=${capabilities.network}`, capabilities.level === "L0-process" ? "warning" : "ok");
118
+ }
119
+ },
120
+ authDoctorCheck,
121
+ {
122
+ id: "install",
123
+ description: "Install",
124
+ run: async () => {
125
+ try {
126
+ const verified = await ctx.assets.verify();
127
+ return verified.ok ? result("install", `${ctx.install.installDir()} (assets verified)`) : result("install", `asset verification failed: ${verified.error.message}`, "error");
128
+ } catch (error) {
129
+ return result("install", `asset verification unavailable: ${String(error)}`, "warning");
130
+ }
131
+ }
132
+ },
133
+ {
134
+ id: "config",
135
+ description: "Config",
136
+ run: async () => {
137
+ try {
138
+ const home = ctx.env.HOME ?? ctx.cwd;
139
+ const keys = createKeyStore({ directory: join(home, ".cohorte", "keys") });
140
+ const trustStore = createTrustStore({
141
+ directory: join(home, ".cohorte", "trust"),
142
+ keys
143
+ });
144
+ const loaded = await loadConfig({
145
+ cwd: ctx.cwd,
146
+ home
147
+ });
148
+ const resolved = await resolveConfig(loaded, {
149
+ trustStore,
150
+ projectKeyId: sha256Hex(loaded.projectRoot).slice(0, 24)
151
+ });
152
+ return resolved.status === "resolved" ? result("config", `project configuration resolved at ${loaded.projectRoot}`) : result("config", `project policy requires trust: ${resolved.loosenedKeys.join(", ")}`, "warning");
153
+ } catch (error) {
154
+ return result("config", `configuration unavailable: ${String(error)}`, "error");
155
+ }
156
+ }
157
+ },
158
+ {
159
+ id: "worktree-root",
160
+ description: "Worktree",
161
+ run: async () => typeof process.getuid === "function" && process.getuid() === 0 ? result("worktree-root", "refusing to operate as uid 0", "error") : result("worktree-root", ctx.cwd)
162
+ }
163
+ ];
164
+ if (options.verifyState) checks.push({
165
+ id: "verify-state",
166
+ description: "State verification",
167
+ run: async () => {
168
+ const store = await ctx.openStore();
169
+ try {
170
+ const runs = await store.listRuns({
171
+ limit: 1e5,
172
+ offset: 0
173
+ });
174
+ for (const run of runs) {
175
+ if (!(await store.verifyChain(run.runId)).ok) return result("verify-state", `run ${run.runId}: event chain is broken`, "error");
176
+ try {
177
+ await verifyProjectionAgainstEvents(store, run);
178
+ } catch (error) {
179
+ return result("verify-state", `run ${run.runId}: projection mismatch (${String(error)})`, "error");
180
+ }
181
+ }
182
+ return result("verify-state", `verified ${runs.length} run(s)`);
183
+ } finally {
184
+ await store.close();
185
+ }
186
+ }
187
+ });
188
+ const byId = new Map(checks.map((check) => [check.id, check]));
189
+ return Promise.all(DOCTOR_CHECK_IDS.map(async (id) => byId.get(id)?.run(ctx) ?? result(id, "check unavailable", "skipped")));
190
+ }
191
+ //#endregion
192
+ //#region src/commands/doctor/index.ts
193
+ const doctor = {
194
+ verb: "doctor",
195
+ async run(ctx, args) {
196
+ const checks = await runDoctor(ctx, { verifyState: args.positionals.includes("--verify-state") });
197
+ if (args.json) {
198
+ let sandbox = {};
199
+ let runtimeCapabilities = {};
200
+ try {
201
+ sandbox = await probeSandbox();
202
+ } catch {}
203
+ try {
204
+ runtimeCapabilities = ctx.runtime.capabilities?.() ?? { provider: ctx.runtime.resolve().id };
205
+ } catch {}
206
+ ctx.stdio.stdout.write(`${JSON.stringify({
207
+ documentVersion: 1,
208
+ cohorteVersion: "3.0.0",
209
+ generatedAt: ctx.clock.now(),
210
+ ok: checks.every((check) => check.status === "ok" || check.status === "skipped"),
211
+ checks,
212
+ sandbox,
213
+ runtimeCapabilities
214
+ })}\n`);
215
+ } else for (const check of checks) ctx.stdio.stdout.write(`${check.status.padEnd(7)} ${check.id}: ${check.summary}\n`);
216
+ return checks.some((check) => check.status === "error") ? 1 : 0;
217
+ }
218
+ };
219
+ //#endregion
220
+ export { doctor as default };
221
+
222
+ //# sourceMappingURL=doctor-B-H2UK5e.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor-B-H2UK5e.mjs","names":[],"sources":["../../../../apps/cli/src/contract/doctor.ts","../../../../apps/cli/src/doctor/checks/auth/index.ts","../../../../apps/cli/src/doctor/index.ts","../../../../apps/cli/src/commands/doctor/index.ts"],"sourcesContent":["// apps/cli/src/contract/doctor.ts — the frozen list of doctor check modules (PLAN PC-4 \"DoctorCheck + the frozen\n// list of doctor check modules\"). `doctor/index.ts` (area barrel, U4.05) runs every id of `DOCTOR_CHECK_IDS`\n// through `doctor/checks/<id>/index.ts`; only `auth` is a Wave-0 owned stub (`apps/cli/src/doctor/checks/auth`,\n// filled by U5.05) — the rest are created, under the same shape, by U4.05.\nimport type { DOCTOR_CHECK_STATUSES } from '@cohorte/protocol';\nimport type { CliContext } from './context.ts';\n\nexport type DoctorCheckStatus = (typeof DOCTOR_CHECK_STATUSES)[number];\n\nexport interface DoctorCheckResult {\n readonly id: string;\n readonly status: DoctorCheckStatus;\n readonly summary: string;\n readonly remediation?: string;\n}\n\nexport interface DoctorCheck {\n readonly id: string;\n readonly description: string;\n run(ctx: CliContext): Promise<DoctorCheckResult>;\n}\n\n/** DESIGN §9 doctor capabilities + F-1 (ripgrep backend) + F-3 (sandbox availability) + 6.3 (protected roots). */\nexport const DOCTOR_CHECK_IDS = [\n 'node-version',\n 'git',\n 'sqlite',\n 'search-backend',\n 'sandbox',\n 'auth',\n 'install',\n 'config',\n 'worktree-root',\n] as const;\nexport type DoctorCheckId = (typeof DOCTOR_CHECK_IDS)[number];\n","// apps/cli/src/doctor/checks/auth/index.ts — the `auth` doctor check (spec 10 \"auth status\"). Wave-0 typed stub:\n// filled by `U5.05`, which owns `apps/cli/src/doctor/checks/auth/**`.\nimport type { DoctorCheck } from '../../../contract/index.ts';\n\nconst authDoctorCheck: DoctorCheck = {\n id: 'auth',\n description: \"Checks that the configured provider's authentication is usable, without reading the token.\",\n async run(ctx) {\n try {\n const statuses = await ctx.runtime.resolve().authStatus([]);\n const usable = statuses.every((status) => status.state !== 'absent' && status.state !== 'unknown-transient');\n const accountNote = statuses.some((status) => status.accountLabel === undefined)\n ? '; account: not exposed by the engine'\n : '';\n return {\n id: 'auth',\n status: usable ? 'ok' : 'warning',\n summary: `${usable ? 'runtime authentication is available' : 'runtime authentication needs attention'}${accountNote}`,\n };\n } catch (error) {\n return { id: 'auth', status: 'error', summary: `authentication status unavailable: ${String(error)}` };\n }\n },\n};\n\nexport default authDoctorCheck;\n","// apps/cli/src/doctor/index.ts — AREA barrel: runs every `DOCTOR_CHECK_IDS` entry (contract/doctor.ts) through\n// `doctor/checks/<id>/index.ts` and assembles a `DoctorReport`. Wave-0 stub: filled by `U4.05`, which owns\n// `apps/cli/src/doctor/**` (this file included); only `checks/auth/**` is carved out to `U5.05`.\nimport { execFile } from 'node:child_process';\nimport { join } from 'node:path';\nimport { promisify } from 'node:util';\nimport { sha256Hex } from '@cohorte/base';\nimport { loadConfig, resolveConfig } from '@cohorte/config';\nimport { verifyProjectionAgainstEvents } from '@cohorte/core';\nimport { createKeyStore, createTrustStore } from '@cohorte/security/auth';\nimport { probeSandbox } from '@cohorte/security/exec';\nimport { type CliContext, DOCTOR_CHECK_IDS, type DoctorCheck, type DoctorCheckResult } from '../contract/index.ts';\nimport authDoctorCheck from './checks/auth/index.ts';\n\nconst exec = promisify(execFile);\nconst result = (id: string, summary: string, status: DoctorCheckResult['status'] = 'ok'): DoctorCheckResult => ({\n id,\n summary,\n status,\n});\n\nfunction versionAtLeast(value: string, required: [number, number]): boolean {\n const [major = 0, minor = 0] = value.split('.').map((part) => Number.parseInt(part, 10));\n return major > required[0] || (major === required[0] && minor >= required[1]);\n}\n\nexport async function runDoctor(\n ctx: CliContext,\n options: { readonly verifyState?: boolean } = {},\n): Promise<readonly DoctorCheckResult[]> {\n const checks: DoctorCheck[] = [\n {\n id: 'node-version',\n description: 'Node',\n run: async () =>\n versionAtLeast(process.versions.node, [24, 16])\n ? result('node-version', `Node ${process.versions.node}`)\n : result('node-version', `Node ${process.versions.node} is below 24.16`, 'error'),\n },\n {\n id: 'git',\n description: 'Git',\n run: async () => {\n try {\n const { stdout } = await exec('git', ['--version']);\n const version = stdout.match(/(\\d+\\.\\d+(?:\\.\\d+)?)/)?.[1] ?? '0.0.0';\n return versionAtLeast(version, [2, 38])\n ? result('git', `Git ${version}`)\n : result('git', `Git ${version} is below 2.38`, 'error');\n } catch (error) {\n return result('git', `git unavailable: ${String(error)}`, 'error');\n }\n },\n },\n {\n id: 'sqlite',\n description: 'SQLite',\n run: async () => {\n try {\n const store = await ctx.openStore();\n const info = await store.open();\n const migration = await store.migrate('check');\n await store.close();\n return migration.pending.length === 0\n ? result('sqlite', `${info.kind} schema ${info.schemaVersion}`)\n : result('sqlite', `${migration.pending.length} migration(s) pending`, 'warning');\n } catch (error) {\n return result('sqlite', `state store unavailable: ${String(error)}`, 'error');\n }\n },\n },\n {\n id: 'search-backend',\n description: 'Search',\n run: async () => {\n try {\n const { stdout } = await exec('rg', ['--version']);\n return result('search-backend', `ripgrep ${stdout.split(/\\s+/u)[1] ?? 'available'}`);\n } catch {\n try {\n await exec('git', ['grep', '--version']);\n return result('search-backend', 'git grep fallback');\n } catch {\n return result('search-backend', 'neither rg nor git grep is available', 'error');\n }\n }\n },\n },\n {\n id: 'sandbox',\n description: 'Sandbox',\n run: async () => {\n const capabilities = await probeSandbox();\n return result(\n 'sandbox',\n `level=${capabilities.level}, backend=${capabilities.backend}, filesystem=${capabilities.filesystem}, network=${capabilities.network}`,\n capabilities.level === 'L0-process' ? 'warning' : 'ok',\n );\n },\n },\n authDoctorCheck,\n {\n id: 'install',\n description: 'Install',\n run: async () => {\n try {\n const verified = await ctx.assets.verify();\n return verified.ok\n ? result('install', `${ctx.install.installDir()} (assets verified)`)\n : result('install', `asset verification failed: ${verified.error.message}`, 'error');\n } catch (error) {\n return result('install', `asset verification unavailable: ${String(error)}`, 'warning');\n }\n },\n },\n {\n id: 'config',\n description: 'Config',\n run: async () => {\n try {\n const home = ctx.env.HOME ?? ctx.cwd;\n const keys = createKeyStore({ directory: join(home, '.cohorte', 'keys') });\n const trustStore = createTrustStore({ directory: join(home, '.cohorte', 'trust'), keys });\n const loaded = await loadConfig({ cwd: ctx.cwd, home });\n const resolved = await resolveConfig(loaded, {\n trustStore,\n projectKeyId: sha256Hex(loaded.projectRoot).slice(0, 24),\n });\n return resolved.status === 'resolved'\n ? result('config', `project configuration resolved at ${loaded.projectRoot}`)\n : result('config', `project policy requires trust: ${resolved.loosenedKeys.join(', ')}`, 'warning');\n } catch (error) {\n return result('config', `configuration unavailable: ${String(error)}`, 'error');\n }\n },\n },\n {\n id: 'worktree-root',\n description: 'Worktree',\n run: async () =>\n typeof process.getuid === 'function' && process.getuid() === 0\n ? result('worktree-root', 'refusing to operate as uid 0', 'error')\n : result('worktree-root', ctx.cwd),\n },\n ];\n if (options.verifyState) {\n checks.push({\n id: 'verify-state',\n description: 'State verification',\n run: async () => {\n const store = await ctx.openStore();\n try {\n const runs = await store.listRuns({ limit: 100_000, offset: 0 });\n for (const run of runs) {\n const chain = await store.verifyChain(run.runId);\n if (!chain.ok) return result('verify-state', `run ${run.runId}: event chain is broken`, 'error');\n try {\n await verifyProjectionAgainstEvents(store, run);\n } catch (error) {\n return result('verify-state', `run ${run.runId}: projection mismatch (${String(error)})`, 'error');\n }\n }\n return result('verify-state', `verified ${runs.length} run(s)`);\n } finally {\n await store.close();\n }\n },\n });\n }\n const byId = new Map(checks.map((check) => [check.id, check]));\n return Promise.all(\n DOCTOR_CHECK_IDS.map(async (id) => byId.get(id)?.run(ctx) ?? result(id, 'check unavailable', 'skipped')),\n );\n}\n","// apps/cli/src/commands/doctor/index.ts — DESIGN §9 verb `doctor` (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/doctor/**`.\nimport { probeSandbox } from '@cohorte/security/exec';\nimport type { CommandModule } from '../../contract/index.ts';\nimport { runDoctor } from '../../doctor/index.ts';\n\nconst doctor: CommandModule = {\n verb: 'doctor',\n async run(ctx, args) {\n const checks = await runDoctor(ctx, { verifyState: args.positionals.includes('--verify-state') });\n if (args.json) {\n let sandbox: unknown = {};\n let runtimeCapabilities: unknown = {};\n try {\n sandbox = await probeSandbox();\n } catch {\n // The corresponding check already reports the diagnostic; keep JSON stable.\n }\n try {\n runtimeCapabilities = ctx.runtime.capabilities?.() ?? { provider: ctx.runtime.resolve().id };\n } catch {\n // A provider may be unavailable while the rest of doctor remains useful.\n }\n ctx.stdio.stdout.write(\n `${JSON.stringify({ documentVersion: 1, cohorteVersion: '3.0.0', generatedAt: ctx.clock.now(), ok: checks.every((check) => check.status === 'ok' || check.status === 'skipped'), checks, sandbox, runtimeCapabilities })}\\n`,\n );\n } else\n for (const check of checks) ctx.stdio.stdout.write(`${check.status.padEnd(7)} ${check.id}: ${check.summary}\\n`);\n return checks.some((check) => check.status === 'error') ? 1 : 0;\n },\n};\n\nexport default doctor;\n"],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ //#region src/commands/fix/index.ts
2
+ const fix = {
3
+ verb: "fix",
4
+ async run(ctx, args) {
5
+ const runId = args.positionals[0];
6
+ if (!runId) return 2;
7
+ const result = await ctx.controller.send("retry", { target: {
8
+ kind: "phase",
9
+ state: "FIX"
10
+ } }, { runId });
11
+ ctx.stdio.stdout.write(`${JSON.stringify(result)}\n`);
12
+ return result.status === "rejected" ? 3 : result.status === "pending" ? 4 : 0;
13
+ }
14
+ };
15
+ //#endregion
16
+ export { fix as default };
17
+
18
+ //# sourceMappingURL=fix-D4Tj8RVA.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fix-D4Tj8RVA.mjs","names":[],"sources":["../../../../apps/cli/src/commands/fix/index.ts"],"sourcesContent":["// apps/cli/src/commands/fix/index.ts — DESIGN §9 verb `fix` (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/fix/**`.\nimport type { CommandModule } from '../../contract/index.ts';\n\nconst fix: CommandModule = {\n verb: 'fix',\n async run(ctx, args) {\n const runId = args.positionals[0];\n if (!runId) return 2;\n const result = await ctx.controller.send('retry', { target: { kind: 'phase', state: 'FIX' } }, { runId });\n ctx.stdio.stdout.write(`${JSON.stringify(result)}\\n`);\n return result.status === 'rejected' ? 3 : result.status === 'pending' ? 4 : 0;\n },\n};\n\nexport default fix;\n"],"mappings":";AAMA,MAAM,MAAqB;CACzB,MAAM;CACN,MAAM,IAAI,KAAK,MAAM;EACnB,MAAM,QAAQ,KAAK,YAAY;EAC/B,IAAI,CAAC,OAAO,OAAO;EACnB,MAAM,SAAS,MAAM,IAAI,WAAW,KAAK,SAAS,EAAE,QAAQ;GAAE,MAAM;GAAS,OAAO;EAAM,EAAE,GAAG,EAAE,MAAM,CAAC;EACxG,IAAI,MAAM,OAAO,MAAM,GAAG,KAAK,UAAU,MAAM,EAAE,GAAG;EACpD,OAAO,OAAO,WAAW,aAAa,IAAI,OAAO,WAAW,YAAY,IAAI;CAC9E;AACF"}
@@ -0,0 +1,161 @@
1
+ import { readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { parse } from "yaml";
4
+ import { promisify } from "node:util";
5
+ import { gzip } from "node:zlib";
6
+ //#region src/commands/gc/index.ts
7
+ const gzipAsync = promisify(gzip);
8
+ const TERMINAL = /* @__PURE__ */ new Set(["COMPLETED", "CANCELLED"]);
9
+ const DEFAULT_RETENTION = {
10
+ transcriptsDays: 30,
11
+ artifactsDays: 90,
12
+ compressAfterDays: 7
13
+ };
14
+ async function filesUnder(root) {
15
+ const result = [];
16
+ async function visit(directory) {
17
+ let entries;
18
+ try {
19
+ entries = await readdir(directory, { withFileTypes: true });
20
+ } catch (error) {
21
+ if (error.code === "ENOENT") return;
22
+ throw error;
23
+ }
24
+ for (const entry of entries) {
25
+ const path = join(directory, entry.name);
26
+ if (entry.isDirectory()) await visit(path);
27
+ else if (entry.isFile()) result.push(path);
28
+ }
29
+ }
30
+ await visit(root);
31
+ return result;
32
+ }
33
+ function classify(path) {
34
+ const normalized = path.replaceAll("\\", "/").toLowerCase();
35
+ if (normalized.endsWith(".gz")) return void 0;
36
+ if (normalized.includes("/sensitive/") || normalized.endsWith(".sensitive") || normalized.endsWith(".sensitive.json")) return "sensitive";
37
+ if (normalized.includes("/transcripts/") || normalized.includes("/wire/") || normalized.includes("/logs/")) return "transcript";
38
+ if (normalized.includes("/artifacts/")) return "artifact";
39
+ if (normalized.includes("/spool/") || normalized.includes("/stream/")) return "spool";
40
+ }
41
+ async function readRetention(cwd) {
42
+ try {
43
+ const value = parse(await readFile(join(cwd, ".cohorte", "config.yaml"), "utf8"));
44
+ return {
45
+ ...DEFAULT_RETENTION,
46
+ ...value.retention ?? {}
47
+ };
48
+ } catch {
49
+ return DEFAULT_RETENTION;
50
+ }
51
+ }
52
+ const CAS_ADDRESS = /^[a-f0-9]{64}$/;
53
+ async function referencedCasAddresses(runs, casRoot) {
54
+ const retained = /* @__PURE__ */ new Set();
55
+ for (const run of runs) {
56
+ const digest = run.snapshotDigest;
57
+ if (!digest || !CAS_ADDRESS.test(digest)) continue;
58
+ retained.add(digest);
59
+ const path = join(casRoot, digest.slice(0, 2), digest);
60
+ try {
61
+ const text = (await readFile(path, "utf8")).toString();
62
+ for (const address of text.match(/[a-f0-9]{64}/g) ?? []) retained.add(address);
63
+ } catch {}
64
+ }
65
+ return retained;
66
+ }
67
+ async function planGc(ctx) {
68
+ const store = await ctx.openStore();
69
+ try {
70
+ const runs = await store.listRuns({
71
+ limit: 1e5,
72
+ offset: 0
73
+ });
74
+ const retention = await readRetention(ctx.cwd);
75
+ const root = join(ctx.cwd, ".cohorte", "state", "runs");
76
+ const casRoot = join(ctx.cwd, ".cohorte", "state", "cas");
77
+ const now = Date.parse(ctx.clock.now());
78
+ const actions = [];
79
+ for (const run of runs) {
80
+ if (!TERMINAL.has(run.state)) continue;
81
+ if (run.purgeable && typeof store.purgeEvents === "function") actions.push({
82
+ kind: "purge-events",
83
+ path: `.cohorte/state/events/${run.runId}`,
84
+ runId: run.runId
85
+ });
86
+ for (const path of await filesUnder(join(root, run.runId))) {
87
+ const kind = classify(path);
88
+ if (!kind) continue;
89
+ const age = Math.max(0, now - (await stat(path)).mtimeMs);
90
+ if (kind === "sensitive" && age >= retention.compressAfterDays * 864e5) actions.push({
91
+ kind: "compress",
92
+ path,
93
+ runId: run.runId
94
+ });
95
+ else if (kind === "transcript" && age >= retention.transcriptsDays * 864e5) actions.push({
96
+ kind: "delete",
97
+ path,
98
+ runId: run.runId
99
+ });
100
+ else if (kind === "artifact" && age >= retention.artifactsDays * 864e5) actions.push({
101
+ kind: "delete",
102
+ path,
103
+ runId: run.runId
104
+ });
105
+ else if (kind === "spool" && age >= 864e5) actions.push({
106
+ kind: "delete",
107
+ path,
108
+ runId: run.runId
109
+ });
110
+ }
111
+ }
112
+ const retainedCas = await referencedCasAddresses(runs, casRoot);
113
+ for (const path of await filesUnder(casRoot)) {
114
+ const address = path.split("/").at(-1) ?? "";
115
+ if (CAS_ADDRESS.test(address) && !retainedCas.has(address)) actions.push({
116
+ kind: "delete-cas",
117
+ path,
118
+ runId: "cas"
119
+ });
120
+ }
121
+ return actions.sort((a, b) => a.path.localeCompare(b.path));
122
+ } finally {
123
+ await store.close();
124
+ }
125
+ }
126
+ async function applyAction(action, store) {
127
+ if (action.kind === "purge-events") {
128
+ await store.purgeEvents?.(action.runId);
129
+ return;
130
+ }
131
+ if (action.kind === "delete" || action.kind === "delete-cas") return rm(action.path, { force: true });
132
+ const compressed = await gzipAsync(await readFile(action.path));
133
+ await writeFile(`${action.path}.gz`, compressed, { mode: 384 });
134
+ await rm(action.path, { force: true });
135
+ }
136
+ const gc = {
137
+ verb: "gc",
138
+ async run(ctx, args) {
139
+ const dryRun = args.positionals.includes("--dry-run");
140
+ const apply = args.positionals.includes("--apply");
141
+ if (dryRun === apply) return 2;
142
+ const actions = await planGc(ctx);
143
+ if (apply) {
144
+ const store = await ctx.openStore();
145
+ try {
146
+ for (const action of actions) await applyAction(action, store);
147
+ } finally {
148
+ await store.close();
149
+ }
150
+ }
151
+ ctx.stdio.stdout.write(`${JSON.stringify({
152
+ mode: dryRun ? "dry-run" : "apply",
153
+ actions
154
+ })}\n`);
155
+ return 0;
156
+ }
157
+ };
158
+ //#endregion
159
+ export { gc as default };
160
+
161
+ //# sourceMappingURL=gc-BBXlBF5w.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gc-BBXlBF5w.mjs","names":[],"sources":["../../../../apps/cli/src/commands/gc/index.ts"],"sourcesContent":["// apps/cli/src/commands/gc/index.ts — DESIGN §9 verb `gc` (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/gc/**`.\nimport type { Dirent } from 'node:fs';\nimport { readdir, readFile, rm, stat, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { promisify } from 'node:util';\nimport { gzip } from 'node:zlib';\nimport type { PipelineState } from '@cohorte/protocol';\nimport { parse } from 'yaml';\nimport type { CommandModule } from '../../contract/index.ts';\n\nconst gzipAsync = promisify(gzip);\nconst TERMINAL = new Set<PipelineState>(['COMPLETED', 'CANCELLED']);\nconst DEFAULT_RETENTION = { transcriptsDays: 30, artifactsDays: 90, compressAfterDays: 7 };\ntype Action =\n | { kind: 'compress' | 'delete' | 'delete-cas'; path: string; runId: string }\n | { kind: 'purge-events'; path: string; runId: string };\n\ntype RetentionStore = Awaited<ReturnType<Parameters<CommandModule['run']>[0]['openStore']>> & {\n purgeEvents?: (runId: string) => Promise<number>;\n};\n\nasync function filesUnder(root: string): Promise<string[]> {\n const result: string[] = [];\n async function visit(directory: string): Promise<void> {\n let entries: Dirent[];\n try {\n entries = await readdir(directory, { withFileTypes: true });\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return;\n throw error;\n }\n for (const entry of entries) {\n const path = join(directory, entry.name);\n if (entry.isDirectory()) await visit(path);\n else if (entry.isFile()) result.push(path);\n }\n }\n await visit(root);\n return result;\n}\n\nfunction classify(path: string): 'sensitive' | 'transcript' | 'artifact' | 'spool' | undefined {\n const normalized = path.replaceAll('\\\\', '/').toLowerCase();\n if (normalized.endsWith('.gz')) return undefined;\n if (normalized.includes('/sensitive/') || normalized.endsWith('.sensitive') || normalized.endsWith('.sensitive.json'))\n return 'sensitive';\n if (normalized.includes('/transcripts/') || normalized.includes('/wire/') || normalized.includes('/logs/'))\n return 'transcript';\n if (normalized.includes('/artifacts/')) return 'artifact';\n if (normalized.includes('/spool/') || normalized.includes('/stream/')) return 'spool';\n return undefined;\n}\n\nasync function readRetention(cwd: string): Promise<typeof DEFAULT_RETENTION> {\n try {\n const value = parse(await readFile(join(cwd, '.cohorte', 'config.yaml'), 'utf8')) as {\n retention?: Partial<typeof DEFAULT_RETENTION>;\n };\n return { ...DEFAULT_RETENTION, ...(value.retention ?? {}) };\n } catch {\n return DEFAULT_RETENTION;\n }\n}\n\nconst CAS_ADDRESS = /^[a-f0-9]{64}$/;\n\nasync function referencedCasAddresses(\n runs: Awaited<ReturnType<RetentionStore['listRuns']>>,\n casRoot: string,\n): Promise<Set<string>> {\n const retained = new Set<string>();\n for (const run of runs) {\n const digest = run.snapshotDigest;\n if (!digest || !CAS_ADDRESS.test(digest)) continue;\n retained.add(digest);\n const path = join(casRoot, digest.slice(0, 2), digest);\n try {\n const text = (await readFile(path, 'utf8')).toString();\n for (const address of text.match(/[a-f0-9]{64}/g) ?? []) retained.add(address);\n } catch {\n // A missing snapshot is reported by resume/doctor; GC must not fail or invent a reference.\n }\n }\n return retained;\n}\n\nasync function planGc(ctx: Parameters<CommandModule['run']>[0]): Promise<Action[]> {\n const store = (await ctx.openStore()) as RetentionStore;\n try {\n const runs = await store.listRuns({ limit: 100_000, offset: 0 });\n const retention = await readRetention(ctx.cwd);\n const root = join(ctx.cwd, '.cohorte', 'state', 'runs');\n const casRoot = join(ctx.cwd, '.cohorte', 'state', 'cas');\n const now = Date.parse(ctx.clock.now());\n const actions: Action[] = [];\n for (const run of runs) {\n if (!TERMINAL.has(run.state)) continue;\n if (run.purgeable && typeof store.purgeEvents === 'function') {\n actions.push({ kind: 'purge-events', path: `.cohorte/state/events/${run.runId}`, runId: run.runId });\n }\n for (const path of await filesUnder(join(root, run.runId))) {\n const kind = classify(path);\n if (!kind) continue;\n const age = Math.max(0, now - (await stat(path)).mtimeMs);\n if (kind === 'sensitive' && age >= retention.compressAfterDays * 86_400_000)\n actions.push({ kind: 'compress', path, runId: run.runId });\n else if (kind === 'transcript' && age >= retention.transcriptsDays * 86_400_000)\n actions.push({ kind: 'delete', path, runId: run.runId });\n else if (kind === 'artifact' && age >= retention.artifactsDays * 86_400_000)\n actions.push({ kind: 'delete', path, runId: run.runId });\n else if (kind === 'spool' && age >= 86_400_000) actions.push({ kind: 'delete', path, runId: run.runId });\n }\n }\n const retainedCas = await referencedCasAddresses(runs, casRoot);\n for (const path of await filesUnder(casRoot)) {\n const address = path.split('/').at(-1) ?? '';\n if (CAS_ADDRESS.test(address) && !retainedCas.has(address))\n actions.push({ kind: 'delete-cas', path, runId: 'cas' });\n }\n return actions.sort((a, b) => a.path.localeCompare(b.path));\n } finally {\n await store.close();\n }\n}\n\nasync function applyAction(action: Action, store: RetentionStore): Promise<void> {\n if (action.kind === 'purge-events') {\n await store.purgeEvents?.(action.runId);\n return;\n }\n if (action.kind === 'delete' || action.kind === 'delete-cas') return rm(action.path, { force: true });\n const compressed = await gzipAsync(await readFile(action.path));\n await writeFile(`${action.path}.gz`, compressed, { mode: 0o600 });\n await rm(action.path, { force: true });\n}\n\nconst gc: CommandModule = {\n verb: 'gc',\n async run(ctx, args) {\n const dryRun = args.positionals.includes('--dry-run');\n const apply = args.positionals.includes('--apply');\n if (dryRun === apply) return 2;\n const actions = await planGc(ctx);\n if (apply) {\n const store = (await ctx.openStore()) as RetentionStore;\n try {\n for (const action of actions) await applyAction(action, store);\n } finally {\n await store.close();\n }\n }\n ctx.stdio.stdout.write(`${JSON.stringify({ mode: dryRun ? 'dry-run' : 'apply', actions })}\\n`);\n return 0;\n },\n};\n\nexport default gc;\n"],"mappings":";;;;;;AAaA,MAAM,YAAY,UAAU,IAAI;AAChC,MAAM,2BAAW,IAAI,IAAmB,CAAC,aAAa,WAAW,CAAC;AAClE,MAAM,oBAAoB;CAAE,iBAAiB;CAAI,eAAe;CAAI,mBAAmB;AAAE;AASzF,eAAe,WAAW,MAAiC;CACzD,MAAM,SAAmB,CAAC;CAC1B,eAAe,MAAM,WAAkC;EACrD,IAAI;EACJ,IAAI;GACF,UAAU,MAAM,QAAQ,WAAW,EAAE,eAAe,KAAK,CAAC;EAC5D,SAAS,OAAO;GACd,IAAK,MAAgC,SAAS,UAAU;GACxD,MAAM;EACR;EACA,KAAK,MAAM,SAAS,SAAS;GAC3B,MAAM,OAAO,KAAK,WAAW,MAAM,IAAI;GACvC,IAAI,MAAM,YAAY,GAAG,MAAM,MAAM,IAAI;QACpC,IAAI,MAAM,OAAO,GAAG,OAAO,KAAK,IAAI;EAC3C;CACF;CACA,MAAM,MAAM,IAAI;CAChB,OAAO;AACT;AAEA,SAAS,SAAS,MAA6E;CAC7F,MAAM,aAAa,KAAK,WAAW,MAAM,GAAG,CAAC,CAAC,YAAY;CAC1D,IAAI,WAAW,SAAS,KAAK,GAAG,OAAO,KAAA;CACvC,IAAI,WAAW,SAAS,aAAa,KAAK,WAAW,SAAS,YAAY,KAAK,WAAW,SAAS,iBAAiB,GAClH,OAAO;CACT,IAAI,WAAW,SAAS,eAAe,KAAK,WAAW,SAAS,QAAQ,KAAK,WAAW,SAAS,QAAQ,GACvG,OAAO;CACT,IAAI,WAAW,SAAS,aAAa,GAAG,OAAO;CAC/C,IAAI,WAAW,SAAS,SAAS,KAAK,WAAW,SAAS,UAAU,GAAG,OAAO;AAEhF;AAEA,eAAe,cAAc,KAAgD;CAC3E,IAAI;EACF,MAAM,QAAQ,MAAM,MAAM,SAAS,KAAK,KAAK,YAAY,aAAa,GAAG,MAAM,CAAC;EAGhF,OAAO;GAAE,GAAG;GAAmB,GAAI,MAAM,aAAa,CAAC;EAAG;CAC5D,QAAQ;EACN,OAAO;CACT;AACF;AAEA,MAAM,cAAc;AAEpB,eAAe,uBACb,MACA,SACsB;CACtB,MAAM,2BAAW,IAAI,IAAY;CACjC,KAAK,MAAM,OAAO,MAAM;EACtB,MAAM,SAAS,IAAI;EACnB,IAAI,CAAC,UAAU,CAAC,YAAY,KAAK,MAAM,GAAG;EAC1C,SAAS,IAAI,MAAM;EACnB,MAAM,OAAO,KAAK,SAAS,OAAO,MAAM,GAAG,CAAC,GAAG,MAAM;EACrD,IAAI;GACF,MAAM,QAAQ,MAAM,SAAS,MAAM,MAAM,EAAA,CAAG,SAAS;GACrD,KAAK,MAAM,WAAW,KAAK,MAAM,eAAe,KAAK,CAAC,GAAG,SAAS,IAAI,OAAO;EAC/E,QAAQ,CAER;CACF;CACA,OAAO;AACT;AAEA,eAAe,OAAO,KAA6D;CACjF,MAAM,QAAS,MAAM,IAAI,UAAU;CACnC,IAAI;EACF,MAAM,OAAO,MAAM,MAAM,SAAS;GAAE,OAAO;GAAS,QAAQ;EAAE,CAAC;EAC/D,MAAM,YAAY,MAAM,cAAc,IAAI,GAAG;EAC7C,MAAM,OAAO,KAAK,IAAI,KAAK,YAAY,SAAS,MAAM;EACtD,MAAM,UAAU,KAAK,IAAI,KAAK,YAAY,SAAS,KAAK;EACxD,MAAM,MAAM,KAAK,MAAM,IAAI,MAAM,IAAI,CAAC;EACtC,MAAM,UAAoB,CAAC;EAC3B,KAAK,MAAM,OAAO,MAAM;GACtB,IAAI,CAAC,SAAS,IAAI,IAAI,KAAK,GAAG;GAC9B,IAAI,IAAI,aAAa,OAAO,MAAM,gBAAgB,YAChD,QAAQ,KAAK;IAAE,MAAM;IAAgB,MAAM,yBAAyB,IAAI;IAAS,OAAO,IAAI;GAAM,CAAC;GAErG,KAAK,MAAM,QAAQ,MAAM,WAAW,KAAK,MAAM,IAAI,KAAK,CAAC,GAAG;IAC1D,MAAM,OAAO,SAAS,IAAI;IAC1B,IAAI,CAAC,MAAM;IACX,MAAM,MAAM,KAAK,IAAI,GAAG,OAAO,MAAM,KAAK,IAAI,EAAA,CAAG,OAAO;IACxD,IAAI,SAAS,eAAe,OAAO,UAAU,oBAAoB,OAC/D,QAAQ,KAAK;KAAE,MAAM;KAAY;KAAM,OAAO,IAAI;IAAM,CAAC;SACtD,IAAI,SAAS,gBAAgB,OAAO,UAAU,kBAAkB,OACnE,QAAQ,KAAK;KAAE,MAAM;KAAU;KAAM,OAAO,IAAI;IAAM,CAAC;SACpD,IAAI,SAAS,cAAc,OAAO,UAAU,gBAAgB,OAC/D,QAAQ,KAAK;KAAE,MAAM;KAAU;KAAM,OAAO,IAAI;IAAM,CAAC;SACpD,IAAI,SAAS,WAAW,OAAO,OAAY,QAAQ,KAAK;KAAE,MAAM;KAAU;KAAM,OAAO,IAAI;IAAM,CAAC;GACzG;EACF;EACA,MAAM,cAAc,MAAM,uBAAuB,MAAM,OAAO;EAC9D,KAAK,MAAM,QAAQ,MAAM,WAAW,OAAO,GAAG;GAC5C,MAAM,UAAU,KAAK,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,KAAK;GAC1C,IAAI,YAAY,KAAK,OAAO,KAAK,CAAC,YAAY,IAAI,OAAO,GACvD,QAAQ,KAAK;IAAE,MAAM;IAAc;IAAM,OAAO;GAAM,CAAC;EAC3D;EACA,OAAO,QAAQ,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;CAC5D,UAAU;EACR,MAAM,MAAM,MAAM;CACpB;AACF;AAEA,eAAe,YAAY,QAAgB,OAAsC;CAC/E,IAAI,OAAO,SAAS,gBAAgB;EAClC,MAAM,MAAM,cAAc,OAAO,KAAK;EACtC;CACF;CACA,IAAI,OAAO,SAAS,YAAY,OAAO,SAAS,cAAc,OAAO,GAAG,OAAO,MAAM,EAAE,OAAO,KAAK,CAAC;CACpG,MAAM,aAAa,MAAM,UAAU,MAAM,SAAS,OAAO,IAAI,CAAC;CAC9D,MAAM,UAAU,GAAG,OAAO,KAAK,MAAM,YAAY,EAAE,MAAM,IAAM,CAAC;CAChE,MAAM,GAAG,OAAO,MAAM,EAAE,OAAO,KAAK,CAAC;AACvC;AAEA,MAAM,KAAoB;CACxB,MAAM;CACN,MAAM,IAAI,KAAK,MAAM;EACnB,MAAM,SAAS,KAAK,YAAY,SAAS,WAAW;EACpD,MAAM,QAAQ,KAAK,YAAY,SAAS,SAAS;EACjD,IAAI,WAAW,OAAO,OAAO;EAC7B,MAAM,UAAU,MAAM,OAAO,GAAG;EAChC,IAAI,OAAO;GACT,MAAM,QAAS,MAAM,IAAI,UAAU;GACnC,IAAI;IACF,KAAK,MAAM,UAAU,SAAS,MAAM,YAAY,QAAQ,KAAK;GAC/D,UAAU;IACR,MAAM,MAAM,MAAM;GACpB;EACF;EACA,IAAI,MAAM,OAAO,MAAM,GAAG,KAAK,UAAU;GAAE,MAAM,SAAS,YAAY;GAAS;EAAQ,CAAC,EAAE,GAAG;EAC7F,OAAO;CACT;AACF"}