@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
@@ -19,8 +19,8 @@ import {
19
19
  checkAgentDocs,
20
20
  checkPeerDeps,
21
21
  checkPackageManager,
22
- } from '../api/doctor.mjs';
23
- import {MIN_NODE_VERSION} from '../lib/node-version.mjs';
22
+ } from '../../api/doctor/doctor.mjs';
23
+ import {MIN_NODE_VERSION} from '../../lib/node-version.mjs';
24
24
 
25
25
  let tmpDir;
26
26
  let logCalls;
@@ -40,6 +40,7 @@ const BUILD_TIMEOUT_MS = 180_000;
40
40
  const STALE_LOCK_MS = BUILD_TIMEOUT_MS + 20_000;
41
41
  const POLL_MS = 250;
42
42
 
43
+ /** @param {number} ms */
43
44
  function sleepSync(ms) {
44
45
  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
45
46
  }
@@ -67,7 +68,7 @@ function tryAcquire() {
67
68
  fs.mkdirSync(LOCK_DIR);
68
69
  return true;
69
70
  } catch (err) {
70
- if (err.code !== 'EEXIST') {
71
+ if (/** @type {NodeJS.ErrnoException} */ (err).code !== 'EEXIST') {
71
72
  throw err;
72
73
  }
73
74
  if (lockIsStale()) {
@@ -76,7 +77,7 @@ function tryAcquire() {
76
77
  fs.mkdirSync(LOCK_DIR);
77
78
  return true;
78
79
  } catch (retryErr) {
79
- if (retryErr.code !== 'EEXIST') {
80
+ if (/** @type {NodeJS.ErrnoException} */ (retryErr).code !== 'EEXIST') {
80
81
  throw retryErr;
81
82
  }
82
83
  }
@@ -4,7 +4,7 @@ import {describe, it, expect, beforeEach, afterEach} from 'vitest';
4
4
  import * as fs from 'node:fs';
5
5
  import * as path from 'node:path';
6
6
  import * as os from 'node:os';
7
- import {findShowcase, findRelatedBlocks} from '../api/template.mjs';
7
+ import {findShowcase, findRelatedBlocks} from '../../api/template/template.mjs';
8
8
 
9
9
  // These tests verify that findShowcase and findRelatedBlocks can discover
10
10
  // blocks from external packages that declare xds.blocks in package.json.
@@ -11,14 +11,29 @@ import {
11
11
  formatHookCompact,
12
12
  formatHookBrief,
13
13
  formatHookParams,
14
- } from '../../lib/hook-format.mjs';
15
- import {getRunPrefix} from '../../utils/package-manager.mjs';
16
- import {jsonOut, humanLog} from '../../lib/json.mjs';
17
- import {cliError} from '../../lib/cli-error.mjs';
18
- import {ERROR_CODES} from '../../lib/error-codes.mjs';
19
- import {hook as hookApi} from '../../api/hook.mjs';
20
- import {findRelatedBlocks} from '../../api/template.mjs';
14
+ } from '../../../lib/hook-format.mjs';
15
+ import {getCliInvocation} from '../../../utils/package-manager.mjs';
16
+ import {jsonOut, humanLog} from '../../../lib/json.mjs';
17
+ import {cliError} from '../../../lib/cli-error.mjs';
18
+ import {ERROR_CODES} from '../../../lib/error-codes.mjs';
19
+ import {hook as hookApi} from '../../../api/hook/hook.mjs';
20
+ import {findRelatedBlocks} from '../../../api/template/template.mjs';
21
21
 
22
+ /**
23
+ * The api layer's hook() widens its return to `{type: string, data: unknown}`,
24
+ * so annotate the command-local result with the precise discriminated union from
25
+ * src/types/hook to get narrowing + typed data.
26
+ *
27
+ * @typedef {(
28
+ * | import('../../../types/hook').HookListResponse
29
+ * | import('../../../types/hook').HookBriefResponse
30
+ * | import('../../../types/hook').HookFullResponse
31
+ * | import('../../../types/hook').HookDetailResponse
32
+ * | import('../../../types/hook').HookDetailParamsResponse
33
+ * )} HookResult
34
+ */
35
+
36
+ /** @param {import('commander').Command} program */
22
37
  export function registerHook(program) {
23
38
  program
24
39
  .command('hook [name]')
@@ -26,8 +41,13 @@ export function registerHook(program) {
26
41
  .option('--list', 'List all hooks grouped by category')
27
42
  .option('--category <category>', 'List hooks in a specific category')
28
43
  .option('--params', 'Print only the parameters table')
29
- .action(async (name, options) => {
30
- const run = getRunPrefix();
44
+ .action(
45
+ /**
46
+ * @param {string|undefined} name
47
+ * @param {{list?: boolean, category?: string, params?: boolean}} options
48
+ */
49
+ async (name, options) => {
50
+ const run = getCliInvocation();
31
51
  const zh = program.opts().zh || false;
32
52
  const lang = program.opts().lang || null;
33
53
  const detailSource = program.getOptionValueSource('detail');
@@ -43,18 +63,20 @@ export function registerHook(program) {
43
63
  return;
44
64
  }
45
65
 
66
+ /** @type {HookResult} */
46
67
  let result;
47
68
  try {
48
- result = await hookApi(name, {
69
+ result = /** @type {HookResult} */ (await hookApi(name, {
49
70
  cwd: process.cwd(),
50
71
  list: options.list,
51
72
  category: options.category,
52
73
  params: options.params,
53
74
  detail,
54
75
  lang, zh,
55
- });
76
+ }));
56
77
  } catch (e) {
57
- cliError(e.message, {suggestions: e.suggestions, code: e.code});
78
+ const err = /** @type {import('../../../api/error.mjs').AstryxError} */ (e);
79
+ cliError(err.message, {suggestions: err.suggestions, code: err.code});
58
80
  return;
59
81
  }
60
82
 
@@ -76,7 +98,7 @@ export function registerHook(program) {
76
98
  for (const h of hookNames) humanLog(` ${h}`);
77
99
  }
78
100
  humanLog('');
79
- humanLog(`Usage: ${run} astryx hook <name>`);
101
+ humanLog(`Usage: ${run} hook <name>`);
80
102
  humanLog('');
81
103
  }
82
104
  break;
@@ -93,7 +115,7 @@ export function registerHook(program) {
93
115
  }
94
116
  humanLog('');
95
117
  }
96
- humanLog(`Usage: ${run} astryx hook <name>`);
118
+ humanLog(`Usage: ${run} hook <name>`);
97
119
  humanLog('');
98
120
  break;
99
121
  }
@@ -123,6 +145,7 @@ export function registerHook(program) {
123
145
  }
124
146
  // Show related block templates from relatedComponents
125
147
  const relatedComps = result.data.relatedComponents || [];
148
+ /** @type {import('../../../api/template/template.mjs').DiscoveredTemplate[]} */
126
149
  const allBlocks = [];
127
150
  for (const comp of relatedComps) {
128
151
  const blocks = await findRelatedBlocks(comp);
@@ -152,12 +175,12 @@ export function registerHook(program) {
152
175
  }
153
176
 
154
177
  // Re-export lib functions for external consumers
155
- export {discoverHooks, findHookDoc, getAllHookNames} from '../../lib/hook-discovery.mjs';
156
- export {loadDocs} from '../../lib/component-loader.mjs';
178
+ export {discoverHooks, findHookDoc, getAllHookNames} from '../../../lib/hook-discovery.mjs';
179
+ export {loadDocs} from '../../../lib/component-loader.mjs';
157
180
  export {
158
181
  formatHookFull,
159
182
  formatHookCompact,
160
183
  formatHookBrief,
161
184
  formatHookBriefAll,
162
185
  formatHookParams,
163
- } from '../../lib/hook-format.mjs';
186
+ } from '../../../lib/hook-format.mjs';
@@ -23,7 +23,7 @@ import {
23
23
  discoverComponents,
24
24
  findComponentSource,
25
25
  } from './component/index.mjs';
26
- import {findCoreDir} from '../utils/paths.mjs';
26
+ import {findCoreDir} from '../../utils/paths.mjs';
27
27
 
28
28
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
29
29
  const CLI_BIN = path.resolve(__dirname, '../../bin/astryx.mjs');
@@ -0,0 +1,195 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file init command — non-interactive setup + feature installer
5
+ *
6
+ * `astryx init` is non-interactive by default: it installs the AGENTS.md/
7
+ * CLAUDE.md cheat sheet with NO prompts, so it behaves identically for humans,
8
+ * AI agents, CI, and piped I/O — it never hangs or errors on a missing TTY.
9
+ * Non-interactive feature install: `astryx init --features agents,theme,template`
10
+ * Re-runnable: safe to run multiple times, idempotent
11
+ *
12
+ * Features:
13
+ * agents — Install AGENTS.md/CLAUDE.md cheat sheet for AI coding agents
14
+ * theme — Point to the theme workflow (`astryx theme`)
15
+ * template — Copy a starter page template
16
+ */
17
+
18
+ import * as path from 'node:path';
19
+ import * as fs from 'node:fs';
20
+ import {CLI_ROOT} from '../../utils/paths.mjs';
21
+ import {PathSafetyError} from '../../utils/path-safety.mjs';
22
+ import {getCliInvocation} from '../../utils/package-manager.mjs';
23
+ import {installAgentDocs, removeAgentDocs} from '../../lib/agent-docs/agent-docs.mjs';
24
+ import {listTemplates} from './template.mjs';
25
+ import {humanLog} from '../../lib/json.mjs';
26
+ import {cliError} from '../../lib/cli-error.mjs';
27
+ import {ERROR_CODES} from '../../lib/error-codes.mjs';
28
+
29
+ const VALID_FEATURES = ['agents', 'theme', 'template'];
30
+ const run = getCliInvocation();
31
+
32
+ /**
33
+ * Build the "Next steps" lines printed at the end of `astryx init`.
34
+ *
35
+ * Theme guidance must match the runtime recommendation emitted by core's
36
+ * <Theme> component (packages/core/src/theme/Theme.tsx): the pre-built theme
37
+ * path (`/built` import + `theme.css`) plus the base CSS import, so users
38
+ * don't end up with an unstyled app or the slower runtime style-injection
39
+ * path. See https://github.com/facebook/astryx/issues/3080.
40
+ *
41
+ * Exported for testing.
42
+ *
43
+ * @param {string} invocation install-aware CLI invocation stem (e.g. `npx astryx`, `pnpm exec astryx`, or `npx @astryxdesign/cli` for one-off runs)
44
+ * @returns {string[]} ordered list of human-facing lines
45
+ */
46
+ export function getNextSteps(invocation) {
47
+ return [
48
+ '',
49
+ ' Next steps:',
50
+ " 1. Import base styles: import '@astryxdesign/core/reset.css'",
51
+ " and import '@astryxdesign/core/astryx.css'",
52
+ " 2. Import components: import { Button } from '@astryxdesign/core'",
53
+ ' 3. Optionally add a theme (use the pre-built path for performance):',
54
+ " import { neutralTheme } from '@astryxdesign/theme-neutral/built'",
55
+ " import '@astryxdesign/theme-neutral/theme.css'",
56
+ ' <Theme theme={neutralTheme}>...</Theme>',
57
+ ` For custom themes, run \`${invocation} theme build <file>\` to generate the built artifacts.`,
58
+ ` 4. ${invocation} --help for all commands`,
59
+ '',
60
+ ];
61
+ }
62
+
63
+ // ─── Feature: agents ─────────────────────────────────────────────────────────
64
+
65
+ /**
66
+ * @param {string} targetDir
67
+ * @param {{agent?: string, agentDocsPath?: string | string[]}} [opts]
68
+ */
69
+ function runAgents(targetDir, {agent, agentDocsPath} = {}) {
70
+ try {
71
+ const paths = agentDocsPath
72
+ ? Array.isArray(agentDocsPath)
73
+ ? agentDocsPath
74
+ : [agentDocsPath]
75
+ : undefined;
76
+ const written = installAgentDocs(targetDir, {agent, paths});
77
+ const summary = written.join(', ');
78
+ humanLog(`✓ AI agent docs installed → ${summary}`);
79
+ } catch (err) {
80
+ // PathSafetyError carries a precise, user-actionable message —
81
+ // surface it instead of the generic "could not install" warning so
82
+ // misconfigured --agent-docs-path values aren't silently swallowed.
83
+ if (err instanceof PathSafetyError) {
84
+ console.error(`Error: ${err.message}`);
85
+ process.exitCode = 1;
86
+ return;
87
+ }
88
+ console.error(`Could not install agent docs. Try again with \`${run} init --features agents\`.`);
89
+ }
90
+ }
91
+
92
+ // ─── Feature: theme ──────────────────────────────────────────────────────────
93
+
94
+ function runTheme() {
95
+ humanLog(`✓ For a custom theme, run \`${run} theme\` (browse) or \`${run} theme add <slug>\` (scaffold).`);
96
+ }
97
+
98
+ // ─── Feature: template ───────────────────────────────────────────────────────
99
+
100
+ /**
101
+ * @param {string} targetDir
102
+ * @param {{templateName?: string}} [opts]
103
+ */
104
+ function runTemplate(targetDir, {templateName} = {}) {
105
+ const templates = listTemplates();
106
+ if (templates.length === 0) return;
107
+
108
+ if (!templateName) {
109
+ // Point agents at the build workflow rather than dumping page-template
110
+ // names — `build` surfaces pages AND blocks AND components for an idea,
111
+ // and `build` with no args is the full how-to-build playbook.
112
+ humanLog('✓ To build UI, use these commands:');
113
+ humanLog('');
114
+ humanLog(` ${run} build "<what you're building>" build a page — kit: closest template + blocks + components`);
115
+ humanLog(` ${run} build the how-to-build workflow (read this first)`);
116
+ humanLog(` ${run} search <query> find anything — components, docs, templates, blocks`);
117
+ humanLog('');
118
+ return;
119
+ }
120
+
121
+ if (!templates.includes(templateName)) {
122
+ cliError(`Unknown template "${templateName}". Available: ${templates.join(', ')}`, {code: ERROR_CODES.ERR_UNKNOWN_TEMPLATE});
123
+ return;
124
+ }
125
+
126
+ const outputDir = path.resolve(targetDir, `./src/pages/${templateName}`);
127
+ const srcPath = path.join(CLI_ROOT, 'templates', 'pages', templateName, 'page.tsx');
128
+ fs.mkdirSync(outputDir, {recursive: true});
129
+ fs.copyFileSync(srcPath, path.join(outputDir, 'page.tsx'));
130
+ humanLog(`✓ Template created at ${path.relative(targetDir, outputDir)}/page.tsx`);
131
+ }
132
+
133
+ // ─── Command ─────────────────────────────────────────────────────────────────
134
+
135
+ /**
136
+ * @param {import('commander').Command} program
137
+ */
138
+ export function registerInit(program) {
139
+ program
140
+ .command('init')
141
+ .description('Initialize the design system in your project')
142
+ .option('--features <list>', 'Comma-separated features to install (agents, theme, template)')
143
+ .option('--all', 'Install all features, no prompts')
144
+ .option('--remove-agents', 'Remove AI agent docs from all agent doc files')
145
+ .option('--agent <tool>', 'Target AI tool for agent docs: claude, cursor, codex, hermes, all')
146
+ .option('--agent-docs-path <path...>', 'Explicit file path(s) for agent docs')
147
+ .action((/** @type {{features?: string, all?: boolean, removeAgents?: boolean, agent?: string, agentDocsPath?: string | string[]}} */ options) => {
148
+ const targetDir = process.cwd();
149
+
150
+ // Remove mode
151
+ if (options.removeAgents) {
152
+ removeAgentDocs(targetDir);
153
+ humanLog('✓ AI agent docs removed.');
154
+ return;
155
+ }
156
+
157
+ // Non-interactive: --features or --all
158
+ if (options.features || options.all) {
159
+ const features = options.all
160
+ ? VALID_FEATURES
161
+ : /** @type {string} */ (options.features).split(',').map(f => f.trim().toLowerCase());
162
+
163
+ const invalid = features.filter(f => !VALID_FEATURES.includes(f));
164
+ if (invalid.length > 0) {
165
+ cliError(`Unknown features: ${invalid.join(', ')}. Valid features: ${VALID_FEATURES.join(', ')}`, {code: ERROR_CODES.ERR_UNKNOWN_FEATURE});
166
+ return;
167
+ }
168
+
169
+ for (const feature of features) {
170
+ if (feature === 'agents') runAgents(targetDir, {
171
+ agent: options.agent,
172
+ agentDocsPath: options.agentDocsPath,
173
+ });
174
+ if (feature === 'theme') runTheme();
175
+ if (feature === 'template') runTemplate(targetDir, {});
176
+ }
177
+ return;
178
+ }
179
+
180
+ // No flags: TTY-free default. `astryx init` installs the AI agent cheat
181
+ // sheet with NO prompts, so it behaves identically for humans, agents, CI,
182
+ // and piped I/O — it never hangs or errors on a missing TTY. (A guided
183
+ // `--interactive` setup may return later as an explicit opt-in.)
184
+ runAgents(targetDir, {
185
+ agent: options.agent,
186
+ agentDocsPath: options.agentDocsPath,
187
+ });
188
+ humanLog('');
189
+ humanLog(` Tip: \`${run} init --all\` also points you to the theme and page-building workflows.`);
190
+
191
+ for (const line of getNextSteps(run)) {
192
+ humanLog(line);
193
+ }
194
+ });
195
+ }
@@ -23,7 +23,7 @@ import {describe, it, expect} from 'vitest';
23
23
  import {getNextSteps} from './init.mjs';
24
24
 
25
25
  describe('init Next steps theme guidance', () => {
26
- const text = getNextSteps('npx').join('\n');
26
+ const text = getNextSteps('npx astryx').join('\n');
27
27
 
28
28
  it('mentions the base CSS imports so the app is not left unstyled', () => {
29
29
  expect(text).toContain("'@astryxdesign/core/reset.css'");
@@ -0,0 +1,66 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file Subprocess no-hang tests for `astryx init`.
5
+ *
6
+ * TTY was removed from the CLI: `init` is non-interactive by default and must
7
+ * run cleanly (exit 0, writing the agent cheat sheet) instead of hanging on a
8
+ * prompt. These tests spawn the CLI with stdin/stdout NOT a TTY (the CI / piped
9
+ * / agent condition). A hang would show as signal SIGTERM + status null; we
10
+ * assert `signal === null && status === 0` to prove it exits cleanly and works.
11
+ */
12
+
13
+ import {describe, it, expect, beforeEach, afterEach} from 'vitest';
14
+ import {spawnSync} from 'node:child_process';
15
+ import * as fs from 'node:fs';
16
+ import * as path from 'node:path';
17
+ import * as os from 'node:os';
18
+ import {fileURLToPath} from 'node:url';
19
+
20
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
21
+ const CLI = path.resolve(__dirname, '..', '..', 'bin', 'astryx.mjs');
22
+
23
+ let tmpDir;
24
+
25
+ function runCli(args) {
26
+ return spawnSync(process.execPath, [CLI, ...args], {
27
+ cwd: tmpDir,
28
+ encoding: 'utf8',
29
+ timeout: 20_000,
30
+ stdio: ['ignore', 'pipe', 'pipe'],
31
+ env: {...process.env, FORCE_COLOR: '0', CI: ''},
32
+ });
33
+ }
34
+
35
+ beforeEach(() => {
36
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-interactive-guard-'));
37
+ });
38
+
39
+ afterEach(() => {
40
+ fs.rmSync(tmpDir, {recursive: true, force: true});
41
+ });
42
+
43
+ describe('init is non-interactive by default (no TTY needed)', () => {
44
+ it('runs cleanly (exit 0, no hang) and writes agent docs', () => {
45
+ const r = runCli(['init']);
46
+ expect(r.signal).toBeNull(); // did not hang
47
+ expect(r.status).toBe(0); // succeeded without a TTY
48
+ expect(fs.readdirSync(tmpDir).length).toBeGreaterThan(0); // wrote the cheat sheet
49
+ });
50
+
51
+ it('writes an agent-doc file containing the ASTRYX cheat-sheet marker', () => {
52
+ runCli(['init']);
53
+ // init injects into existing agent-doc files if present, else creates AGENTS.md
54
+ const candidates = ['AGENTS.md', 'CLAUDE.md', '.cursorrules', path.join('.claude', 'CLAUDE.md')];
55
+ const doc = candidates.find(f => fs.existsSync(path.join(tmpDir, f)));
56
+ expect(doc).toBeTruthy();
57
+ expect(fs.readFileSync(path.join(tmpDir, doc), 'utf8')).toMatch(/ASTRYX:START/);
58
+ });
59
+
60
+ it('still runs --features agents non-interactively', () => {
61
+ const r = runCli(['init', '--features', 'agents']);
62
+ expect(r.signal).toBeNull();
63
+ expect(r.status).toBe(0);
64
+ expect(fs.readdirSync(tmpDir).length).toBeGreaterThan(0);
65
+ });
66
+ });
@@ -45,7 +45,7 @@ function runCli(args, {cwd} = {}) {
45
45
 
46
46
  function parseJson(stdout) {
47
47
  // CLI emits a single JSON document. If anything else snuck onto stdout
48
- // (clack output, console.log strings, etc.) JSON.parse will throw —
48
+ // (log output, console.log strings, etc.) JSON.parse will throw —
49
49
  // which is exactly the failure mode we want to catch in tests.
50
50
  return JSON.parse(stdout);
51
51
  }
@@ -61,7 +61,7 @@ afterEach(() => {
61
61
  });
62
62
 
63
63
  describe('--json contract: rejects before side effects', () => {
64
- it('astryx init --json --features agents does not write .claude/CLAUDE.md', () => {
64
+ it('astryx init --json --features agents does not write agent docs', () => {
65
65
  const before = fs.readdirSync(tmpDir);
66
66
  expect(before).toEqual([]);
67
67
 
@@ -13,21 +13,55 @@
13
13
  */
14
14
 
15
15
  import * as fs from 'node:fs';
16
- import {jsonOut, humanLog} from '../lib/json.mjs';
17
- import {cliError} from '../lib/cli-error.mjs';
18
- import {layoutExpand, layoutCheck, layoutGrammar} from '../api/layout.mjs';
16
+ import {jsonOut, humanLog} from '../../lib/json.mjs';
17
+ import {cliError} from '../../lib/cli-error.mjs';
18
+ import {layoutExpand, layoutCheck, layoutGrammar} from '../../api/layout/layout.mjs';
19
19
 
20
- /** Resolve the expression from arg, --file, or stdin ('-'). */
21
- async function readExpression(expr, options) {
20
+ /**
21
+ * The api layer's @returns for these functions widen the `type` discriminator
22
+ * to `string`, so annotate the command-local result with the precise response
23
+ * shapes from src/types/layout so narrowing + jsonOut typecheck.
24
+ *
25
+ * @typedef {import('../../types/layout').LayoutExpandResponse} LayoutExpandResponse
26
+ * @typedef {import('../../types/layout').LayoutCheckResponse} LayoutCheckResponse
27
+ * @typedef {import('../../types/layout').LayoutGrammarResponse} LayoutGrammarResponse
28
+ */
29
+
30
+ /**
31
+ * @typedef {object} LayoutExpandOptions
32
+ * @property {string} [file]
33
+ * @property {'compact'|'outline'|'auto'} [form]
34
+ * @property {string} [name]
35
+ * @property {boolean} [loose]
36
+ */
37
+
38
+ /**
39
+ * @typedef {object} LayoutCheckOptions
40
+ * @property {string} [file]
41
+ * @property {'compact'|'outline'|'auto'} [form]
42
+ * @property {boolean} [loose]
43
+ */
44
+
45
+ /**
46
+ * Resolve the expression from arg, --file, or stdin ('-').
47
+ * @param {string} [expr]
48
+ * @param {{file?: string}} [options]
49
+ * @returns {Promise<string>}
50
+ */
51
+ async function readExpression(expr, options = {}) {
22
52
  if (options.file) return fs.readFileSync(options.file, 'utf-8');
23
- if (expr === '-' || (!expr && !process.stdin.isTTY)) {
53
+ if (expr === '-') {
54
+ /** @type {Buffer[]} */
24
55
  const chunks = [];
25
- for await (const chunk of process.stdin) chunks.push(chunk);
56
+ for await (const chunk of process.stdin) chunks.push(/** @type {Buffer} */ (chunk));
26
57
  return Buffer.concat(chunks).toString('utf-8');
27
58
  }
28
- return expr;
59
+ return expr ?? '';
29
60
  }
30
61
 
62
+ /**
63
+ * @param {import('commander').Command} program
64
+ */
31
65
  export function registerLayout(program) {
32
66
  const layoutCmd = program
33
67
  .command('layout')
@@ -40,24 +74,26 @@ export function registerLayout(program) {
40
74
  .option('--form <form>', 'Input surface: compact, outline, or auto', 'auto')
41
75
  .option('--name <name>', 'Generated component name (PascalCase)', 'GeneratedLayout')
42
76
  .option('--loose', 'Downgrade unknown {block} hints to TODO placeholders')
43
- .action(async (expression, targetPath, options) => {
77
+ .action(async (/** @type {string} */ expression, /** @type {string} */ targetPath, /** @type {LayoutExpandOptions} */ options) => {
44
78
  const json = program.opts().json || false;
45
79
  const source = await readExpression(expression, options);
46
80
  if (!source || source.trim() === '') {
47
81
  cliError('No layout expression given — pass it as an argument, via --file, or on stdin');
48
82
  return;
49
83
  }
84
+ /** @type {LayoutExpandResponse} */
50
85
  let result;
51
86
  try {
52
- result = await layoutExpand(source, {
87
+ result = /** @type {LayoutExpandResponse} */ (await layoutExpand(source, {
53
88
  targetPath,
54
89
  form: options.form,
55
90
  loose: options.loose || false,
56
91
  name: options.name,
57
92
  cwd: process.cwd(),
58
- });
93
+ }));
59
94
  } catch (e) {
60
- cliError(e.message, {suggestions: e.suggestions || [], code: e.code});
95
+ const err = /** @type {import('../../api/error.mjs').AstryxError} */ (e);
96
+ cliError(err.message, {suggestions: err.suggestions || [], code: err.code});
61
97
  return;
62
98
  }
63
99
  if (json) return jsonOut(result.type, result.data);
@@ -81,22 +117,24 @@ export function registerLayout(program) {
81
117
  .option('--file <file>', 'Read the expression from a file')
82
118
  .option('--form <form>', 'Input surface: compact, outline, or auto', 'auto')
83
119
  .option('--loose', 'Downgrade unknown {block} hints to TODO placeholders')
84
- .action(async (expression, options) => {
120
+ .action(async (/** @type {string} */ expression, /** @type {LayoutCheckOptions} */ options) => {
85
121
  const json = program.opts().json || false;
86
122
  const source = await readExpression(expression, options);
87
123
  if (!source || source.trim() === '') {
88
124
  cliError('No layout expression given — pass it as an argument, via --file, or on stdin');
89
125
  return;
90
126
  }
127
+ /** @type {LayoutCheckResponse} */
91
128
  let result;
92
129
  try {
93
- result = await layoutCheck(source, {
130
+ result = /** @type {LayoutCheckResponse} */ (await layoutCheck(source, {
94
131
  form: options.form,
95
132
  loose: options.loose || false,
96
133
  cwd: process.cwd(),
97
- });
134
+ }));
98
135
  } catch (e) {
99
- cliError(e.message, {suggestions: e.suggestions || [], code: e.code});
136
+ const err = /** @type {import('../../api/error.mjs').AstryxError} */ (e);
137
+ cliError(err.message, {suggestions: err.suggestions || [], code: err.code});
100
138
  return;
101
139
  }
102
140
  if (json) return jsonOut(result.type, result.data);
@@ -106,7 +144,7 @@ export function registerLayout(program) {
106
144
  humanLog(`\n✗ Invalid (${errors.length} error${errors.length === 1 ? '' : 's'}):`);
107
145
  for (const e of errors) {
108
146
  humanLog(` - ${e.formatted}`);
109
- if (e.suggestions?.length > 0) humanLog(` did you mean: ${e.suggestions.join(', ')}?`);
147
+ if (e.suggestions && e.suggestions.length > 0) humanLog(` did you mean: ${e.suggestions.join(', ')}?`);
110
148
  }
111
149
  humanLog('');
112
150
  process.exitCode = 1;
@@ -126,11 +164,13 @@ export function registerLayout(program) {
126
164
  .description('Print the XLE/XLO cheatsheet (alias table generated from this branch)')
127
165
  .action(async () => {
128
166
  const json = program.opts().json || false;
167
+ /** @type {LayoutGrammarResponse} */
129
168
  let result;
130
169
  try {
131
- result = await layoutGrammar({cwd: process.cwd()});
170
+ result = /** @type {LayoutGrammarResponse} */ (await layoutGrammar({cwd: process.cwd()}));
132
171
  } catch (e) {
133
- cliError(e.message, {suggestions: e.suggestions || [], code: e.code});
172
+ const err = /** @type {import('../../api/error.mjs').AstryxError} */ (e);
173
+ cliError(err.message, {suggestions: err.suggestions || [], code: err.code});
134
174
  return;
135
175
  }
136
176
  if (json) return jsonOut(result.type, result.data);