@intentius/chant 0.79.0 → 0.81.0

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 (462) hide show
  1. package/dist/audit/core.d.ts.map +1 -1
  2. package/dist/audit/discover.d.ts +30 -0
  3. package/dist/audit/discover.d.ts.map +1 -1
  4. package/dist/audit/report-html.d.ts +5 -0
  5. package/dist/audit/report-html.d.ts.map +1 -1
  6. package/dist/audit/report-model.d.ts +13 -0
  7. package/dist/audit/report-model.d.ts.map +1 -1
  8. package/dist/audit/terraform-state.d.ts +13 -0
  9. package/dist/audit/terraform-state.d.ts.map +1 -1
  10. package/dist/cli/command-group.d.ts +6 -0
  11. package/dist/cli/command-group.d.ts.map +1 -1
  12. package/dist/cli/commands/audit.d.ts +44 -3
  13. package/dist/cli/commands/audit.d.ts.map +1 -1
  14. package/dist/cli/commands/check-lexicon-examples.d.ts.map +1 -1
  15. package/dist/cli/commands/check-lexicon.d.ts +8 -0
  16. package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
  17. package/dist/cli/commands/doctor.d.ts.map +1 -1
  18. package/dist/cli/commands/import-agents.d.ts.map +1 -1
  19. package/dist/cli/commands/init.d.ts +6 -0
  20. package/dist/cli/commands/init.d.ts.map +1 -1
  21. package/dist/cli/commands/lint.d.ts.map +1 -1
  22. package/dist/cli/commands/onboard.d.ts.map +1 -1
  23. package/dist/cli/commands/update.d.ts +6 -0
  24. package/dist/cli/commands/update.d.ts.map +1 -1
  25. package/dist/cli/commands/vendor.d.ts +5 -0
  26. package/dist/cli/commands/vendor.d.ts.map +1 -1
  27. package/dist/cli/conflict-check.d.ts +6 -0
  28. package/dist/cli/conflict-check.d.ts.map +1 -1
  29. package/dist/cli/handlers/build.d.ts.map +1 -1
  30. package/dist/cli/handlers/components.d.ts.map +1 -1
  31. package/dist/cli/handlers/dev.d.ts.map +1 -1
  32. package/dist/cli/handlers/fan-out.d.ts.map +1 -1
  33. package/dist/cli/handlers/init.d.ts.map +1 -1
  34. package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
  35. package/dist/cli/handlers/misc.d.ts.map +1 -1
  36. package/dist/cli/handlers/operator.d.ts +7 -0
  37. package/dist/cli/handlers/operator.d.ts.map +1 -1
  38. package/dist/cli/handlers/promote.d.ts +20 -0
  39. package/dist/cli/handlers/promote.d.ts.map +1 -0
  40. package/dist/cli/handlers/run-generate.d.ts +39 -0
  41. package/dist/cli/handlers/run-generate.d.ts.map +1 -0
  42. package/dist/cli/handlers/run.d.ts +4 -1
  43. package/dist/cli/handlers/run.d.ts.map +1 -1
  44. package/dist/cli/handlers/vendor.d.ts +8 -3
  45. package/dist/cli/handlers/vendor.d.ts.map +1 -1
  46. package/dist/cli/main.d.ts +12 -0
  47. package/dist/cli/main.d.ts.map +1 -1
  48. package/dist/cli/mcp/op-tools.d.ts.map +1 -1
  49. package/dist/cli/plugins.d.ts +14 -0
  50. package/dist/cli/plugins.d.ts.map +1 -1
  51. package/dist/cli/registry.d.ts +46 -1
  52. package/dist/cli/registry.d.ts.map +1 -1
  53. package/dist/codegen/fetch.d.ts +10 -0
  54. package/dist/codegen/fetch.d.ts.map +1 -1
  55. package/dist/components/capability-plugin-loader.d.ts.map +1 -1
  56. package/dist/components/cli-support.d.ts +3 -1
  57. package/dist/components/cli-support.d.ts.map +1 -1
  58. package/dist/components/discover.d.ts.map +1 -1
  59. package/dist/components/driver.d.ts +16 -2
  60. package/dist/components/driver.d.ts.map +1 -1
  61. package/dist/components/fan-out-support.d.ts.map +1 -1
  62. package/dist/components/gate-plan.d.ts +40 -0
  63. package/dist/components/gate-plan.d.ts.map +1 -0
  64. package/dist/components/pilots/alb-ecs.pilot.d.ts +7 -1
  65. package/dist/components/pilots/alb-ecs.pilot.d.ts.map +1 -1
  66. package/dist/components/presets/ecs-fargate.d.ts +10 -4
  67. package/dist/components/presets/ecs-fargate.d.ts.map +1 -1
  68. package/dist/components/promote.d.ts +264 -0
  69. package/dist/components/promote.d.ts.map +1 -0
  70. package/dist/components/sandbox/driver.d.ts.map +1 -1
  71. package/dist/components/sandbox/run.d.ts.map +1 -1
  72. package/dist/components/verbs/build-archive.d.ts +22 -11
  73. package/dist/components/verbs/build-archive.d.ts.map +1 -1
  74. package/dist/components/verbs/run-agent.d.ts.map +1 -1
  75. package/dist/config-static.d.ts +59 -0
  76. package/dist/config-static.d.ts.map +1 -0
  77. package/dist/config.d.ts +104 -5
  78. package/dist/config.d.ts.map +1 -1
  79. package/dist/content-digest.d.ts +17 -0
  80. package/dist/content-digest.d.ts.map +1 -0
  81. package/dist/discovery/files.d.ts +48 -4
  82. package/dist/discovery/files.d.ts.map +1 -1
  83. package/dist/discovery/fold-import.d.ts +32 -1
  84. package/dist/discovery/fold-import.d.ts.map +1 -1
  85. package/dist/discovery/index.d.ts.map +1 -1
  86. package/dist/discovery/sandbox/driver.d.ts +20 -0
  87. package/dist/discovery/sandbox/driver.d.ts.map +1 -1
  88. package/dist/discovery/sandbox/fork.d.ts +20 -0
  89. package/dist/discovery/sandbox/fork.d.ts.map +1 -1
  90. package/dist/discovery/walk.d.ts +119 -0
  91. package/dist/discovery/walk.d.ts.map +1 -0
  92. package/dist/graph-ir.d.ts +17 -1
  93. package/dist/graph-ir.d.ts.map +1 -1
  94. package/dist/join-key.d.ts +31 -0
  95. package/dist/join-key.d.ts.map +1 -0
  96. package/dist/lexicon-module.d.ts +110 -0
  97. package/dist/lexicon-module.d.ts.map +1 -0
  98. package/dist/lexicon.d.ts +82 -1
  99. package/dist/lexicon.d.ts.map +1 -1
  100. package/dist/lifecycle/build-ledger-store.d.ts.map +1 -1
  101. package/dist/lifecycle/digest.d.ts +5 -1
  102. package/dist/lifecycle/digest.d.ts.map +1 -1
  103. package/dist/lifecycle/gate-ledger.d.ts +20 -5
  104. package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
  105. package/dist/lifecycle/git.d.ts +64 -0
  106. package/dist/lifecycle/git.d.ts.map +1 -1
  107. package/dist/lifecycle/lease.d.ts +8 -1
  108. package/dist/lifecycle/lease.d.ts.map +1 -1
  109. package/dist/lifecycle/legacy-digest.d.ts +59 -0
  110. package/dist/lifecycle/legacy-digest.d.ts.map +1 -0
  111. package/dist/lifecycle/member-ledger.d.ts +67 -0
  112. package/dist/lifecycle/member-ledger.d.ts.map +1 -0
  113. package/dist/lifecycle/plan-digest.d.ts +4 -3
  114. package/dist/lifecycle/plan-digest.d.ts.map +1 -1
  115. package/dist/lifecycle/release-ledger.d.ts +42 -1
  116. package/dist/lifecycle/release-ledger.d.ts.map +1 -1
  117. package/dist/lifecycle/status.d.ts +90 -5
  118. package/dist/lifecycle/status.d.ts.map +1 -1
  119. package/dist/lint/rules/comp/comp003-mutating-no-rollback.d.ts +2 -3
  120. package/dist/lint/rules/comp/comp003-mutating-no-rollback.d.ts.map +1 -1
  121. package/dist/op/activities/activity-contracts.d.ts +32 -0
  122. package/dist/op/activities/activity-contracts.d.ts.map +1 -1
  123. package/dist/op/activities/index.d.ts +2 -0
  124. package/dist/op/activities/index.d.ts.map +1 -1
  125. package/dist/op/activities/propose-upgrade.d.ts +75 -0
  126. package/dist/op/activities/propose-upgrade.d.ts.map +1 -0
  127. package/dist/op/activity-contract-registry.d.ts.map +1 -1
  128. package/dist/op/activity-registry.d.ts.map +1 -1
  129. package/dist/op/discover.d.ts.map +1 -1
  130. package/dist/op/gate-approval.d.ts.map +1 -1
  131. package/dist/op/gate-name.d.ts +7 -0
  132. package/dist/op/gate-name.d.ts.map +1 -1
  133. package/dist/op/gate-summary.d.ts +16 -0
  134. package/dist/op/gate-summary.d.ts.map +1 -1
  135. package/dist/op/gate.d.ts +16 -2
  136. package/dist/op/gate.d.ts.map +1 -1
  137. package/dist/op/generate-pipeline.d.ts.map +1 -1
  138. package/dist/op/runtimes/local.d.ts.map +1 -1
  139. package/dist/project-root.d.ts +26 -0
  140. package/dist/project-root.d.ts.map +1 -1
  141. package/dist/workspace/__fixtures__/contract-repo.d.ts +37 -0
  142. package/dist/workspace/__fixtures__/contract-repo.d.ts.map +1 -0
  143. package/dist/workspace/__fixtures__/two-member-workspace.d.ts +11 -0
  144. package/dist/workspace/__fixtures__/two-member-workspace.d.ts.map +1 -0
  145. package/dist/workspace/checks/generated.d.ts +156 -0
  146. package/dist/workspace/checks/generated.d.ts.map +1 -0
  147. package/dist/workspace/checks/ledgers.d.ts +78 -0
  148. package/dist/workspace/checks/ledgers.d.ts.map +1 -0
  149. package/dist/workspace/checks/links.d.ts +25 -0
  150. package/dist/workspace/checks/links.d.ts.map +1 -0
  151. package/dist/workspace/checks/pipelines.d.ts +76 -0
  152. package/dist/workspace/checks/pipelines.d.ts.map +1 -0
  153. package/dist/workspace/checks.d.ts +161 -0
  154. package/dist/workspace/checks.d.ts.map +1 -0
  155. package/dist/workspace/compose-graph.d.ts +115 -0
  156. package/dist/workspace/compose-graph.d.ts.map +1 -0
  157. package/dist/workspace/compose-reports.d.ts +68 -0
  158. package/dist/workspace/compose-reports.d.ts.map +1 -0
  159. package/dist/workspace/declaration.d.ts +220 -0
  160. package/dist/workspace/declaration.d.ts.map +1 -0
  161. package/dist/workspace/declaration.schema.json +434 -0
  162. package/dist/workspace/generated-files.d.ts +65 -0
  163. package/dist/workspace/generated-files.d.ts.map +1 -0
  164. package/dist/workspace/glob.d.ts +13 -0
  165. package/dist/workspace/glob.d.ts.map +1 -0
  166. package/dist/workspace/graph-cli.d.ts +75 -0
  167. package/dist/workspace/graph-cli.d.ts.map +1 -0
  168. package/dist/workspace/init.d.ts +96 -0
  169. package/dist/workspace/init.d.ts.map +1 -0
  170. package/dist/workspace/jsonc.d.ts +38 -0
  171. package/dist/workspace/jsonc.d.ts.map +1 -0
  172. package/dist/workspace/kinds.d.ts +192 -0
  173. package/dist/workspace/kinds.d.ts.map +1 -0
  174. package/dist/workspace/lineage-check.d.ts +84 -0
  175. package/dist/workspace/lineage-check.d.ts.map +1 -0
  176. package/dist/workspace/lineage-cli.d.ts +31 -0
  177. package/dist/workspace/lineage-cli.d.ts.map +1 -0
  178. package/dist/workspace/lineage-init.d.ts +101 -0
  179. package/dist/workspace/lineage-init.d.ts.map +1 -0
  180. package/dist/workspace/lineage-lock.d.ts +258 -0
  181. package/dist/workspace/lineage-lock.d.ts.map +1 -0
  182. package/dist/workspace/lineage-merge.d.ts +14 -0
  183. package/dist/workspace/lineage-merge.d.ts.map +1 -0
  184. package/dist/workspace/lineage-migrations.d.ts +178 -0
  185. package/dist/workspace/lineage-migrations.d.ts.map +1 -0
  186. package/dist/workspace/lineage-update.d.ts +59 -0
  187. package/dist/workspace/lineage-update.d.ts.map +1 -0
  188. package/dist/workspace/lineage-upgrade-cli.d.ts +40 -0
  189. package/dist/workspace/lineage-upgrade-cli.d.ts.map +1 -0
  190. package/dist/workspace/lineage-upgrade.d.ts +146 -0
  191. package/dist/workspace/lineage-upgrade.d.ts.map +1 -0
  192. package/dist/workspace/lineage-vendor.d.ts +63 -0
  193. package/dist/workspace/lineage-vendor.d.ts.map +1 -0
  194. package/dist/workspace/lineage-version.d.ts +32 -0
  195. package/dist/workspace/lineage-version.d.ts.map +1 -0
  196. package/dist/workspace/links.d.ts +150 -0
  197. package/dist/workspace/links.d.ts.map +1 -0
  198. package/dist/workspace/ls.d.ts +99 -0
  199. package/dist/workspace/ls.d.ts.map +1 -0
  200. package/dist/workspace/member-commands.d.ts +139 -0
  201. package/dist/workspace/member-commands.d.ts.map +1 -0
  202. package/dist/workspace/member-handles.d.ts +54 -0
  203. package/dist/workspace/member-handles.d.ts.map +1 -0
  204. package/dist/workspace/member-pipeline.d.ts +143 -0
  205. package/dist/workspace/member-pipeline.d.ts.map +1 -0
  206. package/dist/workspace/member-run.d.ts +67 -0
  207. package/dist/workspace/member-run.d.ts.map +1 -0
  208. package/dist/workspace/reason-codes.d.ts +65 -0
  209. package/dist/workspace/reason-codes.d.ts.map +1 -0
  210. package/dist/workspace/record-source.d.ts +22 -0
  211. package/dist/workspace/record-source.d.ts.map +1 -0
  212. package/dist/workspace/records-cli.d.ts +81 -0
  213. package/dist/workspace/records-cli.d.ts.map +1 -0
  214. package/dist/workspace/records.d.ts +118 -0
  215. package/dist/workspace/records.d.ts.map +1 -0
  216. package/dist/workspace/root-refusal.d.ts +53 -0
  217. package/dist/workspace/root-refusal.d.ts.map +1 -0
  218. package/dist/workspace/status.d.ts +150 -0
  219. package/dist/workspace/status.d.ts.map +1 -0
  220. package/dist/workspace/template-manifest.d.ts +90 -0
  221. package/dist/workspace/template-manifest.d.ts.map +1 -0
  222. package/dist/workspace/tree.d.ts +36 -0
  223. package/dist/workspace/tree.d.ts.map +1 -0
  224. package/dist/workspace/trust/attestor.d.ts +67 -0
  225. package/dist/workspace/trust/attestor.d.ts.map +1 -0
  226. package/dist/workspace/trust/policy.d.ts +111 -0
  227. package/dist/workspace/trust/policy.d.ts.map +1 -0
  228. package/dist/workspace/trust/provenance.d.ts +67 -0
  229. package/dist/workspace/trust/provenance.d.ts.map +1 -0
  230. package/dist/workspace/trust/ssh-commit.d.ts +43 -0
  231. package/dist/workspace/trust/ssh-commit.d.ts.map +1 -0
  232. package/dist/workspace/trust/test-repo.d.ts +42 -0
  233. package/dist/workspace/trust/test-repo.d.ts.map +1 -0
  234. package/dist/workspace/trust/verify-cli.d.ts +9 -0
  235. package/dist/workspace/trust/verify-cli.d.ts.map +1 -0
  236. package/dist/workspace/trust/verify.d.ts +66 -0
  237. package/dist/workspace/trust/verify.d.ts.map +1 -0
  238. package/dist/workspace/which-chant.d.ts +58 -0
  239. package/dist/workspace/which-chant.d.ts.map +1 -0
  240. package/dist/workspace/workspace-kinds.schema.json +133 -0
  241. package/package.json +4 -3
  242. package/src/audit/core.ts +13 -2
  243. package/src/audit/discover.ts +102 -36
  244. package/src/audit/report-html.ts +3 -1
  245. package/src/audit/report-model.ts +14 -1
  246. package/src/audit/terraform-state.test.ts +81 -1
  247. package/src/audit/terraform-state.ts +25 -0
  248. package/src/cli/command-group.ts +7 -0
  249. package/src/cli/commands/audit-truncation.test.ts +167 -0
  250. package/src/cli/commands/audit.ts +132 -29
  251. package/src/cli/commands/check-lexicon-examples.ts +2 -1
  252. package/src/cli/commands/check-lexicon.ts +24 -0
  253. package/src/cli/commands/doctor.test.ts +30 -0
  254. package/src/cli/commands/doctor.ts +21 -0
  255. package/src/cli/commands/import-agents.ts +15 -4
  256. package/src/cli/commands/init.ts +76 -25
  257. package/src/cli/commands/lint.test.ts +52 -1
  258. package/src/cli/commands/lint.ts +41 -71
  259. package/src/cli/commands/onboard.ts +15 -0
  260. package/src/cli/commands/update.ts +18 -5
  261. package/src/cli/commands/vendor.ts +11 -6
  262. package/src/cli/conflict-check.test.ts +18 -1
  263. package/src/cli/conflict-check.ts +13 -0
  264. package/src/cli/discovery-skip.test.ts +168 -0
  265. package/src/cli/handlers/__snapshots__/build-member.test.ts.snap +38 -0
  266. package/src/cli/handlers/build-member.test.ts +127 -0
  267. package/src/cli/handlers/build.test.ts +11 -0
  268. package/src/cli/handlers/build.ts +35 -1
  269. package/src/cli/handlers/components.test.ts +127 -0
  270. package/src/cli/handlers/components.ts +70 -8
  271. package/src/cli/handlers/dev.ts +6 -1
  272. package/src/cli/handlers/fan-out.ts +9 -2
  273. package/src/cli/handlers/graph.test.ts +20 -0
  274. package/src/cli/handlers/graph.ts +4 -2
  275. package/src/cli/handlers/init.ts +54 -1
  276. package/src/cli/handlers/lifecycle.ts +2 -1
  277. package/src/cli/handlers/misc.ts +16 -0
  278. package/src/cli/handlers/operator.test.ts +68 -0
  279. package/src/cli/handlers/operator.ts +39 -10
  280. package/src/cli/handlers/promote.test.ts +377 -0
  281. package/src/cli/handlers/promote.ts +391 -0
  282. package/src/cli/handlers/run-generate-member.test.ts +83 -0
  283. package/src/cli/handlers/run-generate.test.ts +158 -0
  284. package/src/cli/handlers/run-generate.ts +168 -0
  285. package/src/cli/handlers/run.test.ts +47 -0
  286. package/src/cli/handlers/run.ts +41 -6
  287. package/src/cli/handlers/vendor.ts +147 -43
  288. package/src/cli/main.test.ts +137 -1
  289. package/src/cli/main.ts +290 -24
  290. package/src/cli/mcp/op-tools.ts +2 -1
  291. package/src/cli/path-lexicon-messages.test.ts +152 -0
  292. package/src/cli/plugins.ts +43 -5
  293. package/src/cli/registry.ts +46 -1
  294. package/src/cli/static-config-read.test.ts +208 -0
  295. package/src/codegen/fetch.test.ts +63 -1
  296. package/src/codegen/fetch.ts +30 -1
  297. package/src/components/__fixtures__/alb-ecs-service.json +0 -8
  298. package/src/components/capability-plugin-loader.ts +3 -1
  299. package/src/components/cli-support.ts +9 -3
  300. package/src/components/discover.test.ts +27 -57
  301. package/src/components/discover.ts +20 -51
  302. package/src/components/driver.test.ts +90 -12
  303. package/src/components/driver.ts +43 -5
  304. package/src/components/fan-out-support.ts +2 -1
  305. package/src/components/gate-plan.ts +53 -0
  306. package/src/components/pilots/README.md +1 -1
  307. package/src/components/pilots/alb-ecs.pilot.ts +7 -7
  308. package/src/components/presets/ecs-fargate.ts +10 -5
  309. package/src/components/promote.test.ts +645 -0
  310. package/src/components/promote.ts +641 -0
  311. package/src/components/sandbox/driver.ts +8 -8
  312. package/src/components/sandbox/run.ts +4 -1
  313. package/src/components/verbs/build-archive.ts +27 -21
  314. package/src/components/verbs/run-agent.ts +3 -5
  315. package/src/config-static.test.ts +136 -0
  316. package/src/config-static.ts +232 -0
  317. package/src/config.test.ts +40 -0
  318. package/src/config.ts +161 -11
  319. package/src/content-digest.test.ts +120 -0
  320. package/src/content-digest.ts +21 -0
  321. package/src/discovery/files.test.ts +127 -1
  322. package/src/discovery/files.ts +91 -59
  323. package/src/discovery/fold-import.ts +127 -9
  324. package/src/discovery/index.ts +1 -0
  325. package/src/discovery/sandbox/driver.ts +73 -35
  326. package/src/discovery/sandbox/fork-diagnostic.test.ts +136 -13
  327. package/src/discovery/sandbox/fork.ts +83 -43
  328. package/src/discovery/sandbox/path-lexicon-multifile.test.ts +285 -0
  329. package/src/discovery/sandbox/path-lexicon-parity.test.ts +162 -0
  330. package/src/discovery/walk.test.ts +263 -0
  331. package/src/discovery/walk.ts +334 -0
  332. package/src/effect-receipt.ts +2 -2
  333. package/src/graph-ir.ts +18 -1
  334. package/src/join-key.test.ts +25 -0
  335. package/src/join-key.ts +39 -0
  336. package/src/lexicon-module.test.ts +203 -0
  337. package/src/lexicon-module.ts +215 -0
  338. package/src/lexicon.ts +105 -1
  339. package/src/lifecycle/build-ledger-store.ts +13 -2
  340. package/src/lifecycle/digest.ts +9 -5
  341. package/src/lifecycle/gate-ledger.test.ts +20 -0
  342. package/src/lifecycle/gate-ledger.ts +28 -6
  343. package/src/lifecycle/git.test.ts +13 -1
  344. package/src/lifecycle/git.ts +221 -176
  345. package/src/lifecycle/lease.ts +25 -10
  346. package/src/lifecycle/legacy-digest.test.ts +206 -0
  347. package/src/lifecycle/legacy-digest.ts +103 -0
  348. package/src/lifecycle/member-ledger.test.ts +319 -0
  349. package/src/lifecycle/member-ledger.ts +145 -0
  350. package/src/lifecycle/plan-digest.ts +7 -7
  351. package/src/lifecycle/release-ledger.test.ts +20 -0
  352. package/src/lifecycle/release-ledger.ts +53 -3
  353. package/src/lifecycle/status.test.ts +151 -1
  354. package/src/lifecycle/status.ts +228 -7
  355. package/src/lint/rules/comp/comp003-mutating-no-rollback.ts +2 -3
  356. package/src/op/activities/activity-contracts.ts +37 -0
  357. package/src/op/activities/index.ts +10 -0
  358. package/src/op/activities/propose-upgrade.ts +206 -0
  359. package/src/op/activity-contract-registry.ts +18 -4
  360. package/src/op/activity-registry.ts +26 -0
  361. package/src/op/discover.test.ts +14 -4
  362. package/src/op/discover.ts +19 -23
  363. package/src/op/gate-approval.ts +3 -1
  364. package/src/op/gate-name.ts +8 -0
  365. package/src/op/gate-summary.test.ts +14 -0
  366. package/src/op/gate-summary.ts +25 -2
  367. package/src/op/gate.ts +36 -7
  368. package/src/op/generate-pipeline.test.ts +14 -5
  369. package/src/op/generate-pipeline.ts +2 -1
  370. package/src/op/runtimes/local.ts +2 -1
  371. package/src/project-root.test.ts +66 -1
  372. package/src/project-root.ts +48 -0
  373. package/src/workspace/__fixtures__/contract-repo.ts +100 -0
  374. package/src/workspace/__fixtures__/two-member-workspace.ts +35 -0
  375. package/src/workspace/behold-kinds.test.ts +162 -0
  376. package/src/workspace/check-contract.test.ts +127 -0
  377. package/src/workspace/check.schema.json +347 -0
  378. package/src/workspace/checks/generated.test.ts +287 -0
  379. package/src/workspace/checks/generated.ts +475 -0
  380. package/src/workspace/checks/ledgers.test.ts +139 -0
  381. package/src/workspace/checks/ledgers.ts +246 -0
  382. package/src/workspace/checks/links.ts +183 -0
  383. package/src/workspace/checks/pipelines.test.ts +133 -0
  384. package/src/workspace/checks/pipelines.ts +225 -0
  385. package/src/workspace/checks-repo.test.ts +71 -0
  386. package/src/workspace/checks.test.ts +450 -0
  387. package/src/workspace/checks.ts +461 -0
  388. package/src/workspace/compose-graph.test.ts +146 -0
  389. package/src/workspace/compose-graph.ts +228 -0
  390. package/src/workspace/compose-reports.test.ts +109 -0
  391. package/src/workspace/compose-reports.ts +186 -0
  392. package/src/workspace/declaration.schema.json +434 -0
  393. package/src/workspace/declaration.test.ts +347 -0
  394. package/src/workspace/declaration.ts +623 -0
  395. package/src/workspace/generated-files.test.ts +114 -0
  396. package/src/workspace/generated-files.ts +94 -0
  397. package/src/workspace/glob.ts +49 -0
  398. package/src/workspace/graph-cli.ts +220 -0
  399. package/src/workspace/graph-contract.test.ts +204 -0
  400. package/src/workspace/graph.schema.json +662 -0
  401. package/src/workspace/init.test.ts +223 -0
  402. package/src/workspace/init.ts +585 -0
  403. package/src/workspace/jsonc.test.ts +65 -0
  404. package/src/workspace/jsonc.ts +231 -0
  405. package/src/workspace/kinds.test.ts +288 -0
  406. package/src/workspace/kinds.ts +506 -0
  407. package/src/workspace/lineage-check.ts +264 -0
  408. package/src/workspace/lineage-cli.ts +114 -0
  409. package/src/workspace/lineage-init.test.ts +231 -0
  410. package/src/workspace/lineage-init.ts +365 -0
  411. package/src/workspace/lineage-lock.test.ts +224 -0
  412. package/src/workspace/lineage-lock.ts +357 -0
  413. package/src/workspace/lineage-merge.ts +44 -0
  414. package/src/workspace/lineage-migrations.test.ts +178 -0
  415. package/src/workspace/lineage-migrations.ts +343 -0
  416. package/src/workspace/lineage-update.ts +160 -0
  417. package/src/workspace/lineage-upgrade-cli.ts +151 -0
  418. package/src/workspace/lineage-upgrade.test.ts +474 -0
  419. package/src/workspace/lineage-upgrade.ts +624 -0
  420. package/src/workspace/lineage-vendor.test.ts +126 -0
  421. package/src/workspace/lineage-vendor.ts +215 -0
  422. package/src/workspace/lineage-version.ts +127 -0
  423. package/src/workspace/links.test.ts +344 -0
  424. package/src/workspace/links.ts +331 -0
  425. package/src/workspace/ls-contract.test.ts +200 -0
  426. package/src/workspace/ls.schema.json +190 -0
  427. package/src/workspace/ls.ts +254 -0
  428. package/src/workspace/member-commands.e2e.test.ts +244 -0
  429. package/src/workspace/member-commands.test.ts +217 -0
  430. package/src/workspace/member-commands.ts +540 -0
  431. package/src/workspace/member-handles.test.ts +76 -0
  432. package/src/workspace/member-handles.ts +228 -0
  433. package/src/workspace/member-pipeline.test.ts +135 -0
  434. package/src/workspace/member-pipeline.ts +317 -0
  435. package/src/workspace/member-run.ts +146 -0
  436. package/src/workspace/read-contract.test.ts +129 -0
  437. package/src/workspace/reason-codes.test.ts +113 -0
  438. package/src/workspace/reason-codes.ts +81 -0
  439. package/src/workspace/record-source.ts +117 -0
  440. package/src/workspace/records-cli.ts +198 -0
  441. package/src/workspace/records-contract.test.ts +93 -0
  442. package/src/workspace/records.schema.json +150 -0
  443. package/src/workspace/records.test.ts +243 -0
  444. package/src/workspace/records.ts +384 -0
  445. package/src/workspace/root-refusal.ts +97 -0
  446. package/src/workspace/status-contract.test.ts +308 -0
  447. package/src/workspace/status.schema.json +210 -0
  448. package/src/workspace/status.ts +412 -0
  449. package/src/workspace/template-manifest.test.ts +79 -0
  450. package/src/workspace/template-manifest.ts +221 -0
  451. package/src/workspace/tree.ts +132 -0
  452. package/src/workspace/trust/attestor.ts +93 -0
  453. package/src/workspace/trust/policy.ts +306 -0
  454. package/src/workspace/trust/provenance.ts +181 -0
  455. package/src/workspace/trust/ssh-commit.ts +138 -0
  456. package/src/workspace/trust/test-repo.ts +138 -0
  457. package/src/workspace/trust/trust.test.ts +397 -0
  458. package/src/workspace/trust/verify-cli.ts +56 -0
  459. package/src/workspace/trust/verify.ts +194 -0
  460. package/src/workspace/which-chant.test.ts +117 -0
  461. package/src/workspace/which-chant.ts +151 -0
  462. package/src/workspace/workspace-kinds.schema.json +133 -0
@@ -0,0 +1,114 @@
1
+ import { mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { dirname, join } from "node:path";
4
+ import { afterAll, describe, expect, test } from "vitest";
5
+ import { classifyFile, declaredFilesFor, IGNORED_DIRS, IMPLICIT_GENERATED, SEED_FILES } from "./generated-files";
6
+ import { declaredFilesAt, fileEntries, fileHash, LockError, type Lineage } from "./lineage-lock";
7
+ import { applyUpstream } from "./lineage-update";
8
+
9
+ const scratch: string[] = [];
10
+ afterAll(() => {
11
+ for (const d of scratch) rmSync(d, { recursive: true, force: true });
12
+ });
13
+ function tree(files: Record<string, string>): string {
14
+ const root = realpathSync(mkdtempSync(join(tmpdir(), "chant-generated-files-")));
15
+ scratch.push(root);
16
+ mkdirSync(join(root, ".git"));
17
+ for (const [path, text] of Object.entries(files)) {
18
+ mkdirSync(dirname(join(root, path)), { recursive: true });
19
+ writeFileSync(join(root, path), text);
20
+ }
21
+ return root;
22
+ }
23
+ const bytes = (o: Record<string, string>) => new Map(Object.entries(o).map(([k, v]) => [k, Buffer.from(v)]));
24
+
25
+ const DECLARATION = JSON.stringify({
26
+ name: "acme",
27
+ schema: 1,
28
+ members: [
29
+ {
30
+ name: "app",
31
+ dir: "apps/app",
32
+ kind: "chant",
33
+ generated: [
34
+ { path: "ci.yml", generator: "chant build ci --lexicon github -o ci.yml" },
35
+ { path: "skills/custom/SKILL.md", generator: "chant update", handWritten: { because: "trimmed by hand" } },
36
+ ],
37
+ },
38
+ ],
39
+ });
40
+
41
+ describe("the D14 file classes (#2541)", () => {
42
+ test("core's list: SKILL.md generated by chant update, .mcp.json a seed, .chant/types ignored", () => {
43
+ expect(IMPLICIT_GENERATED.map((i) => [i.glob, i.command])).toEqual([["skills/*/SKILL.md", "chant update"]]);
44
+ expect(SEED_FILES).toEqual([".mcp.json"]);
45
+ expect(IGNORED_DIRS).toEqual([".chant/types"]);
46
+ expect(classifyFile("skills/chant-aws/SKILL.md")).toEqual({ class: "generated", command: "chant update" });
47
+ expect(classifyFile("skills/a/b/SKILL.md")).toEqual({ class: "owned" });
48
+ expect(classifyFile(".mcp.json")).toEqual({ class: "seed" });
49
+ expect(classifyFile(".chant/types/core/index.d.ts")).toEqual({ class: "ignored" });
50
+ expect(classifyFile("src/main.ts")).toEqual({ class: "owned" });
51
+ });
52
+
53
+ test("a declared entry wins: generated by its generator, or owned when kept by hand", () => {
54
+ const declared = new Map([
55
+ ["ci.yml", { command: "chant build ci -o ci.yml", handWritten: false }],
56
+ ["skills/custom/SKILL.md", { command: "chant update", handWritten: true }],
57
+ ]);
58
+ expect(classifyFile("ci.yml", declared)).toEqual({ class: "generated", command: "chant build ci -o ci.yml" });
59
+ expect(classifyFile("skills/custom/SKILL.md", declared)).toEqual({ class: "owned" });
60
+ });
61
+
62
+ test("declaredFilesFor reads the declaration above a directory, relative to that directory", () => {
63
+ const root = tree({ "chant.workspace.json": DECLARATION });
64
+ expect([...declaredFilesFor(join(root, "apps/app"))]).toEqual([
65
+ ["ci.yml", { command: "chant build ci --lexicon github -o ci.yml", handWritten: false }],
66
+ ["skills/custom/SKILL.md", { command: "chant update", handWritten: true }],
67
+ ]);
68
+ expect([...declaredFilesFor(root)].map(([k]) => k)).toEqual(["apps/app/ci.yml", "apps/app/skills/custom/SKILL.md"]);
69
+ expect(declaredFilesFor(join(root, "apps/other")).size).toBe(0);
70
+ expect(declaredFilesFor(tree({})).size).toBe(0);
71
+ });
72
+
73
+ test("the lock's classes read the same list", () => {
74
+ const root = tree({ "chant.workspace.json": DECLARATION });
75
+ const files = fileEntries(
76
+ bytes({ "ci.yml": "on: push", "skills/custom/SKILL.md": "mine", "skills/chant-aws/SKILL.md": "s", ".mcp.json": "{}", ".chant/types/x.d.ts": "", "src/a.ts": "a" }),
77
+ declaredFilesAt(join(root, "apps/app")),
78
+ );
79
+ expect(files).toEqual({
80
+ ".mcp.json": { class: "seed", sha256: fileHash("{}") },
81
+ "ci.yml": { class: "generated", command: "chant build ci --lexicon github -o ci.yml", sha256: fileHash("on: push") },
82
+ "skills/chant-aws/SKILL.md": { class: "generated", command: "chant update", sha256: fileHash("s") },
83
+ "skills/custom/SKILL.md": { class: "owned", sha256: fileHash("mine") },
84
+ "src/a.ts": { class: "owned", sha256: fileHash("a") },
85
+ });
86
+ });
87
+
88
+ test("an unreadable declaration is a LockError, not an empty list", () => {
89
+ const root = tree({ "chant.workspace.json": "{ nope" });
90
+ expect(() => declaredFilesAt(root)).toThrow(LockError);
91
+ expect(() => declaredFilesAt(root)).toThrow(/declaration-unparseable/);
92
+ });
93
+
94
+ test("an update rebuilds a file the member lists after the lock was written, instead of merging it", () => {
95
+ const root = tree({ "chant.workspace.json": DECLARATION, "apps/app/ci.yml": "local", "apps/app/src/a.ts": "a" });
96
+ const lineage: Lineage = {
97
+ kind: "vendor",
98
+ name: "app",
99
+ template: "local:x",
100
+ source: { type: "local", path: "x" },
101
+ address: null,
102
+ parameters: {},
103
+ migrations: [],
104
+ files: { "ci.yml": { class: "owned", sha256: fileHash("base") }, "src/a.ts": { class: "owned", sha256: fileHash("a") } },
105
+ manualSteps: [],
106
+ };
107
+ const result = applyUpstream(join(root, "apps/app"), lineage, bytes({ "ci.yml": "upstream", "src/a.ts": "a2" }));
108
+ expect(result.skipped).toEqual([{ path: "ci.yml", class: "generated", command: "chant build ci --lexicon github -o ci.yml" }]);
109
+ expect(result.manualSteps).toEqual([]);
110
+ expect(lineage.files["ci.yml"]).toEqual({ class: "generated", command: "chant build ci --lexicon github -o ci.yml", sha256: fileHash("base") });
111
+ expect(readFileSync(join(root, "apps/app/ci.yml"), "utf-8")).toBe("local");
112
+ expect(readFileSync(join(root, "apps/app/src/a.ts"), "utf-8")).toBe("a2");
113
+ });
114
+ });
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Which files are generated, and by what (#2524 D14, #2541).
3
+ *
4
+ * One list answers the question for everyone who asks it: the drift check in
5
+ * `checks/generated.ts` and the lineage lock's `generated` class
6
+ * (`lineage-lock.ts`). The list is the member's declared `generated` entries
7
+ * plus the files core registers itself:
8
+ *
9
+ * | File | Class |
10
+ * |-----------------------|-----------|
11
+ * | `skills/*\/SKILL.md` | generated, by `chant update` |
12
+ * | `.mcp.json` | seed |
13
+ * | `.chant/types/` | ignored: gitignored, never listed |
14
+ *
15
+ * A declared entry wins over the implicit rules, so a member can list a
16
+ * `SKILL.md` it keeps by hand. A hand-written entry is the project's own
17
+ * file, so the lock classes it `owned`.
18
+ *
19
+ * Everything here loads only with the lineage and workspace commands.
20
+ */
21
+
22
+ import { relative, resolve, sep } from "node:path";
23
+ import { findWorkspaceRoot } from "../project-root";
24
+ import { isInside, readDeclaration, type Declaration } from "./declaration";
25
+ import { workingTree } from "./tree";
26
+
27
+ /** What `chant update` rewrites, registered as implicit generated entries. */
28
+ export const IMPLICIT_GENERATED = [{ glob: "skills/*/SKILL.md", pattern: /^skills\/[^/]+\/SKILL\.md$/, command: "chant update" }] as const;
29
+
30
+ /** Written once at init and never again. */
31
+ export const SEED_FILES = [".mcp.json"] as const;
32
+
33
+ /** Gitignored output, out of scope for both the lock and the drift check. */
34
+ export const IGNORED_DIRS = [".chant/types"] as const;
35
+
36
+ /** A declared entry, keyed by its path relative to the directory asked about. */
37
+ export interface DeclaredFile {
38
+ command: string;
39
+ handWritten: boolean;
40
+ }
41
+ export type DeclaredFiles = ReadonlyMap<string, DeclaredFile>;
42
+
43
+ export type FileClassification =
44
+ | { class: "owned" }
45
+ | { class: "generated"; command: string }
46
+ | { class: "seed" }
47
+ | { class: "ignored" };
48
+
49
+ /** The implicit entry `path` matches, if any. */
50
+ export function implicitGenerated(path: string): (typeof IMPLICIT_GENERATED)[number] | undefined {
51
+ return IMPLICIT_GENERATED.find((i) => i.pattern.test(path));
52
+ }
53
+
54
+ /** Class a file by the declared entries first, then by the implicit rules (D14). */
55
+ export function classifyFile(path: string, declared?: DeclaredFiles): FileClassification {
56
+ const entry = declared?.get(path);
57
+ if (entry) return entry.handWritten ? { class: "owned" } : { class: "generated", command: entry.command };
58
+ if (IGNORED_DIRS.some((d) => path === d || path.startsWith(`${d}/`))) return { class: "ignored" };
59
+ const implicit = implicitGenerated(path);
60
+ if (implicit) return { class: "generated", command: implicit.command };
61
+ if ((SEED_FILES as readonly string[]).includes(path)) return { class: "seed" };
62
+ return { class: "owned" };
63
+ }
64
+
65
+ /**
66
+ * Every declared generated file of `declaration` that sits under `dir`
67
+ * (relative to the workspace root, `"."` for all of it), keyed by its path
68
+ * relative to `dir`.
69
+ */
70
+ export function declaredFilesUnder(declaration: Declaration, dir: string): Map<string, DeclaredFile> {
71
+ const out = new Map<string, DeclaredFile>();
72
+ for (const m of declaration.members) {
73
+ for (const g of m.generated) {
74
+ const full = m.dir === "." ? g.path : `${m.dir}/${g.path}`;
75
+ if (!isInside(full, dir)) continue;
76
+ out.set(dir === "." ? full : full.slice(dir.length + 1), { command: g.generator, handWritten: g.handWritten !== null });
77
+ }
78
+ }
79
+ return out;
80
+ }
81
+
82
+ /**
83
+ * The declared generated files for the absolute directory `absDir`, from the
84
+ * nearest `chant.workspace.json` above it. Empty when there is none, which is
85
+ * the plain-project case: only the implicit rules apply. A declaration that
86
+ * can't be read throws its `WorkspaceReadError`; it is never read as empty.
87
+ */
88
+ export function declaredFilesFor(absDir: string): Map<string, DeclaredFile> {
89
+ const found = findWorkspaceRoot(absDir);
90
+ if (!found) return new Map();
91
+ const declaration = readDeclaration(workingTree(found.dir));
92
+ const rel = relative(found.dir, resolve(absDir)).split(sep).join("/");
93
+ return declaredFilesUnder(declaration, rel === "" ? "." : rel);
94
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Expand an example group's globs to directories (ws-051).
3
+ *
4
+ * The walk goes one path segment at a time, so a glob such as
5
+ * `lexicons/*\/examples/*` reads only the directories it can match, never the
6
+ * whole tree. Within a segment, picomatch does the matching; `**` matches zero
7
+ * or more directories. `node_modules` and dot-directories are never entered
8
+ * by a wildcard, though a literal segment may name them.
9
+ */
10
+
11
+ // @ts-ignore — picomatch has no types declaration
12
+ import picomatch from "picomatch";
13
+ import { joinPath, skippedDir, type WorkspaceTree } from "./tree";
14
+
15
+ type Matcher = (name: string) => boolean;
16
+
17
+ const isLiteral = (segment: string): boolean => !/[*?[\]{}()!+@]/.test(segment);
18
+
19
+ /** Every directory `glob` matches in `tree`, sorted. */
20
+ export function expandGlob(tree: WorkspaceTree, glob: string): string[] {
21
+ const segments = glob.split("/");
22
+ const matchers: (Matcher | "**" | string)[] = segments.map((s) =>
23
+ s === "**" ? "**" : isLiteral(s) ? s : (picomatch(s, { dot: false }) as Matcher),
24
+ );
25
+ const found = new Set<string>();
26
+ const subdirs = (dir: string): string[] =>
27
+ (tree.list(dir) ?? []).filter((e) => e.type === "dir" && !skippedDir(e.name)).map((e) => e.name);
28
+
29
+ const walk = (dir: string, index: number): void => {
30
+ if (index === matchers.length) {
31
+ if (dir !== "") found.add(dir);
32
+ return;
33
+ }
34
+ const m = matchers[index];
35
+ if (m === "**") {
36
+ walk(dir, index + 1);
37
+ for (const name of subdirs(dir)) walk(joinPath(dir, name), index);
38
+ return;
39
+ }
40
+ if (typeof m === "string") {
41
+ const next = joinPath(dir, m);
42
+ if (tree.stat(next) === "dir") walk(next, index + 1);
43
+ return;
44
+ }
45
+ for (const name of subdirs(dir)) if (m(name)) walk(joinPath(dir, name), index + 1);
46
+ };
47
+ walk("", 0);
48
+ return [...found].sort();
49
+ }
@@ -0,0 +1,220 @@
1
+ /**
2
+ * `chant workspace graph [dir] [--at <rev>] [--member <name>] [-o <file>]
3
+ * [--env <env>] [--dry-run]` (#2537, #2536): every `chant` member's IR, read
4
+ * through the member's own toolchain and composed into one document
5
+ * (`compose-graph.ts`).
6
+ *
7
+ * The document is part of the read contract, described by `graph.schema.json`
8
+ * beside this file. It is printed for a failure too, with the error's reason
9
+ * code, so a reader always has JSON to parse.
10
+ *
11
+ * `--at <rev>` reads the declaration from git objects, as `ls --at` does. A
12
+ * member's IR comes from running its chant config, so for `--at` the
13
+ * workspace root's tree at that revision is exported to a temporary
14
+ * directory with `git archive`, the `node_modules` directories installed in
15
+ * the working tree are linked into it, and each member runs there under the
16
+ * toolchain it resolves, which is the one installed now. Nothing touches the
17
+ * checkout or the network. When no member needs running (a workspace with no
18
+ * `chant` members, or `--dry-run`) nothing is exported.
19
+ */
20
+
21
+ import { execFileSync } from "node:child_process";
22
+ import { existsSync, mkdirSync, mkdtempSync, rmSync, symlinkSync } from "node:fs";
23
+ import { tmpdir } from "node:os";
24
+ import { dirname, join, resolve } from "node:path";
25
+ import { formatError } from "../cli/format";
26
+ import type { CommandContext, ParsedArgs } from "../cli/registry";
27
+ import { composeWorkspaceGraph, readMemberIr, type ComposeInput, type WorkspaceGraph } from "./compose-graph";
28
+ import { readDeclaration, readerVersion, WORKSPACE_ERROR_CODES, WorkspaceReadError, type ErrorLocation, type WorkspaceErrorCode } from "./declaration";
29
+ import { describePlan, emitDocument, executePlan, memberStatus, planJson, planMembers, type MemberPlan, type Toolchain, type UnitResult } from "./member-commands";
30
+ import { loadKindRegistry } from "./kinds";
31
+ import { workingTree } from "./tree";
32
+ import { handToRootChant, locateWorkspace, type LocatedWorkspace } from "./which-chant";
33
+
34
+ /** The version of the `graph` document this chant writes. */
35
+ export const GRAPH_CONTRACT_VERSION = 1;
36
+
37
+ /** `$id` of the JSON Schema for the document, shipped beside this file. */
38
+ export const GRAPH_OUTPUT_SCHEMA_ID = "https://intentius.io/chant/schemas/workspace/graph/v1/graph.schema.json";
39
+
40
+ /** Why the graph couldn't be read at all: the declaration's codes, `--at`'s included. */
41
+ export const GRAPH_ERROR_CODES = WORKSPACE_ERROR_CODES;
42
+
43
+ const USAGE = "chant workspace graph [dir] [--at <rev>] [--member <name>] [-o <file>] [--env <env>] [--dry-run]";
44
+
45
+ interface Head {
46
+ $schema: string;
47
+ contract: number;
48
+ chant: string;
49
+ }
50
+
51
+ export type GraphDocument =
52
+ | (Head & { at: string | null } & WorkspaceGraph)
53
+ | (Head & { error: { code: WorkspaceErrorCode; message: string; location: ErrorLocation | null } });
54
+
55
+ export interface GraphQuery {
56
+ /** Where the walk up to the declaration starts. */
57
+ cwd: string;
58
+ at?: string;
59
+ /** Only these members. */
60
+ members?: string[];
61
+ /** The flags each member's `chant graph` gets (`--env`). */
62
+ args?: Partial<ParsedArgs>;
63
+ /** The chant for members with none of their own; the running chant by default. */
64
+ reader?: Toolchain;
65
+ /** Called with each member's stderr, so the command can pass it on. */
66
+ onStderr?: (text: string) => void;
67
+ }
68
+
69
+ export interface GraphResult {
70
+ doc: GraphDocument;
71
+ /** A member failed or couldn't be read, or the declaration couldn't be read. */
72
+ failed: boolean;
73
+ }
74
+
75
+ /**
76
+ * Export the workspace root's tree at `at` into a temporary directory and link
77
+ * the working tree's `node_modules` into it, so members' configs resolve the
78
+ * packages installed now. Returns the directory; the caller removes it.
79
+ */
80
+ export function exportRevision(located: LocatedWorkspace, memberDirs: string[]): string {
81
+ const { top, at, root, rootOnDisk } = located;
82
+ if (!top || !at) throw new Error("exportRevision needs a revision");
83
+ const scratch = mkdtempSync(join(tmpdir(), "chant-ws-at-"));
84
+ const out = join(scratch, "root");
85
+ const tar = join(scratch, "tree.tar");
86
+ execFileSync("git", ["archive", "--format=tar", "-o", tar, root === "." ? at : `${at}:${root}`], { cwd: top, stdio: ["ignore", "ignore", "pipe"] });
87
+ mkdirSync(out, { recursive: true });
88
+ execFileSync("tar", ["-xf", tar, "-C", out], { stdio: ["ignore", "ignore", "pipe"] });
89
+ rmSync(tar, { force: true });
90
+
91
+ // Every directory from the root down to each member that runs, where an
92
+ // install can sit: services/node_modules serves services/api and services/web.
93
+ const dirs = new Set<string>([""]);
94
+ for (const d of memberDirs) {
95
+ if (d === ".") continue;
96
+ const parts = d.split("/");
97
+ for (let i = 1; i <= parts.length; i++) dirs.add(parts.slice(0, i).join("/"));
98
+ }
99
+ for (const d of dirs) {
100
+ const src = join(rootOnDisk, ...d.split("/"), "node_modules");
101
+ const dst = join(out, ...d.split("/"), "node_modules");
102
+ if (existsSync(src) && existsSync(dirname(dst)) && !existsSync(dst)) symlinkSync(src, dst, "dir");
103
+ }
104
+ // A workspace root below the install (a monorepo package) resolves from above.
105
+ if (!existsSync(join(out, "node_modules"))) {
106
+ for (let dir = dirname(rootOnDisk); ; dir = dirname(dir)) {
107
+ if (existsSync(join(dir, "node_modules"))) {
108
+ symlinkSync(join(dir, "node_modules"), join(out, "node_modules"), "dir");
109
+ break;
110
+ }
111
+ if (dirname(dir) === dir) break;
112
+ }
113
+ }
114
+ return out;
115
+ }
116
+
117
+ function compose(plan: MemberPlan, results: UnitResult[], only: string[] | undefined, declarationMembers: { name: string; dir: string; kind: string }[]): { inputs: ComposeInput[]; failed: boolean } {
118
+ const byId = new Map(results.map((r) => [r.id, r]));
119
+ const inputs: ComposeInput[] = [];
120
+ let failed = plan.unreadable.length > 0;
121
+ for (const m of declarationMembers) {
122
+ if (only?.length && !only.includes(m.name)) continue;
123
+ const r = byId.get(m.name);
124
+ const skip = plan.skipped.find((s) => s.name === m.name) ?? plan.unreadable.find((s) => s.name === m.name);
125
+ if (!r) {
126
+ const status = plan.unreadable.some((s) => s.name === m.name) ? "failed" : "skipped";
127
+ inputs.push({ member: memberStatus(m.name, m.dir, m.kind, status, skip?.reason ?? null, null) });
128
+ continue;
129
+ }
130
+ if (r.exitCode !== 0) {
131
+ failed = true;
132
+ const tail = r.stderr.trim().split("\n").slice(-5).join("\n");
133
+ inputs.push({ member: memberStatus(m.name, m.dir, m.kind, "failed", { code: "command-failed", message: `chant graph exited ${r.exitCode}${tail ? `: ${tail}` : ""}` }, r.chant) });
134
+ continue;
135
+ }
136
+ const read = readMemberIr(r.stdout);
137
+ if ("reason" in read) {
138
+ failed = true;
139
+ inputs.push({ member: memberStatus(m.name, m.dir, m.kind, "failed", read.reason, r.chant) });
140
+ continue;
141
+ }
142
+ const member = memberStatus(m.name, m.dir, m.kind, "composed", null, r.chant);
143
+ member.irVersion = read.irVersion;
144
+ inputs.push({ member, ir: read.ir });
145
+ }
146
+ return { inputs, failed };
147
+ }
148
+
149
+ /** Plan a graph read without running anything, for `--dry-run`. Throws a {@link WorkspaceReadError}. */
150
+ export function planGraph(query: GraphQuery): MemberPlan {
151
+ const located = locateWorkspace(query.cwd, query.at);
152
+ readDeclaration(located.tree, "", { rootChant: true });
153
+ return planMembers("graph", located.rootOnDisk, { only: query.members, reader: query.reader, tree: located.tree });
154
+ }
155
+
156
+ /** Read and compose the graph, and build the document. Never throws a {@link WorkspaceReadError}. */
157
+ export async function workspaceGraph(query: GraphQuery): Promise<GraphResult> {
158
+ const head: Head = { $schema: GRAPH_OUTPUT_SCHEMA_ID, contract: GRAPH_CONTRACT_VERSION, chant: readerVersion() };
159
+ let exported: string | undefined;
160
+ try {
161
+ const located = locateWorkspace(query.cwd, query.at);
162
+ const declaration = readDeclaration(located.tree, "", { rootChant: true });
163
+ let plan = planMembers("graph", located.rootOnDisk, { only: query.members, reader: query.reader, tree: located.tree });
164
+ if (located.at !== null && plan.groups.length > 0) {
165
+ exported = exportRevision(located, plan.groups.flatMap((g) => g.units.map((u) => u.dir)));
166
+ plan = planMembers("graph", exported, { only: query.members, reader: query.reader, tree: workingTree(exported) });
167
+ }
168
+ const results = await executePlan(plan, (query.args ?? {}) as ParsedArgs);
169
+ for (const r of results) if (r.stderr.trim() && query.onStderr) query.onStderr(r.stderr.endsWith("\n") ? r.stderr : `${r.stderr}\n`);
170
+ const { inputs, failed } = compose(plan, results, query.members, declaration.members);
171
+ // Links (#2539) resolve against the declaration that was read, the revision's for --at, and the kinds installed now.
172
+ const kinds = loadKindRegistry(declaration.pins, located.rootOnDisk).registry;
173
+ const graph = composeWorkspaceGraph({ name: declaration.name, root: located.root }, inputs, { declaration, kinds });
174
+ return { doc: { ...head, at: located.at, ...graph }, failed };
175
+ } catch (err) {
176
+ if (!(err instanceof WorkspaceReadError)) throw err;
177
+ return { doc: { ...head, error: { code: err.code, message: err.message, location: err.location ?? null } }, failed: true };
178
+ } finally {
179
+ if (exported) rmSync(dirname(exported), { recursive: true, force: true });
180
+ }
181
+ }
182
+
183
+ export async function runWorkspaceGraph(ctx: CommandContext): Promise<number> {
184
+ const { args } = ctx;
185
+ const cwd = resolve(args.extraPositional ?? ".");
186
+ if (!existsSync(cwd)) {
187
+ console.error(formatError({ message: `${cwd} does not exist`, hint: USAGE }));
188
+ return 1;
189
+ }
190
+ // The root's chant reads the declaration (ws-021).
191
+ const handed = await handToRootChant(cwd, args.at);
192
+ if (handed !== undefined) return handed;
193
+
194
+ if (args.dryRun) {
195
+ let plan: MemberPlan;
196
+ try {
197
+ plan = planGraph({ cwd, at: args.at, members: args.members });
198
+ } catch (err) {
199
+ if (!(err instanceof WorkspaceReadError)) throw err;
200
+ console.error(formatError({ message: `${err.code}: ${err.describe()}`, hint: USAGE }));
201
+ return 1;
202
+ }
203
+ console.log(args.json ? JSON.stringify(planJson(plan), null, 2) : describePlan(plan));
204
+ return 0;
205
+ }
206
+
207
+ const { doc, failed } = await workspaceGraph({
208
+ cwd,
209
+ at: args.at,
210
+ members: args.members,
211
+ args,
212
+ onStderr: (text) => process.stderr.write(text),
213
+ });
214
+ emitDocument(doc, args.output);
215
+ if ("error" in doc) {
216
+ const l = doc.error.location;
217
+ console.error(formatError({ message: `${doc.error.code}: ${l ? `${l.file}:${l.line}:${l.column}: ` : ""}${doc.error.message}`, hint: USAGE }));
218
+ }
219
+ return failed ? 1 : 0;
220
+ }