@astryxdesign/cli 0.6.3 → 0.6.4-canary.06c8fa3

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 (670) hide show
  1. package/CHANGELOG.md +194 -0
  2. package/README.md +121 -85
  3. package/api/blog/blog.doc.mjs +1 -0
  4. package/api/build/_adapter.d.mts +50 -0
  5. package/api/build/_adapter.mjs +60 -0
  6. package/api/build/build.doc.mjs +16 -9
  7. package/api/build/build.test.mjs +197 -8
  8. package/api/build/build.type.d.mts +91 -2
  9. package/api/build/build.type.mjs +52 -8
  10. package/api/build/help/help.d.mts +12 -5
  11. package/api/build/help/help.mjs +69 -6
  12. package/api/build/kit/kit.d.mts +4 -1
  13. package/api/build/kit/kit.mjs +165 -49
  14. package/api/build/kit/rank.d.mts +44 -0
  15. package/api/build/kit/rank.mjs +432 -0
  16. package/api/build/kit/rank.test.mjs +196 -0
  17. package/api/component/_adapter.d.mts +31 -12
  18. package/api/component/_adapter.mjs +79 -15
  19. package/api/component/component.d.mts +6 -3
  20. package/api/component/component.doc.mjs +36 -13
  21. package/api/component/component.mjs +339 -22
  22. package/api/component/component.test.mjs +38 -0
  23. package/api/component/component.type.d.mts +47 -11
  24. package/api/component/component.type.mjs +76 -24
  25. package/api/component/detail/blocks/blocks.d.mts +2 -1
  26. package/api/component/detail/blocks/blocks.mjs +4 -3
  27. package/api/component/list/list.d.mts +0 -5
  28. package/api/component/list/list.mjs +40 -11
  29. package/api/discover/_adapter.d.mts +114 -6
  30. package/api/discover/_adapter.mjs +372 -17
  31. package/api/discover/_adapter.test.mjs +215 -0
  32. package/api/discover/_catalog-view.d.mts +115 -0
  33. package/api/discover/_catalog-view.mjs +203 -0
  34. package/api/discover/_catalog-view.test.mjs +128 -0
  35. package/api/discover/detail/detail.d.mts +18 -6
  36. package/api/discover/detail/detail.mjs +67 -13
  37. package/api/discover/detail/detail.test.mjs +85 -0
  38. package/api/discover/detail/item/item.d.mts +26 -0
  39. package/api/discover/detail/item/item.mjs +78 -0
  40. package/api/discover/detail/item/item.test.mjs +73 -0
  41. package/api/discover/discover.d.mts +3 -9
  42. package/api/discover/discover.doc.mjs +62 -18
  43. package/api/discover/discover.mjs +220 -36
  44. package/api/discover/discover.test.mjs +11 -2
  45. package/api/discover/discover.type.d.mts +150 -11
  46. package/api/discover/discover.type.mjs +107 -17
  47. package/api/discover/list/list.d.mts +20 -6
  48. package/api/discover/list/list.mjs +45 -12
  49. package/api/discover/list/list.test.mjs +46 -0
  50. package/api/discover/search/search.d.mts +18 -16
  51. package/api/discover/search/search.mjs +102 -56
  52. package/api/discover/search/search.test.mjs +144 -10
  53. package/api/docs/_adapter.d.mts +272 -41
  54. package/api/docs/_adapter.mjs +985 -108
  55. package/api/docs/compiled-topics.test.mjs +78 -0
  56. package/api/docs/detail/detail.mjs +22 -63
  57. package/api/docs/detail/section/section.d.mts +1 -1
  58. package/api/docs/detail/section/section.mjs +54 -19
  59. package/api/docs/detail/section/section.test.mjs +50 -0
  60. package/api/docs/docs.d.mts +10 -3
  61. package/api/docs/docs.doc.mjs +55 -16
  62. package/api/docs/docs.mjs +53 -10
  63. package/api/docs/docs.test.mjs +166 -4
  64. package/api/docs/docs.type.d.mts +221 -5
  65. package/api/docs/docs.type.mjs +153 -11
  66. package/api/docs/index/index.d.mts +18 -0
  67. package/api/docs/index/index.mjs +40 -0
  68. package/api/docs/index/index.test.mjs +62 -0
  69. package/api/docs/integration-tree.test.mjs +555 -0
  70. package/api/docs/integrationDocs.test.mjs +114 -8
  71. package/api/docs/list/list.mjs +28 -12
  72. package/api/docs/node/node.d.mts +43 -0
  73. package/api/docs/node/node.mjs +192 -0
  74. package/api/docs/reference-blocks.test.mjs +406 -0
  75. package/api/doctor/doctor.d.mts +104 -1
  76. package/api/doctor/doctor.doc.mjs +1 -0
  77. package/api/doctor/doctor.mjs +635 -7
  78. package/api/doctor/doctor.test.mjs +732 -11
  79. package/api/gap-report/gap-report.doc.mjs +8 -4
  80. package/api/hook/_adapter.mjs +19 -5
  81. package/api/hook/hook.doc.mjs +1 -0
  82. package/api/hook/hook.type.d.mts +3 -3
  83. package/api/hook/hook.type.mjs +11 -11
  84. package/api/hook/list/list.d.mts +2 -2
  85. package/api/hook/list/list.mjs +69 -17
  86. package/api/index.d.mts +2 -3
  87. package/api/index.mjs +5 -4
  88. package/api/init/init.doc.mjs +6 -1
  89. package/api/init/init.test.mjs +41 -1
  90. package/api/init/remove/remove.mjs +1 -1
  91. package/api/init/run/run.mjs +20 -10
  92. package/api/integration/add-contribution.component-names.test.mjs +120 -0
  93. package/api/integration/add-contribution.d.mts +2 -1
  94. package/api/integration/add-contribution.mjs +130 -15
  95. package/api/integration/add-contribution.test.mjs +258 -7
  96. package/api/integration/add-helpers.d.mts +5 -2
  97. package/api/integration/add-helpers.mjs +36 -9
  98. package/api/integration/add-theme.mjs +34 -64
  99. package/api/integration/add-theme.test.mjs +105 -21
  100. package/api/integration/authoring-checks.mjs +138 -28
  101. package/api/integration/authoring-checks.test.mjs +179 -7
  102. package/api/integration/authoring-checks.type.mjs +6 -1
  103. package/api/integration/integration-authoring.type.d.mts +3 -1
  104. package/api/integration/integration-authoring.type.mjs +2 -0
  105. package/api/integration/integration-block-exports.test.mjs +10 -6
  106. package/api/integration/integrationAdd.doc.mjs +14 -4
  107. package/api/integration/integrationAddAgentDoc.doc.mjs +1 -0
  108. package/api/integration/integrationAddCodemod.doc.mjs +1 -0
  109. package/api/integration/integrationAddComponent.doc.mjs +2 -1
  110. package/api/integration/integrationAddDoc.doc.mjs +8 -1
  111. package/api/integration/integrationAddTemplate.doc.mjs +1 -0
  112. package/api/integration/integrationAddTheme.doc.mjs +6 -5
  113. package/api/integration/integrationComponentConflicts.doc.mjs +1 -0
  114. package/api/integration/integrationDocConflicts.doc.mjs +2 -1
  115. package/api/integration/integrationPackCheck.doc.mjs +2 -1
  116. package/api/integration/integrationTemplateConflicts.doc.d.mts +3 -0
  117. package/api/integration/integrationTemplateConflicts.doc.mjs +4 -0
  118. package/api/integration/pack-check.lifecycle-output.test.mjs +105 -0
  119. package/api/integration/pack-check.mjs +111 -10
  120. package/api/integration/pack-check.test.mjs +387 -47
  121. package/api/integration/pack-check.type.d.mts +26 -2
  122. package/api/integration/pack-check.type.mjs +14 -1
  123. package/api/integration/summarizeIssues.doc.mjs +1 -0
  124. package/api/integration/template-conflict-compatibility.test.mjs +73 -0
  125. package/api/integration/validate-integration-fixes.test.mjs +1389 -0
  126. package/api/integration/validate-integration.mjs +52 -102
  127. package/api/integration/validate-integration.test.mjs +179 -26
  128. package/api/integration/validate-unread-theme-folders.test.mjs +110 -0
  129. package/api/integration/validateIntegration.doc.mjs +3 -2
  130. package/api/json/assertResponse.doc.mjs +1 -0
  131. package/api/json/envelope-types.test.mjs +76 -0
  132. package/api/json/index.ts +2 -1
  133. package/api/json/isError.doc.mjs +1 -0
  134. package/api/json/parseResponse.doc.mjs +3 -2
  135. package/api/search/search-return-type.test.mjs +54 -0
  136. package/api/search/search.d.mts +62 -11
  137. package/api/search/search.doc.mjs +8 -2
  138. package/api/search/search.mjs +471 -83
  139. package/api/search/search.test.mjs +142 -1
  140. package/api/search/search.type.d.mts +15 -3
  141. package/api/search/search.type.mjs +5 -2
  142. package/api/swizzle/copy/copy.mjs +28 -11
  143. package/api/swizzle/swizzle.doc.mjs +2 -1
  144. package/api/swizzle/swizzle.type.d.mts +2 -2
  145. package/api/swizzle/swizzle.type.mjs +2 -2
  146. package/api/template/copy/copy.mjs +17 -23
  147. package/api/template/copy/copy.test.mjs +17 -0
  148. package/api/template/list/list.mjs +1 -0
  149. package/api/template/table-floating-bulk-actions.test.mjs +66 -0
  150. package/api/template/template-integration.test.mjs +1008 -3
  151. package/api/template/template-suffix.test.mjs +41 -21
  152. package/api/template/template.d.mts +1 -1
  153. package/api/template/template.doc.mjs +30 -8
  154. package/api/template/template.mjs +46 -9
  155. package/api/template/template.type.d.mts +12 -14
  156. package/api/template/template.type.mjs +15 -14
  157. package/api/theme/_adapter.d.mts +2 -3
  158. package/api/theme/_adapter.mjs +4 -5
  159. package/api/theme/add/add.binary.test.mjs +84 -0
  160. package/api/theme/add/add.mjs +31 -22
  161. package/api/theme/add/add.rollback.test.mjs +158 -0
  162. package/api/theme/add/add.staging.test.mjs +83 -0
  163. package/api/theme/add/add.test.mjs +14 -1
  164. package/api/theme/build/build.family.test.mjs +7 -12
  165. package/api/theme/build/build.mjs +140 -59
  166. package/api/theme/build/build.public-component-vars.test.mjs +1 -1
  167. package/api/theme/build/build.receipt-doc.test.mjs +111 -0
  168. package/api/theme/build/build.rollback.test.mjs +148 -0
  169. package/api/theme/build/build.test.mjs +127 -0
  170. package/api/theme/build/font-warning.mjs +3 -3
  171. package/api/theme/build/font-warning.test.mjs +5 -2
  172. package/api/theme/generateTonalPalette.doc.mjs +1 -0
  173. package/api/theme/integration-themes.test.mjs +39 -28
  174. package/api/theme/list/list.test.mjs +19 -20
  175. package/api/theme/listThemes.doc.mjs +6 -5
  176. package/api/theme/palette/generate/generate.mjs +8 -3
  177. package/api/theme/palette/generate/generate.test.mjs +96 -0
  178. package/api/theme/palette/generate/generator.d.mts +10 -13
  179. package/api/theme/palette/generate/generator.mjs +15 -4
  180. package/api/theme/palette/generate/generator.test.mjs +10 -0
  181. package/api/theme/template/template.mjs +11 -2
  182. package/api/theme/template/template.test.mjs +20 -0
  183. package/api/theme/theme.type.d.mts +170 -11
  184. package/api/theme/theme.type.mjs +94 -27
  185. package/api/theme/themeAdd.doc.mjs +4 -3
  186. package/api/theme/themeBuild.doc.mjs +8 -4
  187. package/api/theme/themeList.doc.mjs +6 -3
  188. package/api/theme/themeListAvailable.doc.mjs +5 -3
  189. package/api/theme/themePaletteGenerate.doc.mjs +1 -0
  190. package/api/theme/themeTargets.doc.mjs +1 -0
  191. package/api/theme/themeTemplate.doc.mjs +6 -2
  192. package/api/upgrade/_adapter.d.mts +32 -5
  193. package/api/upgrade/_adapter.mjs +139 -22
  194. package/api/upgrade/list/list.mjs +2 -1
  195. package/api/upgrade/list/list.test.mjs +73 -0
  196. package/api/upgrade/project-context.test.mjs +272 -0
  197. package/api/upgrade/provider-agreement.test.mjs +152 -0
  198. package/api/upgrade/run/files-changed.test.mjs +111 -0
  199. package/api/upgrade/run/run.mjs +359 -60
  200. package/api/upgrade/status/status.mjs +2 -2
  201. package/api/upgrade/upgrade.doc.mjs +12 -5
  202. package/api/upgrade/upgrade.type.d.mts +43 -5
  203. package/api/upgrade/upgrade.type.mjs +29 -13
  204. package/assets/codemods/__tests__/registry.test.mjs +1 -0
  205. package/assets/codemods/__tests__/runner.test.mjs +332 -8
  206. package/assets/codemods/file-count.test.mjs +163 -0
  207. package/assets/codemods/integration-discovery.mjs +48 -4
  208. package/assets/codemods/integration-discovery.test.mjs +73 -0
  209. package/assets/codemods/integration-runner.mjs +59 -7
  210. package/assets/codemods/integration-runner.protection.test.mjs +153 -0
  211. package/assets/codemods/registry.mjs +1 -0
  212. package/assets/codemods/run-codemod.mjs +177 -34
  213. package/assets/codemods/runner.mjs +353 -104
  214. package/assets/codemods/term-log.mjs +32 -8
  215. package/assets/codemods/term-log.test.mjs +19 -1
  216. package/assets/codemods/transform-prop.mjs +109 -0
  217. package/assets/codemods/transform-prop.test.mjs +95 -0
  218. package/assets/codemods/transforms/v0.0.14/__tests__/rename-status-variants.test.mjs +86 -165
  219. package/assets/codemods/transforms/v0.0.14/rename-status-variants.mjs +72 -210
  220. package/assets/codemods/transforms/v0.1.8/__tests__/rename-avatar-size-scale.test.mjs +83 -115
  221. package/assets/codemods/transforms/v0.1.8/rename-avatar-size-scale.mjs +57 -186
  222. package/assets/codemods/transforms/v0.3.0/__tests__/unwrap-authoring-factories.test.mjs +27 -5
  223. package/assets/codemods/transforms/v0.3.0/unwrap-authoring-factories.mjs +20 -5
  224. package/assets/codemods/transforms/v0.6.0/__tests__/next-codemods.test.mjs +47 -0
  225. package/assets/codemods/transforms/v0.6.0/rename-resizable-pixel-bounds.mjs +57 -10
  226. package/assets/codemods/transforms/v0.6.4/__tests__/migrate-native-picker-to-presentation.test.mjs +63 -0
  227. package/assets/codemods/transforms/v0.6.4/__tests__/migrate-theme-catalog-to-descriptors.test.mjs +220 -0
  228. package/assets/codemods/transforms/v0.6.4/index.mjs +31 -0
  229. package/assets/codemods/transforms/v0.6.4/migrate-native-picker-to-presentation.mjs +148 -0
  230. package/assets/codemods/transforms/v0.6.4/migrate-theme-catalog-to-descriptors.mjs +141 -0
  231. package/assets/docs/README.md +9 -0
  232. package/assets/docs/authoring.doc.mjs +14 -0
  233. package/assets/docs/getting-started.doc.mjs +2 -2
  234. package/assets/docs/internationalization.doc.mjs +7 -5
  235. package/assets/docs/layout.doc.dense.mjs +2 -2
  236. package/assets/docs/layout.doc.mjs +1 -1
  237. package/assets/docs/principles.doc.mjs +6 -6
  238. package/assets/docs/styling-libraries.doc.mjs +4 -4
  239. package/assets/docs/styling.doc.mjs +4 -4
  240. package/assets/docs/theme.doc.mjs +5 -5
  241. package/assets/docs/tokens.doc.mjs +1 -1
  242. package/assets/docs/tree/api.doc.mjs +30 -0
  243. package/assets/docs/tree/cli.doc.mjs +23 -0
  244. package/assets/docs/tree/commands.doc.mjs +25 -0
  245. package/assets/docs/tree/component-lookups.doc.mjs +149 -0
  246. package/assets/docs/{cli-integrations.doc.mjs → tree/integrations.doc.mjs} +144 -26
  247. package/assets/docs/tree/integrations.test.mjs +62 -0
  248. package/assets/docs/tree/writing-docs.doc.mjs +286 -0
  249. package/assets/docs/working-with-ai.doc.mjs +4 -4
  250. package/assets/templates/blocks/components/CheckboxList/CheckboxListSelectAllPattern.doc.mjs +1 -1
  251. package/assets/templates/blocks/components/CheckboxList/CheckboxListSelectAllPattern.tsx +1 -3
  252. package/assets/templates/blocks/components/DateInput/DateInputDateRange.tsx +1 -1
  253. package/assets/templates/blocks/components/InternationalizationProvider/InternationalizationProvider01ShippedLocale.tsx +1 -1
  254. package/assets/templates/blocks/components/Item/ItemDocumentTabs.doc.mjs +14 -0
  255. package/assets/templates/blocks/components/Item/ItemDocumentTabs.tsx +100 -0
  256. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaInlineRail.doc.mjs +14 -0
  257. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaInlineRail.tsx +61 -0
  258. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaOverscrollChaining.doc.mjs +14 -0
  259. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaOverscrollChaining.tsx +126 -0
  260. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaShowcase.doc.mjs +15 -0
  261. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaShowcase.tsx +86 -0
  262. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyGroupHeaders.doc.mjs +14 -0
  263. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyGroupHeaders.tsx +99 -0
  264. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyPassthrough.doc.mjs +14 -0
  265. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyPassthrough.tsx +122 -0
  266. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaTwoAxisBoard.doc.mjs +14 -0
  267. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaTwoAxisBoard.tsx +95 -0
  268. package/assets/templates/blocks/components/Table/TableBulkActionsTable.doc.mjs +14 -0
  269. package/assets/templates/blocks/components/Table/TableBulkActionsTable.tsx +71 -0
  270. package/assets/templates/blocks/components/Table/TableFloatingBulkActionsTable.doc.mjs +19 -0
  271. package/assets/templates/blocks/components/Table/TableFloatingBulkActionsTable.tsx +139 -0
  272. package/assets/templates/blocks/components/TimeInput/TimeInputConstrained.tsx +1 -0
  273. package/assets/templates/blocks/components/Timer/TimerFormats.doc.mjs +14 -0
  274. package/assets/templates/blocks/components/Timer/TimerFormats.tsx +34 -0
  275. package/assets/templates/blocks/components/Timer/TimerInline.doc.mjs +14 -0
  276. package/assets/templates/blocks/components/Timer/TimerInline.tsx +14 -0
  277. package/assets/templates/blocks/components/Timer/TimerShowcase.doc.mjs +13 -0
  278. package/assets/templates/blocks/components/Timer/TimerShowcase.tsx +47 -0
  279. package/assets/templates/blocks/components/Timer/TimerTypography.doc.mjs +14 -0
  280. package/assets/templates/blocks/components/Timer/TimerTypography.tsx +31 -0
  281. package/assets/templates/blocks/components/Toolbar/ToolbarTableFilter.doc.mjs +19 -3
  282. package/assets/templates/blocks/components/Toolbar/ToolbarTableFilter.tsx +383 -65
  283. package/assets/templates/pages/table-tree/page.tsx +1704 -0
  284. package/assets/templates/pages/table-tree/template.doc.mjs +12 -0
  285. package/assets/templates/themes/butter/butterTheme.doc.mjs +11 -0
  286. package/assets/templates/themes/chocolate/chocolateTheme.doc.mjs +11 -0
  287. package/assets/templates/themes/gothic/gothicTheme.doc.mjs +11 -0
  288. package/assets/templates/themes/matcha/matchaTheme.doc.mjs +11 -0
  289. package/assets/templates/themes/neutral/neutralTheme.doc.mjs +11 -0
  290. package/assets/templates/themes/stone/stoneTheme.doc.mjs +11 -0
  291. package/assets/templates/themes/y2k/y2kTheme.doc.mjs +11 -0
  292. package/authoring/_shared/contract.ts +22 -0
  293. package/authoring/codemod/codemod.doc.mjs +7 -2
  294. package/authoring/codemod/parse.d.mts +8 -8
  295. package/authoring/codemod/parse.mjs +8 -6
  296. package/authoring/codemod/type.ts +12 -0
  297. package/authoring/config/config.doc.mjs +11 -3
  298. package/authoring/config/debug-composition.test.mjs +92 -0
  299. package/authoring/config/parse.d.mts +15 -13
  300. package/authoring/config/parse.mjs +27 -8
  301. package/authoring/config/parse.test.mjs +8 -0
  302. package/authoring/config/type.ts +31 -8
  303. package/authoring/debug/debug.doc.d.mts +11 -0
  304. package/authoring/debug/debug.doc.mjs +182 -0
  305. package/authoring/debug/parse.d.mts +8 -8
  306. package/authoring/debug/parse.mjs +3 -3
  307. package/authoring/discover/discover.doc.d.mts +13 -0
  308. package/authoring/discover/discover.doc.mjs +138 -0
  309. package/authoring/discover/parse.d.mts +24 -0
  310. package/authoring/discover/parse.mjs +128 -0
  311. package/authoring/discover/parse.test.mjs +124 -0
  312. package/authoring/discover/type.ts +87 -0
  313. package/authoring/doctypes/_schema.d.mts +790 -23
  314. package/authoring/doctypes/_schema.mjs +543 -39
  315. package/authoring/doctypes/base/graph-fields.doc.d.mts +9 -0
  316. package/authoring/doctypes/base/graph-fields.doc.mjs +64 -0
  317. package/authoring/doctypes/base/type.ts +41 -0
  318. package/authoring/doctypes/command/command.doc.mjs +5 -4
  319. package/authoring/doctypes/command/parse.d.mts +2 -2
  320. package/authoring/doctypes/command/parse.mjs +1 -1
  321. package/authoring/doctypes/command/type.ts +6 -5
  322. package/authoring/doctypes/component/component.doc.mjs +6 -3
  323. package/authoring/doctypes/component/parse.d.mts +2 -2
  324. package/authoring/doctypes/component/parse.mjs +1 -1
  325. package/authoring/doctypes/component/type.ts +6 -5
  326. package/authoring/doctypes/doctypes-new.test.mjs +48 -6
  327. package/authoring/doctypes/enum/enum.doc.mjs +1 -1
  328. package/authoring/doctypes/enum/parse.d.mts +2 -2
  329. package/authoring/doctypes/enum/parse.mjs +1 -1
  330. package/authoring/doctypes/enum/type.ts +4 -2
  331. package/authoring/doctypes/function/function.doc.mjs +7 -2
  332. package/authoring/doctypes/function/parse.d.mts +2 -2
  333. package/authoring/doctypes/function/parse.mjs +1 -1
  334. package/authoring/doctypes/function/type.ts +9 -4
  335. package/authoring/doctypes/hook/hook.doc.mjs +4 -0
  336. package/authoring/doctypes/hook/parse.d.mts +2 -2
  337. package/authoring/doctypes/hook/parse.mjs +1 -1
  338. package/authoring/doctypes/hook/type.ts +5 -4
  339. package/authoring/doctypes/legacy.d.mts +8 -6
  340. package/authoring/doctypes/legacy.mjs +5 -4
  341. package/authoring/doctypes/load-contract.test.mjs +233 -0
  342. package/authoring/doctypes/namespace/namespace.doc.d.mts +9 -0
  343. package/authoring/doctypes/namespace/namespace.doc.mjs +128 -0
  344. package/authoring/doctypes/namespace/parse.d.mts +12 -0
  345. package/authoring/doctypes/namespace/parse.mjs +25 -0
  346. package/authoring/doctypes/namespace/parse.test.mjs +163 -0
  347. package/authoring/doctypes/namespace/type.ts +74 -0
  348. package/authoring/doctypes/parse.d.mts +22 -18
  349. package/authoring/doctypes/parse.mjs +22 -11
  350. package/authoring/doctypes/parse.test.mjs +77 -3
  351. package/authoring/doctypes/reference/parse.d.mts +2 -2
  352. package/authoring/doctypes/reference/parse.mjs +8 -5
  353. package/authoring/doctypes/reference/reference.doc.mjs +48 -6
  354. package/authoring/doctypes/reference/type.ts +70 -7
  355. package/authoring/doctypes/schema/parse.d.mts +2 -2
  356. package/authoring/doctypes/schema/parse.mjs +1 -1
  357. package/authoring/doctypes/schema/schema.doc.mjs +1 -1
  358. package/authoring/doctypes/schema/type.ts +4 -4
  359. package/authoring/doctypes/template/parse.d.mts +94 -1
  360. package/authoring/doctypes/template/parse.mjs +40 -2
  361. package/authoring/doctypes/template/parse.test.mjs +26 -2
  362. package/authoring/doctypes/template/template.doc.mjs +13 -3
  363. package/authoring/doctypes/template/type.ts +13 -2
  364. package/authoring/doctypes/theme/parse.d.mts +35 -0
  365. package/authoring/doctypes/theme/parse.mjs +76 -0
  366. package/authoring/doctypes/theme/theme.doc.d.mts +9 -0
  367. package/authoring/doctypes/theme/theme.doc.mjs +79 -0
  368. package/authoring/doctypes/theme/type.ts +42 -0
  369. package/authoring/doctypes/types.ts +12 -10
  370. package/authoring/gap-report/gap-report.doc.d.mts +12 -0
  371. package/authoring/gap-report/gap-report.doc.mjs +183 -0
  372. package/authoring/gap-report/parse.d.mts +10 -10
  373. package/authoring/gap-report/parse.mjs +6 -6
  374. package/authoring/gap-report/type.ts +1 -1
  375. package/authoring/identity/identity.doc.d.mts +9 -0
  376. package/authoring/identity/identity.doc.mjs +61 -0
  377. package/authoring/identity/type.ts +132 -0
  378. package/authoring/index.d.mts +3 -0
  379. package/authoring/index.d.ts +62 -17
  380. package/authoring/index.mjs +4 -1
  381. package/authoring/integration/integration.doc.mjs +15 -8
  382. package/authoring/integration/parse.d.mts +2 -2
  383. package/authoring/integration/parse.mjs +1 -1
  384. package/authoring/integration/parse.test.mjs +10 -1
  385. package/authoring/integration/schema.d.mts +6 -4
  386. package/authoring/integration/schema.mjs +9 -3
  387. package/authoring/integration/type.ts +19 -8
  388. package/authoring/shadcn/receipt.d.mts +6 -6
  389. package/clients/cli/__tests__/cliManifest.test.ts +27 -29
  390. package/clients/cli/command-load-failure.test.mjs +83 -0
  391. package/clients/cli/command-result-coverage.test.mjs +7 -7
  392. package/clients/cli/commands/blog.doc.mjs +1 -1
  393. package/clients/cli/commands/blog.mjs +23 -8
  394. package/clients/cli/commands/blog.test.mjs +42 -1
  395. package/clients/cli/commands/build-theme.adaptations.test.mjs +100 -1
  396. package/clients/cli/commands/build-theme.ascii-output.test.mjs +161 -0
  397. package/clients/cli/commands/build-theme.flag-docs.test.mjs +110 -0
  398. package/clients/cli/commands/build-theme.mjs +16 -50
  399. package/clients/cli/commands/build-theme.path-safety.test.mjs +86 -1
  400. package/clients/cli/commands/build-theme.variants.test.mjs +76 -0
  401. package/clients/cli/commands/build.doc.mjs +16 -8
  402. package/clients/cli/commands/build.exit-codes-doc.test.mjs +50 -0
  403. package/clients/cli/commands/build.mjs +137 -114
  404. package/clients/cli/commands/build.playbook.test.mjs +75 -0
  405. package/clients/cli/commands/build.text-fields.test.mjs +81 -0
  406. package/clients/cli/commands/component/index.mjs +153 -61
  407. package/clients/cli/commands/component-batch.test.mjs +341 -0
  408. package/clients/cli/commands/component-ownership.test.mjs +92 -3
  409. package/clients/cli/commands/component-package.test.mjs +46 -0
  410. package/clients/cli/commands/component-resolution.test.mjs +21 -0
  411. package/clients/cli/commands/component.doc.mjs +24 -7
  412. package/clients/cli/commands/component.test.mjs +19 -0
  413. package/clients/cli/commands/debug-result-summary.test.mjs +2 -2
  414. package/clients/cli/commands/detail-levels.test.mjs +2 -2
  415. package/clients/cli/commands/discover.broken-integration.test.mjs +52 -5
  416. package/clients/cli/commands/discover.components-flag.test.mjs +97 -0
  417. package/clients/cli/commands/discover.doc.mjs +55 -9
  418. package/clients/cli/commands/discover.mjs +393 -118
  419. package/clients/cli/commands/discover.sources.test.mjs +267 -0
  420. package/clients/cli/commands/discover.text-projection.test.mjs +103 -0
  421. package/clients/cli/commands/docs.doc.mjs +28 -6
  422. package/clients/cli/commands/docs.mjs +240 -26
  423. package/clients/cli/commands/docs.test.mjs +222 -1
  424. package/clients/cli/commands/doctor-integration-components.doc.mjs +1 -1
  425. package/clients/cli/commands/doctor-integration-docs.doc.mjs +3 -3
  426. package/clients/cli/commands/doctor-integration-templates.doc.mjs +15 -8
  427. package/clients/cli/commands/doctor-integration-validate.doc.mjs +1 -1
  428. package/clients/cli/commands/doctor-integration.doc.mjs +1 -1
  429. package/clients/cli/commands/doctor-integration.package-json.test.mjs +53 -0
  430. package/clients/cli/commands/doctor-integration.test.mjs +90 -8
  431. package/clients/cli/commands/doctor.doc.mjs +1 -1
  432. package/clients/cli/commands/doctor.mjs +59 -32
  433. package/clients/cli/commands/doctor.test.mjs +42 -0
  434. package/clients/cli/commands/gap-report.doc.mjs +17 -6
  435. package/clients/cli/commands/gap-report.test.mjs +72 -0
  436. package/clients/cli/commands/hook/index.mjs +7 -17
  437. package/clients/cli/commands/hook.doc.mjs +1 -1
  438. package/clients/cli/commands/hook.text-projection.test.mjs +45 -0
  439. package/clients/cli/commands/init.doc.mjs +20 -9
  440. package/clients/cli/commands/init.flag-help.test.mjs +153 -0
  441. package/clients/cli/commands/integration-add.controls.test.mjs +132 -0
  442. package/clients/cli/commands/integration-add.doc.mjs +32 -6
  443. package/clients/cli/commands/integration-authoring.test.mjs +13 -9
  444. package/clients/cli/commands/integration-pack.doc.mjs +1 -1
  445. package/clients/cli/commands/integration-real-world.test.mjs +3 -9
  446. package/clients/cli/commands/integration.doc.mjs +1 -1
  447. package/clients/cli/commands/integration.mjs +1 -0
  448. package/clients/cli/commands/interactive-guard.test.mjs +101 -24
  449. package/clients/cli/commands/json-contract.test.mjs +33 -0
  450. package/clients/cli/commands/manifest.doc.mjs +1 -1
  451. package/clients/cli/commands/no-prompt-wording.test.mjs +94 -0
  452. package/clients/cli/commands/search.doc.mjs +7 -4
  453. package/clients/cli/commands/search.mjs +28 -9
  454. package/clients/cli/commands/search.test.mjs +75 -0
  455. package/clients/cli/commands/setup-nudge.test.mjs +6 -0
  456. package/clients/cli/commands/swizzle.doc.mjs +3 -2
  457. package/clients/cli/commands/swizzle.path-safety.test.mjs +42 -0
  458. package/clients/cli/commands/template.doc.mjs +52 -13
  459. package/clients/cli/commands/template.flag-help.test.mjs +117 -0
  460. package/clients/cli/commands/template.mjs +4 -91
  461. package/clients/cli/commands/template.path-help.test.mjs +40 -0
  462. package/clients/cli/commands/text-json-parity.test.mjs +702 -0
  463. package/clients/cli/commands/theme-add.doc.mjs +4 -3
  464. package/clients/cli/commands/theme-build.doc.mjs +8 -7
  465. package/clients/cli/commands/theme-list.doc.mjs +2 -2
  466. package/clients/cli/commands/theme-palette-generate.doc.mjs +9 -5
  467. package/clients/cli/commands/theme-palette-generate.test.mjs +19 -0
  468. package/clients/cli/commands/theme-palette.doc.mjs +1 -1
  469. package/clients/cli/commands/theme-targets.behavior.test.mjs +4 -3
  470. package/clients/cli/commands/theme-targets.doc.mjs +1 -1
  471. package/clients/cli/commands/theme-template.behavior.test.mjs +12 -0
  472. package/clients/cli/commands/theme-template.doc.mjs +2 -2
  473. package/clients/cli/commands/theme.doc.mjs +1 -1
  474. package/clients/cli/commands/upgrade.ascii-output.test.mjs +87 -0
  475. package/clients/cli/commands/upgrade.doc.mjs +22 -10
  476. package/clients/cli/commands/upgrade.file-protection.test.mjs +228 -0
  477. package/clients/cli/commands/upgrade.flag-help.test.mjs +188 -0
  478. package/clients/cli/commands/upgrade.hook-output.test.mjs +88 -0
  479. package/clients/cli/commands/upgrade.mjs +29 -7
  480. package/clients/cli/formatters/index.mjs +164 -1
  481. package/clients/cli/formatters/index.test.mjs +97 -0
  482. package/clients/cli/index.mjs +21 -34
  483. package/clients/cli/latest-version-env.test.mjs +50 -0
  484. package/clients/cli/lib/cli-error.test.mjs +7 -0
  485. package/clients/cli/lib/component-format.mjs +9 -9
  486. package/clients/cli/lib/component-format.test.mjs +1 -1
  487. package/clients/cli/lib/define-command.mjs +32 -6
  488. package/clients/cli/lib/doc-text-ascii.test.mjs +82 -0
  489. package/clients/cli/lib/exit-codes.test.mjs +90 -0
  490. package/clients/cli/lib/hook-format.mjs +19 -10
  491. package/clients/cli/lib/json-shim.mjs +62 -16
  492. package/clients/cli/lib/json-shim.test.mjs +69 -0
  493. package/clients/cli/lib/manifest.d.ts +2 -0
  494. package/clients/cli/lib/manifest.mjs +39 -10
  495. package/clients/cli/lib/manifest.test.mjs +22 -2
  496. package/clients/cli/lib/parse-error-format.test.mjs +81 -0
  497. package/foundation/agent-docs/agent-docs.d.mts +7 -2
  498. package/foundation/agent-docs/agent-docs.mjs +82 -12
  499. package/foundation/agent-docs/agent-docs.path-safety.test.mjs +266 -4
  500. package/foundation/agent-docs/agent-docs.test.mjs +19 -1
  501. package/foundation/config/integration-debug.test.mjs +28 -3
  502. package/foundation/config/project-themes.test.mjs +11 -19
  503. package/foundation/config/project.d.mts +20 -11
  504. package/foundation/config/project.mjs +263 -91
  505. package/foundation/config/project.test.mjs +270 -21
  506. package/foundation/discovery/authoring-self-docs.d.mts +87 -0
  507. package/foundation/discovery/authoring-self-docs.mjs +237 -0
  508. package/foundation/discovery/authoring-self-docs.test.mjs +170 -0
  509. package/foundation/discovery/authoring-surface.d.mts +74 -0
  510. package/foundation/discovery/authoring-surface.mjs +525 -0
  511. package/foundation/discovery/authoring-surface.test.mjs +392 -0
  512. package/foundation/discovery/cli-self-docs.d.mts +119 -0
  513. package/foundation/discovery/cli-self-docs.mjs +490 -0
  514. package/foundation/discovery/cli-self-docs.test.mjs +375 -0
  515. package/foundation/discovery/component-discovery.d.mts +39 -1
  516. package/foundation/discovery/component-discovery.mjs +50 -1
  517. package/foundation/discovery/component-loader.d.mts +35 -38
  518. package/foundation/discovery/component-loader.mjs +53 -222
  519. package/foundation/discovery/docs-discovery.d.mts +119 -11
  520. package/foundation/discovery/docs-discovery.mjs +423 -108
  521. package/foundation/discovery/docs-discovery.test.mjs +365 -20
  522. package/foundation/discovery/docs-output-budget.d.mts +28 -0
  523. package/foundation/discovery/docs-output-budget.mjs +50 -0
  524. package/foundation/discovery/docs-section-key.d.mts +116 -0
  525. package/foundation/discovery/docs-section-key.mjs +322 -0
  526. package/foundation/discovery/docs-section-key.test.mjs +246 -0
  527. package/foundation/discovery/template-adapter.d.mts +113 -11
  528. package/foundation/discovery/template-adapter.fixture-refs.test.mjs +248 -0
  529. package/foundation/discovery/template-adapter.integration-isolation.test.mjs +94 -0
  530. package/foundation/discovery/template-adapter.mjs +775 -84
  531. package/foundation/discovery/template-adapter.test.mjs +57 -0
  532. package/foundation/discovery/template-conflict-release.d.mts +13 -0
  533. package/foundation/discovery/template-conflict-release.mjs +40 -0
  534. package/foundation/discovery/template-conflict-release.test.mjs +40 -0
  535. package/foundation/discovery/theme-discovery.d.mts +67 -7
  536. package/foundation/discovery/theme-discovery.mjs +916 -186
  537. package/foundation/discovery/theme-discovery.test.mjs +613 -219
  538. package/foundation/discovery/theming-targets.test.mjs +4 -0
  539. package/foundation/doc-compiler/bundle.d.mts +47 -0
  540. package/foundation/doc-compiler/bundle.mjs +278 -0
  541. package/foundation/doc-compiler/bundle.test.mjs +266 -0
  542. package/foundation/doc-compiler/compile.d.mts +343 -0
  543. package/foundation/doc-compiler/compile.mjs +558 -0
  544. package/foundation/doc-compiler/diagnostics.d.mts +126 -0
  545. package/foundation/doc-compiler/diagnostics.mjs +305 -0
  546. package/foundation/doc-compiler/doc-compiler.test.mjs +714 -0
  547. package/foundation/doc-compiler/doc-loads.test.mjs +1630 -0
  548. package/foundation/doc-compiler/import.d.mts +24 -0
  549. package/foundation/doc-compiler/import.mjs +59 -0
  550. package/foundation/doc-compiler/inputs.d.mts +102 -0
  551. package/foundation/doc-compiler/inputs.mjs +291 -0
  552. package/foundation/doc-compiler/inputs.test.mjs +299 -0
  553. package/foundation/doc-compiler/ir.d.mts +22 -0
  554. package/foundation/doc-compiler/ir.mjs +471 -0
  555. package/foundation/doc-compiler/lenses.d.mts +36 -0
  556. package/foundation/doc-compiler/lenses.mjs +173 -0
  557. package/foundation/doc-compiler/links.d.mts +162 -0
  558. package/foundation/doc-compiler/links.mjs +294 -0
  559. package/foundation/doc-compiler/links.test.mjs +192 -0
  560. package/foundation/doc-compiler/lower-doc.test.mjs +495 -0
  561. package/foundation/doc-compiler/overlays.d.mts +37 -0
  562. package/foundation/doc-compiler/overlays.mjs +206 -0
  563. package/foundation/doc-compiler/parse-readable.d.mts +9 -0
  564. package/foundation/doc-compiler/parse-readable.mjs +29 -0
  565. package/foundation/doc-compiler/read.d.mts +127 -0
  566. package/foundation/doc-compiler/read.mjs +325 -0
  567. package/foundation/doc-compiler/read.test.mjs +313 -0
  568. package/foundation/doc-compiler/source.d.mts +33 -0
  569. package/foundation/doc-compiler/source.mjs +128 -0
  570. package/foundation/doc-compiler/tree.d.mts +288 -0
  571. package/foundation/doc-compiler/tree.mjs +876 -0
  572. package/foundation/doc-compiler/tree.test.mjs +606 -0
  573. package/foundation/fs/file-protection.d.mts +33 -0
  574. package/foundation/fs/file-protection.mjs +825 -0
  575. package/foundation/fs/file-protection.test.mjs +250 -0
  576. package/foundation/fs/module-loader.d.mts +1 -0
  577. package/foundation/fs/module-loader.mjs +50 -1
  578. package/foundation/fs/module-loader.stdout.test.mjs +332 -0
  579. package/foundation/fs/path-safety.d.mts +3 -2
  580. package/foundation/fs/path-safety.mjs +49 -19
  581. package/foundation/fs/path-safety.test.mjs +50 -0
  582. package/foundation/fs/publish-file-hardlink-unavailable.test.mjs +129 -94
  583. package/foundation/identity/provider-identity.d.mts +90 -0
  584. package/foundation/identity/provider-identity.mjs +320 -0
  585. package/foundation/identity/provider-identity.test.mjs +254 -0
  586. package/foundation/identity/providers.d.mts +7 -0
  587. package/foundation/identity/providers.mjs +16 -0
  588. package/foundation/integrations/autolink.d.mts +58 -1
  589. package/foundation/integrations/autolink.mjs +143 -45
  590. package/foundation/integrations/autolink.test.mjs +1 -1
  591. package/foundation/integrations/cli-requirement.d.mts +45 -0
  592. package/foundation/integrations/cli-requirement.mjs +154 -0
  593. package/foundation/integrations/cli-requirement.test.mjs +84 -0
  594. package/foundation/integrations/contribution-fixes.d.mts +145 -0
  595. package/foundation/integrations/contribution-fixes.mjs +1284 -0
  596. package/foundation/integrations/contribution-inventory.d.mts +3 -2
  597. package/foundation/integrations/contribution-inventory.mjs +27 -24
  598. package/foundation/integrations/contribution-inventory.test.mjs +86 -27
  599. package/foundation/integrations/integration-warnings.d.mts +9 -2
  600. package/foundation/integrations/integration-warnings.mjs +52 -21
  601. package/foundation/integrations/integration-warnings.test.mjs +74 -1
  602. package/foundation/integrations/integrations.d.mts +63 -3
  603. package/foundation/integrations/integrations.mjs +122 -9
  604. package/foundation/integrations/integrations.test.mjs +415 -1
  605. package/foundation/integrations/provider-conflicts.test.mjs +125 -0
  606. package/foundation/integrations/provider-ledger.test.mjs +275 -0
  607. package/foundation/integrations/provider-resolution.d.mts +152 -0
  608. package/foundation/integrations/provider-resolution.mjs +576 -0
  609. package/foundation/integrations/provider-resolution.test.mjs +369 -0
  610. package/foundation/integrations/theme-descriptor.d.mts +8 -0
  611. package/foundation/integrations/theme-descriptor.mjs +44 -0
  612. package/foundation/integrations/validate-contributions.d.mts +2 -0
  613. package/foundation/integrations/validate-contributions.mjs +131 -29
  614. package/foundation/response/base.d.ts +8 -4
  615. package/foundation/response/batch.type.d.mts +33 -0
  616. package/foundation/response/batch.type.mjs +34 -0
  617. package/foundation/response/error-codes.d.mts +3 -1
  618. package/foundation/response/error-codes.d.ts +2 -0
  619. package/foundation/response/error-codes.doc.mjs +13 -4
  620. package/foundation/response/error-codes.mjs +8 -2
  621. package/foundation/response/error-codes.test.mjs +137 -10
  622. package/foundation/response/json-contract.test.mjs +57 -17
  623. package/foundation/response/json.d.mts +4 -2
  624. package/foundation/response/json.mjs +8 -10
  625. package/foundation/response/response-types.doc.d.mts +5 -1
  626. package/foundation/response/response-types.doc.mjs +46 -38
  627. package/foundation/response/response-types.doc.test.mjs +181 -0
  628. package/foundation/response/response.doc.mjs +1 -1
  629. package/foundation/text/string-utils.d.mts +8 -0
  630. package/foundation/text/string-utils.mjs +40 -10
  631. package/foundation/xle/browser.d.mts +3 -3
  632. package/foundation/xle/browser.mjs +3 -3
  633. package/foundation/xle/expand.d.mts +2 -0
  634. package/foundation/xle/expand.mjs +6 -5
  635. package/foundation/xle/expand.test.mjs +54 -0
  636. package/foundation/xle/parse.mjs +1 -1
  637. package/foundation/xle/print.mjs +2 -2
  638. package/foundation/xle/splice.mjs +1 -1
  639. package/foundation/xle/xle.test.mjs +13 -0
  640. package/package.json +10 -11
  641. package/api/layout/_adapter.d.mts +0 -34
  642. package/api/layout/_adapter.mjs +0 -133
  643. package/api/layout/check/check.d.mts +0 -16
  644. package/api/layout/check/check.mjs +0 -40
  645. package/api/layout/expand/expand.d.mts +0 -22
  646. package/api/layout/expand/expand.mjs +0 -153
  647. package/api/layout/grammar/grammar.d.mts +0 -13
  648. package/api/layout/grammar/grammar.mjs +0 -86
  649. package/api/layout/layout.d.mts +0 -6
  650. package/api/layout/layout.mjs +0 -17
  651. package/api/layout/layout.test.mjs +0 -297
  652. package/api/layout/layout.type.d.mts +0 -89
  653. package/api/layout/layout.type.mjs +0 -103
  654. package/api/layout/layoutCheck.doc.d.mts +0 -11
  655. package/api/layout/layoutCheck.doc.mjs +0 -84
  656. package/api/layout/layoutExpand.doc.d.mts +0 -11
  657. package/api/layout/layoutExpand.doc.mjs +0 -106
  658. package/api/layout/layoutGrammar.doc.d.mts +0 -11
  659. package/api/layout/layoutGrammar.doc.mjs +0 -56
  660. package/assets/templates/themes/manifest.json +0 -95
  661. package/clients/cli/commands/layout-check.doc.mjs +0 -54
  662. package/clients/cli/commands/layout-expand.doc.mjs +0 -66
  663. package/clients/cli/commands/layout-grammar.doc.mjs +0 -30
  664. package/clients/cli/commands/layout.doc.mjs +0 -34
  665. package/clients/cli/commands/layout.error-codes.test.mjs +0 -66
  666. package/clients/cli/commands/layout.exit-parity.test.mjs +0 -41
  667. package/clients/cli/commands/layout.mjs +0 -263
  668. package/clients/cli/lib/update-check.mjs +0 -83
  669. package/clients/cli/lib/update-check.test.mjs +0 -137
  670. package/clients/cli/update-hint-commands.test.mjs +0 -54
@@ -29,11 +29,16 @@
29
29
  * 4. Routing unknown-subcommand attempts through the same error
30
30
  * envelope path (so `astryx bogus-cmd --json` gets exit 1 + envelope
31
31
  * instead of exit 0 + help envelope).
32
+ * 5. Emitting an error envelope, not the help envelope, when Commander
33
+ * shows help because the invocation failed (`help <unknown>`, or a
34
+ * command group with no subcommand), which exits 1.
32
35
  *
33
- * Non-JSON behavior is preserved exactly: every code path that printed
34
- * to stderr before still prints to stderr. Commander writes its
35
- * "error: ..." line via configureOutput.writeErr, which we pass
36
- * through verbatim outside of --json mode.
36
+ * Commander writes its own "error: ..." line via configureOutput.writeErr.
37
+ * The shim drops that line in both modes. Under --json the error envelope
38
+ * replaces it; in text mode `handleCommanderError` writes the Astryx line
39
+ * instead (`Error: <message>`, the same message the envelope carries), so
40
+ * a parse failure reads like every other CLI error. Other stderr output,
41
+ * such as help printed as the failure report, still passes through.
37
42
  */
38
43
 
39
44
  import {API_VERSION, isJsonMode, toErrorEnvelope} from '../../../foundation/response/json.mjs';
@@ -117,6 +122,35 @@ export function buildHelpEnvelope(cmd) {
117
122
  };
118
123
  }
119
124
 
125
+ /**
126
+ * The error envelope for help Commander shows because the invocation failed
127
+ * (it then exits 1): `help <name>` for an unknown name on the root, or a
128
+ * command group run without a subcommand.
129
+ *
130
+ * @param {import('commander').Command} cmd the command whose help was shown
131
+ * @returns {ReturnType<typeof toErrorEnvelope>}
132
+ */
133
+ export function buildHelpErrorEnvelope(cmd) {
134
+ const available = cmd.commands
135
+ .filter(s => !(/** @type {any} */ (s))._hidden && s.name() !== 'help')
136
+ .map(s => s.name());
137
+ if (!cmd.parent) {
138
+ // Commander dispatches `help <name>` with ['help', <name>, ...] in args.
139
+ const requested = cmd.args[1];
140
+ return toErrorEnvelope(
141
+ requested ? `unknown command '${requested}'` : 'unknown command',
142
+ available.map(name => ({name, reason: 'available command'})),
143
+ ERROR_CODES.ERR_UNKNOWN_COMMAND,
144
+ );
145
+ }
146
+ const group = fullNameOf(cmd);
147
+ return toErrorEnvelope(
148
+ `'${group}' needs a subcommand`,
149
+ available.map(name => ({name: `${group} ${name}`, reason: 'available subcommand'})),
150
+ ERROR_CODES.ERR_MISSING_ARGUMENT,
151
+ );
152
+ }
153
+
120
154
  /**
121
155
  * Emit a JSON error envelope to stdout (the JSON contract uses stdout
122
156
  * for both success and error).
@@ -217,11 +251,20 @@ function applyShimRecursively(cmd) {
217
251
  });
218
252
  cmd.configureOutput({
219
253
  writeOut: (str) => process.stdout.write(str),
254
+ // Commander's own "error: ..." line never reaches the user. Under --json a
255
+ // consumer parsing both streams must not see it alongside the envelope;
256
+ // in text mode it is Commander's format, not Astryx's, so an invalid
257
+ // global option (`--lang zh-Hans`) printed `error: option '--lang
258
+ // <locale>' argument 'zh-Hans' is invalid…` where every other CLI error
259
+ // prints `Error: …`. handleCommanderError writes the Astryx line below,
260
+ // for both modes, from the same message.
261
+ //
262
+ // ONLY that line. Commander also writes HELP through this channel when it
263
+ // shows help because the invocation failed (a command group with no
264
+ // subcommand), and that output is still wanted in text mode.
220
265
  writeErr: (str) => {
221
- // Suppress Commander's "error: ..." stderr line when --json is
222
- // active, so a JSON consumer parsing both streams doesn't see
223
- // it alongside the envelope. Non-JSON callers are unaffected.
224
266
  if (jsonActive()) return;
267
+ if (/^error:\s/i.test(str)) return;
225
268
  process.stderr.write(str);
226
269
  },
227
270
  });
@@ -310,7 +353,9 @@ function patchOutputHelp(cmd) {
310
353
  if (jsonActive()) {
311
354
  if (!process.__xdsJsonHandled) {
312
355
  process.__xdsJsonHandled = true;
313
- const env = buildHelpEnvelope(cmd);
356
+ const env = contextOptions?.error
357
+ ? buildHelpErrorEnvelope(cmd)
358
+ : buildHelpEnvelope(cmd);
314
359
  process.stdout.write(`${JSON.stringify(env, null, 2)}\n`);
315
360
  }
316
361
  return;
@@ -334,7 +379,9 @@ function patchPrototype(CommandCtor) {
334
379
  if (jsonActive()) {
335
380
  if (!process.__xdsJsonHandled) {
336
381
  process.__xdsJsonHandled = true;
337
- const env = buildHelpEnvelope(this);
382
+ const env = contextOptions?.error
383
+ ? buildHelpErrorEnvelope(this)
384
+ : buildHelpEnvelope(this);
338
385
  process.stdout.write(`${JSON.stringify(env, null, 2)}\n`);
339
386
  }
340
387
  return;
@@ -396,16 +443,15 @@ export function handleCommanderError(err) {
396
443
  code: commanderCodeToErrorCode(code, message),
397
444
  });
398
445
 
399
- // Real error paths.
446
+ // Real error paths. Strip Commander's "error: " prefix once: in the envelope
447
+ // the key is already `error`, and in text mode the Astryx prefix replaces it.
448
+ const cleaned = message.replace(/^error:\s*/i, '');
400
449
  if (jsonActive()) {
401
- // Strip Commander's "error: " prefix — the envelope key is `error`
402
- // already, doubled "error" is noise.
403
- const cleaned = message.replace(/^error:\s*/i, '');
404
450
  emitJsonError(cleaned, undefined, commanderCodeToErrorCode(code, cleaned));
405
451
  } else {
406
- // Non-JSON mode: Commander already wrote the "error: ..." line
407
- // to stderr via configureOutput.writeErr before throwing the
408
- // CommanderError. Nothing to do — exit with the original code.
452
+ // Commander's own line was suppressed above, so a parse failure reads the
453
+ // same as every other CLI error — the `Error: …` line cliError prints.
454
+ process.stderr.write(`Error: ${cleaned}\n`);
409
455
  }
410
456
  process.exit(exitCode || 1);
411
457
  }
@@ -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,67 @@ 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('the real binary emits the same envelope for help bogus --json', () => {
229
+ // One program per process, so the root's own outputHelp patch is used here.
230
+ const bin = fileURLToPath(new URL('../bin/astryx.mjs', import.meta.url));
231
+ const res = spawnSync(process.execPath, [bin, 'help', 'bogus', '--json'], {
232
+ encoding: 'utf-8',
233
+ timeout: 20_000,
234
+ });
235
+ expect(res.status).toBe(1);
236
+ expect(res.stderr).toBe('');
237
+ const parsed = parseJson(res.stdout);
238
+ expect(parsed).not.toHaveProperty('type');
239
+ expect(parsed.code).toBe('ERR_UNKNOWN_COMMAND');
240
+ });
241
+
242
+ it('a group added after install gets the same error envelope', () => {
243
+ // This is the only coverage of the no-subcommand path: every group the CLI
244
+ // ships has an action of its own (see command-result-coverage.test.mjs), so
245
+ // a group without one has to be built here.
246
+ const program = new Command('astryx');
247
+ installJsonShim(program);
248
+ // Added later, so only the prototype-level patch covers its outputHelp.
249
+ const late = new Command('late');
250
+ late.command('child');
251
+ program.addCommand(late);
252
+ /** @type {string[]} */
253
+ const writes = [];
254
+ const origWrite = process.stdout.write;
255
+ // @ts-expect-error - test-only capture
256
+ process.stdout.write = (chunk) => writes.push(String(chunk)) > 0;
257
+ setJsonMode(true);
258
+ delete process.__xdsJsonHandled;
259
+ try {
260
+ late.outputHelp({error: true});
261
+ } finally {
262
+ process.stdout.write = origWrite;
263
+ setJsonMode(false);
264
+ delete process.__xdsJsonHandled;
265
+ }
266
+ const parsed = parseJson(writes.join(''));
267
+ expect(parsed.code).toBe('ERR_MISSING_ARGUMENT');
268
+ expect(parsed.suggestions).toEqual([{name: 'late child', reason: 'available subcommand'}]);
269
+ });
270
+
271
+ it('astryx help theme --json still emits the help envelope, exit 0', async () => {
272
+ const {status, stdout} = await runCli(['help', 'theme', '--json']);
273
+ expect(status).toBe(0);
274
+ const parsed = parseJson(stdout);
275
+ expect(parsed.type).toBe('help');
276
+ expect(parsed.data.command).toBe('astryx theme');
277
+ });
278
+ });
@@ -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
@@ -49,18 +51,26 @@ export const RESPONSE_TYPES = {
49
51
  init: ['init.run', 'init.remove'],
50
52
  component: [
51
53
  'component.list',
54
+ 'component.batch',
52
55
  'component.detail',
53
56
  'component.detail.props',
54
57
  'component.detail.source',
55
58
  'component.detail.showcase',
56
59
  'component.detail.blocks',
57
60
  ],
58
- docs: ['docs.list', 'docs.detail', 'docs.detail.section'],
61
+ docs: [
62
+ 'docs.list',
63
+ 'docs.index',
64
+ 'docs.detail',
65
+ 'docs.detail.section',
66
+ 'docs.node',
67
+ ],
59
68
  blog: ['blog.list', 'blog.detail'],
60
69
  discover: [
61
70
  'discover.list',
62
71
  'discover.detail',
63
72
  'discover.detail.doc',
73
+ 'discover.item',
64
74
  'discover.search',
65
75
  ],
66
76
  search: ['search'],
@@ -90,9 +100,6 @@ export const RESPONSE_TYPES = {
90
100
  'doctor integration templates': ['integration.template-conflicts'],
91
101
  'doctor integration components': ['integration.component-conflicts'],
92
102
  'doctor integration docs': ['integration.doc-conflicts'],
93
- 'layout expand': ['layout.expand'],
94
- 'layout check': ['layout.check'],
95
- 'layout grammar': ['layout.grammar'],
96
103
  };
97
104
 
98
105
  /**
@@ -103,9 +110,16 @@ const EXAMPLES = {
103
110
  component: [
104
111
  'astryx component',
105
112
  'astryx component XDSButton',
113
+ 'astryx component Button Badge Text',
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',
@@ -166,11 +180,6 @@ const EXAMPLES = {
166
180
  'astryx doctor integration docs @acme/widgets --json',
167
181
  ],
168
182
  init: ['astryx init', 'astryx init --all --json'],
169
- 'layout expand': [
170
- `astryx layout expand 'V[g6] > C{card-callout}*4' ./src/Page.tsx`,
171
- ],
172
- 'layout check': [`astryx layout check 'A[cp6] > L > LC > S[p6]' --json`],
173
- 'layout grammar': ['astryx layout grammar'],
174
183
  };
175
184
 
176
185
  /**
@@ -232,6 +241,18 @@ function fullName(cmd, root) {
232
241
  return parts.join(' ');
233
242
  }
234
243
 
244
+ /**
245
+ * The docs a command was built from. `manifest` is registered by hand in
246
+ * index.mjs, so its CommandDoc is read here.
247
+ * @param {import('commander').Command} cmd
248
+ * @param {string} name
249
+ */
250
+ function docsOf(cmd, name) {
251
+ return (
252
+ commandDocsOf(cmd) ?? (name === 'manifest' ? {doc: manifestDoc} : undefined)
253
+ );
254
+ }
255
+
235
256
  /**
236
257
  * Recursively describe a Commander command and its subcommands.
237
258
  *
@@ -247,6 +268,8 @@ function describeCommand(cmd, root, jsonSupported) {
247
268
  // `_hidden` is a Commander internal not present on its public types.
248
269
  if (!name || /** @type {any} */ (cmd)._hidden || name === 'help') return null;
249
270
 
271
+ const docs = docsOf(cmd, name);
272
+
250
273
  const subcommands = /** @type {object[]} */ (
251
274
  (cmd.commands || [])
252
275
  .map(sub => describeCommand(sub, root, jsonSupported))
@@ -279,6 +302,12 @@ function describeCommand(cmd, root, jsonSupported) {
279
302
 
280
303
  if (EXAMPLES[name]) entry.examples = [...EXAMPLES[name]];
281
304
 
305
+ const exitCodes = (docs?.doc.exitCodes ?? []).map(({code, when}) => ({
306
+ code,
307
+ when,
308
+ }));
309
+ if (exitCodes.length > 0) entry.exitCodes = exitCodes;
310
+
282
311
  // Sort subcommands by name for a stable, agent-facing contract — the same
283
312
  // guarantee the top-level command list makes. Otherwise Commander
284
313
  // registration order leaks into the manifest and a pure reorder of
@@ -156,7 +156,10 @@ describe('manifest: shape', () => {
156
156
 
157
157
  it('derives arguments from Commander metadata', () => {
158
158
  const component = allEntries.find((c) => c.name === 'component');
159
- expect(component.arguments.map((a) => a.name)).toContain('name');
159
+ const names = component.arguments.find((a) => a.name === 'names');
160
+ expect(names.required).toBe(false);
161
+ expect(names.variadic).toBe(true);
162
+ expect(names.description).toContain('Two or more return one ordered batch');
160
163
  const themeBuild = allEntries.find((c) => c.name === 'theme build');
161
164
  expect(themeBuild.arguments.map((a) => a.name)).toContain('files');
162
165
  const files = themeBuild.arguments.find((a) => a.name === 'files');
@@ -193,6 +196,23 @@ describe('manifest: e2e', () => {
193
196
  // Enriched: the full structured manifest is embedded.
194
197
  expect(parsed.data.manifest).toBeDefined();
195
198
  expect(parsed.data.manifest.commands.find((c) => c.name === 'component').responseTypes)
196
- .toContain('component.list');
199
+ .toEqual(expect.arrayContaining(['component.list', 'component.batch']));
200
+ });
201
+ });
202
+
203
+ describe('manifest: text projection', () => {
204
+ it('astryx manifest (text) uses the JSON entry keys as its field names', async () => {
205
+ const text = await runCli(['manifest']);
206
+ expect(text.status).toBe(0);
207
+ const json = JSON.parse((await runCli(['manifest', '--json'])).stdout);
208
+ const jsonKeys = new Set(json.data.commands.flatMap((c) => Object.keys(c)));
209
+ const textKeys = new Set(
210
+ text.stdout
211
+ .split('\n')
212
+ .map((line) => /^([A-Za-z]+):\s/.exec(line)?.[1])
213
+ .filter(Boolean),
214
+ );
215
+ expect(textKeys).toContain('name');
216
+ for (const key of textKeys) expect(jsonKeys).toContain(key);
197
217
  });
198
218
  });
@@ -0,0 +1,81 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file A parse failure reads like every other Astryx error in text mode.
5
+ *
6
+ * `astryx theme list --lang zh-Hans` printed Commander's own line —
7
+ * `error: option '--lang <locale>' argument 'zh-Hans' is invalid…` — while
8
+ * every other CLI error prints `Error: …`. `--json` was already correct
9
+ * (ERR_INVALID_LANG), so text and JSON disagreed on everything except the exit
10
+ * code. Commander writes that line before any Astryx code runs, so the shim is
11
+ * the only place that can speak for it.
12
+ */
13
+
14
+ import {describe, it, expect} from 'vitest';
15
+ import {Command} from 'commander';
16
+ import {runCli} from '../../../test-utils/run-cli.mjs';
17
+ import {installJsonShim} from './json-shim.mjs';
18
+
19
+ describe('a parse error prints the Astryx error format', () => {
20
+ it.each([
21
+ ['invalid --lang value', ['theme', 'list', '--lang', 'zh-Hans'], 'ERR_INVALID_LANG'],
22
+ ['invalid --detail value', ['theme', 'list', '--detail', 'nope'], 'ERR_INVALID_DETAIL'],
23
+ ['unknown option', ['component', '--bogus-flag'], 'ERR_INVALID_OPTION'],
24
+ ['missing argument', ['theme', 'build'], 'ERR_MISSING_ARGUMENT'],
25
+ ])('%s', async (_label, args, code) => {
26
+ const human = await runCli(args);
27
+
28
+ expect(human.status).toBe(1);
29
+ expect(human.stderr).toContain('Error: ');
30
+ // Commander's own lowercase line must not reach the user.
31
+ expect(human.stderr).not.toMatch(/(^|\n)error: /);
32
+
33
+ // --json is unchanged, and the two modes agree on the exit code.
34
+ const json = await runCli(['--json', ...args]);
35
+ expect(json.status).toBe(human.status);
36
+ expect(JSON.parse(json.stdout).code).toBe(code);
37
+ expect(json.stderr).toBe('');
38
+ });
39
+
40
+ it('carries Commander\'s explanation, not just a generic line', async () => {
41
+ const {stderr} = await runCli(['theme', 'list', '--lang', 'zh-Hans']);
42
+ expect(stderr).toContain("'zh-Hans' is invalid");
43
+ expect(stderr).toContain('en, zh, dense');
44
+ });
45
+
46
+ it('leaves --help and --version at exit 0 with nothing on stderr', async () => {
47
+ for (const args of [['--help'], ['theme', '--help']]) {
48
+ const r = await runCli(args);
49
+ expect(r.status).toBe(0);
50
+ expect(r.stderr).toBe('');
51
+ }
52
+ });
53
+
54
+ // Commander writes help through the same stderr channel when it shows help
55
+ // BECAUSE the invocation failed, so suppressing that channel wholesale would
56
+ // have taken the help with it. Driven on a throwaway program rather than a
57
+ // real command, so the guard outlives whichever command happens to have a
58
+ // subcommand group today.
59
+ it('still lets help reach stderr when help IS the failure report', () => {
60
+ const program = new Command('probe');
61
+ const group = program.command('group');
62
+ group.command('leaf').action(() => {});
63
+ installJsonShim(program);
64
+
65
+ /** @type {string[]} */
66
+ const written = [];
67
+ const original = process.stderr.write;
68
+ // @ts-expect-error test double for the write signature
69
+ process.stderr.write = str => {
70
+ written.push(String(str));
71
+ return true;
72
+ };
73
+ try {
74
+ group.outputHelp({error: true});
75
+ } finally {
76
+ process.stderr.write = original;
77
+ }
78
+
79
+ expect(written.join('')).toMatch(/Usage: probe group/);
80
+ });
81
+ });
@@ -78,8 +78,9 @@ export function detectStylingSystem(targetDir: string): "stylex" | "tailwind" |
78
78
  * Generate the agent cheat sheet from live CLI metadata.
79
79
  *
80
80
  * Structured as: workflow (behavioral) → rules (error prevention) → CLI reference.
81
- * Templates are positioned first in the workflow to teach agents the
82
- * "look at reference code" reflex before writing any UI.
81
+ * Templates lead the workflow: every page starts from a scaffolded template,
82
+ * because the template already carries the frame and spacing that an agent
83
+ * composing from components would have to re-derive, and usually gets wrong.
83
84
  *
84
85
  * `stylingSystem` tailors the custom-styling guidance to what the project has
85
86
  * configured (see {@link detectStylingSystem}) so the agent never reaches for a
@@ -182,6 +183,8 @@ export function removeXdsBlock(filePath: string, { deleteIfEmpty }?: {
182
183
  /**
183
184
  * Remove Astryx section from all known agent doc files.
184
185
  * @param {string} targetDir
186
+ * @throws {PathSafetyError} `ERR_PATH_TRAVERSAL` when a file it would change
187
+ * resolves outside `targetDir`; nothing is changed.
185
188
  */
186
189
  export function removeAgentDocs(targetDir: string): void;
187
190
  /**
@@ -208,6 +211,8 @@ export function removeAgentDocs(targetDir: string): void;
208
211
  * @param {string} [options.renderedBlock] - Fully rendered expected block. Init
209
212
  * and upgrade pass one shared block to every target.
210
213
  * @returns {string[]} List of files written
214
+ * @throws {import('../fs/path-safety.mjs').PathSafetyError} when a file it would
215
+ * write resolves outside `targetDir`; nothing is written.
211
216
  */
212
217
  export function installAgentDocs(targetDir: string, { zh, lang, agent, paths, onlyReplace, topics, renderedBlock, }?: {
213
218
  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,
@@ -353,8 +354,9 @@ export function detectStylingSystem(targetDir) {
353
354
  * Generate the agent cheat sheet from live CLI metadata.
354
355
  *
355
356
  * Structured as: workflow (behavioral) → rules (error prevention) → CLI reference.
356
- * Templates are positioned first in the workflow to teach agents the
357
- * "look at reference code" reflex before writing any UI.
357
+ * Templates lead the workflow: every page starts from a scaffolded template,
358
+ * because the template already carries the frame and spacing that an agent
359
+ * composing from components would have to re-derive, and usually gets wrong.
358
360
  *
359
361
  * `stylingSystem` tailors the custom-styling guidance to what the project has
360
362
  * configured (see {@link detectStylingSystem}) so the agent never reaches for a
@@ -427,17 +429,19 @@ export function generateCompressedIndex(
427
429
  lines.push(' import "@astryxdesign/core/astryx.css";');
428
430
  lines.push('');
429
431
 
430
- // Workflow — `build` is the front door; discover before writing UI.
431
- lines.push("WORKFLOW — discover, don't guess. Before writing UI:");
432
- lines.push('1. `astryx build "<idea>"` — START HERE: returns a kit (closest [page] + [block]s + [component]s). No args = full playbook.');
433
- lines.push('2. `astryx template <name> [--skeleton]` — scaffold the [page]/[block]s it named, or study their layout. Templates are reference code.');
434
- lines.push('3. `astryx component <Name>` — props + examples for every component you use.');
432
+ // Workflow — `build` is the front door, and every page starts from the
433
+ // template it names.
434
+ lines.push('WORKFLOW — start every page from a template. Never lay out a page from scratch:');
435
+ lines.push('1. `astryx build "<idea>"` — START HERE: names the [page] template to start from (always one: the closest match, or the app shell), two other templates, and the [block]s + [component]s for parts it lacks. No args = full playbook.');
436
+ lines.push('2. `astryx template <name> <path>` — scaffold that template into your project. Keep its frame, gap and padding; replace its data, copy and sections; delete sections you do not need.');
437
+ lines.push('3. `astryx template <Block>` for a part the template lacks; `astryx component <Name>` for props + examples before you use or change a component.');
438
+ lines.push('Changing a page you already have? Keep it: skip step 2 and add blocks and components inside its sections.');
435
439
  lines.push('');
436
440
 
437
441
  // Rules — the top error-preventers.
438
442
  lines.push('RULES:');
439
443
  lines.push('- No <div> — components do all layout/spacing, page frame included.');
440
- lines.push('- Frame first: read `astryx docs layout` before writing any page or screen — page frame, region widths, breakpoint behavior.');
444
+ lines.push('- Frame first: the template you scaffold sets the page frame. Read `astryx docs layout` before you change it — region widths, breakpoint behavior.');
441
445
  lines.push('- Dense data = rows (Table, List/Item), never Card-wrapped list items; Card is for standalone widgets. Status = StatusDot/Token; Badge = counts only.');
442
446
  // Styling guidance tailored to the project's configured system — never
443
447
  // recommend a path that isn't compiled here (xstyle needs the StyleX compiler;
@@ -462,7 +466,7 @@ export function generateCompressedIndex(
462
466
  css: 'replace any raw <div>/<span> layout, imported .css/@apply, or hardcoded value (#hex, 16px) with the component or a token (var(--color-*|--spacing-*|…))',
463
467
  };
464
468
  lines.push(
465
- `- SELF-CHECK before you finish: re-read the file and ${selfCheckFix[stylingSystem] ?? selfCheckFix.css}. If unsure a component/prop exists, run \`astryx component <Name>\` / \`astryx search "<thing>"\`; don't hand-roll CSS.`,
469
+ `- SELF-CHECK before you finish: re-read the file and ${selfCheckFix[stylingSystem] ?? selfCheckFix.css}. Confirm the page kept its template's frame, gap and padding. If unsure a component/prop exists, run \`astryx component <Name>\` / \`astryx search "<thing>"\`; don't hand-roll CSS.`,
466
470
  );
467
471
  lines.push('');
468
472
 
@@ -485,6 +489,7 @@ export function generateCompressedIndex(
485
489
  if (resolvedTopics.length > 0) {
486
490
  lines.push(` docs <topic> ${resolvedTopics.join(', ')}`);
487
491
  }
492
+ lines.push(' docs cli commands, API reference, integration authoring (one level at a time)');
488
493
  lines.push(' swizzle <Name> eject component source for deep customization');
489
494
  lines.push(' upgrade --apply run after any Astryx or integration dependency bump');
490
495
  const appendCount = agentDocs.reduce(
@@ -705,12 +710,65 @@ export function removeXdsBlock(filePath, {deleteIfEmpty = false} = {}) {
705
710
  return true;
706
711
  }
707
712
 
713
+ /**
714
+ * Whether an agent-doc file already carries a managed-block marker.
715
+ * @param {string} filePath
716
+ * @returns {boolean}
717
+ */
718
+ function hasManagedMarker(filePath) {
719
+ try {
720
+ const content = fs.readFileSync(filePath, 'utf-8');
721
+ return content.includes(MARKER_START) || content.includes(LEGACY_MARKER_START);
722
+ } catch {
723
+ return false;
724
+ }
725
+ }
726
+
727
+ /**
728
+ * Whether `removeXdsBlock` would change `filePath`: it holds one well-formed
729
+ * managed block.
730
+ * @param {string} filePath
731
+ * @returns {boolean}
732
+ */
733
+ function hasRemovableBlock(filePath) {
734
+ try {
735
+ return findManagedBlock(fs.readFileSync(filePath, 'utf-8')) != null;
736
+ } catch {
737
+ return false;
738
+ }
739
+ }
740
+
741
+ /**
742
+ * Every file a run writes must resolve inside `targetDir`, symlinks
743
+ * included. Checked for the whole write set before the first write, so an
744
+ * escape writes nothing.
745
+ * @param {string} targetDir
746
+ * @param {Iterable<string>} relPaths
747
+ */
748
+ function assertTargetsWithin(targetDir, relPaths) {
749
+ for (const p of relPaths) {
750
+ assertWithin(p, targetDir, {label: 'agent docs path'});
751
+ }
752
+ }
753
+
708
754
  /**
709
755
  * Remove Astryx section from all known agent doc files.
710
756
  * @param {string} targetDir
757
+ * @throws {PathSafetyError} `ERR_PATH_TRAVERSAL` when a file it would change
758
+ * resolves outside `targetDir`; nothing is changed.
711
759
  */
712
760
  export function removeAgentDocs(targetDir) {
713
761
  const allPaths = discoverAgentDocs(targetDir);
762
+ try {
763
+ assertTargetsWithin(
764
+ targetDir,
765
+ allPaths.filter(p => hasRemovableBlock(path.join(targetDir, p))),
766
+ );
767
+ } catch (err) {
768
+ // The code reaches the error envelope as is, so it must be registered.
769
+ if (!(err instanceof PathSafetyError)) throw err;
770
+ throw new PathSafetyError(err.message, ERROR_CODES.ERR_PATH_TRAVERSAL);
771
+ }
714
772
 
715
773
  for (const p of allPaths) {
716
774
  const filePath = path.join(targetDir, p);
@@ -718,9 +776,9 @@ export function removeAgentDocs(targetDir) {
718
776
  const deleteIfEmpty = p === AGENTS_MD || p === CLAUDE_DIR_MD;
719
777
  if (removeXdsBlock(filePath, {deleteIfEmpty})) {
720
778
  if (!fs.existsSync(filePath)) {
721
- humanLog(`✓ Removed empty ${p}`);
779
+ humanLog(`[ok] Removed empty ${p}`);
722
780
  } else {
723
- humanLog(`✓ Removed design system section from ${p}`);
781
+ humanLog(`[ok] Removed design system section from ${p}`);
724
782
  }
725
783
  }
726
784
  }
@@ -750,6 +808,8 @@ export function removeAgentDocs(targetDir) {
750
808
  * @param {string} [options.renderedBlock] - Fully rendered expected block. Init
751
809
  * and upgrade pass one shared block to every target.
752
810
  * @returns {string[]} List of files written
811
+ * @throws {import('../fs/path-safety.mjs').PathSafetyError} when a file it would
812
+ * write resolves outside `targetDir`; nothing is written.
753
813
  */
754
814
  export function installAgentDocs(
755
815
  targetDir,
@@ -806,6 +866,7 @@ export function installAgentDocs(
806
866
  // Agent preset
807
867
  if (agent) {
808
868
  const {inject, create} = resolveAgentPaths(targetDir, agent);
869
+ assertTargetsWithin(targetDir, [...inject, ...create]);
809
870
  for (const p of inject) {
810
871
  injectXdsBlock(path.join(targetDir, p), compressedIndex);
811
872
  written.push(p);
@@ -833,6 +894,14 @@ export function installAgentDocs(
833
894
  if (existing.length > 0) {
834
895
  const wrappers = discoverAgentDocWrappers(targetDir, existing);
835
896
  const targets = existing.filter(p => !wrappers.has(p));
897
+ // A refresh skips unmarked files and a wrapper is only written when it
898
+ // carries a block, so only the files this run writes are checked.
899
+ /** @param {string} p */
900
+ const marked = p => hasManagedMarker(path.join(targetDir, p));
901
+ assertTargetsWithin(targetDir, [
902
+ ...targets.filter(p => !onlyReplace || marked(p)),
903
+ ...[...wrappers].filter(marked),
904
+ ]);
836
905
 
837
906
  for (const p of targets) {
838
907
  const didWrite = injectXdsBlock(path.join(targetDir, p), compressedIndex, {onlyReplace});
@@ -860,6 +929,7 @@ export function installAgentDocs(
860
929
  if (onlyReplace) return written;
861
930
 
862
931
  const defaultPath = AGENTS_MD;
932
+ assertTargetsWithin(targetDir, [defaultPath]);
863
933
  injectXdsBlock(path.join(targetDir, defaultPath), compressedIndex, {
864
934
  createIfMissing: true,
865
935
  header: `# AGENTS.md\n\nProject-specific guidance for AI coding agents.`,