@astryxdesign/cli 0.7.2-canary.fe2c655 → 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 (694) hide show
  1. package/CHANGELOG.md +119 -0
  2. package/README.md +216 -162
  3. package/api/build/_adapter.d.mts +66 -0
  4. package/api/build/_adapter.mjs +139 -4
  5. package/api/build/blocks/blocks.d.mts +92 -0
  6. package/api/build/blocks/blocks.mjs +528 -0
  7. package/api/build/blocks/blocks.test.mjs +238 -0
  8. package/api/build/blocks/blocks.weights.json +1 -0
  9. package/api/build/build.doc.mjs +4 -0
  10. package/api/build/build.mjs +25 -6
  11. package/api/build/build.type.mjs +1 -1
  12. package/api/build/help/help.mjs +1 -1
  13. package/api/build/kit/answer.d.mts +18 -0
  14. package/api/build/kit/answer.mjs +195 -0
  15. package/api/build/kit/answer.test.mjs +108 -0
  16. package/api/build/kit/kind.d.mts +25 -0
  17. package/api/build/kit/kind.mjs +170 -0
  18. package/api/build/kit/kit.d.mts +2 -4
  19. package/api/build/kit/kit.mjs +8 -406
  20. package/api/build/kit/rank.d.mts +3 -32
  21. package/api/build/kit/rank.mjs +25 -342
  22. package/api/build/kit/rank.test.mjs +2 -1
  23. package/api/build/kit/signals.d.mts +79 -0
  24. package/api/build/kit/signals.mjs +295 -0
  25. package/api/build/kit/start.d.mts +32 -0
  26. package/api/build/kit/start.mjs +163 -0
  27. package/api/build/kit/words.d.mts +72 -0
  28. package/api/build/kit/words.mjs +202 -0
  29. package/api/build/tree.d.mts +20 -0
  30. package/api/build/tree.mjs +53 -0
  31. package/api/build/tree.test.mjs +185 -0
  32. package/api/build/tree.weights.json +1 -0
  33. package/api/component/_adapter.d.mts +72 -1
  34. package/api/component/_adapter.mjs +251 -23
  35. package/api/component/component-replacement.test.mjs +1 -0
  36. package/api/component/component.doc.mjs +6 -1
  37. package/api/component/component.mjs +97 -16
  38. package/api/component/component.test.mjs +95 -0
  39. package/api/component/doc-links.test.mjs +386 -0
  40. package/api/component/integration-export-name.test.mjs +644 -0
  41. package/api/component/list/list.mjs +42 -4
  42. package/api/discover/_adapter.d.mts +131 -12
  43. package/api/discover/_adapter.mjs +520 -34
  44. package/api/discover/_adapter.test.mjs +641 -17
  45. package/api/discover/_catalog-view.d.mts +16 -5
  46. package/api/discover/_catalog-view.mjs +32 -8
  47. package/api/discover/detail/doc/doc.mjs +9 -1
  48. package/api/discover/discover.d.mts +11 -0
  49. package/api/discover/discover.doc.mjs +9 -7
  50. package/api/discover/discover.mjs +120 -19
  51. package/api/discover/discover.type.d.mts +20 -7
  52. package/api/discover/discover.type.mjs +17 -7
  53. package/api/discover/list/list.d.mts +4 -3
  54. package/api/discover/list/list.mjs +17 -10
  55. package/api/docs/_adapter.d.mts +76 -2
  56. package/api/docs/_adapter.mjs +630 -34
  57. package/api/docs/docs-homes.test.mjs +127 -0
  58. package/api/docs/docs.doc.mjs +5 -4
  59. package/api/docs/docs.mjs +2 -1
  60. package/api/docs/docs.test.mjs +39 -20
  61. package/api/docs/integration-tree.test.mjs +42 -20
  62. package/api/docs/integrationDocs.test.mjs +125 -1
  63. package/api/docs/list/list.mjs +12 -2
  64. package/api/docs/namespace-section.test.mjs +6 -6
  65. package/api/docs/reference-blocks.test.mjs +3 -5
  66. package/api/doctor/_adapter.d.mts +128 -0
  67. package/api/doctor/_adapter.mjs +253 -4
  68. package/api/doctor/doctor.d.mts +217 -0
  69. package/api/doctor/doctor.doc.mjs +3 -2
  70. package/api/doctor/doctor.mjs +691 -52
  71. package/api/doctor/doctor.test.mjs +267 -18
  72. package/api/doctor/release-checks.test.mjs +156 -0
  73. package/api/doctor/theme-checks.mjs +17 -4
  74. package/api/doctor/theme-checks.test.mjs +223 -7
  75. package/api/error.d.mts +10 -0
  76. package/api/error.mjs +25 -0
  77. package/api/gap-report/gap-report.d.mts +3 -0
  78. package/api/gap-report/gap-report.doc.mjs +11 -3
  79. package/api/gap-report/gap-report.mjs +219 -52
  80. package/api/gap-report/gap-report.test.mjs +233 -0
  81. package/api/gap-report/gap-report.type.d.mts +41 -0
  82. package/api/gap-report/gap-report.type.mjs +18 -0
  83. package/api/hook/_adapter.mjs +3 -11
  84. package/api/hook/hook.doc.mjs +4 -0
  85. package/api/hook/hook.mjs +1 -1
  86. package/api/hook/list/list.d.mts +1 -1
  87. package/api/index.d.mts +2 -0
  88. package/api/index.mjs +2 -0
  89. package/api/init/_adapter.d.mts +22 -0
  90. package/api/init/_adapter.mjs +59 -0
  91. package/api/init/init.d.mts +7 -5
  92. package/api/init/init.doc.mjs +45 -10
  93. package/api/init/init.mjs +28 -4
  94. package/api/init/init.test.mjs +18 -2
  95. package/api/init/init.type.d.mts +50 -5
  96. package/api/init/init.type.mjs +39 -5
  97. package/api/init/remove/remove.mjs +17 -4
  98. package/api/init/remove/remove.test.mjs +114 -2
  99. package/api/init/run/run.d.mts +1 -1
  100. package/api/init/run/run.mjs +86 -12
  101. package/api/init/user/user.d.mts +14 -0
  102. package/api/init/user/user.mjs +87 -0
  103. package/api/init/user/user.test.mjs +232 -0
  104. package/api/integration/_adapter.d.mts +53 -0
  105. package/api/integration/_adapter.mjs +132 -0
  106. package/api/integration/add-contribution.component-names.test.mjs +66 -0
  107. package/api/integration/add-contribution.mjs +8 -4
  108. package/api/integration/add-contribution.test.mjs +4 -2
  109. package/api/integration/authoring-checks.mjs +192 -14
  110. package/api/integration/authoring-checks.test.mjs +1028 -0
  111. package/api/integration/authoring-checks.type.d.mts +143 -0
  112. package/api/integration/authoring-checks.type.mjs +74 -2
  113. package/api/integration/check/check.d.mts +96 -0
  114. package/api/integration/check/check.mjs +357 -0
  115. package/api/integration/check/check.test.mjs +489 -0
  116. package/api/integration/integration-check.type.d.mts +118 -0
  117. package/api/integration/integration-check.type.mjs +84 -0
  118. package/api/integration/integrationCheck.doc.d.mts +10 -0
  119. package/api/integration/integrationCheck.doc.mjs +73 -0
  120. package/api/integration/integrationComponentConflicts.doc.mjs +45 -6
  121. package/api/integration/integrationDocConflicts.doc.mjs +22 -5
  122. package/api/integration/pack-check.mjs +486 -123
  123. package/api/integration/pack-check.test.mjs +817 -10
  124. package/api/integration/validate-integration-case-disk.test.mjs +111 -0
  125. package/api/integration/validate-integration-fixes.test.mjs +73 -5
  126. package/api/integration/validate-integration-gaps.test.mjs +1975 -0
  127. package/api/integration/validate-integration.d.mts +23 -2
  128. package/api/integration/validate-integration.mjs +358 -45
  129. package/api/integration/validate-integration.test.mjs +88 -8
  130. package/api/integration/validate-integration.type.d.mts +105 -0
  131. package/api/integration/validate-integration.type.mjs +79 -1
  132. package/api/integration/validate-unread-theme-folders.test.mjs +3 -2
  133. package/api/integration/validateIntegration.doc.mjs +19 -3
  134. package/api/search/ORDERING.md +28 -0
  135. package/api/search/_adapter.d.mts +91 -0
  136. package/api/search/_adapter.mjs +172 -0
  137. package/api/search/_adapter.test.mjs +173 -0
  138. package/api/search/_candidate-score.d.mts +40 -0
  139. package/api/search/_candidate-score.mjs +238 -0
  140. package/api/search/_cards.json +6098 -0
  141. package/api/search/_compound.d.mts +50 -0
  142. package/api/search/_compound.mjs +170 -0
  143. package/api/search/_doc-searcher.d.mts +4 -0
  144. package/api/search/_doc-searcher.mjs +23 -0
  145. package/api/search/_doc-searcher.test.mjs +36 -0
  146. package/api/search/_doc-searcher.weights.json +1 -0
  147. package/api/search/_doc-text.d.mts +30 -0
  148. package/api/search/_doc-text.mjs +87 -0
  149. package/api/search/_entries.d.mts +63 -0
  150. package/api/search/_entries.mjs +232 -0
  151. package/api/search/_entries.test.mjs +48 -0
  152. package/api/search/_features.d.mts +35 -0
  153. package/api/search/_features.mjs +155 -0
  154. package/api/search/_gather-components.d.mts +26 -0
  155. package/api/search/_gather-components.mjs +284 -0
  156. package/api/search/_gather-docs.d.mts +20 -0
  157. package/api/search/_gather-docs.mjs +254 -0
  158. package/api/search/_gather-hooks.d.mts +12 -0
  159. package/api/search/_gather-hooks.mjs +52 -0
  160. package/api/search/_gather-templates.d.mts +25 -0
  161. package/api/search/_gather-templates.mjs +107 -0
  162. package/api/search/_lexical.d.mts +135 -0
  163. package/api/search/_lexical.mjs +351 -0
  164. package/api/search/_main-searcher.d.mts +19 -0
  165. package/api/search/_main-searcher.mjs +115 -0
  166. package/api/search/_main-searcher.test.mjs +106 -0
  167. package/api/search/_main-searcher.weights.json +1 -0
  168. package/api/search/_module-doc.d.mts +32 -0
  169. package/api/search/_module-doc.mjs +78 -0
  170. package/api/search/_order.d.mts +32 -0
  171. package/api/search/_order.mjs +162 -0
  172. package/api/search/_order.test.mjs +72 -0
  173. package/api/search/_priority.d.mts +50 -0
  174. package/api/search/_priority.mjs +139 -0
  175. package/api/search/_query-words.d.mts +25 -0
  176. package/api/search/_query-words.mjs +92 -0
  177. package/api/search/_query.d.mts +65 -0
  178. package/api/search/_query.mjs +176 -0
  179. package/api/search/_result.d.mts +97 -0
  180. package/api/search/_result.mjs +150 -0
  181. package/api/search/_score.d.mts +60 -0
  182. package/api/search/_score.mjs +283 -0
  183. package/api/search/_synonyms.d.mts +21 -0
  184. package/api/search/_synonyms.mjs +67 -0
  185. package/api/search/_template-searcher.d.mts +4 -0
  186. package/api/search/_template-searcher.mjs +23 -0
  187. package/api/search/_template-searcher.test.mjs +40 -0
  188. package/api/search/_template-searcher.weights.json +1 -0
  189. package/api/search/_titles.d.mts +33 -0
  190. package/api/search/_titles.mjs +65 -0
  191. package/api/search/_words.d.mts +64 -0
  192. package/api/search/_words.mjs +157 -0
  193. package/api/search/search.d.mts +22 -236
  194. package/api/search/search.doc.mjs +14 -7
  195. package/api/search/search.domain-priority.test.mjs +108 -18
  196. package/api/search/search.integration-check.test.mjs +24 -0
  197. package/api/search/search.mjs +59 -1904
  198. package/api/search/search.test.mjs +193 -15
  199. package/api/search/search.type.d.mts +20 -2
  200. package/api/search/search.type.mjs +12 -2
  201. package/api/search/tree.d.mts +15 -0
  202. package/api/search/tree.mjs +40 -0
  203. package/api/search/tree.test.mjs +66 -0
  204. package/api/search/tree.weights.json +1 -0
  205. package/api/swizzle/_adapter.mjs +2 -2
  206. package/api/swizzle/copy/copy.mjs +29 -15
  207. package/api/template/template-integration.test.mjs +1 -1
  208. package/api/template/template.mjs +5 -1
  209. package/api/template/template.test.mjs +120 -3
  210. package/api/theme/_adapter.d.mts +16 -0
  211. package/api/theme/_adapter.mjs +32 -5
  212. package/api/theme/build/build.d.mts +2 -1
  213. package/api/theme/build/build.family.test.mjs +64 -0
  214. package/api/theme/build/build.mjs +250 -38
  215. package/api/theme/build/build.packed-old-core.test.mjs +21 -3
  216. package/api/theme/build/font-warning.mjs +5 -3
  217. package/api/theme/build/font-warning.test.mjs +2 -1
  218. package/api/theme/remove/remove.test.mjs +2 -2
  219. package/api/theme/targets/targets.mjs +3 -10
  220. package/api/theme/themeBuild.doc.mjs +4 -4
  221. package/api/theme/themeTargets.doc.mjs +4 -0
  222. package/api/theme/use/use.test.mjs +1 -1
  223. package/api/upgrade/_adapter.d.mts +97 -11
  224. package/api/upgrade/_adapter.mjs +315 -32
  225. package/api/upgrade/registry/registry.d.mts +10 -4
  226. package/api/upgrade/registry/registry.mjs +33 -16
  227. package/api/upgrade/registry/registry.test.mjs +85 -0
  228. package/api/upgrade/run/cli-behind-core.test.mjs +438 -0
  229. package/api/upgrade/run/run.d.mts +4 -2
  230. package/api/upgrade/run/run.mjs +48 -4
  231. package/api/upgrade/run/run.test.mjs +417 -3
  232. package/api/upgrade/status/status.d.mts +21 -5
  233. package/api/upgrade/status/status.mjs +45 -8
  234. package/api/upgrade/upgrade.doc.mjs +16 -8
  235. package/api/upgrade/upgrade.mjs +18 -3
  236. package/api/upgrade/upgrade.test.mjs +194 -0
  237. package/api/upgrade/upgrade.type.d.mts +28 -3
  238. package/api/upgrade/upgrade.type.mjs +24 -7
  239. package/assets/codemods/__tests__/registry.test.mjs +1 -0
  240. package/assets/codemods/registry.mjs +1 -0
  241. package/assets/codemods/runner.mjs +7 -1
  242. package/assets/codemods/transforms/v0.7.0/__tests__/validate-data-token-ownership.test.mjs +11 -0
  243. package/assets/codemods/transforms/v0.7.0/validate-data-token-ownership.mjs +3 -3
  244. package/assets/codemods/transforms/v0.7.2/__tests__/migrate-list-edge-compensation-to-edge.test.mjs +120 -0
  245. package/assets/codemods/transforms/v0.7.2/index.mjs +20 -0
  246. package/assets/codemods/transforms/v0.7.2/migrate-list-edge-compensation-to-edge.mjs +198 -0
  247. package/assets/discover/official-packages.json +367 -0
  248. package/assets/docs/README.md +10 -0
  249. package/assets/docs/author-a-theme.doc.mjs +5 -5
  250. package/assets/docs/browser-support.doc.mjs +1 -1
  251. package/assets/docs/getting-started.doc.mjs +26 -6
  252. package/assets/docs/is-my-code-good.doc.mjs +287 -0
  253. package/assets/docs/styling-overview.doc.mjs +1 -1
  254. package/assets/docs/theme.doc.dense.mjs +1 -1
  255. package/assets/docs/theme.doc.mjs +2 -2
  256. package/assets/docs/theme.doc.zh.mjs +1 -1
  257. package/assets/docs/tree/add-a-component.doc.mjs +22 -2
  258. package/assets/docs/tree/add-a-theme.doc.mjs +14 -4
  259. package/assets/docs/tree/add-a-topic.doc.mjs +54 -9
  260. package/assets/docs/tree/agent-guidance.doc.mjs +19 -5
  261. package/assets/docs/tree/api.doc.mjs +6 -0
  262. package/assets/docs/tree/authoring.doc.mjs +61 -0
  263. package/assets/docs/tree/block-template.doc.mjs +8 -20
  264. package/assets/docs/tree/build-the-template.doc.mjs +1 -1
  265. package/assets/docs/tree/building-blocks.doc.mjs +6 -2
  266. package/assets/docs/tree/check-the-props.doc.mjs +201 -0
  267. package/assets/docs/tree/check-your-docs.doc.mjs +69 -11
  268. package/assets/docs/tree/checks.doc.mjs +32 -24
  269. package/assets/docs/tree/ci-and-containers.doc.mjs +101 -0
  270. package/assets/docs/tree/cli-command.doc.mjs +142 -0
  271. package/assets/docs/tree/cli.doc.mjs +7 -1
  272. package/assets/docs/tree/codemods.doc.mjs +17 -7
  273. package/assets/docs/tree/commands.doc.mjs +7 -1
  274. package/assets/docs/tree/component-family.doc.mjs +8 -6
  275. package/assets/docs/tree/component-imports.doc.mjs +21 -3
  276. package/assets/docs/tree/components.doc.mjs +7 -1
  277. package/assets/docs/tree/computer-agent-guide.doc.mjs +160 -0
  278. package/assets/docs/tree/computer-integrations.doc.mjs +93 -0
  279. package/assets/docs/tree/configuration.doc.mjs +8 -4
  280. package/assets/docs/tree/debug-and-gap-reports.doc.mjs +41 -22
  281. package/assets/docs/tree/define-the-theme.doc.mjs +7 -6
  282. package/assets/docs/tree/describe-the-component.doc.mjs +3 -3
  283. package/assets/docs/tree/docs.doc.mjs +9 -3
  284. package/assets/docs/tree/document-the-template.doc.mjs +1 -1
  285. package/assets/docs/tree/document-the-theme.doc.mjs +4 -4
  286. package/assets/docs/tree/export-template-assets.doc.mjs +1 -1
  287. package/assets/docs/tree/extend-or-replace.doc.mjs +19 -9
  288. package/assets/docs/{authoring.doc.mjs → tree/field-reference.doc.mjs} +4 -3
  289. package/assets/docs/tree/fonts-and-assets.doc.mjs +3 -3
  290. package/assets/docs/tree/generate-a-palette.doc.mjs +8 -6
  291. package/assets/docs/tree/global-integrations.doc.mjs +124 -0
  292. package/assets/docs/tree/global.doc.mjs +131 -0
  293. package/assets/docs/tree/grade-template-with-agent.doc.mjs +5 -5
  294. package/assets/docs/tree/help.doc.mjs +2 -2
  295. package/assets/docs/tree/host-entry.doc.mjs +128 -0
  296. package/assets/docs/tree/install-once.doc.mjs +105 -0
  297. package/assets/docs/tree/layout-spacing.doc.mjs +2 -3
  298. package/assets/docs/tree/layout.doc.mjs +6 -6
  299. package/assets/docs/tree/links.doc.mjs +7 -6
  300. package/assets/docs/tree/migration-setup.doc.mjs +3 -2
  301. package/assets/docs/tree/migration-verification.doc.mjs +0 -1
  302. package/assets/docs/tree/migration.doc.mjs +0 -1
  303. package/assets/docs/tree/monorepos.doc.mjs +84 -0
  304. package/assets/docs/tree/package-and-test.doc.mjs +1 -1
  305. package/assets/docs/tree/page-template.doc.mjs +4 -8
  306. package/assets/docs/tree/problems.doc.mjs +205 -0
  307. package/assets/docs/tree/project-global-or-both.doc.mjs +131 -0
  308. package/assets/docs/tree/publishing.doc.mjs +6 -6
  309. package/assets/docs/tree/quick-start.doc.mjs +54 -28
  310. package/assets/docs/tree/reference.doc.mjs +192 -0
  311. package/assets/docs/tree/replace-a-core-component.doc.mjs +15 -7
  312. package/assets/docs/tree/replace-a-core-template.doc.mjs +10 -12
  313. package/assets/docs/tree/scaffold.doc.mjs +1 -1
  314. package/assets/docs/tree/sections-and-placement.doc.mjs +9 -11
  315. package/assets/docs/tree/see-it-in-an-app.doc.mjs +17 -5
  316. package/assets/docs/tree/ship.doc.mjs +2 -2
  317. package/assets/docs/tree/short-and-findable.doc.mjs +9 -8
  318. package/assets/docs/tree/single-component.doc.mjs +15 -6
  319. package/assets/docs/tree/start-a-template.doc.mjs +18 -8
  320. package/assets/docs/tree/subcomponent.doc.mjs +6 -6
  321. package/assets/docs/tree/template-assets.doc.mjs +2 -2
  322. package/assets/docs/tree/template-doc-overview.doc.mjs +6 -17
  323. package/assets/docs/tree/template-fonts.doc.mjs +1 -1
  324. package/assets/docs/tree/template-grading-rubric.doc.mjs +17 -38
  325. package/assets/docs/tree/template-icons.doc.mjs +1 -1
  326. package/assets/docs/tree/template-images-media.doc.mjs +1 -1
  327. package/assets/docs/tree/template-styles.doc.mjs +2 -2
  328. package/assets/docs/tree/templates.doc.mjs +7 -1
  329. package/assets/docs/tree/test-in-an-app.doc.mjs +7 -7
  330. package/assets/docs/tree/test-template-in-app.doc.mjs +5 -5
  331. package/assets/docs/tree/themes.doc.mjs +5 -1
  332. package/assets/docs/tree/tokens-and-setup.doc.mjs +19 -15
  333. package/assets/docs/tree/tokens-data.doc.mjs +2 -2
  334. package/assets/docs/tree/troubleshooting.doc.mjs +29 -13
  335. package/assets/docs/tree/type-scale.doc.mjs +3 -3
  336. package/assets/docs/tree/uninstall.doc.mjs +109 -0
  337. package/assets/docs/tree/updates.doc.mjs +92 -0
  338. package/assets/docs/tree/upgrading.doc.mjs +6 -6
  339. package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +15 -3
  340. package/assets/docs/tree/verify-packed-template.doc.mjs +3 -3
  341. package/assets/docs/tree/versioning.doc.mjs +14 -9
  342. package/assets/docs/tree/who-answers.doc.mjs +119 -0
  343. package/assets/docs/tree/write-good-templates.doc.mjs +1 -1
  344. package/assets/docs/tree/write-the-template-file.doc.mjs +7 -6
  345. package/assets/docs/use-a-theme.doc.mjs +2 -2
  346. package/assets/docs/use-integrations.doc.mjs +120 -0
  347. package/assets/docs/working-with-ai.doc.mjs +70 -13
  348. package/assets/templates/themes/butter/butterTheme.ts +4 -2
  349. package/assets/templates/themes/stone/stoneTheme.ts +4 -2
  350. package/assets/templates/themes/y2k/y2kTheme.ts +4 -2
  351. package/assets/theme.template.ts +1 -1
  352. package/authoring/codemod/codemod.doc.mjs +7 -3
  353. package/authoring/config/config.doc.mjs +9 -6
  354. package/authoring/config/type.ts +4 -2
  355. package/authoring/debug/parse.d.mts +5 -5
  356. package/authoring/discover/discover.doc.mjs +7 -1
  357. package/authoring/doctypes/_schema.d.mts +8 -3
  358. package/authoring/doctypes/_schema.mjs +1 -0
  359. package/authoring/doctypes/base/graph-fields.doc.mjs +6 -4
  360. package/authoring/doctypes/command/command.doc.mjs +20 -12
  361. package/authoring/doctypes/command/type.ts +14 -0
  362. package/authoring/doctypes/component/component.doc.mjs +30 -27
  363. package/authoring/doctypes/component/type.ts +21 -8
  364. package/authoring/doctypes/enum/enum.doc.mjs +7 -4
  365. package/authoring/doctypes/function/function.doc.mjs +9 -8
  366. package/authoring/doctypes/hook/hook.doc.mjs +5 -4
  367. package/authoring/doctypes/hook/type.ts +4 -0
  368. package/authoring/doctypes/namespace/namespace.doc.mjs +7 -6
  369. package/authoring/doctypes/reference/reference.doc.mjs +12 -11
  370. package/authoring/doctypes/schema/schema.doc.mjs +10 -10
  371. package/authoring/doctypes/template/integration-template.doc.d.mts +11 -0
  372. package/authoring/doctypes/template/integration-template.doc.mjs +49 -0
  373. package/authoring/doctypes/template/parse.d.mts +6 -2
  374. package/authoring/doctypes/template/parse.mjs +3 -1
  375. package/authoring/doctypes/template/template.doc.mjs +3 -3
  376. package/authoring/doctypes/template/type.ts +22 -0
  377. package/authoring/doctypes/theme/theme.doc.mjs +2 -2
  378. package/authoring/gap-report/gap-report.doc.mjs +2 -2
  379. package/authoring/identity/identity.doc.mjs +3 -3
  380. package/authoring/index.d.ts +2 -0
  381. package/authoring/integration/integration.doc.mjs +30 -6
  382. package/authoring/integration/parse.test.mjs +21 -0
  383. package/authoring/integration/schema.d.mts +16 -3
  384. package/authoring/integration/schema.mjs +34 -2
  385. package/authoring/integration/type.ts +13 -1
  386. package/clients/cli/bin/astryx.mjs +11 -65
  387. package/clients/cli/cli-command.test.mjs +166 -0
  388. package/clients/cli/command-result-coverage.test.mjs +6 -5
  389. package/clients/cli/commands/blog.doc.mjs +1 -0
  390. package/clients/cli/commands/build-theme.batch-report.test.mjs +17 -3
  391. package/clients/cli/commands/build-theme.color-scheme.test.mjs +1 -1
  392. package/clients/cli/commands/build-theme.font-warning.test.mjs +3 -3
  393. package/clients/cli/commands/build-theme.path-safety.test.mjs +224 -6
  394. package/clients/cli/commands/build-theme.registry.test.mjs +5 -2
  395. package/clients/cli/commands/build.doc.mjs +1 -0
  396. package/clients/cli/commands/build.exit-codes-doc.test.mjs +4 -0
  397. package/clients/cli/commands/cli-postinstall.test.mjs +49 -1
  398. package/clients/cli/commands/component.doc.mjs +1 -0
  399. package/clients/cli/commands/{component/index.mjs → component.mjs} +37 -37
  400. package/clients/cli/commands/component.test.mjs +6 -6
  401. package/clients/cli/commands/discover.broken-integration.test.mjs +11 -7
  402. package/clients/cli/commands/discover.doc.mjs +9 -1
  403. package/clients/cli/commands/discover.mjs +144 -93
  404. package/clients/cli/commands/discover.no-source.test.mjs +448 -33
  405. package/clients/cli/commands/discover.sources.test.mjs +20 -1
  406. package/clients/cli/commands/docs.doc.mjs +3 -2
  407. package/clients/cli/commands/docs.mjs +50 -20
  408. package/clients/cli/commands/docs.test.mjs +16 -16
  409. package/clients/cli/commands/doctor-integration-components.doc.mjs +19 -4
  410. package/clients/cli/commands/doctor-integration-docs.doc.mjs +20 -5
  411. package/clients/cli/commands/doctor-integration-templates.doc.mjs +1 -0
  412. package/clients/cli/commands/doctor-integration-validate.doc.mjs +22 -2
  413. package/clients/cli/commands/doctor-integration.test.mjs +936 -3
  414. package/clients/cli/commands/doctor.doc.mjs +12 -2
  415. package/clients/cli/commands/doctor.mjs +332 -7
  416. package/clients/cli/commands/doctor.test.mjs +75 -4
  417. package/clients/cli/commands/gap-report.doc.mjs +15 -2
  418. package/clients/cli/commands/gap-report.mjs +24 -2
  419. package/clients/cli/commands/gap-report.test.mjs +79 -0
  420. package/clients/cli/commands/hook.doc.mjs +1 -0
  421. package/clients/cli/commands/{hook/index.mjs → hook.mjs} +22 -22
  422. package/clients/cli/commands/import-hint-correctness.test.mjs +1 -1
  423. package/clients/cli/commands/init.behavior.test.mjs +235 -1
  424. package/clients/cli/commands/init.doc.mjs +55 -10
  425. package/clients/cli/commands/init.flag-help.test.mjs +1 -0
  426. package/clients/cli/commands/init.mjs +2 -2
  427. package/clients/cli/commands/integration-add.doc.mjs +7 -0
  428. package/clients/cli/commands/integration-authoring.test.mjs +49 -3
  429. package/clients/cli/commands/integration-check.doc.mjs +93 -0
  430. package/clients/cli/commands/integration-check.test.mjs +489 -0
  431. package/clients/cli/commands/integration-pack.doc.mjs +8 -1
  432. package/clients/cli/commands/integration-verify.doc.mjs +8 -1
  433. package/clients/cli/commands/integration.doc.mjs +6 -2
  434. package/clients/cli/commands/integration.mjs +119 -7
  435. package/clients/cli/commands/json-contract.test.mjs +11 -2
  436. package/clients/cli/commands/manifest.doc.mjs +2 -1
  437. package/clients/cli/commands/manifest.mjs +67 -0
  438. package/clients/cli/commands/outside-project.test.mjs +874 -0
  439. package/clients/cli/commands/postinstall.doc.mjs +23 -0
  440. package/clients/cli/commands/postinstall.mjs +31 -0
  441. package/clients/cli/commands/search.doc.mjs +1 -0
  442. package/clients/cli/commands/search.mjs +16 -11
  443. package/clients/cli/commands/search.test.mjs +429 -296
  444. package/clients/cli/commands/setup-nudge.test.mjs +180 -2
  445. package/clients/cli/commands/swizzle.doc.mjs +7 -0
  446. package/clients/cli/commands/swizzle.mjs +5 -3
  447. package/clients/cli/commands/swizzle.routing.test.mjs +19 -2
  448. package/clients/cli/commands/template.doc.mjs +1 -0
  449. package/clients/cli/commands/text-json-parity.test.mjs +5 -0
  450. package/clients/cli/commands/theme-add-lifecycle.test.mjs +3 -3
  451. package/clients/cli/commands/theme-add.doc.mjs +8 -1
  452. package/clients/cli/commands/theme-build.doc.mjs +3 -2
  453. package/clients/cli/commands/theme-eject.doc.mjs +1 -0
  454. package/clients/cli/commands/theme-list.behavior.test.mjs +6 -0
  455. package/clients/cli/commands/theme-list.doc.mjs +1 -0
  456. package/clients/cli/commands/theme-palette-generate.doc.mjs +1 -0
  457. package/clients/cli/commands/theme-remove.doc.mjs +7 -0
  458. package/clients/cli/commands/theme-targets.doc.mjs +1 -0
  459. package/clients/cli/commands/theme-template.doc.mjs +1 -0
  460. package/clients/cli/commands/theme-use.doc.mjs +7 -0
  461. package/clients/cli/commands/{build-theme.mjs → theme.mjs} +27 -14
  462. package/clients/cli/commands/upgrade.config-ordering.test.mjs +2 -1
  463. package/clients/cli/commands/upgrade.doc.mjs +26 -3
  464. package/clients/cli/commands/upgrade.file-protection.test.mjs +3 -2
  465. package/clients/cli/commands/upgrade.hook-output.test.mjs +2 -1
  466. package/clients/cli/commands/upgrade.mjs +9 -2
  467. package/clients/cli/commands/upgrade.older-cli.test.mjs +160 -0
  468. package/clients/cli/commands/upgrade.subfolder-path.test.mjs +97 -0
  469. package/clients/cli/index.mjs +156 -100
  470. package/clients/cli/lib/cli-error.mjs +4 -1
  471. package/clients/cli/lib/component-format.mjs +1 -1
  472. package/clients/cli/lib/component-format.test.mjs +1 -1
  473. package/clients/cli/lib/define-command.mjs +14 -9
  474. package/clients/cli/lib/exit-codes.test.mjs +5 -0
  475. package/clients/cli/lib/json-shim.mjs +3 -1
  476. package/clients/cli/lib/manifest.d.ts +9 -0
  477. package/clients/cli/lib/manifest.mjs +17 -1
  478. package/clients/cli/lib/manifest.test.mjs +37 -0
  479. package/clients/cli/lib/welcome-box.mjs +50 -0
  480. package/clients/cli/run.mjs +108 -0
  481. package/foundation/agent-docs/agent-doc-state.d.mts +67 -13
  482. package/foundation/agent-docs/agent-doc-state.mjs +123 -14
  483. package/foundation/agent-docs/agent-docs.d.mts +104 -15
  484. package/foundation/agent-docs/agent-docs.mjs +1028 -114
  485. package/foundation/agent-docs/agent-docs.path-safety.test.mjs +1239 -17
  486. package/foundation/agent-docs/agent-docs.test.mjs +683 -39
  487. package/foundation/agent-docs/computer-agent-text.d.mts +170 -0
  488. package/foundation/agent-docs/computer-agent-text.mjs +750 -0
  489. package/foundation/agent-docs/computer-agent-text.test.mjs +699 -0
  490. package/foundation/config/project.d.mts +35 -3
  491. package/foundation/config/project.mjs +243 -26
  492. package/foundation/config/project.test.mjs +122 -0
  493. package/foundation/config/theme-state.mjs +5 -4
  494. package/foundation/discovery/authoring-self-docs.d.mts +7 -0
  495. package/foundation/discovery/authoring-self-docs.mjs +17 -4
  496. package/foundation/discovery/authoring-self-docs.test.mjs +15 -3
  497. package/foundation/discovery/authoring-surface.d.mts +1 -1
  498. package/foundation/discovery/authoring-surface.mjs +2 -2
  499. package/foundation/discovery/authoring-surface.test.mjs +1 -1
  500. package/foundation/discovery/cli-self-docs.d.mts +2 -1
  501. package/foundation/discovery/cli-self-docs.mjs +22 -4
  502. package/foundation/discovery/cli-self-docs.test.mjs +3 -2
  503. package/foundation/discovery/component-discovery.d.mts +227 -10
  504. package/foundation/discovery/component-discovery.export-names.test.mjs +760 -0
  505. package/foundation/discovery/component-discovery.mjs +940 -57
  506. package/foundation/discovery/component-discovery.test.mjs +96 -0
  507. package/foundation/discovery/component-loader.d.mts +1 -1
  508. package/foundation/discovery/component-replacement.mjs +20 -1
  509. package/foundation/discovery/component-replacement.test.mjs +40 -0
  510. package/foundation/discovery/copied-passages.d.mts +216 -0
  511. package/foundation/discovery/copied-passages.mjs +720 -0
  512. package/foundation/discovery/copied-passages.test.mjs +124 -0
  513. package/foundation/discovery/doc-consistency.d.mts +34 -0
  514. package/foundation/discovery/doc-consistency.mjs +513 -0
  515. package/foundation/discovery/doc-consistency.test.mjs +347 -0
  516. package/foundation/discovery/doc-fluff.d.mts +112 -0
  517. package/foundation/discovery/doc-fluff.mjs +328 -0
  518. package/foundation/discovery/doc-fluff.test.mjs +142 -0
  519. package/foundation/discovery/doc-types.d.mts +157 -0
  520. package/foundation/discovery/doc-types.mjs +1231 -0
  521. package/foundation/discovery/doc-types.test.mjs +744 -0
  522. package/foundation/discovery/docs-discovery.d.mts +88 -0
  523. package/foundation/discovery/docs-discovery.mjs +97 -3
  524. package/foundation/discovery/integration-doc-files.d.mts +22 -0
  525. package/foundation/discovery/integration-doc-files.mjs +231 -0
  526. package/foundation/discovery/integration-doc-files.test.mjs +246 -0
  527. package/foundation/discovery/module-exports.d.mts +68 -0
  528. package/foundation/discovery/module-exports.mjs +609 -31
  529. package/foundation/discovery/module-exports.test.mjs +172 -0
  530. package/foundation/discovery/package-tsconfig.d.mts +14 -0
  531. package/foundation/discovery/package-tsconfig.mjs +228 -0
  532. package/foundation/discovery/package-tsconfig.test.mjs +210 -0
  533. package/foundation/discovery/project-typescript.d.mts +32 -0
  534. package/foundation/discovery/project-typescript.mjs +89 -0
  535. package/foundation/discovery/prop-drift.d.mts +297 -0
  536. package/foundation/discovery/prop-drift.mjs +2428 -0
  537. package/foundation/discovery/prop-drift.test.mjs +2337 -0
  538. package/foundation/discovery/runner-allowlist.d.mts +38 -0
  539. package/foundation/discovery/runner-allowlist.mjs +51 -0
  540. package/foundation/discovery/runner-commands.d.mts +77 -0
  541. package/foundation/discovery/runner-commands.mjs +294 -0
  542. package/foundation/discovery/runner-commands.test.mjs +128 -0
  543. package/foundation/discovery/stray-pointers.d.mts +52 -0
  544. package/foundation/discovery/stray-pointers.mjs +293 -0
  545. package/foundation/discovery/stray-pointers.test.mjs +86 -0
  546. package/foundation/discovery/template-adapter.mjs +92 -6
  547. package/foundation/discovery/template-needs.mjs +6 -46
  548. package/foundation/discovery/theme-discovery.mjs +147 -3
  549. package/foundation/discovery/theme-imports.mjs +8 -4
  550. package/foundation/discovery/theme-imports.test.mjs +1 -1
  551. package/foundation/discovery/type-match.d.mts +53 -0
  552. package/foundation/discovery/type-match.mjs +660 -0
  553. package/foundation/discovery/type-match.test.mjs +169 -0
  554. package/foundation/discovery/type-text.d.mts +68 -0
  555. package/foundation/discovery/type-text.mjs +658 -0
  556. package/foundation/discovery/type-text.test.mjs +196 -0
  557. package/foundation/discovery/unresolved-references.d.mts +62 -0
  558. package/foundation/discovery/unresolved-references.mjs +790 -0
  559. package/foundation/discovery/unresolved-references.test.mjs +567 -0
  560. package/foundation/doc-compiler/doc-loads.test.mjs +84 -10
  561. package/foundation/doc-compiler/inputs.mjs +27 -1
  562. package/foundation/doc-compiler/inputs.test.mjs +0 -1
  563. package/foundation/doc-compiler/links.d.mts +41 -5
  564. package/foundation/doc-compiler/links.mjs +167 -28
  565. package/foundation/doc-compiler/links.test.mjs +97 -4
  566. package/foundation/doc-compiler/read.d.mts +1 -1
  567. package/foundation/doc-compiler/read.mjs +59 -1
  568. package/foundation/doc-compiler/read.test.mjs +11 -2
  569. package/foundation/doc-compiler/split-topic-keywords.test.mjs +3 -2
  570. package/foundation/doc-compiler/tree.d.mts +32 -6
  571. package/foundation/doc-compiler/tree.mjs +107 -44
  572. package/foundation/doc-compiler/tree.test.mjs +14 -7
  573. package/foundation/env/cli-command.d.mts +83 -0
  574. package/foundation/env/cli-command.mjs +152 -0
  575. package/foundation/env/cli-command.test.mjs +129 -0
  576. package/foundation/env/node-version.test.mjs +15 -3
  577. package/foundation/env/package-manager.d.mts +93 -9
  578. package/foundation/env/package-manager.global.test.mjs +66 -0
  579. package/foundation/env/package-manager.mjs +184 -13
  580. package/foundation/env/package-manager.test.mjs +341 -15
  581. package/foundation/env/project-scope.d.mts +9 -0
  582. package/foundation/env/project-scope.mjs +33 -0
  583. package/foundation/env/search-path.d.mts +42 -0
  584. package/foundation/env/search-path.mjs +205 -0
  585. package/foundation/env/search-path.test.mjs +122 -0
  586. package/foundation/env/user-dirs.d.mts +39 -0
  587. package/foundation/env/user-dirs.mjs +93 -0
  588. package/foundation/env/user-dirs.test.mjs +72 -0
  589. package/foundation/env/user-settings.d.mts +46 -0
  590. package/foundation/env/user-settings.mjs +106 -0
  591. package/foundation/env/user-settings.test.mjs +64 -0
  592. package/foundation/fs/file-protection.mjs +15 -1
  593. package/foundation/fs/module-loader.d.mts +2 -1
  594. package/foundation/fs/module-loader.mjs +60 -4
  595. package/foundation/fs/module-loader.stdout.test.mjs +3 -2
  596. package/foundation/fs/module-loader.test.mjs +194 -0
  597. package/foundation/fs/path-compare.d.mts +33 -0
  598. package/foundation/fs/path-compare.mjs +73 -0
  599. package/foundation/fs/paths.d.mts +122 -4
  600. package/foundation/fs/paths.mjs +675 -29
  601. package/foundation/fs/paths.test.mjs +439 -54
  602. package/foundation/fs/read-record.d.mts +18 -0
  603. package/foundation/fs/read-record.mjs +131 -0
  604. package/foundation/fs/read-record.test.mjs +77 -0
  605. package/foundation/integrations/autolink.d.mts +2 -1
  606. package/foundation/integrations/autolink.mjs +4 -13
  607. package/foundation/integrations/cli-peer-version.d.mts +36 -0
  608. package/foundation/integrations/cli-peer-version.mjs +132 -0
  609. package/foundation/integrations/cli-peer-version.test.mjs +354 -0
  610. package/foundation/integrations/computer-integrations.d.mts +81 -0
  611. package/foundation/integrations/computer-integrations.mjs +345 -0
  612. package/foundation/integrations/computer-integrations.test.mjs +292 -0
  613. package/foundation/integrations/contribution-inventory.d.mts +26 -20
  614. package/foundation/integrations/contribution-inventory.mjs +97 -8
  615. package/foundation/integrations/exports-subpaths.d.mts +57 -0
  616. package/foundation/integrations/exports-subpaths.mjs +113 -0
  617. package/foundation/integrations/integration-warnings.mjs +3 -2
  618. package/foundation/integrations/integration-warnings.test.mjs +8 -9
  619. package/foundation/integrations/integrations.d.mts +51 -1
  620. package/foundation/integrations/integrations.mjs +106 -4
  621. package/foundation/integrations/issue.ts +6 -0
  622. package/foundation/integrations/public-imports.d.mts +118 -0
  623. package/foundation/integrations/public-imports.mjs +1769 -0
  624. package/foundation/integrations/public-imports.test.mjs +1793 -0
  625. package/foundation/integrations/validate-contributions.d.mts +117 -0
  626. package/foundation/integrations/validate-contributions.mjs +1209 -14
  627. package/foundation/release/agent-text-worker.d.mts +4 -0
  628. package/foundation/release/agent-text-worker.mjs +52 -0
  629. package/foundation/release/cache.d.mts +310 -0
  630. package/foundation/release/cache.mjs +1216 -0
  631. package/foundation/release/cache.test.mjs +923 -0
  632. package/foundation/release/choose.d.mts +207 -0
  633. package/foundation/release/choose.mjs +677 -0
  634. package/foundation/release/choose.test.mjs +427 -0
  635. package/foundation/release/core-worker.d.mts +4 -0
  636. package/foundation/release/core-worker.mjs +38 -0
  637. package/foundation/release/global-state.d.mts +263 -0
  638. package/foundation/release/global-state.mjs +474 -0
  639. package/foundation/release/global-state.test.mjs +381 -0
  640. package/foundation/release/handoff.d.mts +79 -0
  641. package/foundation/release/handoff.e2e.test.mjs +278 -0
  642. package/foundation/release/handoff.mjs +524 -0
  643. package/foundation/release/lock-lease.d.mts +23 -0
  644. package/foundation/release/lock-lease.mjs +80 -0
  645. package/foundation/release/notices.d.mts +307 -0
  646. package/foundation/release/notices.mjs +707 -0
  647. package/foundation/release/notices.test.mjs +586 -0
  648. package/foundation/release/npm-guard.d.mts +4 -0
  649. package/foundation/release/npm-guard.mjs +126 -0
  650. package/foundation/release/project.d.mts +141 -0
  651. package/foundation/release/project.mjs +466 -0
  652. package/foundation/release/project.test.mjs +288 -0
  653. package/foundation/release/refresh-worker.d.mts +4 -0
  654. package/foundation/release/refresh-worker.mjs +27 -0
  655. package/foundation/release/run-context.d.mts +47 -0
  656. package/foundation/release/run-context.mjs +163 -0
  657. package/foundation/release/update-notice-env.d.mts +28 -0
  658. package/foundation/release/update-notice-env.mjs +39 -0
  659. package/foundation/response/base.d.ts +26 -3
  660. package/foundation/response/error-codes.d.mts +3 -1
  661. package/foundation/response/error-codes.doc.mjs +12 -0
  662. package/foundation/response/error-codes.mjs +17 -0
  663. package/foundation/response/error-codes.test.mjs +2 -0
  664. package/foundation/response/json-contract.test.mjs +7 -2
  665. package/foundation/response/json.mjs +4 -1
  666. package/foundation/response/response-types.doc.mjs +22 -11
  667. package/foundation/response/response-types.doc.test.mjs +46 -0
  668. package/foundation/response/response.doc.mjs +3 -3
  669. package/foundation/route/route.d.mts +285 -0
  670. package/foundation/route/route.mjs +324 -0
  671. package/foundation/route/route.test.mjs +153 -0
  672. package/foundation/runtime/hosted-runtime.test.mjs +403 -0
  673. package/foundation/runtime/locked-versions.d.mts +32 -0
  674. package/foundation/runtime/locked-versions.mjs +65 -0
  675. package/foundation/runtime/package-root.d.mts +91 -0
  676. package/foundation/runtime/package-root.mjs +174 -0
  677. package/foundation/runtime/package-set.d.mts +62 -0
  678. package/foundation/runtime/package-set.mjs +130 -0
  679. package/foundation/runtime/package-set.test.mjs +432 -0
  680. package/foundation/text/string-utils.d.mts +2 -1
  681. package/foundation/text/string-utils.mjs +16 -5
  682. package/foundation/text/suggest-order.d.mts +72 -0
  683. package/foundation/text/suggest-order.mjs +363 -0
  684. package/foundation/text/suggest-order.test.mjs +259 -0
  685. package/foundation/xle/browser-imports.test.mjs +74 -0
  686. package/foundation/xle/registry.mjs +4 -0
  687. package/foundation/xle/registry.test.mjs +28 -0
  688. package/host/door-conformance.test.mjs +274 -0
  689. package/host/index.d.ts +116 -0
  690. package/host/index.mjs +295 -0
  691. package/host/index.test.mjs +302 -0
  692. package/package.json +16 -11
  693. package/scripts/postinstall.mjs +24 -5
  694. package/assets/docs/tree/integrations.doc.mjs +0 -40
@@ -21,6 +21,7 @@ import {docs} from '../docs/docs.mjs';
21
21
  import {auditCliSelfDocs} from '../../foundation/discovery/cli-self-docs.mjs';
22
22
  import {
23
23
  doctor,
24
+ checkCliCommand,
24
25
  checkAuthoringDocs,
25
26
  checkCliDocs,
26
27
  checkDocsTree,
@@ -30,7 +31,11 @@ import {
30
31
  checkIntegrationIssues,
31
32
  checkVersionAlignment,
32
33
  checkPackageManager,
34
+ checkComputerIntegrationsAvailable,
33
35
  } from './doctor.mjs';
36
+ import {installCommandFor} from './_adapter.mjs';
37
+ import {formatCliCommand} from '../../foundation/env/package-manager.mjs';
38
+ import {Project} from '../../foundation/config/project.mjs';
34
39
 
35
40
  const REPO = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../..');
36
41
  const cwd = REPO;
@@ -86,6 +91,30 @@ describe('doctor leaf', () => {
86
91
  }, SLOW);
87
92
  });
88
93
 
94
+ describe('doctor never reads the carried Core (spec AST-081 FR3)', () => {
95
+ it('loads the project to read only its installed Core, in a project with nothing installed', async () => {
96
+ const dir = mkProject({
97
+ 'package.json': JSON.stringify({
98
+ name: 'fresh-clone',
99
+ dependencies: {'@astryxdesign/core': '^0.7.0'},
100
+ }),
101
+ });
102
+ const load = vi.spyOn(Project, 'load');
103
+ const coreDir = vi.spyOn(Project.prototype, 'coreDir');
104
+ try {
105
+ await doctor({cwd: dir});
106
+ // Doctor's own project reads only the installed Core, and no Core a
107
+ // project read during the run is the carried one.
108
+ expect(load.mock.calls.some(([, options]) => options?.core === 'project')).toBe(true);
109
+ expect(coreDir).toHaveBeenCalled();
110
+ for (const result of coreDir.mock.results) expect(result.value).toBeNull();
111
+ } finally {
112
+ load.mockRestore();
113
+ coreDir.mockRestore();
114
+ }
115
+ });
116
+ });
117
+
89
118
  describe('integration issue check', () => {
90
119
  it('surfaces cross-package replacement warnings', () => {
91
120
  const check = checkIntegrationIssues({
@@ -110,7 +139,7 @@ describe('integration issue check', () => {
110
139
  describe('doctor leaf — degradation & error paths', () => {
111
140
  it('does not crash on multiple config files; reports a config FAIL', async () => {
112
141
  const dir = mkProject({
113
- 'package.json': '{"name":"x"}',
142
+ 'package.json': '{"name":"x","dependencies":{"@astryxdesign/core":"*"}}',
114
143
  'astryx.config.mjs': 'export default {};',
115
144
  'astryx.config.js': 'export default {};',
116
145
  });
@@ -123,7 +152,7 @@ describe('doctor leaf — degradation & error paths', () => {
123
152
 
124
153
  it('reports a config FAIL (not a crash) when astryx.config.mjs throws on import', async () => {
125
154
  const dir = mkProject({
126
- 'package.json': '{"name":"x"}',
155
+ 'package.json': '{"name":"x","dependencies":{"@astryxdesign/core":"*"}}',
127
156
  'astryx.config.mjs': 'throw new Error("boom");\nexport default {};',
128
157
  });
129
158
  const r = await doctor({cwd: dir});
@@ -134,7 +163,7 @@ describe('doctor leaf — degradation & error paths', () => {
134
163
 
135
164
  it('flags a non-object config default export as FAIL', async () => {
136
165
  const dir = mkProject({
137
- 'package.json': '{"name":"x"}',
166
+ 'package.json': '{"name":"x","dependencies":{"@astryxdesign/core":"*"}}',
138
167
  'astryx.config.mjs': 'export default 42;',
139
168
  });
140
169
  const r = await doctor({cwd: dir});
@@ -190,6 +219,158 @@ describe('doctor — checkVersionAlignment', () => {
190
219
  });
191
220
  });
192
221
 
222
+ describe("doctor — the project's own CLI and Core on different releases", () => {
223
+ const cli = JSON.parse(
224
+ fs.readFileSync(new URL('../../package.json', import.meta.url), 'utf8'),
225
+ ).version;
226
+ /** @param {string} core */
227
+ const at = core => {
228
+ // A project as spec:AST-040 FR9 defines it: its package.json declares
229
+ // an Astryx package.
230
+ const dir = mkProject({
231
+ 'package.json': JSON.stringify({
232
+ name: 'app',
233
+ dependencies: {'@astryxdesign/core': core},
234
+ }),
235
+ 'package-lock.json': '{}',
236
+ 'node_modules/@astryxdesign/core/package.json': JSON.stringify({
237
+ name: '@astryxdesign/core',
238
+ version: core,
239
+ }),
240
+ });
241
+ return checkVersionAlignment({
242
+ cwd: dir,
243
+ coreDir: path.join(dir, 'node_modules/@astryxdesign/core'),
244
+ nodeVersion: '',
245
+ configPath: null,
246
+ configTheme: null,
247
+ });
248
+ };
249
+
250
+ it('warns on any difference, patch included, and names the align command', () => {
251
+ const older = at('0.0.1');
252
+ expect(older.status).toBe('warn');
253
+ expect(older.message).toContain(
254
+ `This project's CLI ${cli} answers it, but its Core is 0.0.1`,
255
+ );
256
+ expect(older.fix).toBe(
257
+ `To align them, run: npm install @astryxdesign/core@${cli}, then npx astryx upgrade --from 0.0.1 --apply.`,
258
+ );
259
+ const newer = at('99.0.0');
260
+ expect(newer.fix).toBe(
261
+ 'To align them, run: npm install @astryxdesign/cli@99.0.0.',
262
+ );
263
+ expect(at(cli).status).toBe('pass');
264
+ });
265
+
266
+ it('in a project with no CLI of its own, warns that the started copy answered and names the command that adds the CLI at Core', () => {
267
+ const dir = mkProject({
268
+ 'package.json': JSON.stringify({
269
+ name: 'app',
270
+ dependencies: {'@astryxdesign/core': '0.0.1'},
271
+ }),
272
+ 'package-lock.json': '{}',
273
+ 'node_modules/@astryxdesign/core/package.json': JSON.stringify({
274
+ name: '@astryxdesign/core',
275
+ version: '0.0.1',
276
+ }),
277
+ });
278
+ const c = checkVersionAlignment({
279
+ cwd: dir,
280
+ coreDir: path.join(dir, 'node_modules/@astryxdesign/core'),
281
+ nodeVersion: '',
282
+ configPath: null,
283
+ configTheme: null,
284
+ ownCli: false,
285
+ });
286
+ expect(c.status).toBe('warn');
287
+ expect(c.message).toBe(
288
+ `This project has no Astryx CLI installed; CLI ${cli}, the one you started, answered while its Core is 0.0.1.`,
289
+ );
290
+ expect(c.fix).toBe('To match, run: npm install @astryxdesign/cli@0.0.1.');
291
+ });
292
+ });
293
+
294
+ describe('the command that adds an integration installed for the computer', () => {
295
+ const app = () =>
296
+ mkProject({
297
+ 'package.json': JSON.stringify({
298
+ name: 'app',
299
+ dependencies: {'@astryxdesign/core': '0.7.0'},
300
+ }),
301
+ 'pnpm-lock.yaml': '',
302
+ 'tools/codegen/package.json': JSON.stringify({name: 'codegen'}),
303
+ });
304
+
305
+ it('adds it in the project, with its package manager', () => {
306
+ const dir = app();
307
+ expect(installCommandFor(dir)?.('@acme/a')).toBe('pnpm add @acme/a');
308
+ });
309
+
310
+ // A package.json that is not the project's (spec AST-040 FR9): a run
311
+ // there loads integrations from it, so a package added to the project
312
+ // stays unused there. No command; the fix points at the project.
313
+ it("from a tool folder inside the project, names no command and points at the project's folder", () => {
314
+ const dir = app();
315
+ expect(installCommandFor(path.join(dir, 'tools', 'codegen'))).toBeNull();
316
+ const c = checkComputerIntegrationsAvailable({
317
+ installed: new Set(),
318
+ candidates: [/** @type {any} */ ({name: '@acme/a'})],
319
+ install: null,
320
+ elsewhere: true,
321
+ });
322
+ expect(c.fix).toBe(
323
+ "Run this from the Astryx project's folder, or declare an Astryx package in this folder's package.json to make it a project of its own.",
324
+ );
325
+ });
326
+
327
+ it('in a folder that belongs to no project, names no command', () => {
328
+ const dir = mkProject({'package.json': JSON.stringify({name: 'tool'})});
329
+ expect(installCommandFor(dir)).toBeNull();
330
+ const c = checkComputerIntegrationsAvailable({
331
+ installed: new Set(),
332
+ candidates: [/** @type {any} */ ({name: '@acme/a'})],
333
+ install: null,
334
+ });
335
+ expect(c.status).toBe('info');
336
+ expect(c).not.toHaveProperty('fix');
337
+ });
338
+ });
339
+
340
+ describe('checkCliCommand', () => {
341
+ it('is informational when no integration declares a command', () => {
342
+ const dir = mkProject({'pnpm-lock.yaml': ''});
343
+ const c = checkCliCommand({cwd: dir, integrations: [{name: '@acme/a'}]});
344
+ expect(c).toMatchObject({id: 'cli-command', status: 'info'});
345
+ });
346
+
347
+ it('passes and names the command and who declared it', () => {
348
+ const c = checkCliCommand({
349
+ cwd: mkProject({}),
350
+ integrations: [{name: '@acme/a', cliCommand: 'acme astryx'}],
351
+ });
352
+ expect(c).toMatchObject({id: 'cli-command', status: 'pass'});
353
+ expect(c.message).toContain('`acme astryx`');
354
+ expect(c.message).toContain('@acme/a');
355
+ });
356
+
357
+ it('warns when integrations declare different commands', () => {
358
+ const dir = mkProject({'pnpm-lock.yaml': ''});
359
+ const c = checkCliCommand({
360
+ cwd: dir,
361
+ integrations: [
362
+ {name: '@acme/a', cliCommand: 'acme astryx'},
363
+ {name: '@acme/b', cliCommand: 'other astryx'},
364
+ ],
365
+ });
366
+ expect(c).toMatchObject({id: 'cli-command', status: 'warn'});
367
+ expect(c.message).toContain('@acme/a (`acme astryx`)');
368
+ expect(c.message).toContain('@acme/b (`other astryx`)');
369
+ expect(c.message).toContain('pnpm dlx @astryxdesign/cli');
370
+ expect(c.fix).toContain('cliCommand');
371
+ });
372
+ });
373
+
193
374
  describe('checkPackageManager', () => {
194
375
  it('is informational when one lockfile answers', () => {
195
376
  const dir = mkProject({'pnpm-lock.yaml': ''});
@@ -353,6 +534,49 @@ describe('checkImplicitIntegrations', () => {
353
534
  expect(c.message).toContain('named in astryx.config');
354
535
  });
355
536
 
537
+ // A package with no astryx.config that loads its own manifest: there is no
538
+ // config file, so the message must not say anything is named in one.
539
+ it("names the package's own manifest instead of a config that does not exist", () => {
540
+ const c = checkImplicitIntegrations({
541
+ integrations: [autolinked({__autolinked: false, __local: true, name: '@acme/kit'})],
542
+ });
543
+ expect(c.status).toBe('info');
544
+ expect(c.message).toBe(
545
+ "None — the only loaded integration is this package's own manifest (@acme/kit).",
546
+ );
547
+ expect(c.message).not.toContain('astryx.config');
548
+ });
549
+
550
+ // An own manifest that failed to load keeps a `__local` record carrying
551
+ // `__loadError`; it loaded nothing, so it is never named as loaded.
552
+ it("does not name the package's own manifest as loaded when it failed to load", () => {
553
+ const failed = autolinked({
554
+ __autolinked: false,
555
+ __local: true,
556
+ name: '@acme/kit',
557
+ __loadError: 'astryx.integration.mjs threw: boom',
558
+ });
559
+ const alone = checkImplicitIntegrations({integrations: [failed]});
560
+ expect(alone.status).toBe('info');
561
+ expect(alone.message).toBe('None loaded.');
562
+ const beside = checkImplicitIntegrations({
563
+ integrations: [autolinked({__autolinked: false}), failed],
564
+ });
565
+ expect(beside.message).toBe('None — every loaded integration is named in astryx.config.');
566
+ });
567
+
568
+ it('names both when configured integrations load beside the own manifest', () => {
569
+ const c = checkImplicitIntegrations({
570
+ integrations: [
571
+ autolinked({__autolinked: false}),
572
+ autolinked({__autolinked: false, __local: true, name: '@acme/kit'}),
573
+ ],
574
+ });
575
+ expect(c.message).toBe(
576
+ "None — every loaded integration is named in astryx.config or is this package's own manifest (@acme/kit).",
577
+ );
578
+ });
579
+
356
580
  it('names the package, the field, and what it contributes', () => {
357
581
  // Roots count only when they exist, so this one gives them real folders.
358
582
  const root = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-implicit-'));
@@ -486,7 +710,7 @@ describe('checkAuthoringDocs', () => {
486
710
  it('passes when every authoring self-doc is reachable and fits one read', async () => {
487
711
  const c = await checkAuthoringDocs();
488
712
  expect(c).toMatchObject({id: 'authoring-docs', status: 'pass'});
489
- expect(c.message).toContain('astryx docs authoring');
713
+ expect(c.message).toContain(formatCliCommand('docs authoring/field-reference'));
490
714
  }, SLOW);
491
715
  });
492
716
 
@@ -514,7 +738,7 @@ describe('checkCliDocs', () => {
514
738
  id: 'cli-docs',
515
739
  label: 'CLI docs',
516
740
  status: 'pass',
517
- message: `All ${audit.docs} CLI docs are readable: ${audit.tree} in the \`astryx docs cli\` tree and ${audit.authoring} in \`astryx docs authoring\`.`,
741
+ message: `All ${audit.docs} CLI docs are readable: ${audit.tree} in the \`${formatCliCommand('docs cli')}\` tree and ${audit.authoring} in \`${formatCliCommand('docs authoring/field-reference')}\`.`,
518
742
  });
519
743
  },
520
744
  SLOW,
@@ -548,7 +772,7 @@ describe('checkCliDocs', () => {
548
772
  label: 'CLI docs',
549
773
  status: 'fail',
550
774
  message:
551
- '2 problems: api/alpha/alpha.doc.mjs has no namespace, so no `astryx docs` topic reads it; clients/cli/commands/beta.doc.mjs has namespace "cli", which no `astryx docs` topic reads',
775
+ `2 problems: api/alpha/alpha.doc.mjs has no namespace, so no \`${formatCliCommand('docs')}\` topic reads it; clients/cli/commands/beta.doc.mjs has namespace "cli", which no \`${formatCliCommand('docs')}\` topic reads`,
552
776
  fix: CLI_DOCS_FIX,
553
777
  });
554
778
  },
@@ -624,7 +848,7 @@ describe('checkDocsTree', () => {
624
848
  describe('checkAuthoringDocs against the public authoring surface', () => {
625
849
  const NAMESPACE = 'doctypes/namespace/namespace.doc.mjs';
626
850
  const NAMESPACE_TYPES =
627
- 'NamespaceDoc, NamespaceProviderScope, NamespaceSlotAcceptance, NamespaceSlot, NamespaceAdoptionSource, NamespaceAdoptionRule from doctypes/namespace/type.ts have no doc in `astryx docs authoring`';
851
+ `NamespaceDoc, NamespaceProviderScope, NamespaceSlotAcceptance, NamespaceSlot, NamespaceAdoptionSource, NamespaceAdoptionRule from doctypes/namespace/type.ts have no doc in \`${formatCliCommand('docs authoring/field-reference')}\``;
628
852
  const SURFACE_FIX =
629
853
  'Put a self-doc beside each module whose types @astryxdesign/cli/authoring exports and list it in AUTHORING_SELF_DOCS; export what each listed self-doc documents, or remove that self-doc.';
630
854
 
@@ -654,7 +878,7 @@ describe('checkAuthoringDocs against the public authoring surface', () => {
654
878
  id: 'authoring-docs',
655
879
  label: 'Authoring docs',
656
880
  status: 'pass',
657
- message: `All ${AUTHORING_SELF_DOCS.length} authoring schemas are readable in \`astryx docs authoring\`.`,
881
+ message: `All ${AUTHORING_SELF_DOCS.length} authoring schemas are readable in \`${formatCliCommand('docs authoring/field-reference')}\`.`,
658
882
  });
659
883
  },
660
884
  SLOW,
@@ -683,7 +907,7 @@ describe('checkAuthoringDocs against the public authoring surface', () => {
683
907
  });
684
908
  expect(c.status).toBe('fail');
685
909
  expect(c.message).toBe(
686
- `2 problems: ${NAMESPACE} is not in \`astryx docs authoring\`; ${NAMESPACE_TYPES}: ${NAMESPACE} is not listed in AUTHORING_SELF_DOCS`,
910
+ `2 problems: ${NAMESPACE} is not in \`${formatCliCommand('docs authoring/field-reference')}\`; ${NAMESPACE_TYPES}: ${NAMESPACE} is not listed in AUTHORING_SELF_DOCS`,
687
911
  );
688
912
  },
689
913
  SLOW,
@@ -692,7 +916,7 @@ describe('checkAuthoringDocs against the public authoring surface', () => {
692
916
  it(
693
917
  "fails when the topic's index no longer exposes the NamespaceDoc section",
694
918
  async () => {
695
- const index = await docs('authoring', undefined, {index: true});
919
+ const index = await docs('authoring/field-reference', undefined, {index: true});
696
920
  const topicKeys = new Set(
697
921
  index.data.sections
698
922
  .map((/** @type {any} */ s) => s.id)
@@ -781,7 +1005,7 @@ describe('checkAuthoringDocs against the public authoring surface', () => {
781
1005
  expect(c.fix).toBe(SURFACE_FIX);
782
1006
  expect(c.message).toMatch(
783
1007
  new RegExp(
784
- `^[A-Za-z, ]+ from ${escape(module)} have no doc in \`astryx docs authoring\`: no self-doc sits beside ${escape(module)}$`,
1008
+ `^[A-Za-z, ]+ from ${escape(module)} have no doc in \`${escape(formatCliCommand('docs authoring/field-reference'))}\`: no self-doc sits beside ${escape(module)}$`,
785
1009
  ),
786
1010
  );
787
1011
  for (const name of names) {
@@ -824,7 +1048,7 @@ describe('checkAuthoringDocs against the public authoring surface', () => {
824
1048
  expect(await checkAuthoringDocs()).toMatchObject({
825
1049
  status: 'fail',
826
1050
  message:
827
- '`astryx docs authoring` could not be read: no built-in docs',
1051
+ `\`${formatCliCommand('docs authoring/field-reference')}\` could not be read: no built-in docs`,
828
1052
  fix: SURFACE_FIX,
829
1053
  });
830
1054
  } finally {
@@ -845,7 +1069,7 @@ describe('checkAuthoringDocs against the public authoring surface', () => {
845
1069
  const c = await checkAuthoringDocs(undefined, {root});
846
1070
  expect(c.status).toBe('fail');
847
1071
  expect(c.message).toContain(
848
- '@astryxdesign/cli/authoring exports no types, so nothing was compared with `astryx docs authoring`',
1072
+ `@astryxdesign/cli/authoring exports no types, so nothing was compared with \`${formatCliCommand('docs authoring/field-reference')}\``,
849
1073
  );
850
1074
  },
851
1075
  SLOW,
@@ -1055,7 +1279,7 @@ describe('doctor says why it skipped and what it checked', () => {
1055
1279
  expect(by.config.message).toMatch(
1056
1280
  /^astryx\.config\.mjs loads, but the CLI could not load the project from it: .*integrations/,
1057
1281
  );
1058
- expect(by.config.fix).toContain('astryx docs authoring config');
1282
+ expect(by.config.fix).toContain(' docs authoring config`');
1059
1283
  for (const id of ['implicit-integrations', 'provider-identity']) {
1060
1284
  expect(by[id].message).toMatch(
1061
1285
  /^Skipped — the project configuration could not be read: .*integrations/,
@@ -1151,8 +1375,12 @@ describe('a configured integration that cannot load fails the gate', () => {
1151
1375
  'astryx.config.mjs': "export default { integrations: ['@acme/widgets'] };\n",
1152
1376
  };
1153
1377
 
1154
- /** Run doctor on a project under the working directory. */
1155
- async function report(files) {
1378
+ /**
1379
+ * Run doctor on a project under the working directory, in its folder `at`.
1380
+ * @param {Record<string, string>} files
1381
+ * @param {string} [at]
1382
+ */
1383
+ async function report(files, at = '.') {
1156
1384
  const dir = fs.mkdtempSync(path.join(process.cwd(), '.astryx-doctor-gate-'));
1157
1385
  tmpDirs.push(dir);
1158
1386
  for (const [rel, content] of Object.entries(files)) {
@@ -1160,7 +1388,7 @@ describe('a configured integration that cannot load fails the gate', () => {
1160
1388
  fs.mkdirSync(path.dirname(abs), {recursive: true});
1161
1389
  fs.writeFileSync(abs, content);
1162
1390
  }
1163
- const {checks, summary} = (await doctor({cwd: dir})).data;
1391
+ const {checks, summary} = (await doctor({cwd: path.join(dir, at)})).data;
1164
1392
  return {by: Object.fromEntries(checks.map(c => [c.id, c])), summary};
1165
1393
  }
1166
1394
 
@@ -1182,12 +1410,33 @@ describe('a configured integration that cannot load fails the gate', () => {
1182
1410
  /^1 of 1 integration named in astryx\.config could not be loaded, so it contributes nothing: @acme\/widgets \(/,
1183
1411
  );
1184
1412
  expect(check.message).toMatch(reason);
1185
- expect(check.fix).toContain('astryx doctor integration validate <package>');
1413
+ expect(check.fix).toContain(' doctor integration validate <package>`');
1186
1414
  expect(summary.fail).toBe(1);
1187
1415
  },
1188
1416
  SLOW,
1189
1417
  );
1190
1418
 
1419
+ it(
1420
+ 'passes in a workspace member whose configured integration is hoisted to the root',
1421
+ async () => {
1422
+ const {by} = await report(
1423
+ {
1424
+ 'package.json': JSON.stringify({name: 'root', private: true, workspaces: ['apps/*']}),
1425
+ ...CORE_STUB,
1426
+ ...widgets(),
1427
+ 'apps/web/package.json': '{"name":"web"}',
1428
+ 'apps/web/astryx.config.mjs': "export default { integrations: ['@acme/widgets'] };\n",
1429
+ },
1430
+ 'apps/web',
1431
+ );
1432
+ expect(by['configured-integrations'].status).toBe('pass');
1433
+ expect(by['configured-integrations'].message).toBe(
1434
+ '1 integration named in astryx.config loaded: @acme/widgets.',
1435
+ );
1436
+ },
1437
+ SLOW,
1438
+ );
1439
+
1191
1440
  it(
1192
1441
  'counts the package being authored when its own config names it',
1193
1442
  async () => {
@@ -0,0 +1,156 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file doctor's release checks (spec AST-081 FR10, FR11): the newest
5
+ * release beside a project's or the global copy's, with the fix, and the
6
+ * `astryx` commands on the search path.
7
+ */
8
+
9
+ import {describe, expect, it} from 'vitest';
10
+ import {checkCliOnPath, checkNewestRelease} from './doctor.mjs';
11
+
12
+ const answeredBy = {cli: '0.7.1', core: '0.7.1', copy: 'project'};
13
+
14
+ /** @param {Partial<import('../../foundation/env/search-path.mjs').SearchPathCommand>} row */
15
+ const row = row => ({
16
+ file: '/bin/astryx',
17
+ astryx: true,
18
+ cliRoot: '/lib/cli',
19
+ version: '0.8.0',
20
+ handsOff: true,
21
+ ...row,
22
+ });
23
+
24
+ describe('checkNewestRelease', () => {
25
+ it("names a project's install and upgrade commands", () => {
26
+ const check = checkNewestRelease({
27
+ nodeVersion: '24.0.0',
28
+ answeredBy,
29
+ latest: '0.7.3',
30
+ projectLag: {
31
+ root: '/app',
32
+ current: '0.7.1',
33
+ newest: '0.7.3',
34
+ install: 'npm install @astryxdesign/core@0.7.3 @astryxdesign/cli@0.7.3',
35
+ upgrade: 'npx astryx upgrade --from 0.7.1 --apply',
36
+ },
37
+ });
38
+ expect(check).toEqual({
39
+ id: 'newest-release',
40
+ label: 'Newest release',
41
+ status: 'info',
42
+ message: 'This project uses Astryx 0.7.1; 0.7.3 is the newest.',
43
+ fix: 'To upgrade, run: npm install @astryxdesign/core@0.7.3 @astryxdesign/cli@0.7.3, then npx astryx upgrade --from 0.7.1 --apply.',
44
+ });
45
+ });
46
+
47
+ it('adds the last notice skip and a held state lock, for diagnosis', () => {
48
+ const check = checkNewestRelease({
49
+ nodeVersion: '24.0.0',
50
+ answeredBy,
51
+ latest: '0.7.1',
52
+ lastNoticeSkip: {
53
+ key: 'global',
54
+ reason: 'already-shown',
55
+ at: Date.UTC(2026, 9, 20, 18),
56
+ },
57
+ stateLock: {pid: 42, host: 'mac', since: Date.UTC(2026, 9, 20, 17)},
58
+ });
59
+ expect(check.message).toBe(
60
+ 'No newer release is known: the newest is 0.7.1. ' +
61
+ 'Last update notice skipped 2026-10-20T18:00:00.000Z (global): it had already printed that day. ' +
62
+ "The state file's lock is held by process 42 on mac since 2026-10-20T17:00:00.000Z.",
63
+ );
64
+ });
65
+
66
+ it('says when the last update check did not reach the registry, and why', () => {
67
+ const check = checkNewestRelease({
68
+ nodeVersion: '24.0.0',
69
+ answeredBy,
70
+ latest: null,
71
+ lastCheckFailure: {
72
+ at: Date.UTC(2026, 9, 20, 18),
73
+ reason: 'npm-failed: ECONNREFUSED',
74
+ },
75
+ });
76
+ expect(check.message).toContain(
77
+ 'The last update check, 2026-10-20T18:00:00.000Z, did not reach the registry: npm failed (ECONNREFUSED); its registry and proxy settings decide where it asks.',
78
+ );
79
+ expect(
80
+ checkNewestRelease({
81
+ nodeVersion: '24.0.0',
82
+ answeredBy,
83
+ latest: null,
84
+ lastCheckFailure: {at: 0, reason: 'npm-missing'},
85
+ }).message,
86
+ ).toContain('did not reach the registry: npm could not be started.');
87
+ });
88
+
89
+ it("names the global copy's upgrade, after the Node the newest release needs", () => {
90
+ const check = checkNewestRelease({
91
+ nodeVersion: '22.13.0',
92
+ answeredBy: {...answeredBy, copy: 'carried'},
93
+ latest: '0.8.0',
94
+ globalBehind: {
95
+ current: '0.7.1',
96
+ newest: '0.8.0',
97
+ node: '>=99',
98
+ command: 'pnpm add -g @astryxdesign/cli@latest',
99
+ },
100
+ });
101
+ expect(check).toEqual({
102
+ id: 'newest-release',
103
+ label: 'Newest release',
104
+ status: 'info',
105
+ message:
106
+ 'A new release of Astryx is available: 0.7.1 → 0.8.0. The CLI installed for the computer stays on its release until you upgrade it.',
107
+ fix: `To upgrade, install Node >=99 (this computer runs Node ${process.versions.node}), then run: pnpm add -g @astryxdesign/cli@latest.`,
108
+ });
109
+ });
110
+ });
111
+
112
+ describe('checkCliOnPath', () => {
113
+ const base = {nodeVersion: '24.0.0', answeredBy, latest: null};
114
+
115
+ it('passes for a release that hands off, and lists the later ones', () => {
116
+ const check = checkCliOnPath({
117
+ ...base,
118
+ searchPathCommands: [
119
+ row({}),
120
+ row({file: '/other/astryx', version: '0.7.1', handsOff: false}),
121
+ ],
122
+ });
123
+ expect(check).toMatchObject({
124
+ status: 'pass',
125
+ message:
126
+ '`astryx` runs /bin/astryx (CLI 0.8.0). Later on the search path: /other/astryx (CLI 0.7.1).',
127
+ });
128
+ });
129
+
130
+ it('warns about a first release that predates the hand-off, naming the replacement', () => {
131
+ expect(
132
+ checkCliOnPath({
133
+ ...base,
134
+ searchPathCommands: [row({version: '0.7.1', handsOff: false})],
135
+ }),
136
+ ).toMatchObject({
137
+ status: 'warn',
138
+ fix: 'To upgrade, run: `npm i -g @astryxdesign/cli@latest`.',
139
+ });
140
+ });
141
+
142
+ it('warns about another program first, and informs when there is none', () => {
143
+ expect(
144
+ checkCliOnPath({
145
+ ...base,
146
+ searchPathCommands: [
147
+ row({astryx: false, cliRoot: null, version: null, handsOff: null}),
148
+ ],
149
+ }),
150
+ ).toMatchObject({status: 'warn'});
151
+ expect(checkCliOnPath({...base, searchPathCommands: []})).toMatchObject({
152
+ status: 'info',
153
+ fix: 'To run the CLI in any folder, install it for the computer: `npm i -g @astryxdesign/cli`.',
154
+ });
155
+ });
156
+ });
@@ -32,7 +32,10 @@ import {
32
32
  themeRecordOwner,
33
33
  validateThemePrivateInputs,
34
34
  } from './_adapter.mjs';
35
- import {getCliInvocation} from '../../foundation/env/package-manager.mjs';
35
+ import {
36
+ getCliInvocation,
37
+ formatCliCommand,
38
+ } from '../../foundation/env/package-manager.mjs';
36
39
  import {satisfiesRange} from '../../foundation/env/semver.mjs';
37
40
  import {logger} from '../logger.mjs';
38
41
 
@@ -69,7 +72,7 @@ function unavailableChecks(message, moduleError = null) {
69
72
  moduleError && id === 'theme-module' ? moduleError.message : message,
70
73
  ...(moduleError && id === 'theme-module'
71
74
  ? {
72
- fix: 'Keep one CLI-generated astryx-themes.ts or astryx-themes.js file, then run `astryx theme add <slug>`.',
75
+ fix: `Keep one CLI-generated astryx-themes.ts or astryx-themes.js file, then run \`${formatCliCommand('theme add <slug>')}\`.`,
73
76
  }
74
77
  : {}),
75
78
  }));
@@ -736,7 +739,7 @@ export async function checkAppThemes(cwd) {
736
739
  label: 'App imports the theme module',
737
740
  status: 'warn',
738
741
  message: `Could not prove that project source imports ${state.module.path}${importEvidence.parseFailures ? `; ${importEvidence.parseFailures} source file(s) could not be parsed` : ''}.`,
739
- fix: `Import {themes, defaultThemeSlug} from the generated module and {Theme} from '@astryxdesign/core', then render <Theme theme={themes[defaultThemeSlug]}>.`,
742
+ fix: `Import {themes, defaultThemeSlug} from the generated module and {Theme} from '@astryxdesign/core/theme', then render <Theme theme={themes[defaultThemeSlug]}>.`,
740
743
  });
741
744
  }
742
745
 
@@ -794,6 +797,16 @@ export async function checkAppThemes(cwd) {
794
797
  message:
795
798
  'Could not check local build freshness until every local output resolves.',
796
799
  });
800
+ } else if (localThemes.length > 0 && !findDoctorCoreDir(state.projectDir)) {
801
+ // A build reads Core, and doctor reads only the Core the project
802
+ // installed, never the one the answering copy carries (spec AST-081 FR3).
803
+ checks.push({
804
+ id: 'theme-local-builds',
805
+ label: 'Local theme build freshness',
806
+ status: 'info',
807
+ message:
808
+ "The project's Core isn't installed, so local theme builds were not checked. Install the project's dependencies, then run doctor again.",
809
+ });
797
810
  } else if (localThemes.length === 0) {
798
811
  checks.push({
799
812
  id: 'theme-local-builds',
@@ -817,7 +830,7 @@ export async function checkAppThemes(cwd) {
817
830
  const receipt = await themeBuild(
818
831
  path.join(availableTheme.sourceDir, availableTheme.entry),
819
832
  {check: true},
820
- {cwd: state.projectDir},
833
+ {cwd: state.projectDir, projectCore: true},
821
834
  );
822
835
  if (
823
836
  receipt == null ||