@astryxdesign/cli 0.6.3-canary.98a2e4e → 0.6.3-canary.9c4d44d

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 (612) hide show
  1. package/README.md +117 -79
  2. package/api/blog/blog.doc.mjs +1 -0
  3. package/api/build/_adapter.d.mts +50 -0
  4. package/api/build/_adapter.mjs +60 -0
  5. package/api/build/build.doc.mjs +16 -9
  6. package/api/build/build.test.mjs +197 -8
  7. package/api/build/build.type.d.mts +91 -2
  8. package/api/build/build.type.mjs +52 -8
  9. package/api/build/help/help.d.mts +12 -5
  10. package/api/build/help/help.mjs +69 -6
  11. package/api/build/kit/kit.d.mts +4 -1
  12. package/api/build/kit/kit.mjs +165 -49
  13. package/api/build/kit/rank.d.mts +44 -0
  14. package/api/build/kit/rank.mjs +432 -0
  15. package/api/build/kit/rank.test.mjs +196 -0
  16. package/api/component/_adapter.d.mts +6 -12
  17. package/api/component/_adapter.mjs +20 -10
  18. package/api/component/component.doc.mjs +13 -3
  19. package/api/component/component.mjs +91 -14
  20. package/api/component/component.test.mjs +38 -0
  21. package/api/component/component.type.d.mts +22 -11
  22. package/api/component/component.type.mjs +32 -24
  23. package/api/component/detail/blocks/blocks.d.mts +2 -1
  24. package/api/component/detail/blocks/blocks.mjs +4 -3
  25. package/api/component/list/list.d.mts +0 -5
  26. package/api/component/list/list.mjs +40 -11
  27. package/api/discover/_adapter.d.mts +114 -6
  28. package/api/discover/_adapter.mjs +372 -17
  29. package/api/discover/_adapter.test.mjs +215 -0
  30. package/api/discover/_catalog-view.d.mts +115 -0
  31. package/api/discover/_catalog-view.mjs +203 -0
  32. package/api/discover/_catalog-view.test.mjs +128 -0
  33. package/api/discover/detail/detail.d.mts +18 -6
  34. package/api/discover/detail/detail.mjs +67 -13
  35. package/api/discover/detail/detail.test.mjs +85 -0
  36. package/api/discover/detail/item/item.d.mts +26 -0
  37. package/api/discover/detail/item/item.mjs +78 -0
  38. package/api/discover/detail/item/item.test.mjs +73 -0
  39. package/api/discover/discover.d.mts +3 -9
  40. package/api/discover/discover.doc.mjs +62 -18
  41. package/api/discover/discover.mjs +220 -36
  42. package/api/discover/discover.test.mjs +11 -2
  43. package/api/discover/discover.type.d.mts +150 -11
  44. package/api/discover/discover.type.mjs +107 -17
  45. package/api/discover/list/list.d.mts +20 -6
  46. package/api/discover/list/list.mjs +45 -12
  47. package/api/discover/list/list.test.mjs +46 -0
  48. package/api/discover/search/search.d.mts +18 -16
  49. package/api/discover/search/search.mjs +102 -56
  50. package/api/discover/search/search.test.mjs +144 -10
  51. package/api/docs/_adapter.d.mts +268 -47
  52. package/api/docs/_adapter.mjs +970 -149
  53. package/api/docs/compiled-topics.test.mjs +78 -0
  54. package/api/docs/detail/detail.d.mts +0 -15
  55. package/api/docs/detail/detail.mjs +22 -78
  56. package/api/docs/detail/section/section.mjs +39 -24
  57. package/api/docs/detail/section/section.test.mjs +19 -9
  58. package/api/docs/docs.d.mts +5 -3
  59. package/api/docs/docs.doc.mjs +39 -17
  60. package/api/docs/docs.mjs +44 -8
  61. package/api/docs/docs.test.mjs +158 -4
  62. package/api/docs/docs.type.d.mts +185 -6
  63. package/api/docs/docs.type.mjs +127 -13
  64. package/api/docs/index/index.mjs +15 -6
  65. package/api/docs/index/index.test.mjs +1 -1
  66. package/api/docs/integration-tree.test.mjs +555 -0
  67. package/api/docs/integrationDocs.test.mjs +14 -14
  68. package/api/docs/list/list.mjs +28 -12
  69. package/api/docs/node/node.d.mts +43 -0
  70. package/api/docs/node/node.mjs +192 -0
  71. package/api/docs/reference-blocks.test.mjs +406 -0
  72. package/api/doctor/doctor.d.mts +54 -4
  73. package/api/doctor/doctor.doc.mjs +1 -0
  74. package/api/doctor/doctor.mjs +332 -21
  75. package/api/doctor/doctor.test.mjs +420 -7
  76. package/api/gap-report/gap-report.doc.mjs +8 -4
  77. package/api/hook/_adapter.mjs +19 -5
  78. package/api/hook/hook.doc.mjs +1 -0
  79. package/api/hook/hook.type.d.mts +3 -3
  80. package/api/hook/hook.type.mjs +11 -11
  81. package/api/hook/list/list.d.mts +1 -1
  82. package/api/hook/list/list.mjs +69 -17
  83. package/api/index.d.mts +1 -1
  84. package/api/index.mjs +1 -0
  85. package/api/init/init.doc.mjs +6 -1
  86. package/api/init/init.test.mjs +41 -1
  87. package/api/init/remove/remove.mjs +1 -1
  88. package/api/init/run/run.mjs +20 -10
  89. package/api/integration/add-contribution.component-names.test.mjs +120 -0
  90. package/api/integration/add-contribution.d.mts +2 -1
  91. package/api/integration/add-contribution.mjs +130 -15
  92. package/api/integration/add-contribution.test.mjs +258 -7
  93. package/api/integration/add-helpers.d.mts +5 -2
  94. package/api/integration/add-helpers.mjs +36 -9
  95. package/api/integration/add-theme.mjs +34 -64
  96. package/api/integration/add-theme.test.mjs +105 -21
  97. package/api/integration/authoring-checks.mjs +138 -28
  98. package/api/integration/authoring-checks.test.mjs +179 -7
  99. package/api/integration/authoring-checks.type.mjs +6 -1
  100. package/api/integration/integration-authoring.type.d.mts +2 -0
  101. package/api/integration/integration-authoring.type.mjs +2 -0
  102. package/api/integration/integration-block-exports.test.mjs +10 -6
  103. package/api/integration/integrationAdd.doc.mjs +14 -4
  104. package/api/integration/integrationAddAgentDoc.doc.mjs +1 -0
  105. package/api/integration/integrationAddCodemod.doc.mjs +1 -0
  106. package/api/integration/integrationAddComponent.doc.mjs +2 -1
  107. package/api/integration/integrationAddDoc.doc.mjs +8 -1
  108. package/api/integration/integrationAddTemplate.doc.mjs +1 -0
  109. package/api/integration/integrationAddTheme.doc.mjs +6 -5
  110. package/api/integration/integrationComponentConflicts.doc.mjs +1 -0
  111. package/api/integration/integrationDocConflicts.doc.mjs +2 -1
  112. package/api/integration/integrationPackCheck.doc.mjs +2 -1
  113. package/api/integration/integrationTemplateConflicts.doc.d.mts +3 -0
  114. package/api/integration/integrationTemplateConflicts.doc.mjs +4 -0
  115. package/api/integration/pack-check.mjs +83 -7
  116. package/api/integration/pack-check.test.mjs +387 -47
  117. package/api/integration/pack-check.type.d.mts +26 -2
  118. package/api/integration/pack-check.type.mjs +14 -1
  119. package/api/integration/summarizeIssues.doc.mjs +1 -0
  120. package/api/integration/template-conflict-compatibility.test.mjs +73 -0
  121. package/api/integration/validate-integration-fixes.test.mjs +1389 -0
  122. package/api/integration/validate-integration.mjs +52 -102
  123. package/api/integration/validate-integration.test.mjs +179 -26
  124. package/api/integration/validate-unread-theme-folders.test.mjs +110 -0
  125. package/api/integration/validateIntegration.doc.mjs +3 -2
  126. package/api/json/assertResponse.doc.mjs +1 -0
  127. package/api/json/envelope-types.test.mjs +76 -0
  128. package/api/json/index.ts +2 -0
  129. package/api/json/isError.doc.mjs +1 -0
  130. package/api/json/parseResponse.doc.mjs +3 -2
  131. package/api/layout/_adapter.mjs +20 -5
  132. package/api/layout/expand/expand.mjs +7 -5
  133. package/api/layout/expand/expand.path-safety.test.mjs +53 -0
  134. package/api/layout/grammar/grammar.mjs +2 -1
  135. package/api/layout/layoutCheck.doc.mjs +1 -0
  136. package/api/layout/layoutExpand.doc.mjs +2 -1
  137. package/api/layout/layoutGrammar.doc.mjs +1 -0
  138. package/api/search/search-return-type.test.mjs +54 -0
  139. package/api/search/search.d.mts +61 -10
  140. package/api/search/search.doc.mjs +8 -2
  141. package/api/search/search.mjs +471 -83
  142. package/api/search/search.test.mjs +124 -1
  143. package/api/search/search.type.d.mts +14 -2
  144. package/api/search/search.type.mjs +5 -2
  145. package/api/swizzle/copy/copy.mjs +28 -11
  146. package/api/swizzle/swizzle.doc.mjs +2 -1
  147. package/api/swizzle/swizzle.type.d.mts +2 -2
  148. package/api/swizzle/swizzle.type.mjs +2 -2
  149. package/api/template/copy/copy.mjs +17 -23
  150. package/api/template/copy/copy.test.mjs +17 -0
  151. package/api/template/list/list.mjs +1 -0
  152. package/api/template/table-floating-bulk-actions.test.mjs +66 -0
  153. package/api/template/template-integration.test.mjs +1072 -3
  154. package/api/template/template-suffix.test.mjs +41 -21
  155. package/api/template/template.doc.mjs +30 -8
  156. package/api/template/template.mjs +45 -8
  157. package/api/template/template.type.d.mts +11 -13
  158. package/api/template/template.type.mjs +15 -14
  159. package/api/theme/_adapter.d.mts +2 -3
  160. package/api/theme/_adapter.mjs +4 -5
  161. package/api/theme/add/add.binary.test.mjs +84 -0
  162. package/api/theme/add/add.mjs +31 -22
  163. package/api/theme/add/add.rollback.test.mjs +158 -0
  164. package/api/theme/add/add.staging.test.mjs +83 -0
  165. package/api/theme/add/add.test.mjs +14 -1
  166. package/api/theme/build/build.family.test.mjs +7 -12
  167. package/api/theme/build/build.mjs +140 -59
  168. package/api/theme/build/build.public-component-vars.test.mjs +1 -1
  169. package/api/theme/build/build.receipt-doc.test.mjs +111 -0
  170. package/api/theme/build/build.rollback.test.mjs +148 -0
  171. package/api/theme/build/build.test.mjs +127 -0
  172. package/api/theme/build/font-warning.mjs +3 -3
  173. package/api/theme/build/font-warning.test.mjs +5 -2
  174. package/api/theme/generateTonalPalette.doc.mjs +1 -0
  175. package/api/theme/integration-themes.test.mjs +39 -28
  176. package/api/theme/list/list.test.mjs +19 -20
  177. package/api/theme/listThemes.doc.mjs +6 -5
  178. package/api/theme/palette/generate/generate.mjs +8 -3
  179. package/api/theme/palette/generate/generate.test.mjs +96 -0
  180. package/api/theme/palette/generate/generator.d.mts +10 -13
  181. package/api/theme/palette/generate/generator.mjs +15 -4
  182. package/api/theme/palette/generate/generator.test.mjs +10 -0
  183. package/api/theme/template/template.mjs +11 -2
  184. package/api/theme/template/template.test.mjs +20 -0
  185. package/api/theme/theme.type.d.mts +170 -11
  186. package/api/theme/theme.type.mjs +94 -27
  187. package/api/theme/themeAdd.doc.mjs +4 -3
  188. package/api/theme/themeBuild.doc.mjs +8 -4
  189. package/api/theme/themeList.doc.mjs +6 -3
  190. package/api/theme/themeListAvailable.doc.mjs +5 -3
  191. package/api/theme/themePaletteGenerate.doc.mjs +1 -0
  192. package/api/theme/themeTargets.doc.mjs +1 -0
  193. package/api/theme/themeTemplate.doc.mjs +6 -2
  194. package/api/upgrade/_adapter.d.mts +32 -5
  195. package/api/upgrade/_adapter.mjs +124 -73
  196. package/api/upgrade/list/list.mjs +2 -1
  197. package/api/upgrade/list/list.test.mjs +73 -0
  198. package/api/upgrade/provider-agreement.test.mjs +152 -0
  199. package/api/upgrade/run/run.mjs +356 -59
  200. package/api/upgrade/status/status.mjs +2 -2
  201. package/api/upgrade/upgrade.doc.mjs +8 -2
  202. package/api/upgrade/upgrade.type.d.mts +42 -4
  203. package/api/upgrade/upgrade.type.mjs +26 -10
  204. package/assets/codemods/__tests__/runner.test.mjs +330 -8
  205. package/assets/codemods/integration-discovery.mjs +48 -4
  206. package/assets/codemods/integration-discovery.test.mjs +73 -0
  207. package/assets/codemods/integration-runner.mjs +56 -4
  208. package/assets/codemods/integration-runner.protection.test.mjs +153 -0
  209. package/assets/codemods/run-codemod.mjs +177 -34
  210. package/assets/codemods/runner.mjs +350 -102
  211. package/assets/codemods/term-log.mjs +32 -8
  212. package/assets/codemods/term-log.test.mjs +19 -1
  213. package/assets/codemods/transform-prop.mjs +109 -0
  214. package/assets/codemods/transform-prop.test.mjs +95 -0
  215. package/assets/codemods/transforms/next/__tests__/migrate-native-picker-to-presentation.test.mjs +63 -0
  216. package/assets/codemods/transforms/next/__tests__/migrate-theme-catalog-to-descriptors.test.mjs +220 -0
  217. package/assets/codemods/transforms/next/index.mjs +19 -1
  218. package/assets/codemods/transforms/next/migrate-native-picker-to-presentation.mjs +148 -0
  219. package/assets/codemods/transforms/next/migrate-theme-catalog-to-descriptors.mjs +141 -0
  220. package/assets/codemods/transforms/v0.0.14/__tests__/rename-status-variants.test.mjs +86 -165
  221. package/assets/codemods/transforms/v0.0.14/rename-status-variants.mjs +72 -210
  222. package/assets/codemods/transforms/v0.1.8/__tests__/rename-avatar-size-scale.test.mjs +83 -115
  223. package/assets/codemods/transforms/v0.1.8/rename-avatar-size-scale.mjs +57 -186
  224. package/assets/codemods/transforms/v0.3.0/__tests__/unwrap-authoring-factories.test.mjs +27 -5
  225. package/assets/codemods/transforms/v0.3.0/unwrap-authoring-factories.mjs +20 -5
  226. package/assets/codemods/transforms/v0.6.0/__tests__/next-codemods.test.mjs +47 -0
  227. package/assets/codemods/transforms/v0.6.0/rename-resizable-pixel-bounds.mjs +57 -10
  228. package/assets/docs/getting-started.doc.mjs +2 -2
  229. package/assets/docs/internationalization.doc.mjs +7 -5
  230. package/assets/docs/layout.doc.dense.mjs +2 -2
  231. package/assets/docs/layout.doc.mjs +1 -1
  232. package/assets/docs/principles.doc.mjs +6 -6
  233. package/assets/docs/styling-libraries.doc.mjs +4 -4
  234. package/assets/docs/styling.doc.mjs +4 -4
  235. package/assets/docs/theme.doc.mjs +5 -5
  236. package/assets/docs/tokens.doc.mjs +1 -1
  237. package/assets/docs/tree/api.doc.mjs +30 -0
  238. package/assets/docs/tree/cli.doc.mjs +23 -0
  239. package/assets/docs/tree/commands.doc.mjs +25 -0
  240. package/assets/docs/{cli-integrations.doc.mjs → tree/integrations.doc.mjs} +95 -39
  241. package/assets/docs/tree/integrations.test.mjs +62 -0
  242. package/assets/docs/tree/writing-docs.doc.mjs +286 -0
  243. package/assets/docs/working-with-ai.doc.mjs +4 -4
  244. package/assets/templates/blocks/components/CheckboxList/CheckboxListSelectAllPattern.doc.mjs +1 -1
  245. package/assets/templates/blocks/components/CheckboxList/CheckboxListSelectAllPattern.tsx +1 -3
  246. package/assets/templates/blocks/components/DateInput/DateInputDateRange.tsx +1 -1
  247. package/assets/templates/blocks/components/InternationalizationProvider/InternationalizationProvider01ShippedLocale.tsx +1 -1
  248. package/assets/templates/blocks/components/Item/ItemDocumentTabs.doc.mjs +14 -0
  249. package/assets/templates/blocks/components/Item/ItemDocumentTabs.tsx +100 -0
  250. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaInlineRail.doc.mjs +14 -0
  251. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaInlineRail.tsx +61 -0
  252. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaOverscrollChaining.doc.mjs +14 -0
  253. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaOverscrollChaining.tsx +126 -0
  254. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaShowcase.doc.mjs +15 -0
  255. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaShowcase.tsx +86 -0
  256. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyGroupHeaders.doc.mjs +14 -0
  257. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyGroupHeaders.tsx +99 -0
  258. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyPassthrough.doc.mjs +14 -0
  259. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyPassthrough.tsx +122 -0
  260. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaTwoAxisBoard.doc.mjs +14 -0
  261. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaTwoAxisBoard.tsx +95 -0
  262. package/assets/templates/blocks/components/Table/TableBulkActionsTable.doc.mjs +14 -0
  263. package/assets/templates/blocks/components/Table/TableBulkActionsTable.tsx +71 -0
  264. package/assets/templates/blocks/components/Table/TableFloatingBulkActionsTable.doc.mjs +19 -0
  265. package/assets/templates/blocks/components/Table/TableFloatingBulkActionsTable.tsx +139 -0
  266. package/assets/templates/blocks/components/TimeInput/TimeInputConstrained.tsx +1 -0
  267. package/assets/templates/pages/table-tree/page.tsx +1704 -0
  268. package/assets/templates/pages/table-tree/template.doc.mjs +12 -0
  269. package/assets/templates/themes/butter/butterTheme.doc.mjs +11 -0
  270. package/assets/templates/themes/chocolate/chocolateTheme.doc.mjs +11 -0
  271. package/assets/templates/themes/gothic/gothicTheme.doc.mjs +11 -0
  272. package/assets/templates/themes/matcha/matchaTheme.doc.mjs +11 -0
  273. package/assets/templates/themes/neutral/neutralTheme.doc.mjs +11 -0
  274. package/assets/templates/themes/stone/stoneTheme.doc.mjs +11 -0
  275. package/assets/templates/themes/y2k/y2kTheme.doc.mjs +11 -0
  276. package/authoring/_shared/contract.ts +22 -0
  277. package/authoring/codemod/codemod.doc.mjs +7 -2
  278. package/authoring/codemod/parse.d.mts +8 -8
  279. package/authoring/codemod/parse.mjs +8 -6
  280. package/authoring/codemod/type.ts +12 -0
  281. package/authoring/config/config.doc.mjs +10 -2
  282. package/authoring/config/debug-composition.test.mjs +92 -0
  283. package/authoring/config/parse.d.mts +15 -13
  284. package/authoring/config/parse.mjs +27 -8
  285. package/authoring/config/parse.test.mjs +8 -0
  286. package/authoring/config/type.ts +29 -6
  287. package/authoring/debug/debug.doc.d.mts +11 -0
  288. package/authoring/debug/debug.doc.mjs +182 -0
  289. package/authoring/debug/parse.d.mts +7 -7
  290. package/authoring/debug/parse.mjs +3 -3
  291. package/authoring/discover/discover.doc.d.mts +13 -0
  292. package/authoring/discover/discover.doc.mjs +138 -0
  293. package/authoring/discover/parse.d.mts +24 -0
  294. package/authoring/discover/parse.mjs +128 -0
  295. package/authoring/discover/parse.test.mjs +124 -0
  296. package/authoring/discover/type.ts +87 -0
  297. package/authoring/doctypes/_schema.d.mts +240 -128
  298. package/authoring/doctypes/_schema.mjs +198 -16
  299. package/authoring/doctypes/base/graph-fields.doc.mjs +15 -6
  300. package/authoring/doctypes/base/type.ts +9 -4
  301. package/authoring/doctypes/command/command.doc.mjs +4 -3
  302. package/authoring/doctypes/command/parse.d.mts +2 -2
  303. package/authoring/doctypes/command/parse.mjs +1 -1
  304. package/authoring/doctypes/command/type.ts +4 -4
  305. package/authoring/doctypes/component/component.doc.mjs +5 -2
  306. package/authoring/doctypes/component/parse.d.mts +2 -2
  307. package/authoring/doctypes/component/parse.mjs +1 -1
  308. package/authoring/doctypes/component/type.ts +3 -3
  309. package/authoring/doctypes/doctypes-new.test.mjs +48 -6
  310. package/authoring/doctypes/enum/enum.doc.mjs +1 -1
  311. package/authoring/doctypes/enum/parse.d.mts +2 -2
  312. package/authoring/doctypes/enum/parse.mjs +1 -1
  313. package/authoring/doctypes/enum/type.ts +2 -2
  314. package/authoring/doctypes/function/function.doc.mjs +7 -2
  315. package/authoring/doctypes/function/parse.d.mts +2 -2
  316. package/authoring/doctypes/function/parse.mjs +1 -1
  317. package/authoring/doctypes/function/type.ts +4 -3
  318. package/authoring/doctypes/hook/hook.doc.mjs +4 -0
  319. package/authoring/doctypes/hook/parse.d.mts +2 -2
  320. package/authoring/doctypes/hook/parse.mjs +1 -1
  321. package/authoring/doctypes/hook/type.ts +3 -3
  322. package/authoring/doctypes/legacy.d.mts +6 -6
  323. package/authoring/doctypes/legacy.mjs +3 -3
  324. package/authoring/doctypes/load-contract.test.mjs +233 -0
  325. package/authoring/doctypes/namespace/namespace.doc.mjs +9 -9
  326. package/authoring/doctypes/namespace/parse.d.mts +2 -2
  327. package/authoring/doctypes/namespace/parse.mjs +1 -1
  328. package/authoring/doctypes/namespace/parse.test.mjs +23 -25
  329. package/authoring/doctypes/namespace/type.ts +6 -3
  330. package/authoring/doctypes/parse.d.mts +22 -20
  331. package/authoring/doctypes/parse.mjs +19 -14
  332. package/authoring/doctypes/reference/parse.d.mts +2 -2
  333. package/authoring/doctypes/reference/parse.mjs +1 -1
  334. package/authoring/doctypes/reference/reference.doc.mjs +39 -6
  335. package/authoring/doctypes/reference/type.ts +31 -14
  336. package/authoring/doctypes/schema/parse.d.mts +2 -2
  337. package/authoring/doctypes/schema/parse.mjs +1 -1
  338. package/authoring/doctypes/schema/schema.doc.mjs +1 -1
  339. package/authoring/doctypes/schema/type.ts +3 -4
  340. package/authoring/doctypes/template/parse.d.mts +94 -1
  341. package/authoring/doctypes/template/parse.mjs +37 -1
  342. package/authoring/doctypes/template/parse.test.mjs +18 -0
  343. package/authoring/doctypes/template/template.doc.mjs +13 -3
  344. package/authoring/doctypes/template/type.ts +12 -1
  345. package/authoring/doctypes/theme/parse.d.mts +35 -0
  346. package/authoring/doctypes/theme/parse.mjs +76 -0
  347. package/authoring/doctypes/theme/theme.doc.d.mts +9 -0
  348. package/authoring/doctypes/theme/theme.doc.mjs +79 -0
  349. package/authoring/doctypes/theme/type.ts +42 -0
  350. package/authoring/doctypes/types.ts +12 -11
  351. package/authoring/gap-report/gap-report.doc.d.mts +12 -0
  352. package/authoring/gap-report/gap-report.doc.mjs +183 -0
  353. package/authoring/gap-report/parse.d.mts +9 -9
  354. package/authoring/gap-report/parse.mjs +6 -6
  355. package/authoring/gap-report/type.ts +1 -1
  356. package/authoring/identity/identity.doc.mjs +3 -2
  357. package/authoring/identity/type.ts +10 -10
  358. package/authoring/index.d.mts +2 -0
  359. package/authoring/index.d.ts +32 -19
  360. package/authoring/index.mjs +3 -1
  361. package/authoring/integration/integration.doc.mjs +2 -2
  362. package/authoring/integration/parse.d.mts +2 -2
  363. package/authoring/integration/parse.mjs +1 -1
  364. package/authoring/integration/schema.d.mts +4 -4
  365. package/authoring/integration/schema.mjs +3 -3
  366. package/authoring/integration/type.ts +5 -11
  367. package/clients/cli/__tests__/cliManifest.test.ts +27 -29
  368. package/clients/cli/command-load-failure.test.mjs +83 -0
  369. package/clients/cli/commands/blog.doc.mjs +1 -1
  370. package/clients/cli/commands/blog.mjs +23 -8
  371. package/clients/cli/commands/blog.test.mjs +42 -1
  372. package/clients/cli/commands/build-theme.adaptations.test.mjs +100 -1
  373. package/clients/cli/commands/build-theme.ascii-output.test.mjs +161 -0
  374. package/clients/cli/commands/build-theme.flag-docs.test.mjs +110 -0
  375. package/clients/cli/commands/build-theme.mjs +16 -50
  376. package/clients/cli/commands/build-theme.path-safety.test.mjs +86 -1
  377. package/clients/cli/commands/build-theme.variants.test.mjs +76 -0
  378. package/clients/cli/commands/build.doc.mjs +16 -8
  379. package/clients/cli/commands/build.exit-codes-doc.test.mjs +50 -0
  380. package/clients/cli/commands/build.mjs +137 -114
  381. package/clients/cli/commands/build.playbook.test.mjs +75 -0
  382. package/clients/cli/commands/build.text-fields.test.mjs +81 -0
  383. package/clients/cli/commands/component/index.mjs +3 -8
  384. package/clients/cli/commands/component-ownership.test.mjs +3 -3
  385. package/clients/cli/commands/component-package.test.mjs +46 -0
  386. package/clients/cli/commands/component-resolution.test.mjs +21 -0
  387. package/clients/cli/commands/component.doc.mjs +1 -1
  388. package/clients/cli/commands/component.test.mjs +19 -0
  389. package/clients/cli/commands/detail-levels.test.mjs +2 -2
  390. package/clients/cli/commands/discover.broken-integration.test.mjs +52 -5
  391. package/clients/cli/commands/discover.components-flag.test.mjs +97 -0
  392. package/clients/cli/commands/discover.doc.mjs +55 -9
  393. package/clients/cli/commands/discover.mjs +393 -118
  394. package/clients/cli/commands/discover.sources.test.mjs +267 -0
  395. package/clients/cli/commands/discover.text-projection.test.mjs +103 -0
  396. package/clients/cli/commands/docs.doc.mjs +21 -9
  397. package/clients/cli/commands/docs.mjs +184 -70
  398. package/clients/cli/commands/docs.test.mjs +120 -16
  399. package/clients/cli/commands/doctor-integration-components.doc.mjs +1 -1
  400. package/clients/cli/commands/doctor-integration-docs.doc.mjs +3 -3
  401. package/clients/cli/commands/doctor-integration-templates.doc.mjs +15 -8
  402. package/clients/cli/commands/doctor-integration-validate.doc.mjs +1 -1
  403. package/clients/cli/commands/doctor-integration.doc.mjs +1 -1
  404. package/clients/cli/commands/doctor-integration.package-json.test.mjs +53 -0
  405. package/clients/cli/commands/doctor-integration.test.mjs +90 -8
  406. package/clients/cli/commands/doctor.doc.mjs +1 -1
  407. package/clients/cli/commands/doctor.mjs +59 -32
  408. package/clients/cli/commands/doctor.test.mjs +42 -0
  409. package/clients/cli/commands/gap-report.doc.mjs +17 -6
  410. package/clients/cli/commands/gap-report.test.mjs +72 -0
  411. package/clients/cli/commands/hook/index.mjs +7 -17
  412. package/clients/cli/commands/hook.doc.mjs +1 -1
  413. package/clients/cli/commands/hook.text-projection.test.mjs +45 -0
  414. package/clients/cli/commands/init.doc.mjs +20 -9
  415. package/clients/cli/commands/init.flag-help.test.mjs +153 -0
  416. package/clients/cli/commands/integration-add.controls.test.mjs +132 -0
  417. package/clients/cli/commands/integration-add.doc.mjs +32 -6
  418. package/clients/cli/commands/integration-authoring.test.mjs +13 -9
  419. package/clients/cli/commands/integration-pack.doc.mjs +1 -1
  420. package/clients/cli/commands/integration-real-world.test.mjs +3 -9
  421. package/clients/cli/commands/integration.doc.mjs +1 -1
  422. package/clients/cli/commands/integration.mjs +1 -0
  423. package/clients/cli/commands/interactive-guard.test.mjs +101 -24
  424. package/clients/cli/commands/json-contract.test.mjs +33 -0
  425. package/clients/cli/commands/layout-check.doc.mjs +15 -4
  426. package/clients/cli/commands/layout-expand.doc.mjs +22 -5
  427. package/clients/cli/commands/layout-grammar.doc.mjs +1 -1
  428. package/clients/cli/commands/layout.doc.mjs +3 -3
  429. package/clients/cli/commands/layout.mjs +21 -9
  430. package/clients/cli/commands/layout.path-help.test.mjs +33 -0
  431. package/clients/cli/commands/layout.stdin-cap.test.mjs +47 -0
  432. package/clients/cli/commands/layout.text-fields.test.mjs +39 -0
  433. package/clients/cli/commands/manifest.doc.mjs +1 -1
  434. package/clients/cli/commands/no-prompt-wording.test.mjs +94 -0
  435. package/clients/cli/commands/search.doc.mjs +7 -4
  436. package/clients/cli/commands/search.mjs +28 -9
  437. package/clients/cli/commands/search.test.mjs +75 -0
  438. package/clients/cli/commands/setup-nudge.test.mjs +6 -0
  439. package/clients/cli/commands/swizzle.doc.mjs +3 -2
  440. package/clients/cli/commands/swizzle.path-safety.test.mjs +42 -0
  441. package/clients/cli/commands/template.doc.mjs +52 -13
  442. package/clients/cli/commands/template.flag-help.test.mjs +117 -0
  443. package/clients/cli/commands/template.mjs +4 -91
  444. package/clients/cli/commands/template.path-help.test.mjs +40 -0
  445. package/clients/cli/commands/text-json-parity.test.mjs +719 -0
  446. package/clients/cli/commands/theme-add.doc.mjs +4 -3
  447. package/clients/cli/commands/theme-build.doc.mjs +8 -7
  448. package/clients/cli/commands/theme-list.doc.mjs +2 -2
  449. package/clients/cli/commands/theme-palette-generate.doc.mjs +3 -3
  450. package/clients/cli/commands/theme-palette-generate.test.mjs +19 -0
  451. package/clients/cli/commands/theme-palette.doc.mjs +1 -1
  452. package/clients/cli/commands/theme-targets.behavior.test.mjs +4 -3
  453. package/clients/cli/commands/theme-targets.doc.mjs +1 -1
  454. package/clients/cli/commands/theme-template.behavior.test.mjs +12 -0
  455. package/clients/cli/commands/theme-template.doc.mjs +2 -2
  456. package/clients/cli/commands/theme.doc.mjs +1 -1
  457. package/clients/cli/commands/upgrade.ascii-output.test.mjs +87 -0
  458. package/clients/cli/commands/upgrade.doc.mjs +20 -8
  459. package/clients/cli/commands/upgrade.file-protection.test.mjs +228 -0
  460. package/clients/cli/commands/upgrade.flag-help.test.mjs +188 -0
  461. package/clients/cli/commands/upgrade.hook-output.test.mjs +88 -0
  462. package/clients/cli/commands/upgrade.mjs +29 -7
  463. package/clients/cli/formatters/index.mjs +2 -0
  464. package/clients/cli/formatters/index.test.mjs +6 -0
  465. package/clients/cli/index.mjs +21 -30
  466. package/clients/cli/latest-version-env.test.mjs +50 -0
  467. package/clients/cli/lib/cli-error.test.mjs +7 -0
  468. package/clients/cli/lib/component-format.mjs +9 -9
  469. package/clients/cli/lib/component-format.test.mjs +1 -1
  470. package/clients/cli/lib/define-command.mjs +32 -6
  471. package/clients/cli/lib/doc-text-ascii.test.mjs +82 -0
  472. package/clients/cli/lib/exit-codes.test.mjs +97 -0
  473. package/clients/cli/lib/hook-format.mjs +19 -10
  474. package/clients/cli/lib/json-shim.mjs +38 -2
  475. package/clients/cli/lib/json-shim.test.mjs +83 -0
  476. package/clients/cli/lib/manifest.d.ts +2 -0
  477. package/clients/cli/lib/manifest.mjs +32 -2
  478. package/clients/cli/lib/manifest.test.mjs +17 -0
  479. package/foundation/agent-docs/agent-docs.d.mts +7 -2
  480. package/foundation/agent-docs/agent-docs.mjs +82 -12
  481. package/foundation/agent-docs/agent-docs.path-safety.test.mjs +266 -4
  482. package/foundation/agent-docs/agent-docs.test.mjs +19 -1
  483. package/foundation/config/integration-debug.test.mjs +28 -3
  484. package/foundation/config/project-themes.test.mjs +11 -19
  485. package/foundation/config/project.d.mts +20 -11
  486. package/foundation/config/project.mjs +246 -89
  487. package/foundation/config/project.test.mjs +270 -21
  488. package/foundation/discovery/authoring-self-docs.d.mts +18 -0
  489. package/foundation/discovery/authoring-self-docs.mjs +38 -15
  490. package/foundation/discovery/authoring-self-docs.test.mjs +87 -12
  491. package/foundation/discovery/authoring-surface.d.mts +74 -0
  492. package/foundation/discovery/authoring-surface.mjs +525 -0
  493. package/foundation/discovery/authoring-surface.test.mjs +392 -0
  494. package/foundation/discovery/cli-self-docs.d.mts +119 -0
  495. package/foundation/discovery/cli-self-docs.mjs +490 -0
  496. package/foundation/discovery/cli-self-docs.test.mjs +375 -0
  497. package/foundation/discovery/component-discovery.d.mts +38 -0
  498. package/foundation/discovery/component-discovery.mjs +48 -0
  499. package/foundation/discovery/component-loader.d.mts +35 -38
  500. package/foundation/discovery/component-loader.mjs +53 -222
  501. package/foundation/discovery/docs-discovery.d.mts +114 -9
  502. package/foundation/discovery/docs-discovery.mjs +239 -30
  503. package/foundation/discovery/docs-discovery.test.mjs +150 -35
  504. package/foundation/discovery/docs-output-budget.d.mts +2 -2
  505. package/foundation/discovery/docs-output-budget.mjs +1 -1
  506. package/foundation/discovery/docs-section-key.d.mts +28 -10
  507. package/foundation/discovery/docs-section-key.mjs +134 -33
  508. package/foundation/discovery/docs-section-key.test.mjs +41 -19
  509. package/foundation/discovery/template-adapter.d.mts +113 -11
  510. package/foundation/discovery/template-adapter.fixture-refs.test.mjs +248 -0
  511. package/foundation/discovery/template-adapter.integration-isolation.test.mjs +94 -0
  512. package/foundation/discovery/template-adapter.mjs +772 -82
  513. package/foundation/discovery/template-adapter.test.mjs +57 -0
  514. package/foundation/discovery/template-conflict-release.d.mts +13 -0
  515. package/foundation/discovery/template-conflict-release.mjs +40 -0
  516. package/foundation/discovery/template-conflict-release.test.mjs +40 -0
  517. package/foundation/discovery/theme-discovery.d.mts +67 -7
  518. package/foundation/discovery/theme-discovery.mjs +916 -186
  519. package/foundation/discovery/theme-discovery.test.mjs +613 -219
  520. package/foundation/discovery/theming-targets.test.mjs +4 -0
  521. package/foundation/doc-compiler/bundle.d.mts +47 -0
  522. package/foundation/doc-compiler/bundle.mjs +278 -0
  523. package/foundation/doc-compiler/bundle.test.mjs +266 -0
  524. package/foundation/doc-compiler/compile.d.mts +343 -0
  525. package/foundation/doc-compiler/compile.mjs +558 -0
  526. package/foundation/doc-compiler/diagnostics.d.mts +126 -0
  527. package/foundation/doc-compiler/diagnostics.mjs +305 -0
  528. package/foundation/doc-compiler/doc-compiler.test.mjs +714 -0
  529. package/foundation/doc-compiler/doc-loads.test.mjs +1642 -0
  530. package/foundation/doc-compiler/import.d.mts +24 -0
  531. package/foundation/doc-compiler/import.mjs +59 -0
  532. package/foundation/doc-compiler/inputs.d.mts +102 -0
  533. package/foundation/doc-compiler/inputs.mjs +291 -0
  534. package/foundation/doc-compiler/inputs.test.mjs +299 -0
  535. package/foundation/doc-compiler/ir.d.mts +22 -0
  536. package/foundation/doc-compiler/ir.mjs +471 -0
  537. package/foundation/doc-compiler/lenses.d.mts +36 -0
  538. package/foundation/doc-compiler/lenses.mjs +173 -0
  539. package/foundation/doc-compiler/links.d.mts +162 -0
  540. package/foundation/doc-compiler/links.mjs +294 -0
  541. package/foundation/doc-compiler/links.test.mjs +192 -0
  542. package/foundation/doc-compiler/lower-doc.test.mjs +495 -0
  543. package/foundation/doc-compiler/overlays.d.mts +37 -0
  544. package/foundation/doc-compiler/overlays.mjs +206 -0
  545. package/foundation/doc-compiler/parse-readable.d.mts +9 -0
  546. package/foundation/doc-compiler/parse-readable.mjs +29 -0
  547. package/foundation/doc-compiler/read.d.mts +127 -0
  548. package/foundation/doc-compiler/read.mjs +325 -0
  549. package/foundation/doc-compiler/read.test.mjs +313 -0
  550. package/foundation/doc-compiler/source.d.mts +33 -0
  551. package/foundation/doc-compiler/source.mjs +128 -0
  552. package/foundation/doc-compiler/tree.d.mts +288 -0
  553. package/foundation/doc-compiler/tree.mjs +876 -0
  554. package/foundation/doc-compiler/tree.test.mjs +598 -0
  555. package/foundation/fs/file-protection.d.mts +33 -0
  556. package/foundation/fs/file-protection.mjs +825 -0
  557. package/foundation/fs/file-protection.test.mjs +250 -0
  558. package/foundation/fs/module-loader.d.mts +1 -0
  559. package/foundation/fs/module-loader.mjs +50 -1
  560. package/foundation/fs/module-loader.stdout.test.mjs +332 -0
  561. package/foundation/fs/path-safety.d.mts +3 -2
  562. package/foundation/fs/path-safety.mjs +49 -19
  563. package/foundation/fs/path-safety.test.mjs +50 -0
  564. package/foundation/fs/publish-file-hardlink-unavailable.test.mjs +129 -94
  565. package/foundation/integrations/autolink.d.mts +58 -1
  566. package/foundation/integrations/autolink.mjs +143 -52
  567. package/foundation/integrations/autolink.test.mjs +1 -1
  568. package/foundation/integrations/cli-requirement.d.mts +45 -0
  569. package/foundation/integrations/cli-requirement.mjs +154 -0
  570. package/foundation/integrations/cli-requirement.test.mjs +84 -0
  571. package/foundation/integrations/contribution-fixes.d.mts +145 -0
  572. package/foundation/integrations/contribution-fixes.mjs +1284 -0
  573. package/foundation/integrations/contribution-inventory.d.mts +3 -2
  574. package/foundation/integrations/contribution-inventory.mjs +27 -24
  575. package/foundation/integrations/contribution-inventory.test.mjs +86 -27
  576. package/foundation/integrations/integration-warnings.d.mts +9 -2
  577. package/foundation/integrations/integration-warnings.mjs +51 -26
  578. package/foundation/integrations/integration-warnings.test.mjs +74 -1
  579. package/foundation/integrations/integrations.d.mts +17 -1
  580. package/foundation/integrations/integrations.mjs +52 -98
  581. package/foundation/integrations/integrations.test.mjs +31 -0
  582. package/foundation/integrations/provider-ledger.test.mjs +275 -0
  583. package/foundation/integrations/provider-resolution.d.mts +152 -0
  584. package/foundation/integrations/provider-resolution.mjs +576 -0
  585. package/foundation/integrations/provider-resolution.test.mjs +369 -0
  586. package/foundation/integrations/theme-descriptor.d.mts +8 -0
  587. package/foundation/integrations/theme-descriptor.mjs +44 -0
  588. package/foundation/integrations/validate-contributions.mjs +121 -29
  589. package/foundation/response/base.d.ts +8 -4
  590. package/foundation/response/error-codes.d.mts +3 -1
  591. package/foundation/response/error-codes.d.ts +2 -0
  592. package/foundation/response/error-codes.doc.mjs +13 -4
  593. package/foundation/response/error-codes.mjs +8 -2
  594. package/foundation/response/error-codes.test.mjs +137 -10
  595. package/foundation/response/json-contract.test.mjs +11 -0
  596. package/foundation/response/json.d.mts +4 -2
  597. package/foundation/response/json.mjs +8 -10
  598. package/foundation/response/response-types.doc.d.mts +5 -1
  599. package/foundation/response/response-types.doc.mjs +37 -22
  600. package/foundation/response/response-types.doc.test.mjs +158 -0
  601. package/foundation/response/response.doc.mjs +1 -1
  602. package/foundation/text/string-utils.d.mts +8 -0
  603. package/foundation/text/string-utils.mjs +40 -10
  604. package/foundation/xle/expand.d.mts +2 -0
  605. package/foundation/xle/expand.mjs +4 -3
  606. package/foundation/xle/expand.test.mjs +54 -0
  607. package/foundation/xle/xle.test.mjs +13 -0
  608. package/package.json +10 -11
  609. package/assets/templates/themes/manifest.json +0 -95
  610. package/clients/cli/lib/update-check.mjs +0 -83
  611. package/clients/cli/lib/update-check.test.mjs +0 -137
  612. package/clients/cli/update-hint-commands.test.mjs +0 -54
@@ -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.`,
@@ -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
+ });
@@ -55,6 +55,15 @@ describe('generateCompressedIndex', () => {
55
55
  expect(frameRule).not.toMatch(/https?:/);
56
56
  });
57
57
 
58
+ it('starts every page from a template before any component', () => {
59
+ const lines = generateCompressedIndex('1.0.0').split('\n');
60
+ const workflow = lines.findIndex(l => l.startsWith('WORKFLOW'));
61
+ expect(lines[workflow]).toMatch(/start every page from a template/);
62
+ expect(lines[workflow + 1]).toMatch(/^1\. `astryx build /);
63
+ expect(lines[workflow + 2]).toMatch(/^2\. `astryx template <name> <path>`/);
64
+ expect(lines.join('\n')).not.toMatch(/reference code/);
65
+ });
66
+
58
67
  it('includes the post-generation self-check rule', () => {
59
68
  const result = generateCompressedIndex('1.0.0');
60
69
  expect(result).toContain('SELF-CHECK before you finish');
@@ -134,7 +143,6 @@ describe('generateCompressedIndex', () => {
134
143
  const line = topicLine(generateCompressedIndex('1.0.0'));
135
144
  for (const topic of [
136
145
  'getting-started',
137
- 'cli-integrations',
138
146
  'browser-support',
139
147
  'styling-libraries',
140
148
  'working-with-ai',
@@ -143,6 +151,16 @@ describe('generateCompressedIndex', () => {
143
151
  }
144
152
  });
145
153
 
154
+ it('points to the CLI docs tree on a line of its own', () => {
155
+ // The integration guide lives in the docs tree now (cli/integrations), so
156
+ // the topic line no longer names it; the tree's entry point does.
157
+ const block = generateCompressedIndex('1.0.0');
158
+ expect(block).toContain(
159
+ ' docs cli commands, API reference, integration authoring (one level at a time)',
160
+ );
161
+ expect(topicLine(block)).not.toContain('cli-integrations');
162
+ });
163
+
146
164
  it('lists the topics it is given, so an integration’s reach the agent', () => {
147
165
  const line = topicLine(
148
166
  generateCompressedIndex('1.0.0', {topics: ['tokens', 'deploying']}),
@@ -63,11 +63,18 @@ function config(integrations, ownHandler = false) {
63
63
  );
64
64
  }
65
65
 
66
- /** @param {Record<string, unknown>} [astryx] */
67
- function packageJson(astryx) {
66
+ /**
67
+ * @param {Record<string, unknown>} [astryx]
68
+ * @param {Record<string, string>} [dependencies]
69
+ */
70
+ function packageJson(astryx, dependencies) {
68
71
  fs.writeFileSync(
69
72
  path.join(tmpDir, 'package.json'),
70
- JSON.stringify({name: 'consumer', ...(astryx ? {astryx} : {})}),
73
+ JSON.stringify({
74
+ name: 'consumer',
75
+ ...(dependencies ? {dependencies} : {}),
76
+ ...(astryx ? {astryx} : {}),
77
+ }),
71
78
  );
72
79
  }
73
80
 
@@ -172,6 +179,24 @@ describe('an app can refuse an inherited handler', () => {
172
179
 
173
180
  expect(fired()).toEqual(['reporting-integration']);
174
181
  });
182
+
183
+ it('keeps an autolinked handler in a project with no astryx.config', async () => {
184
+ packageJson(undefined, {'reporting-integration': '1.0.0'});
185
+ integration('reporting-integration');
186
+
187
+ await runOneCommand();
188
+
189
+ expect(fired()).toEqual(['reporting-integration']);
190
+ });
191
+
192
+ it('drops an autolinked handler under astryx.inheritDebug false with no astryx.config', async () => {
193
+ packageJson({inheritDebug: false}, {'reporting-integration': '1.0.0'});
194
+ integration('reporting-integration');
195
+
196
+ await runOneCommand();
197
+
198
+ expect(fired()).toEqual([]);
199
+ });
175
200
  });
176
201
 
177
202
  describe('loading the project twice delivers once', () => {
@@ -15,6 +15,7 @@ function installThemeIntegration(packageName, slug) {
15
15
  ...packageName.split('/'),
16
16
  );
17
17
  const themeDir = path.join(packageDir, 'themes', slug);
18
+ const stem = `${slug.replace(/-([a-z0-9])/gu, (_, character) => character.toUpperCase())}Theme`;
18
19
  fs.mkdirSync(themeDir, {recursive: true});
19
20
  fs.writeFileSync(
20
21
  path.join(packageDir, 'package.json'),
@@ -22,28 +23,19 @@ function installThemeIntegration(packageName, slug) {
22
23
  );
23
24
  fs.writeFileSync(
24
25
  path.join(packageDir, 'astryx.integration.mjs'),
25
- `export default {themes: './themes'};\n`,
26
+ `export default {themes: './themes'};
27
+ `,
26
28
  );
27
29
  fs.writeFileSync(
28
- path.join(packageDir, 'themes', 'manifest.json'),
29
- JSON.stringify({
30
- version: 1,
31
- themes: [
32
- {
33
- slug,
34
- displayName: 'Ocean',
35
- description: 'Blue and calm.',
36
- maintained: true,
37
- entry: 'oceanTheme.ts',
38
- exportName: 'oceanTheme',
39
- files: ['oceanTheme.ts'],
40
- },
41
- ],
42
- }),
30
+ path.join(themeDir, `${stem}.doc.mjs`),
31
+ `/** @type {import('@astryxdesign/cli/authoring').ThemeDoc} */
32
+ export default {type: 'theme', name: '${slug}', displayName: 'Ocean', description: 'Blue and calm.', maintained: true};
33
+ `,
43
34
  );
44
35
  fs.writeFileSync(
45
- path.join(themeDir, 'oceanTheme.ts'),
46
- `export const oceanTheme = {};\n`,
36
+ path.join(themeDir, `${stem}.ts`),
37
+ `export const ${stem} = {};
38
+ `,
47
39
  );
48
40
  }
49
41
 
@@ -99,7 +91,7 @@ describe('Project themes', () => {
99
91
  ]);
100
92
  });
101
93
 
102
- it('records an invalid integration theme catalog as an issue', async () => {
94
+ it('records an invalid integration theme descriptor as an issue', async () => {
103
95
  installThemeIntegration('@acme/themes', 'ocean');
104
96
  fs.rmSync(
105
97
  path.join(