@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
@@ -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,
@@ -31,6 +32,8 @@ import {
31
32
  checkVersionAlignment,
32
33
  checkPackageManager,
33
34
  } from './doctor.mjs';
35
+ import {formatCliCommand} from '../../foundation/env/package-manager.mjs';
36
+ import {Project} from '../../foundation/config/project.mjs';
34
37
 
35
38
  const REPO = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../..');
36
39
  const cwd = REPO;
@@ -86,6 +89,30 @@ describe('doctor leaf', () => {
86
89
  }, SLOW);
87
90
  });
88
91
 
92
+ describe('doctor never reads the carried Core (spec AST-081 FR3)', () => {
93
+ it('loads the project to read only its installed Core, in a project with nothing installed', async () => {
94
+ const dir = mkProject({
95
+ 'package.json': JSON.stringify({
96
+ name: 'fresh-clone',
97
+ dependencies: {'@astryxdesign/core': '^0.7.0'},
98
+ }),
99
+ });
100
+ const load = vi.spyOn(Project, 'load');
101
+ const coreDir = vi.spyOn(Project.prototype, 'coreDir');
102
+ try {
103
+ await doctor({cwd: dir});
104
+ // Doctor's own project reads only the installed Core, and no Core a
105
+ // project read during the run is the carried one.
106
+ expect(load.mock.calls.some(([, options]) => options?.core === 'project')).toBe(true);
107
+ expect(coreDir).toHaveBeenCalled();
108
+ for (const result of coreDir.mock.results) expect(result.value).toBeNull();
109
+ } finally {
110
+ load.mockRestore();
111
+ coreDir.mockRestore();
112
+ }
113
+ });
114
+ });
115
+
89
116
  describe('integration issue check', () => {
90
117
  it('surfaces cross-package replacement warnings', () => {
91
118
  const check = checkIntegrationIssues({
@@ -110,7 +137,7 @@ describe('integration issue check', () => {
110
137
  describe('doctor leaf — degradation & error paths', () => {
111
138
  it('does not crash on multiple config files; reports a config FAIL', async () => {
112
139
  const dir = mkProject({
113
- 'package.json': '{"name":"x"}',
140
+ 'package.json': '{"name":"x","dependencies":{"@astryxdesign/core":"*"}}',
114
141
  'astryx.config.mjs': 'export default {};',
115
142
  'astryx.config.js': 'export default {};',
116
143
  });
@@ -123,7 +150,7 @@ describe('doctor leaf — degradation & error paths', () => {
123
150
 
124
151
  it('reports a config FAIL (not a crash) when astryx.config.mjs throws on import', async () => {
125
152
  const dir = mkProject({
126
- 'package.json': '{"name":"x"}',
153
+ 'package.json': '{"name":"x","dependencies":{"@astryxdesign/core":"*"}}',
127
154
  'astryx.config.mjs': 'throw new Error("boom");\nexport default {};',
128
155
  });
129
156
  const r = await doctor({cwd: dir});
@@ -134,7 +161,7 @@ describe('doctor leaf — degradation & error paths', () => {
134
161
 
135
162
  it('flags a non-object config default export as FAIL', async () => {
136
163
  const dir = mkProject({
137
- 'package.json': '{"name":"x"}',
164
+ 'package.json': '{"name":"x","dependencies":{"@astryxdesign/core":"*"}}',
138
165
  'astryx.config.mjs': 'export default 42;',
139
166
  });
140
167
  const r = await doctor({cwd: dir});
@@ -190,6 +217,102 @@ describe('doctor — checkVersionAlignment', () => {
190
217
  });
191
218
  });
192
219
 
220
+ describe("doctor — the project's own CLI and Core on different releases", () => {
221
+ const cli = JSON.parse(
222
+ fs.readFileSync(new URL('../../package.json', import.meta.url), 'utf8'),
223
+ ).version;
224
+ /** @param {string} core */
225
+ const at = core => {
226
+ const dir = mkProject({
227
+ 'package-lock.json': '{}',
228
+ 'node_modules/@astryxdesign/core/package.json': JSON.stringify({
229
+ name: '@astryxdesign/core',
230
+ version: core,
231
+ }),
232
+ });
233
+ return checkVersionAlignment({
234
+ cwd: dir,
235
+ coreDir: path.join(dir, 'node_modules/@astryxdesign/core'),
236
+ nodeVersion: '',
237
+ configPath: null,
238
+ configTheme: null,
239
+ });
240
+ };
241
+
242
+ it('warns on any difference, patch included, and names the align command', () => {
243
+ const older = at('0.0.1');
244
+ expect(older.status).toBe('warn');
245
+ expect(older.message).toContain(
246
+ `This project's CLI ${cli} answers it, but its Core is 0.0.1`,
247
+ );
248
+ expect(older.fix).toBe(
249
+ `To align them, run: npm install @astryxdesign/core@${cli}, then npx astryx upgrade --from 0.0.1 --apply.`,
250
+ );
251
+ const newer = at('99.0.0');
252
+ expect(newer.fix).toBe(
253
+ 'To align them, run: npm install @astryxdesign/cli@99.0.0.',
254
+ );
255
+ expect(at(cli).status).toBe('pass');
256
+ });
257
+
258
+ 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', () => {
259
+ const dir = mkProject({
260
+ 'package-lock.json': '{}',
261
+ 'node_modules/@astryxdesign/core/package.json': JSON.stringify({
262
+ name: '@astryxdesign/core',
263
+ version: '0.0.1',
264
+ }),
265
+ });
266
+ const c = checkVersionAlignment({
267
+ cwd: dir,
268
+ coreDir: path.join(dir, 'node_modules/@astryxdesign/core'),
269
+ nodeVersion: '',
270
+ configPath: null,
271
+ configTheme: null,
272
+ ownCli: false,
273
+ });
274
+ expect(c.status).toBe('warn');
275
+ expect(c.message).toBe(
276
+ `This project has no Astryx CLI installed; CLI ${cli}, the one you started, answered while its Core is 0.0.1.`,
277
+ );
278
+ expect(c.fix).toBe('To match, run: npm install @astryxdesign/cli@0.0.1.');
279
+ });
280
+ });
281
+
282
+ describe('checkCliCommand', () => {
283
+ it('is informational when no integration declares a command', () => {
284
+ const dir = mkProject({'pnpm-lock.yaml': ''});
285
+ const c = checkCliCommand({cwd: dir, integrations: [{name: '@acme/a'}]});
286
+ expect(c).toMatchObject({id: 'cli-command', status: 'info'});
287
+ });
288
+
289
+ it('passes and names the command and who declared it', () => {
290
+ const c = checkCliCommand({
291
+ cwd: mkProject({}),
292
+ integrations: [{name: '@acme/a', cliCommand: 'acme astryx'}],
293
+ });
294
+ expect(c).toMatchObject({id: 'cli-command', status: 'pass'});
295
+ expect(c.message).toContain('`acme astryx`');
296
+ expect(c.message).toContain('@acme/a');
297
+ });
298
+
299
+ it('warns when integrations declare different commands', () => {
300
+ const dir = mkProject({'pnpm-lock.yaml': ''});
301
+ const c = checkCliCommand({
302
+ cwd: dir,
303
+ integrations: [
304
+ {name: '@acme/a', cliCommand: 'acme astryx'},
305
+ {name: '@acme/b', cliCommand: 'other astryx'},
306
+ ],
307
+ });
308
+ expect(c).toMatchObject({id: 'cli-command', status: 'warn'});
309
+ expect(c.message).toContain('@acme/a (`acme astryx`)');
310
+ expect(c.message).toContain('@acme/b (`other astryx`)');
311
+ expect(c.message).toContain('pnpm dlx @astryxdesign/cli');
312
+ expect(c.fix).toContain('cliCommand');
313
+ });
314
+ });
315
+
193
316
  describe('checkPackageManager', () => {
194
317
  it('is informational when one lockfile answers', () => {
195
318
  const dir = mkProject({'pnpm-lock.yaml': ''});
@@ -353,6 +476,49 @@ describe('checkImplicitIntegrations', () => {
353
476
  expect(c.message).toContain('named in astryx.config');
354
477
  });
355
478
 
479
+ // A package with no astryx.config that loads its own manifest: there is no
480
+ // config file, so the message must not say anything is named in one.
481
+ it("names the package's own manifest instead of a config that does not exist", () => {
482
+ const c = checkImplicitIntegrations({
483
+ integrations: [autolinked({__autolinked: false, __local: true, name: '@acme/kit'})],
484
+ });
485
+ expect(c.status).toBe('info');
486
+ expect(c.message).toBe(
487
+ "None — the only loaded integration is this package's own manifest (@acme/kit).",
488
+ );
489
+ expect(c.message).not.toContain('astryx.config');
490
+ });
491
+
492
+ // An own manifest that failed to load keeps a `__local` record carrying
493
+ // `__loadError`; it loaded nothing, so it is never named as loaded.
494
+ it("does not name the package's own manifest as loaded when it failed to load", () => {
495
+ const failed = autolinked({
496
+ __autolinked: false,
497
+ __local: true,
498
+ name: '@acme/kit',
499
+ __loadError: 'astryx.integration.mjs threw: boom',
500
+ });
501
+ const alone = checkImplicitIntegrations({integrations: [failed]});
502
+ expect(alone.status).toBe('info');
503
+ expect(alone.message).toBe('None loaded.');
504
+ const beside = checkImplicitIntegrations({
505
+ integrations: [autolinked({__autolinked: false}), failed],
506
+ });
507
+ expect(beside.message).toBe('None — every loaded integration is named in astryx.config.');
508
+ });
509
+
510
+ it('names both when configured integrations load beside the own manifest', () => {
511
+ const c = checkImplicitIntegrations({
512
+ integrations: [
513
+ autolinked({__autolinked: false}),
514
+ autolinked({__autolinked: false, __local: true, name: '@acme/kit'}),
515
+ ],
516
+ });
517
+ expect(c.message).toBe(
518
+ "None — every loaded integration is named in astryx.config or is this package's own manifest (@acme/kit).",
519
+ );
520
+ });
521
+
356
522
  it('names the package, the field, and what it contributes', () => {
357
523
  // Roots count only when they exist, so this one gives them real folders.
358
524
  const root = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-implicit-'));
@@ -486,7 +652,7 @@ describe('checkAuthoringDocs', () => {
486
652
  it('passes when every authoring self-doc is reachable and fits one read', async () => {
487
653
  const c = await checkAuthoringDocs();
488
654
  expect(c).toMatchObject({id: 'authoring-docs', status: 'pass'});
489
- expect(c.message).toContain('astryx docs authoring');
655
+ expect(c.message).toContain(formatCliCommand('docs authoring'));
490
656
  }, SLOW);
491
657
  });
492
658
 
@@ -514,7 +680,7 @@ describe('checkCliDocs', () => {
514
680
  id: 'cli-docs',
515
681
  label: 'CLI docs',
516
682
  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\`.`,
683
+ message: `All ${audit.docs} CLI docs are readable: ${audit.tree} in the \`${formatCliCommand('docs cli')}\` tree and ${audit.authoring} in \`${formatCliCommand('docs authoring')}\`.`,
518
684
  });
519
685
  },
520
686
  SLOW,
@@ -548,7 +714,7 @@ describe('checkCliDocs', () => {
548
714
  label: 'CLI docs',
549
715
  status: 'fail',
550
716
  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',
717
+ `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
718
  fix: CLI_DOCS_FIX,
553
719
  });
554
720
  },
@@ -624,7 +790,7 @@ describe('checkDocsTree', () => {
624
790
  describe('checkAuthoringDocs against the public authoring surface', () => {
625
791
  const NAMESPACE = 'doctypes/namespace/namespace.doc.mjs';
626
792
  const NAMESPACE_TYPES =
627
- 'NamespaceDoc, NamespaceProviderScope, NamespaceSlotAcceptance, NamespaceSlot, NamespaceAdoptionSource, NamespaceAdoptionRule from doctypes/namespace/type.ts have no doc in `astryx docs authoring`';
793
+ `NamespaceDoc, NamespaceProviderScope, NamespaceSlotAcceptance, NamespaceSlot, NamespaceAdoptionSource, NamespaceAdoptionRule from doctypes/namespace/type.ts have no doc in \`${formatCliCommand('docs authoring')}\``;
628
794
  const SURFACE_FIX =
629
795
  '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
796
 
@@ -654,7 +820,7 @@ describe('checkAuthoringDocs against the public authoring surface', () => {
654
820
  id: 'authoring-docs',
655
821
  label: 'Authoring docs',
656
822
  status: 'pass',
657
- message: `All ${AUTHORING_SELF_DOCS.length} authoring schemas are readable in \`astryx docs authoring\`.`,
823
+ message: `All ${AUTHORING_SELF_DOCS.length} authoring schemas are readable in \`${formatCliCommand('docs authoring')}\`.`,
658
824
  });
659
825
  },
660
826
  SLOW,
@@ -683,7 +849,7 @@ describe('checkAuthoringDocs against the public authoring surface', () => {
683
849
  });
684
850
  expect(c.status).toBe('fail');
685
851
  expect(c.message).toBe(
686
- `2 problems: ${NAMESPACE} is not in \`astryx docs authoring\`; ${NAMESPACE_TYPES}: ${NAMESPACE} is not listed in AUTHORING_SELF_DOCS`,
852
+ `2 problems: ${NAMESPACE} is not in \`${formatCliCommand('docs authoring')}\`; ${NAMESPACE_TYPES}: ${NAMESPACE} is not listed in AUTHORING_SELF_DOCS`,
687
853
  );
688
854
  },
689
855
  SLOW,
@@ -781,7 +947,7 @@ describe('checkAuthoringDocs against the public authoring surface', () => {
781
947
  expect(c.fix).toBe(SURFACE_FIX);
782
948
  expect(c.message).toMatch(
783
949
  new RegExp(
784
- `^[A-Za-z, ]+ from ${escape(module)} have no doc in \`astryx docs authoring\`: no self-doc sits beside ${escape(module)}$`,
950
+ `^[A-Za-z, ]+ from ${escape(module)} have no doc in \`${escape(formatCliCommand('docs authoring'))}\`: no self-doc sits beside ${escape(module)}$`,
785
951
  ),
786
952
  );
787
953
  for (const name of names) {
@@ -824,7 +990,7 @@ describe('checkAuthoringDocs against the public authoring surface', () => {
824
990
  expect(await checkAuthoringDocs()).toMatchObject({
825
991
  status: 'fail',
826
992
  message:
827
- '`astryx docs authoring` could not be read: no built-in docs',
993
+ `\`${formatCliCommand('docs authoring')}\` could not be read: no built-in docs`,
828
994
  fix: SURFACE_FIX,
829
995
  });
830
996
  } finally {
@@ -845,7 +1011,7 @@ describe('checkAuthoringDocs against the public authoring surface', () => {
845
1011
  const c = await checkAuthoringDocs(undefined, {root});
846
1012
  expect(c.status).toBe('fail');
847
1013
  expect(c.message).toContain(
848
- '@astryxdesign/cli/authoring exports no types, so nothing was compared with `astryx docs authoring`',
1014
+ `@astryxdesign/cli/authoring exports no types, so nothing was compared with \`${formatCliCommand('docs authoring')}\``,
849
1015
  );
850
1016
  },
851
1017
  SLOW,
@@ -1055,7 +1221,7 @@ describe('doctor says why it skipped and what it checked', () => {
1055
1221
  expect(by.config.message).toMatch(
1056
1222
  /^astryx\.config\.mjs loads, but the CLI could not load the project from it: .*integrations/,
1057
1223
  );
1058
- expect(by.config.fix).toContain('astryx docs authoring config');
1224
+ expect(by.config.fix).toContain(' docs authoring config`');
1059
1225
  for (const id of ['implicit-integrations', 'provider-identity']) {
1060
1226
  expect(by[id].message).toMatch(
1061
1227
  /^Skipped — the project configuration could not be read: .*integrations/,
@@ -1182,7 +1348,7 @@ describe('a configured integration that cannot load fails the gate', () => {
1182
1348
  /^1 of 1 integration named in astryx\.config could not be loaded, so it contributes nothing: @acme\/widgets \(/,
1183
1349
  );
1184
1350
  expect(check.message).toMatch(reason);
1185
- expect(check.fix).toContain('astryx doctor integration validate <package>');
1351
+ expect(check.fix).toContain(' doctor integration validate <package>`');
1186
1352
  expect(summary.fail).toBe(1);
1187
1353
  },
1188
1354
  SLOW,
@@ -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
  }));
@@ -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 ||