@astryxdesign/cli 0.6.3-canary.ea2f048 → 0.6.3-canary.ee13359

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 (571) hide show
  1. package/README.md +114 -76
  2. package/api/blog/blog.doc.mjs +1 -0
  3. package/api/build/build.doc.mjs +4 -3
  4. package/api/build/build.test.mjs +14 -3
  5. package/api/build/build.type.d.mts +36 -2
  6. package/api/build/build.type.mjs +25 -4
  7. package/api/build/help/help.d.mts +8 -5
  8. package/api/build/help/help.mjs +56 -6
  9. package/api/component/_adapter.d.mts +6 -12
  10. package/api/component/_adapter.mjs +20 -10
  11. package/api/component/component.doc.mjs +13 -3
  12. package/api/component/component.mjs +91 -14
  13. package/api/component/component.test.mjs +38 -0
  14. package/api/component/component.type.d.mts +22 -11
  15. package/api/component/component.type.mjs +32 -24
  16. package/api/component/detail/blocks/blocks.d.mts +2 -1
  17. package/api/component/detail/blocks/blocks.mjs +4 -3
  18. package/api/component/list/list.d.mts +0 -5
  19. package/api/component/list/list.mjs +40 -11
  20. package/api/discover/discover.doc.mjs +2 -1
  21. package/api/discover/discover.type.d.mts +4 -4
  22. package/api/discover/discover.type.mjs +10 -10
  23. package/api/docs/_adapter.d.mts +230 -41
  24. package/api/docs/_adapter.mjs +734 -108
  25. package/api/docs/compiled-topics.test.mjs +78 -0
  26. package/api/docs/detail/detail.mjs +22 -63
  27. package/api/docs/detail/section/section.d.mts +1 -1
  28. package/api/docs/detail/section/section.mjs +54 -19
  29. package/api/docs/detail/section/section.test.mjs +50 -0
  30. package/api/docs/docs.d.mts +10 -3
  31. package/api/docs/docs.doc.mjs +54 -16
  32. package/api/docs/docs.mjs +53 -10
  33. package/api/docs/docs.test.mjs +164 -4
  34. package/api/docs/docs.type.d.mts +205 -5
  35. package/api/docs/docs.type.mjs +137 -11
  36. package/api/docs/index/index.d.mts +18 -0
  37. package/api/docs/index/index.mjs +40 -0
  38. package/api/docs/index/index.test.mjs +62 -0
  39. package/api/docs/integration-tree.test.mjs +547 -0
  40. package/api/docs/integrationDocs.test.mjs +114 -8
  41. package/api/docs/list/list.mjs +28 -12
  42. package/api/docs/node/node.d.mts +43 -0
  43. package/api/docs/node/node.mjs +192 -0
  44. package/api/doctor/doctor.d.mts +99 -1
  45. package/api/doctor/doctor.doc.mjs +1 -0
  46. package/api/doctor/doctor.mjs +544 -1
  47. package/api/doctor/doctor.test.mjs +610 -1
  48. package/api/gap-report/gap-report.doc.mjs +8 -4
  49. package/api/hook/_adapter.mjs +19 -5
  50. package/api/hook/hook.doc.mjs +1 -0
  51. package/api/hook/hook.type.d.mts +3 -3
  52. package/api/hook/hook.type.mjs +11 -11
  53. package/api/hook/list/list.d.mts +2 -2
  54. package/api/hook/list/list.mjs +69 -17
  55. package/api/index.d.mts +1 -1
  56. package/api/index.mjs +1 -0
  57. package/api/init/init.doc.mjs +6 -1
  58. package/api/init/init.test.mjs +41 -1
  59. package/api/init/remove/remove.mjs +1 -1
  60. package/api/init/run/run.mjs +20 -10
  61. package/api/integration/add-contribution.component-names.test.mjs +120 -0
  62. package/api/integration/add-contribution.d.mts +2 -1
  63. package/api/integration/add-contribution.mjs +130 -15
  64. package/api/integration/add-contribution.test.mjs +258 -7
  65. package/api/integration/add-theme.mjs +34 -64
  66. package/api/integration/add-theme.test.mjs +105 -21
  67. package/api/integration/authoring-checks.mjs +108 -31
  68. package/api/integration/authoring-checks.test.mjs +171 -6
  69. package/api/integration/authoring-checks.type.d.mts +3 -1
  70. package/api/integration/authoring-checks.type.mjs +3 -1
  71. package/api/integration/integration-authoring.type.d.mts +3 -1
  72. package/api/integration/integration-authoring.type.mjs +2 -0
  73. package/api/integration/integration-block-exports.test.mjs +10 -6
  74. package/api/integration/integrationAdd.doc.mjs +14 -4
  75. package/api/integration/integrationAddAgentDoc.doc.mjs +1 -0
  76. package/api/integration/integrationAddCodemod.doc.mjs +1 -0
  77. package/api/integration/integrationAddComponent.doc.mjs +2 -1
  78. package/api/integration/integrationAddDoc.doc.mjs +8 -1
  79. package/api/integration/integrationAddTemplate.doc.mjs +1 -0
  80. package/api/integration/integrationAddTheme.doc.mjs +6 -5
  81. package/api/integration/integrationComponentConflicts.doc.mjs +1 -0
  82. package/api/integration/integrationDocConflicts.doc.mjs +2 -1
  83. package/api/integration/integrationPackCheck.doc.mjs +2 -1
  84. package/api/integration/integrationTemplateConflicts.doc.mjs +7 -5
  85. package/api/integration/pack-check.mjs +83 -7
  86. package/api/integration/pack-check.test.mjs +387 -47
  87. package/api/integration/pack-check.type.d.mts +26 -2
  88. package/api/integration/pack-check.type.mjs +14 -1
  89. package/api/integration/summarizeIssues.doc.mjs +1 -0
  90. package/api/integration/validate-integration-fixes.test.mjs +1389 -0
  91. package/api/integration/validate-integration.mjs +52 -102
  92. package/api/integration/validate-integration.test.mjs +179 -26
  93. package/api/integration/validate-unread-theme-folders.test.mjs +110 -0
  94. package/api/integration/validateIntegration.doc.mjs +3 -2
  95. package/api/json/assertResponse.doc.mjs +1 -0
  96. package/api/json/envelope-types.test.mjs +76 -0
  97. package/api/json/index.ts +2 -0
  98. package/api/json/isError.doc.mjs +1 -0
  99. package/api/json/parseResponse.doc.mjs +3 -2
  100. package/api/layout/_adapter.mjs +20 -5
  101. package/api/layout/expand/expand.mjs +7 -5
  102. package/api/layout/expand/expand.path-safety.test.mjs +53 -0
  103. package/api/layout/grammar/grammar.mjs +2 -1
  104. package/api/layout/layoutCheck.doc.mjs +1 -0
  105. package/api/layout/layoutExpand.doc.mjs +2 -1
  106. package/api/layout/layoutGrammar.doc.mjs +1 -0
  107. package/api/search/search-return-type.test.mjs +54 -0
  108. package/api/search/search.d.mts +29 -11
  109. package/api/search/search.doc.mjs +8 -2
  110. package/api/search/search.mjs +337 -41
  111. package/api/search/search.test.mjs +124 -1
  112. package/api/search/search.type.d.mts +15 -3
  113. package/api/search/search.type.mjs +5 -2
  114. package/api/swizzle/copy/copy.mjs +28 -11
  115. package/api/swizzle/swizzle.doc.mjs +2 -1
  116. package/api/swizzle/swizzle.type.d.mts +2 -2
  117. package/api/swizzle/swizzle.type.mjs +2 -2
  118. package/api/template/copy/copy.mjs +17 -23
  119. package/api/template/copy/copy.test.mjs +17 -0
  120. package/api/template/list/list.mjs +1 -0
  121. package/api/template/template-integration.test.mjs +1022 -3
  122. package/api/template/template-suffix.test.mjs +41 -21
  123. package/api/template/template.d.mts +1 -1
  124. package/api/template/template.doc.mjs +30 -8
  125. package/api/template/template.mjs +45 -8
  126. package/api/template/template.type.d.mts +12 -14
  127. package/api/template/template.type.mjs +15 -14
  128. package/api/theme/_adapter.d.mts +2 -3
  129. package/api/theme/_adapter.mjs +4 -5
  130. package/api/theme/add/add.binary.test.mjs +84 -0
  131. package/api/theme/add/add.mjs +20 -3
  132. package/api/theme/add/add.staging.test.mjs +66 -0
  133. package/api/theme/add/add.test.mjs +14 -1
  134. package/api/theme/build/build.mjs +133 -42
  135. package/api/theme/build/build.public-component-vars.test.mjs +1 -1
  136. package/api/theme/build/build.receipt-doc.test.mjs +111 -0
  137. package/api/theme/build/build.test.mjs +127 -0
  138. package/api/theme/build/font-warning.mjs +3 -3
  139. package/api/theme/build/font-warning.test.mjs +5 -2
  140. package/api/theme/generateTonalPalette.doc.mjs +1 -0
  141. package/api/theme/integration-themes.test.mjs +39 -28
  142. package/api/theme/list/list.test.mjs +19 -20
  143. package/api/theme/listThemes.doc.mjs +6 -5
  144. package/api/theme/palette/generate/generate.mjs +8 -3
  145. package/api/theme/palette/generate/generate.test.mjs +96 -0
  146. package/api/theme/palette/generate/generator.d.mts +10 -13
  147. package/api/theme/palette/generate/generator.mjs +15 -4
  148. package/api/theme/palette/generate/generator.test.mjs +10 -0
  149. package/api/theme/template/template.mjs +11 -2
  150. package/api/theme/template/template.test.mjs +20 -0
  151. package/api/theme/theme.type.d.mts +170 -11
  152. package/api/theme/theme.type.mjs +94 -27
  153. package/api/theme/themeAdd.doc.mjs +4 -3
  154. package/api/theme/themeBuild.doc.mjs +8 -4
  155. package/api/theme/themeList.doc.mjs +6 -3
  156. package/api/theme/themeListAvailable.doc.mjs +5 -3
  157. package/api/theme/themePaletteGenerate.doc.mjs +1 -0
  158. package/api/theme/themeTargets.doc.mjs +1 -0
  159. package/api/theme/themeTemplate.doc.mjs +6 -2
  160. package/api/upgrade/_adapter.d.mts +32 -5
  161. package/api/upgrade/_adapter.mjs +139 -22
  162. package/api/upgrade/list/list.mjs +2 -1
  163. package/api/upgrade/list/list.test.mjs +73 -0
  164. package/api/upgrade/project-context.test.mjs +272 -0
  165. package/api/upgrade/provider-agreement.test.mjs +152 -0
  166. package/api/upgrade/run/run.mjs +356 -59
  167. package/api/upgrade/status/status.mjs +2 -2
  168. package/api/upgrade/upgrade.doc.mjs +12 -5
  169. package/api/upgrade/upgrade.type.d.mts +43 -5
  170. package/api/upgrade/upgrade.type.mjs +27 -11
  171. package/assets/codemods/__tests__/runner.test.mjs +330 -8
  172. package/assets/codemods/integration-discovery.mjs +48 -4
  173. package/assets/codemods/integration-discovery.test.mjs +73 -0
  174. package/assets/codemods/integration-runner.mjs +56 -4
  175. package/assets/codemods/integration-runner.protection.test.mjs +153 -0
  176. package/assets/codemods/run-codemod.mjs +177 -34
  177. package/assets/codemods/runner.mjs +350 -102
  178. package/assets/codemods/term-log.mjs +32 -8
  179. package/assets/codemods/term-log.test.mjs +19 -1
  180. package/assets/codemods/transform-prop.mjs +109 -0
  181. package/assets/codemods/transform-prop.test.mjs +95 -0
  182. package/assets/codemods/transforms/next/__tests__/migrate-native-picker-to-presentation.test.mjs +63 -0
  183. package/assets/codemods/transforms/next/__tests__/migrate-theme-catalog-to-descriptors.test.mjs +220 -0
  184. package/assets/codemods/transforms/next/index.mjs +19 -1
  185. package/assets/codemods/transforms/next/migrate-native-picker-to-presentation.mjs +148 -0
  186. package/assets/codemods/transforms/next/migrate-theme-catalog-to-descriptors.mjs +141 -0
  187. package/assets/codemods/transforms/v0.0.14/__tests__/rename-status-variants.test.mjs +86 -165
  188. package/assets/codemods/transforms/v0.0.14/rename-status-variants.mjs +72 -210
  189. package/assets/codemods/transforms/v0.1.8/__tests__/rename-avatar-size-scale.test.mjs +83 -115
  190. package/assets/codemods/transforms/v0.1.8/rename-avatar-size-scale.mjs +57 -186
  191. package/assets/codemods/transforms/v0.3.0/__tests__/unwrap-authoring-factories.test.mjs +27 -5
  192. package/assets/codemods/transforms/v0.3.0/unwrap-authoring-factories.mjs +20 -5
  193. package/assets/codemods/transforms/v0.6.0/__tests__/next-codemods.test.mjs +47 -0
  194. package/assets/codemods/transforms/v0.6.0/rename-resizable-pixel-bounds.mjs +57 -10
  195. package/assets/docs/README.md +9 -0
  196. package/assets/docs/authoring.doc.mjs +14 -0
  197. package/assets/docs/getting-started.doc.mjs +2 -2
  198. package/assets/docs/principles.doc.mjs +6 -6
  199. package/assets/docs/styling-libraries.doc.mjs +3 -3
  200. package/assets/docs/styling.doc.mjs +4 -4
  201. package/assets/docs/theme.doc.mjs +4 -4
  202. package/assets/docs/tokens.doc.mjs +1 -1
  203. package/assets/docs/tree/api.doc.mjs +30 -0
  204. package/assets/docs/tree/cli.doc.mjs +23 -0
  205. package/assets/docs/tree/commands.doc.mjs +25 -0
  206. package/assets/docs/{cli-integrations.doc.mjs → tree/integrations.doc.mjs} +124 -25
  207. package/assets/docs/tree/integrations.test.mjs +62 -0
  208. package/assets/docs/tree/writing-docs.doc.mjs +286 -0
  209. package/assets/docs/working-with-ai.doc.mjs +1 -1
  210. package/assets/templates/blocks/components/DateInput/DateInputDateRange.tsx +1 -1
  211. package/assets/templates/blocks/components/Item/ItemDocumentTabs.doc.mjs +14 -0
  212. package/assets/templates/blocks/components/Item/ItemDocumentTabs.tsx +100 -0
  213. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaInlineRail.doc.mjs +14 -0
  214. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaInlineRail.tsx +61 -0
  215. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaOverscrollChaining.doc.mjs +14 -0
  216. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaOverscrollChaining.tsx +126 -0
  217. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaShowcase.doc.mjs +15 -0
  218. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaShowcase.tsx +86 -0
  219. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyGroupHeaders.doc.mjs +14 -0
  220. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyGroupHeaders.tsx +99 -0
  221. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyPassthrough.doc.mjs +14 -0
  222. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyPassthrough.tsx +122 -0
  223. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaTwoAxisBoard.doc.mjs +14 -0
  224. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaTwoAxisBoard.tsx +95 -0
  225. package/assets/templates/blocks/components/TimeInput/TimeInputConstrained.tsx +1 -0
  226. package/assets/templates/pages/table-tree/page.tsx +1704 -0
  227. package/assets/templates/pages/table-tree/template.doc.mjs +12 -0
  228. package/assets/templates/themes/butter/butterTheme.doc.mjs +11 -0
  229. package/assets/templates/themes/chocolate/chocolateTheme.doc.mjs +11 -0
  230. package/assets/templates/themes/gothic/gothicTheme.doc.mjs +11 -0
  231. package/assets/templates/themes/matcha/matchaTheme.doc.mjs +11 -0
  232. package/assets/templates/themes/neutral/neutralTheme.doc.mjs +11 -0
  233. package/assets/templates/themes/stone/stoneTheme.doc.mjs +11 -0
  234. package/assets/templates/themes/y2k/y2kTheme.doc.mjs +11 -0
  235. package/authoring/_shared/contract.ts +22 -0
  236. package/authoring/codemod/codemod.doc.mjs +7 -2
  237. package/authoring/codemod/parse.d.mts +8 -8
  238. package/authoring/codemod/parse.mjs +8 -6
  239. package/authoring/codemod/type.ts +12 -0
  240. package/authoring/config/config.doc.mjs +2 -2
  241. package/authoring/config/debug-composition.test.mjs +92 -0
  242. package/authoring/config/parse.d.mts +13 -13
  243. package/authoring/config/parse.mjs +8 -8
  244. package/authoring/config/type.ts +18 -6
  245. package/authoring/debug/debug.doc.d.mts +11 -0
  246. package/authoring/debug/debug.doc.mjs +182 -0
  247. package/authoring/debug/parse.d.mts +8 -8
  248. package/authoring/debug/parse.mjs +3 -3
  249. package/authoring/doctypes/_schema.d.mts +718 -23
  250. package/authoring/doctypes/_schema.mjs +519 -39
  251. package/authoring/doctypes/base/graph-fields.doc.d.mts +9 -0
  252. package/authoring/doctypes/base/graph-fields.doc.mjs +64 -0
  253. package/authoring/doctypes/base/type.ts +41 -0
  254. package/authoring/doctypes/command/command.doc.mjs +4 -3
  255. package/authoring/doctypes/command/parse.d.mts +2 -2
  256. package/authoring/doctypes/command/parse.mjs +1 -1
  257. package/authoring/doctypes/command/type.ts +5 -4
  258. package/authoring/doctypes/component/component.doc.mjs +6 -3
  259. package/authoring/doctypes/component/parse.d.mts +2 -2
  260. package/authoring/doctypes/component/parse.mjs +1 -1
  261. package/authoring/doctypes/component/type.ts +6 -5
  262. package/authoring/doctypes/doctypes-new.test.mjs +48 -6
  263. package/authoring/doctypes/enum/enum.doc.mjs +1 -1
  264. package/authoring/doctypes/enum/parse.d.mts +2 -2
  265. package/authoring/doctypes/enum/parse.mjs +1 -1
  266. package/authoring/doctypes/enum/type.ts +4 -2
  267. package/authoring/doctypes/function/function.doc.mjs +7 -2
  268. package/authoring/doctypes/function/parse.d.mts +2 -2
  269. package/authoring/doctypes/function/parse.mjs +1 -1
  270. package/authoring/doctypes/function/type.ts +9 -4
  271. package/authoring/doctypes/hook/hook.doc.mjs +4 -0
  272. package/authoring/doctypes/hook/parse.d.mts +2 -2
  273. package/authoring/doctypes/hook/parse.mjs +1 -1
  274. package/authoring/doctypes/hook/type.ts +5 -4
  275. package/authoring/doctypes/legacy.d.mts +8 -6
  276. package/authoring/doctypes/legacy.mjs +5 -4
  277. package/authoring/doctypes/load-contract.test.mjs +208 -0
  278. package/authoring/doctypes/namespace/namespace.doc.d.mts +9 -0
  279. package/authoring/doctypes/namespace/namespace.doc.mjs +128 -0
  280. package/authoring/doctypes/namespace/parse.d.mts +12 -0
  281. package/authoring/doctypes/namespace/parse.mjs +25 -0
  282. package/authoring/doctypes/namespace/parse.test.mjs +163 -0
  283. package/authoring/doctypes/namespace/type.ts +74 -0
  284. package/authoring/doctypes/parse.d.mts +22 -18
  285. package/authoring/doctypes/parse.mjs +22 -11
  286. package/authoring/doctypes/parse.test.mjs +77 -3
  287. package/authoring/doctypes/reference/parse.d.mts +2 -2
  288. package/authoring/doctypes/reference/parse.mjs +8 -5
  289. package/authoring/doctypes/reference/reference.doc.mjs +20 -5
  290. package/authoring/doctypes/reference/type.ts +53 -5
  291. package/authoring/doctypes/schema/parse.d.mts +2 -2
  292. package/authoring/doctypes/schema/parse.mjs +1 -1
  293. package/authoring/doctypes/schema/schema.doc.mjs +1 -1
  294. package/authoring/doctypes/schema/type.ts +4 -4
  295. package/authoring/doctypes/template/parse.d.mts +94 -1
  296. package/authoring/doctypes/template/parse.mjs +40 -2
  297. package/authoring/doctypes/template/parse.test.mjs +26 -2
  298. package/authoring/doctypes/template/template.doc.mjs +13 -3
  299. package/authoring/doctypes/template/type.ts +13 -2
  300. package/authoring/doctypes/theme/parse.d.mts +35 -0
  301. package/authoring/doctypes/theme/parse.mjs +76 -0
  302. package/authoring/doctypes/theme/theme.doc.d.mts +9 -0
  303. package/authoring/doctypes/theme/theme.doc.mjs +79 -0
  304. package/authoring/doctypes/theme/type.ts +42 -0
  305. package/authoring/doctypes/types.ts +12 -10
  306. package/authoring/gap-report/gap-report.doc.d.mts +12 -0
  307. package/authoring/gap-report/gap-report.doc.mjs +183 -0
  308. package/authoring/gap-report/parse.d.mts +10 -10
  309. package/authoring/gap-report/parse.mjs +6 -6
  310. package/authoring/gap-report/type.ts +1 -1
  311. package/authoring/identity/identity.doc.d.mts +9 -0
  312. package/authoring/identity/identity.doc.mjs +61 -0
  313. package/authoring/identity/type.ts +132 -0
  314. package/authoring/index.d.mts +2 -0
  315. package/authoring/index.d.ts +52 -17
  316. package/authoring/index.mjs +3 -1
  317. package/authoring/integration/integration.doc.mjs +15 -8
  318. package/authoring/integration/parse.d.mts +2 -2
  319. package/authoring/integration/parse.mjs +1 -1
  320. package/authoring/integration/parse.test.mjs +10 -1
  321. package/authoring/integration/schema.d.mts +6 -4
  322. package/authoring/integration/schema.mjs +9 -3
  323. package/authoring/integration/type.ts +19 -8
  324. package/authoring/shadcn/receipt.d.mts +6 -6
  325. package/clients/cli/__tests__/cliManifest.test.ts +27 -29
  326. package/clients/cli/command-load-failure.test.mjs +83 -0
  327. package/clients/cli/commands/blog.doc.mjs +1 -1
  328. package/clients/cli/commands/blog.mjs +23 -8
  329. package/clients/cli/commands/blog.test.mjs +42 -1
  330. package/clients/cli/commands/build-theme.adaptations.test.mjs +100 -1
  331. package/clients/cli/commands/build-theme.ascii-output.test.mjs +161 -0
  332. package/clients/cli/commands/build-theme.flag-docs.test.mjs +110 -0
  333. package/clients/cli/commands/build-theme.mjs +16 -50
  334. package/clients/cli/commands/build-theme.path-safety.test.mjs +86 -1
  335. package/clients/cli/commands/build-theme.variants.test.mjs +76 -0
  336. package/clients/cli/commands/build.doc.mjs +5 -2
  337. package/clients/cli/commands/build.exit-codes-doc.test.mjs +50 -0
  338. package/clients/cli/commands/build.mjs +37 -49
  339. package/clients/cli/commands/build.playbook.test.mjs +75 -0
  340. package/clients/cli/commands/build.text-fields.test.mjs +40 -0
  341. package/clients/cli/commands/component/index.mjs +3 -8
  342. package/clients/cli/commands/component-ownership.test.mjs +3 -3
  343. package/clients/cli/commands/component-package.test.mjs +46 -0
  344. package/clients/cli/commands/component-resolution.test.mjs +21 -0
  345. package/clients/cli/commands/component.doc.mjs +1 -1
  346. package/clients/cli/commands/component.test.mjs +19 -0
  347. package/clients/cli/commands/detail-levels.test.mjs +2 -2
  348. package/clients/cli/commands/discover.broken-integration.test.mjs +52 -5
  349. package/clients/cli/commands/discover.components-flag.test.mjs +97 -0
  350. package/clients/cli/commands/discover.doc.mjs +5 -3
  351. package/clients/cli/commands/discover.mjs +4 -4
  352. package/clients/cli/commands/discover.text-projection.test.mjs +103 -0
  353. package/clients/cli/commands/docs.doc.mjs +28 -6
  354. package/clients/cli/commands/docs.mjs +238 -24
  355. package/clients/cli/commands/docs.test.mjs +193 -1
  356. package/clients/cli/commands/doctor-integration-components.doc.mjs +1 -1
  357. package/clients/cli/commands/doctor-integration-docs.doc.mjs +3 -3
  358. package/clients/cli/commands/doctor-integration-templates.doc.mjs +15 -8
  359. package/clients/cli/commands/doctor-integration-validate.doc.mjs +1 -1
  360. package/clients/cli/commands/doctor-integration.doc.mjs +1 -1
  361. package/clients/cli/commands/doctor-integration.package-json.test.mjs +53 -0
  362. package/clients/cli/commands/doctor-integration.test.mjs +83 -8
  363. package/clients/cli/commands/doctor.doc.mjs +1 -1
  364. package/clients/cli/commands/doctor.mjs +30 -25
  365. package/clients/cli/commands/doctor.test.mjs +42 -0
  366. package/clients/cli/commands/gap-report.doc.mjs +17 -6
  367. package/clients/cli/commands/gap-report.test.mjs +72 -0
  368. package/clients/cli/commands/hook/index.mjs +7 -17
  369. package/clients/cli/commands/hook.doc.mjs +1 -1
  370. package/clients/cli/commands/hook.text-projection.test.mjs +45 -0
  371. package/clients/cli/commands/init.doc.mjs +20 -9
  372. package/clients/cli/commands/init.flag-help.test.mjs +153 -0
  373. package/clients/cli/commands/integration-add.controls.test.mjs +132 -0
  374. package/clients/cli/commands/integration-add.doc.mjs +32 -6
  375. package/clients/cli/commands/integration-authoring.test.mjs +13 -9
  376. package/clients/cli/commands/integration-pack.doc.mjs +1 -1
  377. package/clients/cli/commands/integration-real-world.test.mjs +3 -9
  378. package/clients/cli/commands/integration.doc.mjs +1 -1
  379. package/clients/cli/commands/integration.mjs +1 -0
  380. package/clients/cli/commands/interactive-guard.test.mjs +101 -24
  381. package/clients/cli/commands/json-contract.test.mjs +33 -0
  382. package/clients/cli/commands/layout-check.doc.mjs +15 -4
  383. package/clients/cli/commands/layout-expand.doc.mjs +22 -5
  384. package/clients/cli/commands/layout-grammar.doc.mjs +1 -1
  385. package/clients/cli/commands/layout.doc.mjs +3 -3
  386. package/clients/cli/commands/layout.mjs +21 -9
  387. package/clients/cli/commands/layout.path-help.test.mjs +33 -0
  388. package/clients/cli/commands/layout.stdin-cap.test.mjs +47 -0
  389. package/clients/cli/commands/layout.text-fields.test.mjs +39 -0
  390. package/clients/cli/commands/manifest.doc.mjs +1 -1
  391. package/clients/cli/commands/no-prompt-wording.test.mjs +94 -0
  392. package/clients/cli/commands/search.doc.mjs +7 -4
  393. package/clients/cli/commands/search.mjs +28 -9
  394. package/clients/cli/commands/search.test.mjs +75 -0
  395. package/clients/cli/commands/setup-nudge.test.mjs +6 -0
  396. package/clients/cli/commands/swizzle.doc.mjs +3 -2
  397. package/clients/cli/commands/swizzle.path-safety.test.mjs +42 -0
  398. package/clients/cli/commands/template.doc.mjs +52 -13
  399. package/clients/cli/commands/template.flag-help.test.mjs +117 -0
  400. package/clients/cli/commands/template.mjs +4 -91
  401. package/clients/cli/commands/template.path-help.test.mjs +40 -0
  402. package/clients/cli/commands/text-json-parity.test.mjs +719 -0
  403. package/clients/cli/commands/theme-add.doc.mjs +4 -3
  404. package/clients/cli/commands/theme-build.doc.mjs +8 -7
  405. package/clients/cli/commands/theme-list.doc.mjs +2 -2
  406. package/clients/cli/commands/theme-palette-generate.doc.mjs +9 -5
  407. package/clients/cli/commands/theme-palette-generate.test.mjs +19 -0
  408. package/clients/cli/commands/theme-palette.doc.mjs +1 -1
  409. package/clients/cli/commands/theme-targets.behavior.test.mjs +4 -3
  410. package/clients/cli/commands/theme-targets.doc.mjs +1 -1
  411. package/clients/cli/commands/theme-template.behavior.test.mjs +12 -0
  412. package/clients/cli/commands/theme-template.doc.mjs +2 -2
  413. package/clients/cli/commands/theme.doc.mjs +1 -1
  414. package/clients/cli/commands/upgrade.ascii-output.test.mjs +87 -0
  415. package/clients/cli/commands/upgrade.doc.mjs +22 -10
  416. package/clients/cli/commands/upgrade.file-protection.test.mjs +228 -0
  417. package/clients/cli/commands/upgrade.flag-help.test.mjs +188 -0
  418. package/clients/cli/commands/upgrade.hook-output.test.mjs +88 -0
  419. package/clients/cli/commands/upgrade.mjs +29 -7
  420. package/clients/cli/formatters/index.mjs +164 -1
  421. package/clients/cli/formatters/index.test.mjs +97 -0
  422. package/clients/cli/index.mjs +21 -30
  423. package/clients/cli/latest-version-env.test.mjs +50 -0
  424. package/clients/cli/lib/cli-error.test.mjs +7 -0
  425. package/clients/cli/lib/component-format.mjs +9 -9
  426. package/clients/cli/lib/component-format.test.mjs +1 -1
  427. package/clients/cli/lib/define-command.mjs +32 -6
  428. package/clients/cli/lib/doc-text-ascii.test.mjs +82 -0
  429. package/clients/cli/lib/exit-codes.test.mjs +97 -0
  430. package/clients/cli/lib/hook-format.mjs +19 -10
  431. package/clients/cli/lib/json-shim.mjs +38 -2
  432. package/clients/cli/lib/json-shim.test.mjs +83 -0
  433. package/clients/cli/lib/manifest.d.ts +2 -0
  434. package/clients/cli/lib/manifest.mjs +36 -2
  435. package/clients/cli/lib/manifest.test.mjs +17 -0
  436. package/foundation/agent-docs/agent-docs.d.mts +4 -0
  437. package/foundation/agent-docs/agent-docs.mjs +70 -3
  438. package/foundation/agent-docs/agent-docs.path-safety.test.mjs +266 -4
  439. package/foundation/agent-docs/agent-docs.test.mjs +10 -1
  440. package/foundation/config/integration-debug.test.mjs +28 -3
  441. package/foundation/config/project-themes.test.mjs +11 -19
  442. package/foundation/config/project.d.mts +20 -11
  443. package/foundation/config/project.mjs +263 -91
  444. package/foundation/config/project.test.mjs +270 -21
  445. package/foundation/discovery/authoring-self-docs.d.mts +75 -0
  446. package/foundation/discovery/authoring-self-docs.mjs +224 -0
  447. package/foundation/discovery/authoring-self-docs.test.mjs +165 -0
  448. package/foundation/discovery/authoring-surface.d.mts +74 -0
  449. package/foundation/discovery/authoring-surface.mjs +525 -0
  450. package/foundation/discovery/authoring-surface.test.mjs +392 -0
  451. package/foundation/discovery/cli-self-docs.d.mts +119 -0
  452. package/foundation/discovery/cli-self-docs.mjs +490 -0
  453. package/foundation/discovery/cli-self-docs.test.mjs +375 -0
  454. package/foundation/discovery/component-discovery.d.mts +39 -1
  455. package/foundation/discovery/component-discovery.mjs +50 -1
  456. package/foundation/discovery/component-loader.d.mts +35 -38
  457. package/foundation/discovery/component-loader.mjs +53 -222
  458. package/foundation/discovery/docs-discovery.d.mts +116 -11
  459. package/foundation/discovery/docs-discovery.mjs +389 -108
  460. package/foundation/discovery/docs-discovery.test.mjs +325 -20
  461. package/foundation/discovery/docs-output-budget.d.mts +28 -0
  462. package/foundation/discovery/docs-output-budget.mjs +50 -0
  463. package/foundation/discovery/docs-section-key.d.mts +116 -0
  464. package/foundation/discovery/docs-section-key.mjs +322 -0
  465. package/foundation/discovery/docs-section-key.test.mjs +246 -0
  466. package/foundation/discovery/template-adapter.d.mts +113 -11
  467. package/foundation/discovery/template-adapter.fixture-refs.test.mjs +248 -0
  468. package/foundation/discovery/template-adapter.integration-isolation.test.mjs +94 -0
  469. package/foundation/discovery/template-adapter.mjs +774 -83
  470. package/foundation/discovery/template-adapter.test.mjs +57 -0
  471. package/foundation/discovery/theme-discovery.d.mts +67 -7
  472. package/foundation/discovery/theme-discovery.mjs +916 -186
  473. package/foundation/discovery/theme-discovery.test.mjs +613 -219
  474. package/foundation/discovery/theming-targets.test.mjs +4 -0
  475. package/foundation/doc-compiler/bundle.d.mts +47 -0
  476. package/foundation/doc-compiler/bundle.mjs +278 -0
  477. package/foundation/doc-compiler/bundle.test.mjs +266 -0
  478. package/foundation/doc-compiler/compile.d.mts +343 -0
  479. package/foundation/doc-compiler/compile.mjs +558 -0
  480. package/foundation/doc-compiler/diagnostics.d.mts +126 -0
  481. package/foundation/doc-compiler/diagnostics.mjs +305 -0
  482. package/foundation/doc-compiler/doc-compiler.test.mjs +714 -0
  483. package/foundation/doc-compiler/doc-loads.test.mjs +1642 -0
  484. package/foundation/doc-compiler/import.d.mts +24 -0
  485. package/foundation/doc-compiler/import.mjs +59 -0
  486. package/foundation/doc-compiler/inputs.d.mts +102 -0
  487. package/foundation/doc-compiler/inputs.mjs +291 -0
  488. package/foundation/doc-compiler/inputs.test.mjs +299 -0
  489. package/foundation/doc-compiler/ir.d.mts +22 -0
  490. package/foundation/doc-compiler/ir.mjs +471 -0
  491. package/foundation/doc-compiler/lenses.d.mts +34 -0
  492. package/foundation/doc-compiler/lenses.mjs +128 -0
  493. package/foundation/doc-compiler/links.d.mts +135 -0
  494. package/foundation/doc-compiler/links.mjs +253 -0
  495. package/foundation/doc-compiler/links.test.mjs +139 -0
  496. package/foundation/doc-compiler/lower-doc.test.mjs +495 -0
  497. package/foundation/doc-compiler/overlays.d.mts +37 -0
  498. package/foundation/doc-compiler/overlays.mjs +206 -0
  499. package/foundation/doc-compiler/parse-readable.d.mts +9 -0
  500. package/foundation/doc-compiler/parse-readable.mjs +29 -0
  501. package/foundation/doc-compiler/read.d.mts +127 -0
  502. package/foundation/doc-compiler/read.mjs +325 -0
  503. package/foundation/doc-compiler/read.test.mjs +313 -0
  504. package/foundation/doc-compiler/source.d.mts +33 -0
  505. package/foundation/doc-compiler/source.mjs +128 -0
  506. package/foundation/doc-compiler/tree.d.mts +288 -0
  507. package/foundation/doc-compiler/tree.mjs +876 -0
  508. package/foundation/doc-compiler/tree.test.mjs +598 -0
  509. package/foundation/fs/file-protection.d.mts +33 -0
  510. package/foundation/fs/file-protection.mjs +825 -0
  511. package/foundation/fs/file-protection.test.mjs +250 -0
  512. package/foundation/fs/module-loader.d.mts +1 -0
  513. package/foundation/fs/module-loader.mjs +50 -1
  514. package/foundation/fs/module-loader.stdout.test.mjs +332 -0
  515. package/foundation/fs/path-safety.d.mts +3 -2
  516. package/foundation/fs/path-safety.mjs +49 -19
  517. package/foundation/fs/path-safety.test.mjs +50 -0
  518. package/foundation/fs/publish-file-hardlink-unavailable.test.mjs +129 -94
  519. package/foundation/identity/provider-identity.d.mts +90 -0
  520. package/foundation/identity/provider-identity.mjs +320 -0
  521. package/foundation/identity/provider-identity.test.mjs +254 -0
  522. package/foundation/identity/providers.d.mts +7 -0
  523. package/foundation/identity/providers.mjs +16 -0
  524. package/foundation/integrations/autolink.d.mts +58 -1
  525. package/foundation/integrations/autolink.mjs +143 -45
  526. package/foundation/integrations/autolink.test.mjs +1 -1
  527. package/foundation/integrations/cli-requirement.d.mts +45 -0
  528. package/foundation/integrations/cli-requirement.mjs +154 -0
  529. package/foundation/integrations/cli-requirement.test.mjs +109 -0
  530. package/foundation/integrations/contribution-fixes.d.mts +145 -0
  531. package/foundation/integrations/contribution-fixes.mjs +1284 -0
  532. package/foundation/integrations/contribution-inventory.d.mts +3 -2
  533. package/foundation/integrations/contribution-inventory.mjs +27 -24
  534. package/foundation/integrations/contribution-inventory.test.mjs +86 -27
  535. package/foundation/integrations/integration-warnings.d.mts +9 -2
  536. package/foundation/integrations/integration-warnings.mjs +52 -21
  537. package/foundation/integrations/integration-warnings.test.mjs +74 -1
  538. package/foundation/integrations/integrations.d.mts +49 -2
  539. package/foundation/integrations/integrations.mjs +81 -8
  540. package/foundation/integrations/integrations.test.mjs +384 -1
  541. package/foundation/integrations/provider-conflicts.test.mjs +125 -0
  542. package/foundation/integrations/provider-ledger.test.mjs +275 -0
  543. package/foundation/integrations/provider-resolution.d.mts +152 -0
  544. package/foundation/integrations/provider-resolution.mjs +576 -0
  545. package/foundation/integrations/provider-resolution.test.mjs +369 -0
  546. package/foundation/integrations/theme-descriptor.d.mts +8 -0
  547. package/foundation/integrations/theme-descriptor.mjs +44 -0
  548. package/foundation/integrations/validate-contributions.d.mts +2 -0
  549. package/foundation/integrations/validate-contributions.mjs +131 -29
  550. package/foundation/response/base.d.ts +8 -4
  551. package/foundation/response/error-codes.d.mts +3 -1
  552. package/foundation/response/error-codes.d.ts +2 -0
  553. package/foundation/response/error-codes.doc.mjs +13 -4
  554. package/foundation/response/error-codes.mjs +8 -2
  555. package/foundation/response/error-codes.test.mjs +137 -10
  556. package/foundation/response/json-contract.test.mjs +57 -17
  557. package/foundation/response/json.d.mts +4 -2
  558. package/foundation/response/json.mjs +8 -10
  559. package/foundation/response/response-types.doc.mjs +26 -16
  560. package/foundation/response/response-types.doc.test.mjs +158 -0
  561. package/foundation/response/response.doc.mjs +1 -1
  562. package/foundation/text/string-utils.mjs +22 -10
  563. package/foundation/xle/expand.d.mts +2 -0
  564. package/foundation/xle/expand.mjs +4 -3
  565. package/foundation/xle/expand.test.mjs +54 -0
  566. package/foundation/xle/xle.test.mjs +13 -0
  567. package/package.json +10 -11
  568. package/assets/templates/themes/manifest.json +0 -95
  569. package/clients/cli/lib/update-check.mjs +0 -83
  570. package/clients/cli/lib/update-check.test.mjs +0 -137
  571. package/clients/cli/update-hint-commands.test.mjs +0 -54
@@ -20,7 +20,12 @@
20
20
  */
21
21
 
22
22
  import {describe, it, expect} from 'vitest';
23
+ import {spawnSync} from 'node:child_process';
24
+ import {fileURLToPath} from 'node:url';
25
+ import {Command} from 'commander';
23
26
  import {runCli} from '../../../test-utils/run-cli.mjs';
27
+ import {installJsonShim} from './json-shim.mjs';
28
+ import {setJsonMode} from '../../../foundation/response/json.mjs';
24
29
 
25
30
  function parseJson(stdout) {
26
31
  return JSON.parse(stdout);
@@ -207,3 +212,81 @@ describe('--json shim: stdout discipline under --json', () => {
207
212
  }
208
213
  });
209
214
  });
215
+
216
+ describe('--json shim: help shown for a failed invocation is an error envelope', () => {
217
+ it('astryx help bogus --json emits ERR_UNKNOWN_COMMAND, exit 1', async () => {
218
+ const {status, stdout, stderr} = await runCli(['help', 'bogus', '--json']);
219
+ expect(status).toBe(1);
220
+ expect(stderr).toBe('');
221
+ const parsed = parseJson(stdout);
222
+ expect(parsed).not.toHaveProperty('type');
223
+ expect(parsed.apiVersion).toBe(1);
224
+ expect(parsed.code).toBe('ERR_UNKNOWN_COMMAND');
225
+ expect(parsed.error).toMatch(/bogus/);
226
+ });
227
+
228
+ it('astryx layout --json (group, no subcommand) emits ERR_MISSING_ARGUMENT, exit 1', async () => {
229
+ const {status, stdout, stderr} = await runCli(['layout', '--json']);
230
+ expect(status).toBe(1);
231
+ expect(stderr).toBe('');
232
+ const parsed = parseJson(stdout);
233
+ expect(parsed).not.toHaveProperty('type');
234
+ expect(parsed.code).toBe('ERR_MISSING_ARGUMENT');
235
+ expect(parsed.suggestions.map((s) => s.name)).toContain('layout expand');
236
+ });
237
+
238
+ it('astryx layout (no --json) still prints help to stderr, exit 1', async () => {
239
+ const {status, stdout, stderr} = await runCli(['layout']);
240
+ expect(status).toBe(1);
241
+ expect(stdout).toBe('');
242
+ expect(stderr).toMatch(/Usage: astryx layout/);
243
+ });
244
+
245
+ it('the real binary emits the same envelope for help bogus --json', () => {
246
+ // One program per process, so the root's own outputHelp patch is used here.
247
+ const bin = fileURLToPath(new URL('../bin/astryx.mjs', import.meta.url));
248
+ const res = spawnSync(process.execPath, [bin, 'help', 'bogus', '--json'], {
249
+ encoding: 'utf-8',
250
+ timeout: 20_000,
251
+ });
252
+ expect(res.status).toBe(1);
253
+ expect(res.stderr).toBe('');
254
+ const parsed = parseJson(res.stdout);
255
+ expect(parsed).not.toHaveProperty('type');
256
+ expect(parsed.code).toBe('ERR_UNKNOWN_COMMAND');
257
+ });
258
+
259
+ it('a group added after install gets the same error envelope', () => {
260
+ const program = new Command('astryx');
261
+ installJsonShim(program);
262
+ // Added later, so only the prototype-level patch covers its outputHelp.
263
+ const late = new Command('late');
264
+ late.command('child');
265
+ program.addCommand(late);
266
+ /** @type {string[]} */
267
+ const writes = [];
268
+ const origWrite = process.stdout.write;
269
+ // @ts-expect-error - test-only capture
270
+ process.stdout.write = (chunk) => writes.push(String(chunk)) > 0;
271
+ setJsonMode(true);
272
+ delete process.__xdsJsonHandled;
273
+ try {
274
+ late.outputHelp({error: true});
275
+ } finally {
276
+ process.stdout.write = origWrite;
277
+ setJsonMode(false);
278
+ delete process.__xdsJsonHandled;
279
+ }
280
+ const parsed = parseJson(writes.join(''));
281
+ expect(parsed.code).toBe('ERR_MISSING_ARGUMENT');
282
+ expect(parsed.suggestions).toEqual([{name: 'late child', reason: 'available subcommand'}]);
283
+ });
284
+
285
+ it('astryx help layout --json still emits the help envelope, exit 0', async () => {
286
+ const {status, stdout} = await runCli(['help', 'layout', '--json']);
287
+ expect(status).toBe(0);
288
+ const parsed = parseJson(stdout);
289
+ expect(parsed.type).toBe('help');
290
+ expect(parsed.data.command).toBe('astryx layout');
291
+ });
292
+ });
@@ -48,6 +48,8 @@ export interface ManifestCommand {
48
48
  responseTypes?: string[];
49
49
  /** Example invocations. */
50
50
  examples?: string[];
51
+ /** Documented exit codes: the process exit status and when it occurs. */
52
+ exitCodes?: {code: number; when: string}[];
51
53
  /** Nested subcommands (e.g. `theme build` under `theme`). */
52
54
  subcommands?: ManifestCommand[];
53
55
  }
@@ -35,6 +35,8 @@
35
35
  */
36
36
 
37
37
  import {API_VERSION} from '../../../foundation/response/json.mjs';
38
+ import {commandDocsOf} from './define-command.mjs';
39
+ import {doc as manifestDoc} from '../commands/manifest.doc.mjs';
38
40
 
39
41
  /**
40
42
  * Response `type` discriminators each fully-qualified command can emit in
@@ -55,7 +57,13 @@ export const RESPONSE_TYPES = {
55
57
  'component.detail.showcase',
56
58
  'component.detail.blocks',
57
59
  ],
58
- docs: ['docs.list', 'docs.detail', 'docs.detail.section'],
60
+ docs: [
61
+ 'docs.list',
62
+ 'docs.index',
63
+ 'docs.detail',
64
+ 'docs.detail.section',
65
+ 'docs.node',
66
+ ],
59
67
  blog: ['blog.list', 'blog.detail'],
60
68
  discover: [
61
69
  'discover.list',
@@ -105,7 +113,13 @@ const EXAMPLES = {
105
113
  'astryx component XDSButton',
106
114
  'astryx component XDSButton --props --json',
107
115
  ],
108
- docs: ['astryx docs', 'astryx docs spacing --json'],
116
+ docs: [
117
+ 'astryx docs',
118
+ 'astryx docs spacing --json',
119
+ 'astryx docs theme',
120
+ 'astryx docs theme quick-start',
121
+ 'astryx docs cli/integrations --full',
122
+ ],
109
123
  discover: ['astryx discover --json'],
110
124
  search: [
111
125
  'astryx search modal --json',
@@ -232,6 +246,18 @@ function fullName(cmd, root) {
232
246
  return parts.join(' ');
233
247
  }
234
248
 
249
+ /**
250
+ * The docs a command was built from. `manifest` is registered by hand in
251
+ * index.mjs, so its CommandDoc is read here.
252
+ * @param {import('commander').Command} cmd
253
+ * @param {string} name
254
+ */
255
+ function docsOf(cmd, name) {
256
+ return (
257
+ commandDocsOf(cmd) ?? (name === 'manifest' ? {doc: manifestDoc} : undefined)
258
+ );
259
+ }
260
+
235
261
  /**
236
262
  * Recursively describe a Commander command and its subcommands.
237
263
  *
@@ -247,6 +273,8 @@ function describeCommand(cmd, root, jsonSupported) {
247
273
  // `_hidden` is a Commander internal not present on its public types.
248
274
  if (!name || /** @type {any} */ (cmd)._hidden || name === 'help') return null;
249
275
 
276
+ const docs = docsOf(cmd, name);
277
+
250
278
  const subcommands = /** @type {object[]} */ (
251
279
  (cmd.commands || [])
252
280
  .map(sub => describeCommand(sub, root, jsonSupported))
@@ -279,6 +307,12 @@ function describeCommand(cmd, root, jsonSupported) {
279
307
 
280
308
  if (EXAMPLES[name]) entry.examples = [...EXAMPLES[name]];
281
309
 
310
+ const exitCodes = (docs?.doc.exitCodes ?? []).map(({code, when}) => ({
311
+ code,
312
+ when,
313
+ }));
314
+ if (exitCodes.length > 0) entry.exitCodes = exitCodes;
315
+
282
316
  // Sort subcommands by name for a stable, agent-facing contract — the same
283
317
  // guarantee the top-level command list makes. Otherwise Commander
284
318
  // registration order leaks into the manifest and a pure reorder of
@@ -196,3 +196,20 @@ describe('manifest: e2e', () => {
196
196
  .toContain('component.list');
197
197
  });
198
198
  });
199
+
200
+ describe('manifest: text projection', () => {
201
+ it('astryx manifest (text) uses the JSON entry keys as its field names', async () => {
202
+ const text = await runCli(['manifest']);
203
+ expect(text.status).toBe(0);
204
+ const json = JSON.parse((await runCli(['manifest', '--json'])).stdout);
205
+ const jsonKeys = new Set(json.data.commands.flatMap((c) => Object.keys(c)));
206
+ const textKeys = new Set(
207
+ text.stdout
208
+ .split('\n')
209
+ .map((line) => /^([A-Za-z]+):\s/.exec(line)?.[1])
210
+ .filter(Boolean),
211
+ );
212
+ expect(textKeys).toContain('name');
213
+ for (const key of textKeys) expect(jsonKeys).toContain(key);
214
+ });
215
+ });
@@ -182,6 +182,8 @@ export function removeXdsBlock(filePath: string, { deleteIfEmpty }?: {
182
182
  /**
183
183
  * Remove Astryx section from all known agent doc files.
184
184
  * @param {string} targetDir
185
+ * @throws {PathSafetyError} `ERR_PATH_TRAVERSAL` when a file it would change
186
+ * resolves outside `targetDir`; nothing is changed.
185
187
  */
186
188
  export function removeAgentDocs(targetDir: string): void;
187
189
  /**
@@ -208,6 +210,8 @@ export function removeAgentDocs(targetDir: string): void;
208
210
  * @param {string} [options.renderedBlock] - Fully rendered expected block. Init
209
211
  * and upgrade pass one shared block to every target.
210
212
  * @returns {string[]} List of files written
213
+ * @throws {import('../fs/path-safety.mjs').PathSafetyError} when a file it would
214
+ * write resolves outside `targetDir`; nothing is written.
211
215
  */
212
216
  export function installAgentDocs(targetDir: string, { zh, lang, agent, paths, onlyReplace, topics, renderedBlock, }?: {
213
217
  zh?: boolean | undefined;
@@ -22,11 +22,12 @@
22
22
  import * as fs from 'node:fs';
23
23
  import * as path from 'node:path';
24
24
  import {findCoreDir, CLI_ROOT} from '../fs/paths.mjs';
25
- import {assertWithin} from '../fs/path-safety.mjs';
25
+ import {assertWithin, PathSafetyError} from '../fs/path-safety.mjs';
26
26
  import {getCliInvocation} from '../env/package-manager.mjs';
27
27
  import {discoverComponents} from '../discovery/component-discovery.mjs';
28
28
  import {Project} from '../config/project.mjs';
29
29
  import {humanLog} from '../response/json.mjs';
30
+ import {ERROR_CODES} from '../response/error-codes.mjs';
30
31
  import {
31
32
  AGENTS_MD,
32
33
  CLAUDE_MD,
@@ -485,6 +486,7 @@ export function generateCompressedIndex(
485
486
  if (resolvedTopics.length > 0) {
486
487
  lines.push(` docs <topic> ${resolvedTopics.join(', ')}`);
487
488
  }
489
+ lines.push(' docs cli commands, API reference, integration authoring (one level at a time)');
488
490
  lines.push(' swizzle <Name> eject component source for deep customization');
489
491
  lines.push(' upgrade --apply run after any Astryx or integration dependency bump');
490
492
  const appendCount = agentDocs.reduce(
@@ -705,12 +707,65 @@ export function removeXdsBlock(filePath, {deleteIfEmpty = false} = {}) {
705
707
  return true;
706
708
  }
707
709
 
710
+ /**
711
+ * Whether an agent-doc file already carries a managed-block marker.
712
+ * @param {string} filePath
713
+ * @returns {boolean}
714
+ */
715
+ function hasManagedMarker(filePath) {
716
+ try {
717
+ const content = fs.readFileSync(filePath, 'utf-8');
718
+ return content.includes(MARKER_START) || content.includes(LEGACY_MARKER_START);
719
+ } catch {
720
+ return false;
721
+ }
722
+ }
723
+
724
+ /**
725
+ * Whether `removeXdsBlock` would change `filePath`: it holds one well-formed
726
+ * managed block.
727
+ * @param {string} filePath
728
+ * @returns {boolean}
729
+ */
730
+ function hasRemovableBlock(filePath) {
731
+ try {
732
+ return findManagedBlock(fs.readFileSync(filePath, 'utf-8')) != null;
733
+ } catch {
734
+ return false;
735
+ }
736
+ }
737
+
738
+ /**
739
+ * Every file a run writes must resolve inside `targetDir`, symlinks
740
+ * included. Checked for the whole write set before the first write, so an
741
+ * escape writes nothing.
742
+ * @param {string} targetDir
743
+ * @param {Iterable<string>} relPaths
744
+ */
745
+ function assertTargetsWithin(targetDir, relPaths) {
746
+ for (const p of relPaths) {
747
+ assertWithin(p, targetDir, {label: 'agent docs path'});
748
+ }
749
+ }
750
+
708
751
  /**
709
752
  * Remove Astryx section from all known agent doc files.
710
753
  * @param {string} targetDir
754
+ * @throws {PathSafetyError} `ERR_PATH_TRAVERSAL` when a file it would change
755
+ * resolves outside `targetDir`; nothing is changed.
711
756
  */
712
757
  export function removeAgentDocs(targetDir) {
713
758
  const allPaths = discoverAgentDocs(targetDir);
759
+ try {
760
+ assertTargetsWithin(
761
+ targetDir,
762
+ allPaths.filter(p => hasRemovableBlock(path.join(targetDir, p))),
763
+ );
764
+ } catch (err) {
765
+ // The code reaches the error envelope as is, so it must be registered.
766
+ if (!(err instanceof PathSafetyError)) throw err;
767
+ throw new PathSafetyError(err.message, ERROR_CODES.ERR_PATH_TRAVERSAL);
768
+ }
714
769
 
715
770
  for (const p of allPaths) {
716
771
  const filePath = path.join(targetDir, p);
@@ -718,9 +773,9 @@ export function removeAgentDocs(targetDir) {
718
773
  const deleteIfEmpty = p === AGENTS_MD || p === CLAUDE_DIR_MD;
719
774
  if (removeXdsBlock(filePath, {deleteIfEmpty})) {
720
775
  if (!fs.existsSync(filePath)) {
721
- humanLog(`✓ Removed empty ${p}`);
776
+ humanLog(`[ok] Removed empty ${p}`);
722
777
  } else {
723
- humanLog(`✓ Removed design system section from ${p}`);
778
+ humanLog(`[ok] Removed design system section from ${p}`);
724
779
  }
725
780
  }
726
781
  }
@@ -750,6 +805,8 @@ export function removeAgentDocs(targetDir) {
750
805
  * @param {string} [options.renderedBlock] - Fully rendered expected block. Init
751
806
  * and upgrade pass one shared block to every target.
752
807
  * @returns {string[]} List of files written
808
+ * @throws {import('../fs/path-safety.mjs').PathSafetyError} when a file it would
809
+ * write resolves outside `targetDir`; nothing is written.
753
810
  */
754
811
  export function installAgentDocs(
755
812
  targetDir,
@@ -806,6 +863,7 @@ export function installAgentDocs(
806
863
  // Agent preset
807
864
  if (agent) {
808
865
  const {inject, create} = resolveAgentPaths(targetDir, agent);
866
+ assertTargetsWithin(targetDir, [...inject, ...create]);
809
867
  for (const p of inject) {
810
868
  injectXdsBlock(path.join(targetDir, p), compressedIndex);
811
869
  written.push(p);
@@ -833,6 +891,14 @@ export function installAgentDocs(
833
891
  if (existing.length > 0) {
834
892
  const wrappers = discoverAgentDocWrappers(targetDir, existing);
835
893
  const targets = existing.filter(p => !wrappers.has(p));
894
+ // A refresh skips unmarked files and a wrapper is only written when it
895
+ // carries a block, so only the files this run writes are checked.
896
+ /** @param {string} p */
897
+ const marked = p => hasManagedMarker(path.join(targetDir, p));
898
+ assertTargetsWithin(targetDir, [
899
+ ...targets.filter(p => !onlyReplace || marked(p)),
900
+ ...[...wrappers].filter(marked),
901
+ ]);
836
902
 
837
903
  for (const p of targets) {
838
904
  const didWrite = injectXdsBlock(path.join(targetDir, p), compressedIndex, {onlyReplace});
@@ -860,6 +926,7 @@ export function installAgentDocs(
860
926
  if (onlyReplace) return written;
861
927
 
862
928
  const defaultPath = AGENTS_MD;
929
+ assertTargetsWithin(targetDir, [defaultPath]);
863
930
  injectXdsBlock(path.join(targetDir, defaultPath), compressedIndex, {
864
931
  createIfMissing: true,
865
932
  header: `# AGENTS.md\n\nProject-specific guidance for AI coding agents.`,
@@ -1,22 +1,31 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
2
 
3
3
  /**
4
- * @file Path-safety regression tests for `--agent-docs-path`.
4
+ * @file Path-safety regression tests for agent-doc writes.
5
5
  *
6
6
  * `installAgentDocs(targetDir, {paths})` previously joined each path
7
7
  * onto `targetDir` with no validation, so:
8
8
  * - `../OUT.md` wrote outside `targetDir`
9
9
  * - `/tmp/x.md` was silently re-rooted under `targetDir`
10
10
  *
11
- * Both cases now throw PathSafetyError before any file is touched.
11
+ * Auto-detected and preset files were never checked at all, so a `CLAUDE.md`
12
+ * or `.claude/` symlinked out of the project had the block written through
13
+ * it. Every case now throws PathSafetyError before any file is touched.
14
+ *
15
+ * `removeAgentDocs` (`init --remove-agents`) stripped or deleted a block
16
+ * through the same kind of link and reported success. It now refuses with
17
+ * `ERR_PATH_TRAVERSAL` before any file is touched.
12
18
  */
13
19
 
14
- import {describe, it, expect, beforeEach, afterEach} from 'vitest';
20
+ import {describe, it, expect, beforeEach, afterEach, vi} from 'vitest';
15
21
  import * as fs from 'node:fs';
16
22
  import * as path from 'node:path';
17
23
  import * as os from 'node:os';
18
- import {installAgentDocs} from './agent-docs.mjs';
24
+ import {installAgentDocs, removeAgentDocs} from './agent-docs.mjs';
19
25
  import {PathSafetyError} from '../fs/path-safety.mjs';
26
+ import {runCli} from '../../test-utils/run-cli.mjs';
27
+
28
+ const BLOCK = '<!-- ASTRYX:START -->\nmanaged\n<!-- ASTRYX:END -->';
20
29
 
21
30
  let tmpDir;
22
31
  beforeEach(() => {
@@ -26,6 +35,28 @@ afterEach(() => {
26
35
  fs.rmSync(tmpDir, {recursive: true, force: true});
27
36
  });
28
37
 
38
+ /** A project dir plus a sibling dir outside it. */
39
+ function layout() {
40
+ const project = path.join(tmpDir, 'project');
41
+ const outside = path.join(tmpDir, 'outside');
42
+ fs.mkdirSync(project, {recursive: true});
43
+ fs.mkdirSync(outside, {recursive: true});
44
+ return {project, outside};
45
+ }
46
+
47
+ /**
48
+ * @param {() => unknown} fn
49
+ * @returns {unknown} what `fn` threw
50
+ */
51
+ function thrownBy(fn) {
52
+ try {
53
+ fn();
54
+ } catch (err) {
55
+ return err;
56
+ }
57
+ throw new Error('expected a throw');
58
+ }
59
+
29
60
  describe('installAgentDocs path safety', () => {
30
61
  it('rejects --agent-docs-path that escapes the target directory', () => {
31
62
  const target = path.join(tmpDir, 'project');
@@ -64,3 +95,234 @@ describe('installAgentDocs path safety', () => {
64
95
  expect(fs.existsSync(path.join(tmpDir, 'escaped.md'))).toBe(false);
65
96
  });
66
97
  });
98
+
99
+ describe('installAgentDocs never writes through a symlink out of the project', () => {
100
+ it('refuses an auto-detected file that links outside', () => {
101
+ const {project, outside} = layout();
102
+ const shared = path.join(outside, 'CLAUDE.md');
103
+ fs.writeFileSync(shared, '# Shared\n');
104
+ fs.symlinkSync(shared, path.join(project, 'CLAUDE.md'));
105
+
106
+ expect(() => installAgentDocs(project, {renderedBlock: BLOCK})).toThrow(
107
+ /outside the project root/,
108
+ );
109
+ expect(fs.readFileSync(shared, 'utf-8')).toBe('# Shared\n');
110
+ });
111
+
112
+ it('refuses a preset file reached through a symlinked directory', () => {
113
+ const {project, outside} = layout();
114
+ fs.symlinkSync(outside, path.join(project, '.claude'));
115
+
116
+ expect(() =>
117
+ installAgentDocs(project, {agent: 'claude', renderedBlock: BLOCK}),
118
+ ).toThrow(PathSafetyError);
119
+ expect(fs.readdirSync(outside)).toEqual([]);
120
+ });
121
+
122
+ it('refuses an existing preset file that links outside', () => {
123
+ const {project, outside} = layout();
124
+ const shared = path.join(outside, 'rules');
125
+ fs.writeFileSync(shared, 'rules\n');
126
+ fs.symlinkSync(shared, path.join(project, '.cursorrules'));
127
+
128
+ expect(() =>
129
+ installAgentDocs(project, {agent: 'cursor', renderedBlock: BLOCK}),
130
+ ).toThrow(PathSafetyError);
131
+ expect(fs.readFileSync(shared, 'utf-8')).toBe('rules\n');
132
+ });
133
+
134
+ it('writes nothing when any file of the set escapes', () => {
135
+ const {project, outside} = layout();
136
+ fs.writeFileSync(path.join(project, 'AGENTS.md'), '# Agents\n');
137
+ const shared = path.join(outside, 'CLAUDE.md');
138
+ fs.writeFileSync(shared, '# Shared\n');
139
+ fs.symlinkSync(shared, path.join(project, 'CLAUDE.md'));
140
+
141
+ expect(() => installAgentDocs(project, {renderedBlock: BLOCK})).toThrow(
142
+ PathSafetyError,
143
+ );
144
+ expect(fs.readFileSync(path.join(project, 'AGENTS.md'), 'utf-8')).toBe(
145
+ '# Agents\n',
146
+ );
147
+ expect(fs.readFileSync(shared, 'utf-8')).toBe('# Shared\n');
148
+ });
149
+
150
+ it('refuses to refresh a managed block that lives outside', () => {
151
+ const {project, outside} = layout();
152
+ const shared = path.join(outside, 'CLAUDE.md');
153
+ fs.writeFileSync(shared, `# Shared\n\n${BLOCK.replace('managed', 'old')}\n`);
154
+ const before = fs.readFileSync(shared, 'utf-8');
155
+ fs.symlinkSync(shared, path.join(project, 'CLAUDE.md'));
156
+
157
+ expect(() =>
158
+ installAgentDocs(project, {onlyReplace: true, renderedBlock: BLOCK}),
159
+ ).toThrow(PathSafetyError);
160
+ expect(fs.readFileSync(shared, 'utf-8')).toBe(before);
161
+ });
162
+
163
+ it('refuses to strip a duplicate block from a wrapper that links outside', () => {
164
+ const {project, outside} = layout();
165
+ fs.writeFileSync(path.join(project, 'AGENTS.md'), `# Agents\n\n${BLOCK}\n`);
166
+ const shared = path.join(outside, 'CLAUDE.md');
167
+ fs.writeFileSync(shared, `@AGENTS.md\n\n${BLOCK}\n`);
168
+ fs.symlinkSync(shared, path.join(project, 'CLAUDE.md'));
169
+
170
+ expect(() => installAgentDocs(project, {renderedBlock: BLOCK})).toThrow(
171
+ PathSafetyError,
172
+ );
173
+ expect(fs.readFileSync(shared, 'utf-8')).toBe(`@AGENTS.md\n\n${BLOCK}\n`);
174
+ });
175
+
176
+ it('still refreshes in-project blocks beside an unmarked outside link it skips', () => {
177
+ const {project, outside} = layout();
178
+ fs.writeFileSync(
179
+ path.join(project, 'AGENTS.md'),
180
+ `# Agents\n\n${BLOCK.replace('managed', 'old')}\n`,
181
+ );
182
+ const shared = path.join(outside, 'rules');
183
+ fs.writeFileSync(shared, 'rules\n');
184
+ fs.symlinkSync(shared, path.join(project, '.cursorrules'));
185
+
186
+ expect(
187
+ installAgentDocs(project, {onlyReplace: true, renderedBlock: BLOCK}),
188
+ ).toEqual(['AGENTS.md']);
189
+ expect(fs.readFileSync(path.join(project, 'AGENTS.md'), 'utf-8')).toContain(
190
+ 'managed',
191
+ );
192
+ expect(fs.readFileSync(shared, 'utf-8')).toBe('rules\n');
193
+ });
194
+
195
+ it('writes through a symlink that stays inside the project', () => {
196
+ const {project} = layout();
197
+ fs.writeFileSync(path.join(project, 'AGENTS.md'), '# Agents\n');
198
+ fs.symlinkSync('AGENTS.md', path.join(project, 'CLAUDE.md'));
199
+
200
+ expect(installAgentDocs(project, {renderedBlock: BLOCK})).toEqual([
201
+ 'AGENTS.md',
202
+ 'CLAUDE.md',
203
+ ]);
204
+ const content = fs.readFileSync(path.join(project, 'AGENTS.md'), 'utf-8');
205
+ expect(content.split('<!-- ASTRYX:START -->')).toHaveLength(2);
206
+ });
207
+ });
208
+
209
+ describe('init --features agents with an agent doc linked outside the project', () => {
210
+ it('writes nothing outside, reports path-safety, and exits 1 in both modes', async () => {
211
+ const project = path.join(tmpDir, 'project');
212
+ const outside = path.join(tmpDir, 'outside');
213
+ fs.mkdirSync(project, {recursive: true});
214
+ fs.mkdirSync(outside, {recursive: true});
215
+ fs.writeFileSync(path.join(project, 'package.json'), '{"name":"app"}\n');
216
+ const shared = path.join(outside, 'CLAUDE.md');
217
+ fs.writeFileSync(shared, '# Shared\n');
218
+ fs.symlinkSync(shared, path.join(project, 'CLAUDE.md'));
219
+
220
+ const json = await runCli(['init', '--features', 'agents', '--json'], project);
221
+ expect(json.status).toBe(1);
222
+ const env = JSON.parse(json.stdout);
223
+ expect(env.type).toBe('init.run');
224
+ expect(env.data.docsError).toMatchObject({kind: 'path-safety'});
225
+
226
+ const text = await runCli(['init', '--features', 'agents'], project);
227
+ expect(text.status).toBe(1);
228
+
229
+ expect(fs.readFileSync(shared, 'utf-8')).toBe('# Shared\n');
230
+ }, 30_000);
231
+ });
232
+
233
+ describe('removeAgentDocs never changes a file out of the project', () => {
234
+ beforeEach(() => {
235
+ vi.spyOn(console, 'log').mockImplementation(() => {});
236
+ });
237
+ afterEach(() => {
238
+ vi.restoreAllMocks();
239
+ });
240
+
241
+ it('refuses to strip a block from a file that links outside', () => {
242
+ const {project, outside} = layout();
243
+ const shared = path.join(outside, 'CLAUDE.md');
244
+ fs.writeFileSync(shared, `# Shared\n\n${BLOCK}\n`);
245
+ fs.symlinkSync(shared, path.join(project, 'CLAUDE.md'));
246
+
247
+ const err = thrownBy(() => removeAgentDocs(project));
248
+ expect(err).toBeInstanceOf(PathSafetyError);
249
+ expect(err).toMatchObject({code: 'ERR_PATH_TRAVERSAL'});
250
+ expect(fs.readFileSync(shared, 'utf-8')).toBe(`# Shared\n\n${BLOCK}\n`);
251
+ });
252
+
253
+ it('refuses to delete a file reached through a symlinked directory', () => {
254
+ const {project, outside} = layout();
255
+ const shared = path.join(outside, 'CLAUDE.md');
256
+ const content = `# CLAUDE\n\nProject-specific guidance for AI coding agents.\n\n${BLOCK}\n`;
257
+ fs.writeFileSync(shared, content);
258
+ fs.symlinkSync(outside, path.join(project, '.claude'));
259
+
260
+ expect(thrownBy(() => removeAgentDocs(project))).toMatchObject({
261
+ code: 'ERR_PATH_TRAVERSAL',
262
+ });
263
+ expect(fs.readFileSync(shared, 'utf-8')).toBe(content);
264
+ });
265
+
266
+ it('changes nothing when any file of the set escapes', () => {
267
+ const {project, outside} = layout();
268
+ const agents = `# Agents\n\nkeep\n\n${BLOCK}\n`;
269
+ fs.writeFileSync(path.join(project, 'AGENTS.md'), agents);
270
+ const shared = path.join(outside, 'CLAUDE.md');
271
+ fs.writeFileSync(shared, `# Shared\n\n${BLOCK}\n`);
272
+ fs.symlinkSync(shared, path.join(project, 'CLAUDE.md'));
273
+
274
+ expect(thrownBy(() => removeAgentDocs(project))).toBeInstanceOf(PathSafetyError);
275
+ expect(fs.readFileSync(path.join(project, 'AGENTS.md'), 'utf-8')).toBe(agents);
276
+ expect(fs.readFileSync(shared, 'utf-8')).toBe(`# Shared\n\n${BLOCK}\n`);
277
+ });
278
+
279
+ it('still strips in-project blocks beside an unmarked outside link it skips', () => {
280
+ const {project, outside} = layout();
281
+ fs.writeFileSync(path.join(project, 'AGENTS.md'), `# Agents\n\nkeep\n\n${BLOCK}\n`);
282
+ const shared = path.join(outside, 'rules');
283
+ fs.writeFileSync(shared, 'rules\n');
284
+ fs.symlinkSync(shared, path.join(project, '.cursorrules'));
285
+
286
+ removeAgentDocs(project);
287
+ expect(fs.readFileSync(path.join(project, 'AGENTS.md'), 'utf-8')).toBe(
288
+ '# Agents\n\nkeep\n',
289
+ );
290
+ expect(fs.readFileSync(shared, 'utf-8')).toBe('rules\n');
291
+ });
292
+
293
+ it('strips through a symlink that stays inside the project', () => {
294
+ const {project} = layout();
295
+ fs.writeFileSync(path.join(project, 'AGENTS.md'), `# Agents\n\nkeep\n\n${BLOCK}\n`);
296
+ fs.symlinkSync('AGENTS.md', path.join(project, 'CLAUDE.md'));
297
+
298
+ removeAgentDocs(project);
299
+ expect(fs.readFileSync(path.join(project, 'AGENTS.md'), 'utf-8')).toBe(
300
+ '# Agents\n\nkeep\n',
301
+ );
302
+ });
303
+ });
304
+
305
+ describe('init --remove-agents with an agent doc linked outside the project', () => {
306
+ it('changes nothing outside, fails with ERR_PATH_TRAVERSAL, and exits 1 in both modes', async () => {
307
+ const {project, outside} = layout();
308
+ fs.writeFileSync(path.join(project, 'package.json'), '{"name":"app"}\n');
309
+ const shared = path.join(outside, 'CLAUDE.md');
310
+ const content = `# Shared\n\n${BLOCK}\n`;
311
+ fs.writeFileSync(shared, content);
312
+ fs.symlinkSync(shared, path.join(project, 'CLAUDE.md'));
313
+
314
+ const json = await runCli(['init', '--remove-agents', '--json'], project);
315
+ expect(json.status).toBe(1);
316
+ const env = JSON.parse(json.stdout);
317
+ expect(env).toMatchObject({apiVersion: 1, code: 'ERR_PATH_TRAVERSAL'});
318
+ expect(env.error).toMatch(/"CLAUDE\.md": resolves outside the project root/);
319
+ expect(env).not.toHaveProperty('data');
320
+
321
+ const text = await runCli(['init', '--remove-agents'], project);
322
+ expect(text.status).toBe(1);
323
+ expect(text.stderr).toMatch(/^Error: .*"CLAUDE\.md": resolves outside the project root/m);
324
+ expect(text.stdout).not.toMatch(/removed/i);
325
+
326
+ expect(fs.readFileSync(shared, 'utf-8')).toBe(content);
327
+ }, 30_000);
328
+ });
@@ -134,7 +134,6 @@ describe('generateCompressedIndex', () => {
134
134
  const line = topicLine(generateCompressedIndex('1.0.0'));
135
135
  for (const topic of [
136
136
  'getting-started',
137
- 'cli-integrations',
138
137
  'browser-support',
139
138
  'styling-libraries',
140
139
  'working-with-ai',
@@ -143,6 +142,16 @@ describe('generateCompressedIndex', () => {
143
142
  }
144
143
  });
145
144
 
145
+ it('points to the CLI docs tree on a line of its own', () => {
146
+ // The integration guide lives in the docs tree now (cli/integrations), so
147
+ // the topic line no longer names it; the tree's entry point does.
148
+ const block = generateCompressedIndex('1.0.0');
149
+ expect(block).toContain(
150
+ ' docs cli commands, API reference, integration authoring (one level at a time)',
151
+ );
152
+ expect(topicLine(block)).not.toContain('cli-integrations');
153
+ });
154
+
146
155
  it('lists the topics it is given, so an integration’s reach the agent', () => {
147
156
  const line = topicLine(
148
157
  generateCompressedIndex('1.0.0', {topics: ['tokens', 'deploying']}),