@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
@@ -4,7 +4,11 @@
4
4
  * @file Health-check engine for `astryx doctor`.
5
5
  *
6
6
  * Runs a series of diagnostic checks against the user's project and
7
- * environment, returning a structured report. Each check is a small,
7
+ * environment, returning a structured report. Outside a project it checks the
8
+ * computer instead (spec AST-081 FR4): the Node version, the release that
9
+ * answered, the newest release the computer knows of, and whether the
10
+ * computer-level agent text is present and current; no check fails because
11
+ * the folder has no project. Each check is a small,
8
12
  * self-contained function that returns a {@link DoctorCheck} record, so
9
13
  * adding a new diagnostic is just appending a function to {@link SYNC_CHECKS}.
10
14
  *
@@ -25,8 +29,9 @@ import * as path from 'node:path';
25
29
 
26
30
  import {MIN_NODE_VERSION, isNodeVersionSupported} from '../../foundation/env/node-version.mjs';
27
31
  import {AGENT_DOC_PATHS} from '../../foundation/agent-docs/agent-doc-state.mjs';
28
- import {CLI_ROOT, findCoreDir, findInstalledPackage} from '../../foundation/fs/paths.mjs';
29
- import {explainPackageManager, getCliInvocation} from '../../foundation/env/package-manager.mjs';
32
+ import {CLI_ROOT, findProjectCoreDir, findInstalledPackage} from '../../foundation/fs/paths.mjs';
33
+ import {explainPackageManager, formatCliCommand, getCliInvocation, guessCliInvocation} from '../../foundation/env/package-manager.mjs';
34
+ import {chooseDeclaredCliCommand, declaredCliCommandsOf} from '../../foundation/env/cli-command.mjs';
30
35
  import {
31
36
  findConfigPath,
32
37
  Project,
@@ -52,6 +57,8 @@ import {typedEdges} from '../docs/node/node.mjs';
52
57
  import {detailView, indexView} from '../../foundation/doc-compiler/lenses.mjs';
53
58
  import {semverCompare, isValidSemver, satisfiesRange} from '../../foundation/env/semver.mjs';
54
59
  import {checkAppThemes, checkThemes} from './theme-checks.mjs';
60
+ import {availableLine, upgradeLine} from '../../foundation/release/notices.mjs';
61
+ import {alignCommand, addCliCommandFor, computerAgentTextState, installCommandFor, projectCoreVersion, projectHasOwnCli, computerIntegrationsOutside, computerIntegrationsToAdd, displayHomePath, outsideProject, releaseFacts} from './_adapter.mjs';
55
62
 
56
63
  /**
57
64
  * @typedef {'pass'|'warn'|'fail'|'info'} DoctorStatus
@@ -71,6 +78,10 @@ import {checkAppThemes, checkThemes} from './theme-checks.mjs';
71
78
  * @property {string} cwd - Directory to diagnose.
72
79
  * @property {string} nodeVersion - Running Node version.
73
80
  * @property {string|null} coreDir - Resolved core package directory, or null.
81
+ * @property {boolean} [ownCli] - Whether the project installs its own CLI;
82
+ * false means the copy that started the run answered it (spec AST-081 FR2).
83
+ * @property {string|null} [projectCore] - The Core release the project names
84
+ * (installed, else locked, else declared), as the daily hint reads it.
74
85
  * @property {string|null} configPath - Resolved astryx.config.mjs path, or null.
75
86
  * @property {import('../../foundation/integrations/integrations.mjs').LoadedIntegration[]|null} [integrations]
76
87
  * Every integration the project loaded, or null when the project could not be
@@ -229,7 +240,12 @@ export function checkCoreInstalled(ctx) {
229
240
  * @returns {DoctorCheck}
230
241
  */
231
242
  export function checkVersionAlignment(ctx) {
232
- const coreVersion = pkgVersion(ctx.coreDir);
243
+ // A project with no CLI of its own and no Core installed yet (a fresh
244
+ // clone) is compared by the Core it locks or declares, as the daily hint
245
+ // compares it (spec AST-081 FR2).
246
+ const coreVersion =
247
+ pkgVersion(ctx.coreDir) ??
248
+ (ctx.ownCli === false ? (ctx.projectCore ?? null) : null);
233
249
  const cliPkg = readPkg(path.join(CLI_ROOT, 'package.json'));
234
250
  const cliVersion = cliPkg?.version ?? null;
235
251
 
@@ -258,25 +274,33 @@ export function checkVersionAlignment(ctx) {
258
274
  };
259
275
  }
260
276
 
261
- const [coreMajor, coreMinor] = coreVersion.split('.').map(Number);
262
- const [cliMajor, cliMinor] = cliVersion.split('.').map(Number);
263
- const drift = coreMajor !== cliMajor || coreMinor !== cliMinor;
264
-
277
+ // A project with no CLI of its own is answered by the copy that started
278
+ // the run (spec AST-081 FR2): say so, and how to add the CLI at Core's
279
+ // version.
280
+ if (ctx.ownCli === false) {
281
+ return {
282
+ id: 'version-alignment',
283
+ label: '@astryxdesign/core <-> @astryxdesign/cli alignment',
284
+ status: coreVersion === cliVersion ? 'info' : 'warn',
285
+ message:
286
+ coreVersion === cliVersion
287
+ ? `This project has no Astryx CLI installed; CLI ${cliVersion}, the one you started, answered and matches its Core.`
288
+ : `This project has no Astryx CLI installed; CLI ${cliVersion}, the one you started, answered while its Core is ${coreVersion}.`,
289
+ fix: `${coreVersion === cliVersion ? 'To pin it for the team and CI' : 'To match'}, run: ${addCliCommandFor(ctx.cwd, coreVersion)}.`,
290
+ };
291
+ }
292
+ // The project's own CLI answers this project whatever its version
293
+ // (spec AST-081 FR2), so any difference means templates, docs, and agent
294
+ // text come from one release and components from another.
295
+ const drift = coreVersion !== cliVersion;
265
296
  return {
266
297
  id: 'version-alignment',
267
298
  label: '@astryxdesign/core <-> @astryxdesign/cli alignment',
268
299
  status: drift ? 'warn' : 'pass',
269
300
  message: drift
270
- ? `@astryxdesign/core v${coreVersion} drifts from @astryxdesign/cli v${cliVersion} (major/minor mismatch).`
301
+ ? `This project's CLI ${cliVersion} answers it, but its Core is ${coreVersion}: templates, docs, and agent text come from CLI ${cliVersion}, components from Core ${coreVersion}.`
271
302
  : `@astryxdesign/core v${coreVersion} is in step with @astryxdesign/cli v${cliVersion}.`,
272
- ...(drift
273
- ? {
274
- fix:
275
- semverCompare(cliVersion, coreVersion) > 0
276
- ? `Update @astryxdesign/core to ${cliMajor}.${cliMinor}.x to match the CLI.`
277
- : `Update @astryxdesign/cli to ${coreMajor}.${coreMinor}.x to match @astryxdesign/core.`,
278
- }
279
- : {}),
303
+ ...(drift ? {fix: `To align them, run: ${alignCommand(ctx.cwd, cliVersion, coreVersion)}.`} : {}),
280
304
  };
281
305
  }
282
306
 
@@ -331,7 +355,7 @@ export async function checkConfig(ctx) {
331
355
  label: 'astryx.config.mjs',
332
356
  status: 'fail',
333
357
  message: `astryx.config.mjs loads, but the CLI could not load the project from it: ${firstLine(ctx.projectError)}`,
334
- fix: 'Fix what the message names. `astryx docs authoring config` lists every field astryx.config accepts.',
358
+ fix: `Fix what the message names. \`${formatCliCommand('docs authoring config', ctx.cwd)}\` lists every field astryx.config accepts.`,
335
359
  };
336
360
  }
337
361
  return {
@@ -351,6 +375,26 @@ export async function checkConfig(ctx) {
351
375
  }
352
376
  }
353
377
 
378
+ /**
379
+ * Why no integration is implicitly linked, from what actually loaded. A
380
+ * configured integration is named in astryx.config; the package's own manifest
381
+ * loads as itself (`__local`). Only the first may be described as named in
382
+ * astryx.config: a package with no config that loads its own manifest has no
383
+ * config file to name it.
384
+ *
385
+ * @param {Array<{name?: string, __local?: boolean}>} integrations loaded, none
386
+ * autolinked and none that failed to load
387
+ * @returns {string}
388
+ */
389
+ function describeNoImplicit(integrations) {
390
+ const own = integrations.filter(integration => integration.__local);
391
+ const configured = integrations.length - own.length;
392
+ const ownNames = own.map(integration => integration.name ?? 'this package').join(', ');
393
+ if (own.length === 0) return 'None — every loaded integration is named in astryx.config.';
394
+ if (configured === 0) return `None — the only loaded integration is this package's own manifest (${ownNames}).`;
395
+ return `None — every loaded integration is named in astryx.config or is this package's own manifest (${ownNames}).`;
396
+ }
397
+
354
398
  /**
355
399
  * Check 6 — integrations that are loaded without an astryx.config entry.
356
400
  *
@@ -395,14 +439,18 @@ export function checkImplicitIntegrations(ctx) {
395
439
  const unreadable = describeUnreadableManifests(ctx.autolinkFailures ?? []);
396
440
 
397
441
  if (implicit.length === 0) {
442
+ // A record that carries `__loadError` (the package's own manifest, or a
443
+ // configured one, that failed to load) loaded nothing, so it is not named
444
+ // as loaded beside the line reporting that it could not be loaded.
445
+ const loaded = ctx.integrations.filter(integration => !integration.__loadError);
398
446
  return {
399
447
  id,
400
448
  label,
401
449
  status: 'info',
402
450
  message:
403
- (ctx.integrations.length > 0
404
- ? 'None — every loaded integration is named in astryx.config.'
405
- : unreadable
451
+ (loaded.length > 0
452
+ ? describeNoImplicit(loaded)
453
+ : ctx.integrations.length > 0 || unreadable
406
454
  ? 'None loaded.'
407
455
  : 'None — no installed dependency ships an astryx.integration.* manifest.') +
408
456
  unreadable,
@@ -476,7 +524,7 @@ function describeUnreadableManifests(failures) {
476
524
  return (
477
525
  ` ${failures.length} installed ${one ? 'dependency ships' : 'dependencies ship'} ` +
478
526
  `an astryx.integration.* manifest that could not be loaded, so ${one ? 'it contributes' : 'they contribute'} ` +
479
- `nothing: ${listed}. Run \`astryx doctor integration validate <package>\` for details.`
527
+ `nothing: ${listed}. Run \`${formatCliCommand('doctor integration validate <package>')}\` for details.`
480
528
  );
481
529
  }
482
530
 
@@ -550,7 +598,7 @@ export function checkConfiguredIntegrations(ctx) {
550
598
  `${withdrawn.length} of ${configured.length} integration${plural} named in astryx.config could not be loaded, ` +
551
599
  `so ${withdrawn.length === 1 ? 'it contributes' : 'they contribute'} nothing: ${listed}.`,
552
600
  fix:
553
- 'Install each one, fix its astryx.integration.* manifest (`astryx doctor integration validate <package>` says what is wrong), ' +
601
+ `Install each one, fix its astryx.integration.* manifest (\`${formatCliCommand('doctor integration validate <package>', ctx.cwd)}\` says what is wrong), ` +
554
602
  'or remove it from `integrations` in astryx.config.',
555
603
  };
556
604
  }
@@ -748,7 +796,7 @@ export function checkIntegrationIssues(ctx) {
748
796
  label: 'Integration contributions',
749
797
  status: 'warn',
750
798
  message: `${issues.length} integration issue(s)${errors > 0 ? `, ${errors} of them errors` : ''}: ${details}`,
751
- fix: 'Run `astryx doctor integration` for package-specific diagnostics and resolve cross-package precedence in astryx.config.',
799
+ fix: `Run \`${formatCliCommand('doctor integration', ctx.cwd)}\` for package-specific diagnostics and resolve cross-package precedence in astryx.config.`,
752
800
  };
753
801
  }
754
802
 
@@ -954,7 +1002,7 @@ function surfaceProblems(surface) {
954
1002
  const problems = [];
955
1003
  if (surface.types === 0 && surface.untraced.length === 0) {
956
1004
  problems.push(
957
- '@astryxdesign/cli/authoring exports no types, so nothing was compared with `astryx docs authoring`',
1005
+ `@astryxdesign/cli/authoring exports no types, so nothing was compared with \`${formatCliCommand('docs authoring')}\``,
958
1006
  );
959
1007
  }
960
1008
  for (const {module, names, reason, source, key} of surface.unreadable) {
@@ -967,7 +1015,7 @@ function surfaceProblems(surface) {
967
1015
  ? `${source} does not load`
968
1016
  : `${source} renders section "${key}", which the topic's index does not list`;
969
1017
  problems.push(
970
- `${nameTypes(names)} from ${module} ${names.length === 1 ? 'has' : 'have'} no doc in \`astryx docs authoring\`: ${why}`,
1018
+ `${nameTypes(names)} from ${module} ${names.length === 1 ? 'has' : 'have'} no doc in \`${formatCliCommand('docs authoring')}\`: ${why}`,
971
1019
  );
972
1020
  }
973
1021
  for (const {name, reason} of surface.untraced) {
@@ -1008,7 +1056,7 @@ export async function checkAuthoringDocs(_ctx, options = {}) {
1008
1056
  const audit = await auditAuthoringSelfDocs({root, sources});
1009
1057
  const problems = [
1010
1058
  ...audit.unreachable.map(
1011
- source => `${source} is not in \`astryx docs authoring\``,
1059
+ source => `${source} is not in \`${formatCliCommand('docs authoring')}\``,
1012
1060
  ),
1013
1061
  ...audit.failed.map(
1014
1062
  ({source, error}) => `${source} failed to load: ${error}`,
@@ -1024,7 +1072,7 @@ export async function checkAuthoringDocs(_ctx, options = {}) {
1024
1072
  : {keys: options.topicKeys};
1025
1073
  const surface = [
1026
1074
  ...('error' in topic
1027
- ? [`\`astryx docs authoring\` could not be read: ${topic.error}`]
1075
+ ? [`\`${formatCliCommand('docs authoring')}\` could not be read: ${topic.error}`]
1028
1076
  : []),
1029
1077
  ...surfaceProblems(
1030
1078
  await auditAuthoringSurface({root, sources, topicKeys: topic.keys}),
@@ -1048,7 +1096,7 @@ export async function checkAuthoringDocs(_ctx, options = {}) {
1048
1096
  id,
1049
1097
  label,
1050
1098
  status: 'pass',
1051
- message: `All ${audit.sections} authoring schemas are readable in \`astryx docs authoring\`.`,
1099
+ message: `All ${audit.sections} authoring schemas are readable in \`${formatCliCommand('docs authoring')}\`.`,
1052
1100
  };
1053
1101
  } catch (err) {
1054
1102
  return {
@@ -1088,11 +1136,11 @@ export async function checkCliDocs(_ctx, options = {}) {
1088
1136
  const problems = [
1089
1137
  ...audit.missing.map(
1090
1138
  source =>
1091
- `${source} has no namespace, so no \`astryx docs\` topic reads it`,
1139
+ `${source} has no namespace, so no \`${formatCliCommand('docs')}\` topic reads it`,
1092
1140
  ),
1093
1141
  ...audit.unknown.map(
1094
1142
  ({source, namespace}) =>
1095
- `${source} has namespace "${namespace}", which no \`astryx docs\` topic reads`,
1143
+ `${source} has namespace "${namespace}", which no \`${formatCliCommand('docs')}\` topic reads`,
1096
1144
  ),
1097
1145
  ...audit.misfiled.map(({message}) => message),
1098
1146
  ...audit.failed.map(
@@ -1116,7 +1164,7 @@ export async function checkCliDocs(_ctx, options = {}) {
1116
1164
  id,
1117
1165
  label,
1118
1166
  status: 'pass',
1119
- message: `All ${audit.docs} CLI docs are readable: ${audit.tree} in the \`astryx docs cli\` tree and ${audit.authoring} in \`astryx docs authoring\`.`,
1167
+ message: `All ${audit.docs} CLI docs are readable: ${audit.tree} in the \`${formatCliCommand('docs cli')}\` tree and ${audit.authoring} in \`${formatCliCommand('docs authoring')}\`.`,
1120
1168
  };
1121
1169
  } catch (err) {
1122
1170
  return {
@@ -1278,6 +1326,423 @@ export async function checkDocsProgressiveDisclosure(ctx) {
1278
1326
  };
1279
1327
  }
1280
1328
 
1329
+ /**
1330
+ * Check 11 — the command every printed command starts with, and a warning when
1331
+ * loaded integrations declare different ones (their `cliCommand`). Then the
1332
+ * picker uses neither, so commands fall back to the package-manager guess.
1333
+ *
1334
+ * @param {DoctorContext} ctx
1335
+ * @returns {DoctorCheck}
1336
+ */
1337
+ export function checkCliCommand(ctx) {
1338
+ const id = 'cli-command';
1339
+ const label = 'CLI command';
1340
+ const {command, conflict, declared} = chooseDeclaredCliCommand(
1341
+ declaredCliCommandsOf(ctx.integrations ?? []),
1342
+ );
1343
+ if (conflict) {
1344
+ const claims = declared
1345
+ .map(entry => `${entry.package} (\`${entry.command}\`)`)
1346
+ .join(', ');
1347
+ return {
1348
+ id,
1349
+ label,
1350
+ status: 'warn',
1351
+ message: `Loaded integrations declare different CLI commands: ${claims}. Neither is used, so printed commands start with \`${guessCliInvocation(ctx.cwd)}\`.`,
1352
+ fix: 'Remove the integration that does not belong here, or have every package declare the same `cliCommand` in its astryx.integration manifest.',
1353
+ };
1354
+ }
1355
+ if (command) {
1356
+ const packages = declared.map(entry => entry.package).join(', ');
1357
+ return {
1358
+ id,
1359
+ label,
1360
+ status: 'pass',
1361
+ message: `Printed commands start with \`${command}\`, the cliCommand of ${packages}.`,
1362
+ };
1363
+ }
1364
+ return {
1365
+ id,
1366
+ label,
1367
+ status: 'info',
1368
+ message: `No loaded integration declares a cliCommand, so printed commands start with \`${getCliInvocation(ctx.cwd)}\`.`,
1369
+ };
1370
+ }
1371
+
1372
+ /* ── outside a project: the computer ─────────────────────────────────── */
1373
+
1374
+ /**
1375
+ * What a computer check reads: the running Node, the release that answered
1376
+ * (`meta.answeredBy`), and the newest release the computer knows of.
1377
+ * @typedef {object} ComputerContext
1378
+ * @property {string} nodeVersion
1379
+ * @property {{cli: string, core: string | null, copy: string}} answeredBy
1380
+ * @property {string | null} latest the newest release known, or null
1381
+ * @property {Array<{path: string, state: import('../../foundation/agent-docs/computer-agent-text.mjs').ComputerAgentTextState}>} [agentText]
1382
+ * each file of the computer-level agent text against this release's rendering
1383
+ * @property {import('../../foundation/env/user-settings.mjs').ComputerAgentTextSettings} [agentTextSettings]
1384
+ * the person's choices about the computer-level agent text
1385
+ * @property {string} [cwd] the folder doctor runs in, for the printed fix
1386
+ * @property {import('../../foundation/release/notices.mjs').GlobalBehind | null} [globalBehind]
1387
+ * the CLI installed for the computer, when it is older than the newest
1388
+ * release (outside a project)
1389
+ * @property {import('../../foundation/release/notices.mjs').ProjectLag | null} [projectLag]
1390
+ * the project's release, when it is behind the newest (inside a project)
1391
+ * @property {{key: string, reason: string, at: number} | null} [lastNoticeSkip]
1392
+ * @property {{at: number, reason: string} | null} [lastCheckFailure] why the
1393
+ * last update check did not reach the registry, after the last that did
1394
+ * the newest update notice that did not print, and why (diagnostic only)
1395
+ * @property {{pid: number | null, host: string | null, since: number} | null} [stateLock]
1396
+ * who holds the state file's lock now (diagnostic only)
1397
+ * @property {Array<import('../../foundation/env/search-path.mjs').SearchPathCommand & {upgrade?: string | null}>} [searchPathCommands]
1398
+ * every `astryx` on the search path, with the command that upgrades it
1399
+ * the `astryx` commands on the search path, in order
1400
+ * @property {import('../../foundation/integrations/computer-integrations.mjs').ComputerIntegrationCandidate[]} [computerIntegrations]
1401
+ * the integrations installed beside the running CLI
1402
+ * @property {string[]} [loadedComputerIntegrations] the names of those that
1403
+ * loaded outside a project
1404
+ * @property {string[]} [folderIntegrations] the names of the integrations the
1405
+ * folder loads itself, whose copies win over the computer's
1406
+ */
1407
+
1408
+ /**
1409
+ * The release that answered this run outside a project, and the Core it
1410
+ * reads from.
1411
+ * @param {ComputerContext} ctx
1412
+ * @returns {DoctorCheck}
1413
+ */
1414
+ export function checkAnsweringRelease(ctx) {
1415
+ const {cli, core} = ctx.answeredBy;
1416
+ return {
1417
+ id: 'answering-release',
1418
+ label: 'Release that answered',
1419
+ status: core ? 'pass' : 'info',
1420
+ message: `${
1421
+ core
1422
+ ? `CLI ${cli} with Core ${core} answered, outside a project.`
1423
+ : `CLI ${cli} answered, outside a project. It installs Core ${cli} into the per-user cache the first time a command needs it.`
1424
+ } How each folder is answered: \`${formatCliCommand('astryx docs global', ctx.cwd)}\`.`,
1425
+ };
1426
+ }
1427
+
1428
+ /**
1429
+ * The newest release this computer knows of, beside the one that answered
1430
+ * (spec AST-081 FR10). Runs inside and outside a project, and is always
1431
+ * info. Inside one, a release behind the newest names the install and
1432
+ * upgrade commands; outside one, a CLI installed for the computer that is
1433
+ * behind names the command of the package manager that installed it, after
1434
+ * the Node it needs when the computer runs an older one. Doctor installs
1435
+ * nothing.
1436
+ * @param {ComputerContext} ctx
1437
+ * @returns {DoctorCheck}
1438
+ */
1439
+ export function checkNewestRelease(ctx) {
1440
+ const check = newestReleaseCheck(ctx);
1441
+ const notes = [
1442
+ checkFailureNote(ctx.lastCheckFailure),
1443
+ skipNote(ctx.lastNoticeSkip),
1444
+ lockNote(ctx.stateLock),
1445
+ ].filter(Boolean);
1446
+ return notes.length > 0 ? {...check, message: [check.message, ...notes].join(' ')} : check;
1447
+ }
1448
+
1449
+ /**
1450
+ * The note on a state-file lock held right now, which keeps notices from
1451
+ * being recorded (diagnostic only).
1452
+ * @param {{pid: number | null, host: string | null, since: number} | null | undefined} lock
1453
+ * @returns {string | null}
1454
+ */
1455
+ function lockNote(lock) {
1456
+ if (!lock) return null;
1457
+ const by = lock.pid == null ? '' : ` by process ${lock.pid}${lock.host ? ` on ${lock.host}` : ''}`;
1458
+ return `The state file's lock is held${by} since ${new Date(lock.since).toISOString()}.`;
1459
+ }
1460
+
1461
+ /**
1462
+ * The note on an update check that did not reach the registry (diagnostic
1463
+ * only): no check, no notices.
1464
+ * @param {{at: number, reason: string} | null | undefined} failed
1465
+ * @returns {string | null}
1466
+ */
1467
+ function checkFailureNote(failed) {
1468
+ if (!failed) return null;
1469
+ const why =
1470
+ failed.reason === 'npm-missing'
1471
+ ? 'npm could not be started'
1472
+ : failed.reason === 'bad-reply'
1473
+ ? "npm's answer named no release"
1474
+ : failed.reason.startsWith('npm-failed: ')
1475
+ ? `npm failed (${failed.reason.slice('npm-failed: '.length)}); its registry and proxy settings decide where it asks`
1476
+ : failed.reason;
1477
+ return `The last update check, ${new Date(failed.at).toISOString()}, did not reach the registry: ${why}.`;
1478
+ }
1479
+
1480
+ /** What a recorded notice skip's reason means, for doctor's update line. */
1481
+ const SKIP_REASONS = {
1482
+ 'already-shown': 'it had already printed that day',
1483
+ 'no-newer': 'no newer release was known',
1484
+ };
1485
+
1486
+ /**
1487
+ * The note on the last update notice that did not print, for diagnosis
1488
+ * (diagnostic state, not a contract).
1489
+ * @param {{key: string, reason: string, at: number} | null | undefined} skip
1490
+ * @returns {string | null}
1491
+ */
1492
+ function skipNote(skip) {
1493
+ if (!skip) return null;
1494
+ const why = /** @type {Record<string, string>} */ (SKIP_REASONS)[skip.reason] ?? skip.reason;
1495
+ return `Last update notice skipped ${new Date(skip.at).toISOString()} (${skip.key}): ${why}.`;
1496
+ }
1497
+
1498
+ /**
1499
+ * {@link checkNewestRelease} without the skip note.
1500
+ * @param {ComputerContext} ctx
1501
+ * @returns {DoctorCheck}
1502
+ */
1503
+ function newestReleaseCheck(ctx) {
1504
+ const {latest, globalBehind: behind, projectLag: lag} = ctx;
1505
+ const {cli} = ctx.answeredBy;
1506
+ const base = {id: 'newest-release', label: 'Newest release', status: /** @type {const} */ ('info')};
1507
+ if (behind) {
1508
+ return {
1509
+ ...base,
1510
+ message: `${availableLine(behind.current, behind.newest)}. The CLI installed for the computer stays on its release until you upgrade it.`,
1511
+ fix: `${upgradeLine(behind.command, behind.node)}.`,
1512
+ };
1513
+ }
1514
+ if (lag) {
1515
+ return {
1516
+ ...base,
1517
+ message: `This project uses Astryx ${lag.current}; ${lag.newest} is the newest.`,
1518
+ fix: `${upgradeLine(`${lag.install}, then ${lag.upgrade}`, lag.node)}.`,
1519
+ };
1520
+ }
1521
+ if (!latest) {
1522
+ return {
1523
+ ...base,
1524
+ message: 'Update check: not run yet, so the newest release is not known here; it runs in the background once a day, after a command.',
1525
+ };
1526
+ }
1527
+ if (isValidSemver(latest) && isValidSemver(cli) && semverCompare(latest, cli) > 0) {
1528
+ return {
1529
+ ...base,
1530
+ message: `Release ${latest} is newer than ${cli}, the release that answered.`,
1531
+ };
1532
+ }
1533
+ return {...base, message: `No newer release is known: the newest is ${latest}.`};
1534
+ }
1535
+
1536
+ /**
1537
+ * The `astryx` commands on the search path (spec AST-081 FR11): the first is
1538
+ * the one a shell runs. Warns when it is not the Astryx CLI, or is a release
1539
+ * that predates the hand-off and so answers every folder itself.
1540
+ * @param {ComputerContext} ctx
1541
+ * @returns {DoctorCheck}
1542
+ */
1543
+ export function checkCliOnPath(ctx) {
1544
+ const rows = ctx.searchPathCommands ?? [];
1545
+ const base = {id: 'cli-on-path', label: '`astryx` on the search path'};
1546
+ const describe = (/** @type {import('../../foundation/env/search-path.mjs').SearchPathCommand} */ row) =>
1547
+ row.astryx ? `${row.file} (CLI ${row.version ?? 'of unknown version'})` : `${row.file} (not the Astryx CLI)`;
1548
+ const others = rows.slice(1);
1549
+ const othersNote = others.length > 0 ? ` Later on the search path: ${others.map(describe).join(', ')}.` : '';
1550
+ const [first] = rows;
1551
+ if (!first) {
1552
+ return {
1553
+ ...base,
1554
+ status: 'info',
1555
+ message: 'No `astryx` command on the search path, so `astryx` runs only through a package manager.',
1556
+ fix: 'To run the CLI in any folder, install it for the computer: `npm i -g @astryxdesign/cli`.',
1557
+ };
1558
+ }
1559
+ if (!first.astryx) {
1560
+ return {
1561
+ ...base,
1562
+ status: 'warn',
1563
+ message: `The first \`astryx\` on the search path, ${first.file}, is not the Astryx CLI, so \`astryx\` runs another program.${othersNote}`,
1564
+ fix: 'Remove that program from the search path, or put the folder of the CLI installed for the computer before it.',
1565
+ };
1566
+ }
1567
+ if (first.handsOff === false) {
1568
+ return {
1569
+ ...base,
1570
+ status: 'warn',
1571
+ message: `The first \`astryx\` on the search path, ${first.file}, runs CLI ${first.version ?? 'of unknown version'}, which predates the hand-off and answers every folder itself.${othersNote}`,
1572
+ fix: `To upgrade, run: \`${first.upgrade ?? 'npm i -g @astryxdesign/cli@latest'}\`.`,
1573
+ };
1574
+ }
1575
+ return {
1576
+ ...base,
1577
+ status: 'pass',
1578
+ message: `\`astryx\` runs ${describe(first)}.${othersNote}`,
1579
+ };
1580
+ }
1581
+
1582
+ /**
1583
+ * The computer-level agent text (spec AST-081 FR4, FR8): turned off, present
1584
+ * and equal to this release's rendering, present but different, or absent,
1585
+ * for the files the chosen agents read. Always `info`: the text is optional,
1586
+ * so it never warns or fails. A file Astryx did not write counts as absent,
1587
+ * and a file a person edited is named; both are kept as they are.
1588
+ * @param {ComputerContext} ctx
1589
+ * @returns {DoctorCheck}
1590
+ */
1591
+ export function checkComputerAgentText(ctx) {
1592
+ const state = ctx.agentTextSettings && ctx.agentText ? null : computerAgentTextState();
1593
+ const settings = ctx.agentTextSettings ?? /** @type {NonNullable<typeof state>} */ (state).settings;
1594
+ const base = {
1595
+ id: 'computer-agent-text',
1596
+ label: 'Computer-level agent text',
1597
+ status: /** @type {const} */ ('info'),
1598
+ };
1599
+ if (!settings.install) {
1600
+ return {
1601
+ ...base,
1602
+ message: 'The computer-level agent text is turned off (`astryx init --user --remove-agents`), so the CLI installed for the computer does not write it.',
1603
+ fix: `To turn it back on, run \`${formatCliCommand('astryx init --user', ctx.cwd)}\`.`,
1604
+ };
1605
+ }
1606
+ const entries = ctx.agentText ?? /** @type {NonNullable<typeof state>} */ (state).entries;
1607
+ const fix = `Run \`${formatCliCommand('astryx init --user', ctx.cwd)}\`.`;
1608
+ const [file, ...links] = entries;
1609
+ const home = (/** @type {{path: string}} */ e) => displayHomePath(e.path);
1610
+ const linked = links.filter(l => l.state === 'linked').map(home);
1611
+ const copied = links.filter(l => l.state === 'current').map(home);
1612
+ const skipped = links.filter(l => l.state === 'foreign' || l.state === 'edited').map(home);
1613
+ const dangling = links.filter(l => l.state === 'dangling').map(home);
1614
+ const missing = links.filter(l => l.state === 'absent' || l.state === 'differs').map(home);
1615
+ const linkNote =
1616
+ (linked.length > 0 ? ` Linked into: ${linked.join(', ')}.` : '') +
1617
+ (copied.length > 0 ? ` Copied into: ${copied.join(', ')}.` : '') +
1618
+ (skipped.length > 0 ? ` Not linked, because something Astryx did not create is there: ${skipped.join(', ')}.` : '') +
1619
+ (dangling.length > 0 ? ` Linked, but the skill folder the link points to is not there, so agents reading it see nothing (as in a container that mounts only that folder): ${dangling.join(', ')}.` : '') +
1620
+ (missing.length > 0 ? ` Not linked yet: ${missing.join(', ')}.` : '');
1621
+ if (file.state === 'foreign' || file.state === 'edited') {
1622
+ return {
1623
+ ...base,
1624
+ message: `${home(file)} ${file.state === 'edited' ? 'was edited since Astryx wrote it' : 'was not written by Astryx'}, so it is left as it is (delete it to get the current text).${linkNote}`,
1625
+ };
1626
+ }
1627
+ if (file.state === 'absent') {
1628
+ return {
1629
+ ...base,
1630
+ message: `No computer-level agent text: agents outside a project with Astryx agent docs do not know Astryx is here.${linkNote}`,
1631
+ fix,
1632
+ };
1633
+ }
1634
+ const current = file.state === 'current';
1635
+ return {
1636
+ ...base,
1637
+ message: `${current ? 'The computer-level agent text matches this release' : "The computer-level agent text differs from this release's"}: ${home(file)}.${linkNote}`,
1638
+ ...(!current || missing.length > 0 || dangling.length > 0 ? {fix} : {}),
1639
+ };
1640
+ }
1641
+
1642
+ /**
1643
+ * The integrations installed for the computer (spec AST-081 FR12). Outside a
1644
+ * project: each one that loads, and a warning for each that is skipped
1645
+ * because its peer ranges exclude the answering release, or fails to load.
1646
+ * @param {ComputerContext} ctx
1647
+ * @returns {DoctorCheck}
1648
+ */
1649
+ export function checkComputerIntegrations(ctx) {
1650
+ const found = ctx.computerIntegrations ?? [];
1651
+ const loaded = new Set(ctx.loadedComputerIntegrations ?? []);
1652
+ const base = {id: 'computer-integrations', label: 'Integrations installed for this computer'};
1653
+ if (found.length === 0) {
1654
+ return {
1655
+ ...base,
1656
+ status: 'info',
1657
+ message: 'No integration is installed beside the CLI installed for this computer.',
1658
+ };
1659
+ }
1660
+ const label = (/** @type {{name: string, version?: string}} */ c) => (c.version ? `${c.name}@${c.version}` : c.name);
1661
+ const own = new Set(ctx.folderIntegrations ?? []);
1662
+ const shadowed = found.filter(c => own.has(c.name));
1663
+ const rest = found.filter(c => !own.has(c.name));
1664
+ const used = rest.filter(c => !c.incompatible && loaded.has(c.name));
1665
+ const skipped = rest.filter(c => c.incompatible);
1666
+ const failed = rest.filter(c => !c.incompatible && !loaded.has(c.name));
1667
+ const notes = [
1668
+ used.length > 0 ? `Used outside a project: ${used.map(label).join(', ')}.` : '',
1669
+ ...shadowed.map(c => `${label(c)} is not used here: this folder loads its own copy of ${c.name}.`),
1670
+ ...skipped.map(c => `Skipped ${label(c)}: ${c.incompatible}.`),
1671
+ ...failed.map(c => `${label(c)} could not be loaded.`),
1672
+ ].filter(Boolean);
1673
+ const fixes = [
1674
+ ...skipped.map(c => `install a release of ${c.name} that supports this CLI`),
1675
+ ...failed.map(c => `run \`${formatCliCommand(`astryx doctor integration validate ${c.name}`, ctx.cwd)}\``),
1676
+ ];
1677
+ return {
1678
+ ...base,
1679
+ status: skipped.length + failed.length > 0 ? 'warn' : 'pass',
1680
+ message: notes.join(' '),
1681
+ ...(fixes.length > 0 ? {fix: `To fix: ${fixes.join('; ')}.`} : {}),
1682
+ };
1683
+ }
1684
+
1685
+ /**
1686
+ * Inside a project, the integrations installed for the computer that the
1687
+ * project does not install: available, never used (spec AST-081 FR12).
1688
+ * Always info.
1689
+ * @param {{installed: Set<string>, candidates: import('../../foundation/integrations/computer-integrations.mjs').ComputerIntegrationCandidate[], install: (name: string) => string}} input
1690
+ * `install`: the command that adds a package with the project's package
1691
+ * manager (read by the adapter)
1692
+ * @returns {DoctorCheck}
1693
+ */
1694
+ export function checkComputerIntegrationsAvailable({installed, candidates, install}) {
1695
+ const available = candidates.filter(c => !installed.has(c.name));
1696
+ const base = {id: 'computer-integrations', label: 'Integrations installed for this computer', status: /** @type {const} */ ('info')};
1697
+ if (available.length === 0) {
1698
+ return {...base, message: 'No integration installed for this computer is missing from this project.'};
1699
+ }
1700
+ const add = install;
1701
+ return {
1702
+ ...base,
1703
+ message: `Installed for this computer but not in this project, so not used here: ${available.map(c => c.name).join(', ')}.`,
1704
+ fix: `To use one in this project, run ${available.map(c => `\`${add(c.name)}\``).join(' or ')}.`,
1705
+ };
1706
+ }
1707
+
1708
+ /**
1709
+ * The checks doctor runs outside a project, in order.
1710
+ * @type {Array<(ctx: ComputerContext) => DoctorCheck>}
1711
+ */
1712
+ export const COMPUTER_CHECKS = [
1713
+ ctx =>
1714
+ checkNodeVersion({
1715
+ cwd: '',
1716
+ nodeVersion: ctx.nodeVersion,
1717
+ coreDir: null,
1718
+ configPath: null,
1719
+ }),
1720
+ checkAnsweringRelease,
1721
+ checkNewestRelease,
1722
+ checkCliOnPath,
1723
+ checkComputerIntegrations,
1724
+ checkComputerAgentText,
1725
+ ];
1726
+
1727
+ /**
1728
+ * Doctor outside a project: the computer, never the folder.
1729
+ * @param {string} cwd
1730
+ * @returns {Promise<DoctorReport>}
1731
+ */
1732
+ async function runComputerChecks(cwd) {
1733
+ /** @type {ComputerContext} */
1734
+ const ctx = {
1735
+ nodeVersion: process.versions.node,
1736
+ cwd,
1737
+ ...releaseFacts(cwd, {project: false}),
1738
+ };
1739
+ Object.assign(ctx, await computerIntegrationsOutside(cwd));
1740
+ const checks = COMPUTER_CHECKS.map(fn => fn(ctx));
1741
+ const summary = {pass: 0, warn: 0, fail: 0, info: 0};
1742
+ for (const c of checks) summary[c.status] += 1;
1743
+ return {checks, summary};
1744
+ }
1745
+
1281
1746
  /**
1282
1747
  * Ordered list of synchronous check functions. Append here to add a check.
1283
1748
  * (checkConfig is async and is awaited separately by {@link runChecks}.)
@@ -1294,6 +1759,7 @@ export const SYNC_CHECKS = [
1294
1759
  checkAgentDocs,
1295
1760
  checkPeerDeps,
1296
1761
  checkPackageManager,
1762
+ checkCliCommand,
1297
1763
  ];
1298
1764
 
1299
1765
  /**
@@ -1305,7 +1771,8 @@ export const SYNC_CHECKS = [
1305
1771
  */
1306
1772
  export async function runChecks(options = {}) {
1307
1773
  const cwd = options.cwd ?? process.cwd();
1308
- const coreDir = findCoreDir(cwd);
1774
+ if (outsideProject(cwd)) return runComputerChecks(cwd);
1775
+ const coreDir = findProjectCoreDir(cwd);
1309
1776
  // findConfigPath throws when multiple config files coexist. That's a
1310
1777
  // misconfiguration doctor exists to report — catch it and surface it through
1311
1778
  // checkConfig as a FAIL rather than crashing the whole diagnostic engine.
@@ -1339,7 +1806,9 @@ export async function runChecks(options = {}) {
1339
1806
  /** @type {import('../../foundation/config/project.mjs').Project | null} */
1340
1807
  let project = null;
1341
1808
  try {
1342
- project = await Project.load(cwd);
1809
+ // Doctor judges the project, so it reads only the Core the project
1810
+ // installed, never a carried one (spec AST-081 FR3).
1811
+ project = await Project.load(cwd, {core: 'project'});
1343
1812
  } catch (err) {
1344
1813
  // A project the CLI cannot load leaves the checks that need it
1345
1814
  // skipped. The reason is kept: those checks and the config
@@ -1442,6 +1911,8 @@ export async function runChecks(options = {}) {
1442
1911
  cwd,
1443
1912
  nodeVersion: process.versions.node,
1444
1913
  coreDir,
1914
+ ownCli: projectHasOwnCli(cwd),
1915
+ projectCore: projectCoreVersion(cwd),
1445
1916
  configPath,
1446
1917
  integrations,
1447
1918
  docsCatalog,
@@ -1466,6 +1937,31 @@ export async function runChecks(options = {}) {
1466
1937
  checks.push(await checkConfig(ctx));
1467
1938
  }
1468
1939
  }
1940
+ const facts = releaseFacts(cwd, {project: true});
1941
+ checks.push(
1942
+ checkNewestRelease({
1943
+ nodeVersion: ctx.nodeVersion,
1944
+ answeredBy: facts.answeredBy,
1945
+ latest: facts.latest,
1946
+ projectLag: facts.projectLag,
1947
+ lastNoticeSkip: facts.lastNoticeSkip,
1948
+ lastCheckFailure: facts.lastCheckFailure,
1949
+ stateLock: facts.stateLock,
1950
+ }),
1951
+ );
1952
+ checks.push(
1953
+ checkCliOnPath({
1954
+ nodeVersion: ctx.nodeVersion,
1955
+ answeredBy: facts.answeredBy,
1956
+ latest: null,
1957
+ searchPathCommands: facts.searchPathCommands,
1958
+ }),
1959
+ );
1960
+ {
1961
+ const loadedNames = (project?.loadedIntegrations ?? []).map((/** @type {{name: string}} */ i) => i.name);
1962
+ const candidates = await computerIntegrationsToAdd(cwd, loadedNames);
1963
+ checks.push(checkComputerIntegrationsAvailable({installed: new Set(loadedNames), candidates, install: installCommandFor(cwd)}));
1964
+ }
1469
1965
  checks.push(await checkAuthoringDocs(ctx));
1470
1966
  checks.push(await checkCliDocs(ctx));
1471
1967
  checks.push(await checkDocsTree(ctx));