@astryxdesign/cli 0.1.7 → 0.1.8-canary.07362a9

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 (389) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +115 -19
  3. package/{src/api → api/blog}/blog.mjs +42 -6
  4. package/{src/api → api/blog}/blog.test.mjs +2 -2
  5. package/api/build/build.mjs +92 -0
  6. package/api/build/build.test.mjs +92 -0
  7. package/{src/api → api/component}/component.mjs +49 -26
  8. package/{src/api → api/discover}/discover.mjs +55 -18
  9. package/{src/api → api/docs}/docs.mjs +47 -21
  10. package/{src/api → api/doctor}/doctor.mjs +13 -10
  11. package/{src/api → api}/error.mjs +2 -2
  12. package/{src/api → api/hook}/hook.mjs +11 -11
  13. package/{src/api → api}/index.mjs +16 -9
  14. package/{src/api → api/integration}/validate-integration.mjs +48 -15
  15. package/{src/api → api/integration}/validate-integration.test.mjs +1 -1
  16. package/{src/api → api/layout}/layout.mjs +66 -17
  17. package/{src/api → api/layout}/layout.test.mjs +1 -1
  18. package/{src/api → api/search}/search.mjs +71 -12
  19. package/api/swizzle/swizzle.mjs +283 -0
  20. package/{src/commands → api/swizzle}/swizzle.test.mjs +26 -1
  21. package/{src/api → api/template}/template-integration.test.mjs +2 -2
  22. package/{src/api → api/template}/template-suffix.test.mjs +3 -3
  23. package/{src/api → api/template}/template.mjs +96 -19
  24. package/{src/api → api/theme}/theme-add.mjs +35 -7
  25. package/{src → authoring}/codemod.mjs +6 -6
  26. package/{src → authoring}/config.test.mjs +1 -1
  27. package/{src → authoring}/doc.mjs +1 -1
  28. package/{src → authoring}/doc.test.mjs +1 -1
  29. package/{src → authoring}/template.mjs +1 -1
  30. package/bin/astryx.mjs +12 -10
  31. package/{src → cli}/commands/blog.mjs +48 -7
  32. package/{src → cli}/commands/build-theme.mjs +133 -115
  33. package/cli/commands/build.mjs +172 -0
  34. package/cli/commands/cli-postinstall.test.mjs +42 -0
  35. package/{src → cli}/commands/component/index.mjs +52 -25
  36. package/{src → cli}/commands/component-ownership.test.mjs +3 -3
  37. package/{src → cli}/commands/component-package.test.mjs +1 -1
  38. package/{src → cli}/commands/component-resolution.test.mjs +2 -2
  39. package/{src → cli}/commands/discover.mjs +23 -12
  40. package/{src → cli}/commands/docs.mjs +42 -10
  41. package/{src → cli}/commands/docs.test.mjs +1 -1
  42. package/{src → cli}/commands/doctor.mjs +3 -3
  43. package/{src → cli}/commands/doctor.test.mjs +2 -2
  44. package/{src → cli}/commands/ensure-core-built.mjs +3 -2
  45. package/{src → cli}/commands/external-showcase.test.mjs +1 -1
  46. package/{src → cli}/commands/hook/index.mjs +40 -17
  47. package/{src → cli}/commands/import-hint-correctness.test.mjs +1 -1
  48. package/cli/commands/init.mjs +195 -0
  49. package/{src → cli}/commands/init.next-steps.test.mjs +1 -1
  50. package/cli/commands/interactive-guard.test.mjs +66 -0
  51. package/{src → cli}/commands/json-contract.test.mjs +2 -2
  52. package/{src → cli}/commands/layout.mjs +59 -19
  53. package/{src → cli}/commands/search.mjs +22 -15
  54. package/{src → cli}/commands/search.test.mjs +1 -1
  55. package/cli/commands/setup-nudge.test.mjs +108 -0
  56. package/cli/commands/swizzle.mjs +103 -0
  57. package/{src → cli}/commands/template.mjs +84 -53
  58. package/{src → cli}/commands/template.path-safety.test.mjs +2 -2
  59. package/{src → cli}/commands/upgrade.integration-policy.test.mjs +1 -1
  60. package/{src → cli}/commands/upgrade.mjs +291 -77
  61. package/cli/commands/upgrade.test.mjs +314 -0
  62. package/{src → cli}/commands/validate-integration.mjs +17 -37
  63. package/{src → cli}/index.mjs +55 -19
  64. package/{src/codemods → codemods}/__tests__/registry.test.mjs +1 -0
  65. package/{src/codemods → codemods}/ensure-jscodeshift.mjs +12 -28
  66. package/{src/codemods → codemods}/integration-discovery.mjs +6 -3
  67. package/{src/codemods → codemods}/integration-discovery.test.mjs +1 -1
  68. package/{src/codemods → codemods}/integration-runner.mjs +8 -3
  69. package/{src/codemods → codemods}/registry.mjs +4 -1
  70. package/{src/codemods → codemods}/run-codemod.mjs +18 -11
  71. package/{src/codemods → codemods}/runner.mjs +24 -14
  72. package/{src/codemods → codemods}/transforms/v0.0.10/remove-size-props.mjs +9 -4
  73. package/{src/codemods → codemods}/transforms/v0.0.12/add-is-icon-only.mjs +27 -22
  74. package/{src/codemods → codemods}/transforms/v0.0.13/icon-name-deprecations.mjs +12 -5
  75. package/{src/codemods → codemods}/transforms/v0.0.13/rename-attachments-to-drawer.mjs +15 -8
  76. package/{src/codemods → codemods}/transforms/v0.0.13/toolbar-density-to-size.mjs +10 -4
  77. package/{src/codemods → codemods}/transforms/v0.0.14/rename-action-props.mjs +11 -5
  78. package/{src/codemods → codemods}/transforms/v0.0.14/rename-section-wash-to-muted.mjs +11 -6
  79. package/{src/codemods → codemods}/transforms/v0.0.14/rename-status-variants.mjs +15 -10
  80. package/{src/codemods → codemods}/transforms/v0.0.15/migrate-item-children-to-endcontent.mjs +13 -8
  81. package/{src/codemods → codemods}/transforms/v0.0.15/migrate-selector-children-to-render-option.mjs +18 -13
  82. package/{src/codemods → codemods}/transforms/v0.0.15/migrate-theme-selectors-to-data-attrs.mjs +8 -4
  83. package/{src/codemods → codemods}/transforms/v0.0.15/rename-date-picker-to-input.mjs +11 -6
  84. package/{src/codemods → codemods}/transforms/v0.0.15/rename-imperative-ref-to-handleRef.mjs +6 -1
  85. package/{src/codemods → codemods}/transforms/v0.0.15/rename-isStreaming-to-isStopShown.mjs +6 -1
  86. package/{src/codemods → codemods}/transforms/v0.0.15/rename-stack-element-to-as.mjs +8 -3
  87. package/{src/codemods → codemods}/transforms/v0.0.2/migrate-badge-dot-to-statusdot.mjs +17 -10
  88. package/{src/codemods → codemods}/transforms/v0.0.2/migrate-gap-to-numeric.mjs +7 -1
  89. package/{src/codemods → codemods}/transforms/v0.0.2/migrate-isFullBleed-to-padding.mjs +9 -4
  90. package/{src/codemods → codemods}/transforms/v0.0.2/migrate-useXDSIcon-to-getIcon.mjs +12 -7
  91. package/{src/codemods → codemods}/transforms/v0.0.2/rename-banner-endButton-to-endContent.mjs +7 -2
  92. package/{src/codemods → codemods}/transforms/v0.0.2/rename-form-tooltip-startIcon.mjs +8 -2
  93. package/{src/codemods → codemods}/transforms/v0.0.2/rename-isShown-to-isOpen.mjs +7 -2
  94. package/{src/codemods → codemods}/transforms/v0.0.2/rename-selector-items-to-options.mjs +7 -2
  95. package/{src/codemods → codemods}/transforms/v0.0.2/rename-sidenav-header-to-heading.mjs +11 -4
  96. package/{src/codemods → codemods}/transforms/v0.0.2/rename-topnav-title-to-heading.mjs +10 -4
  97. package/{src/codemods → codemods}/transforms/v0.0.2/unify-uncontrolled-to-defaultX.mjs +7 -2
  98. package/{src/codemods → codemods}/transforms/v0.0.2/unify-visibility-to-onOpenChange.mjs +10 -5
  99. package/{src/codemods → codemods}/transforms/v0.0.6/migrate-badge-children-to-label.mjs +7 -2
  100. package/{src/codemods → codemods}/transforms/v0.0.6/migrate-collapse-to-collapsible.mjs +9 -3
  101. package/{src/codemods → codemods}/transforms/v0.0.6/migrate-radius-tokens.mjs +11 -5
  102. package/{src/codemods → codemods}/transforms/v0.0.6/migrate-shadow-tokens.mjs +13 -6
  103. package/{src/codemods → codemods}/transforms/v0.0.6/migrate-skeleton-radius.mjs +7 -1
  104. package/{src/codemods → codemods}/transforms/v0.0.6/migrate-token-names.mjs +11 -5
  105. package/{src/codemods → codemods}/transforms/v0.0.7/rename-banner-variant-to-container.mjs +8 -3
  106. package/{src/codemods → codemods}/transforms/v0.0.8/migrate-token-renames.mjs +15 -8
  107. package/{src/codemods → codemods}/transforms/v0.0.8/rename-endslot-to-endcontent.mjs +7 -2
  108. package/{src/codemods → codemods}/transforms/v0.1.0/__tests__/drop-xds-prefix-imports.test.mjs +42 -7
  109. package/{src/codemods → codemods}/transforms/v0.1.0/__tests__/migrate-xds-module-specifiers.test.mjs +43 -0
  110. package/{src/codemods → codemods}/transforms/v0.1.0/drop-xds-prefix-imports.mjs +117 -13
  111. package/{src/codemods → codemods}/transforms/v0.1.0/migrate-xds-css-surfaces.mjs +21 -17
  112. package/{src/codemods → codemods}/transforms/v0.1.0/migrate-xds-declare-module.mjs +7 -2
  113. package/{src/codemods → codemods}/transforms/v0.1.0/migrate-xds-module-specifiers.mjs +86 -15
  114. package/{src/codemods → codemods}/transforms/v0.1.2/rename-text-color-active-to-accent.mjs +15 -9
  115. package/{src/codemods → codemods}/transforms/v0.1.3/migrate-layout-components-to-experimental.mjs +17 -12
  116. package/{src/codemods → codemods}/transforms/v0.1.5/rename-switch-label-spacing-default-to-hug.mjs +15 -9
  117. package/{src/codemods → codemods}/transforms/v0.1.7/migrate-table-tableprops-to-direct-props.mjs +17 -10
  118. package/{src/codemods → codemods}/transforms/v0.1.7/rename-table-renderprops-styles-to-xstyle.mjs +18 -13
  119. package/codemods/transforms/v0.1.8/__tests__/rename-avatar-size-scale.test.mjs +161 -0
  120. package/codemods/transforms/v0.1.8/index.mjs +19 -0
  121. package/codemods/transforms/v0.1.8/rename-avatar-size-scale.mjs +239 -0
  122. package/docs/cli-integrations.doc.mjs +150 -0
  123. package/docs/elevation.doc.mjs +79 -1
  124. package/docs/getting-started.doc.mjs +9 -9
  125. package/docs/internationalization.doc.mjs +92 -46
  126. package/docs/migration.doc.mjs +18 -18
  127. package/docs/principles.doc.dense.mjs +1 -1
  128. package/docs/principles.doc.mjs +6 -6
  129. package/docs/principles.doc.zh.mjs +1 -1
  130. package/docs/styling-libraries.doc.mjs +3 -3
  131. package/docs/styling.doc.mjs +4 -4
  132. package/docs/theme.doc.dense.mjs +2 -2
  133. package/docs/theme.doc.mjs +7 -7
  134. package/docs/theme.doc.zh.mjs +1 -1
  135. package/docs/tokens.doc.mjs +1 -1
  136. package/docs/working-with-ai.doc.mjs +19 -19
  137. package/{src/commands → lib/agent-docs}/agent-docs.mjs +164 -29
  138. package/{src/commands → lib/agent-docs}/agent-docs.path-safety.test.mjs +1 -1
  139. package/{src/commands → lib/agent-docs}/agent-docs.test.mjs +123 -10
  140. package/{src/lib → lib}/cli-error.mjs +2 -2
  141. package/{src/lib → lib}/component-discovery.mjs +44 -2
  142. package/{src/lib → lib}/component-format.mjs +67 -25
  143. package/{src/lib → lib}/component-loader.mjs +27 -13
  144. package/{src/lib → lib}/componentDocOverlay.test.mjs +0 -1
  145. package/{src/lib → lib}/error-codes.test.mjs +1 -1
  146. package/{src/lib → lib}/hook-discovery.mjs +14 -1
  147. package/{src/lib → lib}/hook-format.mjs +29 -5
  148. package/{src/lib → lib}/integration-warnings.mjs +2 -2
  149. package/{src/lib → lib}/integrations.mjs +20 -0
  150. package/{src/lib → lib}/integrations.test.mjs +1 -1
  151. package/{src/lib → lib}/json-contract.test.mjs +1 -1
  152. package/{src/lib → lib}/json-shim.mjs +11 -6
  153. package/{src/lib → lib}/json-shim.test.mjs +1 -1
  154. package/{src/lib → lib}/json.mjs +3 -3
  155. package/{src/lib → lib}/levenshtein.mjs +6 -0
  156. package/{src/lib → lib}/manifest.mjs +7 -5
  157. package/{src/lib → lib}/manifest.test.mjs +2 -2
  158. package/{src/lib → lib}/module-loader.mjs +1 -0
  159. package/{src/lib → lib}/package-scanner.mjs +47 -0
  160. package/{src/lib → lib}/project.mjs +48 -18
  161. package/{src/lib → lib}/project.test.mjs +1 -1
  162. package/{src/lib → lib}/resolve-theme.mjs +5 -0
  163. package/{src/lib → lib}/string-utils.mjs +15 -0
  164. package/lib/term-log.mjs +57 -0
  165. package/{src/lib → lib}/xle/browser.mjs +18 -8
  166. package/{src/lib → lib}/xle/expand.mjs +155 -22
  167. package/{src/lib → lib}/xle/parse.mjs +119 -13
  168. package/{src/lib → lib}/xle/print.mjs +54 -14
  169. package/{src/lib → lib}/xle/registry-core.mjs +28 -3
  170. package/{src/lib → lib}/xle/registry.mjs +9 -4
  171. package/{src/lib → lib}/xle/splice.mjs +13 -2
  172. package/{src/lib → lib}/xle/validate.mjs +95 -14
  173. package/lib/xle/xle-ast.d.ts +214 -0
  174. package/package.json +37 -28
  175. package/scripts/postinstall.mjs +74 -0
  176. package/templates/blocks/components/Avatar/AvatarFallbackChain.tsx +4 -4
  177. package/templates/blocks/components/Avatar/AvatarGroup.tsx +2 -2
  178. package/templates/blocks/components/Avatar/AvatarInitialsFallback.tsx +1 -1
  179. package/templates/blocks/components/Avatar/AvatarShowcase.tsx +5 -5
  180. package/templates/blocks/components/Avatar/AvatarUserCard.tsx +1 -1
  181. package/templates/blocks/components/Avatar/AvatarWithImage.tsx +4 -4
  182. package/templates/blocks/components/Avatar/AvatarWithStatus.tsx +3 -3
  183. package/templates/blocks/components/AvatarGroup/AvatarGroupShowcase.tsx +2 -2
  184. package/templates/blocks/components/AvatarGroupOverflow/AvatarGroupOverflowCustomText.tsx +1 -1
  185. package/templates/blocks/components/AvatarGroupOverflow/AvatarGroupOverflowDefault.tsx +1 -1
  186. package/templates/blocks/components/AvatarGroupOverflow/AvatarGroupOverflowShowcase.tsx +2 -2
  187. package/templates/blocks/components/AvatarStatusDot/AvatarStatusDotShowcase.tsx +3 -3
  188. package/templates/blocks/components/AvatarStatusDot/AvatarStatusDotVariants.tsx +3 -3
  189. package/templates/blocks/components/Banner/BannerFloating.doc.mjs +14 -0
  190. package/templates/blocks/components/Banner/BannerFloating.tsx +16 -0
  191. package/templates/blocks/components/Button/ButtonFloating.doc.mjs +14 -0
  192. package/templates/blocks/components/Button/ButtonFloating.tsx +37 -0
  193. package/templates/blocks/components/ButtonGroup/ButtonGroupFloating.doc.mjs +14 -0
  194. package/templates/blocks/components/ButtonGroup/ButtonGroupFloating.tsx +23 -0
  195. package/templates/blocks/components/Card/CardElevations.doc.mjs +14 -0
  196. package/templates/blocks/components/Card/CardElevations.tsx +32 -0
  197. package/templates/blocks/components/Card/ClickableCardElevated.doc.mjs +14 -0
  198. package/templates/blocks/components/Card/ClickableCardElevated.tsx +21 -0
  199. package/templates/blocks/components/Card/SelectableCardElevated.doc.mjs +14 -0
  200. package/templates/blocks/components/Card/SelectableCardElevated.tsx +37 -0
  201. package/templates/blocks/components/Carousel/CarouselSnap.tsx +1 -1
  202. package/templates/blocks/components/ChatComposer/ChatComposerFlat.doc.mjs +14 -0
  203. package/templates/blocks/components/ChatComposer/ChatComposerFlat.tsx +75 -0
  204. package/templates/blocks/components/ChatMessage/ChatMessageAvatarName.tsx +2 -2
  205. package/templates/blocks/components/ChatMessage/ChatMessageMultiBubble.tsx +1 -1
  206. package/templates/blocks/components/ChatMessageBubble/ChatMessageBubbleGrouping.tsx +1 -1
  207. package/templates/blocks/components/ChatMessageBubble/ChatMessageBubbleMetadata.tsx +1 -1
  208. package/templates/blocks/components/ChatMessageList/ChatMessageListDensity.tsx +5 -9
  209. package/templates/blocks/components/ChatMessageList/ChatMessageListFullFeatured.tsx +1 -1
  210. package/templates/blocks/components/CodeBlock/CodeBlockTerminal.tsx +1 -1
  211. package/templates/blocks/components/HoverCard/HoverCardShowcase.tsx +1 -1
  212. package/templates/blocks/components/IconButton/IconButtonFloating.doc.mjs +14 -0
  213. package/templates/blocks/components/IconButton/IconButtonFloating.tsx +37 -0
  214. package/templates/blocks/components/InternationalizationProvider/InternationalizationProvider01ShippedLocale.doc.mjs +19 -0
  215. package/templates/blocks/components/InternationalizationProvider/InternationalizationProvider01ShippedLocale.tsx +46 -0
  216. package/templates/blocks/components/InternationalizationProvider/InternationalizationProvider02Overrides.doc.mjs +14 -0
  217. package/templates/blocks/components/InternationalizationProvider/InternationalizationProvider02Overrides.tsx +33 -0
  218. package/templates/blocks/components/Item/ItemWithMedia.tsx +2 -2
  219. package/templates/blocks/components/ListItem/ListItemWithMedia.tsx +2 -2
  220. package/templates/blocks/components/OverflowList/OverflowListCappedToolbar.doc.mjs +14 -0
  221. package/templates/blocks/components/OverflowList/OverflowListCappedToolbar.tsx +39 -0
  222. package/templates/blocks/components/OverflowList/OverflowListMultiRowTags.doc.mjs +14 -0
  223. package/templates/blocks/components/OverflowList/OverflowListMultiRowTags.tsx +44 -0
  224. package/templates/blocks/components/Pagination/PaginationDotsCarousel.tsx +2 -6
  225. package/templates/blocks/components/Stack/StackFillItem.tsx +2 -6
  226. package/templates/blocks/components/TabList/TabListTabsWithActions.doc.mjs +1 -1
  227. package/templates/blocks/components/TabList/TabListTabsWithActions.tsx +2 -7
  228. package/templates/blocks/components/Table/TableRowStatusTable.doc.mjs +14 -0
  229. package/templates/blocks/components/Table/TableRowStatusTable.tsx +59 -0
  230. package/templates/blocks/components/Thumbnail/ThumbnailDisabled.tsx +6 -1
  231. package/templates/blocks/components/Thumbnail/ThumbnailGallery.tsx +1 -0
  232. package/templates/blocks/components/Thumbnail/ThumbnailRemovable.tsx +1 -0
  233. package/templates/blocks/components/TypeaheadItem/TypeaheadItemShowcase.tsx +1 -1
  234. package/templates/pages/ai-chat/page.tsx +4 -4
  235. package/templates/pages/dashboard-portfolio/page.tsx +3 -11
  236. package/templates/pages/detail-page/page.tsx +5 -12
  237. package/templates/pages/documentation-design/page.tsx +1 -1
  238. package/templates/pages/messaging-shell/page.tsx +6 -8
  239. package/templates/pages/table-grouped/page.tsx +9 -14
  240. package/templates/pages/table-page/page.tsx +7 -9
  241. package/templates/pages/table-page-heatmap-status/page.tsx +5 -13
  242. package/{src/types → types}/api.contract.assert.ts +17 -8
  243. package/{src/types → types}/api.d.ts +110 -9
  244. package/{src/types → types}/base.d.ts +40 -4
  245. package/types/build.d.ts +53 -0
  246. package/types/codemod.d.ts +166 -0
  247. package/{src/types → types}/component.d.ts +1 -1
  248. package/{src/types → types}/discover.d.ts +1 -1
  249. package/{src/types → types}/docs.d.ts +1 -4
  250. package/{src/types → types}/hook.d.ts +1 -1
  251. package/{src/types → types}/index.d.ts +1 -0
  252. package/types/jscodeshift.d.ts +19 -0
  253. package/types/layout.d.ts +74 -0
  254. package/{src/types → types}/swizzle.d.ts +4 -0
  255. package/{src/types → types}/template-api.d.ts +4 -1
  256. package/types/theme.d.ts +53 -0
  257. package/types/upgrade.d.ts +108 -0
  258. package/utils/package-manager.mjs +178 -0
  259. package/utils/package-manager.test.mjs +220 -0
  260. package/{src/utils → utils}/path-safety.mjs +0 -18
  261. package/{src/utils → utils}/paths.mjs +4 -1
  262. package/{src/utils → utils}/update-check.mjs +5 -4
  263. package/{src/utils → utils}/update-check.test.mjs +3 -3
  264. package/docs/integration-authoring.md +0 -105
  265. package/src/commands/build.mjs +0 -196
  266. package/src/commands/init.mjs +0 -288
  267. package/src/commands/interactive-guard.test.mjs +0 -69
  268. package/src/commands/swizzle.mjs +0 -440
  269. package/src/commands/upgrade.test.mjs +0 -160
  270. package/src/types/codemod.d.ts +0 -81
  271. package/src/types/theme.d.ts +0 -23
  272. package/src/types/upgrade.d.ts +0 -64
  273. package/src/utils/interactive.mjs +0 -76
  274. package/src/utils/interactive.test.mjs +0 -70
  275. package/src/utils/package-manager.mjs +0 -74
  276. package/src/utils/package-manager.test.mjs +0 -113
  277. /package/{src/api → api/docs}/docOverlays.test.mjs +0 -0
  278. /package/{src/api → api/integration}/integration-block-exports.test.mjs +0 -0
  279. /package/{src/api → api/template}/template.test.mjs +0 -0
  280. /package/{src → authoring}/codemod.test.mjs +0 -0
  281. /package/{src → authoring}/config.mjs +0 -0
  282. /package/{src → authoring}/integration.mjs +0 -0
  283. /package/{src → authoring}/template.test.mjs +0 -0
  284. /package/{src → cli}/cli-exit-codes.test.mjs +0 -0
  285. /package/{src → cli}/commands/build-theme.color-scheme.test.mjs +0 -0
  286. /package/{src → cli}/commands/build-theme.import-path.test.mjs +0 -0
  287. /package/{src → cli}/commands/build-theme.path-safety.test.mjs +0 -0
  288. /package/{src → cli}/commands/build-theme.prose.test.mjs +0 -0
  289. /package/{src → cli}/commands/build-theme.variants.test.mjs +0 -0
  290. /package/{src → cli}/commands/build-theme.watch.test.mjs +0 -0
  291. /package/{src → cli}/commands/component.test.mjs +0 -0
  292. /package/{src → cli}/commands/detail-levels.test.mjs +0 -0
  293. /package/{src → cli}/commands/swizzle.path-safety.test.mjs +0 -0
  294. /package/{src → cli}/commands/swizzle.routing.test.mjs +0 -0
  295. /package/{src → cli}/commands/template.test.mjs +0 -0
  296. /package/{src → cli}/commands/upgrade.config-ordering.test.mjs +0 -0
  297. /package/{src → cli}/commands/validate-integration.test.mjs +0 -0
  298. /package/{src → cli}/update-hint-commands.test.mjs +0 -0
  299. /package/{src/codemods → codemods}/__tests__/rename-imperative-ref-to-handleRef.test.mjs +0 -0
  300. /package/{src/codemods → codemods}/__tests__/rename-isStreaming-to-isStopShown.test.mjs +0 -0
  301. /package/{src/codemods → codemods}/__tests__/rename-section-wash-to-muted.test.mjs +0 -0
  302. /package/{src/codemods → codemods}/__tests__/runner.test.mjs +0 -0
  303. /package/{src/codemods → codemods}/__tests__/toolbar-density-to-size.test.mjs +0 -0
  304. /package/{src/codemods → codemods}/__tests__/validation.test.mjs +0 -0
  305. /package/{src/codemods → codemods}/transforms/v0.0.10/__tests__/remove-size-props.test.mjs +0 -0
  306. /package/{src/codemods → codemods}/transforms/v0.0.10/index.mjs +0 -0
  307. /package/{src/codemods → codemods}/transforms/v0.0.12/__tests__/add-is-icon-only.test.mjs +0 -0
  308. /package/{src/codemods → codemods}/transforms/v0.0.12/index.mjs +0 -0
  309. /package/{src/codemods → codemods}/transforms/v0.0.13/__tests__/icon-name-deprecations.test.mjs +0 -0
  310. /package/{src/codemods → codemods}/transforms/v0.0.13/__tests__/rename-attachments-to-drawer.test.mjs +0 -0
  311. /package/{src/codemods → codemods}/transforms/v0.0.13/index.mjs +0 -0
  312. /package/{src/codemods → codemods}/transforms/v0.0.14/__tests__/rename-action-props.test.mjs +0 -0
  313. /package/{src/codemods → codemods}/transforms/v0.0.14/__tests__/rename-status-variants.test.mjs +0 -0
  314. /package/{src/codemods → codemods}/transforms/v0.0.14/index.mjs +0 -0
  315. /package/{src/codemods → codemods}/transforms/v0.0.15/__tests__/migrate-item-children-to-endcontent.test.mjs +0 -0
  316. /package/{src/codemods → codemods}/transforms/v0.0.15/__tests__/migrate-selector-children-to-render-option.test.mjs +0 -0
  317. /package/{src/codemods → codemods}/transforms/v0.0.15/__tests__/migrate-theme-selectors-to-data-attrs.test.mjs +0 -0
  318. /package/{src/codemods → codemods}/transforms/v0.0.15/__tests__/rename-date-picker-to-input.test.mjs +0 -0
  319. /package/{src/codemods → codemods}/transforms/v0.0.15/__tests__/rename-stack-element-to-as.test.mjs +0 -0
  320. /package/{src/codemods → codemods}/transforms/v0.0.15/index.mjs +0 -0
  321. /package/{src/codemods → codemods}/transforms/v0.0.2/__tests__/migrate-badge-dot-to-statusdot.test.mjs +0 -0
  322. /package/{src/codemods → codemods}/transforms/v0.0.2/__tests__/migrate-gap-to-numeric.test.mjs +0 -0
  323. /package/{src/codemods → codemods}/transforms/v0.0.2/__tests__/migrate-isFullBleed-to-padding.test.mjs +0 -0
  324. /package/{src/codemods → codemods}/transforms/v0.0.2/__tests__/migrate-useXDSIcon-to-getIcon.test.mjs +0 -0
  325. /package/{src/codemods → codemods}/transforms/v0.0.2/__tests__/rename-banner-endButton-to-endContent.test.mjs +0 -0
  326. /package/{src/codemods → codemods}/transforms/v0.0.2/__tests__/rename-form-tooltip-startIcon.test.mjs +0 -0
  327. /package/{src/codemods → codemods}/transforms/v0.0.2/__tests__/rename-isShown-to-isOpen.test.mjs +0 -0
  328. /package/{src/codemods → codemods}/transforms/v0.0.2/__tests__/rename-selector-items-to-options.test.mjs +0 -0
  329. /package/{src/codemods → codemods}/transforms/v0.0.2/__tests__/rename-sidenav-header-to-heading.test.mjs +0 -0
  330. /package/{src/codemods → codemods}/transforms/v0.0.2/__tests__/rename-topnav-title-to-heading.test.mjs +0 -0
  331. /package/{src/codemods → codemods}/transforms/v0.0.2/__tests__/unify-uncontrolled-to-defaultX.test.mjs +0 -0
  332. /package/{src/codemods → codemods}/transforms/v0.0.2/__tests__/unify-visibility-to-onOpenChange.test.mjs +0 -0
  333. /package/{src/codemods → codemods}/transforms/v0.0.2/index.mjs +0 -0
  334. /package/{src/codemods → codemods}/transforms/v0.0.6/__tests__/migrate-collapse-to-collapsible.test.mjs +0 -0
  335. /package/{src/codemods → codemods}/transforms/v0.0.6/__tests__/migrate-radius-tokens.test.mjs +0 -0
  336. /package/{src/codemods → codemods}/transforms/v0.0.6/__tests__/migrate-shadow-tokens.test.mjs +0 -0
  337. /package/{src/codemods → codemods}/transforms/v0.0.6/__tests__/migrate-skeleton-radius.test.mjs +0 -0
  338. /package/{src/codemods → codemods}/transforms/v0.0.6/__tests__/migrate-token-names.test.mjs +0 -0
  339. /package/{src/codemods → codemods}/transforms/v0.0.6/index.mjs +0 -0
  340. /package/{src/codemods → codemods}/transforms/v0.0.7/__tests__/rename-banner-variant-to-container.test.mjs +0 -0
  341. /package/{src/codemods → codemods}/transforms/v0.0.7/index.mjs +0 -0
  342. /package/{src/codemods → codemods}/transforms/v0.0.8/__tests__/migrate-token-renames.test.mjs +0 -0
  343. /package/{src/codemods → codemods}/transforms/v0.0.8/__tests__/rename-endslot-to-endcontent.test.mjs +0 -0
  344. /package/{src/codemods → codemods}/transforms/v0.0.8/index.mjs +0 -0
  345. /package/{src/codemods → codemods}/transforms/v0.1.0/__tests__/migrate-xds-css-surfaces.test.mjs +0 -0
  346. /package/{src/codemods → codemods}/transforms/v0.1.0/__tests__/migrate-xds-declare-module.test.mjs +0 -0
  347. /package/{src/codemods → codemods}/transforms/v0.1.0/__tests__/v0.1.0-ordering.test.mjs +0 -0
  348. /package/{src/codemods → codemods}/transforms/v0.1.0/index.mjs +0 -0
  349. /package/{src/codemods → codemods}/transforms/v0.1.2/__tests__/rename-text-color-active-to-accent.test.mjs +0 -0
  350. /package/{src/codemods → codemods}/transforms/v0.1.2/index.mjs +0 -0
  351. /package/{src/codemods → codemods}/transforms/v0.1.3/__tests__/migrate-layout-components-to-experimental.test.mjs +0 -0
  352. /package/{src/codemods → codemods}/transforms/v0.1.3/index.mjs +0 -0
  353. /package/{src/codemods → codemods}/transforms/v0.1.5/__tests__/rename-switch-label-spacing-default-to-hug.test.mjs +0 -0
  354. /package/{src/codemods → codemods}/transforms/v0.1.5/index.mjs +0 -0
  355. /package/{src/codemods → codemods}/transforms/v0.1.7/__tests__/migrate-table-tableprops-to-direct-props.test.mjs +0 -0
  356. /package/{src/codemods → codemods}/transforms/v0.1.7/__tests__/rename-table-renderprops-styles-to-xstyle.test.mjs +0 -0
  357. /package/{src/codemods → codemods}/transforms/v0.1.7/index.mjs +0 -0
  358. /package/{src/lib → lib}/cli-error.test.mjs +0 -0
  359. /package/{src/lib → lib}/component-discovery.importpath.test.mjs +0 -0
  360. /package/{src/lib → lib}/component-format.test.mjs +0 -0
  361. /package/{src/lib → lib}/component-loader.test.mjs +0 -0
  362. /package/{src/lib → lib}/config-cache.mjs +0 -0
  363. /package/{src/lib → lib}/config-cache.test.mjs +0 -0
  364. /package/{src/lib → lib}/config-schema.mjs +0 -0
  365. /package/{src/lib → lib}/error-codes.mjs +0 -0
  366. /package/{src/lib → lib}/integration-warnings.test.mjs +0 -0
  367. /package/{src/lib → lib}/module-loader.test.mjs +0 -0
  368. /package/{src/lib → lib}/node-version.mjs +0 -0
  369. /package/{src/lib → lib}/node-version.test.mjs +0 -0
  370. /package/{src/lib → lib}/parse.mjs +0 -0
  371. /package/{src/lib → lib}/site.mjs +0 -0
  372. /package/{src/lib → lib}/xle/browser.d.ts +0 -0
  373. /package/{src/lib → lib}/xle/xle.test.mjs +0 -0
  374. /package/{src/schemas → schemas}/doc-schema.mjs +0 -0
  375. /package/{src/schemas → schemas}/template-schema.mjs +0 -0
  376. /package/{src/types → types}/config.d.ts +0 -0
  377. /package/{src/types → types}/doc.d.ts +0 -0
  378. /package/{src/types → types}/doctor.d.ts +0 -0
  379. /package/{src/types → types}/error-codes.d.ts +0 -0
  380. /package/{src/types → types}/integration.d.ts +0 -0
  381. /package/{src/types → types}/manifest.d.ts +0 -0
  382. /package/{src/types → types}/search.d.ts +0 -0
  383. /package/{src/types → types}/template.d.ts +0 -0
  384. /package/{src/types → types}/validate-integration.d.ts +0 -0
  385. /package/{src/utils → utils}/github.mjs +0 -0
  386. /package/{src/utils → utils}/path-safety.test.mjs +0 -0
  387. /package/{src/utils → utils}/paths.test.mjs +0 -0
  388. /package/{src/utils → utils}/semver.mjs +0 -0
  389. /package/{src/utils → utils}/semver.test.mjs +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,45 @@
1
1
  # @xds/cli
2
2
 
3
+ # 0.1.8
4
+
5
+ #### Breaking Changes
6
+
7
+ - Avatar and AvatarGroup adopt Icon's abbreviated size scale — `size` now takes `xsm`/`sm`/`md`/`lg`/`xl` instead of `tiny`/`xsmall`/`small`/`medium`/`large`. Pixel values are unchanged (20/24/36/48/128px) and the default is now `md` (still 36px, formerly `small`). Avatar's tiers stay larger than Icon's because avatars align with media rather than glyphs. Run `astryx upgrade` to migrate call sites. (#2672)
8
+
9
+ #### New Features
10
+
11
+ - `astryx init --features agents` now defaults to creating root `AGENTS.md` — the tool-agnostic standard that Codex/Copilot, Cursor, and most agents read — instead of the Claude-specific `.claude/CLAUDE.md`. Claude output is now opt-in via `--agent claude` (→ `.claude/CLAUDE.md`), and `--agent all` still writes both. Projects with existing agent-doc files are unaffected: init still discovers and updates every file already present, so this only changes the from-scratch default. (#4216)
12
+ - "Foolproof init": both `@astryxdesign/core` and `@astryxdesign/cli` now print a postinstall nudge pointing you to `npx @astryxdesign/cli init`, `astryx` commands nudge you to finish setup until init has run, and `astryx init` runs non-interactively (no TTY required) so it works in CI and agent environments. (#4147, #4153, #4154, #4155)
13
+
14
+ #### Fixes
15
+
16
+ - Stop suggesting bare `npx astryx` before the CLI is installed — it resolves to an unrelated package on the npm registry.
17
+ The CLI now emits an install-aware invocation everywhere it prints a command:
18
+ - Extend the v0.1.0 upgrade codemods to cover test files that mock `@xds/core` modules, which were previously left half-migrated and broke after upgrade:
19
+ - `astryx upgrade` now keeps the managed agent-docs block (`<!-- ASTRYX:START --> … <!-- ASTRYX:END -->`) in sync with the installed version on **every** path — including the up-to-date and no-codemods short-circuits that previously returned before any refresh, leaving AI agents reading a stale component index and superseded rules. The block documents the installed library, so it's now refreshed up front (independent of codemods) and reported in the `--json` receipt as `agentDocs`. One detection pass covers three cases: a stale block is rewritten (`--apply`) or reported as a pending change (dry-run, which no longer writes); a project with core installed but no managed block is nudged to run `astryx init --features agents`; an already-current block stays silent. (#4168, #4169)
20
+
21
+ #### Documentation
22
+
23
+ - Add a `cli-integrations` CLI docs topic (`astryx docs cli-integrations`) so the integration-authoring guide (originally written by @ejhammond) is discoverable through the CLI and docsite instead of an unreferenced markdown file. Rewrite the CLI README's Configuration section to match the current strict config schema (`integrations`, `issuesUrl`, `hooks.postCodemod`, `experimental.xle`) and reframe the Integrations section around the two-file API.
24
+
25
+ #### Other Changes
26
+
27
+ - Installed / global / dev runs suggest `<pm> astryx <cmd>` (e.g. `pnpm exec astryx …`), unchanged.
28
+ - One-off runs (launched via `npx`/`pnpm dlx`/`yarn dlx`/`bunx`) suggest the scoped package `<dlx> @astryxdesign/cli <cmd>`, which always resolves to us.
29
+ - **migrate-xds-module-specifiers**: rewrite the mocked-module path in `vi.mock`/`vi.doMock`/`jest.mock`/`jest.doMock` (and bare `mock`) calls, plus `import(...)` specifiers used in TS type positions (`typeof import('@xds/core/Text')`), so the mock still intercepts the renamed `@astryxdesign/*` import.
30
+ - **drop-xds-prefix-imports**: un-prefix partial-mock override keys inside an `@xds/core` mock factory (e.g. `useXDSTruncation` → `useTruncation`) so the override matches the renamed export instead of silently overriding nothing. Scoped to recognized `@xds/core` mock factories only; unrelated object keys are untouched.
31
+
32
+ #### Contributors
33
+
34
+ Thanks to everyone who contributed to this release:
35
+
36
+ - @cixzhang
37
+ - @ejhammond
38
+ - @joeyfarina
39
+ - @josephfarina
40
+
41
+ ---
42
+
3
43
  # 0.1.7
4
44
 
5
45
  #### New Features
package/README.md CHANGED
@@ -2,15 +2,19 @@
2
2
 
3
3
  The CLI is the primary interface for working with the design system, for humans and machines alike. It provides component documentation, design tokens, page templates, theming tools, and upgrade codemods, all accessible via terminal commands, a typed JSON API, or programmatic imports. AI agents and build tools use the same API that powers the CLI, enabling end-to-end frontend development loops.
4
4
 
5
+ Run it one-off with the scoped package (works whether or not it's installed):
6
+
5
7
  ```bash
6
- npx astryx --help
7
- npx astryx search button
8
- npx astryx component Button
9
- npx astryx docs tokens
10
- npx astryx docs migration
11
- npx astryx template --list
8
+ npx @astryxdesign/cli --help
9
+ npx @astryxdesign/cli search button
10
+ npx @astryxdesign/cli component Button
11
+ npx @astryxdesign/cli docs tokens
12
+ npx @astryxdesign/cli docs migration
13
+ npx @astryxdesign/cli template --list
12
14
  ```
13
15
 
16
+ Once it's a project dependency (`npm install -D @astryxdesign/cli`), drop the scope and use the shorter `astryx` — e.g. `npx astryx component Button` or `pnpm exec astryx component Button`. Bare `astryx` resolves to an unrelated npm package until the CLI is installed, so prefer the scoped form above for first-run/one-off use.
17
+
14
18
  ## Finding things: `astryx search`
15
19
 
16
20
  When you don't know whether what you need is a component, a hook, a docs topic,
@@ -20,27 +24,29 @@ fuzzy matching for typos) and tagged with their domain plus the follow-up
20
24
  command to run:
21
25
 
22
26
  ```bash
23
- $ npx astryx search button
27
+ $ astryx search button
24
28
 
25
29
  Results for "button" (20):
26
30
 
27
31
  [component] Button
28
32
  Button triggers an action when clicked. Use it for form submissions…
29
- npx astryx component Button
33
+ → astryx component Button
30
34
 
31
35
  [component] IconButton
32
36
  A button that shows only an icon with no visible text…
33
- npx astryx component IconButton
37
+ → astryx component IconButton
34
38
 
35
39
  [hook] useClickableContainer
36
40
  Makes a container element clickable while preserving nested…
37
- npx astryx hook useClickableContainer
41
+ → astryx hook useClickableContainer
38
42
 
39
43
  [template] Banner — Collapsible
40
44
  Combine an action button, dismiss control, and expandable detail area…
41
- npx astryx template BannerCollapsibleContent
45
+ → astryx template BannerCollapsibleContent
42
46
  ```
43
47
 
48
+ (The CLI prints the follow-up commands with your actual runner — `npx astryx …` when installed, or `npx @astryxdesign/cli …` when run one-off.)
49
+
44
50
  Options:
45
51
 
46
52
  - `--type <component|hook|doc|template>`: restrict to a single domain
@@ -415,7 +421,7 @@ failures (warnings are fine) and `1` when any check fails. That makes it
415
421
  usable directly as a CI step:
416
422
 
417
423
  ```yaml
418
- - run: npx astryx doctor
424
+ - run: npx @astryxdesign/cli doctor
419
425
  ```
420
426
 
421
427
  Use `--json` for a structured envelope (`{ apiVersion, type: "doctor",
@@ -423,13 +429,103 @@ data: { checks, summary } }`) that AI agents and scripts can parse.
423
429
 
424
430
  ## Configuration
425
431
 
426
- The CLI reads from an optional `astryx.config.mjs` in your project root:
432
+ The CLI reads an optional `astryx.config.{ts,mjs,js}` from your project root
433
+ (a sibling of `package.json`). Every field is optional; with no config file the
434
+ CLI runs on defaults.
427
435
 
428
- ```javascript
429
- export default {
430
- templates: {
431
- get: async id => fetchTemplateFromAPI(id),
432
- },
436
+ ```typescript
437
+ import {createConfig} from '@astryxdesign/core/config';
438
+
439
+ export default createConfig({
440
+ integrations: ['@acme/astryx-widgets'],
433
441
  issuesUrl: 'https://github.com/your-org/your-repo/issues',
434
- };
442
+ });
443
+ ```
444
+
445
+ `createConfig` is a type-preserving helper: it returns its argument unchanged
446
+ and exists only to give the config file editor autocomplete and type-checking. A
447
+ plain `export default {}` object works identically. It's exported from
448
+ `@astryxdesign/core` (not the CLI) so your config file gets type feedback
449
+ without depending on the CLI; the same helper is re-exported from
450
+ `@astryxdesign/cli/config` for back-compat.
451
+
452
+ | Field | Type | Purpose |
453
+ | ----------------------------- | ------------------------------ | ----------------------------------------------------------------------------------------------- |
454
+ | `integrations` | `string[]` | Integration package names to load (see [Integrations](#integrations)). |
455
+ | `issuesUrl` | `string` | Where "report an issue" links point for your project. Defaults to the core issue tracker. |
456
+ | `hooks.postCodemod` | `PostCodemodHook[]` | Commands to run after `astryx upgrade` applies codemods (e.g. reinstall, rebuild, reformat). |
457
+ | `experimental.xle.components` | `Record<string, XleComponent>` | Register app-local components so layout (XLE) expressions can reference them by name. Unstable. |
458
+
459
+ The config is validated against a strict schema when the CLI loads it, so an
460
+ unknown field is a hard error rather than a silent no-op. `astryx doctor`
461
+ reports whether the config loads cleanly.
462
+
463
+ ## Integrations
464
+
465
+ An **integration** is any npm package that contributes its own components,
466
+ templates, and upgrade codemods to Astryx. The CLI surfaces them next to core's,
467
+ through the same commands, so a consumer can `astryx component`,
468
+ `astryx template`, and `astryx upgrade` across core and every integration
469
+ uniformly. Use it to ship a first-party add-on, publish a third-party component
470
+ library, or share an internal design-system package across apps.
471
+
472
+ The system runs on two files, each with a small typed API:
473
+
474
+ | File | Written by | Role |
475
+ | -------------------------------- | ---------- | ----------------------------------------- |
476
+ | `astryx.config.{ts,mjs,js}` | Consumer | Lists which integration packages to load. |
477
+ | `astryx.integration.{ts,mjs,js}` | Author | Declares what a package contributes. |
478
+
479
+ The consumer side is the `integrations` field of [`astryx.config`](#configuration).
480
+ The author side is the integration manifest below.
481
+
482
+ ### The integration manifest
483
+
484
+ A package becomes an integration by exporting a manifest from
485
+ `astryx.integration.{ts,mjs,js}` at its root (a sibling of `package.json`). The
486
+ manifest points at where each kind of contribution lives; identity (name,
487
+ version) comes from `package.json`, not the manifest.
488
+
489
+ ```typescript
490
+ import {createIntegration} from '@astryxdesign/core/authoring';
491
+
492
+ export default createIntegration({
493
+ components: './components',
494
+ templates: './templates',
495
+ codemods: './codemods',
496
+ issuesUrl: 'https://github.com/acme/widgets/issues',
497
+ });
498
+ ```
499
+
500
+ | Field | Type | Purpose |
501
+ | ------------ | -------- | --------------------------------------------------------------------- |
502
+ | `components` | `string` | Directory holding the package's components and their `.doc.*` files. |
503
+ | `templates` | `string` | Directory holding the package's page/block templates. |
504
+ | `codemods` | `string` | Directory holding upgrade codemods run by `astryx upgrade`. |
505
+ | `issuesUrl` | `string` | Where "report an issue" links for this package's contributions point. |
506
+
507
+ Every field is optional; declare only the roots the package ships.
508
+ `createIntegration` is a type-preserving helper (editor autocomplete and
509
+ type-checking); it lives in `@astryxdesign/core/authoring` and is re-exported
510
+ from `@astryxdesign/cli/integration` for back-compat.
511
+
512
+ ### How it works
513
+
514
+ Every command loads the consumer's `astryx.config`, resolves each listed
515
+ integration's manifest from `node_modules`, and discovers its contributions.
516
+ Everything is validated against one strict schema at the load boundary, so the
517
+ CLI presents core and integration contributions through a single, uniform
518
+ surface.
519
+
520
+ Discovery is resilient: a broken or misconfigured integration is skipped with a
521
+ one-line warning on stderr instead of crashing the CLI, and it never corrupts a
522
+ `--json` envelope. To inspect problems, run
523
+ `astryx validate-integration <package>` for a detailed report on one package, or
524
+ `astryx doctor` for an overall health check.
525
+
526
+ For the full authoring walkthrough (component doc format, template packaging
527
+ and `exports` requirements, and codemod authoring), see the guide:
528
+
529
+ ```bash
530
+ astryx docs cli-integrations
435
531
  ```
@@ -10,9 +10,9 @@
10
10
  * blog's structure can change freely without touching the CLI.
11
11
  */
12
12
 
13
- import {AstryxError} from './error.mjs';
14
- import {ERROR_CODES} from '../lib/error-codes.mjs';
15
- import {SITE_URL, SITE_ORIGIN} from '../lib/site.mjs';
13
+ import {AstryxError} from '../error.mjs';
14
+ import {ERROR_CODES} from '../../lib/error-codes.mjs';
15
+ import {SITE_URL, SITE_ORIGIN} from '../../lib/site.mjs';
16
16
 
17
17
  /** Abort a feed/post fetch that hangs, and cap how much we'll read. */
18
18
  const FETCH_TIMEOUT_MS = 15000;
@@ -20,6 +20,10 @@ const MAX_BYTES = 5 * 1024 * 1024; // 5 MB — a blog feed is never larger.
20
20
 
21
21
  const FEED_URL = new URL('/rss.xml', SITE_URL).toString();
22
22
 
23
+ /**
24
+ * @param {string} url
25
+ * @returns {Promise<string>}
26
+ */
23
27
  async function fetchText(url) {
24
28
  const controller = new AbortController();
25
29
  const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
@@ -31,7 +35,12 @@ async function fetchText(url) {
31
35
  });
32
36
  } catch (e) {
33
37
  clearTimeout(timer);
34
- const reason = e.name === 'AbortError' ? 'timed out' : e.message;
38
+ const reason =
39
+ e instanceof Error
40
+ ? e.name === 'AbortError'
41
+ ? 'timed out'
42
+ : e.message
43
+ : String(e);
35
44
  throw new AstryxError(
36
45
  `Could not reach ${url}: ${reason}`,
37
46
  [],
@@ -61,6 +70,7 @@ async function fetchText(url) {
61
70
  * Defense in depth: a post's plaintext URL comes from feed content. Require it
62
71
  * to live on the canonical origin so even a tampered feed can't redirect the
63
72
  * CLI to another host.
73
+ * @param {string} target
64
74
  */
65
75
  function assertCanonicalOrigin(target) {
66
76
  let targetOrigin;
@@ -82,6 +92,10 @@ function assertCanonicalOrigin(target) {
82
92
  }
83
93
  }
84
94
 
95
+ /**
96
+ * @param {string} value
97
+ * @returns {string}
98
+ */
85
99
  function unescapeXml(value) {
86
100
  return value
87
101
  .replace(/&lt;/g, '<')
@@ -91,12 +105,23 @@ function unescapeXml(value) {
91
105
  .replace(/&amp;/g, '&');
92
106
  }
93
107
 
108
+ /**
109
+ * @param {string} item
110
+ * @param {string} name
111
+ * @returns {string}
112
+ */
94
113
  function tag(item, name) {
95
114
  const m = item.match(new RegExp(`<${name}[^>]*>([\\s\\S]*?)</${name}>`));
96
115
  return m ? unescapeXml(m[1].trim()) : '';
97
116
  }
98
117
 
118
+ /**
119
+ * @param {string} item
120
+ * @param {string} name
121
+ * @returns {string[]}
122
+ */
99
123
  function tagAll(item, name) {
124
+ /** @type {string[]} */
100
125
  const out = [];
101
126
  const re = new RegExp(`<${name}[^>]*>([\\s\\S]*?)</${name}>`, 'g');
102
127
  let m;
@@ -104,7 +129,11 @@ function tagAll(item, name) {
104
129
  return out;
105
130
  }
106
131
 
107
- /** Extract the plaintext alternate href from an <item>. */
132
+ /**
133
+ * Extract the plaintext alternate href from an <item>.
134
+ * @param {string} item
135
+ * @returns {string | null}
136
+ */
108
137
  function textHref(item) {
109
138
  // Match the atom:link alternate regardless of attribute order/quoting; then
110
139
  // confirm it's the text/plain alternate before trusting the href.
@@ -121,7 +150,11 @@ function textHref(item) {
121
150
  return null;
122
151
  }
123
152
 
124
- /** Derive a slug from a post link (last path segment). */
153
+ /**
154
+ * Derive a slug from a post link (last path segment).
155
+ * @param {string} link
156
+ * @returns {string}
157
+ */
125
158
  function slugFromLink(link) {
126
159
  try {
127
160
  const path = new URL(link).pathname.replace(/\/$/, '');
@@ -131,6 +164,9 @@ function slugFromLink(link) {
131
164
  }
132
165
  }
133
166
 
167
+ /**
168
+ * @param {string} xml
169
+ */
134
170
  function parseFeed(xml) {
135
171
  const items = [];
136
172
  const re = /<item>([\s\S]*?)<\/item>/g;
@@ -9,8 +9,8 @@
9
9
 
10
10
  import {describe, it, expect, beforeEach, afterEach, vi} from 'vitest';
11
11
  import {blog} from './blog.mjs';
12
- import {AstryxError} from './error.mjs';
13
- import {SITE_URL} from '../lib/site.mjs';
12
+ import {AstryxError} from '../error.mjs';
13
+ import {SITE_URL} from '../../lib/site.mjs';
14
14
 
15
15
  const FEED = `<?xml version="1.0" encoding="UTF-8"?>
16
16
  <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
@@ -0,0 +1,92 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file build API — the "assemble a page" assistant, as data.
5
+ *
6
+ * `build()` with no query signals the workflow playbook (`build.help`). With a
7
+ * query it runs the unified search and groups the results into a composition
8
+ * KIT (`build.kit`): the closest page templates, the blocks that cover parts,
9
+ * and the domain components to fill gaps, plus the always-on frame + foundation.
10
+ *
11
+ * The kit carries RAW `SearchResultEntry` objects and static name arrays only —
12
+ * never pre-formatted command strings. All CLI prefixing (formatCliCommand /
13
+ * getCliInvocation) and the section prose live in the command renderer, so the
14
+ * JSON shape stays package-manager-agnostic and stable across environments.
15
+ */
16
+
17
+ import {search} from '../search/search.mjs';
18
+
19
+ /** A page at/above this score is a confident direct match. */
20
+ const PAGE_DIRECT = 95;
21
+ /** Below this a page is too weak to offer even as a layout reference. */
22
+ const PAGE_FLOOR = 50;
23
+ /** Below this a block/domain-component match is incidental noise. */
24
+ const DOMAIN_FLOOR = 55;
25
+
26
+ /**
27
+ * Always-surfaced primitives. Every page needs a shell + layout/typography/
28
+ * action atoms, but these never keyword-match an idea ("dashboard" != "Stack"),
29
+ * so search alone never returns them. Kept here (not the renderer) because they
30
+ * are ALSO used to exclude these names from the idea-specific `domain` group.
31
+ */
32
+ const FRAME = ['AppShell', 'TopNav', 'SideNav', 'Layout'];
33
+ const FOUNDATION = [
34
+ 'VStack', 'HStack', 'Grid', 'StackItem', 'Card', 'Section',
35
+ 'Text', 'Heading', 'Button', 'Icon', 'Badge', 'Divider',
36
+ ];
37
+ const ALWAYS = new Set([...FRAME, ...FOUNDATION]);
38
+
39
+ /**
40
+ * The page-building assistant. No query → the playbook signal; a query → the
41
+ * grouped composition kit.
42
+ *
43
+ * @param {string} [query] what you're building (e.g. "analytics dashboard")
44
+ * @param {{cwd?: string, type?: import('../../types/search').SearchDomain, limit?: number}} [options]
45
+ * @returns {Promise<import('../../types/build').BuildHelpResponse | import('../../types/build').BuildKitResponse>}
46
+ */
47
+ export async function build(query, options = {}) {
48
+ if (!query || !String(query).trim()) {
49
+ return {type: 'build.help', data: {playbook: true}};
50
+ }
51
+
52
+ const {cwd = process.cwd(), type, limit = 60} = options;
53
+ // search()'s JSDoc @returns widens results to object[]; the SearchResponse
54
+ // shape is the contract (api.d.ts). Cast locally rather than tightening the
55
+ // search @returns (a separate follow-up).
56
+ const result = /** @type {import('../../types/search').SearchResponse} */ (
57
+ await search(query, {cwd, type, limit})
58
+ );
59
+ const results = result.data.results;
60
+
61
+ const pages = results
62
+ .filter(r => r.domain === 'template' && r.kind !== 'block' && r.score >= PAGE_FLOOR)
63
+ .slice(0, 3);
64
+ const blocks = results
65
+ .filter(r => r.domain === 'template' && r.kind === 'block' && r.score >= DOMAIN_FLOOR)
66
+ .slice(0, 5);
67
+ const domain = results
68
+ .filter(
69
+ r =>
70
+ (r.domain === 'component' || r.domain === 'hook') &&
71
+ r.score >= DOMAIN_FLOOR &&
72
+ !ALWAYS.has(r.name),
73
+ )
74
+ .slice(0, 6);
75
+ const directMatch = pages.length > 0 && pages[0].score >= PAGE_DIRECT;
76
+
77
+ return {
78
+ type: 'build.kit',
79
+ data: {
80
+ query: result.data.query,
81
+ // Distinguishes "search found nothing" (renderer shows "No matches")
82
+ // from a weak-but-non-empty result set (renderer still shows the kit).
83
+ hasResults: results.length > 0,
84
+ directMatch,
85
+ pages,
86
+ blocks,
87
+ domain,
88
+ frame: FRAME,
89
+ foundation: FOUNDATION,
90
+ },
91
+ };
92
+ }
@@ -0,0 +1,92 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file Tests for the build API (playbook signal + composition kit).
5
+ */
6
+
7
+ import {describe, it, expect, vi} from 'vitest';
8
+ import * as path from 'node:path';
9
+ import {fileURLToPath} from 'node:url';
10
+ import {build} from './build.mjs';
11
+
12
+ // api/build/ -> up 3 = packages/cli, up 4 = repo root (has packages/core).
13
+ const REPO = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../..');
14
+
15
+ // build() delegates to search(), whose first (cold) call is slow under vitest.
16
+ vi.setConfig({testTimeout: 30000});
17
+
18
+ describe('build API', () => {
19
+ it('no query → build.help playbook signal', async () => {
20
+ const r = await build();
21
+ expect(r.type).toBe('build.help');
22
+ expect(r.data).toEqual({playbook: true});
23
+ });
24
+
25
+ it('query → build.kit with raw entries + static frame/foundation', async () => {
26
+ const r = await build('dashboard', {cwd: REPO});
27
+ expect(r.type).toBe('build.kit');
28
+ if (r.type !== 'build.kit') return;
29
+ expect(r.data.query).toBe('dashboard');
30
+ expect(r.data.hasResults).toBe(true);
31
+ expect(r.data.frame).toContain('AppShell');
32
+ expect(r.data.foundation).toContain('Button');
33
+ expect(Array.isArray(r.data.pages)).toBe(true);
34
+
35
+ // Entries are RAW SearchResultEntry objects — never package-manager-prefixed
36
+ // command strings (that formatting is the CLI renderer's job).
37
+ for (const e of [...r.data.pages, ...r.data.blocks, ...r.data.domain]) {
38
+ expect(typeof e.name).toBe('string');
39
+ expect(typeof e.score).toBe('number');
40
+ expect(e.command).not.toMatch(/^(pnpm|npm|yarn|bun|npx)\b/);
41
+ }
42
+ });
43
+
44
+ it('excludes always-on frame/foundation names from the domain group', async () => {
45
+ const r = await build('dashboard', {cwd: REPO});
46
+ if (r.type !== 'build.kit') throw new Error('expected build.kit');
47
+ const names = r.data.domain.map(d => d.name);
48
+ expect(names).not.toContain('Button');
49
+ expect(names).not.toContain('AppShell');
50
+ });
51
+
52
+ it('applies grouping caps, score floors, and directMatch threshold', async () => {
53
+ const r = await build('dashboard', {cwd: REPO});
54
+ if (r.type !== 'build.kit') throw new Error('expected build.kit');
55
+ const {pages, blocks, domain, directMatch} = r.data;
56
+
57
+ // Caps: pages ≤ 3, blocks ≤ 5, domain ≤ 6.
58
+ expect(pages.length).toBeLessThanOrEqual(3);
59
+ expect(blocks.length).toBeLessThanOrEqual(5);
60
+ expect(domain.length).toBeLessThanOrEqual(6);
61
+
62
+ // Score floors: pages ≥ PAGE_FLOOR(50); blocks/domain ≥ DOMAIN_FLOOR(55).
63
+ for (const p of pages) expect(p.score).toBeGreaterThanOrEqual(50);
64
+ for (const b of blocks) expect(b.score).toBeGreaterThanOrEqual(55);
65
+ for (const d of domain) expect(d.score).toBeGreaterThanOrEqual(55);
66
+
67
+ // directMatch iff the top page is a confident match (PAGE_DIRECT = 95).
68
+ expect(directMatch).toBe(pages.length > 0 && pages[0].score >= 95);
69
+
70
+ // Domain partitioning: pages = non-block templates, blocks = block templates,
71
+ // domain = components/hooks.
72
+ for (const p of pages) {
73
+ expect(p.domain).toBe('template');
74
+ expect(p.kind).not.toBe('block');
75
+ }
76
+ for (const b of blocks) {
77
+ expect(b.domain).toBe('template');
78
+ expect(b.kind).toBe('block');
79
+ }
80
+ for (const d of domain) expect(['component', 'hook']).toContain(d.domain);
81
+ });
82
+
83
+ it('no matches → build.kit with hasResults=false and empty groups', async () => {
84
+ const r = await build('zzznomatch99', {cwd: REPO});
85
+ expect(r.type).toBe('build.kit');
86
+ if (r.type !== 'build.kit') return;
87
+ expect(r.data.hasResults).toBe(false);
88
+ expect(r.data.pages).toHaveLength(0);
89
+ expect(r.data.blocks).toHaveLength(0);
90
+ expect(r.data.domain).toHaveLength(0);
91
+ });
92
+ });