@intentius/chant 0.124.27 → 0.124.28

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 (450) hide show
  1. package/README.md +1 -1
  2. package/dist/__golden__/wave-loop/fan-out-gated.json +8 -0
  3. package/dist/__golden__/wave-loop/fan-out-no-members.json +4 -0
  4. package/dist/__golden__/wave-loop/fan-out-only.json +6 -0
  5. package/dist/__golden__/wave-loop/op-by-apply-hold.json +2 -0
  6. package/dist/__golden__/wave-loop/op-cost-member.json +3 -0
  7. package/dist/__golden__/wave-loop/op-held-on-before-apply.json +1 -0
  8. package/dist/__golden__/wave-loop/op-held-on.json +2 -0
  9. package/dist/__golden__/wave-loop/op-hooks-held.json +2 -0
  10. package/dist/__golden__/wave-loop/op-hooks-hold-before-apply.json +2 -0
  11. package/dist/__golden__/wave-loop/op-hooks-refused-at-base.json +3 -0
  12. package/dist/__golden__/wave-loop/op-hooks-share.json +3 -0
  13. package/dist/__golden__/wave-loop/op-per-run.json +7 -0
  14. package/dist/__golden__/wave-loop/op-pr-review.json +7 -0
  15. package/dist/__golden__/wave-loop/op-sealed.json +3 -0
  16. package/dist/__golden__/wave-loop/op-spend.json +3 -0
  17. package/dist/__golden__/wave-loop/op-wave-commands.json +7 -0
  18. package/dist/__golden__/wave-loop/op-wide-waiting.json +4 -0
  19. package/dist/change-set-graph.d.ts +56 -0
  20. package/dist/change-set-graph.d.ts.map +1 -0
  21. package/dist/change-set-graph.js +128 -0
  22. package/dist/change-set.d.ts +52 -3
  23. package/dist/change-set.d.ts.map +1 -1
  24. package/dist/change-set.js +35 -4
  25. package/dist/chant-version.d.ts +9 -0
  26. package/dist/chant-version.d.ts.map +1 -0
  27. package/dist/chant-version.js +16 -0
  28. package/dist/cli/commands/build.d.ts +8 -0
  29. package/dist/cli/commands/build.d.ts.map +1 -1
  30. package/dist/cli/commands/build.js +9 -8
  31. package/dist/cli/commands/check-lexicon-docs.d.ts.map +1 -1
  32. package/dist/cli/commands/check-lexicon-docs.js +30 -2
  33. package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
  34. package/dist/cli/commands/check-lexicon.js +13 -2
  35. package/dist/cli/commands/init-lexicon/templates/docs.d.ts +4 -1
  36. package/dist/cli/commands/init-lexicon/templates/docs.d.ts.map +1 -1
  37. package/dist/cli/commands/init-lexicon/templates/docs.js +6 -6
  38. package/dist/cli/commands/init-lexicon.d.ts.map +1 -1
  39. package/dist/cli/commands/init-lexicon.js +17 -8
  40. package/dist/cli/handlers/approve.d.ts.map +1 -1
  41. package/dist/cli/handlers/approve.js +19 -4
  42. package/dist/cli/handlers/build.d.ts.map +1 -1
  43. package/dist/cli/handlers/build.js +28 -0
  44. package/dist/cli/handlers/change-set.d.ts +10 -0
  45. package/dist/cli/handlers/change-set.d.ts.map +1 -1
  46. package/dist/cli/handlers/change-set.js +51 -1
  47. package/dist/cli/handlers/fan-out.d.ts +1 -1
  48. package/dist/cli/handlers/fan-out.d.ts.map +1 -1
  49. package/dist/cli/handlers/fan-out.js +14 -3
  50. package/dist/cli/handlers/pr.d.ts +8 -2
  51. package/dist/cli/handlers/pr.d.ts.map +1 -1
  52. package/dist/cli/handlers/pr.js +148 -24
  53. package/dist/cli/handlers/promote.d.ts.map +1 -1
  54. package/dist/cli/handlers/promote.js +256 -21
  55. package/dist/cli/handlers/release.d.ts +34 -0
  56. package/dist/cli/handlers/release.d.ts.map +1 -0
  57. package/dist/cli/handlers/release.js +203 -0
  58. package/dist/cli/handlers/run.d.ts +28 -2
  59. package/dist/cli/handlers/run.d.ts.map +1 -1
  60. package/dist/cli/handlers/run.js +170 -13
  61. package/dist/cli/main.d.ts.map +1 -1
  62. package/dist/cli/main.js +88 -13
  63. package/dist/cli/mcp/workspace-tools.js +1 -1
  64. package/dist/cli/parse-args.d.ts.map +1 -1
  65. package/dist/cli/parse-args.js +49 -3
  66. package/dist/cli/registry.d.ts +29 -3
  67. package/dist/cli/registry.d.ts.map +1 -1
  68. package/dist/cli/run-events.d.ts +29 -0
  69. package/dist/cli/run-events.d.ts.map +1 -0
  70. package/dist/cli/run-events.js +47 -0
  71. package/dist/cli/version.d.ts +3 -5
  72. package/dist/cli/version.d.ts.map +1 -1
  73. package/dist/cli/version.js +3 -13
  74. package/dist/codegen/docs-in-site.d.ts +71 -0
  75. package/dist/codegen/docs-in-site.d.ts.map +1 -0
  76. package/dist/codegen/docs-in-site.js +177 -0
  77. package/dist/codegen/docs-types.d.ts +14 -1
  78. package/dist/codegen/docs-types.d.ts.map +1 -1
  79. package/dist/codegen/docs.d.ts +18 -1
  80. package/dist/codegen/docs.d.ts.map +1 -1
  81. package/dist/codegen/docs.js +40 -7
  82. package/dist/codegen/rehype-base-url.mjs +15 -9
  83. package/dist/components/auto-release.d.ts +4 -1
  84. package/dist/components/auto-release.d.ts.map +1 -1
  85. package/dist/components/auto-release.js +2 -0
  86. package/dist/components/cli-support.d.ts +6 -0
  87. package/dist/components/cli-support.d.ts.map +1 -1
  88. package/dist/components/cli-support.js +10 -9
  89. package/dist/components/driver.d.ts +42 -9
  90. package/dist/components/driver.d.ts.map +1 -1
  91. package/dist/components/driver.js +81 -54
  92. package/dist/components/fan-out-run.d.ts +7 -0
  93. package/dist/components/fan-out-run.d.ts.map +1 -1
  94. package/dist/components/fan-out-run.js +35 -26
  95. package/dist/components/pr-run.d.ts +11 -0
  96. package/dist/components/pr-run.d.ts.map +1 -1
  97. package/dist/components/pr-run.js +20 -1
  98. package/dist/components/promote-order.d.ts +38 -0
  99. package/dist/components/promote-order.d.ts.map +1 -0
  100. package/dist/components/promote-order.js +62 -0
  101. package/dist/components/promote.d.ts +15 -2
  102. package/dist/components/promote.d.ts.map +1 -1
  103. package/dist/components/promote.js +17 -0
  104. package/dist/components/release-compile.d.ts +133 -0
  105. package/dist/components/release-compile.d.ts.map +1 -0
  106. package/dist/components/release-compile.js +376 -0
  107. package/dist/components/release-gate.d.ts +125 -0
  108. package/dist/components/release-gate.d.ts.map +1 -0
  109. package/dist/components/release-gate.js +282 -0
  110. package/dist/components/release-promote.d.ts +96 -0
  111. package/dist/components/release-promote.d.ts.map +1 -0
  112. package/dist/components/release-promote.js +204 -0
  113. package/dist/components/run-progress.d.ts +7 -0
  114. package/dist/components/run-progress.d.ts.map +1 -1
  115. package/dist/components/wave-plan.d.ts +3 -1
  116. package/dist/components/wave-plan.d.ts.map +1 -1
  117. package/dist/components/wave-plan.js +1 -1
  118. package/dist/config.d.ts +7 -0
  119. package/dist/config.d.ts.map +1 -1
  120. package/dist/config.js +1 -0
  121. package/dist/index.d.ts +1 -0
  122. package/dist/index.d.ts.map +1 -1
  123. package/dist/index.js +2 -0
  124. package/dist/lifecycle/change-set-store.d.ts +67 -0
  125. package/dist/lifecycle/change-set-store.d.ts.map +1 -0
  126. package/dist/lifecycle/change-set-store.js +96 -0
  127. package/dist/lifecycle/commit-sha.d.ts +13 -0
  128. package/dist/lifecycle/commit-sha.d.ts.map +1 -0
  129. package/dist/lifecycle/commit-sha.js +21 -0
  130. package/dist/lifecycle/gate-change-set.d.ts +69 -0
  131. package/dist/lifecycle/gate-change-set.d.ts.map +1 -0
  132. package/dist/lifecycle/gate-change-set.js +106 -0
  133. package/dist/lifecycle/gate-ledger.d.ts +49 -1
  134. package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
  135. package/dist/lifecycle/gate-ledger.js +18 -0
  136. package/dist/lifecycle/release-cut.d.ts +107 -0
  137. package/dist/lifecycle/release-cut.d.ts.map +1 -0
  138. package/dist/lifecycle/release-cut.js +273 -0
  139. package/dist/lifecycle/release-ledger.d.ts +54 -0
  140. package/dist/lifecycle/release-ledger.d.ts.map +1 -1
  141. package/dist/lifecycle/release-ledger.js +53 -20
  142. package/dist/lifecycle/release-store.d.ts +190 -0
  143. package/dist/lifecycle/release-store.d.ts.map +1 -0
  144. package/dist/lifecycle/release-store.js +318 -0
  145. package/dist/lifecycle/run-ledger.d.ts +4 -0
  146. package/dist/lifecycle/run-ledger.d.ts.map +1 -1
  147. package/dist/lifecycle/run-ledger.js +2 -0
  148. package/dist/op/activities/change-coverage.d.ts.map +1 -1
  149. package/dist/op/activities/change-coverage.js +4 -2
  150. package/dist/op/activities/source-release.d.ts.map +1 -1
  151. package/dist/op/activities/source-release.js +7 -3
  152. package/dist/op/activities/source-rollback.d.ts.map +1 -1
  153. package/dist/op/activities/source-rollback.js +7 -3
  154. package/dist/op/gate-name.d.ts +11 -2
  155. package/dist/op/gate-name.d.ts.map +1 -1
  156. package/dist/op/gate-name.js +12 -3
  157. package/dist/op/gate-run-locator.d.ts +22 -0
  158. package/dist/op/gate-run-locator.d.ts.map +1 -1
  159. package/dist/op/gate-run-locator.js +31 -0
  160. package/dist/op/gate.d.ts +27 -3
  161. package/dist/op/gate.d.ts.map +1 -1
  162. package/dist/op/gate.js +57 -3
  163. package/dist/op/local-executor.d.ts +36 -1
  164. package/dist/op/local-executor.d.ts.map +1 -1
  165. package/dist/op/local-executor.js +158 -52
  166. package/dist/op/op-waves-run.d.ts.map +1 -1
  167. package/dist/op/op-waves-run.js +4 -0
  168. package/dist/op/run-live.d.ts +2 -0
  169. package/dist/op/run-live.d.ts.map +1 -1
  170. package/dist/op/run-live.js +1 -1
  171. package/dist/op/runtime.d.ts +46 -0
  172. package/dist/op/runtime.d.ts.map +1 -1
  173. package/dist/op/runtimes/local.d.ts.map +1 -1
  174. package/dist/op/runtimes/local.js +8 -0
  175. package/dist/point-decision.d.ts +2 -0
  176. package/dist/point-decision.d.ts.map +1 -1
  177. package/dist/point-decision.js +9 -2
  178. package/dist/run-event-store.d.ts +77 -0
  179. package/dist/run-event-store.d.ts.map +1 -0
  180. package/dist/run-event-store.js +204 -0
  181. package/dist/run-event-stream.d.ts +132 -0
  182. package/dist/run-event-stream.d.ts.map +1 -0
  183. package/dist/run-event-stream.js +238 -0
  184. package/dist/run-event.d.ts +267 -0
  185. package/dist/run-event.d.ts.map +1 -0
  186. package/dist/run-event.js +207 -0
  187. package/dist/wave-runtime.mjs +204 -199
  188. package/dist/workspace/__fixtures__/run-event-check.d.ts +19 -0
  189. package/dist/workspace/__fixtures__/run-event-check.d.ts.map +1 -0
  190. package/dist/workspace/__fixtures__/run-event-check.js +59 -0
  191. package/dist/workspace/change-set-show.d.ts +56 -0
  192. package/dist/workspace/change-set-show.d.ts.map +1 -0
  193. package/dist/workspace/change-set-show.js +54 -0
  194. package/dist/workspace/change-set-show.schema.json +102 -0
  195. package/dist/workspace/change-set.schema.json +28 -1
  196. package/dist/workspace/changes-cli.d.ts.map +1 -1
  197. package/dist/workspace/changes-cli.js +10 -1
  198. package/dist/workspace/changes.d.ts +37 -0
  199. package/dist/workspace/changes.d.ts.map +1 -1
  200. package/dist/workspace/changes.js +49 -2
  201. package/dist/workspace/changes.schema.json +135 -0
  202. package/dist/workspace/ci-green-cli.d.ts +19 -4
  203. package/dist/workspace/ci-green-cli.d.ts.map +1 -1
  204. package/dist/workspace/ci-green-cli.js +75 -12
  205. package/dist/workspace/ci-green-forge.d.ts +20 -3
  206. package/dist/workspace/ci-green-forge.d.ts.map +1 -1
  207. package/dist/workspace/ci-green-forge.js +6 -4
  208. package/dist/workspace/ci-green-workflow.d.ts +11 -0
  209. package/dist/workspace/ci-green-workflow.d.ts.map +1 -1
  210. package/dist/workspace/ci-green-workflow.js +6 -0
  211. package/dist/workspace/ci-green.d.ts +273 -13
  212. package/dist/workspace/ci-green.d.ts.map +1 -1
  213. package/dist/workspace/ci-green.js +449 -38
  214. package/dist/workspace/ci-last-green.schema.json +32 -2
  215. package/dist/workspace/ci-list.schema.json +525 -0
  216. package/dist/workspace/commit-links.d.ts +85 -0
  217. package/dist/workspace/commit-links.d.ts.map +1 -0
  218. package/dist/workspace/commit-links.js +163 -0
  219. package/dist/workspace/declaration.d.ts +19 -0
  220. package/dist/workspace/declaration.d.ts.map +1 -1
  221. package/dist/workspace/declaration.js +1 -1
  222. package/dist/workspace/declaration.schema.json +12 -0
  223. package/dist/workspace/identity-gate-rule.d.ts +37 -0
  224. package/dist/workspace/identity-gate-rule.d.ts.map +1 -1
  225. package/dist/workspace/identity-gate-rule.js +72 -7
  226. package/dist/workspace/identity.d.ts +12 -0
  227. package/dist/workspace/identity.d.ts.map +1 -1
  228. package/dist/workspace/identity.js +13 -1
  229. package/dist/workspace/intent.d.ts +6 -1
  230. package/dist/workspace/intent.d.ts.map +1 -1
  231. package/dist/workspace/intent.js +51 -1
  232. package/dist/workspace/ls.d.ts +4 -0
  233. package/dist/workspace/ls.d.ts.map +1 -1
  234. package/dist/workspace/ls.js +2 -0
  235. package/dist/workspace/ls.schema.json +3 -1
  236. package/dist/workspace/member-pipeline.d.ts +6 -0
  237. package/dist/workspace/member-pipeline.d.ts.map +1 -1
  238. package/dist/workspace/member-pipeline.js +6 -0
  239. package/dist/workspace/promotion-order.d.ts +41 -0
  240. package/dist/workspace/promotion-order.d.ts.map +1 -0
  241. package/dist/workspace/promotion-order.js +95 -0
  242. package/dist/workspace/reason-codes.d.ts +2 -0
  243. package/dist/workspace/reason-codes.d.ts.map +1 -1
  244. package/dist/workspace/reason-codes.js +4 -1
  245. package/dist/workspace/releases.d.ts +72 -0
  246. package/dist/workspace/releases.d.ts.map +1 -0
  247. package/dist/workspace/releases.js +94 -0
  248. package/dist/workspace/releases.schema.json +391 -0
  249. package/dist/workspace/run-event.schema.json +288 -0
  250. package/dist/workspace/status-gates.d.ts +26 -1
  251. package/dist/workspace/status-gates.d.ts.map +1 -1
  252. package/dist/workspace/status-gates.js +31 -2
  253. package/dist/workspace/status-stewards.d.ts +4 -0
  254. package/dist/workspace/status-stewards.d.ts.map +1 -1
  255. package/dist/workspace/status-stewards.js +2 -0
  256. package/dist/workspace/status.d.ts +39 -0
  257. package/dist/workspace/status.d.ts.map +1 -1
  258. package/dist/workspace/status.js +48 -1
  259. package/dist/workspace/status.schema.json +70 -1
  260. package/dist/workspace/trust/ci-tag-statement.d.ts +102 -0
  261. package/dist/workspace/trust/ci-tag-statement.d.ts.map +1 -0
  262. package/dist/workspace/trust/ci-tag-statement.js +123 -0
  263. package/dist/workspace/trust/op-run-statement.d.ts +91 -0
  264. package/dist/workspace/trust/op-run-statement.d.ts.map +1 -0
  265. package/dist/workspace/trust/op-run-statement.js +143 -0
  266. package/dist/workspace/trust/rotation.d.ts +18 -2
  267. package/dist/workspace/trust/rotation.d.ts.map +1 -1
  268. package/dist/workspace/trust/rotation.js +65 -16
  269. package/dist/workspace/trust/seal.d.ts +11 -0
  270. package/dist/workspace/trust/seal.d.ts.map +1 -1
  271. package/dist/workspace/trust/seal.js +9 -0
  272. package/dist/workspace/trust/ssh-commit.d.ts.map +1 -1
  273. package/dist/workspace/trust/ssh-commit.js +10 -10
  274. package/package.json +1 -1
  275. package/src/__golden__/wave-loop/fan-out-gated.json +8 -0
  276. package/src/__golden__/wave-loop/fan-out-no-members.json +4 -0
  277. package/src/__golden__/wave-loop/fan-out-only.json +6 -0
  278. package/src/__golden__/wave-loop/op-by-apply-hold.json +2 -0
  279. package/src/__golden__/wave-loop/op-cost-member.json +3 -0
  280. package/src/__golden__/wave-loop/op-held-on-before-apply.json +1 -0
  281. package/src/__golden__/wave-loop/op-held-on.json +2 -0
  282. package/src/__golden__/wave-loop/op-hooks-held.json +2 -0
  283. package/src/__golden__/wave-loop/op-hooks-hold-before-apply.json +2 -0
  284. package/src/__golden__/wave-loop/op-hooks-refused-at-base.json +3 -0
  285. package/src/__golden__/wave-loop/op-hooks-share.json +3 -0
  286. package/src/__golden__/wave-loop/op-per-run.json +7 -0
  287. package/src/__golden__/wave-loop/op-pr-review.json +7 -0
  288. package/src/__golden__/wave-loop/op-sealed.json +3 -0
  289. package/src/__golden__/wave-loop/op-spend.json +3 -0
  290. package/src/__golden__/wave-loop/op-wave-commands.json +7 -0
  291. package/src/__golden__/wave-loop/op-wide-waiting.json +4 -0
  292. package/src/change-set-graph.test.ts +72 -0
  293. package/src/change-set-graph.ts +133 -0
  294. package/src/change-set.ts +76 -6
  295. package/src/chant-version.ts +16 -0
  296. package/src/cli/commands/build.ts +19 -8
  297. package/src/cli/commands/check-lexicon-docs.test.ts +51 -0
  298. package/src/cli/commands/check-lexicon-docs.ts +39 -2
  299. package/src/cli/commands/check-lexicon.ts +14 -2
  300. package/src/cli/commands/init-lexicon/templates/docs.ts +7 -6
  301. package/src/cli/commands/init-lexicon.test.ts +25 -0
  302. package/src/cli/commands/init-lexicon.ts +18 -8
  303. package/src/cli/handlers/approve-release.test.ts +80 -0
  304. package/src/cli/handlers/approve.ts +18 -2
  305. package/src/cli/handlers/build.test.ts +52 -0
  306. package/src/cli/handlers/build.ts +29 -0
  307. package/src/cli/handlers/change-set.ts +49 -1
  308. package/src/cli/handlers/fan-out.test.ts +48 -0
  309. package/src/cli/handlers/fan-out.ts +15 -3
  310. package/src/cli/handlers/operator.test.ts +37 -0
  311. package/src/cli/handlers/pr.test.ts +194 -0
  312. package/src/cli/handlers/pr.ts +168 -34
  313. package/src/cli/handlers/promote.test.ts +389 -5
  314. package/src/cli/handlers/promote.ts +279 -20
  315. package/src/cli/handlers/release-implicit.e2e.test.ts +153 -0
  316. package/src/cli/handlers/release.e2e.test.ts +85 -0
  317. package/src/cli/handlers/release.ts +206 -0
  318. package/src/cli/handlers/run-auto-release.test.ts +71 -0
  319. package/src/cli/handlers/run-events-cmd.test.ts +92 -0
  320. package/src/cli/handlers/run.test.ts +30 -0
  321. package/src/cli/handlers/run.ts +176 -15
  322. package/src/cli/main.test.ts +7 -0
  323. package/src/cli/main.ts +88 -13
  324. package/src/cli/mcp/never-over-mcp.test.ts +2 -0
  325. package/src/cli/mcp/workspace-tools.ts +1 -1
  326. package/src/cli/parse-args.ts +37 -3
  327. package/src/cli/registry.ts +29 -3
  328. package/src/cli/run-events.ts +54 -0
  329. package/src/cli/version.ts +3 -13
  330. package/src/codegen/docs-in-site.test.ts +166 -0
  331. package/src/codegen/docs-in-site.ts +182 -0
  332. package/src/codegen/docs-site/AgentPrompt.astro +26 -1
  333. package/src/codegen/docs-types.ts +14 -1
  334. package/src/codegen/docs.ts +60 -7
  335. package/src/codegen/rehype-base-url.d.mts +3 -1
  336. package/src/codegen/rehype-base-url.mjs +15 -9
  337. package/src/codegen/rehype-base-url.test.ts +34 -0
  338. package/src/components/auto-release.test.ts +21 -0
  339. package/src/components/auto-release.ts +6 -1
  340. package/src/components/cli-support.test.ts +82 -0
  341. package/src/components/cli-support.ts +16 -9
  342. package/src/components/driver.ts +129 -56
  343. package/src/components/fan-out-run.ts +56 -35
  344. package/src/components/pr-run.ts +30 -1
  345. package/src/components/promote-order.test.ts +90 -0
  346. package/src/components/promote-order.ts +75 -0
  347. package/src/components/promote.test.ts +10 -1
  348. package/src/components/promote.ts +26 -1
  349. package/src/components/release-compile.test.ts +147 -0
  350. package/src/components/release-compile.ts +445 -0
  351. package/src/components/release-gate.test.ts +215 -0
  352. package/src/components/release-gate.ts +341 -0
  353. package/src/components/release-promote.test.ts +159 -0
  354. package/src/components/release-promote.ts +232 -0
  355. package/src/components/run-progress.ts +7 -0
  356. package/src/components/wave-plan.ts +1 -1
  357. package/src/config.ts +7 -1
  358. package/src/index.ts +2 -0
  359. package/src/lifecycle/change-set-store.test.ts +217 -0
  360. package/src/lifecycle/change-set-store.ts +112 -0
  361. package/src/lifecycle/commit-sha.ts +23 -0
  362. package/src/lifecycle/gate-change-set.e2e.test.ts +122 -0
  363. package/src/lifecycle/gate-change-set.ts +133 -0
  364. package/src/lifecycle/gate-ledger.test.ts +33 -0
  365. package/src/lifecycle/gate-ledger.ts +66 -1
  366. package/src/lifecycle/release-cut-signed.test.ts +180 -0
  367. package/src/lifecycle/release-cut.test.ts +411 -0
  368. package/src/lifecycle/release-cut.ts +367 -0
  369. package/src/lifecycle/release-ledger.test.ts +14 -0
  370. package/src/lifecycle/release-ledger.ts +85 -17
  371. package/src/lifecycle/release-store.ts +423 -0
  372. package/src/lifecycle/run-ledger.test.ts +10 -0
  373. package/src/lifecycle/run-ledger.ts +6 -0
  374. package/src/op/activities/change-coverage.ts +4 -1
  375. package/src/op/activities/source-release.ts +8 -3
  376. package/src/op/activities/source-rollback.ts +8 -4
  377. package/src/op/commit-run-gate-deploy-join.test.ts +117 -0
  378. package/src/op/gate-name.ts +13 -3
  379. package/src/op/gate-release.test.ts +126 -0
  380. package/src/op/gate-run-locator.test.ts +110 -0
  381. package/src/op/gate-run-locator.ts +35 -0
  382. package/src/op/gate.ts +88 -6
  383. package/src/op/local-executor.ts +207 -52
  384. package/src/op/op-waves-run.ts +4 -0
  385. package/src/op/run-events.test.ts +166 -0
  386. package/src/op/run-live.ts +1 -1
  387. package/src/op/runtime.ts +39 -0
  388. package/src/op/runtimes/local.test.ts +16 -0
  389. package/src/op/runtimes/local.ts +9 -0
  390. package/src/point-decision.ts +12 -2
  391. package/src/run-event-store.test.ts +144 -0
  392. package/src/run-event-store.ts +213 -0
  393. package/src/run-event-stream.test.ts +175 -0
  394. package/src/run-event-stream.ts +306 -0
  395. package/src/run-event.test.ts +277 -0
  396. package/src/run-event.ts +451 -0
  397. package/src/wave-loop-golden.test.ts +9 -0
  398. package/src/workspace/__fixtures__/run-event-check.ts +58 -0
  399. package/src/workspace/ancestry-index.test.ts +93 -0
  400. package/src/workspace/change-set-show.schema.json +102 -0
  401. package/src/workspace/change-set-show.ts +72 -0
  402. package/src/workspace/change-set.schema.json +28 -1
  403. package/src/workspace/changes-cli.ts +10 -1
  404. package/src/workspace/changes.schema.json +135 -0
  405. package/src/workspace/changes.test.ts +1 -1
  406. package/src/workspace/changes.ts +84 -2
  407. package/src/workspace/ci-green-cli.ts +75 -13
  408. package/src/workspace/ci-green-forge.ts +24 -4
  409. package/src/workspace/ci-green-runs.test.ts +242 -0
  410. package/src/workspace/ci-green-signed.test.ts +302 -0
  411. package/src/workspace/ci-green-workflow.test.ts +16 -0
  412. package/src/workspace/ci-green-workflow.ts +12 -0
  413. package/src/workspace/ci-green.test.ts +131 -2
  414. package/src/workspace/ci-green.ts +624 -42
  415. package/src/workspace/ci-last-green.schema.json +32 -2
  416. package/src/workspace/ci-list.schema.json +525 -0
  417. package/src/workspace/commit-links.test.ts +279 -0
  418. package/src/workspace/commit-links.ts +183 -0
  419. package/src/workspace/declaration.schema.json +12 -0
  420. package/src/workspace/declaration.ts +22 -2
  421. package/src/workspace/identity-gate-rule.ts +91 -7
  422. package/src/workspace/identity.ts +14 -1
  423. package/src/workspace/intent.ts +48 -1
  424. package/src/workspace/ls.schema.json +3 -1
  425. package/src/workspace/ls.ts +6 -0
  426. package/src/workspace/member-pipeline.test.ts +17 -0
  427. package/src/workspace/member-pipeline.ts +9 -0
  428. package/src/workspace/promotion-order.ts +97 -0
  429. package/src/workspace/read-contract.e2e.test.ts +4 -1
  430. package/src/workspace/reason-codes.test.ts +2 -1
  431. package/src/workspace/reason-codes.ts +4 -1
  432. package/src/workspace/releases.schema.json +391 -0
  433. package/src/workspace/releases.test.ts +108 -0
  434. package/src/workspace/releases.ts +122 -0
  435. package/src/workspace/run-event.schema.json +288 -0
  436. package/src/workspace/status-contract.test.ts +52 -1
  437. package/src/workspace/status-gates.ts +56 -1
  438. package/src/workspace/status-stewards.ts +6 -0
  439. package/src/workspace/status.schema.json +70 -1
  440. package/src/workspace/status.ts +80 -1
  441. package/src/workspace/trust/ci-tag-statement.ts +151 -0
  442. package/src/workspace/trust/op-run-statement.ts +148 -0
  443. package/src/workspace/trust/rotation.ts +61 -14
  444. package/src/workspace/trust/seal.ts +10 -0
  445. package/src/workspace/trust/ssh-commit.ts +10 -9
  446. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/astro.config.mjs +0 -14
  447. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/package.json +0 -16
  448. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content/docs/index.mdx +0 -8
  449. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content.config.ts +0 -15
  450. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/tsconfig.json +0 -10
@@ -72,6 +72,16 @@ export interface ParsedArgs {
72
72
  * unchanged; omitted (undefined, not false) when the flag isn't passed.
73
73
  */
74
74
  progressJson?: boolean;
75
+ /**
76
+ * `--events-json` (#4041) — stream the run's events to stdout, one
77
+ * `run-event.schema.json` line each (`../../run-event.ts`), on `chant run
78
+ * <op>`, `chant run --components`, `components promote|rollback|redeploy`,
79
+ * `components fan-out` and `components pr-plan|pr-apply`. Refused beside
80
+ * `--progress-json`, which prints the older shapes to the same stdout.
81
+ */
82
+ eventsJson?: boolean;
83
+ /** `chant run events <run> --follow` (#4042) — stream the run's events as they happen, until `run-settle`. */
84
+ follow?: boolean;
75
85
  live: boolean;
76
86
  /** `chant migrate --from <name>` (default "github") */
77
87
  migrateFrom?: string;
@@ -137,6 +147,8 @@ export interface ParsedArgs {
137
147
  namespace?: string;
138
148
  /** `chant workspace upgrade --allow-code` (#2550) — run migrations whose body is the template's own code. */
139
149
  allowCode?: boolean;
150
+ /** `chant components promote --allow-skip` (#4030): promote past a step of the member's declared promotion order. */
151
+ allowSkip?: boolean;
140
152
  /** `chant workspace upgrade --source <repo>[#<member>]` (#2551): move the scope to another template. */
141
153
  source?: string;
142
154
  /** `chant workspace adopt-lineage --tags <glob>` and `hash-index --tags` (#2551): the tags to compare. */
@@ -160,15 +172,21 @@ export interface ParsedArgs {
160
172
  verbatim?: boolean;
161
173
  /** `chant import --skip-undeclarable` — import what can be declared and warn about the live objects that cannot, instead of failing */
162
174
  skipUndeclarable?: boolean;
163
- /** `chant lifecycle … --src <dir>` — build root override for lifecycle commands */
175
+ /** `chant lifecycle … --src <dir>` — build root override for lifecycle commands. Also `chant release cut --src <dir>` (#4046): the build root of a project with no components. */
164
176
  src?: string;
165
177
  /** `--env <name>` — active environment: sets CHANT_ENV so env-aware source
166
178
  * re-evaluates for that environment (`build` + `graph`), and drives policy. */
167
179
  env?: string;
168
180
  /** `chant emulator up --port <container-port>=<host-port>`, repeatable (#3673); a bare `<host-port>` when one emulator is selected. */
169
181
  port?: string[];
170
- /** `chant emulator up --image <ref>` (#3952): the image to start, when one emulator is selected. */
182
+ /**
183
+ * `chant emulator up --image <ref>` (#3952): the image to start, when one
184
+ * emulator is selected. `chant build --components --generate --image <ref>`
185
+ * (#4002): the image every generated job runs in.
186
+ */
171
187
  image?: string;
188
+ /** `chant build --components --generate --before-script <line>`, repeatable (#4002): shell lines every generated job runs before its command. */
189
+ beforeScript?: string[];
172
190
  /** `chant emulator up --network <name>` (#3952): the Docker network each started container joins. */
173
191
  network?: string;
174
192
  /** `chant emulator up --setting <KEY>=<VALUE>`, repeatable (#3952): an emulator setting such as `FLOCI_<NAME>`, passed as `-e`. */
@@ -434,6 +452,8 @@ export interface ParsedArgs {
434
452
  ciAppIdVar?: string;
435
453
  /** `chant ci workflow --app-key-secret <NAME>` (#3573): the secret holding that App's private key. */
436
454
  ciAppKeySecret?: string;
455
+ /** `chant ci workflow --runner-key-secret <NAME>` (#4031): the secret holding the runner key the tick signs its tags with. */
456
+ ciRunnerKeySecret?: string;
437
457
  /**
438
458
  * `chant workspace records review <id> --by <principal>` (#2670): the
439
459
  * reviewer, as the caller names them. Also `chant workspace records new
@@ -460,7 +480,7 @@ export interface ParsedArgs {
460
480
  prefix?: string;
461
481
  /** `chant workspace records|verify --require attested` (#2547): the provenance level a gate requires. */
462
482
  require?: string;
463
- /** `chant workspace evidence sign --key` (a runner's Ed25519 PEM) and `signers sign --key` (an ssh private key) (#2553). */
483
+ /** `chant workspace evidence sign --key` (a runner's Ed25519 PEM) and `signers sign --key` (an ssh private key) (#2553). Also `chant ci tick --key` (#4031): the runner key the tick signs its tags with. */
464
484
  key?: string;
465
485
  /** `chant workspace evidence sign --check-id <id>` (#2553): the check the evidence is for. */
466
486
  checkId?: string;
@@ -541,6 +561,12 @@ export interface ParsedArgs {
541
561
  digest?: string;
542
562
  /** `chant components release record --release-plan <file>` (ws-055, #2733) — path to a release plan JSON file, persisted content-addressed to `_plans/<digest>.json` on chant/lifecycle and read back through the read contract (`chant workspace status --json`). The plan's own `digest` field supplies `--digest` when it is omitted, and must match it when both are given. Distinct from `--plan` (#2300, below), the Op gate-approval plan digest. */
543
563
  releasePlanFile?: string;
564
+ /** `chant components promote --release <label|digest>` (#4026): deploy under that release, reading the templates its target for `--to` recorded. */
565
+ release?: string;
566
+ /** `chant release cut --build <command>` (#4026): the shell command run in the member's directory before each environment's templates are read, with `CHANT_ENV` set to the environment. */
567
+ buildCommand?: string;
568
+ /** `chant release compile --artifacts` (#4026, internal): build the artifacts of `--component` instead of compiling the environment. */
569
+ artifacts?: boolean;
544
570
  /** Every `--digest` value, in order (#2602). `chant components promote --digest <component>=<sha256:...>` is repeatable, one per component; the other commands read the single {@link digest}. */
545
571
  digests?: string[];
546
572
  /** `chant components release record --git-sha <sha>` (#568) — git commit the deploy was built from. */
@@ -0,0 +1,54 @@
1
+ /**
2
+ * `--events-json` (#4041): a command's run-event stream on stdout, one
3
+ * `run-event.schema.json` line per event, also kept on this host for replay
4
+ * (#4042, `../run-event-store.ts`). Shared by `chant run`, the
5
+ * components commands that deploy (promote, rollback, redeploy, fan-out) and
6
+ * the pull-request loop (pr-plan, pr-apply).
7
+ */
8
+
9
+ import { formatError } from "./format";
10
+ import { openRunEventStore, teeLines } from "../run-event-store";
11
+ import { openRunEventStream, resolveRunEventRun, type RunEventStream } from "../run-event-stream";
12
+ import type { RunEventKind } from "../run-event";
13
+ import type { ParsedArgs } from "./registry";
14
+
15
+ /** Refuse `--events-json` beside `--progress-json`: both print to stdout. Returns true after printing why. */
16
+ export function refusesBothStreams(args: Pick<ParsedArgs, "eventsJson" | "progressJson">): boolean {
17
+ if (!args.eventsJson || !args.progressJson) return false;
18
+ console.error(formatError({
19
+ message: "--events-json and --progress-json both stream to stdout",
20
+ hint: "Pass one. --events-json prints the run-event stream; --progress-json prints the older progress lines.",
21
+ }));
22
+ return true;
23
+ }
24
+
25
+ /** Refuse `--events-json` beside `--json` where `--json` prints a report to stdout (pr-plan, pr-apply). Returns true after printing why. */
26
+ export function refusesEventsWithJson(args: Pick<ParsedArgs, "eventsJson" | "json">, report: string): boolean {
27
+ if (!args.eventsJson || !args.json) return false;
28
+ console.error(formatError({
29
+ message: "--events-json and --json both stream to stdout",
30
+ hint: `Pass one. --events-json prints the run-event stream; --json prints ${report}.`,
31
+ }));
32
+ return true;
33
+ }
34
+
35
+ /** Writes one line to stdout. */
36
+ export const stdoutLine = (line: string): void => void process.stdout.write(line);
37
+
38
+ /**
39
+ * The run's stream on stdout when `--events-json` was passed, else
40
+ * undefined. The run is named by `runId` when the caller has one (so a
41
+ * release record and the stream name the same run), else by the CI run or a
42
+ * local id.
43
+ */
44
+ export async function openCliRunEvents(
45
+ args: Pick<ParsedArgs, "eventsJson">,
46
+ run: { kind: RunEventKind; name: string; runId?: string; cwd?: string },
47
+ ): Promise<RunEventStream | undefined> {
48
+ if (!args.eventsJson) return undefined;
49
+ const resolved = await resolveRunEventRun(run);
50
+ // #4042: the stream is also kept on this host, so `chant run events <run>`
51
+ // replays it. Only a command that was asked for a stream keeps one.
52
+ const store = await openRunEventStore(run.cwd ?? process.cwd(), resolved.runId);
53
+ return openRunEventStream(resolved, teeLines(stdoutLine, store?.write.bind(store))!);
54
+ }
@@ -1,15 +1,5 @@
1
- import { createRequire } from "node:module";
2
-
3
1
  /**
4
- * The installed chant's version, read from `@intentius/chant`'s own
5
- * package.json, or "0.0.0" when it can't be read. The path is the same from
6
- * `src/cli/` and `dist/cli/`, so it holds whether chant runs from source or
7
- * from its build.
2
+ * The installed chant's version. It lives in `../chant-version.ts`, outside
3
+ * `cli/`, so the Op entry can name it without loading the CLI (#4041).
8
4
  */
9
- export const CHANT_VERSION: string = (() => {
10
- try {
11
- return (createRequire(import.meta.url)("../../package.json") as { version?: string }).version ?? "0.0.0";
12
- } catch {
13
- return "0.0.0";
14
- }
15
- })();
5
+ export { CHANT_VERSION } from "../chant-version";
@@ -0,0 +1,166 @@
1
+ import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "fs";
2
+ import { tmpdir } from "os";
3
+ import { join } from "path";
4
+ import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
5
+
6
+ import { docsPipeline, writeDocsSite } from "./docs";
7
+ import { DOCS_SITE_DEPENDENCIES, findChantDocsSite, rebaseImports, resolveDocsMode } from "./docs-in-site";
8
+ import type { DocsConfig } from "./docs-types";
9
+
10
+ /**
11
+ * chant #3993: inside the chant repository a lexicon's docs are a section of
12
+ * the main Starlight site. These run against a fake repository (a `docs/`
13
+ * whose package.json is the chant docs package) with one lexicon under it.
14
+ */
15
+
16
+ let repo: string;
17
+ let lex: string;
18
+
19
+ function config(overrides: Partial<DocsConfig> = {}): DocsConfig {
20
+ return {
21
+ name: "fixture",
22
+ displayName: "Fixture",
23
+ description: "fixture lexicon",
24
+ distDir: join(lex, "dist"),
25
+ outDir: join(lex, "docs"),
26
+ srcDir: join(lex, "src"),
27
+ basePath: "/chant/lexicons/fixture/",
28
+ ...overrides,
29
+ };
30
+ }
31
+
32
+ function page(name: string, frontmatter: string, body = "Body.\n"): void {
33
+ mkdirSync(join(lex, "docs", "pages"), { recursive: true });
34
+ writeFileSync(join(lex, "docs", "pages", name), `---\n${frontmatter}\n---\n\n${body}`);
35
+ }
36
+
37
+ const section = () => join(repo, "docs", "src", "content", "docs", "lexicons", "fixture");
38
+
39
+ beforeEach(() => {
40
+ vi.stubEnv("CHANT_DOCS_MODE", "");
41
+ vi.stubEnv("CHANT_DOCS_SITE", "");
42
+ repo = mkdtempSync(join(tmpdir(), "chant-docs-in-site-"));
43
+ mkdirSync(join(repo, "docs"), { recursive: true });
44
+ writeFileSync(join(repo, "docs", "package.json"), JSON.stringify({ name: "@intentius/chant-docs" }));
45
+ writeFileSync(join(repo, "docs", "astro.config.mjs"), "export default {};\n");
46
+ lex = join(repo, "lexicons", "fixture");
47
+ mkdirSync(join(lex, "dist"), { recursive: true });
48
+ mkdirSync(join(lex, "src"), { recursive: true });
49
+ writeFileSync(
50
+ join(lex, "dist", "manifest.json"),
51
+ JSON.stringify({ name: "fixture", version: "0.0.0", intrinsics: [{ name: "ref", description: "a ref" }] }),
52
+ );
53
+ writeFileSync(join(lex, "dist", "meta.json"), JSON.stringify({}));
54
+ });
55
+
56
+ afterEach(() => {
57
+ vi.unstubAllEnvs();
58
+ rmSync(repo, { recursive: true, force: true });
59
+ });
60
+
61
+ describe("which mode writeDocsSite runs in", () => {
62
+ test("a lexicon inside the chant repository goes in-site", () => {
63
+ expect(findChantDocsSite(lex)).toBe(join(repo, "docs"));
64
+ expect(resolveDocsMode(config())).toEqual({ mode: "in-site", siteDir: join(repo, "docs") });
65
+ });
66
+
67
+ test("a docs site that is not chant's does not count", () => {
68
+ writeFileSync(join(repo, "docs", "package.json"), JSON.stringify({ name: "someone-elses-docs" }));
69
+ expect(findChantDocsSite(lex)).toBeUndefined();
70
+ expect(resolveDocsMode(config()).mode).toBe("standalone");
71
+ });
72
+
73
+ test("config.mode and CHANT_DOCS_MODE override the detection", () => {
74
+ expect(resolveDocsMode(config({ mode: "standalone" })).mode).toBe("standalone");
75
+ vi.stubEnv("CHANT_DOCS_MODE", "standalone");
76
+ expect(resolveDocsMode(config()).mode).toBe("standalone");
77
+ });
78
+
79
+ test("in-site mode with no main site to write into is an error", () => {
80
+ rmSync(join(repo, "docs"), { recursive: true });
81
+ expect(() => resolveDocsMode(config({ mode: "in-site" }))).toThrow(/in-site mode needs the chant docs site/);
82
+ });
83
+ });
84
+
85
+ describe("in-site mode", () => {
86
+ test("writes pages and a sidebar fragment into the main site, and no app files", () => {
87
+ page("start.mdx", "title: Start\ndiataxis: tutorial");
88
+ page("secret.mdx", "title: Secret\ndiataxis: reference\nhidden: true");
89
+ const cfg = config();
90
+ writeDocsSite(cfg, docsPipeline(cfg));
91
+
92
+ for (const f of ["index.mdx", "start.mdx", "secret.mdx", "intrinsics.mdx", "serialization.mdx"]) {
93
+ expect(existsSync(join(section(), f)), f).toBe(true);
94
+ }
95
+ for (const f of ["package.json", "astro.config.mjs", "tsconfig.json", "src"]) {
96
+ expect(existsSync(join(lex, "docs", f)), f).toBe(false);
97
+ }
98
+
99
+ const fragment = JSON.parse(readFileSync(join(section(), "_sidebar.json"), "utf-8"));
100
+ expect(fragment.hidden).toEqual(["lexicons/fixture/secret"]);
101
+ expect(fragment.group.label).toBe("Fixture");
102
+ expect(fragment.group.collapsed).toBe(true);
103
+ expect(fragment.group.items.map((i: { label: string }) => i.label)).toEqual(["Overview", "Tutorials", "Reference"]);
104
+ expect(fragment.group.items[0]).toEqual({ label: "Overview", slug: "lexicons/fixture" });
105
+ expect(fragment.group.items[1]).toEqual({
106
+ label: "Tutorials",
107
+ collapsed: true,
108
+ items: [{ label: "Start", slug: "lexicons/fixture/start" }],
109
+ });
110
+ });
111
+
112
+ test("generated reference tables get the diataxis field their sidebar group names", () => {
113
+ const cfg = config();
114
+ writeDocsSite(cfg, docsPipeline(cfg));
115
+ expect(readFileSync(join(section(), "intrinsics.mdx"), "utf-8")).toMatch(/^---\n[\s\S]*\ndiataxis: reference\n---/);
116
+ expect(readFileSync(join(section(), "index.mdx"), "utf-8")).not.toContain("diataxis:");
117
+ });
118
+
119
+ test("a page the pipeline stops emitting is gone after the next run", () => {
120
+ page("start.mdx", "title: Start\ndiataxis: tutorial");
121
+ const cfg = config();
122
+ writeDocsSite(cfg, docsPipeline(cfg));
123
+ rmSync(join(lex, "docs", "pages", "start.mdx"));
124
+ writeDocsSite(cfg, docsPipeline(cfg));
125
+ expect(existsSync(join(section(), "start.mdx"))).toBe(false);
126
+ });
127
+
128
+ test("a page's import of the app's Diagram component points at the main site's", () => {
129
+ page("concepts.mdx", "title: Concepts\ndiataxis: explanation", "import Diagram from '../../components/Diagram.astro';\n\n<Diagram name=\"x\" alt=\"x\" />\n");
130
+ const cfg = config();
131
+ writeDocsSite(cfg, docsPipeline(cfg));
132
+ expect(readFileSync(join(section(), "concepts.mdx"), "utf-8")).toContain(
133
+ "import Diagram from '../../../../components/Diagram.astro';",
134
+ );
135
+ });
136
+ });
137
+
138
+ describe("rebaseImports", () => {
139
+ test("re-points relative imports and leaves code fences and packages alone", () => {
140
+ const outDir = "/r/lexicons/x/docs";
141
+ const pageDir = "/r/docs/src/content/docs/lexicons/x";
142
+ const src = [
143
+ "import { Aside } from '@astrojs/starlight/components';",
144
+ "import raw from '../../../../examples/a.ts?raw';",
145
+ "```ts",
146
+ "import { y } from '../src/y';",
147
+ "```",
148
+ ].join("\n");
149
+ expect(rebaseImports(src, { outDir, siteDir: "/r/docs", pageDir }).split("\n")).toEqual([
150
+ "import { Aside } from '@astrojs/starlight/components';",
151
+ "import raw from '../../../../../../lexicons/x/examples/a.ts?raw';",
152
+ "```ts",
153
+ "import { y } from '../src/y';",
154
+ "```",
155
+ ]);
156
+ });
157
+ });
158
+
159
+ describe("standalone docs app pins", () => {
160
+ test("match the main docs site's Starlight and Astro", () => {
161
+ const docsPkg = JSON.parse(readFileSync(join(import.meta.dirname, "..", "..", "..", "..", "docs", "package.json"), "utf-8"));
162
+ for (const dep of ["@astrojs/starlight", "astro", "sharp"]) {
163
+ expect(DOCS_SITE_DEPENDENCIES[dep], dep).toBe(docsPkg.dependencies[dep]);
164
+ }
165
+ });
166
+ });
@@ -0,0 +1,182 @@
1
+ /**
2
+ * The in-site mode of the lexicon docs pipeline (chant #3993).
3
+ *
4
+ * Inside the chant repository every lexicon's docs are a section of the main
5
+ * Starlight site in `docs/`: one Astro build, one search index, one design.
6
+ * `writeDocsSite` writes a lexicon's pages to
7
+ * `docs/src/content/docs/lexicons/<name>/` and, beside them, `_sidebar.json`,
8
+ * the lexicon's collapsed sidebar group with its Diátaxis subgroups. The main
9
+ * site's `astro.config.mjs` reads every fragment through
10
+ * `docs/src/lexicon-sidebar.mjs`. Nothing else is written: no package.json, no
11
+ * Astro config, no components.
12
+ *
13
+ * Outside the repository (a third-party lexicon, a project scaffolded by
14
+ * `chant init-lexicon`) the pipeline keeps writing a standalone Starlight app
15
+ * into the lexicon's own `docs/`, as it always has.
16
+ */
17
+
18
+ import { existsSync, readFileSync } from "fs";
19
+ import { dirname, join, relative, resolve, sep } from "path";
20
+
21
+ import { QUADRANT_LABELS, QUADRANTS } from "./docs-pages";
22
+ import { quadrantItems } from "./docs-sidebar";
23
+ import type { DocsConfig, DocsResult, Quadrant, SidebarPage } from "./docs-types";
24
+
25
+ export type DocsSiteMode = "in-site" | "standalone";
26
+
27
+ /**
28
+ * The dependencies a standalone lexicon docs app installs. They match
29
+ * `docs/package.json`, so a lexicon built on its own renders with the same
30
+ * Starlight and Astro as the main site; a test holds the two together.
31
+ */
32
+ export const DOCS_SITE_DEPENDENCIES: Readonly<Record<string, string>> = {
33
+ "@astrojs/starlight": "^0.37.6",
34
+ astro: "5.6.1",
35
+ sharp: "^0.34.2",
36
+ };
37
+
38
+ /** Package name of the main docs site, `docs/package.json` in the chant repository. */
39
+ const CHANT_DOCS_PACKAGE = "@intentius/chant-docs";
40
+
41
+ /**
42
+ * The chant repository's main docs site (`<repo>/docs`) above `start`, or
43
+ * undefined. A directory qualifies when its `docs/package.json` is the chant
44
+ * docs package and `docs/astro.config.mjs` exists beside it, so a third-party
45
+ * repository with a docs site of its own is never mistaken for chant.
46
+ */
47
+ export function findChantDocsSite(start: string): string | undefined {
48
+ let dir = resolve(start);
49
+ for (;;) {
50
+ const docs = join(dir, "docs");
51
+ const pkg = join(docs, "package.json");
52
+ if (existsSync(pkg) && existsSync(join(docs, "astro.config.mjs"))) {
53
+ try {
54
+ if ((JSON.parse(readFileSync(pkg, "utf-8")) as { name?: unknown }).name === CHANT_DOCS_PACKAGE) return docs;
55
+ } catch {
56
+ // Not JSON: not chant's docs site. Keep walking.
57
+ }
58
+ }
59
+ const parent = dirname(dir);
60
+ if (parent === dir) return undefined;
61
+ dir = parent;
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Which mode `writeDocsSite` runs in, and the main site's directory for
67
+ * in-site mode. `config.mode` wins, then `CHANT_DOCS_MODE`; with neither, a
68
+ * lexicon inside the chant repository goes in-site and any other standalone.
69
+ * `config.siteDir` or `CHANT_DOCS_SITE` names the main site when the walk up
70
+ * from `outDir` would not find it.
71
+ */
72
+ export function resolveDocsMode(config: DocsConfig): { mode: DocsSiteMode; siteDir?: string } {
73
+ const env = process.env.CHANT_DOCS_MODE;
74
+ const requested = config.mode ?? (env === "in-site" || env === "standalone" ? env : undefined);
75
+ if (requested === "standalone") return { mode: "standalone" };
76
+ const siteDir = config.siteDir || process.env.CHANT_DOCS_SITE || findChantDocsSite(config.outDir);
77
+ if (requested === "in-site") {
78
+ if (!siteDir) {
79
+ throw new Error(
80
+ `[docs:${config.name}] in-site mode needs the chant docs site: none above ${resolve(config.outDir)}. ` +
81
+ `Set siteDir or CHANT_DOCS_SITE, or use standalone mode.`,
82
+ );
83
+ }
84
+ return { mode: "in-site", siteDir: resolve(siteDir) };
85
+ }
86
+ return siteDir ? { mode: "in-site", siteDir: resolve(siteDir) } : { mode: "standalone" };
87
+ }
88
+
89
+ /** Where a lexicon's section lives inside the main site's content collection. */
90
+ export function sectionContentDir(siteDir: string, name: string): string {
91
+ return join(siteDir, "src", "content", "docs", "lexicons", name);
92
+ }
93
+
94
+ /**
95
+ * The lexicon's sidebar group for the main site: the lexicon's display name,
96
+ * collapsed, holding its overview and one collapsed group per non-empty
97
+ * Diátaxis quadrant. Slugs are prefixed with `lexicons/<name>/`.
98
+ */
99
+ export function buildSectionGroup(config: DocsConfig, result: DocsResult): Record<string, unknown> {
100
+ const prefix = `lexicons/${config.name}`;
101
+ const byQuadrant = new Map<Quadrant, SidebarPage[]>();
102
+ for (const page of result.sidebarPages) {
103
+ if (page.hidden) continue;
104
+ const list = byQuadrant.get(page.quadrant) ?? [];
105
+ list.push({ ...page, slug: `${prefix}/${page.slug}` });
106
+ byQuadrant.set(page.quadrant, list);
107
+ }
108
+ const items: Array<Record<string, unknown>> = [{ label: "Overview", slug: prefix }];
109
+ for (const q of QUADRANTS) {
110
+ const pages = byQuadrant.get(q);
111
+ if (!pages || pages.length === 0) continue;
112
+ items.push({ label: QUADRANT_LABELS[q], collapsed: true, items: collapseGroups(quadrantItems(pages)) });
113
+ }
114
+ return { label: config.displayName, collapsed: true, items };
115
+ }
116
+
117
+ /** Every nested group starts collapsed, as on the rest of the main site. */
118
+ function collapseGroups(items: Array<Record<string, unknown>>): Array<Record<string, unknown>> {
119
+ return items.map((item) =>
120
+ Array.isArray(item.items)
121
+ ? { label: item.label, collapsed: true, items: collapseGroups(item.items as Array<Record<string, unknown>>) }
122
+ : item,
123
+ );
124
+ }
125
+
126
+ /** Slugs of pages written but kept out of the sidebar, prefixed for the main site. */
127
+ export function hiddenSectionSlugs(config: DocsConfig, result: DocsResult): string[] {
128
+ const listed = new Set(result.sidebarPages.map((p) => p.slug));
129
+ return [...result.pages.keys()]
130
+ .map((f) => f.replace(/\.mdx?$/, ""))
131
+ .filter((slug) => slug !== "index" && !listed.has(slug))
132
+ .sort()
133
+ .map((slug) => `lexicons/${config.name}/${slug}`);
134
+ }
135
+
136
+ /**
137
+ * Give a page the `diataxis` field of the quadrant the sidebar files it under,
138
+ * when it has none. Authored pages always carry one; the generated reference
139
+ * tables do not, and the main site's Diátaxis check reads the field.
140
+ */
141
+ export function withQuadrant(content: string, quadrant: Quadrant | undefined): string {
142
+ if (!quadrant || !content.startsWith("---\n")) return content;
143
+ const end = content.indexOf("\n---", 4);
144
+ if (end === -1) return content;
145
+ if (/^diataxis:/m.test(content.slice(4, end))) return content;
146
+ return `${content.slice(0, end)}\ndiataxis: ${quadrant}${content.slice(end)}`;
147
+ }
148
+
149
+ const IMPORT_RE = /^(\s*import\s[^'"]*?\sfrom\s+|\s*import\s+)(['"])(\.\.?\/[^'"]+)\2(.*)$/;
150
+
151
+ /**
152
+ * Re-point a page's relative ESM imports from where the standalone app kept
153
+ * the page (`<outDir>/src/content/docs/`) to where the main site keeps it.
154
+ * An import of the app's own components (`<outDir>/src/components/X`) goes to
155
+ * the main site's `src/components/X`, which holds the one shared copy.
156
+ * Fenced code blocks are left alone; their `import` lines are examples.
157
+ */
158
+ export function rebaseImports(content: string, opts: { outDir: string; siteDir: string; pageDir: string }): string {
159
+ const fromDir = join(resolve(opts.outDir), "src", "content", "docs");
160
+ const appComponents = join(resolve(opts.outDir), "src", "components") + sep;
161
+ const siteComponents = join(opts.siteDir, "src", "components");
162
+ let fence: string | null = null;
163
+ return content
164
+ .split("\n")
165
+ .map((line) => {
166
+ const marker = line.match(/^\s*(`{3,}|~{3,})/)?.[1];
167
+ if (marker) {
168
+ if (fence === null) fence = marker[0];
169
+ else if (marker[0] === fence) fence = null;
170
+ return line;
171
+ }
172
+ if (fence !== null) return line;
173
+ const m = line.match(IMPORT_RE);
174
+ if (!m) return line;
175
+ let target = resolve(fromDir, m[3]);
176
+ if (target.startsWith(appComponents)) target = join(siteComponents, target.slice(appComponents.length));
177
+ let spec = relative(opts.pageDir, target).split(sep).join("/");
178
+ if (!spec.startsWith(".")) spec = `./${spec}`;
179
+ return `${m[1]}${m[2]}${spec}${m[2]}${m[4]}`;
180
+ })
181
+ .join("\n");
182
+ }
@@ -6,14 +6,26 @@
6
6
  // render it from their PageTitle override; lexicon sites get a copy of this
7
7
  // file from writeDocsSite (docs.ts). The content schema refuses a prompt with
8
8
  // no "Never" line. No Starlight import here, so the file works from any site.
9
+ // `text`, `label` and `note` replace the page's prompt and the caption (the
10
+ // main site's home page passes the setup prompt); an `icon` slot goes before
11
+ // the label (the main site passes its logo).
9
12
  interface Props {
10
13
  text?: string;
14
+ label?: string;
15
+ note?: string;
11
16
  }
17
+ const {
18
+ label = 'Optional: hand this page to your coding agent',
19
+ note = 'The steps work by hand too.',
20
+ } = Astro.props;
12
21
  const prompt = (Astro.props.text ?? Astro.locals.starlightRoute?.entry.data.prompt)?.trim();
13
22
  ---
14
23
  {prompt && (
15
24
  <figure class="chant-prompt not-content" id="agent-prompt">
16
- <figcaption>Optional: hand this page to your coding agent</figcaption>
25
+ <figcaption>
26
+ <span class="label"><slot name="icon" />{label}</span>
27
+ {note && <span class="note">{note}</span>}
28
+ </figcaption>
17
29
  <details>
18
30
  <summary>
19
31
  <pre class="peek" aria-hidden="true">{prompt}</pre>
@@ -69,11 +81,24 @@ const prompt = (Astro.props.text ?? Astro.locals.starlightRoute?.entry.data.prom
69
81
  --chant-prompt-blue: #60a5fa;
70
82
  }
71
83
  figcaption {
84
+ display: flex;
85
+ flex-wrap: wrap;
86
+ align-items: baseline;
87
+ gap: 0.25rem 0.75rem;
72
88
  padding: 0.7rem 1rem 0.4rem;
89
+ }
90
+ .label {
91
+ display: inline-flex;
92
+ align-items: center;
93
+ gap: 0.5rem;
73
94
  font-size: 0.85rem;
74
95
  font-weight: 600;
75
96
  color: var(--sl-color-white);
76
97
  }
98
+ .note {
99
+ font-size: 0.75rem;
100
+ color: var(--sl-color-gray-3);
101
+ }
77
102
  summary {
78
103
  display: block;
79
104
  list-style: none;
@@ -38,8 +38,21 @@ export interface DocsConfig {
38
38
  suppressPages?: string[];
39
39
  /** Source directory for scanning rule files (defaults to srcDir sibling of distDir) */
40
40
  srcDir?: string;
41
- /** Base path for the generated Astro site (e.g. '/lexicons/aws/') */
41
+ /** Base path for the generated Astro site (e.g. '/lexicons/aws/'). Standalone mode only. */
42
42
  basePath?: string;
43
+ /**
44
+ * Where `writeDocsSite` writes (chant #3993). `in-site`: the lexicon's pages
45
+ * and sidebar fragment go into the chant repository's main docs site.
46
+ * `standalone`: a Starlight app of its own in `outDir`. Unset, the
47
+ * `CHANT_DOCS_MODE` environment variable decides, and without it a lexicon
48
+ * inside the chant repository goes in-site and any other standalone.
49
+ */
50
+ mode?: "in-site" | "standalone";
51
+ /**
52
+ * The main docs site (`<chant repo>/docs`) for in-site mode. Defaults to
53
+ * `CHANT_DOCS_SITE`, then to the first one found walking up from `outDir`.
54
+ */
55
+ siteDir?: string;
43
56
  /** Root directory for resolving {{file:...}} markers in extra page content */
44
57
  examplesDir?: string;
45
58
  /**