@astryxdesign/cli 0.7.2 → 0.7.3-canary.02f5fe4

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 (373) hide show
  1. package/README.md +104 -71
  2. package/api/build/_adapter.d.mts +58 -0
  3. package/api/build/_adapter.mjs +127 -3
  4. package/api/build/blocks/blocks.d.mts +92 -0
  5. package/api/build/blocks/blocks.mjs +528 -0
  6. package/api/build/blocks/blocks.test.mjs +238 -0
  7. package/api/build/blocks/blocks.weights.json +1 -0
  8. package/api/build/build.mjs +25 -6
  9. package/api/build/build.type.mjs +1 -1
  10. package/api/build/kit/answer.d.mts +18 -0
  11. package/api/build/kit/answer.mjs +195 -0
  12. package/api/build/kit/answer.test.mjs +108 -0
  13. package/api/build/kit/kind.d.mts +25 -0
  14. package/api/build/kit/kind.mjs +170 -0
  15. package/api/build/kit/kit.d.mts +2 -4
  16. package/api/build/kit/kit.mjs +8 -398
  17. package/api/build/kit/rank.d.mts +3 -32
  18. package/api/build/kit/rank.mjs +25 -342
  19. package/api/build/kit/rank.test.mjs +2 -1
  20. package/api/build/kit/signals.d.mts +79 -0
  21. package/api/build/kit/signals.mjs +295 -0
  22. package/api/build/kit/start.d.mts +32 -0
  23. package/api/build/kit/start.mjs +163 -0
  24. package/api/build/kit/words.d.mts +72 -0
  25. package/api/build/kit/words.mjs +202 -0
  26. package/api/build/tree.d.mts +20 -0
  27. package/api/build/tree.mjs +53 -0
  28. package/api/build/tree.test.mjs +185 -0
  29. package/api/build/tree.weights.json +1 -0
  30. package/api/component/_adapter.mjs +39 -5
  31. package/api/component/component-replacement.test.mjs +1 -0
  32. package/api/component/component.mjs +17 -3
  33. package/api/component/component.test.mjs +15 -0
  34. package/api/component/doc-links.test.mjs +2 -2
  35. package/api/discover/_adapter.d.mts +1 -2
  36. package/api/discover/_adapter.mjs +42 -17
  37. package/api/discover/_adapter.test.mjs +42 -0
  38. package/api/discover/discover.mjs +0 -1
  39. package/api/docs/_adapter.d.mts +2 -2
  40. package/api/docs/_adapter.mjs +82 -23
  41. package/api/docs/docs-homes.test.mjs +127 -0
  42. package/api/docs/docs.doc.mjs +3 -3
  43. package/api/docs/docs.test.mjs +35 -19
  44. package/api/docs/integration-tree.test.mjs +20 -20
  45. package/api/docs/integrationDocs.test.mjs +1 -1
  46. package/api/docs/namespace-section.test.mjs +6 -6
  47. package/api/docs/reference-blocks.test.mjs +2 -2
  48. package/api/doctor/_adapter.d.mts +33 -13
  49. package/api/doctor/_adapter.mjs +55 -25
  50. package/api/doctor/doctor.d.mts +23 -5
  51. package/api/doctor/doctor.mjs +183 -40
  52. package/api/doctor/doctor.test.mjs +95 -12
  53. package/api/hook/hook.mjs +1 -1
  54. package/api/init/init.doc.mjs +20 -6
  55. package/api/init/init.test.mjs +18 -2
  56. package/api/init/init.type.d.mts +3 -3
  57. package/api/init/init.type.mjs +3 -3
  58. package/api/init/remove/remove.mjs +17 -4
  59. package/api/init/remove/remove.test.mjs +114 -2
  60. package/api/init/run/run.mjs +69 -6
  61. package/api/integration/_adapter.d.mts +8 -0
  62. package/api/integration/_adapter.mjs +21 -1
  63. package/api/integration/authoring-checks.mjs +83 -9
  64. package/api/integration/authoring-checks.test.mjs +220 -0
  65. package/api/integration/pack-check.mjs +123 -4
  66. package/api/integration/pack-check.test.mjs +87 -0
  67. package/api/integration/validate-integration-case-disk.test.mjs +111 -0
  68. package/api/integration/validate-integration-fixes.test.mjs +6 -5
  69. package/api/integration/validate-integration-gaps.test.mjs +1975 -0
  70. package/api/integration/validate-integration.d.mts +15 -2
  71. package/api/integration/validate-integration.mjs +298 -39
  72. package/api/integration/validate-integration.type.d.mts +90 -1
  73. package/api/integration/validate-integration.type.mjs +67 -2
  74. package/api/integration/validateIntegration.doc.mjs +1 -1
  75. package/api/search/ORDERING.md +28 -0
  76. package/api/search/_adapter.d.mts +82 -2
  77. package/api/search/_adapter.mjs +157 -10
  78. package/api/search/_adapter.test.mjs +173 -0
  79. package/api/search/_candidate-score.d.mts +40 -0
  80. package/api/search/_candidate-score.mjs +238 -0
  81. package/api/search/_cards.json +6098 -0
  82. package/api/search/_compound.d.mts +50 -0
  83. package/api/search/_compound.mjs +170 -0
  84. package/api/search/_doc-searcher.d.mts +4 -0
  85. package/api/search/_doc-searcher.mjs +23 -0
  86. package/api/search/_doc-searcher.test.mjs +36 -0
  87. package/api/search/_doc-searcher.weights.json +1 -0
  88. package/api/search/_doc-text.d.mts +30 -0
  89. package/api/search/_doc-text.mjs +87 -0
  90. package/api/search/_entries.d.mts +63 -0
  91. package/api/search/_entries.mjs +232 -0
  92. package/api/search/_entries.test.mjs +48 -0
  93. package/api/search/_features.d.mts +35 -0
  94. package/api/search/_features.mjs +155 -0
  95. package/api/search/_gather-components.d.mts +26 -0
  96. package/api/search/_gather-components.mjs +284 -0
  97. package/api/search/_gather-docs.d.mts +20 -0
  98. package/api/search/_gather-docs.mjs +254 -0
  99. package/api/search/_gather-hooks.d.mts +12 -0
  100. package/api/search/_gather-hooks.mjs +52 -0
  101. package/api/search/_gather-templates.d.mts +25 -0
  102. package/api/search/_gather-templates.mjs +107 -0
  103. package/api/search/_lexical.d.mts +135 -0
  104. package/api/search/_lexical.mjs +351 -0
  105. package/api/search/_main-searcher.d.mts +19 -0
  106. package/api/search/_main-searcher.mjs +115 -0
  107. package/api/search/_main-searcher.test.mjs +106 -0
  108. package/api/search/_main-searcher.weights.json +1 -0
  109. package/api/search/_module-doc.d.mts +32 -0
  110. package/api/search/_module-doc.mjs +78 -0
  111. package/api/search/_order.d.mts +32 -0
  112. package/api/search/_order.mjs +162 -0
  113. package/api/search/_order.test.mjs +72 -0
  114. package/api/search/_priority.d.mts +50 -0
  115. package/api/search/_priority.mjs +139 -0
  116. package/api/search/_query-words.d.mts +25 -0
  117. package/api/search/_query-words.mjs +92 -0
  118. package/api/search/_query.d.mts +65 -0
  119. package/api/search/_query.mjs +176 -0
  120. package/api/search/_result.d.mts +97 -0
  121. package/api/search/_result.mjs +150 -0
  122. package/api/search/_score.d.mts +60 -0
  123. package/api/search/_score.mjs +283 -0
  124. package/api/search/_synonyms.d.mts +21 -0
  125. package/api/search/_synonyms.mjs +67 -0
  126. package/api/search/_template-searcher.d.mts +4 -0
  127. package/api/search/_template-searcher.mjs +23 -0
  128. package/api/search/_template-searcher.test.mjs +40 -0
  129. package/api/search/_template-searcher.weights.json +1 -0
  130. package/api/search/_titles.d.mts +33 -0
  131. package/api/search/_titles.mjs +65 -0
  132. package/api/search/_words.d.mts +64 -0
  133. package/api/search/_words.mjs +157 -0
  134. package/api/search/search.d.mts +22 -305
  135. package/api/search/search.doc.mjs +8 -6
  136. package/api/search/search.mjs +56 -2482
  137. package/api/search/search.test.mjs +14 -14
  138. package/api/search/search.type.d.mts +6 -1
  139. package/api/search/search.type.mjs +6 -1
  140. package/api/search/tree.d.mts +15 -0
  141. package/api/search/tree.mjs +40 -0
  142. package/api/search/tree.test.mjs +66 -0
  143. package/api/search/tree.weights.json +1 -0
  144. package/api/template/template.mjs +5 -1
  145. package/api/theme/build/build.packed-old-core.test.mjs +1 -0
  146. package/api/upgrade/_adapter.d.mts +57 -9
  147. package/api/upgrade/_adapter.mjs +198 -28
  148. package/api/upgrade/registry/registry.d.mts +10 -4
  149. package/api/upgrade/registry/registry.mjs +33 -16
  150. package/api/upgrade/registry/registry.test.mjs +85 -0
  151. package/api/upgrade/run/run.d.mts +4 -2
  152. package/api/upgrade/run/run.mjs +13 -2
  153. package/api/upgrade/run/run.test.mjs +417 -3
  154. package/api/upgrade/upgrade.doc.mjs +10 -5
  155. package/api/upgrade/upgrade.mjs +13 -2
  156. package/api/upgrade/upgrade.test.mjs +194 -0
  157. package/api/upgrade/upgrade.type.d.mts +5 -3
  158. package/api/upgrade/upgrade.type.mjs +8 -6
  159. package/assets/codemods/transforms/v0.7.0/__tests__/validate-data-token-ownership.test.mjs +11 -0
  160. package/assets/codemods/transforms/v0.7.0/validate-data-token-ownership.mjs +3 -3
  161. package/assets/docs/author-a-theme.doc.mjs +2 -2
  162. package/assets/docs/is-my-code-good.doc.mjs +287 -0
  163. package/assets/docs/tree/add-a-component.doc.mjs +1 -1
  164. package/assets/docs/tree/add-a-theme.doc.mjs +1 -1
  165. package/assets/docs/tree/add-a-topic.doc.mjs +2 -2
  166. package/assets/docs/tree/agent-guidance.doc.mjs +1 -1
  167. package/assets/docs/tree/api.doc.mjs +6 -0
  168. package/assets/docs/tree/{integrations.doc.mjs → authoring.doc.mjs} +15 -6
  169. package/assets/docs/tree/block-template.doc.mjs +1 -1
  170. package/assets/docs/tree/build-the-template.doc.mjs +1 -1
  171. package/assets/docs/tree/building-blocks.doc.mjs +2 -2
  172. package/assets/docs/tree/check-the-props.doc.mjs +1 -1
  173. package/assets/docs/tree/check-your-docs.doc.mjs +2 -2
  174. package/assets/docs/tree/checks.doc.mjs +3 -3
  175. package/assets/docs/tree/ci-and-containers.doc.mjs +13 -0
  176. package/assets/docs/tree/cli-command.doc.mjs +1 -1
  177. package/assets/docs/tree/cli.doc.mjs +1 -1
  178. package/assets/docs/tree/codemods.doc.mjs +1 -1
  179. package/assets/docs/tree/component-family.doc.mjs +1 -1
  180. package/assets/docs/tree/component-imports.doc.mjs +1 -1
  181. package/assets/docs/tree/components.doc.mjs +1 -1
  182. package/assets/docs/tree/configuration.doc.mjs +1 -1
  183. package/assets/docs/tree/debug-and-gap-reports.doc.mjs +1 -1
  184. package/assets/docs/tree/define-the-theme.doc.mjs +1 -1
  185. package/assets/docs/tree/describe-the-component.doc.mjs +1 -1
  186. package/assets/docs/tree/docs.doc.mjs +1 -1
  187. package/assets/docs/tree/document-the-template.doc.mjs +1 -1
  188. package/assets/docs/tree/document-the-theme.doc.mjs +1 -1
  189. package/assets/docs/tree/export-template-assets.doc.mjs +1 -1
  190. package/assets/docs/tree/extend-or-replace.doc.mjs +1 -1
  191. package/assets/docs/{authoring.doc.mjs → tree/field-reference.doc.mjs} +4 -3
  192. package/assets/docs/tree/fonts-and-assets.doc.mjs +1 -1
  193. package/assets/docs/tree/generate-a-palette.doc.mjs +1 -1
  194. package/assets/docs/tree/global-integrations.doc.mjs +2 -2
  195. package/assets/docs/tree/grade-template-with-agent.doc.mjs +4 -4
  196. package/assets/docs/tree/help.doc.mjs +2 -2
  197. package/assets/docs/tree/host-entry.doc.mjs +128 -0
  198. package/assets/docs/tree/links.doc.mjs +1 -1
  199. package/assets/docs/tree/package-and-test.doc.mjs +1 -1
  200. package/assets/docs/tree/page-template.doc.mjs +1 -1
  201. package/assets/docs/tree/publishing.doc.mjs +1 -1
  202. package/assets/docs/tree/quick-start.doc.mjs +1 -1
  203. package/assets/docs/tree/replace-a-core-component.doc.mjs +1 -1
  204. package/assets/docs/tree/replace-a-core-template.doc.mjs +1 -1
  205. package/assets/docs/tree/sections-and-placement.doc.mjs +1 -1
  206. package/assets/docs/tree/see-it-in-an-app.doc.mjs +1 -1
  207. package/assets/docs/tree/ship.doc.mjs +2 -2
  208. package/assets/docs/tree/short-and-findable.doc.mjs +1 -1
  209. package/assets/docs/tree/single-component.doc.mjs +1 -1
  210. package/assets/docs/tree/start-a-template.doc.mjs +1 -1
  211. package/assets/docs/tree/subcomponent.doc.mjs +1 -1
  212. package/assets/docs/tree/template-assets.doc.mjs +1 -1
  213. package/assets/docs/tree/template-doc-overview.doc.mjs +2 -2
  214. package/assets/docs/tree/template-fonts.doc.mjs +1 -1
  215. package/assets/docs/tree/template-grading-rubric.doc.mjs +17 -38
  216. package/assets/docs/tree/template-icons.doc.mjs +1 -1
  217. package/assets/docs/tree/template-images-media.doc.mjs +1 -1
  218. package/assets/docs/tree/template-styles.doc.mjs +1 -1
  219. package/assets/docs/tree/templates.doc.mjs +1 -1
  220. package/assets/docs/tree/test-in-an-app.doc.mjs +1 -1
  221. package/assets/docs/tree/test-template-in-app.doc.mjs +1 -1
  222. package/assets/docs/tree/themes.doc.mjs +1 -1
  223. package/assets/docs/tree/tokens-data.doc.mjs +2 -2
  224. package/assets/docs/tree/troubleshooting.doc.mjs +1 -1
  225. package/assets/docs/tree/upgrading.doc.mjs +1 -1
  226. package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +1 -1
  227. package/assets/docs/tree/verify-packed-template.doc.mjs +1 -1
  228. package/assets/docs/tree/versioning.doc.mjs +5 -1
  229. package/assets/docs/tree/write-good-templates.doc.mjs +1 -1
  230. package/assets/docs/tree/write-the-template-file.doc.mjs +1 -1
  231. package/assets/docs/use-integrations.doc.mjs +120 -0
  232. package/assets/docs/working-with-ai.doc.mjs +6 -2
  233. package/assets/templates/themes/butter/butterTheme.ts +4 -2
  234. package/assets/templates/themes/stone/stoneTheme.ts +4 -2
  235. package/assets/templates/themes/y2k/y2kTheme.ts +4 -2
  236. package/authoring/debug/parse.d.mts +1 -1
  237. package/authoring/doctypes/schema/schema.doc.mjs +1 -1
  238. package/authoring/integration/integration.doc.mjs +1 -1
  239. package/authoring/integration/type.ts +1 -1
  240. package/clients/cli/bin/astryx.mjs +7 -78
  241. package/clients/cli/command-result-coverage.test.mjs +6 -5
  242. package/clients/cli/commands/build-theme.color-scheme.test.mjs +1 -1
  243. package/clients/cli/commands/{component/index.mjs → component.mjs} +37 -37
  244. package/clients/cli/commands/component.test.mjs +6 -6
  245. package/clients/cli/commands/docs.doc.mjs +2 -2
  246. package/clients/cli/commands/docs.mjs +1 -1
  247. package/clients/cli/commands/docs.test.mjs +14 -14
  248. package/clients/cli/commands/doctor-integration.test.mjs +49 -1
  249. package/clients/cli/commands/doctor.doc.mjs +1 -1
  250. package/clients/cli/commands/doctor.mjs +40 -0
  251. package/clients/cli/commands/doctor.test.mjs +65 -0
  252. package/clients/cli/commands/{hook/index.mjs → hook.mjs} +22 -22
  253. package/clients/cli/commands/import-hint-correctness.test.mjs +1 -1
  254. package/clients/cli/commands/init.behavior.test.mjs +230 -1
  255. package/clients/cli/commands/init.doc.mjs +28 -9
  256. package/clients/cli/commands/init.flag-help.test.mjs +1 -0
  257. package/clients/cli/commands/init.mjs +2 -2
  258. package/clients/cli/commands/integration-check.test.mjs +48 -0
  259. package/clients/cli/commands/integration.doc.mjs +1 -1
  260. package/clients/cli/commands/manifest.doc.mjs +1 -1
  261. package/clients/cli/commands/manifest.mjs +67 -0
  262. package/clients/cli/commands/outside-project.test.mjs +101 -1
  263. package/clients/cli/commands/postinstall.doc.mjs +23 -0
  264. package/clients/cli/commands/postinstall.mjs +31 -0
  265. package/clients/cli/commands/setup-nudge.test.mjs +78 -0
  266. package/clients/cli/commands/{build-theme.mjs → theme.mjs} +1 -1
  267. package/clients/cli/commands/upgrade.doc.mjs +4 -2
  268. package/clients/cli/commands/upgrade.mjs +9 -2
  269. package/clients/cli/commands/upgrade.subfolder-path.test.mjs +97 -0
  270. package/clients/cli/index.mjs +54 -94
  271. package/clients/cli/lib/component-format.mjs +1 -1
  272. package/clients/cli/lib/define-command.mjs +12 -7
  273. package/clients/cli/lib/exit-codes.test.mjs +5 -0
  274. package/clients/cli/lib/manifest.test.mjs +12 -0
  275. package/clients/cli/lib/welcome-box.mjs +50 -0
  276. package/clients/cli/run.mjs +108 -0
  277. package/foundation/agent-docs/agent-doc-state.d.mts +32 -7
  278. package/foundation/agent-docs/agent-doc-state.mjs +58 -9
  279. package/foundation/agent-docs/agent-docs.d.mts +100 -12
  280. package/foundation/agent-docs/agent-docs.mjs +1017 -105
  281. package/foundation/agent-docs/agent-docs.path-safety.test.mjs +1239 -17
  282. package/foundation/agent-docs/agent-docs.test.mjs +683 -39
  283. package/foundation/agent-docs/computer-agent-text.d.mts +39 -2
  284. package/foundation/agent-docs/computer-agent-text.mjs +135 -8
  285. package/foundation/agent-docs/computer-agent-text.test.mjs +99 -2
  286. package/foundation/config/project.mjs +20 -22
  287. package/foundation/config/project.test.mjs +56 -0
  288. package/foundation/discovery/authoring-self-docs.d.mts +7 -0
  289. package/foundation/discovery/authoring-self-docs.mjs +16 -4
  290. package/foundation/discovery/authoring-self-docs.test.mjs +15 -3
  291. package/foundation/discovery/authoring-surface.d.mts +1 -1
  292. package/foundation/discovery/authoring-surface.mjs +2 -2
  293. package/foundation/discovery/authoring-surface.test.mjs +1 -1
  294. package/foundation/discovery/cli-self-docs.d.mts +2 -1
  295. package/foundation/discovery/cli-self-docs.mjs +4 -3
  296. package/foundation/discovery/cli-self-docs.test.mjs +2 -2
  297. package/foundation/discovery/component-discovery.d.mts +37 -0
  298. package/foundation/discovery/component-discovery.mjs +80 -5
  299. package/foundation/discovery/component-loader.d.mts +1 -1
  300. package/foundation/discovery/docs-discovery.mjs +14 -1
  301. package/foundation/discovery/module-exports.d.mts +41 -0
  302. package/foundation/discovery/module-exports.mjs +253 -0
  303. package/foundation/discovery/runner-allowlist.d.mts +2 -4
  304. package/foundation/discovery/runner-allowlist.mjs +2 -4
  305. package/foundation/discovery/runner-commands.d.mts +77 -0
  306. package/foundation/discovery/runner-commands.mjs +294 -0
  307. package/foundation/discovery/runner-commands.test.mjs +128 -0
  308. package/foundation/discovery/stray-pointers.test.mjs +1 -1
  309. package/foundation/discovery/template-adapter.mjs +18 -1
  310. package/foundation/discovery/template-needs.mjs +1 -44
  311. package/foundation/discovery/theme-discovery.mjs +147 -3
  312. package/foundation/doc-compiler/doc-loads.test.mjs +33 -9
  313. package/foundation/doc-compiler/read.mjs +59 -1
  314. package/foundation/doc-compiler/read.test.mjs +11 -2
  315. package/foundation/doc-compiler/split-topic-keywords.test.mjs +3 -2
  316. package/foundation/doc-compiler/tree.d.mts +32 -6
  317. package/foundation/doc-compiler/tree.mjs +77 -43
  318. package/foundation/doc-compiler/tree.test.mjs +7 -7
  319. package/foundation/fs/paths.d.mts +70 -8
  320. package/foundation/fs/paths.mjs +556 -38
  321. package/foundation/fs/paths.test.mjs +430 -54
  322. package/foundation/fs/read-record.d.mts +18 -0
  323. package/foundation/fs/read-record.mjs +131 -0
  324. package/foundation/fs/read-record.test.mjs +77 -0
  325. package/foundation/integrations/autolink.d.mts +2 -1
  326. package/foundation/integrations/autolink.mjs +4 -13
  327. package/foundation/integrations/cli-peer-version.d.mts +36 -0
  328. package/foundation/integrations/cli-peer-version.mjs +132 -0
  329. package/foundation/integrations/cli-peer-version.test.mjs +354 -0
  330. package/foundation/integrations/computer-integrations.d.mts +6 -3
  331. package/foundation/integrations/computer-integrations.mjs +38 -9
  332. package/foundation/integrations/contribution-inventory.d.mts +15 -20
  333. package/foundation/integrations/contribution-inventory.mjs +41 -5
  334. package/foundation/integrations/exports-subpaths.d.mts +57 -0
  335. package/foundation/integrations/exports-subpaths.mjs +113 -0
  336. package/foundation/integrations/integrations.d.mts +27 -1
  337. package/foundation/integrations/integrations.mjs +61 -2
  338. package/foundation/integrations/public-imports.d.mts +118 -0
  339. package/foundation/integrations/public-imports.mjs +1769 -0
  340. package/foundation/integrations/public-imports.test.mjs +1793 -0
  341. package/foundation/integrations/validate-contributions.d.mts +115 -0
  342. package/foundation/integrations/validate-contributions.mjs +1168 -13
  343. package/foundation/release/cache.test.mjs +24 -21
  344. package/foundation/release/global-state.d.mts +8 -4
  345. package/foundation/release/global-state.mjs +4 -2
  346. package/foundation/release/handoff.d.mts +23 -4
  347. package/foundation/release/handoff.mjs +175 -8
  348. package/foundation/release/notices.d.mts +62 -8
  349. package/foundation/release/notices.mjs +136 -25
  350. package/foundation/release/notices.test.mjs +21 -0
  351. package/foundation/release/run-context.mjs +14 -0
  352. package/foundation/response/response-types.doc.mjs +1 -1
  353. package/foundation/route/route.d.mts +285 -0
  354. package/foundation/route/route.mjs +324 -0
  355. package/foundation/route/route.test.mjs +153 -0
  356. package/foundation/runtime/hosted-runtime.test.mjs +403 -0
  357. package/foundation/runtime/locked-versions.d.mts +32 -0
  358. package/foundation/runtime/locked-versions.mjs +65 -0
  359. package/foundation/runtime/package-root.d.mts +91 -0
  360. package/foundation/runtime/package-root.mjs +174 -0
  361. package/foundation/runtime/package-set.d.mts +62 -0
  362. package/foundation/runtime/package-set.mjs +130 -0
  363. package/foundation/runtime/package-set.test.mjs +432 -0
  364. package/foundation/text/string-utils.d.mts +2 -1
  365. package/foundation/text/string-utils.mjs +16 -5
  366. package/foundation/text/suggest-order.d.mts +72 -0
  367. package/foundation/text/suggest-order.mjs +363 -0
  368. package/foundation/text/suggest-order.test.mjs +259 -0
  369. package/host/door-conformance.test.mjs +274 -0
  370. package/host/index.d.ts +116 -0
  371. package/host/index.mjs +295 -0
  372. package/host/index.test.mjs +302 -0
  373. package/package.json +14 -9
@@ -0,0 +1,31 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file postinstall command — the hidden `astryx postinstall` hook.
5
+ *
6
+ * Prints the welcome box (lib/welcome-box.mjs). Hidden from help and the
7
+ * manifest; it looks nothing up and does not support --json.
8
+ *
9
+ * The command surface comes from the colocated CommandDoc via `defineCommand`.
10
+ */
11
+
12
+ import {getCliInvocation} from '../../../foundation/env/package-manager.mjs';
13
+ import {emit, code} from '../formatters/index.mjs';
14
+ import {defineCommand} from '../lib/define-command.mjs';
15
+ import {welcomeBox} from '../lib/welcome-box.mjs';
16
+ import {NO_RESULT_SET} from '../../../foundation/debug/index.mjs';
17
+ import {doc as postinstallCommand} from './postinstall.doc.mjs';
18
+
19
+ /**
20
+ * @param {import('commander').Command} program
21
+ */
22
+ export function registerPostinstall(program) {
23
+ defineCommand(program, postinstallCommand, {
24
+ hidden: true,
25
+ action: () => {
26
+ emit(code(welcomeBox(getCliInvocation())));
27
+ // Prints the welcome box. Nothing is looked up.
28
+ return NO_RESULT_SET;
29
+ },
30
+ });
31
+ }
@@ -121,6 +121,25 @@ describe('enforcement layer 3 — per-command setup nudge', () => {
121
121
  expect(r.stderr).toMatch(NUDGE);
122
122
  });
123
123
 
124
+ it.each([
125
+ ['in the home folder', ''],
126
+ ['at the top of a settings folder in it', '.dotfiles'],
127
+ ])('stays quiet %s, where init writes no agent docs', async (_label, rel) => {
128
+ const saved = {HOME: process.env.HOME, USERPROFILE: process.env.USERPROFILE};
129
+ process.env.HOME = tmp;
130
+ process.env.USERPROFILE = tmp;
131
+ try {
132
+ write(path.join(rel, 'package.json'), '{"name":"t"}');
133
+ const r = await runCli(['docs', 'tokens'], path.join(tmp, rel));
134
+ expect(r.stderr).not.toMatch(NUDGE);
135
+ } finally {
136
+ for (const [key, value] of Object.entries(saved)) {
137
+ if (value === undefined) delete process.env[key];
138
+ else process.env[key] = value;
139
+ }
140
+ }
141
+ });
142
+
124
143
  it("in a project with only Core of another release, names adding the CLI at Core's version first, as the daily hint does", async () => {
125
144
  write(
126
145
  'package.json',
@@ -167,6 +186,65 @@ describe('enforcement layer 3 — per-command setup nudge', () => {
167
186
  expect((await runCli(['docs', 'tokens'], tmp)).stderr).not.toMatch(NUDGE);
168
187
  });
169
188
 
189
+ it('is quiet in a nested package once init has written the docs at the project root', async () => {
190
+ // init run in functions/ writes at the root that declares Astryx.
191
+ write('package.json', JSON.stringify({name: 'app', dependencies: {'@astryxdesign/core': '0.7.0'}}));
192
+ write('AGENTS.md', MARKER);
193
+ write('functions/package.json', '{"name":"functions"}');
194
+ const r = await runCli(['docs', 'tokens'], path.join(tmp, 'functions'));
195
+ expect(r.stderr).not.toMatch(NUDGE);
196
+ });
197
+
198
+ it('nudges in a nested package while the project root has no docs', async () => {
199
+ write('package.json', JSON.stringify({name: 'app', dependencies: {'@astryxdesign/core': '0.7.0'}}));
200
+ write('functions/package.json', '{"name":"functions"}');
201
+ const r = await runCli(['docs', 'tokens'], path.join(tmp, 'functions'));
202
+ expect(r.stderr).toMatch(NUDGE);
203
+ });
204
+
205
+ // A nested package.json that is not the project's (spec AST-040 FR9): the
206
+ // reminder sends the person to the project's folder, never adds the CLI to
207
+ // the nested package.json (spec AST-081 FR2).
208
+ it("in a nested package whose package.json is not the project's, says to run from the project's folder", async () => {
209
+ write('package.json', JSON.stringify({name: 'app', dependencies: {'@astryxdesign/core': '0.7.0'}}));
210
+ write('package-lock.json', '{}');
211
+ write('functions/package.json', '{"name":"functions"}');
212
+ const r = await runCli(['docs', 'tokens'], path.join(tmp, 'functions'));
213
+ // Only `cd` comes first, so the app has no CLI of its own yet: the
214
+ // command is the scoped package at the answering release, never the bare
215
+ // `astryx` name a package runner would fetch from the registry.
216
+ const {version} = JSON.parse(fs.readFileSync(new URL('../../../package.json', import.meta.url), 'utf8'));
217
+ expect(r.stderr.trim()).toBe(
218
+ `Next step: run \`cd ${tmp}\`, then \`npx @astryxdesign/cli@${version} init\` to finish setup and install the Astryx agent prompt.`,
219
+ );
220
+ });
221
+
222
+ // Inside a git repository the project-root check above adds nothing to the
223
+ // walk up to the repository root: the same outcomes as without it.
224
+ it('inside a repository, is quiet in a nested package once init has written the docs at the project root', async () => {
225
+ fs.mkdirSync(path.join(tmp, '.git'));
226
+ write('package.json', JSON.stringify({name: 'app', dependencies: {'@astryxdesign/core': '0.7.0'}}));
227
+ write('AGENTS.md', MARKER);
228
+ write('functions/package.json', '{"name":"functions"}');
229
+ const r = await runCli(['docs', 'tokens'], path.join(tmp, 'functions'));
230
+ expect(r.stderr).not.toMatch(NUDGE);
231
+ });
232
+
233
+ it('inside a repository, still nudges when the only docs lie above the repository root', async () => {
234
+ write('package.json', JSON.stringify({name: 'outer', dependencies: {'@astryxdesign/core': '0.7.0'}}));
235
+ write('AGENTS.md', MARKER);
236
+ fs.mkdirSync(path.join(tmp, 'repo', '.git'), {recursive: true});
237
+ write('repo/package.json', '{"name":"repo"}');
238
+ // It declares Astryx: a folder that declares nothing is outside a
239
+ // project, where no reminder prints (spec AST-081 FR3).
240
+ write(
241
+ 'repo/functions/package.json',
242
+ JSON.stringify({name: 'functions', dependencies: {'@astryxdesign/core': '0.7.0'}}),
243
+ );
244
+ const r = await runCli(['docs', 'tokens'], path.join(tmp, 'repo', 'functions'));
245
+ expect(r.stderr).toMatch(NUDGE);
246
+ });
247
+
170
248
  it('does not nudge for the installer command itself', async () => {
171
249
  asProject();
172
250
  expect((await runCli(['init'], tmp)).stderr).not.toMatch(NUDGE);
@@ -96,7 +96,7 @@ import {doc as themePaletteGenerateFn} from '../../../api/theme/themePaletteGene
96
96
 
97
97
  /**
98
98
  * Path to this CLI's real entry (clients/cli/bin/astryx.mjs), resolved from
99
- * this module's location (clients/cli/commands/build-theme.mjs → ../bin/
99
+ * this module's location (clients/cli/commands/theme.mjs → ../bin/
100
100
  * astryx.mjs). Used to re-invoke `theme build` as a child process in watch mode.
101
101
  */
102
102
  function resolveCliBin() {
@@ -21,6 +21,7 @@ export const doc = {
21
21
  'agent-docs block when Core or configured integration guidance changes. ' +
22
22
  'Dry-run by default. --apply writes codemod and receipt changes, runs hooks, then refreshes agent docs. ' +
23
23
  'Anything a post-codemod hook prints goes to stderr, so stdout carries only the result. ' +
24
+ 'Run from a folder inside a project, it upgrades that project and names files in the result relative to the folder you ran it in. ' +
24
25
  'ShadCN-copied compositions are checked automatically during a normal upgrade, or alone with --registry. ' +
25
26
  'Outside a project, --list lists the codemods this release ships and --registry finds no copied compositions to update; ' +
26
27
  'a migration writes nothing and fails with ERR_VERSION_DETECT, because no installed Core names its target.',
@@ -69,8 +70,9 @@ export const doc = {
69
70
  {
70
71
  flag: '--path <dir>',
71
72
  param: 'options.path',
72
- description: 'Source directory to scan',
73
- default: './src',
73
+ description:
74
+ "Source directory to scan, relative to where you run the command, however you spell it. Without --path, the project's src folder, from any folder in the project",
75
+ default: './src at the project root',
74
76
  },
75
77
  {
76
78
  flag: '--install-deps',
@@ -31,8 +31,9 @@ export function registerUpgrade(program) {
31
31
  action:
32
32
  /**
33
33
  * @param {import('../../../api/upgrade/upgrade.mjs').UpgradeOptions} options
34
+ * @param {import('commander').Command} command
34
35
  */
35
- async options => {
36
+ async (options, command) => {
36
37
  const json = program.opts().json || false;
37
38
  const isList = Boolean(options.list);
38
39
  // Silence the logger for --list so the handler renders from the
@@ -42,8 +43,14 @@ export function registerUpgrade(program) {
42
43
 
43
44
  /** @type {import('../../../api/upgrade/upgrade.type.mjs').UpgradeListResponse | import('../../../api/upgrade/upgrade.type.mjs').UpgradeRegistryResponse | import('../../../api/upgrade/upgrade.type.mjs').UpgradeStatusResponse | import('../../../api/upgrade/upgrade.type.mjs').UpgradeRunResponse} */
44
45
  let result;
46
+ // `--path` shows its default in help, but a path nobody typed is the
47
+ // project's src, not one read from this folder: leave it unset.
48
+ const apiOptions =
49
+ command.getOptionValueSource('path') === 'default'
50
+ ? {...options, path: undefined}
51
+ : options;
45
52
  try {
46
- result = await upgradeApi(options, {cwd: process.cwd()});
53
+ result = await upgradeApi(apiOptions, {cwd: process.cwd()});
47
54
  } catch (e) {
48
55
  // Re-enable the logger so the error is visible in text mode.
49
56
  // For --list the logger was silent; for other modes the API
@@ -0,0 +1,97 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file `astryx upgrade` from a folder inside a project: a `--path` you type
5
+ * is read from that folder, however you spell it, and without `--path` the
6
+ * upgrade scans the project's own src. The option's default must not read as
7
+ * a typed `./src`. The `--json` result names files relative to the folder the
8
+ * command ran in.
9
+ */
10
+
11
+ import {describe, it, expect, beforeEach, afterEach} from 'vitest';
12
+ import * as fs from 'node:fs';
13
+ import * as os from 'node:os';
14
+ import * as path from 'node:path';
15
+ import {runCli} from '../../../test-utils/run-cli.mjs';
16
+
17
+ /** Core 0.7.0 renamed this prop; its codemod renames it on Typeahead. */
18
+ const OLD_PROP_SOURCE =
19
+ "import {Typeahead} from '@astryxdesign/core';\n" +
20
+ 'export const App = () => <Typeahead label="Fruit" emptySearchResultsText="None" />;\n';
21
+
22
+ let root;
23
+
24
+ /** @param {string} rel @param {string} body */
25
+ function write(rel, body) {
26
+ const file = path.join(root, rel);
27
+ fs.mkdirSync(path.dirname(file), {recursive: true});
28
+ fs.writeFileSync(file, body);
29
+ }
30
+
31
+ /** @param {string} rel */
32
+ const renamed = rel =>
33
+ fs
34
+ .readFileSync(path.join(root, rel), 'utf-8')
35
+ .includes('emptySearchText="None"');
36
+
37
+ beforeEach(() => {
38
+ root = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-upgrade-subfolder-'));
39
+ write(
40
+ 'package.json',
41
+ JSON.stringify({
42
+ name: 'app',
43
+ private: true,
44
+ dependencies: {'@astryxdesign/core': '0.7.0'},
45
+ }),
46
+ );
47
+ write(
48
+ 'node_modules/@astryxdesign/core/package.json',
49
+ JSON.stringify({name: '@astryxdesign/core', version: '0.7.0'}),
50
+ );
51
+ write('src/App.tsx', OLD_PROP_SOURCE);
52
+ // A folder inside the project with source of its own and no package.json.
53
+ write('frontend/src/App.tsx', OLD_PROP_SOURCE);
54
+ });
55
+
56
+ afterEach(() => {
57
+ fs.rmSync(root, {recursive: true, force: true});
58
+ });
59
+
60
+ /** @param {string[]} args @param {string} [folder] */
61
+ async function upgradeFrom(args, folder = 'frontend') {
62
+ const r = await runCli(
63
+ ['upgrade', '--from', '0.6.8', ...args, '--apply', '--json'],
64
+ {
65
+ cwd: path.join(root, folder),
66
+ },
67
+ );
68
+ expect(r.status).toBe(0);
69
+ return JSON.parse(r.stdout);
70
+ }
71
+
72
+ describe('upgrade --path from a folder inside the project', () => {
73
+ it.each([['./src'], ['src'], ['src/'], ['./src/']])(
74
+ 'reads --path %s from the folder you ran in',
75
+ async spelling => {
76
+ const result = await upgradeFrom(['--path', spelling]);
77
+
78
+ expect(result.data.modifiedFiles).toEqual(['src/App.tsx']);
79
+ expect(renamed('frontend/src/App.tsx')).toBe(true);
80
+ expect(renamed('src/App.tsx')).toBe(false);
81
+ },
82
+ );
83
+
84
+ it("scans the project's src without --path", async () => {
85
+ const result = await upgradeFrom([]);
86
+
87
+ expect(result.data.modifiedFiles).toEqual(['../src/App.tsx']);
88
+ expect(renamed('src/App.tsx')).toBe(true);
89
+ expect(renamed('frontend/src/App.tsx')).toBe(false);
90
+ });
91
+
92
+ it('names files from the project root when run there', async () => {
93
+ const result = await upgradeFrom([], '.');
94
+
95
+ expect(result.data.modifiedFiles).toEqual(['src/App.tsx']);
96
+ });
97
+ });
@@ -16,23 +16,25 @@ import {Command, Option} from 'commander';
16
16
  import {fileURLToPath} from 'node:url';
17
17
  import * as fs from 'node:fs';
18
18
  import * as path from 'node:path';
19
- import {formatCliCommand, getCliInvocation} from '../../foundation/env/package-manager.mjs';
19
+ import {formatCliCommand, getCliInvocation, quoteCommandArgument} from '../../foundation/env/package-manager.mjs';
20
20
  import {hasRecordedProject} from '../../foundation/env/cli-command.mjs';
21
21
  import {isOutsideProject} from '../../foundation/env/project-scope.mjs';
22
22
  import {API_VERSION, setJsonMode} from '../../foundation/response/json.mjs';
23
23
  import {answeredBy, runChoice} from '../../foundation/release/run-context.mjs';
24
- import {addCliCommand} from '../../foundation/release/notices.mjs';
25
- import {projectRootOf} from '../../foundation/release/choose.mjs';
26
- import {readReleaseFacts} from '../../foundation/release/project.mjs';
24
+ import {cliInstallAdvice, installFolder} from '../../foundation/release/notices.mjs';
27
25
  import {buildManifest} from './lib/manifest.mjs';
28
26
  import {cliError} from './lib/cli-error.mjs';
29
- import {emit, section, text, records} from './formatters/index.mjs';
27
+ import {section, text, records} from './formatters/index.mjs';
30
28
  import {ERROR_CODES} from '../../foundation/response/error-codes.mjs';
31
29
  import {levenshteinDistance} from '../../foundation/text/string-utils.mjs';
32
30
  import {installJsonShim} from './lib/json-shim.mjs';
33
- import {addDocHelp, markReportsResult} from './lib/define-command.mjs';
34
- import {doc as manifestDoc} from './commands/manifest.doc.mjs';
35
- import {isAstryxInitialized, isAstryxPromptInReach} from '../../foundation/agent-docs/agent-docs.mjs';
31
+ import {markReportsResult} from './lib/define-command.mjs';
32
+ import {
33
+ agentDocsTargetDir,
34
+ assertAgentDocsTarget,
35
+ isAstryxInitialized,
36
+ isAstryxPromptInReach,
37
+ } from '../../foundation/agent-docs/agent-docs.mjs';
36
38
  import {CLI_ROOT} from '../../foundation/fs/paths.mjs';
37
39
  import {setupNudge} from '../../foundation/agent-docs/agent-doc-state.mjs';
38
40
  import * as debug from '../../foundation/debug/index.mjs';
@@ -292,7 +294,7 @@ function captureInvocation(actionCommand, root) {
292
294
  */
293
295
  const commands = [
294
296
  {name: 'init', path: './commands/init.mjs', register: 'registerInit'},
295
- {name: 'component', path: './commands/component/index.mjs', register: 'registerComponent'},
297
+ {name: 'component', path: './commands/component.mjs', register: 'registerComponent'},
296
298
  {name: 'docs', path: './commands/docs.mjs', register: 'registerDocs'},
297
299
  {name: 'blog', path: './commands/blog.mjs', register: 'registerBlog'},
298
300
  {name: 'swizzle', path: './commands/swizzle.mjs', register: 'registerSwizzle'},
@@ -300,13 +302,17 @@ const commands = [
300
302
  // agent-docs folded into init — functions still importable from agent-docs.mjs
301
303
  {name: 'template', path: './commands/template.mjs', register: 'registerTemplate'},
302
304
  {name: 'upgrade', path: './commands/upgrade.mjs', register: 'registerUpgrade'},
303
- {name: 'theme', path: './commands/build-theme.mjs', register: 'registerTheme'},
305
+ {name: 'theme', path: './commands/theme.mjs', register: 'registerTheme'},
304
306
  {name: 'integration', path: './commands/integration.mjs', register: 'registerIntegration'},
305
- {name: 'hook', path: './commands/hook/index.mjs', register: 'registerHook'},
307
+ {name: 'hook', path: './commands/hook.mjs', register: 'registerHook'},
306
308
  {name: 'discover', path: './commands/discover.mjs', register: 'registerDiscover'},
307
309
  {name: 'search', path: './commands/search.mjs', register: 'registerSearch'},
308
310
  {name: 'build', path: './commands/build.mjs', register: 'registerBuild'},
309
311
  {name: 'doctor', path: './commands/doctor.mjs', register: 'registerDoctor'},
312
+ // Registered after every other command: the manifest lists the commands in
313
+ // registration order.
314
+ {name: 'manifest', path: './commands/manifest.mjs', register: 'registerManifest'},
315
+ {name: 'postinstall', path: './commands/postinstall.mjs', register: 'registerPostinstall'},
310
316
  ];
311
317
 
312
318
  const SETUP_NUDGE_EXEMPT = new Set(['init', 'agent-docs']);
@@ -635,6 +641,19 @@ export async function createProgram() {
635
641
  if (INTEGRATION_MANIFEST_FILES.some(file => fs.existsSync(path.join(cwd, file)))) return;
636
642
  if (inOwnSourceCheckout(cwd)) return; // developing Astryx itself, not an app
637
643
  if (isAstryxPromptInReach(cwd)) return; // already set up — stay quiet
644
+ // Where init writes the docs, by init's own rule: the project root, which
645
+ // can sit above a nested package such as functions/. Inside a git
646
+ // repository the walk above already reaches it (the root never lies
647
+ // above the repository root), so this changes nothing there; outside a
648
+ // repository the walk checks only cwd, and this finds the root.
649
+ if (isAstryxInitialized(agentDocsTargetDir(cwd))) return;
650
+ // In the home folder, or at the top of a settings folder in it, init
651
+ // writes no agent docs, so naming it would repeat on every command.
652
+ try {
653
+ assertAgentDocsTarget(agentDocsTargetDir(cwd));
654
+ } catch {
655
+ return;
656
+ }
638
657
  // Same wording as the core/cli postinstall nudges. getCliInvocation()
639
658
  // renders the correct form for THIS project — an integration's cliCommand,
640
659
  // scoped `npx @astryxdesign/cli` one-off, or `<pm> astryx` when installed —
@@ -642,14 +661,33 @@ export async function createProgram() {
642
661
  // A project with no CLI of its own, answered by another release than its
643
662
  // Core's: add the CLI at Core's version first, as the daily hint says,
644
663
  // then run that CLI's init (spec AST-081 FR2).
645
- const choice = runChoice(cwd);
646
- if (choice.copy === 'starting' && choice.coreVersion && choice.coreVersion !== choice.version) {
647
- const root = projectRootOf(readReleaseFacts(cwd)) ?? cwd;
664
+ // A folder that belongs to no project spec:AST-040 FR9 defines gets no
665
+ // reminder, as outside a project.
666
+ const target = installFolder(cwd);
667
+ if (target == null) return;
668
+ // From a folder inside the project whose package.json is not the
669
+ // project's, init runs from the project's folder. Only `cd` comes
670
+ // before it, so the command is the picker's for that folder, never the
671
+ // bare `astryx` name a package runner would fetch from the registry
672
+ // (spec AST-081 FR5).
673
+ if (target.moved) {
648
674
  console.error(
649
- setupNudge(getCliInvocation(root, {installed: true}), {before: addCliCommand(root, choice.coreVersion)}).trimEnd(),
675
+ setupNudge(getCliInvocation(target.dir), {before: `cd ${quoteCommandArgument(target.dir)}`}).trimEnd(),
650
676
  );
651
677
  return;
652
678
  }
679
+ const choice = runChoice(cwd);
680
+ if (choice.copy === 'starting' && choice.coreVersion && choice.coreVersion !== choice.version) {
681
+ // The daily hint's comparison and command: add the CLI at Core's
682
+ // version, then run that CLI's init.
683
+ const add = cliInstallAdvice({cwd, core: choice.coreVersion});
684
+ if (add?.command) {
685
+ console.error(
686
+ setupNudge(getCliInvocation(add.root, {installed: true}), {before: add.command}).trimEnd(),
687
+ );
688
+ return;
689
+ }
690
+ }
653
691
  console.error(setupNudge(getCliInvocation(cwd)).trimEnd());
654
692
  } catch {
655
693
  // Never let the nudge break a command.
@@ -659,7 +697,7 @@ export async function createProgram() {
659
697
  for (const cmd of commands) {
660
698
  try {
661
699
  const mod = await import(cmd.path);
662
- mod[cmd.register](program);
700
+ mod[cmd.register](program, {jsonSupported: JSON_SUPPORTED, version: pkg.version});
663
701
  } catch (e) {
664
702
  // Command fails to load but CLI still works
665
703
  const reason = /** @type {any} */ (e).message;
@@ -679,84 +717,6 @@ export async function createProgram() {
679
717
  }
680
718
  }
681
719
 
682
- // Capability manifest — a single, self-describing view of the whole CLI so
683
- // agents can discover every command, argument, flag, and response type without
684
- // scraping `--help`. `astryx manifest --json` is the dedicated surface; the bare
685
- // `xds --json` embeds the same payload under data.manifest for convenience.
686
- // Intentionally CLI-special — no `api/manifest`. It introspects the live
687
- // Commander `program`, so extracting it to `api/` would create the `api → cli`
688
- // cycle from #4302. `buildManifest(program)` lives in lib/; see its header.
689
- const manifestCommand = program
690
- .command('manifest')
691
- .description('Print the full CLI capability manifest (use with --json)')
692
- .action(() => {
693
- const manifest = buildManifest(program, {
694
- jsonSupported: JSON_SUPPORTED,
695
- version: pkg.version,
696
- });
697
- // The manifest IS a result set: one entry per command the CLI ships.
698
- debug.recordCommandResult(
699
- debug.resultSet({
700
- count: manifest.commands.length,
701
- resultKind: 'command',
702
- }),
703
- );
704
- if (program.opts().json) {
705
- process.__xdsJsonHandled = true;
706
- console.log(JSON.stringify({apiVersion: API_VERSION, type: 'manifest', data: manifest, meta: {answeredBy: answeredBy()}}, null, 2));
707
- return;
708
- }
709
- // Human-readable summary as greppable records (agents should use --json).
710
- // One record per command: name, whether it supports --json, and the
711
- // description. Field names are the manifest entry's own keys.
712
- emit(
713
- section(`${manifest.name} v${manifest.version} (${manifest.commands.length} commands)`),
714
- records(
715
- manifest.commands.map(c => ({
716
- name: c.name,
717
- json: c.json ? 'yes' : '',
718
- description: c.description || '',
719
- })),
720
- {fields: ['name', 'json', 'description']},
721
- ),
722
- text(`Run \`${getCliInvocation()} manifest --json\` for the full structured manifest.`),
723
- );
724
- });
725
- addDocHelp(manifestCommand, manifestDoc);
726
- markReportsResult(manifestCommand);
727
-
728
- // Hidden command used by package.json postinstall scripts
729
- const postinstallCommand = program
730
- .command('postinstall', {hidden: true})
731
- .action(() => {
732
- // Prints the welcome box. Nothing is looked up.
733
- debug.recordCommandResult(debug.NO_RESULT_SET);
734
- const r = getCliInvocation();
735
- const pad = (/** @type {string} */ s, /** @type {number} */ len) => s + ' '.repeat(Math.max(0, len - s.length));
736
- const W = 49; // inner width of the box
737
- const line = (/** @type {string} */ s) => ` │ ${pad(s, W)}│`;
738
- console.log(`
739
- ╭${'─'.repeat(W + 2)}╮
740
- ${line('')}
741
- ${line(' Design system installed!')}
742
- ${line('')}
743
- ${line(' Get started:')}
744
- ${line(` ${r} init Setup + AI agent docs`)}
745
- ${line(` ${r} --help See all commands`)}
746
- ${line('')}
747
- ${line(' Or run directly:')}
748
- ${line(` ${r} init Setup + AI agent docs`)}
749
- ${line(` ${r} component Browse component docs`)}
750
- ${line(` ${r} hook Browse hook docs`)}
751
- ${line(` ${r} docs Design system reference`)}
752
- ${line(` ${r} swizzle Customize a component`)}
753
- ${line(` ${r} template Add a page template`)}
754
- ${line('')}
755
- ╰${'─'.repeat(W + 2)}╯
756
- `);
757
- });
758
- markReportsResult(postinstallCommand);
759
-
760
720
  // Install the JSON shim AFTER all commands are registered so we can
761
721
  // patch outputHelp on every command (root + subcommands). The shim
762
722
  // extends the --json contract to cover Commander's parse-time short
@@ -21,7 +21,7 @@ import {getCliInvocation} from '../../../foundation/env/package-manager.mjs';
21
21
  * (→ `.astryx-table-cell`), and `astryx-button` → key `button`.
22
22
  *
23
23
  * Keep the `astryx-` literal in sync with packages/core/src/naming.ts
24
- * (NAMESPACE / classPrefix), the same way build-theme.mjs mirrors it.
24
+ * (NAMESPACE / classPrefix), the same way commands/theme.mjs mirrors it.
25
25
  * <!-- SYNC: packages/core/src/naming.ts (namespace prefix source of truth) -->
26
26
  * @param {import('@astryxdesign/cli/authoring').ComponentThemingTarget} target
27
27
  * @returns {string}
@@ -63,10 +63,10 @@ export function reportsResultVia(cmd) {
63
63
  /**
64
64
  * Mark a hand-registered command as reporting its result.
65
65
  *
66
- * For the handful of commands that cannot come from a CommandDoc — the root
67
- * program, `manifest` (it introspects the live program, so it cannot be
68
- * declared by one), `postinstall`, and the stub left behind when a command
69
- * module fails to load. They call {@link recordCommandResult} themselves; this
66
+ * For what does not come from a CommandDoc: the root program, and the stub
67
+ * left behind when a command module fails to load. Every command, `manifest`
68
+ * and the hidden `postinstall` hook included, registers through
69
+ * {@link defineCommand}. These call {@link recordCommandResult} themselves; this
70
70
  * says so, and the coverage test pins the set of commands allowed to, so the
71
71
  * mark cannot become a quiet way around the contract.
72
72
  *
@@ -105,10 +105,12 @@ export function commandDocsOf(cmd) {
105
105
  * fn?: import('@astryxdesign/cli/authoring').FunctionDoc,
106
106
  * action?: (...args: any[]) => import('../../../foundation/debug/command-result.mjs').CommandResult
107
107
  * | Promise<import('../../../foundation/debug/command-result.mjs').CommandResult>,
108
- * }} [impl]
108
+ * hidden?: boolean,
109
+ * }} [impl] `hidden` keeps the command out of help and the manifest (a
110
+ * package-script hook, not a command for people or agents).
109
111
  * @returns {import('commander').Command} the created command.
110
112
  */
111
- export function defineCommand(parent, doc, {fn, action} = {}) {
113
+ export function defineCommand(parent, doc, {fn, action, hidden} = {}) {
112
114
  // The command token is the last path segment ("theme build" -> "build"),
113
115
  // since subcommands are added to their group command, not the program.
114
116
  const token = doc.name.split(' ').pop() ?? doc.name;
@@ -119,7 +121,10 @@ export function defineCommand(parent, doc, {fn, action} = {}) {
119
121
  })
120
122
  .join(' ');
121
123
 
122
- const cmd = parent.command(argSpec ? `${token} ${argSpec}` : token);
124
+ const cmd = parent.command(
125
+ argSpec ? `${token} ${argSpec}` : token,
126
+ hidden ? {hidden: true} : {},
127
+ );
123
128
  Object.defineProperty(cmd, COMMAND_DOCS, {value: {doc, fn}, configurable: true});
124
129
  if (doc.summary) cmd.description(doc.summary);
125
130
  if (doc.deprecated) {
@@ -24,6 +24,10 @@ for (const file of fs.readdirSync(COMMANDS).sort()) {
24
24
  }
25
25
 
26
26
  const manifest = buildManifest(program, {jsonSupported: JSON_SUPPORTED, version: '0.0.0-test'});
27
+ /** Hidden commands (a package-script hook): documented, never in the manifest. */
28
+ const HIDDEN = new Set(
29
+ program.commands.filter((c) => /** @type {any} */ (c)._hidden).map((c) => c.name()),
30
+ );
27
31
  /** @type {Map<string, any>} */
28
32
  const entries = new Map();
29
33
  const walk = (/** @type {any} */ c) => {
@@ -98,6 +102,7 @@ describe('command exit codes', () => {
98
102
 
99
103
  it('the manifest carries every command exit code', () => {
100
104
  for (const doc of commandDocs) {
105
+ if (HIDDEN.has(doc.name)) continue;
101
106
  expect(entries.get(doc.name)?.exitCodes, doc.name).toEqual(
102
107
  doc.exitCodes.map(({code, when}) => ({code, when})),
103
108
  );
@@ -55,6 +55,10 @@ const CLI_LAYER_TYPES = {
55
55
  };
56
56
 
57
57
  const manifest = buildManifest(program, {jsonSupported: JSON_SUPPORTED, version: '0.0.0-test'});
58
+ /** Hidden commands (a package-script hook): documented, never in the manifest. */
59
+ const HIDDEN = new Set(
60
+ program.commands.filter((c) => /** @type {any} */ (c)._hidden).map((c) => c.name()),
61
+ );
58
62
 
59
63
  /** Flatten the manifest command tree into fully-qualified names. */
60
64
  function flatten(cmds, out = []) {
@@ -116,12 +120,19 @@ describe('manifest: drift guards', () => {
116
120
 
117
121
  it('takes every command example from its CommandDoc', () => {
118
122
  for (const [name, doc] of commandDocs) {
123
+ if (HIDDEN.has(name)) continue;
119
124
  const entry = allEntries.find((c) => c.name === name);
120
125
  expect(entry, `CommandDoc "${name}" has no manifest entry`).toBeDefined();
121
126
  expect(entry.examples ?? [], name).toEqual((doc.examples ?? []).map((e) => e.cli));
122
127
  }
123
128
  });
124
129
 
130
+ it('documents the hidden postinstall hook and keeps it out of the manifest', () => {
131
+ expect([...HIDDEN]).toEqual(['postinstall']);
132
+ expect(commandDocs.has('postinstall')).toBe(true);
133
+ expect(allNames.has('postinstall')).toBe(false);
134
+ });
135
+
125
136
  it('lists every response type the wrapped API function returns', () => {
126
137
  for (const [name, doc] of commandDocs) {
127
138
  if (!doc.fn) continue;
@@ -152,6 +163,7 @@ describe('manifest: drift guards', () => {
152
163
 
153
164
  it('takes response types from the wrapped FunctionDoc, plus CLI-layer envelopes', () => {
154
165
  for (const [name, doc] of commandDocs) {
166
+ if (HIDDEN.has(name)) continue;
155
167
  const entry = allEntries.find((c) => c.name === name);
156
168
  const returns = doc.fn ? functionDocs.get(doc.fn).returns.map((r) => r.type) : [];
157
169
  const expected = [...returns, ...(CLI_LAYER_TYPES[name] ?? [])];
@@ -0,0 +1,50 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file The welcome box `astryx postinstall` prints after the package installs.
5
+ *
6
+ * A fixed banner, not tabular output, so it is drawn here rather than with the
7
+ * formatter kit; the command prints it verbatim through `emit(code(...))`.
8
+ * @input The CLI invocation for this project (e.g. `pnpm exec astryx`).
9
+ * @output {@link welcomeBox}
10
+ * @position clients/cli/lib — presentation helper for commands/postinstall.mjs
11
+ */
12
+
13
+ /** Inner width of the box. */
14
+ const W = 49;
15
+
16
+ /**
17
+ * @param {string} s
18
+ * @param {number} len
19
+ */
20
+ const pad = (s, len) => s + ' '.repeat(Math.max(0, len - s.length));
21
+
22
+ /** @param {string} s */
23
+ const line = s => ` │ ${pad(s, W)}│`;
24
+
25
+ /**
26
+ * The welcome box, with a blank line before and after it.
27
+ * @param {string} r the CLI invocation, e.g. `pnpm exec astryx`
28
+ * @returns {string}
29
+ */
30
+ export function welcomeBox(r) {
31
+ return `
32
+ ╭${'─'.repeat(W + 2)}╮
33
+ ${line('')}
34
+ ${line(' Design system installed!')}
35
+ ${line('')}
36
+ ${line(' Get started:')}
37
+ ${line(` ${r} init Setup + AI agent docs`)}
38
+ ${line(` ${r} --help See all commands`)}
39
+ ${line('')}
40
+ ${line(' Or run directly:')}
41
+ ${line(` ${r} init Setup + AI agent docs`)}
42
+ ${line(` ${r} component Browse component docs`)}
43
+ ${line(` ${r} hook Browse hook docs`)}
44
+ ${line(` ${r} docs Design system reference`)}
45
+ ${line(` ${r} swizzle Customize a component`)}
46
+ ${line(` ${r} template Add a page template`)}
47
+ ${line('')}
48
+ ╰${'─'.repeat(W + 2)}╯
49
+ `;
50
+ }