@astryxdesign/cli 0.7.2-canary.fdd384f → 0.7.2

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 (450) hide show
  1. package/CHANGELOG.md +119 -0
  2. package/README.md +169 -149
  3. package/api/build/_adapter.d.mts +8 -0
  4. package/api/build/_adapter.mjs +12 -1
  5. package/api/build/build.doc.mjs +4 -0
  6. package/api/build/kit/kit.mjs +3 -11
  7. package/api/component/_adapter.d.mts +31 -0
  8. package/api/component/_adapter.mjs +119 -11
  9. package/api/component/component.doc.mjs +6 -1
  10. package/api/component/component.mjs +60 -10
  11. package/api/component/doc-links.test.mjs +386 -0
  12. package/api/discover/_adapter.d.mts +132 -12
  13. package/api/discover/_adapter.mjs +488 -27
  14. package/api/discover/_adapter.test.mjs +599 -17
  15. package/api/discover/_catalog-view.d.mts +16 -5
  16. package/api/discover/_catalog-view.mjs +32 -8
  17. package/api/discover/detail/doc/doc.mjs +9 -1
  18. package/api/discover/discover.d.mts +11 -0
  19. package/api/discover/discover.doc.mjs +9 -7
  20. package/api/discover/discover.mjs +121 -19
  21. package/api/discover/discover.type.d.mts +20 -7
  22. package/api/discover/discover.type.mjs +17 -7
  23. package/api/discover/list/list.d.mts +4 -3
  24. package/api/discover/list/list.mjs +17 -10
  25. package/api/docs/_adapter.d.mts +63 -0
  26. package/api/docs/_adapter.mjs +312 -18
  27. package/api/docs/docs.doc.mjs +2 -1
  28. package/api/docs/docs.mjs +2 -1
  29. package/api/docs/docs.test.mjs +5 -2
  30. package/api/docs/integration-tree.test.mjs +22 -0
  31. package/api/docs/list/list.mjs +12 -2
  32. package/api/docs/reference-blocks.test.mjs +1 -3
  33. package/api/doctor/_adapter.d.mts +108 -0
  34. package/api/doctor/_adapter.mjs +223 -4
  35. package/api/doctor/doctor.d.mts +199 -0
  36. package/api/doctor/doctor.doc.mjs +3 -2
  37. package/api/doctor/doctor.mjs +530 -34
  38. package/api/doctor/doctor.test.mjs +180 -14
  39. package/api/doctor/release-checks.test.mjs +156 -0
  40. package/api/doctor/theme-checks.mjs +16 -3
  41. package/api/doctor/theme-checks.test.mjs +223 -7
  42. package/api/error.d.mts +10 -0
  43. package/api/error.mjs +25 -0
  44. package/api/gap-report/gap-report.d.mts +3 -0
  45. package/api/gap-report/gap-report.doc.mjs +11 -3
  46. package/api/gap-report/gap-report.mjs +152 -48
  47. package/api/gap-report/gap-report.test.mjs +233 -0
  48. package/api/gap-report/gap-report.type.d.mts +41 -0
  49. package/api/gap-report/gap-report.type.mjs +18 -0
  50. package/api/hook/_adapter.mjs +3 -11
  51. package/api/hook/hook.doc.mjs +4 -0
  52. package/api/hook/list/list.d.mts +1 -1
  53. package/api/init/_adapter.d.mts +22 -0
  54. package/api/init/_adapter.mjs +59 -0
  55. package/api/init/init.d.mts +7 -5
  56. package/api/init/init.doc.mjs +28 -7
  57. package/api/init/init.mjs +28 -4
  58. package/api/init/init.type.d.mts +48 -3
  59. package/api/init/init.type.mjs +37 -3
  60. package/api/init/run/run.d.mts +1 -1
  61. package/api/init/run/run.mjs +15 -4
  62. package/api/init/user/user.d.mts +14 -0
  63. package/api/init/user/user.mjs +87 -0
  64. package/api/init/user/user.test.mjs +232 -0
  65. package/api/integration/_adapter.d.mts +33 -0
  66. package/api/integration/_adapter.mjs +58 -0
  67. package/api/integration/authoring-checks.mjs +100 -5
  68. package/api/integration/authoring-checks.test.mjs +769 -0
  69. package/api/integration/authoring-checks.type.d.mts +134 -0
  70. package/api/integration/authoring-checks.type.mjs +60 -1
  71. package/api/integration/integrationCheck.doc.mjs +1 -1
  72. package/api/integration/integrationComponentConflicts.doc.mjs +11 -3
  73. package/api/integration/integrationDocConflicts.doc.mjs +22 -5
  74. package/api/integration/pack-check.mjs +303 -13
  75. package/api/integration/pack-check.test.mjs +371 -10
  76. package/api/integration/validate-integration-fixes.test.mjs +67 -0
  77. package/api/integration/validate-integration.d.mts +8 -0
  78. package/api/integration/validate-integration.mjs +55 -9
  79. package/api/integration/validate-integration.test.mjs +77 -8
  80. package/api/integration/validate-integration.type.d.mts +8 -0
  81. package/api/integration/validate-integration.type.mjs +3 -1
  82. package/api/integration/validate-unread-theme-folders.test.mjs +3 -2
  83. package/api/integration/validateIntegration.doc.mjs +4 -2
  84. package/api/search/_adapter.d.mts +11 -0
  85. package/api/search/_adapter.mjs +25 -0
  86. package/api/search/search.d.mts +73 -13
  87. package/api/search/search.doc.mjs +8 -3
  88. package/api/search/search.domain-priority.test.mjs +108 -18
  89. package/api/search/search.mjs +541 -52
  90. package/api/search/search.test.mjs +143 -1
  91. package/api/search/search.type.d.mts +14 -1
  92. package/api/search/search.type.mjs +6 -1
  93. package/api/swizzle/_adapter.mjs +2 -2
  94. package/api/swizzle/copy/copy.mjs +1 -1
  95. package/api/template/template-integration.test.mjs +1 -1
  96. package/api/theme/_adapter.d.mts +16 -0
  97. package/api/theme/_adapter.mjs +32 -5
  98. package/api/theme/build/build.d.mts +2 -1
  99. package/api/theme/build/build.mjs +70 -14
  100. package/api/theme/build/build.packed-old-core.test.mjs +20 -3
  101. package/api/theme/build/font-warning.mjs +5 -3
  102. package/api/theme/build/font-warning.test.mjs +2 -1
  103. package/api/theme/remove/remove.test.mjs +2 -2
  104. package/api/theme/targets/targets.mjs +3 -10
  105. package/api/theme/themeTargets.doc.mjs +4 -0
  106. package/api/theme/use/use.test.mjs +1 -1
  107. package/api/upgrade/_adapter.d.mts +40 -2
  108. package/api/upgrade/_adapter.mjs +118 -5
  109. package/api/upgrade/run/cli-behind-core.test.mjs +438 -0
  110. package/api/upgrade/run/run.mjs +35 -2
  111. package/api/upgrade/status/status.d.mts +21 -5
  112. package/api/upgrade/status/status.mjs +45 -8
  113. package/api/upgrade/upgrade.doc.mjs +6 -3
  114. package/api/upgrade/upgrade.mjs +5 -1
  115. package/api/upgrade/upgrade.type.d.mts +23 -0
  116. package/api/upgrade/upgrade.type.mjs +16 -1
  117. package/assets/codemods/__tests__/registry.test.mjs +1 -0
  118. package/assets/codemods/registry.mjs +1 -0
  119. package/assets/codemods/runner.mjs +7 -1
  120. package/assets/codemods/transforms/next/index.mjs +1 -11
  121. package/assets/codemods/transforms/v0.7.2/index.mjs +20 -0
  122. package/assets/discover/official-packages.json +367 -0
  123. package/assets/docs/author-a-theme.doc.mjs +2 -2
  124. package/assets/docs/browser-support.doc.mjs +1 -1
  125. package/assets/docs/getting-started.doc.mjs +23 -3
  126. package/assets/docs/styling-overview.doc.mjs +1 -1
  127. package/assets/docs/theme.doc.mjs +1 -1
  128. package/assets/docs/tree/add-a-component.doc.mjs +12 -2
  129. package/assets/docs/tree/add-a-theme.doc.mjs +13 -3
  130. package/assets/docs/tree/add-a-topic.doc.mjs +54 -9
  131. package/assets/docs/tree/agent-guidance.doc.mjs +19 -5
  132. package/assets/docs/tree/block-template.doc.mjs +6 -18
  133. package/assets/docs/tree/build-the-template.doc.mjs +1 -1
  134. package/assets/docs/tree/building-blocks.doc.mjs +4 -0
  135. package/assets/docs/tree/check-the-props.doc.mjs +8 -3
  136. package/assets/docs/tree/check-your-docs.doc.mjs +30 -13
  137. package/assets/docs/tree/checks.doc.mjs +17 -17
  138. package/assets/docs/tree/ci-and-containers.doc.mjs +88 -0
  139. package/assets/docs/tree/cli-command.doc.mjs +142 -0
  140. package/assets/docs/tree/cli.doc.mjs +6 -0
  141. package/assets/docs/tree/codemods.doc.mjs +17 -7
  142. package/assets/docs/tree/commands.doc.mjs +7 -1
  143. package/assets/docs/tree/component-family.doc.mjs +3 -5
  144. package/assets/docs/tree/component-imports.doc.mjs +13 -3
  145. package/assets/docs/tree/components.doc.mjs +7 -1
  146. package/assets/docs/tree/computer-agent-guide.doc.mjs +160 -0
  147. package/assets/docs/tree/computer-integrations.doc.mjs +93 -0
  148. package/assets/docs/tree/configuration.doc.mjs +7 -3
  149. package/assets/docs/tree/debug-and-gap-reports.doc.mjs +41 -22
  150. package/assets/docs/tree/define-the-theme.doc.mjs +6 -5
  151. package/assets/docs/tree/describe-the-component.doc.mjs +2 -2
  152. package/assets/docs/tree/docs.doc.mjs +9 -3
  153. package/assets/docs/tree/document-the-template.doc.mjs +1 -1
  154. package/assets/docs/tree/document-the-theme.doc.mjs +3 -3
  155. package/assets/docs/tree/export-template-assets.doc.mjs +1 -1
  156. package/assets/docs/tree/extend-or-replace.doc.mjs +19 -9
  157. package/assets/docs/tree/fonts-and-assets.doc.mjs +2 -2
  158. package/assets/docs/tree/generate-a-palette.doc.mjs +7 -5
  159. package/assets/docs/tree/global-integrations.doc.mjs +124 -0
  160. package/assets/docs/tree/global.doc.mjs +131 -0
  161. package/assets/docs/tree/grade-template-with-agent.doc.mjs +5 -5
  162. package/assets/docs/tree/install-once.doc.mjs +105 -0
  163. package/assets/docs/tree/integrations.doc.mjs +13 -1
  164. package/assets/docs/tree/layout-spacing.doc.mjs +2 -3
  165. package/assets/docs/tree/layout.doc.mjs +6 -6
  166. package/assets/docs/tree/links.doc.mjs +6 -5
  167. package/assets/docs/tree/migration-setup.doc.mjs +3 -2
  168. package/assets/docs/tree/migration-verification.doc.mjs +0 -1
  169. package/assets/docs/tree/migration.doc.mjs +0 -1
  170. package/assets/docs/tree/monorepos.doc.mjs +84 -0
  171. package/assets/docs/tree/package-and-test.doc.mjs +1 -1
  172. package/assets/docs/tree/page-template.doc.mjs +3 -7
  173. package/assets/docs/tree/problems.doc.mjs +205 -0
  174. package/assets/docs/tree/project-global-or-both.doc.mjs +131 -0
  175. package/assets/docs/tree/publishing.doc.mjs +6 -6
  176. package/assets/docs/tree/quick-start.doc.mjs +52 -26
  177. package/assets/docs/tree/reference.doc.mjs +192 -0
  178. package/assets/docs/tree/replace-a-core-component.doc.mjs +15 -7
  179. package/assets/docs/tree/replace-a-core-template.doc.mjs +9 -11
  180. package/assets/docs/tree/scaffold.doc.mjs +1 -1
  181. package/assets/docs/tree/sections-and-placement.doc.mjs +9 -11
  182. package/assets/docs/tree/see-it-in-an-app.doc.mjs +17 -5
  183. package/assets/docs/tree/short-and-findable.doc.mjs +9 -8
  184. package/assets/docs/tree/single-component.doc.mjs +14 -6
  185. package/assets/docs/tree/start-a-template.doc.mjs +18 -8
  186. package/assets/docs/tree/subcomponent.doc.mjs +6 -6
  187. package/assets/docs/tree/template-assets.doc.mjs +2 -2
  188. package/assets/docs/tree/template-doc-overview.doc.mjs +6 -17
  189. package/assets/docs/tree/template-fonts.doc.mjs +1 -1
  190. package/assets/docs/tree/template-icons.doc.mjs +1 -1
  191. package/assets/docs/tree/template-images-media.doc.mjs +1 -1
  192. package/assets/docs/tree/template-styles.doc.mjs +2 -2
  193. package/assets/docs/tree/templates.doc.mjs +7 -1
  194. package/assets/docs/tree/test-in-an-app.doc.mjs +7 -7
  195. package/assets/docs/tree/test-template-in-app.doc.mjs +5 -5
  196. package/assets/docs/tree/themes.doc.mjs +4 -0
  197. package/assets/docs/tree/tokens-and-setup.doc.mjs +19 -15
  198. package/assets/docs/tree/troubleshooting.doc.mjs +22 -14
  199. package/assets/docs/tree/uninstall.doc.mjs +109 -0
  200. package/assets/docs/tree/updates.doc.mjs +92 -0
  201. package/assets/docs/tree/upgrading.doc.mjs +6 -6
  202. package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +14 -2
  203. package/assets/docs/tree/verify-packed-template.doc.mjs +3 -3
  204. package/assets/docs/tree/versioning.doc.mjs +9 -9
  205. package/assets/docs/tree/who-answers.doc.mjs +119 -0
  206. package/assets/docs/tree/write-the-template-file.doc.mjs +5 -5
  207. package/assets/docs/working-with-ai.doc.mjs +23 -9
  208. package/authoring/codemod/codemod.doc.mjs +7 -3
  209. package/authoring/config/config.doc.mjs +9 -6
  210. package/authoring/config/type.ts +4 -2
  211. package/authoring/debug/parse.d.mts +5 -5
  212. package/authoring/discover/discover.doc.mjs +7 -1
  213. package/authoring/doctypes/_schema.d.mts +8 -3
  214. package/authoring/doctypes/_schema.mjs +1 -0
  215. package/authoring/doctypes/base/graph-fields.doc.mjs +6 -4
  216. package/authoring/doctypes/command/command.doc.mjs +20 -12
  217. package/authoring/doctypes/command/type.ts +14 -0
  218. package/authoring/doctypes/component/component.doc.mjs +29 -26
  219. package/authoring/doctypes/component/type.ts +15 -5
  220. package/authoring/doctypes/enum/enum.doc.mjs +7 -4
  221. package/authoring/doctypes/function/function.doc.mjs +9 -8
  222. package/authoring/doctypes/hook/hook.doc.mjs +5 -4
  223. package/authoring/doctypes/hook/type.ts +4 -0
  224. package/authoring/doctypes/namespace/namespace.doc.mjs +7 -6
  225. package/authoring/doctypes/reference/reference.doc.mjs +12 -11
  226. package/authoring/doctypes/schema/schema.doc.mjs +9 -9
  227. package/authoring/doctypes/template/template.doc.mjs +2 -2
  228. package/authoring/doctypes/theme/theme.doc.mjs +2 -2
  229. package/authoring/gap-report/gap-report.doc.mjs +2 -2
  230. package/authoring/identity/identity.doc.mjs +3 -3
  231. package/authoring/index.d.ts +1 -0
  232. package/authoring/integration/integration.doc.mjs +22 -5
  233. package/authoring/integration/parse.test.mjs +11 -0
  234. package/authoring/integration/schema.d.mts +14 -3
  235. package/authoring/integration/schema.mjs +33 -2
  236. package/authoring/integration/type.ts +8 -0
  237. package/clients/cli/bin/astryx.mjs +20 -3
  238. package/clients/cli/cli-command.test.mjs +166 -0
  239. package/clients/cli/commands/blog.doc.mjs +1 -0
  240. package/clients/cli/commands/build-theme.batch-report.test.mjs +14 -2
  241. package/clients/cli/commands/build-theme.font-warning.test.mjs +3 -3
  242. package/clients/cli/commands/build-theme.mjs +25 -12
  243. package/clients/cli/commands/build.doc.mjs +1 -0
  244. package/clients/cli/commands/build.exit-codes-doc.test.mjs +4 -0
  245. package/clients/cli/commands/cli-postinstall.test.mjs +49 -1
  246. package/clients/cli/commands/component.doc.mjs +1 -0
  247. package/clients/cli/commands/discover.broken-integration.test.mjs +11 -7
  248. package/clients/cli/commands/discover.doc.mjs +9 -1
  249. package/clients/cli/commands/discover.mjs +144 -93
  250. package/clients/cli/commands/discover.no-source.test.mjs +448 -33
  251. package/clients/cli/commands/discover.sources.test.mjs +20 -1
  252. package/clients/cli/commands/docs.doc.mjs +1 -0
  253. package/clients/cli/commands/docs.mjs +49 -19
  254. package/clients/cli/commands/docs.test.mjs +2 -2
  255. package/clients/cli/commands/doctor-integration-components.doc.mjs +7 -3
  256. package/clients/cli/commands/doctor-integration-docs.doc.mjs +20 -5
  257. package/clients/cli/commands/doctor-integration-templates.doc.mjs +1 -0
  258. package/clients/cli/commands/doctor-integration-validate.doc.mjs +13 -2
  259. package/clients/cli/commands/doctor-integration.test.mjs +52 -4
  260. package/clients/cli/commands/doctor.doc.mjs +12 -2
  261. package/clients/cli/commands/doctor.mjs +102 -7
  262. package/clients/cli/commands/doctor.test.mjs +10 -4
  263. package/clients/cli/commands/gap-report.doc.mjs +15 -2
  264. package/clients/cli/commands/gap-report.mjs +24 -2
  265. package/clients/cli/commands/gap-report.test.mjs +79 -0
  266. package/clients/cli/commands/hook.doc.mjs +1 -0
  267. package/clients/cli/commands/init.behavior.test.mjs +5 -0
  268. package/clients/cli/commands/init.doc.mjs +29 -3
  269. package/clients/cli/commands/integration-add.doc.mjs +7 -0
  270. package/clients/cli/commands/integration-authoring.test.mjs +49 -3
  271. package/clients/cli/commands/integration-check.doc.mjs +10 -2
  272. package/clients/cli/commands/integration-check.test.mjs +175 -1
  273. package/clients/cli/commands/integration-pack.doc.mjs +8 -1
  274. package/clients/cli/commands/integration-verify.doc.mjs +8 -1
  275. package/clients/cli/commands/integration.mjs +15 -9
  276. package/clients/cli/commands/json-contract.test.mjs +11 -2
  277. package/clients/cli/commands/manifest.doc.mjs +1 -0
  278. package/clients/cli/commands/outside-project.test.mjs +774 -0
  279. package/clients/cli/commands/search.doc.mjs +1 -0
  280. package/clients/cli/commands/search.mjs +16 -11
  281. package/clients/cli/commands/search.test.mjs +429 -296
  282. package/clients/cli/commands/setup-nudge.test.mjs +102 -2
  283. package/clients/cli/commands/swizzle.doc.mjs +7 -0
  284. package/clients/cli/commands/swizzle.mjs +5 -3
  285. package/clients/cli/commands/swizzle.routing.test.mjs +19 -2
  286. package/clients/cli/commands/template.doc.mjs +1 -0
  287. package/clients/cli/commands/theme-add-lifecycle.test.mjs +3 -3
  288. package/clients/cli/commands/theme-add.doc.mjs +8 -1
  289. package/clients/cli/commands/theme-build.doc.mjs +1 -0
  290. package/clients/cli/commands/theme-eject.doc.mjs +1 -0
  291. package/clients/cli/commands/theme-list.behavior.test.mjs +6 -0
  292. package/clients/cli/commands/theme-list.doc.mjs +1 -0
  293. package/clients/cli/commands/theme-palette-generate.doc.mjs +1 -0
  294. package/clients/cli/commands/theme-remove.doc.mjs +7 -0
  295. package/clients/cli/commands/theme-targets.doc.mjs +1 -0
  296. package/clients/cli/commands/theme-template.doc.mjs +1 -0
  297. package/clients/cli/commands/theme-use.doc.mjs +7 -0
  298. package/clients/cli/commands/upgrade.config-ordering.test.mjs +2 -1
  299. package/clients/cli/commands/upgrade.doc.mjs +22 -1
  300. package/clients/cli/commands/upgrade.file-protection.test.mjs +3 -2
  301. package/clients/cli/commands/upgrade.hook-output.test.mjs +2 -1
  302. package/clients/cli/commands/upgrade.older-cli.test.mjs +160 -0
  303. package/clients/cli/index.mjs +111 -16
  304. package/clients/cli/lib/cli-error.mjs +4 -1
  305. package/clients/cli/lib/component-format.test.mjs +1 -1
  306. package/clients/cli/lib/define-command.mjs +2 -2
  307. package/clients/cli/lib/json-shim.mjs +3 -1
  308. package/clients/cli/lib/manifest.d.ts +9 -0
  309. package/clients/cli/lib/manifest.mjs +17 -1
  310. package/clients/cli/lib/manifest.test.mjs +25 -0
  311. package/foundation/agent-docs/agent-doc-state.d.mts +35 -6
  312. package/foundation/agent-docs/agent-doc-state.mjs +66 -6
  313. package/foundation/agent-docs/agent-docs.d.mts +4 -3
  314. package/foundation/agent-docs/agent-docs.mjs +11 -9
  315. package/foundation/agent-docs/computer-agent-text.d.mts +133 -0
  316. package/foundation/agent-docs/computer-agent-text.mjs +623 -0
  317. package/foundation/agent-docs/computer-agent-text.test.mjs +602 -0
  318. package/foundation/config/project.d.mts +35 -3
  319. package/foundation/config/project.mjs +145 -8
  320. package/foundation/config/project.test.mjs +66 -0
  321. package/foundation/config/theme-state.mjs +5 -4
  322. package/foundation/discovery/authoring-self-docs.mjs +1 -1
  323. package/foundation/discovery/cli-self-docs.mjs +20 -3
  324. package/foundation/discovery/cli-self-docs.test.mjs +3 -2
  325. package/foundation/discovery/component-replacement.mjs +2 -1
  326. package/foundation/discovery/copied-passages.d.mts +216 -0
  327. package/foundation/discovery/copied-passages.mjs +720 -0
  328. package/foundation/discovery/copied-passages.test.mjs +124 -0
  329. package/foundation/discovery/doc-consistency.d.mts +34 -0
  330. package/foundation/discovery/doc-consistency.mjs +513 -0
  331. package/foundation/discovery/doc-consistency.test.mjs +347 -0
  332. package/foundation/discovery/doc-fluff.d.mts +112 -0
  333. package/foundation/discovery/doc-fluff.mjs +328 -0
  334. package/foundation/discovery/doc-fluff.test.mjs +142 -0
  335. package/foundation/discovery/doc-types.d.mts +1 -1
  336. package/foundation/discovery/doc-types.mjs +15 -8
  337. package/foundation/discovery/doc-types.test.mjs +94 -2
  338. package/foundation/discovery/docs-discovery.d.mts +7 -0
  339. package/foundation/discovery/docs-discovery.mjs +26 -2
  340. package/foundation/discovery/project-typescript.d.mts +11 -2
  341. package/foundation/discovery/project-typescript.mjs +32 -4
  342. package/foundation/discovery/prop-drift.d.mts +26 -7
  343. package/foundation/discovery/prop-drift.mjs +173 -8
  344. package/foundation/discovery/prop-drift.test.mjs +120 -2
  345. package/foundation/discovery/runner-allowlist.d.mts +40 -0
  346. package/foundation/discovery/runner-allowlist.mjs +53 -0
  347. package/foundation/discovery/stray-pointers.d.mts +52 -0
  348. package/foundation/discovery/stray-pointers.mjs +293 -0
  349. package/foundation/discovery/stray-pointers.test.mjs +86 -0
  350. package/foundation/discovery/template-needs.mjs +5 -2
  351. package/foundation/discovery/theme-imports.mjs +8 -4
  352. package/foundation/discovery/theme-imports.test.mjs +1 -1
  353. package/foundation/discovery/type-text.mjs +1 -1
  354. package/foundation/discovery/type-text.test.mjs +1 -1
  355. package/foundation/doc-compiler/doc-loads.test.mjs +42 -2
  356. package/foundation/doc-compiler/inputs.mjs +4 -1
  357. package/foundation/doc-compiler/links.d.mts +32 -0
  358. package/foundation/doc-compiler/links.mjs +163 -26
  359. package/foundation/doc-compiler/links.test.mjs +97 -4
  360. package/foundation/doc-compiler/read.d.mts +1 -1
  361. package/foundation/doc-compiler/tree.mjs +30 -1
  362. package/foundation/doc-compiler/tree.test.mjs +8 -1
  363. package/foundation/env/cli-command.d.mts +83 -0
  364. package/foundation/env/cli-command.mjs +152 -0
  365. package/foundation/env/cli-command.test.mjs +129 -0
  366. package/foundation/env/node-version.test.mjs +15 -3
  367. package/foundation/env/package-manager.d.mts +93 -9
  368. package/foundation/env/package-manager.global.test.mjs +66 -0
  369. package/foundation/env/package-manager.mjs +184 -13
  370. package/foundation/env/package-manager.test.mjs +341 -15
  371. package/foundation/env/project-scope.d.mts +9 -0
  372. package/foundation/env/project-scope.mjs +33 -0
  373. package/foundation/env/search-path.d.mts +42 -0
  374. package/foundation/env/search-path.mjs +205 -0
  375. package/foundation/env/search-path.test.mjs +122 -0
  376. package/foundation/env/user-dirs.d.mts +39 -0
  377. package/foundation/env/user-dirs.mjs +93 -0
  378. package/foundation/env/user-dirs.test.mjs +72 -0
  379. package/foundation/env/user-settings.d.mts +46 -0
  380. package/foundation/env/user-settings.mjs +106 -0
  381. package/foundation/env/user-settings.test.mjs +64 -0
  382. package/foundation/fs/file-protection.mjs +15 -1
  383. package/foundation/fs/module-loader.d.mts +2 -1
  384. package/foundation/fs/module-loader.mjs +60 -4
  385. package/foundation/fs/module-loader.stdout.test.mjs +3 -2
  386. package/foundation/fs/module-loader.test.mjs +194 -0
  387. package/foundation/fs/path-compare.d.mts +33 -0
  388. package/foundation/fs/path-compare.mjs +73 -0
  389. package/foundation/fs/paths.d.mts +57 -1
  390. package/foundation/fs/paths.mjs +131 -3
  391. package/foundation/fs/paths.test.mjs +10 -1
  392. package/foundation/integrations/computer-integrations.d.mts +78 -0
  393. package/foundation/integrations/computer-integrations.mjs +316 -0
  394. package/foundation/integrations/computer-integrations.test.mjs +292 -0
  395. package/foundation/integrations/contribution-inventory.d.mts +11 -0
  396. package/foundation/integrations/contribution-inventory.mjs +50 -2
  397. package/foundation/integrations/integration-warnings.mjs +3 -2
  398. package/foundation/integrations/integration-warnings.test.mjs +8 -9
  399. package/foundation/integrations/integrations.d.mts +25 -1
  400. package/foundation/integrations/integrations.mjs +46 -3
  401. package/foundation/integrations/issue.ts +6 -0
  402. package/foundation/integrations/validate-contributions.d.mts +2 -0
  403. package/foundation/integrations/validate-contributions.mjs +41 -1
  404. package/foundation/release/agent-text-worker.d.mts +4 -0
  405. package/foundation/release/agent-text-worker.mjs +52 -0
  406. package/foundation/release/cache.d.mts +310 -0
  407. package/foundation/release/cache.mjs +1216 -0
  408. package/foundation/release/cache.test.mjs +920 -0
  409. package/foundation/release/choose.d.mts +207 -0
  410. package/foundation/release/choose.mjs +677 -0
  411. package/foundation/release/choose.test.mjs +427 -0
  412. package/foundation/release/core-worker.d.mts +4 -0
  413. package/foundation/release/core-worker.mjs +38 -0
  414. package/foundation/release/global-state.d.mts +259 -0
  415. package/foundation/release/global-state.mjs +472 -0
  416. package/foundation/release/global-state.test.mjs +381 -0
  417. package/foundation/release/handoff.d.mts +60 -0
  418. package/foundation/release/handoff.e2e.test.mjs +278 -0
  419. package/foundation/release/handoff.mjs +357 -0
  420. package/foundation/release/lock-lease.d.mts +23 -0
  421. package/foundation/release/lock-lease.mjs +80 -0
  422. package/foundation/release/notices.d.mts +253 -0
  423. package/foundation/release/notices.mjs +596 -0
  424. package/foundation/release/notices.test.mjs +565 -0
  425. package/foundation/release/npm-guard.d.mts +4 -0
  426. package/foundation/release/npm-guard.mjs +126 -0
  427. package/foundation/release/project.d.mts +141 -0
  428. package/foundation/release/project.mjs +466 -0
  429. package/foundation/release/project.test.mjs +288 -0
  430. package/foundation/release/refresh-worker.d.mts +4 -0
  431. package/foundation/release/refresh-worker.mjs +27 -0
  432. package/foundation/release/run-context.d.mts +47 -0
  433. package/foundation/release/run-context.mjs +149 -0
  434. package/foundation/release/update-notice-env.d.mts +28 -0
  435. package/foundation/release/update-notice-env.mjs +39 -0
  436. package/foundation/response/base.d.ts +26 -3
  437. package/foundation/response/error-codes.d.mts +3 -1
  438. package/foundation/response/error-codes.doc.mjs +12 -0
  439. package/foundation/response/error-codes.mjs +17 -0
  440. package/foundation/response/error-codes.test.mjs +2 -0
  441. package/foundation/response/json-contract.test.mjs +7 -2
  442. package/foundation/response/json.mjs +4 -1
  443. package/foundation/response/response-types.doc.mjs +17 -11
  444. package/foundation/response/response-types.doc.test.mjs +46 -0
  445. package/foundation/response/response.doc.mjs +3 -3
  446. package/foundation/xle/browser-imports.test.mjs +74 -0
  447. package/package.json +11 -11
  448. package/scripts/postinstall.mjs +24 -5
  449. /package/assets/codemods/transforms/{next → v0.7.2}/__tests__/migrate-list-edge-compensation-to-edge.test.mjs +0 -0
  450. /package/assets/codemods/transforms/{next → v0.7.2}/migrate-list-edge-compensation-to-edge.mjs +0 -0
@@ -36,6 +36,7 @@ import {
36
36
  tokenizeQuery,
37
37
  SEARCH_DOMAINS,
38
38
  } from './search.mjs';
39
+ import {linkThisCliAsProjectCli} from '../../test-utils/release-fixtures.mjs';
39
40
 
40
41
  const REPO = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../..');
41
42
  const cwd = REPO;
@@ -470,8 +471,10 @@ describe('search leaf — error paths (pinned)', () => {
470
471
 
471
472
  it('searches the docs without @astryxdesign/core, and throws for a domain that needs it', async () => {
472
473
  const empty = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-search-no-core-'));
474
+ // A project whose own CLI answers, with no Core installed.
475
+ linkThisCliAsProjectCli(empty);
473
476
  try {
474
- // An open search outside an app covers the docs, as `astryx docs` does.
477
+ // An open search without Core covers the docs, as `astryx docs` does.
475
478
  const open = await search('make an integration', {cwd: empty});
476
479
  expect(open.data.results.length).toBeGreaterThan(0);
477
480
  expect(new Set(open.data.results.map(r => r.domain))).toEqual(
@@ -863,3 +866,142 @@ describe('search — usage guidance is indexed, a tier below description', () =>
863
866
  expect(hit?.score).toBe(45);
864
867
  });
865
868
  });
869
+
870
+ describe('search leaf — compound names, other names, and word forms', () => {
871
+ it('finds a compound name typed as separate words inside a longer query', async () => {
872
+ const {data} = await search('table header cell with sort arrow', {cwd});
873
+ expect(
874
+ data.results.slice(0, 3).map(r => r.name),
875
+ ).toContain('TableHeaderCell');
876
+ }, SLOW);
877
+
878
+ it('finds a one-word keyword typed as separate words', async () => {
879
+ // MetadataList declares `keyvalue`.
880
+ const {data} = await search('key value pairs', {cwd});
881
+ expect(data.results[0]).toMatchObject({
882
+ domain: 'component',
883
+ name: 'MetadataList',
884
+ });
885
+ }, SLOW);
886
+
887
+ it('finds a hook named by its words without the `use` prefix', async () => {
888
+ const {data} = await search('persisted table column settings', {cwd});
889
+ expect(data.results[0]).toMatchObject({
890
+ domain: 'hook',
891
+ name: 'useTableColumnSettings',
892
+ });
893
+ }, SLOW);
894
+
895
+ it('reads a camelCase identifier as the words it spells', async () => {
896
+ const {data} = await search('useTablePaginationState', {cwd});
897
+ expect(data.results[0]).toMatchObject({
898
+ domain: 'hook',
899
+ name: 'useTablePagination',
900
+ });
901
+ }, SLOW);
902
+
903
+ it('keeps each identifier of a multi-word query whole', async () => {
904
+ const {data} = await search('ChatMessageList ChatComposer', {cwd});
905
+ expect(data.results.slice(0, 2).map(r => r.name).sort()).toEqual([
906
+ 'ChatComposer',
907
+ 'ChatMessageList',
908
+ ]);
909
+ }, SLOW);
910
+
911
+ it("finds a component by another library's name for it", async () => {
912
+ for (const [q, name] of [
913
+ ['ScrollArea viewport', 'ScrollableArea'],
914
+ ['toggle group of buttons', 'ToggleButtonGroup'],
915
+ ['circular progress for uploads', 'Spinner'],
916
+ ]) {
917
+ const {data} = await search(q, {cwd});
918
+ expect(data.results[0], q).toMatchObject({domain: 'component', name});
919
+ }
920
+ }, SLOW);
921
+
922
+ it('reads a one-identifier other name as typed', async () => {
923
+ const {data} = await search('AppHeader', {cwd});
924
+ expect(data.results[0]).toMatchObject({
925
+ domain: 'component',
926
+ name: 'LayoutHeader',
927
+ });
928
+ }, SLOW);
929
+
930
+ it('adds no near siblings beside an entry the query names outright', async () => {
931
+ const {data} = await search('top nav mega menu', {cwd});
932
+ expect(data.results[0]).toMatchObject({name: 'TopNavMegaMenu'});
933
+ // Its sub-parts start with its name; read joined they would score in the
934
+ // whole-name tier right under it.
935
+ const siblings = data.results.filter(
936
+ r => r.name !== 'TopNavMegaMenu' && r.name.startsWith('TopNavMegaMenu'),
937
+ );
938
+ for (const sibling of siblings) expect(sibling.score).toBeLessThan(190);
939
+ }, SLOW);
940
+
941
+ it('ranks an entry the query names above a match on part of another name', async () => {
942
+ // `table header` names TableHeader; useTableStickyColumns only holds the
943
+ // words `sticky columns`.
944
+ const {data} = await search('table header with sticky columns', {cwd});
945
+ const names = data.results.map(r => r.name);
946
+ expect(names.indexOf('TableHeader')).toBeLessThan(
947
+ names.indexOf('useTableStickyColumns') === -1
948
+ ? Infinity
949
+ : names.indexOf('useTableStickyColumns'),
950
+ );
951
+ }, SLOW);
952
+
953
+ it('keeps an entry a query word names by its whole name at its own rank', async () => {
954
+ // Two words join into TextInput, and TextArea also matches two words
955
+ // through its joined keyword; one word is still TextArea's whole name.
956
+ const {data} = await search('text input and textarea with multi line', {
957
+ cwd,
958
+ });
959
+ expect(data.results[0]).toMatchObject({name: 'TextArea'});
960
+ }, SLOW);
961
+
962
+ it('ranks a match on more of the query above a shorter named entry', async () => {
963
+ const {data} = await search('dropdown menu radio options', {cwd});
964
+ expect(data.results[0]).toMatchObject({name: 'DropdownMenuRadioItem'});
965
+ }, SLOW);
966
+
967
+ it('lets a plural start a name through its singular', () => {
968
+ expect(
969
+ scoreCandidate('tokens', {domain: 'component', name: 'Tokenizer'}),
970
+ ).toMatchObject({score: 60});
971
+ });
972
+
973
+ it('finds Icon by a glyph name', async () => {
974
+ for (const q of ['cog', 'lightning bolt']) {
975
+ const {data} = await search(q, {cwd});
976
+ expect(data.results[0], q).toMatchObject({
977
+ domain: 'component',
978
+ name: 'Icon',
979
+ });
980
+ }
981
+ }, SLOW);
982
+
983
+ it('matches a verb form against its verb', () => {
984
+ // "spacing" lost the final e of "space".
985
+ expect(
986
+ scoreCandidate('spacing', {domain: 'component', name: 'Gap', keywords: ['space']}),
987
+ ).toMatchObject({score: 88});
988
+ expect(
989
+ scoreCandidate('dragging', {domain: 'component', name: 'Gap', keywords: ['drag']}),
990
+ ).toMatchObject({score: 88});
991
+ });
992
+
993
+ it('lets a plural start a longer word through its singular', () => {
994
+ expect(
995
+ scoreCandidate('chips', {domain: 'component', name: 'Chipset'}),
996
+ ).toMatchObject({score: 60});
997
+ // A four-letter singular is too short: "stats" does not start "statusbar".
998
+ expect(
999
+ scoreCandidate('stats', {domain: 'component', name: 'Statusbar'}),
1000
+ ).toBeNull();
1001
+ });
1002
+
1003
+ it('leaves an identifier joined with _ unmatched when nothing has it', async () => {
1004
+ const {data} = await search('qqzz_no_match_here', {cwd});
1005
+ expect(data.results).toEqual([]);
1006
+ }, SLOW);
1007
+ });
@@ -30,7 +30,7 @@ export type SearchResultEntry = {
30
30
  */
31
31
  description: string;
32
32
  /**
33
- * - Follow-up command to act on this result (e.g. `astryx component Button`, or `astryx theme add neutral` for a theme).
33
+ * - Follow-up command to act on this result (e.g. `astryx component Button`, or `astryx theme add neutral` for a theme; `astryx theme eject neutral` outside a project).
34
34
  */
35
35
  command: string;
36
36
  /**
@@ -72,6 +72,19 @@ export type SearchResponse = {
72
72
  matchCount: number;
73
73
  results: SearchResultEntry[];
74
74
  };
75
+ /**
76
+ * -
77
+ * `partial`, when an open search could not get the Core that components,
78
+ * hooks, and templates are read from (such as outside a project, offline,
79
+ * before the release's Core was downloaded): the domains it skipped and why.
80
+ * The results are the docs and themes.
81
+ */
82
+ meta?: {
83
+ partial?: {
84
+ skipped: SearchDomain[];
85
+ reason: string;
86
+ };
87
+ } | undefined;
75
88
  };
76
89
  /**
77
90
  * Options for `search()`.
@@ -20,7 +20,7 @@
20
20
  * @property {number} score - Relevance score (higher is better).
21
21
  * @property {string} reason - Human-readable reason the candidate matched (e.g. `keyword "button"`).
22
22
  * @property {string} description - One-line description, when available.
23
- * @property {string} command - Follow-up command to act on this result (e.g. `astryx component Button`, or `astryx theme add neutral` for a theme).
23
+ * @property {string} command - Follow-up command to act on this result (e.g. `astryx component Button`, or `astryx theme add neutral` for a theme; `astryx theme eject neutral` outside a project).
24
24
  * @property {string} [import] - Import path — present for component and hook results.
25
25
  * @property {string} [title] - Doc title — present for doc results.
26
26
  * @property {string} [section] - Section key — present when a doc result is one section of a topic; `command` reads only that section.
@@ -38,6 +38,11 @@
38
38
  * @property {string} data.query
39
39
  * @property {number} data.matchCount - How many candidates matched the query in total, before `limit` was applied. `results` is the bounded slice of that set, so `matchCount > results.length` means the answer was capped.
40
40
  * @property {SearchResultEntry[]} data.results
41
+ * @property {{partial?: {skipped: SearchDomain[], reason: string}}} [meta] -
42
+ * `partial`, when an open search could not get the Core that components,
43
+ * hooks, and templates are read from (such as outside a project, offline,
44
+ * before the release's Core was downloaded): the domains it skipped and why.
45
+ * The results are the docs and themes.
41
46
  */
42
47
 
43
48
  /**
@@ -11,7 +11,7 @@
11
11
  * (ERR_CORE_NOT_FOUND) when core can't be located.
12
12
  */
13
13
 
14
- import {findCoreDir, listComponents} from '../../foundation/fs/paths.mjs';
14
+ import {findProjectCoreDir, listComponents} from '../../foundation/fs/paths.mjs';
15
15
  import {resolveComponentReplacements} from '../../foundation/discovery/component-replacement.mjs';
16
16
  import {ERROR_CODES} from '../../foundation/response/error-codes.mjs';
17
17
  import {AstryxError} from '../error.mjs';
@@ -44,7 +44,7 @@ export async function resolveComponentReplacement(
44
44
  * @returns {{coreDir: string, components: string[]}}
45
45
  */
46
46
  export function resolveCore(cwd) {
47
- const coreDir = findCoreDir(cwd);
47
+ const coreDir = findProjectCoreDir(cwd);
48
48
  if (!coreDir) {
49
49
  throw new AstryxError(
50
50
  'Could not find @astryxdesign/core package. Make sure you are inside the design system monorepo or have @astryxdesign/core installed.',
@@ -115,7 +115,7 @@ function buildFeedback(component, issuesUrl) {
115
115
  */
116
116
  async function loadConfigSafely(cwd) {
117
117
  try {
118
- const project = await Project.load(cwd);
118
+ const project = await Project.load(cwd, {core: 'project'});
119
119
  return {
120
120
  loadedIntegrations: project.loadedIntegrations,
121
121
  issuesUrl: project.config.issuesUrl,
@@ -736,7 +736,7 @@ describe('integration template discovery', () => {
736
736
  const command = await runCli(['template', 'shell-side-nav'], tmpDir);
737
737
  expect(command.status).toBe(0);
738
738
  expect(command.stderr).toContain('@acme/extra has 1 integration issue');
739
- expect(command.stderr).toContain('astryx doctor');
739
+ expect(command.stderr).toMatch(/Run: .+ doctor/);
740
740
 
741
741
  const doctor = await runCli(['doctor'], tmpDir);
742
742
  expect(doctor.status).toBe(0);
@@ -129,6 +129,22 @@ export function removeThemeFromApp(slug: string, options?: {
129
129
  export function useThemeInApp(slug: string, options?: {
130
130
  cwd?: string;
131
131
  }): Promise<import("./theme.type.mjs").ThemeAppResponse>;
132
+ /**
133
+ * The Core theme targets are read from: the project's, else the release's
134
+ * own, installed into the per-user cache first when needed (a network install
135
+ * and an npm subprocess; spec AST-081 FR3). Throws as requireCoreDir does.
136
+ * @param {string} cwd
137
+ * @returns {string}
138
+ */
139
+ export function requireThemeCoreDir(cwd: string): string;
140
+ /**
141
+ * The Core a theme build reads outside a project: the one the answering copy
142
+ * carries, installed into the per-user cache first when needed (a network
143
+ * install and an npm subprocess; spec AST-081 FR3); null when there is none.
144
+ * @param {string} cwd
145
+ * @returns {string | null}
146
+ */
147
+ export function fetchCarriedThemeCoreDir(cwd: string): string | null;
132
148
  /**
133
149
  * A bundled theme entry kept for the historical public `listThemes()` helper.
134
150
  */
@@ -11,6 +11,10 @@
11
11
 
12
12
  import * as fs from 'node:fs';
13
13
  import * as path from 'node:path';
14
+ import {
15
+ findCarriedCoreDir,
16
+ requireCoreDir,
17
+ } from '../../foundation/fs/paths.mjs';
14
18
  import {Project} from '../../foundation/config/project.mjs';
15
19
  import {
16
20
  BUNDLED_THEME_PACKAGE,
@@ -41,6 +45,7 @@ import {stripCopyrightHeader} from '../../foundation/text/copyright-header.mjs';
41
45
  import {loadIntegrations} from '../../foundation/integrations/integrations.mjs';
42
46
  import {importThemeArtifact} from '../../foundation/fs/module-loader.mjs';
43
47
  import {applyWrites} from '../integration/add-helpers.mjs';
48
+ import {formatCliCommand} from '../../foundation/env/package-manager.mjs';
44
49
 
45
50
  export {BUNDLED_THEME_PACKAGE, THEMES_DIR};
46
51
 
@@ -565,7 +570,7 @@ export function resolveRecordedThemes(state, available) {
565
570
  );
566
571
  if (!theme) {
567
572
  throw new AstryxError(
568
- `Added theme "${slug}" from ${owner} is unavailable. Reinstall the package or restore the local theme, then run \`astryx theme add ${slug} --package ${owner}\`.`,
573
+ `Added theme "${slug}" from ${owner} is unavailable. Reinstall the package or restore the local theme, then run \`${formatCliCommand(`theme add ${slug} --package ${owner}`, state.projectDir)}\`.`,
569
574
  undefined,
570
575
  ERROR_CODES.ERR_UNKNOWN_THEME,
571
576
  );
@@ -732,14 +737,14 @@ export async function removeThemeFromApp(slug, options = {}) {
732
737
  const recordedSlug = recordedThemeSlug(state, slug);
733
738
  if (!state.configured || recordedSlug == null) {
734
739
  throw new AstryxError(
735
- `Theme "${slug}" is not added. Run \`astryx theme add ${slug}\` first.`,
740
+ `Theme "${slug}" is not added. Run \`${formatCliCommand(`theme add ${slug}`, state.projectDir)}\` first.`,
736
741
  undefined,
737
742
  ERROR_CODES.ERR_UNKNOWN_THEME,
738
743
  );
739
744
  }
740
745
  if (state.defaultSlug === recordedSlug) {
741
746
  throw new AstryxError(
742
- `Theme "${recordedSlug}" is the default. Run \`astryx theme use <other-slug>\` before removing it.`,
747
+ `Theme "${recordedSlug}" is the default. Run \`${formatCliCommand('theme use <other-slug>', state.projectDir)}\` before removing it.`,
743
748
  undefined,
744
749
  ERROR_CODES.ERR_THEME_INVALID,
745
750
  );
@@ -748,7 +753,7 @@ export async function removeThemeFromApp(slug, options = {}) {
748
753
  delete themes[recordedSlug];
749
754
  if (state.defaultSlug == null) {
750
755
  throw new AstryxError(
751
- 'The generated theme module has no default theme. Run `astryx theme use <slug>` first.',
756
+ `The generated theme module has no default theme. Run \`${formatCliCommand('theme use <slug>', state.projectDir)}\` first.`,
752
757
  undefined,
753
758
  ERROR_CODES.ERR_THEME_INVALID,
754
759
  );
@@ -776,7 +781,7 @@ export async function useThemeInApp(slug, options = {}) {
776
781
  const recordedSlug = recordedThemeSlug(state, slug);
777
782
  if (!state.configured || recordedSlug == null) {
778
783
  throw new AstryxError(
779
- `Theme "${slug}" is not added. Run \`astryx theme add ${slug}\` first.`,
784
+ `Theme "${slug}" is not added. Run \`${formatCliCommand(`theme add ${slug}`, state.projectDir)}\` first.`,
780
785
  undefined,
781
786
  ERROR_CODES.ERR_UNKNOWN_THEME,
782
787
  );
@@ -792,3 +797,25 @@ export async function useThemeInApp(slug, options = {}) {
792
797
  available,
793
798
  );
794
799
  }
800
+
801
+ /**
802
+ * The Core theme targets are read from: the project's, else the release's
803
+ * own, installed into the per-user cache first when needed (a network install
804
+ * and an npm subprocess; spec AST-081 FR3). Throws as requireCoreDir does.
805
+ * @param {string} cwd
806
+ * @returns {string}
807
+ */
808
+ export function requireThemeCoreDir(cwd) {
809
+ return requireCoreDir(cwd);
810
+ }
811
+
812
+ /**
813
+ * The Core a theme build reads outside a project: the one the answering copy
814
+ * carries, installed into the per-user cache first when needed (a network
815
+ * install and an npm subprocess; spec AST-081 FR3); null when there is none.
816
+ * @param {string} cwd
817
+ * @returns {string | null}
818
+ */
819
+ export function fetchCarriedThemeCoreDir(cwd) {
820
+ return findCarriedCoreDir(cwd, {fetch: true});
821
+ }
@@ -110,13 +110,14 @@ export function themeBuildForReport(file: string, options?: {
110
110
  receipt: import("../theme.type.mjs").ThemeBuildResponse | import("../theme.type.mjs").ThemeBuildCheckResponse | null;
111
111
  trailer: ThemeBuildTrailer | null;
112
112
  }>;
113
- /** @param {string} file @param {{out?: string, check?: boolean, iconsSpecifier?: string}} [options] @param {{cwd?: string}} [ctx] @returns {Promise<import('../theme.type.mjs').ThemeBuildResponse | import('../theme.type.mjs').ThemeBuildCheckResponse | null>} */
113
+ /** @param {string} file @param {{out?: string, check?: boolean, iconsSpecifier?: string}} [options] @param {{cwd?: string, projectCore?: boolean}} [ctx] `projectCore`: read only the project's installed Core, never the carried one (doctor; spec AST-081 FR3) @returns {Promise<import('../theme.type.mjs').ThemeBuildResponse | import('../theme.type.mjs').ThemeBuildCheckResponse | null>} */
114
114
  export function themeBuild(file: string, options?: {
115
115
  out?: string;
116
116
  check?: boolean;
117
117
  iconsSpecifier?: string;
118
118
  }, ctx?: {
119
119
  cwd?: string;
120
+ projectCore?: boolean;
120
121
  }): Promise<import("../theme.type.mjs").ThemeBuildResponse | import("../theme.type.mjs").ThemeBuildCheckResponse | null>;
121
122
  /** @param {string[]} files @param {{familyKey: string, check?: boolean, iconsSpecifier?: string, __onPrivateVarError?: () => void}} options @param {{cwd?: string}} [ctx] */
122
123
  export function themeBuildFamily(files: string[], options: {
@@ -70,7 +70,11 @@ import {
70
70
  CLI_ROOT,
71
71
  findCoreDir,
72
72
  findInstalledPackage,
73
+ findProjectCoreDir,
73
74
  } from '../../../foundation/fs/paths.mjs';
75
+ import {isGlobalCopy} from '../../../foundation/release/choose.mjs';
76
+ import {isInReleaseCache} from '../../../foundation/release/cache.mjs';
77
+ import {fetchCarriedThemeCoreDir} from '../_adapter.mjs';
74
78
  import {
75
79
  assertWithin,
76
80
  sanitizeName,
@@ -306,10 +310,15 @@ function realLocation(target) {
306
310
  *
307
311
  * @param {string} cwd
308
312
  * @param {CoreBindings} cliCore - The CLI's own Core, from {@link loadCliCore}.
313
+ * @param {{projectOnly?: boolean}} [options] `projectOnly`: never the
314
+ * carried Core, as for doctor (spec AST-081 FR3)
309
315
  * @returns {Promise<CoreBindings | null>}
310
316
  */
311
- async function loadProjectCore(cwd, cliCore) {
312
- const dir = findInstalledPackage(cwd, '@astryxdesign/core');
317
+ async function loadProjectCore(cwd, cliCore, {projectOnly = false} = {}) {
318
+ // Outside a project, the Core the answering copy carries (spec AST-081 FR3).
319
+ const dir =
320
+ findInstalledPackage(cwd, '@astryxdesign/core') ??
321
+ (projectOnly ? null : fetchCarriedThemeCoreDir(cwd));
313
322
  const themeFile = dir ? importTarget(dir, './theme') : null;
314
323
  if (!dir || !themeFile) return null;
315
324
  if (cliCore.themeModule) {
@@ -355,14 +364,54 @@ const _projectCores = new Map();
355
364
 
356
365
  /**
357
366
  * Choose the Core this build generates with: the project's installed one when
358
- * {@link loadProjectCore} finds it, else the CLI's own.
367
+ * {@link loadProjectCore} finds it, else the CLI's own. With `projectOnly`,
368
+ * only the project's installed Core: a build that cannot use it fails rather
369
+ * than read the Core the answering copy carries (spec AST-081 FR3).
359
370
  * @param {string} cwd
371
+ * @param {{projectOnly?: boolean}} [options]
360
372
  */
361
- async function selectCore(cwd) {
373
+ async function selectCore(cwd, {projectOnly = false} = {}) {
362
374
  const cliCore = await loadCliCore();
363
- if (!_projectCores.has(cwd))
364
- _projectCores.set(cwd, await loadProjectCore(cwd, cliCore));
365
- setCore(_projectCores.get(cwd) ?? cliCore);
375
+ const key = `${projectOnly ? 'project' : 'any'}\0${cwd}`;
376
+ if (!_projectCores.has(key))
377
+ _projectCores.set(key, await loadProjectCore(cwd, cliCore, {projectOnly}));
378
+ const projectCore = _projectCores.get(key);
379
+ // A global or cached copy's own Core is the carried one.
380
+ if (projectOnly && !projectCore && carriesCore() && !cliCoreIsProjects(cwd)) {
381
+ throw new AstryxError(
382
+ "The project's Core isn't installed or does not load.",
383
+ undefined,
384
+ ERROR_CODES.ERR_CORE_NOT_FOUND,
385
+ );
386
+ }
387
+ setCore(projectCore ?? cliCore);
388
+ }
389
+
390
+ /**
391
+ * Whether this CLI copy is one installed for the computer or kept in the
392
+ * per-user release cache, whose own Core import is the carried Core.
393
+ * @returns {boolean}
394
+ */
395
+ function carriesCore() {
396
+ return isGlobalCopy(CLI_ROOT) || isInReleaseCache(CLI_ROOT);
397
+ }
398
+
399
+ /**
400
+ * Whether the Core the CLI's own import loads is the one the project at
401
+ * `cwd` installed.
402
+ * @param {string} cwd
403
+ * @returns {boolean}
404
+ */
405
+ function cliCoreIsProjects(cwd) {
406
+ const dir = findProjectCoreDir(cwd);
407
+ if (!dir) return false;
408
+ try {
409
+ const own = fileURLToPath(import.meta.resolve('@astryxdesign/core/theme'));
410
+ const theirs = importTarget(dir, './theme');
411
+ return theirs != null && realOrSelf(own) === realOrSelf(theirs);
412
+ } catch {
413
+ return false;
414
+ }
366
415
  }
367
416
 
368
417
  /**
@@ -401,8 +450,7 @@ function inAstryxRepository(cwd) {
401
450
  * @returns {string}
402
451
  */
403
452
  export function coreUnavailableMessage(cwd, importError) {
404
- const why =
405
- '`astryx theme build` generates CSS with @astryxdesign/core, the same code the runtime <Theme> uses';
453
+ const why = `\`${formatCliCommand('theme build')}\` generates CSS with @astryxdesign/core, the same code the runtime <Theme> uses`;
406
454
  const detail = importError ? `\n Import error: ${importError.message}` : '';
407
455
  if (inAstryxRepository(cwd)) {
408
456
  return (
@@ -2497,13 +2545,14 @@ function validateHeadingTypeAugmentationSupport(themeDef) {
2497
2545
  * output must stay inside, for a CLI-internal scratch build. `iconsSpecifier` overrides the icon registry import
2498
2546
  * specifier in the generated module (e.g. `./icons.mjs`); when omitted, the
2499
2547
  * direct registry specifiers from the theme source are emitted unchanged.
2500
- * @param {{cwd?: string}} [ctx]
2548
+ * @param {{cwd?: string, projectCore?: boolean}} [ctx] `projectCore`: only the
2549
+ * project's installed Core (spec AST-081 FR3)
2501
2550
  * @returns {Promise<any>}
2502
2551
  */
2503
2552
  async function themeBuildInternal(
2504
2553
  file,
2505
2554
  options = {},
2506
- {cwd = process.cwd()} = {},
2555
+ {cwd = process.cwd(), projectCore = false} = {},
2507
2556
  /** @type {{compact?: boolean, trailers?: ThemeBuildTrailer[]}} */ report = {},
2508
2557
  ) {
2509
2558
  const filePath = path.resolve(cwd, file);
@@ -2522,7 +2571,14 @@ async function themeBuildInternal(
2522
2571
  // A compact report has no per-theme heading, so each warning names its file.
2523
2572
  const source = report.compact ? `${path.relative(cwd, filePath)}: ` : '';
2524
2573
 
2525
- await selectCore(cwd);
2574
+ if (projectCore && !findProjectCoreDir(cwd)) {
2575
+ throw new AstryxError(
2576
+ "The project's Core isn't installed.",
2577
+ undefined,
2578
+ ERROR_CODES.ERR_CORE_NOT_FOUND,
2579
+ );
2580
+ }
2581
+ await selectCore(cwd, {projectOnly: projectCore});
2526
2582
 
2527
2583
  // Standalone builds only need interception when an older Core could erase
2528
2584
  // adaptations. Family preparation always supplies the same recorder as its
@@ -3272,7 +3328,7 @@ export function printCompactTrailer(trailers, {hint = false} = {}) {
3272
3328
  )
3273
3329
  .join(', ');
3274
3330
  logger.log(
3275
- `[note] Fonts named but not loaded: ${named}. Load them in your app (recipe: astryx docs typography).`,
3331
+ `[note] Fonts named but not loaded: ${named}. Load them in your app (recipe: ${formatCliCommand('docs typography')}).`,
3276
3332
  );
3277
3333
  }
3278
3334
  if (hint) {
@@ -3327,7 +3383,7 @@ export async function themeBuildForReport(
3327
3383
  return {receipt, trailer: trailers[0] ?? null};
3328
3384
  }
3329
3385
 
3330
- /** @param {string} file @param {{out?: string, check?: boolean, iconsSpecifier?: string}} [options] @param {{cwd?: string}} [ctx] @returns {Promise<import('../theme.type.mjs').ThemeBuildResponse | import('../theme.type.mjs').ThemeBuildCheckResponse | null>} */
3386
+ /** @param {string} file @param {{out?: string, check?: boolean, iconsSpecifier?: string}} [options] @param {{cwd?: string, projectCore?: boolean}} [ctx] `projectCore`: read only the project's installed Core, never the carried one (doctor; spec AST-081 FR3) @returns {Promise<import('../theme.type.mjs').ThemeBuildResponse | import('../theme.type.mjs').ThemeBuildCheckResponse | null>} */
3331
3387
  export async function themeBuild(file, options = {}, ctx = {}) {
3332
3388
  return /** @type {any} */ (themeBuildInternal(file, options, ctx));
3333
3389
  }
@@ -37,6 +37,7 @@ import * as path from 'node:path';
37
37
  import {fileURLToPath} from 'node:url';
38
38
 
39
39
  import {findInstalledPackage} from '../../../foundation/fs/paths.mjs';
40
+ import {CLI_VERSION} from '../../../test-utils/release-fixtures.mjs';
40
41
 
41
42
  const HERE = path.dirname(fileURLToPath(import.meta.url));
42
43
  const REPO_ROOT = path.resolve(HERE, '../../../../..');
@@ -55,8 +56,12 @@ const RUNTIME_DEPS = [
55
56
  'zod',
56
57
  ];
57
58
 
58
- const OLD_CORE_VERSION = '0.5.4';
59
- const CURRENT_CORE_VERSION = '0.6.0';
59
+ // Both Cores carry the packed CLI's own version: a run through the bin in a
60
+ // project whose Core is another release is handed to that release (spec
61
+ // AST-081 FR2), so the old surface is told apart by what it exports, not by
62
+ // its version.
63
+ const OLD_CORE_VERSION = CLI_VERSION;
64
+ const CURRENT_CORE_VERSION = CLI_VERSION;
60
65
  const TSC_BIN = path.join(REPO_ROOT, 'node_modules/typescript/bin/tsc');
61
66
 
62
67
  /**
@@ -278,8 +283,20 @@ function buildTheme(themeFile, extraArgs = []) {
278
283
  'node_modules/@astryxdesign/cli/clients/cli/bin/astryx.mjs',
279
284
  );
280
285
  try {
286
+ // A packed CLI outside any project's package.json reads as installed for
287
+ // the computer, so it gets a home folder of its own: nothing it keeps for
288
+ // the computer lands in the real one.
289
+ const home = path.join(root, 'home');
290
+ const env = {
291
+ ...process.env,
292
+ HOME: home,
293
+ USERPROFILE: home,
294
+ XDG_CONFIG_HOME: path.join(home, '.config'),
295
+ XDG_CACHE_HOME: path.join(home, '.cache'),
296
+ NO_UPDATE_NOTIFIER: '1',
297
+ };
281
298
  // prettier-ignore
282
- const stdout = execFileSync(process.execPath, [bin, 'theme', 'build', ...(Array.isArray(themeFile) ? themeFile : [themeFile]), ...extraArgs, '--json'], {cwd: consumer, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe']});
299
+ const stdout = execFileSync(process.execPath, [bin, 'theme', 'build', ...(Array.isArray(themeFile) ? themeFile : [themeFile]), ...extraArgs, '--json'], {cwd: consumer, encoding: 'utf8', env, stdio: ['ignore', 'pipe', 'pipe']});
283
300
  return {status: 0, envelope: JSON.parse(stdout)};
284
301
  } catch (error) {
285
302
  const e = /** @type {any} */ (error);
@@ -21,9 +21,11 @@
21
21
  * @output Ordered, case-insensitively deduped family names, and the human
22
22
  * help snippet (<link> pair + @font-face) printed after the install
23
23
  * instructions.
24
- * @position Sits beside build.mjs (api/theme/build/). Pure functions, no IO,
25
- * so the `--json` receipt and the human output share one source of truth.
24
+ * @position Sits beside build.mjs (api/theme/build/). Pure functions, no IO
25
+ * beyond the CLI invocation the human help names (formatCliCommand), so the
26
+ * `--json` receipt and the human output share one source of truth.
26
27
  */
28
+ import {formatCliCommand} from '../../../foundation/env/package-manager.mjs';
27
29
 
28
30
  // font-family values that never need loading: CSS generic families, the ui-*
29
31
  // system keywords, and CSS-wide keywords that can appear in a token value.
@@ -254,6 +256,6 @@ function fontLoadingRecipe(families) {
254
256
  font-display: swap;
255
257
  }
256
258
 
257
- Recipe and fallback-stack guidance: astryx docs typography
259
+ Recipe and fallback-stack guidance: ${formatCliCommand('docs typography')}
258
260
  `;
259
261
  }
@@ -15,6 +15,7 @@
15
15
 
16
16
  import {describe, it, expect} from 'vitest';
17
17
  import {collectUnloadedFonts, formatFontLoadingHelp} from './font-warning.mjs';
18
+ import {formatCliCommand} from '../../../foundation/env/package-manager.mjs';
18
19
 
19
20
  describe('collectUnloadedFonts', () => {
20
21
  it('returns webfont families from font-family tokens, skipping known fallbacks', () => {
@@ -255,7 +256,7 @@ describe('formatFontLoadingHelp', () => {
255
256
  });
256
257
 
257
258
  it('points at the docs recipe', () => {
258
- expect(help).toContain('astryx docs typography');
259
+ expect(help).toContain(formatCliCommand('docs typography'));
259
260
  });
260
261
 
261
262
  it('percent-encodes family names in the css2 URL', () => {
@@ -81,14 +81,14 @@ describe('themeRemove', () => {
81
81
  it('refuses to remove the default and names theme use', async () => {
82
82
  await expect(themeRemove('ocean', {cwd: tmpDir})).rejects.toMatchObject({
83
83
  code: 'ERR_THEME_INVALID',
84
- message: expect.stringContaining('astryx theme use'),
84
+ message: expect.stringContaining(' theme use <other-slug>`'),
85
85
  });
86
86
  });
87
87
 
88
88
  it('names theme add when the slug is not added', async () => {
89
89
  await expect(themeRemove('missing', {cwd: tmpDir})).rejects.toMatchObject({
90
90
  code: 'ERR_UNKNOWN_THEME',
91
- message: expect.stringContaining('astryx theme add missing'),
91
+ message: expect.stringContaining(' theme add missing`'),
92
92
  });
93
93
  });
94
94
  });
@@ -12,10 +12,10 @@
12
12
  */
13
13
 
14
14
  import * as path from 'node:path';
15
- import {findCoreDir} from '../../../foundation/fs/paths.mjs';
15
+ import {requireThemeCoreDir} from '../_adapter.mjs';
16
16
  import {collectThemingTargets} from '../../../foundation/discovery/theming-targets.mjs';
17
17
  import {ERROR_CODES} from '../../../foundation/response/error-codes.mjs';
18
- import {AstryxError} from '../../error.mjs';
18
+ import {AstryxError, coreDirOrThrow} from '../../error.mjs';
19
19
 
20
20
  /**
21
21
  * Whether a target matches the caller's filter loosely: any target whose key,
@@ -46,14 +46,7 @@ function matchesLoosely(target, filter) {
46
46
  * @returns {Promise<import('../theme.type.mjs').ThemeTargetsResponse>}
47
47
  */
48
48
  export async function themeTargets(filter, {cwd = process.cwd()} = {}) {
49
- const coreDir = findCoreDir(cwd);
50
- if (!coreDir) {
51
- throw new AstryxError(
52
- 'Could not find @astryxdesign/core package',
53
- undefined,
54
- ERROR_CODES.ERR_CORE_NOT_FOUND,
55
- );
56
- }
49
+ const coreDir = coreDirOrThrow(requireThemeCoreDir, cwd);
57
50
 
58
51
  const all = await collectThemingTargets(path.join(coreDir, 'src'));
59
52
  const needle = filter ? String(filter).toLowerCase() : null;