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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (694) hide show
  1. package/CHANGELOG.md +119 -0
  2. package/README.md +216 -162
  3. package/api/build/_adapter.d.mts +66 -0
  4. package/api/build/_adapter.mjs +139 -4
  5. package/api/build/blocks/blocks.d.mts +92 -0
  6. package/api/build/blocks/blocks.mjs +528 -0
  7. package/api/build/blocks/blocks.test.mjs +238 -0
  8. package/api/build/blocks/blocks.weights.json +1 -0
  9. package/api/build/build.doc.mjs +4 -0
  10. package/api/build/build.mjs +25 -6
  11. package/api/build/build.type.mjs +1 -1
  12. package/api/build/help/help.mjs +1 -1
  13. package/api/build/kit/answer.d.mts +18 -0
  14. package/api/build/kit/answer.mjs +195 -0
  15. package/api/build/kit/answer.test.mjs +108 -0
  16. package/api/build/kit/kind.d.mts +25 -0
  17. package/api/build/kit/kind.mjs +170 -0
  18. package/api/build/kit/kit.d.mts +2 -4
  19. package/api/build/kit/kit.mjs +8 -406
  20. package/api/build/kit/rank.d.mts +3 -32
  21. package/api/build/kit/rank.mjs +25 -342
  22. package/api/build/kit/rank.test.mjs +2 -1
  23. package/api/build/kit/signals.d.mts +79 -0
  24. package/api/build/kit/signals.mjs +295 -0
  25. package/api/build/kit/start.d.mts +32 -0
  26. package/api/build/kit/start.mjs +163 -0
  27. package/api/build/kit/words.d.mts +72 -0
  28. package/api/build/kit/words.mjs +202 -0
  29. package/api/build/tree.d.mts +20 -0
  30. package/api/build/tree.mjs +53 -0
  31. package/api/build/tree.test.mjs +185 -0
  32. package/api/build/tree.weights.json +1 -0
  33. package/api/component/_adapter.d.mts +72 -1
  34. package/api/component/_adapter.mjs +251 -23
  35. package/api/component/component-replacement.test.mjs +1 -0
  36. package/api/component/component.doc.mjs +6 -1
  37. package/api/component/component.mjs +97 -16
  38. package/api/component/component.test.mjs +95 -0
  39. package/api/component/doc-links.test.mjs +386 -0
  40. package/api/component/integration-export-name.test.mjs +644 -0
  41. package/api/component/list/list.mjs +42 -4
  42. package/api/discover/_adapter.d.mts +131 -12
  43. package/api/discover/_adapter.mjs +520 -34
  44. package/api/discover/_adapter.test.mjs +641 -17
  45. package/api/discover/_catalog-view.d.mts +16 -5
  46. package/api/discover/_catalog-view.mjs +32 -8
  47. package/api/discover/detail/doc/doc.mjs +9 -1
  48. package/api/discover/discover.d.mts +11 -0
  49. package/api/discover/discover.doc.mjs +9 -7
  50. package/api/discover/discover.mjs +120 -19
  51. package/api/discover/discover.type.d.mts +20 -7
  52. package/api/discover/discover.type.mjs +17 -7
  53. package/api/discover/list/list.d.mts +4 -3
  54. package/api/discover/list/list.mjs +17 -10
  55. package/api/docs/_adapter.d.mts +76 -2
  56. package/api/docs/_adapter.mjs +630 -34
  57. package/api/docs/docs-homes.test.mjs +127 -0
  58. package/api/docs/docs.doc.mjs +5 -4
  59. package/api/docs/docs.mjs +2 -1
  60. package/api/docs/docs.test.mjs +39 -20
  61. package/api/docs/integration-tree.test.mjs +42 -20
  62. package/api/docs/integrationDocs.test.mjs +125 -1
  63. package/api/docs/list/list.mjs +12 -2
  64. package/api/docs/namespace-section.test.mjs +6 -6
  65. package/api/docs/reference-blocks.test.mjs +3 -5
  66. package/api/doctor/_adapter.d.mts +128 -0
  67. package/api/doctor/_adapter.mjs +253 -4
  68. package/api/doctor/doctor.d.mts +217 -0
  69. package/api/doctor/doctor.doc.mjs +3 -2
  70. package/api/doctor/doctor.mjs +691 -52
  71. package/api/doctor/doctor.test.mjs +267 -18
  72. package/api/doctor/release-checks.test.mjs +156 -0
  73. package/api/doctor/theme-checks.mjs +17 -4
  74. package/api/doctor/theme-checks.test.mjs +223 -7
  75. package/api/error.d.mts +10 -0
  76. package/api/error.mjs +25 -0
  77. package/api/gap-report/gap-report.d.mts +3 -0
  78. package/api/gap-report/gap-report.doc.mjs +11 -3
  79. package/api/gap-report/gap-report.mjs +219 -52
  80. package/api/gap-report/gap-report.test.mjs +233 -0
  81. package/api/gap-report/gap-report.type.d.mts +41 -0
  82. package/api/gap-report/gap-report.type.mjs +18 -0
  83. package/api/hook/_adapter.mjs +3 -11
  84. package/api/hook/hook.doc.mjs +4 -0
  85. package/api/hook/hook.mjs +1 -1
  86. package/api/hook/list/list.d.mts +1 -1
  87. package/api/index.d.mts +2 -0
  88. package/api/index.mjs +2 -0
  89. package/api/init/_adapter.d.mts +22 -0
  90. package/api/init/_adapter.mjs +59 -0
  91. package/api/init/init.d.mts +7 -5
  92. package/api/init/init.doc.mjs +45 -10
  93. package/api/init/init.mjs +28 -4
  94. package/api/init/init.test.mjs +18 -2
  95. package/api/init/init.type.d.mts +50 -5
  96. package/api/init/init.type.mjs +39 -5
  97. package/api/init/remove/remove.mjs +17 -4
  98. package/api/init/remove/remove.test.mjs +114 -2
  99. package/api/init/run/run.d.mts +1 -1
  100. package/api/init/run/run.mjs +86 -12
  101. package/api/init/user/user.d.mts +14 -0
  102. package/api/init/user/user.mjs +87 -0
  103. package/api/init/user/user.test.mjs +232 -0
  104. package/api/integration/_adapter.d.mts +53 -0
  105. package/api/integration/_adapter.mjs +132 -0
  106. package/api/integration/add-contribution.component-names.test.mjs +66 -0
  107. package/api/integration/add-contribution.mjs +8 -4
  108. package/api/integration/add-contribution.test.mjs +4 -2
  109. package/api/integration/authoring-checks.mjs +192 -14
  110. package/api/integration/authoring-checks.test.mjs +1028 -0
  111. package/api/integration/authoring-checks.type.d.mts +143 -0
  112. package/api/integration/authoring-checks.type.mjs +74 -2
  113. package/api/integration/check/check.d.mts +96 -0
  114. package/api/integration/check/check.mjs +357 -0
  115. package/api/integration/check/check.test.mjs +489 -0
  116. package/api/integration/integration-check.type.d.mts +118 -0
  117. package/api/integration/integration-check.type.mjs +84 -0
  118. package/api/integration/integrationCheck.doc.d.mts +10 -0
  119. package/api/integration/integrationCheck.doc.mjs +73 -0
  120. package/api/integration/integrationComponentConflicts.doc.mjs +45 -6
  121. package/api/integration/integrationDocConflicts.doc.mjs +22 -5
  122. package/api/integration/pack-check.mjs +486 -123
  123. package/api/integration/pack-check.test.mjs +817 -10
  124. package/api/integration/validate-integration-case-disk.test.mjs +111 -0
  125. package/api/integration/validate-integration-fixes.test.mjs +73 -5
  126. package/api/integration/validate-integration-gaps.test.mjs +1975 -0
  127. package/api/integration/validate-integration.d.mts +23 -2
  128. package/api/integration/validate-integration.mjs +358 -45
  129. package/api/integration/validate-integration.test.mjs +88 -8
  130. package/api/integration/validate-integration.type.d.mts +105 -0
  131. package/api/integration/validate-integration.type.mjs +79 -1
  132. package/api/integration/validate-unread-theme-folders.test.mjs +3 -2
  133. package/api/integration/validateIntegration.doc.mjs +19 -3
  134. package/api/search/ORDERING.md +28 -0
  135. package/api/search/_adapter.d.mts +91 -0
  136. package/api/search/_adapter.mjs +172 -0
  137. package/api/search/_adapter.test.mjs +173 -0
  138. package/api/search/_candidate-score.d.mts +40 -0
  139. package/api/search/_candidate-score.mjs +238 -0
  140. package/api/search/_cards.json +6098 -0
  141. package/api/search/_compound.d.mts +50 -0
  142. package/api/search/_compound.mjs +170 -0
  143. package/api/search/_doc-searcher.d.mts +4 -0
  144. package/api/search/_doc-searcher.mjs +23 -0
  145. package/api/search/_doc-searcher.test.mjs +36 -0
  146. package/api/search/_doc-searcher.weights.json +1 -0
  147. package/api/search/_doc-text.d.mts +30 -0
  148. package/api/search/_doc-text.mjs +87 -0
  149. package/api/search/_entries.d.mts +63 -0
  150. package/api/search/_entries.mjs +232 -0
  151. package/api/search/_entries.test.mjs +48 -0
  152. package/api/search/_features.d.mts +35 -0
  153. package/api/search/_features.mjs +155 -0
  154. package/api/search/_gather-components.d.mts +26 -0
  155. package/api/search/_gather-components.mjs +284 -0
  156. package/api/search/_gather-docs.d.mts +20 -0
  157. package/api/search/_gather-docs.mjs +254 -0
  158. package/api/search/_gather-hooks.d.mts +12 -0
  159. package/api/search/_gather-hooks.mjs +52 -0
  160. package/api/search/_gather-templates.d.mts +25 -0
  161. package/api/search/_gather-templates.mjs +107 -0
  162. package/api/search/_lexical.d.mts +135 -0
  163. package/api/search/_lexical.mjs +351 -0
  164. package/api/search/_main-searcher.d.mts +19 -0
  165. package/api/search/_main-searcher.mjs +115 -0
  166. package/api/search/_main-searcher.test.mjs +106 -0
  167. package/api/search/_main-searcher.weights.json +1 -0
  168. package/api/search/_module-doc.d.mts +32 -0
  169. package/api/search/_module-doc.mjs +78 -0
  170. package/api/search/_order.d.mts +32 -0
  171. package/api/search/_order.mjs +162 -0
  172. package/api/search/_order.test.mjs +72 -0
  173. package/api/search/_priority.d.mts +50 -0
  174. package/api/search/_priority.mjs +139 -0
  175. package/api/search/_query-words.d.mts +25 -0
  176. package/api/search/_query-words.mjs +92 -0
  177. package/api/search/_query.d.mts +65 -0
  178. package/api/search/_query.mjs +176 -0
  179. package/api/search/_result.d.mts +97 -0
  180. package/api/search/_result.mjs +150 -0
  181. package/api/search/_score.d.mts +60 -0
  182. package/api/search/_score.mjs +283 -0
  183. package/api/search/_synonyms.d.mts +21 -0
  184. package/api/search/_synonyms.mjs +67 -0
  185. package/api/search/_template-searcher.d.mts +4 -0
  186. package/api/search/_template-searcher.mjs +23 -0
  187. package/api/search/_template-searcher.test.mjs +40 -0
  188. package/api/search/_template-searcher.weights.json +1 -0
  189. package/api/search/_titles.d.mts +33 -0
  190. package/api/search/_titles.mjs +65 -0
  191. package/api/search/_words.d.mts +64 -0
  192. package/api/search/_words.mjs +157 -0
  193. package/api/search/search.d.mts +22 -236
  194. package/api/search/search.doc.mjs +14 -7
  195. package/api/search/search.domain-priority.test.mjs +108 -18
  196. package/api/search/search.integration-check.test.mjs +24 -0
  197. package/api/search/search.mjs +59 -1904
  198. package/api/search/search.test.mjs +193 -15
  199. package/api/search/search.type.d.mts +20 -2
  200. package/api/search/search.type.mjs +12 -2
  201. package/api/search/tree.d.mts +15 -0
  202. package/api/search/tree.mjs +40 -0
  203. package/api/search/tree.test.mjs +66 -0
  204. package/api/search/tree.weights.json +1 -0
  205. package/api/swizzle/_adapter.mjs +2 -2
  206. package/api/swizzle/copy/copy.mjs +29 -15
  207. package/api/template/template-integration.test.mjs +1 -1
  208. package/api/template/template.mjs +5 -1
  209. package/api/template/template.test.mjs +120 -3
  210. package/api/theme/_adapter.d.mts +16 -0
  211. package/api/theme/_adapter.mjs +32 -5
  212. package/api/theme/build/build.d.mts +2 -1
  213. package/api/theme/build/build.family.test.mjs +64 -0
  214. package/api/theme/build/build.mjs +250 -38
  215. package/api/theme/build/build.packed-old-core.test.mjs +21 -3
  216. package/api/theme/build/font-warning.mjs +5 -3
  217. package/api/theme/build/font-warning.test.mjs +2 -1
  218. package/api/theme/remove/remove.test.mjs +2 -2
  219. package/api/theme/targets/targets.mjs +3 -10
  220. package/api/theme/themeBuild.doc.mjs +4 -4
  221. package/api/theme/themeTargets.doc.mjs +4 -0
  222. package/api/theme/use/use.test.mjs +1 -1
  223. package/api/upgrade/_adapter.d.mts +97 -11
  224. package/api/upgrade/_adapter.mjs +315 -32
  225. package/api/upgrade/registry/registry.d.mts +10 -4
  226. package/api/upgrade/registry/registry.mjs +33 -16
  227. package/api/upgrade/registry/registry.test.mjs +85 -0
  228. package/api/upgrade/run/cli-behind-core.test.mjs +438 -0
  229. package/api/upgrade/run/run.d.mts +4 -2
  230. package/api/upgrade/run/run.mjs +48 -4
  231. package/api/upgrade/run/run.test.mjs +417 -3
  232. package/api/upgrade/status/status.d.mts +21 -5
  233. package/api/upgrade/status/status.mjs +45 -8
  234. package/api/upgrade/upgrade.doc.mjs +16 -8
  235. package/api/upgrade/upgrade.mjs +18 -3
  236. package/api/upgrade/upgrade.test.mjs +194 -0
  237. package/api/upgrade/upgrade.type.d.mts +28 -3
  238. package/api/upgrade/upgrade.type.mjs +24 -7
  239. package/assets/codemods/__tests__/registry.test.mjs +1 -0
  240. package/assets/codemods/registry.mjs +1 -0
  241. package/assets/codemods/runner.mjs +7 -1
  242. package/assets/codemods/transforms/v0.7.0/__tests__/validate-data-token-ownership.test.mjs +11 -0
  243. package/assets/codemods/transforms/v0.7.0/validate-data-token-ownership.mjs +3 -3
  244. package/assets/codemods/transforms/v0.7.2/__tests__/migrate-list-edge-compensation-to-edge.test.mjs +120 -0
  245. package/assets/codemods/transforms/v0.7.2/index.mjs +20 -0
  246. package/assets/codemods/transforms/v0.7.2/migrate-list-edge-compensation-to-edge.mjs +198 -0
  247. package/assets/discover/official-packages.json +367 -0
  248. package/assets/docs/README.md +10 -0
  249. package/assets/docs/author-a-theme.doc.mjs +5 -5
  250. package/assets/docs/browser-support.doc.mjs +1 -1
  251. package/assets/docs/getting-started.doc.mjs +26 -6
  252. package/assets/docs/is-my-code-good.doc.mjs +287 -0
  253. package/assets/docs/styling-overview.doc.mjs +1 -1
  254. package/assets/docs/theme.doc.dense.mjs +1 -1
  255. package/assets/docs/theme.doc.mjs +2 -2
  256. package/assets/docs/theme.doc.zh.mjs +1 -1
  257. package/assets/docs/tree/add-a-component.doc.mjs +22 -2
  258. package/assets/docs/tree/add-a-theme.doc.mjs +14 -4
  259. package/assets/docs/tree/add-a-topic.doc.mjs +54 -9
  260. package/assets/docs/tree/agent-guidance.doc.mjs +19 -5
  261. package/assets/docs/tree/api.doc.mjs +6 -0
  262. package/assets/docs/tree/authoring.doc.mjs +61 -0
  263. package/assets/docs/tree/block-template.doc.mjs +8 -20
  264. package/assets/docs/tree/build-the-template.doc.mjs +1 -1
  265. package/assets/docs/tree/building-blocks.doc.mjs +6 -2
  266. package/assets/docs/tree/check-the-props.doc.mjs +201 -0
  267. package/assets/docs/tree/check-your-docs.doc.mjs +69 -11
  268. package/assets/docs/tree/checks.doc.mjs +32 -24
  269. package/assets/docs/tree/ci-and-containers.doc.mjs +101 -0
  270. package/assets/docs/tree/cli-command.doc.mjs +142 -0
  271. package/assets/docs/tree/cli.doc.mjs +7 -1
  272. package/assets/docs/tree/codemods.doc.mjs +17 -7
  273. package/assets/docs/tree/commands.doc.mjs +7 -1
  274. package/assets/docs/tree/component-family.doc.mjs +8 -6
  275. package/assets/docs/tree/component-imports.doc.mjs +21 -3
  276. package/assets/docs/tree/components.doc.mjs +7 -1
  277. package/assets/docs/tree/computer-agent-guide.doc.mjs +160 -0
  278. package/assets/docs/tree/computer-integrations.doc.mjs +93 -0
  279. package/assets/docs/tree/configuration.doc.mjs +8 -4
  280. package/assets/docs/tree/debug-and-gap-reports.doc.mjs +41 -22
  281. package/assets/docs/tree/define-the-theme.doc.mjs +7 -6
  282. package/assets/docs/tree/describe-the-component.doc.mjs +3 -3
  283. package/assets/docs/tree/docs.doc.mjs +9 -3
  284. package/assets/docs/tree/document-the-template.doc.mjs +1 -1
  285. package/assets/docs/tree/document-the-theme.doc.mjs +4 -4
  286. package/assets/docs/tree/export-template-assets.doc.mjs +1 -1
  287. package/assets/docs/tree/extend-or-replace.doc.mjs +19 -9
  288. package/assets/docs/{authoring.doc.mjs → tree/field-reference.doc.mjs} +4 -3
  289. package/assets/docs/tree/fonts-and-assets.doc.mjs +3 -3
  290. package/assets/docs/tree/generate-a-palette.doc.mjs +8 -6
  291. package/assets/docs/tree/global-integrations.doc.mjs +124 -0
  292. package/assets/docs/tree/global.doc.mjs +131 -0
  293. package/assets/docs/tree/grade-template-with-agent.doc.mjs +5 -5
  294. package/assets/docs/tree/help.doc.mjs +2 -2
  295. package/assets/docs/tree/host-entry.doc.mjs +128 -0
  296. package/assets/docs/tree/install-once.doc.mjs +105 -0
  297. package/assets/docs/tree/layout-spacing.doc.mjs +2 -3
  298. package/assets/docs/tree/layout.doc.mjs +6 -6
  299. package/assets/docs/tree/links.doc.mjs +7 -6
  300. package/assets/docs/tree/migration-setup.doc.mjs +3 -2
  301. package/assets/docs/tree/migration-verification.doc.mjs +0 -1
  302. package/assets/docs/tree/migration.doc.mjs +0 -1
  303. package/assets/docs/tree/monorepos.doc.mjs +84 -0
  304. package/assets/docs/tree/package-and-test.doc.mjs +1 -1
  305. package/assets/docs/tree/page-template.doc.mjs +4 -8
  306. package/assets/docs/tree/problems.doc.mjs +205 -0
  307. package/assets/docs/tree/project-global-or-both.doc.mjs +131 -0
  308. package/assets/docs/tree/publishing.doc.mjs +6 -6
  309. package/assets/docs/tree/quick-start.doc.mjs +54 -28
  310. package/assets/docs/tree/reference.doc.mjs +192 -0
  311. package/assets/docs/tree/replace-a-core-component.doc.mjs +15 -7
  312. package/assets/docs/tree/replace-a-core-template.doc.mjs +10 -12
  313. package/assets/docs/tree/scaffold.doc.mjs +1 -1
  314. package/assets/docs/tree/sections-and-placement.doc.mjs +9 -11
  315. package/assets/docs/tree/see-it-in-an-app.doc.mjs +17 -5
  316. package/assets/docs/tree/ship.doc.mjs +2 -2
  317. package/assets/docs/tree/short-and-findable.doc.mjs +9 -8
  318. package/assets/docs/tree/single-component.doc.mjs +15 -6
  319. package/assets/docs/tree/start-a-template.doc.mjs +18 -8
  320. package/assets/docs/tree/subcomponent.doc.mjs +6 -6
  321. package/assets/docs/tree/template-assets.doc.mjs +2 -2
  322. package/assets/docs/tree/template-doc-overview.doc.mjs +6 -17
  323. package/assets/docs/tree/template-fonts.doc.mjs +1 -1
  324. package/assets/docs/tree/template-grading-rubric.doc.mjs +17 -38
  325. package/assets/docs/tree/template-icons.doc.mjs +1 -1
  326. package/assets/docs/tree/template-images-media.doc.mjs +1 -1
  327. package/assets/docs/tree/template-styles.doc.mjs +2 -2
  328. package/assets/docs/tree/templates.doc.mjs +7 -1
  329. package/assets/docs/tree/test-in-an-app.doc.mjs +7 -7
  330. package/assets/docs/tree/test-template-in-app.doc.mjs +5 -5
  331. package/assets/docs/tree/themes.doc.mjs +5 -1
  332. package/assets/docs/tree/tokens-and-setup.doc.mjs +19 -15
  333. package/assets/docs/tree/tokens-data.doc.mjs +2 -2
  334. package/assets/docs/tree/troubleshooting.doc.mjs +29 -13
  335. package/assets/docs/tree/type-scale.doc.mjs +3 -3
  336. package/assets/docs/tree/uninstall.doc.mjs +109 -0
  337. package/assets/docs/tree/updates.doc.mjs +92 -0
  338. package/assets/docs/tree/upgrading.doc.mjs +6 -6
  339. package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +15 -3
  340. package/assets/docs/tree/verify-packed-template.doc.mjs +3 -3
  341. package/assets/docs/tree/versioning.doc.mjs +14 -9
  342. package/assets/docs/tree/who-answers.doc.mjs +119 -0
  343. package/assets/docs/tree/write-good-templates.doc.mjs +1 -1
  344. package/assets/docs/tree/write-the-template-file.doc.mjs +7 -6
  345. package/assets/docs/use-a-theme.doc.mjs +2 -2
  346. package/assets/docs/use-integrations.doc.mjs +120 -0
  347. package/assets/docs/working-with-ai.doc.mjs +70 -13
  348. package/assets/templates/themes/butter/butterTheme.ts +4 -2
  349. package/assets/templates/themes/stone/stoneTheme.ts +4 -2
  350. package/assets/templates/themes/y2k/y2kTheme.ts +4 -2
  351. package/assets/theme.template.ts +1 -1
  352. package/authoring/codemod/codemod.doc.mjs +7 -3
  353. package/authoring/config/config.doc.mjs +9 -6
  354. package/authoring/config/type.ts +4 -2
  355. package/authoring/debug/parse.d.mts +5 -5
  356. package/authoring/discover/discover.doc.mjs +7 -1
  357. package/authoring/doctypes/_schema.d.mts +8 -3
  358. package/authoring/doctypes/_schema.mjs +1 -0
  359. package/authoring/doctypes/base/graph-fields.doc.mjs +6 -4
  360. package/authoring/doctypes/command/command.doc.mjs +20 -12
  361. package/authoring/doctypes/command/type.ts +14 -0
  362. package/authoring/doctypes/component/component.doc.mjs +30 -27
  363. package/authoring/doctypes/component/type.ts +21 -8
  364. package/authoring/doctypes/enum/enum.doc.mjs +7 -4
  365. package/authoring/doctypes/function/function.doc.mjs +9 -8
  366. package/authoring/doctypes/hook/hook.doc.mjs +5 -4
  367. package/authoring/doctypes/hook/type.ts +4 -0
  368. package/authoring/doctypes/namespace/namespace.doc.mjs +7 -6
  369. package/authoring/doctypes/reference/reference.doc.mjs +12 -11
  370. package/authoring/doctypes/schema/schema.doc.mjs +10 -10
  371. package/authoring/doctypes/template/integration-template.doc.d.mts +11 -0
  372. package/authoring/doctypes/template/integration-template.doc.mjs +49 -0
  373. package/authoring/doctypes/template/parse.d.mts +6 -2
  374. package/authoring/doctypes/template/parse.mjs +3 -1
  375. package/authoring/doctypes/template/template.doc.mjs +3 -3
  376. package/authoring/doctypes/template/type.ts +22 -0
  377. package/authoring/doctypes/theme/theme.doc.mjs +2 -2
  378. package/authoring/gap-report/gap-report.doc.mjs +2 -2
  379. package/authoring/identity/identity.doc.mjs +3 -3
  380. package/authoring/index.d.ts +2 -0
  381. package/authoring/integration/integration.doc.mjs +30 -6
  382. package/authoring/integration/parse.test.mjs +21 -0
  383. package/authoring/integration/schema.d.mts +16 -3
  384. package/authoring/integration/schema.mjs +34 -2
  385. package/authoring/integration/type.ts +13 -1
  386. package/clients/cli/bin/astryx.mjs +11 -65
  387. package/clients/cli/cli-command.test.mjs +166 -0
  388. package/clients/cli/command-result-coverage.test.mjs +6 -5
  389. package/clients/cli/commands/blog.doc.mjs +1 -0
  390. package/clients/cli/commands/build-theme.batch-report.test.mjs +17 -3
  391. package/clients/cli/commands/build-theme.color-scheme.test.mjs +1 -1
  392. package/clients/cli/commands/build-theme.font-warning.test.mjs +3 -3
  393. package/clients/cli/commands/build-theme.path-safety.test.mjs +224 -6
  394. package/clients/cli/commands/build-theme.registry.test.mjs +5 -2
  395. package/clients/cli/commands/build.doc.mjs +1 -0
  396. package/clients/cli/commands/build.exit-codes-doc.test.mjs +4 -0
  397. package/clients/cli/commands/cli-postinstall.test.mjs +49 -1
  398. package/clients/cli/commands/component.doc.mjs +1 -0
  399. package/clients/cli/commands/{component/index.mjs → component.mjs} +37 -37
  400. package/clients/cli/commands/component.test.mjs +6 -6
  401. package/clients/cli/commands/discover.broken-integration.test.mjs +11 -7
  402. package/clients/cli/commands/discover.doc.mjs +9 -1
  403. package/clients/cli/commands/discover.mjs +144 -93
  404. package/clients/cli/commands/discover.no-source.test.mjs +448 -33
  405. package/clients/cli/commands/discover.sources.test.mjs +20 -1
  406. package/clients/cli/commands/docs.doc.mjs +3 -2
  407. package/clients/cli/commands/docs.mjs +50 -20
  408. package/clients/cli/commands/docs.test.mjs +16 -16
  409. package/clients/cli/commands/doctor-integration-components.doc.mjs +19 -4
  410. package/clients/cli/commands/doctor-integration-docs.doc.mjs +20 -5
  411. package/clients/cli/commands/doctor-integration-templates.doc.mjs +1 -0
  412. package/clients/cli/commands/doctor-integration-validate.doc.mjs +22 -2
  413. package/clients/cli/commands/doctor-integration.test.mjs +936 -3
  414. package/clients/cli/commands/doctor.doc.mjs +12 -2
  415. package/clients/cli/commands/doctor.mjs +332 -7
  416. package/clients/cli/commands/doctor.test.mjs +75 -4
  417. package/clients/cli/commands/gap-report.doc.mjs +15 -2
  418. package/clients/cli/commands/gap-report.mjs +24 -2
  419. package/clients/cli/commands/gap-report.test.mjs +79 -0
  420. package/clients/cli/commands/hook.doc.mjs +1 -0
  421. package/clients/cli/commands/{hook/index.mjs → hook.mjs} +22 -22
  422. package/clients/cli/commands/import-hint-correctness.test.mjs +1 -1
  423. package/clients/cli/commands/init.behavior.test.mjs +235 -1
  424. package/clients/cli/commands/init.doc.mjs +55 -10
  425. package/clients/cli/commands/init.flag-help.test.mjs +1 -0
  426. package/clients/cli/commands/init.mjs +2 -2
  427. package/clients/cli/commands/integration-add.doc.mjs +7 -0
  428. package/clients/cli/commands/integration-authoring.test.mjs +49 -3
  429. package/clients/cli/commands/integration-check.doc.mjs +93 -0
  430. package/clients/cli/commands/integration-check.test.mjs +489 -0
  431. package/clients/cli/commands/integration-pack.doc.mjs +8 -1
  432. package/clients/cli/commands/integration-verify.doc.mjs +8 -1
  433. package/clients/cli/commands/integration.doc.mjs +6 -2
  434. package/clients/cli/commands/integration.mjs +119 -7
  435. package/clients/cli/commands/json-contract.test.mjs +11 -2
  436. package/clients/cli/commands/manifest.doc.mjs +2 -1
  437. package/clients/cli/commands/manifest.mjs +67 -0
  438. package/clients/cli/commands/outside-project.test.mjs +874 -0
  439. package/clients/cli/commands/postinstall.doc.mjs +23 -0
  440. package/clients/cli/commands/postinstall.mjs +31 -0
  441. package/clients/cli/commands/search.doc.mjs +1 -0
  442. package/clients/cli/commands/search.mjs +16 -11
  443. package/clients/cli/commands/search.test.mjs +429 -296
  444. package/clients/cli/commands/setup-nudge.test.mjs +180 -2
  445. package/clients/cli/commands/swizzle.doc.mjs +7 -0
  446. package/clients/cli/commands/swizzle.mjs +5 -3
  447. package/clients/cli/commands/swizzle.routing.test.mjs +19 -2
  448. package/clients/cli/commands/template.doc.mjs +1 -0
  449. package/clients/cli/commands/text-json-parity.test.mjs +5 -0
  450. package/clients/cli/commands/theme-add-lifecycle.test.mjs +3 -3
  451. package/clients/cli/commands/theme-add.doc.mjs +8 -1
  452. package/clients/cli/commands/theme-build.doc.mjs +3 -2
  453. package/clients/cli/commands/theme-eject.doc.mjs +1 -0
  454. package/clients/cli/commands/theme-list.behavior.test.mjs +6 -0
  455. package/clients/cli/commands/theme-list.doc.mjs +1 -0
  456. package/clients/cli/commands/theme-palette-generate.doc.mjs +1 -0
  457. package/clients/cli/commands/theme-remove.doc.mjs +7 -0
  458. package/clients/cli/commands/theme-targets.doc.mjs +1 -0
  459. package/clients/cli/commands/theme-template.doc.mjs +1 -0
  460. package/clients/cli/commands/theme-use.doc.mjs +7 -0
  461. package/clients/cli/commands/{build-theme.mjs → theme.mjs} +27 -14
  462. package/clients/cli/commands/upgrade.config-ordering.test.mjs +2 -1
  463. package/clients/cli/commands/upgrade.doc.mjs +26 -3
  464. package/clients/cli/commands/upgrade.file-protection.test.mjs +3 -2
  465. package/clients/cli/commands/upgrade.hook-output.test.mjs +2 -1
  466. package/clients/cli/commands/upgrade.mjs +9 -2
  467. package/clients/cli/commands/upgrade.older-cli.test.mjs +160 -0
  468. package/clients/cli/commands/upgrade.subfolder-path.test.mjs +97 -0
  469. package/clients/cli/index.mjs +156 -100
  470. package/clients/cli/lib/cli-error.mjs +4 -1
  471. package/clients/cli/lib/component-format.mjs +1 -1
  472. package/clients/cli/lib/component-format.test.mjs +1 -1
  473. package/clients/cli/lib/define-command.mjs +14 -9
  474. package/clients/cli/lib/exit-codes.test.mjs +5 -0
  475. package/clients/cli/lib/json-shim.mjs +3 -1
  476. package/clients/cli/lib/manifest.d.ts +9 -0
  477. package/clients/cli/lib/manifest.mjs +17 -1
  478. package/clients/cli/lib/manifest.test.mjs +37 -0
  479. package/clients/cli/lib/welcome-box.mjs +50 -0
  480. package/clients/cli/run.mjs +108 -0
  481. package/foundation/agent-docs/agent-doc-state.d.mts +67 -13
  482. package/foundation/agent-docs/agent-doc-state.mjs +123 -14
  483. package/foundation/agent-docs/agent-docs.d.mts +104 -15
  484. package/foundation/agent-docs/agent-docs.mjs +1028 -114
  485. package/foundation/agent-docs/agent-docs.path-safety.test.mjs +1239 -17
  486. package/foundation/agent-docs/agent-docs.test.mjs +683 -39
  487. package/foundation/agent-docs/computer-agent-text.d.mts +170 -0
  488. package/foundation/agent-docs/computer-agent-text.mjs +750 -0
  489. package/foundation/agent-docs/computer-agent-text.test.mjs +699 -0
  490. package/foundation/config/project.d.mts +35 -3
  491. package/foundation/config/project.mjs +243 -26
  492. package/foundation/config/project.test.mjs +122 -0
  493. package/foundation/config/theme-state.mjs +5 -4
  494. package/foundation/discovery/authoring-self-docs.d.mts +7 -0
  495. package/foundation/discovery/authoring-self-docs.mjs +17 -4
  496. package/foundation/discovery/authoring-self-docs.test.mjs +15 -3
  497. package/foundation/discovery/authoring-surface.d.mts +1 -1
  498. package/foundation/discovery/authoring-surface.mjs +2 -2
  499. package/foundation/discovery/authoring-surface.test.mjs +1 -1
  500. package/foundation/discovery/cli-self-docs.d.mts +2 -1
  501. package/foundation/discovery/cli-self-docs.mjs +22 -4
  502. package/foundation/discovery/cli-self-docs.test.mjs +3 -2
  503. package/foundation/discovery/component-discovery.d.mts +227 -10
  504. package/foundation/discovery/component-discovery.export-names.test.mjs +760 -0
  505. package/foundation/discovery/component-discovery.mjs +940 -57
  506. package/foundation/discovery/component-discovery.test.mjs +96 -0
  507. package/foundation/discovery/component-loader.d.mts +1 -1
  508. package/foundation/discovery/component-replacement.mjs +20 -1
  509. package/foundation/discovery/component-replacement.test.mjs +40 -0
  510. package/foundation/discovery/copied-passages.d.mts +216 -0
  511. package/foundation/discovery/copied-passages.mjs +720 -0
  512. package/foundation/discovery/copied-passages.test.mjs +124 -0
  513. package/foundation/discovery/doc-consistency.d.mts +34 -0
  514. package/foundation/discovery/doc-consistency.mjs +513 -0
  515. package/foundation/discovery/doc-consistency.test.mjs +347 -0
  516. package/foundation/discovery/doc-fluff.d.mts +112 -0
  517. package/foundation/discovery/doc-fluff.mjs +328 -0
  518. package/foundation/discovery/doc-fluff.test.mjs +142 -0
  519. package/foundation/discovery/doc-types.d.mts +157 -0
  520. package/foundation/discovery/doc-types.mjs +1231 -0
  521. package/foundation/discovery/doc-types.test.mjs +744 -0
  522. package/foundation/discovery/docs-discovery.d.mts +88 -0
  523. package/foundation/discovery/docs-discovery.mjs +97 -3
  524. package/foundation/discovery/integration-doc-files.d.mts +22 -0
  525. package/foundation/discovery/integration-doc-files.mjs +231 -0
  526. package/foundation/discovery/integration-doc-files.test.mjs +246 -0
  527. package/foundation/discovery/module-exports.d.mts +68 -0
  528. package/foundation/discovery/module-exports.mjs +609 -31
  529. package/foundation/discovery/module-exports.test.mjs +172 -0
  530. package/foundation/discovery/package-tsconfig.d.mts +14 -0
  531. package/foundation/discovery/package-tsconfig.mjs +228 -0
  532. package/foundation/discovery/package-tsconfig.test.mjs +210 -0
  533. package/foundation/discovery/project-typescript.d.mts +32 -0
  534. package/foundation/discovery/project-typescript.mjs +89 -0
  535. package/foundation/discovery/prop-drift.d.mts +297 -0
  536. package/foundation/discovery/prop-drift.mjs +2428 -0
  537. package/foundation/discovery/prop-drift.test.mjs +2337 -0
  538. package/foundation/discovery/runner-allowlist.d.mts +38 -0
  539. package/foundation/discovery/runner-allowlist.mjs +51 -0
  540. package/foundation/discovery/runner-commands.d.mts +77 -0
  541. package/foundation/discovery/runner-commands.mjs +294 -0
  542. package/foundation/discovery/runner-commands.test.mjs +128 -0
  543. package/foundation/discovery/stray-pointers.d.mts +52 -0
  544. package/foundation/discovery/stray-pointers.mjs +293 -0
  545. package/foundation/discovery/stray-pointers.test.mjs +86 -0
  546. package/foundation/discovery/template-adapter.mjs +92 -6
  547. package/foundation/discovery/template-needs.mjs +6 -46
  548. package/foundation/discovery/theme-discovery.mjs +147 -3
  549. package/foundation/discovery/theme-imports.mjs +8 -4
  550. package/foundation/discovery/theme-imports.test.mjs +1 -1
  551. package/foundation/discovery/type-match.d.mts +53 -0
  552. package/foundation/discovery/type-match.mjs +660 -0
  553. package/foundation/discovery/type-match.test.mjs +169 -0
  554. package/foundation/discovery/type-text.d.mts +68 -0
  555. package/foundation/discovery/type-text.mjs +658 -0
  556. package/foundation/discovery/type-text.test.mjs +196 -0
  557. package/foundation/discovery/unresolved-references.d.mts +62 -0
  558. package/foundation/discovery/unresolved-references.mjs +790 -0
  559. package/foundation/discovery/unresolved-references.test.mjs +567 -0
  560. package/foundation/doc-compiler/doc-loads.test.mjs +84 -10
  561. package/foundation/doc-compiler/inputs.mjs +27 -1
  562. package/foundation/doc-compiler/inputs.test.mjs +0 -1
  563. package/foundation/doc-compiler/links.d.mts +41 -5
  564. package/foundation/doc-compiler/links.mjs +167 -28
  565. package/foundation/doc-compiler/links.test.mjs +97 -4
  566. package/foundation/doc-compiler/read.d.mts +1 -1
  567. package/foundation/doc-compiler/read.mjs +59 -1
  568. package/foundation/doc-compiler/read.test.mjs +11 -2
  569. package/foundation/doc-compiler/split-topic-keywords.test.mjs +3 -2
  570. package/foundation/doc-compiler/tree.d.mts +32 -6
  571. package/foundation/doc-compiler/tree.mjs +107 -44
  572. package/foundation/doc-compiler/tree.test.mjs +14 -7
  573. package/foundation/env/cli-command.d.mts +83 -0
  574. package/foundation/env/cli-command.mjs +152 -0
  575. package/foundation/env/cli-command.test.mjs +129 -0
  576. package/foundation/env/node-version.test.mjs +15 -3
  577. package/foundation/env/package-manager.d.mts +93 -9
  578. package/foundation/env/package-manager.global.test.mjs +66 -0
  579. package/foundation/env/package-manager.mjs +184 -13
  580. package/foundation/env/package-manager.test.mjs +341 -15
  581. package/foundation/env/project-scope.d.mts +9 -0
  582. package/foundation/env/project-scope.mjs +33 -0
  583. package/foundation/env/search-path.d.mts +42 -0
  584. package/foundation/env/search-path.mjs +205 -0
  585. package/foundation/env/search-path.test.mjs +122 -0
  586. package/foundation/env/user-dirs.d.mts +39 -0
  587. package/foundation/env/user-dirs.mjs +93 -0
  588. package/foundation/env/user-dirs.test.mjs +72 -0
  589. package/foundation/env/user-settings.d.mts +46 -0
  590. package/foundation/env/user-settings.mjs +106 -0
  591. package/foundation/env/user-settings.test.mjs +64 -0
  592. package/foundation/fs/file-protection.mjs +15 -1
  593. package/foundation/fs/module-loader.d.mts +2 -1
  594. package/foundation/fs/module-loader.mjs +60 -4
  595. package/foundation/fs/module-loader.stdout.test.mjs +3 -2
  596. package/foundation/fs/module-loader.test.mjs +194 -0
  597. package/foundation/fs/path-compare.d.mts +33 -0
  598. package/foundation/fs/path-compare.mjs +73 -0
  599. package/foundation/fs/paths.d.mts +122 -4
  600. package/foundation/fs/paths.mjs +675 -29
  601. package/foundation/fs/paths.test.mjs +439 -54
  602. package/foundation/fs/read-record.d.mts +18 -0
  603. package/foundation/fs/read-record.mjs +131 -0
  604. package/foundation/fs/read-record.test.mjs +77 -0
  605. package/foundation/integrations/autolink.d.mts +2 -1
  606. package/foundation/integrations/autolink.mjs +4 -13
  607. package/foundation/integrations/cli-peer-version.d.mts +36 -0
  608. package/foundation/integrations/cli-peer-version.mjs +132 -0
  609. package/foundation/integrations/cli-peer-version.test.mjs +354 -0
  610. package/foundation/integrations/computer-integrations.d.mts +81 -0
  611. package/foundation/integrations/computer-integrations.mjs +345 -0
  612. package/foundation/integrations/computer-integrations.test.mjs +292 -0
  613. package/foundation/integrations/contribution-inventory.d.mts +26 -20
  614. package/foundation/integrations/contribution-inventory.mjs +97 -8
  615. package/foundation/integrations/exports-subpaths.d.mts +57 -0
  616. package/foundation/integrations/exports-subpaths.mjs +113 -0
  617. package/foundation/integrations/integration-warnings.mjs +3 -2
  618. package/foundation/integrations/integration-warnings.test.mjs +8 -9
  619. package/foundation/integrations/integrations.d.mts +51 -1
  620. package/foundation/integrations/integrations.mjs +106 -4
  621. package/foundation/integrations/issue.ts +6 -0
  622. package/foundation/integrations/public-imports.d.mts +118 -0
  623. package/foundation/integrations/public-imports.mjs +1769 -0
  624. package/foundation/integrations/public-imports.test.mjs +1793 -0
  625. package/foundation/integrations/validate-contributions.d.mts +117 -0
  626. package/foundation/integrations/validate-contributions.mjs +1209 -14
  627. package/foundation/release/agent-text-worker.d.mts +4 -0
  628. package/foundation/release/agent-text-worker.mjs +52 -0
  629. package/foundation/release/cache.d.mts +310 -0
  630. package/foundation/release/cache.mjs +1216 -0
  631. package/foundation/release/cache.test.mjs +923 -0
  632. package/foundation/release/choose.d.mts +207 -0
  633. package/foundation/release/choose.mjs +677 -0
  634. package/foundation/release/choose.test.mjs +427 -0
  635. package/foundation/release/core-worker.d.mts +4 -0
  636. package/foundation/release/core-worker.mjs +38 -0
  637. package/foundation/release/global-state.d.mts +263 -0
  638. package/foundation/release/global-state.mjs +474 -0
  639. package/foundation/release/global-state.test.mjs +381 -0
  640. package/foundation/release/handoff.d.mts +79 -0
  641. package/foundation/release/handoff.e2e.test.mjs +278 -0
  642. package/foundation/release/handoff.mjs +524 -0
  643. package/foundation/release/lock-lease.d.mts +23 -0
  644. package/foundation/release/lock-lease.mjs +80 -0
  645. package/foundation/release/notices.d.mts +307 -0
  646. package/foundation/release/notices.mjs +707 -0
  647. package/foundation/release/notices.test.mjs +586 -0
  648. package/foundation/release/npm-guard.d.mts +4 -0
  649. package/foundation/release/npm-guard.mjs +126 -0
  650. package/foundation/release/project.d.mts +141 -0
  651. package/foundation/release/project.mjs +466 -0
  652. package/foundation/release/project.test.mjs +288 -0
  653. package/foundation/release/refresh-worker.d.mts +4 -0
  654. package/foundation/release/refresh-worker.mjs +27 -0
  655. package/foundation/release/run-context.d.mts +47 -0
  656. package/foundation/release/run-context.mjs +163 -0
  657. package/foundation/release/update-notice-env.d.mts +28 -0
  658. package/foundation/release/update-notice-env.mjs +39 -0
  659. package/foundation/response/base.d.ts +26 -3
  660. package/foundation/response/error-codes.d.mts +3 -1
  661. package/foundation/response/error-codes.doc.mjs +12 -0
  662. package/foundation/response/error-codes.mjs +17 -0
  663. package/foundation/response/error-codes.test.mjs +2 -0
  664. package/foundation/response/json-contract.test.mjs +7 -2
  665. package/foundation/response/json.mjs +4 -1
  666. package/foundation/response/response-types.doc.mjs +22 -11
  667. package/foundation/response/response-types.doc.test.mjs +46 -0
  668. package/foundation/response/response.doc.mjs +3 -3
  669. package/foundation/route/route.d.mts +285 -0
  670. package/foundation/route/route.mjs +324 -0
  671. package/foundation/route/route.test.mjs +153 -0
  672. package/foundation/runtime/hosted-runtime.test.mjs +403 -0
  673. package/foundation/runtime/locked-versions.d.mts +32 -0
  674. package/foundation/runtime/locked-versions.mjs +65 -0
  675. package/foundation/runtime/package-root.d.mts +91 -0
  676. package/foundation/runtime/package-root.mjs +174 -0
  677. package/foundation/runtime/package-set.d.mts +62 -0
  678. package/foundation/runtime/package-set.mjs +130 -0
  679. package/foundation/runtime/package-set.test.mjs +432 -0
  680. package/foundation/text/string-utils.d.mts +2 -1
  681. package/foundation/text/string-utils.mjs +16 -5
  682. package/foundation/text/suggest-order.d.mts +72 -0
  683. package/foundation/text/suggest-order.mjs +363 -0
  684. package/foundation/text/suggest-order.test.mjs +259 -0
  685. package/foundation/xle/browser-imports.test.mjs +74 -0
  686. package/foundation/xle/registry.mjs +4 -0
  687. package/foundation/xle/registry.test.mjs +28 -0
  688. package/host/door-conformance.test.mjs +274 -0
  689. package/host/index.d.ts +116 -0
  690. package/host/index.mjs +295 -0
  691. package/host/index.test.mjs +302 -0
  692. package/package.json +16 -11
  693. package/scripts/postinstall.mjs +24 -5
  694. package/assets/docs/tree/integrations.doc.mjs +0 -40
@@ -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
  *
@@ -24,9 +28,18 @@ import * as fs from 'node:fs';
24
28
  import * as path from 'node:path';
25
29
 
26
30
  import {MIN_NODE_VERSION, isNodeVersionSupported} from '../../foundation/env/node-version.mjs';
27
- 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';
31
+ import {
32
+ AGENT_DOC_PATHS,
33
+ holdsAstryxBlock,
34
+ } from '../../foundation/agent-docs/agent-doc-state.mjs';
35
+ import {
36
+ agentDocsTargetDir,
37
+ assertAgentDocsTarget,
38
+ resolveAgentDocsRoot,
39
+ } from '../../foundation/agent-docs/agent-docs.mjs';
40
+ import {CLI_ROOT, findProjectCoreDir, findInstalledPackage} from '../../foundation/fs/paths.mjs';
41
+ import {explainPackageManager, formatCliCommand, getCliInvocation, guessCliInvocation} from '../../foundation/env/package-manager.mjs';
42
+ import {chooseDeclaredCliCommand, declaredCliCommandsOf} from '../../foundation/env/cli-command.mjs';
30
43
  import {
31
44
  findConfigPath,
32
45
  Project,
@@ -52,6 +65,9 @@ import {typedEdges} from '../docs/node/node.mjs';
52
65
  import {detailView, indexView} from '../../foundation/doc-compiler/lenses.mjs';
53
66
  import {semverCompare, isValidSemver, satisfiesRange} from '../../foundation/env/semver.mjs';
54
67
  import {checkAppThemes, checkThemes} from './theme-checks.mjs';
68
+ import {availableLine, RUN_FROM_PROJECT, upgradeLine} from '../../foundation/release/notices.mjs';
69
+ import {COMPUTER_COMMAND, COMPUTER_INSTALL_STEP} from '../../foundation/agent-docs/computer-agent-text.mjs';
70
+ import {alignCommand, addCliCommandFor, computerAgentTextState, installCommandFor, insideProjectElsewhere, projectCoreVersion, projectHasOwnCli, computerIntegrationsOutside, computerIntegrationsToAdd, displayHomePath, outsideProject, releaseFacts} from './_adapter.mjs';
55
71
 
56
72
  /**
57
73
  * @typedef {'pass'|'warn'|'fail'|'info'} DoctorStatus
@@ -71,6 +87,10 @@ import {checkAppThemes, checkThemes} from './theme-checks.mjs';
71
87
  * @property {string} cwd - Directory to diagnose.
72
88
  * @property {string} nodeVersion - Running Node version.
73
89
  * @property {string|null} coreDir - Resolved core package directory, or null.
90
+ * @property {boolean} [ownCli] - Whether the project installs its own CLI;
91
+ * false means the copy that started the run answered it (spec AST-081 FR2).
92
+ * @property {string|null} [projectCore] - The Core release the project names
93
+ * (installed, else locked, else declared), as the daily hint reads it.
74
94
  * @property {string|null} configPath - Resolved astryx.config.mjs path, or null.
75
95
  * @property {import('../../foundation/integrations/integrations.mjs').LoadedIntegration[]|null} [integrations]
76
96
  * Every integration the project loaded, or null when the project could not be
@@ -229,7 +249,12 @@ export function checkCoreInstalled(ctx) {
229
249
  * @returns {DoctorCheck}
230
250
  */
231
251
  export function checkVersionAlignment(ctx) {
232
- const coreVersion = pkgVersion(ctx.coreDir);
252
+ // A project with no CLI of its own and no Core installed yet (a fresh
253
+ // clone) is compared by the Core it locks or declares, as the daily hint
254
+ // compares it (spec AST-081 FR2).
255
+ const coreVersion =
256
+ pkgVersion(ctx.coreDir) ??
257
+ (ctx.ownCli === false ? (ctx.projectCore ?? null) : null);
233
258
  const cliPkg = readPkg(path.join(CLI_ROOT, 'package.json'));
234
259
  const cliVersion = cliPkg?.version ?? null;
235
260
 
@@ -258,28 +283,67 @@ export function checkVersionAlignment(ctx) {
258
283
  };
259
284
  }
260
285
 
261
- const [coreMajor, coreMinor] = coreVersion.split('.').map(Number);
262
- const [cliMajor, cliMinor] = cliVersion.split('.').map(Number);
263
- const drift = coreMajor !== cliMajor || coreMinor !== cliMinor;
264
-
286
+ // A project with no CLI of its own is answered by the copy that started
287
+ // the run (spec AST-081 FR2): say so, and how to add the CLI at Core's
288
+ // version.
289
+ if (ctx.ownCli === false) {
290
+ // The same comparison as the daily hint: the CLI against the Core the
291
+ // run reads (spec AST-081 FR2). The command adds the CLI in the project
292
+ // spec:AST-040 FR9 defines; from a folder inside it whose package.json
293
+ // is not the project's, or in no project, the fix says how to reach one.
294
+ const add = addCliCommandFor(ctx.cwd, coreVersion);
295
+ const matches = coreVersion === cliVersion;
296
+ const fix = add
297
+ ? `${matches ? 'To pin it for the team and CI' : 'To match'}, run: ${add.command}.`
298
+ : matches
299
+ ? null
300
+ : NO_PROJECT_FIX;
301
+ return {
302
+ id: 'version-alignment',
303
+ label: '@astryxdesign/core <-> @astryxdesign/cli alignment',
304
+ status: matches ? 'info' : 'warn',
305
+ message: matches
306
+ ? `This project has no Astryx CLI installed; CLI ${cliVersion}, the one you started, answered and matches its Core.`
307
+ : `This project has no Astryx CLI installed; CLI ${cliVersion}, the one you started, answered while its Core is ${coreVersion}.`,
308
+ ...(fix ? {fix} : {}),
309
+ };
310
+ }
311
+ // The project's own CLI answers this project whatever its version
312
+ // (spec AST-081 FR2), so any difference means templates, docs, and agent
313
+ // text come from one release and components from another.
314
+ const drift = coreVersion !== cliVersion;
265
315
  return {
266
316
  id: 'version-alignment',
267
317
  label: '@astryxdesign/core <-> @astryxdesign/cli alignment',
268
318
  status: drift ? 'warn' : 'pass',
269
319
  message: drift
270
- ? `@astryxdesign/core v${coreVersion} drifts from @astryxdesign/cli v${cliVersion} (major/minor mismatch).`
320
+ ? `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
321
  : `@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
- : {}),
322
+ ...(drift ? alignFix(ctx.cwd, cliVersion, coreVersion) : {}),
280
323
  };
281
324
  }
282
325
 
326
+ /**
327
+ * The fix for a CLI and Core on different releases, when a command fits.
328
+ * @param {string} cwd
329
+ * @param {string} cli
330
+ * @param {string} core
331
+ * @returns {{fix?: string}}
332
+ */
333
+ function alignFix(cwd, cli, core) {
334
+ // From a folder inside a project whose package.json is not the project's,
335
+ // aligning the project would leave this warning in place, so no command
336
+ // fits there (alignCommand returns none): point at the project instead.
337
+ const command = alignCommand(cwd, cli, core);
338
+ return {fix: command ? `To align them, run: ${command}.` : NO_PROJECT_FIX};
339
+ }
340
+
341
+ /**
342
+ * The fix where no install command fits: the folder's package.json is not an
343
+ * Astryx project's (spec AST-040 FR9), and adding there would make it one.
344
+ */
345
+ const NO_PROJECT_FIX = RUN_FROM_PROJECT;
346
+
283
347
  /**
284
348
  * Check 5 — astryx.config.mjs (if present) loads and has a valid shape.
285
349
  * @param {DoctorContext} ctx
@@ -331,7 +395,7 @@ export async function checkConfig(ctx) {
331
395
  label: 'astryx.config.mjs',
332
396
  status: 'fail',
333
397
  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.',
398
+ fix: `Fix what the message names. \`${formatCliCommand('docs authoring config', ctx.cwd)}\` lists every field astryx.config accepts.`,
335
399
  };
336
400
  }
337
401
  return {
@@ -351,6 +415,26 @@ export async function checkConfig(ctx) {
351
415
  }
352
416
  }
353
417
 
418
+ /**
419
+ * Why no integration is implicitly linked, from what actually loaded. A
420
+ * configured integration is named in astryx.config; the package's own manifest
421
+ * loads as itself (`__local`). Only the first may be described as named in
422
+ * astryx.config: a package with no config that loads its own manifest has no
423
+ * config file to name it.
424
+ *
425
+ * @param {Array<{name?: string, __local?: boolean}>} integrations loaded, none
426
+ * autolinked and none that failed to load
427
+ * @returns {string}
428
+ */
429
+ function describeNoImplicit(integrations) {
430
+ const own = integrations.filter(integration => integration.__local);
431
+ const configured = integrations.length - own.length;
432
+ const ownNames = own.map(integration => integration.name ?? 'this package').join(', ');
433
+ if (own.length === 0) return 'None — every loaded integration is named in astryx.config.';
434
+ if (configured === 0) return `None — the only loaded integration is this package's own manifest (${ownNames}).`;
435
+ return `None — every loaded integration is named in astryx.config or is this package's own manifest (${ownNames}).`;
436
+ }
437
+
354
438
  /**
355
439
  * Check 6 — integrations that are loaded without an astryx.config entry.
356
440
  *
@@ -395,14 +479,18 @@ export function checkImplicitIntegrations(ctx) {
395
479
  const unreadable = describeUnreadableManifests(ctx.autolinkFailures ?? []);
396
480
 
397
481
  if (implicit.length === 0) {
482
+ // A record that carries `__loadError` (the package's own manifest, or a
483
+ // configured one, that failed to load) loaded nothing, so it is not named
484
+ // as loaded beside the line reporting that it could not be loaded.
485
+ const loaded = ctx.integrations.filter(integration => !integration.__loadError);
398
486
  return {
399
487
  id,
400
488
  label,
401
489
  status: 'info',
402
490
  message:
403
- (ctx.integrations.length > 0
404
- ? 'None — every loaded integration is named in astryx.config.'
405
- : unreadable
491
+ (loaded.length > 0
492
+ ? describeNoImplicit(loaded)
493
+ : ctx.integrations.length > 0 || unreadable
406
494
  ? 'None loaded.'
407
495
  : 'None — no installed dependency ships an astryx.integration.* manifest.') +
408
496
  unreadable,
@@ -476,7 +564,7 @@ function describeUnreadableManifests(failures) {
476
564
  return (
477
565
  ` ${failures.length} installed ${one ? 'dependency ships' : 'dependencies ship'} ` +
478
566
  `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.`
567
+ `nothing: ${listed}. Run \`${formatCliCommand('doctor integration validate <package>')}\` for details.`
480
568
  );
481
569
  }
482
570
 
@@ -550,7 +638,7 @@ export function checkConfiguredIntegrations(ctx) {
550
638
  `${withdrawn.length} of ${configured.length} integration${plural} named in astryx.config could not be loaded, ` +
551
639
  `so ${withdrawn.length === 1 ? 'it contributes' : 'they contribute'} nothing: ${listed}.`,
552
640
  fix:
553
- 'Install each one, fix its astryx.integration.* manifest (`astryx doctor integration validate <package>` says what is wrong), ' +
641
+ `Install each one, fix its astryx.integration.* manifest (\`${formatCliCommand('doctor integration validate <package>', ctx.cwd)}\` says what is wrong), ` +
554
642
  'or remove it from `integrations` in astryx.config.',
555
643
  };
556
644
  }
@@ -562,6 +650,21 @@ export function checkConfiguredIntegrations(ctx) {
562
650
  };
563
651
  }
564
652
 
653
+ /**
654
+ * Why init writes no agent docs for `cwd`, or null when it does: the folder
655
+ * it would write to is the home folder or the top of a settings folder in it.
656
+ * @param {string} cwd
657
+ * @returns {string | null}
658
+ */
659
+ function agentDocsRefusal(cwd) {
660
+ try {
661
+ assertAgentDocsTarget(agentDocsTargetDir(cwd));
662
+ return null;
663
+ } catch (err) {
664
+ return err instanceof Error && err.name === 'AgentDocsHomeError' ? err.message : null;
665
+ }
666
+ }
667
+
565
668
  /**
566
669
  * Check 7 — agent docs exist and contain the Astryx section markers.
567
670
  * @param {DoctorContext} ctx
@@ -569,8 +672,15 @@ export function checkConfiguredIntegrations(ctx) {
569
672
  */
570
673
  export function checkAgentDocs(ctx) {
571
674
  // Every file init can write (one shared list), looked for in the working
572
- // directory and in the project root: run from src/, the docs sit at the root.
573
- const dirs = [...new Set([ctx.cwd, projectRootOf(ctx)])];
675
+ // directory, the project root, and the folder init writes them to: run from
676
+ // src/ or a nested package, the docs sit at the root.
677
+ const dirs = [
678
+ ...new Set(
679
+ [ctx.cwd, projectRootOf(ctx), resolveAgentDocsRoot(ctx.cwd) ?? ctx.cwd].map(dir =>
680
+ path.resolve(dir),
681
+ ),
682
+ ),
683
+ ];
574
684
  const present = dirs.flatMap(dir =>
575
685
  AGENT_DOC_PATHS.map(rel => path.join(dir, rel)).filter(abs =>
576
686
  fs.existsSync(abs),
@@ -578,6 +688,15 @@ export function checkAgentDocs(ctx) {
578
688
  );
579
689
  /** @param {string} abs */
580
690
  const shown = abs => path.relative(ctx.cwd, abs) || path.basename(abs);
691
+ // In the home folder, or at the top of a settings folder in it, init
692
+ // writes no agent docs (INV31), so the fix is moving the project or a hand
693
+ // edit, never init.
694
+ const refusal = agentDocsRefusal(ctx.cwd);
695
+ /** @param {string} initFix */
696
+ const fixFor = initFix =>
697
+ refusal
698
+ ? `${refusal} Move the project into a folder of its own, or edit the Astryx block in its agent file by hand.`
699
+ : initFix;
581
700
 
582
701
  if (present.length === 0) {
583
702
  return {
@@ -585,29 +704,64 @@ export function checkAgentDocs(ctx) {
585
704
  label: 'AI agent docs',
586
705
  status: 'info',
587
706
  message: `No agent docs found: looked for ${AGENT_DOC_PATHS.join(', ')} in ${dirs.map(dir => path.relative(ctx.cwd, dir) || '.').join(' and ')}.`,
588
- fix: `Generate agent docs with \`${getCliInvocation(ctx.cwd)} init --features agents\`.`,
707
+ fix: fixFor(
708
+ `Generate agent docs with \`${getCliInvocation(ctx.cwd)} init --features agents\`.`,
709
+ ),
589
710
  };
590
711
  }
591
712
 
592
- const withMarkers = present.filter(abs => {
713
+ /** @param {string} abs */
714
+ const read = abs => {
593
715
  try {
594
- const content = fs.readFileSync(abs, 'utf-8');
595
- return (
596
- (content.includes('<!-- ASTRYX:START -->') || content.includes('<!-- XDS:START -->')) &&
597
- (content.includes('<!-- ASTRYX:END -->') || content.includes('<!-- XDS:END -->'))
598
- );
716
+ return fs.readFileSync(abs, 'utf-8');
599
717
  } catch {
600
- return false;
718
+ return '';
601
719
  }
720
+ };
721
+ const withMarkers = present.filter(abs => {
722
+ const content = read(abs);
723
+ return (
724
+ (content.includes('<!-- ASTRYX:START -->') || content.includes('<!-- XDS:START -->')) &&
725
+ (content.includes('<!-- ASTRYX:END -->') || content.includes('<!-- XDS:END -->')) &&
726
+ holdsAstryxBlock(content)
727
+ );
602
728
  });
603
729
 
730
+ // A managed block that only imports another file (init's GEMINI.md import
731
+ // of AGENTS.md) is broken when that file holds no block: the agents that
732
+ // read either file get no Astryx docs.
733
+ const dangling = present.flatMap(abs => {
734
+ const match = /<!-- ASTRYX:START -->\s*@(\S+)\s*<!-- ASTRYX:END -->/.exec(read(abs));
735
+ if (match == null) return [];
736
+ const target = path.resolve(path.dirname(abs), match[1]);
737
+ return holdsAstryxBlock(read(target)) ? [] : [{abs, target}];
738
+ });
739
+ if (dangling.length > 0) {
740
+ return {
741
+ id: 'agent-docs',
742
+ label: 'AI agent docs',
743
+ status: 'fail',
744
+ message: dangling
745
+ .map(
746
+ ({abs, target}) =>
747
+ `${shown(abs)} imports ${shown(target)}, which holds no Astryx block, so agents that read them get no Astryx docs.`,
748
+ )
749
+ .join(' '),
750
+ fix: fixFor(
751
+ `Write the block again with \`${getCliInvocation(ctx.cwd)} init --features agents\`.`,
752
+ ),
753
+ };
754
+ }
755
+
604
756
  if (withMarkers.length === 0) {
605
757
  return {
606
758
  id: 'agent-docs',
607
759
  label: 'AI agent docs',
608
760
  status: 'warn',
609
761
  message: `Agent docs present (${present.map(shown).join(', ')}) but no Astryx section markers found.`,
610
- fix: `Add the Astryx section to your agent docs with \`${getCliInvocation(ctx.cwd)} init --features agents\`.`,
762
+ fix: fixFor(
763
+ `Add the Astryx section to your agent docs with \`${getCliInvocation(ctx.cwd)} init --features agents\`.`,
764
+ ),
611
765
  };
612
766
  }
613
767
 
@@ -748,7 +902,7 @@ export function checkIntegrationIssues(ctx) {
748
902
  label: 'Integration contributions',
749
903
  status: 'warn',
750
904
  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.',
905
+ fix: `Run \`${formatCliCommand('doctor integration', ctx.cwd)}\` for package-specific diagnostics and resolve cross-package precedence in astryx.config.`,
752
906
  };
753
907
  }
754
908
 
@@ -926,16 +1080,20 @@ function nameTypes(names) {
926
1080
  }
927
1081
 
928
1082
  /**
929
- * The section keys `astryx docs authoring --index` lists, read the way that
930
- * command reads them.
1083
+ * The section keys the field reference lists (`astryx docs
1084
+ * authoring/field-reference --index`), read the way that command reads them.
931
1085
  * @returns {Promise<{keys: Set<string>} | {keys: null, error: string}>}
932
1086
  */
933
1087
  async function authoringTopicKeys() {
934
1088
  try {
1089
+ const {AUTHORING_REFERENCE_ROUTE} =
1090
+ await import('../../foundation/discovery/authoring-self-docs.mjs');
935
1091
  const catalog = DocsCatalog.fromBuiltins();
936
- const entry = catalog.resolve('authoring');
937
- if (!entry) return {keys: null, error: 'it is not a built-in topic'};
938
- const index = indexView(await lowerTopic(catalog, entry));
1092
+ const node = (await projectTree(catalog)).get(AUTHORING_REFERENCE_ROUTE);
1093
+ if (!node?.ref?.topicFile) {
1094
+ return {keys: null, error: 'the docs tree has no field reference'};
1095
+ }
1096
+ const index = indexView(await lowerTopic(catalog, guideEntry(node)));
939
1097
  return {keys: new Set(index.sections.map(section => section.id))};
940
1098
  } catch (err) {
941
1099
  return {
@@ -954,7 +1112,7 @@ function surfaceProblems(surface) {
954
1112
  const problems = [];
955
1113
  if (surface.types === 0 && surface.untraced.length === 0) {
956
1114
  problems.push(
957
- '@astryxdesign/cli/authoring exports no types, so nothing was compared with `astryx docs authoring`',
1115
+ `@astryxdesign/cli/authoring exports no types, so nothing was compared with \`${formatCliCommand('docs authoring/field-reference')}\``,
958
1116
  );
959
1117
  }
960
1118
  for (const {module, names, reason, source, key} of surface.unreadable) {
@@ -967,7 +1125,7 @@ function surfaceProblems(surface) {
967
1125
  ? `${source} does not load`
968
1126
  : `${source} renders section "${key}", which the topic's index does not list`;
969
1127
  problems.push(
970
- `${nameTypes(names)} from ${module} ${names.length === 1 ? 'has' : 'have'} no doc in \`astryx docs authoring\`: ${why}`,
1128
+ `${nameTypes(names)} from ${module} ${names.length === 1 ? 'has' : 'have'} no doc in \`${formatCliCommand('docs authoring/field-reference')}\`: ${why}`,
971
1129
  );
972
1130
  }
973
1131
  for (const {name, reason} of surface.untraced) {
@@ -1008,7 +1166,7 @@ export async function checkAuthoringDocs(_ctx, options = {}) {
1008
1166
  const audit = await auditAuthoringSelfDocs({root, sources});
1009
1167
  const problems = [
1010
1168
  ...audit.unreachable.map(
1011
- source => `${source} is not in \`astryx docs authoring\``,
1169
+ source => `${source} is not in \`${formatCliCommand('docs authoring/field-reference')}\``,
1012
1170
  ),
1013
1171
  ...audit.failed.map(
1014
1172
  ({source, error}) => `${source} failed to load: ${error}`,
@@ -1024,7 +1182,7 @@ export async function checkAuthoringDocs(_ctx, options = {}) {
1024
1182
  : {keys: options.topicKeys};
1025
1183
  const surface = [
1026
1184
  ...('error' in topic
1027
- ? [`\`astryx docs authoring\` could not be read: ${topic.error}`]
1185
+ ? [`\`${formatCliCommand('docs authoring/field-reference')}\` could not be read: ${topic.error}`]
1028
1186
  : []),
1029
1187
  ...surfaceProblems(
1030
1188
  await auditAuthoringSurface({root, sources, topicKeys: topic.keys}),
@@ -1048,7 +1206,7 @@ export async function checkAuthoringDocs(_ctx, options = {}) {
1048
1206
  id,
1049
1207
  label,
1050
1208
  status: 'pass',
1051
- message: `All ${audit.sections} authoring schemas are readable in \`astryx docs authoring\`.`,
1209
+ message: `All ${audit.sections} authoring schemas are readable in \`${formatCliCommand('docs authoring/field-reference')}\`.`,
1052
1210
  };
1053
1211
  } catch (err) {
1054
1212
  return {
@@ -1088,11 +1246,11 @@ export async function checkCliDocs(_ctx, options = {}) {
1088
1246
  const problems = [
1089
1247
  ...audit.missing.map(
1090
1248
  source =>
1091
- `${source} has no namespace, so no \`astryx docs\` topic reads it`,
1249
+ `${source} has no namespace, so no \`${formatCliCommand('docs')}\` topic reads it`,
1092
1250
  ),
1093
1251
  ...audit.unknown.map(
1094
1252
  ({source, namespace}) =>
1095
- `${source} has namespace "${namespace}", which no \`astryx docs\` topic reads`,
1253
+ `${source} has namespace "${namespace}", which no \`${formatCliCommand('docs')}\` topic reads`,
1096
1254
  ),
1097
1255
  ...audit.misfiled.map(({message}) => message),
1098
1256
  ...audit.failed.map(
@@ -1116,7 +1274,7 @@ export async function checkCliDocs(_ctx, options = {}) {
1116
1274
  id,
1117
1275
  label,
1118
1276
  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\`.`,
1277
+ message: `All ${audit.docs} CLI docs are readable: ${audit.tree} in the \`${formatCliCommand('docs cli')}\` tree and ${audit.authoring} in \`${formatCliCommand('docs authoring/field-reference')}\`.`,
1120
1278
  };
1121
1279
  } catch (err) {
1122
1280
  return {
@@ -1233,7 +1391,7 @@ export async function checkDocsProgressiveDisclosure(ctx) {
1233
1391
  // A guide the docs tree places is a topic read by its route; it is held
1234
1392
  // to the same budget as every flat topic.
1235
1393
  const guides = [...(await projectTree(catalog)).nodes.values()]
1236
- // A flat topic in the Unorganized level is checked above, as a topic.
1394
+ // A flat topic in a topic level is checked above, as a topic.
1237
1395
  .filter(node => node.kind === 'generic' && !node.ref?.flatTopic)
1238
1396
  .map(guideEntry);
1239
1397
  for (const entry of [...catalog.entries(), ...guides]) {
@@ -1278,6 +1436,456 @@ export async function checkDocsProgressiveDisclosure(ctx) {
1278
1436
  };
1279
1437
  }
1280
1438
 
1439
+ /**
1440
+ * Check 11 — the command every printed command starts with, and a warning when
1441
+ * loaded integrations declare different ones (their `cliCommand`). Then the
1442
+ * picker uses neither, so commands fall back to the package-manager guess.
1443
+ *
1444
+ * @param {DoctorContext} ctx
1445
+ * @returns {DoctorCheck}
1446
+ */
1447
+ export function checkCliCommand(ctx) {
1448
+ const id = 'cli-command';
1449
+ const label = 'CLI command';
1450
+ const {command, conflict, declared} = chooseDeclaredCliCommand(
1451
+ declaredCliCommandsOf(ctx.integrations ?? []),
1452
+ );
1453
+ if (conflict) {
1454
+ const claims = declared
1455
+ .map(entry => `${entry.package} (\`${entry.command}\`)`)
1456
+ .join(', ');
1457
+ return {
1458
+ id,
1459
+ label,
1460
+ status: 'warn',
1461
+ message: `Loaded integrations declare different CLI commands: ${claims}. Neither is used, so printed commands start with \`${guessCliInvocation(ctx.cwd)}\`.`,
1462
+ fix: 'Remove the integration that does not belong here, or have every package declare the same `cliCommand` in its astryx.integration manifest.',
1463
+ };
1464
+ }
1465
+ if (command) {
1466
+ const packages = declared.map(entry => entry.package).join(', ');
1467
+ return {
1468
+ id,
1469
+ label,
1470
+ status: 'pass',
1471
+ message: `Printed commands start with \`${command}\`, the cliCommand of ${packages}.`,
1472
+ };
1473
+ }
1474
+ return {
1475
+ id,
1476
+ label,
1477
+ status: 'info',
1478
+ message: `No loaded integration declares a cliCommand, so printed commands start with \`${getCliInvocation(ctx.cwd)}\`.`,
1479
+ };
1480
+ }
1481
+
1482
+ /* ── outside a project: the computer ─────────────────────────────────── */
1483
+
1484
+ /**
1485
+ * What a computer check reads: the running Node, the release that answered
1486
+ * (`meta.answeredBy`), and the newest release the computer knows of.
1487
+ * @typedef {object} ComputerContext
1488
+ * @property {string} nodeVersion
1489
+ * @property {{cli: string, core: string | null, copy: string}} answeredBy
1490
+ * @property {string | null} latest the newest release known, or null
1491
+ * @property {Array<{path: string, state: import('../../foundation/agent-docs/computer-agent-text.mjs').ComputerAgentTextState}>} [agentText]
1492
+ * each file of the computer-level agent text against this release's rendering
1493
+ * @property {import('../../foundation/env/user-settings.mjs').ComputerAgentTextSettings} [agentTextSettings]
1494
+ * the person's choices about the computer-level agent text
1495
+ * @property {string | null} [agentTextCommand] the command the skill file
1496
+ * tells agents to run, or null
1497
+ * @property {string | null} [agentTextInstall] the install step the skill
1498
+ * file records for when that command is not found, or null
1499
+ * @property {string} [cwd] the folder doctor runs in, for the printed fix
1500
+ * @property {import('../../foundation/release/notices.mjs').GlobalBehind | null} [globalBehind]
1501
+ * the CLI installed for the computer, when it is older than the newest
1502
+ * release (outside a project)
1503
+ * @property {import('../../foundation/release/notices.mjs').ProjectLag | null} [projectLag]
1504
+ * the project's release, when it is behind the newest (inside a project)
1505
+ * @property {{key: string, reason: string, at: number} | null} [lastNoticeSkip]
1506
+ * @property {{at: number, reason: string} | null} [lastCheckFailure] why the
1507
+ * last update check did not reach the registry, after the last that did
1508
+ * the newest update notice that did not print, and why (diagnostic only)
1509
+ * @property {{pid: number | null, host: string | null, since: number} | null} [stateLock]
1510
+ * who holds the state file's lock now (diagnostic only)
1511
+ * @property {Array<import('../../foundation/env/search-path.mjs').SearchPathCommand & {upgrade?: string | null, runner?: boolean}>} [searchPathCommands]
1512
+ * every `astryx` on the search path, with the command that upgrades it
1513
+ * the `astryx` commands on the search path, in order
1514
+ * @property {import('../../foundation/integrations/computer-integrations.mjs').ComputerIntegrationCandidate[]} [computerIntegrations]
1515
+ * the integrations installed beside the running CLI
1516
+ * @property {string[]} [loadedComputerIntegrations] the names of those that
1517
+ * loaded outside a project
1518
+ * @property {string[]} [folderIntegrations] the names of the integrations the
1519
+ * folder loads itself, whose copies win over the computer's
1520
+ */
1521
+
1522
+ /**
1523
+ * The release that answered this run outside a project, and the Core it
1524
+ * reads from.
1525
+ * @param {ComputerContext} ctx
1526
+ * @returns {DoctorCheck}
1527
+ */
1528
+ export function checkAnsweringRelease(ctx) {
1529
+ const {cli, core} = ctx.answeredBy;
1530
+ return {
1531
+ id: 'answering-release',
1532
+ label: 'Release that answered',
1533
+ status: core ? 'pass' : 'info',
1534
+ message: `${
1535
+ core
1536
+ ? `CLI ${cli} with Core ${core} answered, outside a project.`
1537
+ : `CLI ${cli} answered, outside a project. It installs Core ${cli} into the per-user cache the first time a command needs it.`
1538
+ } How each folder is answered: \`${formatCliCommand('astryx docs global', ctx.cwd)}\`.`,
1539
+ };
1540
+ }
1541
+
1542
+ /**
1543
+ * The newest release this computer knows of, beside the one that answered
1544
+ * (spec AST-081 FR10). Runs inside and outside a project, and is always
1545
+ * info. Inside one, a release behind the newest names the install and
1546
+ * upgrade commands; outside one, a CLI installed for the computer that is
1547
+ * behind names the command of the package manager that installed it, after
1548
+ * the Node it needs when the computer runs an older one. Doctor installs
1549
+ * nothing.
1550
+ * @param {ComputerContext} ctx
1551
+ * @returns {DoctorCheck}
1552
+ */
1553
+ export function checkNewestRelease(ctx) {
1554
+ const check = newestReleaseCheck(ctx);
1555
+ const notes = [
1556
+ checkFailureNote(ctx.lastCheckFailure),
1557
+ skipNote(ctx.lastNoticeSkip),
1558
+ lockNote(ctx.stateLock),
1559
+ ].filter(Boolean);
1560
+ return notes.length > 0 ? {...check, message: [check.message, ...notes].join(' ')} : check;
1561
+ }
1562
+
1563
+ /**
1564
+ * The note on a state-file lock held right now, which keeps notices from
1565
+ * being recorded (diagnostic only).
1566
+ * @param {{pid: number | null, host: string | null, since: number} | null | undefined} lock
1567
+ * @returns {string | null}
1568
+ */
1569
+ function lockNote(lock) {
1570
+ if (!lock) return null;
1571
+ const by = lock.pid == null ? '' : ` by process ${lock.pid}${lock.host ? ` on ${lock.host}` : ''}`;
1572
+ return `The state file's lock is held${by} since ${new Date(lock.since).toISOString()}.`;
1573
+ }
1574
+
1575
+ /**
1576
+ * The note on an update check that did not reach the registry (diagnostic
1577
+ * only): no check, no notices.
1578
+ * @param {{at: number, reason: string} | null | undefined} failed
1579
+ * @returns {string | null}
1580
+ */
1581
+ function checkFailureNote(failed) {
1582
+ if (!failed) return null;
1583
+ const why =
1584
+ failed.reason === 'npm-missing'
1585
+ ? 'npm could not be started'
1586
+ : failed.reason === 'bad-reply'
1587
+ ? "npm's answer named no release"
1588
+ : failed.reason.startsWith('npm-failed: ')
1589
+ ? `npm failed (${failed.reason.slice('npm-failed: '.length)}); its registry and proxy settings decide where it asks`
1590
+ : failed.reason;
1591
+ return `The last update check, ${new Date(failed.at).toISOString()}, did not reach the registry: ${why}.`;
1592
+ }
1593
+
1594
+ /** What a recorded notice skip's reason means, for doctor's update line. */
1595
+ const SKIP_REASONS = {
1596
+ 'already-shown': 'it had already printed that day',
1597
+ 'no-newer': 'no newer release was known',
1598
+ 'no-project': 'the folder belongs to no Astryx project, so no install command fits',
1599
+ };
1600
+
1601
+ /**
1602
+ * The note on the last update notice that did not print, for diagnosis
1603
+ * (diagnostic state, not a contract).
1604
+ * @param {{key: string, reason: string, at: number} | null | undefined} skip
1605
+ * @returns {string | null}
1606
+ */
1607
+ function skipNote(skip) {
1608
+ if (!skip) return null;
1609
+ const why = /** @type {Record<string, string>} */ (SKIP_REASONS)[skip.reason] ?? skip.reason;
1610
+ return `Last update notice skipped ${new Date(skip.at).toISOString()} (${skip.key}): ${why}.`;
1611
+ }
1612
+
1613
+ /**
1614
+ * {@link checkNewestRelease} without the skip note.
1615
+ * @param {ComputerContext} ctx
1616
+ * @returns {DoctorCheck}
1617
+ */
1618
+ function newestReleaseCheck(ctx) {
1619
+ const {latest, globalBehind: behind, projectLag: lag} = ctx;
1620
+ const {cli} = ctx.answeredBy;
1621
+ const base = {id: 'newest-release', label: 'Newest release', status: /** @type {const} */ ('info')};
1622
+ if (behind) {
1623
+ return {
1624
+ ...base,
1625
+ message: `${availableLine(behind.current, behind.newest)}. The CLI installed for the computer stays on its release until you upgrade it.`,
1626
+ fix: `${upgradeLine(behind.command, behind.node)}.`,
1627
+ };
1628
+ }
1629
+ if (lag) {
1630
+ return {
1631
+ ...base,
1632
+ message: `This project uses Astryx ${lag.current}; ${lag.newest} is the newest.`,
1633
+ fix: `${upgradeLine(`${lag.install}, then ${lag.upgrade}`, lag.node)}.`,
1634
+ };
1635
+ }
1636
+ if (!latest) {
1637
+ return {
1638
+ ...base,
1639
+ 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.',
1640
+ };
1641
+ }
1642
+ if (isValidSemver(latest) && isValidSemver(cli) && semverCompare(latest, cli) > 0) {
1643
+ return {
1644
+ ...base,
1645
+ message: `Release ${latest} is newer than ${cli}, the release that answered.`,
1646
+ };
1647
+ }
1648
+ return {...base, message: `No newer release is known: the newest is ${latest}.`};
1649
+ }
1650
+
1651
+ /**
1652
+ * The `astryx` commands on the search path (spec AST-081 FR11): the first is
1653
+ * the one a shell runs. Warns when it is not the Astryx CLI, or is a release
1654
+ * that predates the hand-off and so answers every folder itself.
1655
+ * @param {ComputerContext} ctx
1656
+ * @returns {DoctorCheck}
1657
+ */
1658
+ export function checkCliOnPath(ctx) {
1659
+ const rows = ctx.searchPathCommands ?? [];
1660
+ const base = {id: 'cli-on-path', label: '`astryx` on the search path'};
1661
+ const describe = (/** @type {import('../../foundation/env/search-path.mjs').SearchPathCommand} */ row) =>
1662
+ row.astryx ? `${row.file} (CLI ${row.version ?? 'of unknown version'})` : `${row.file} (not the Astryx CLI)`;
1663
+ const others = rows.slice(1);
1664
+ const othersNote = others.length > 0 ? ` Later on the search path: ${others.map(describe).join(', ')}.` : '';
1665
+ const [first] = rows;
1666
+ if (!first) {
1667
+ return {
1668
+ ...base,
1669
+ status: 'info',
1670
+ message: 'No `astryx` command on the search path, so `astryx` runs only through a package manager.',
1671
+ fix: 'To run the CLI in any folder, install it for the computer: `npm i -g @astryxdesign/cli`.',
1672
+ };
1673
+ }
1674
+ if (!first.astryx) {
1675
+ return {
1676
+ ...base,
1677
+ status: 'warn',
1678
+ message: `The first \`astryx\` on the search path, ${first.file}, is not the Astryx CLI, so \`astryx\` runs another program.${othersNote}`,
1679
+ fix: 'Remove that program from the search path, or put the folder of the CLI installed for the computer before it.',
1680
+ };
1681
+ }
1682
+ if (first.handsOff === false) {
1683
+ return {
1684
+ ...base,
1685
+ status: 'warn',
1686
+ 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}`,
1687
+ fix: `To upgrade, run: \`${first.upgrade ?? 'npm i -g @astryxdesign/cli@latest'}\`.`,
1688
+ };
1689
+ }
1690
+ return {
1691
+ ...base,
1692
+ status: 'pass',
1693
+ message: `\`astryx\` runs ${describe(first)}.${othersNote}`,
1694
+ };
1695
+ }
1696
+
1697
+ /**
1698
+ * The computer-level agent text (spec AST-081 FR4, FR8): turned off, present
1699
+ * and equal to this release's rendering, present but different, or absent,
1700
+ * for the files the chosen agents read. Always `info`: the text is optional,
1701
+ * so it never warns or fails. A file Astryx did not write counts as absent,
1702
+ * and a file a person edited is named; both are kept as they are.
1703
+ * @param {ComputerContext} ctx
1704
+ * @returns {DoctorCheck}
1705
+ */
1706
+ export function checkComputerAgentText(ctx) {
1707
+ const state = ctx.agentTextSettings && ctx.agentText ? null : computerAgentTextState();
1708
+ const settings = ctx.agentTextSettings ?? /** @type {NonNullable<typeof state>} */ (state).settings;
1709
+ const base = {
1710
+ id: 'computer-agent-text',
1711
+ label: 'Computer-level agent text',
1712
+ status: /** @type {const} */ ('info'),
1713
+ };
1714
+ if (!settings.install) {
1715
+ return {
1716
+ ...base,
1717
+ 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.',
1718
+ fix: `To turn it back on, run \`${formatCliCommand('astryx init --user', ctx.cwd)}\`.`,
1719
+ };
1720
+ }
1721
+ const entries = ctx.agentText ?? /** @type {NonNullable<typeof state>} */ (state).entries;
1722
+ const command = ctx.agentText ? (ctx.agentTextCommand ?? null) : /** @type {NonNullable<typeof state>} */ (state).command;
1723
+ const recorded = (ctx.agentText ? ctx.agentTextInstall : /** @type {NonNullable<typeof state>} */ (state).install) ?? null;
1724
+ const fix = `Run \`${formatCliCommand('astryx init --user', ctx.cwd)}\`.`;
1725
+ const [file, ...links] = entries;
1726
+ const home = (/** @type {{path: string}} */ e) => displayHomePath(e.path);
1727
+ const linked = links.filter(l => l.state === 'linked').map(home);
1728
+ const copied = links.filter(l => l.state === 'current').map(home);
1729
+ const skipped = links.filter(l => l.state === 'foreign' || l.state === 'edited').map(home);
1730
+ const dangling = links.filter(l => l.state === 'dangling').map(home);
1731
+ const missing = links.filter(l => l.state === 'absent' || l.state === 'differs').map(home);
1732
+ const linkNote =
1733
+ (linked.length > 0 ? ` Linked into: ${linked.join(', ')}.` : '') +
1734
+ (copied.length > 0 ? ` Copied into: ${copied.join(', ')}.` : '') +
1735
+ (skipped.length > 0 ? ` Not linked, because something Astryx did not create is there: ${skipped.join(', ')}.` : '') +
1736
+ (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(', ')}.` : '') +
1737
+ (missing.length > 0 ? ` Not linked yet: ${missing.join(', ')}.` : '');
1738
+ if (file.state === 'foreign' || file.state === 'edited') {
1739
+ return {
1740
+ ...base,
1741
+ 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}`,
1742
+ };
1743
+ }
1744
+ if (file.state === 'absent') {
1745
+ return {
1746
+ ...base,
1747
+ message: `No computer-level agent text: agents outside a project with Astryx agent docs do not know Astryx is here.${linkNote}`,
1748
+ fix,
1749
+ };
1750
+ }
1751
+ const current = file.state === 'current';
1752
+ const status = `${current ? 'The computer-level agent text matches this release' : "The computer-level agent text differs from this release's"}: ${home(file)}.`;
1753
+ // The skill outlives the CLI it names when the home folder outlasts the
1754
+ // folder the CLI is installed in, as on a machine whose system folders
1755
+ // reset on restart. Reported only when no `astryx` command is on the search
1756
+ // path at all, apart from a package runner's own copy, which it puts first
1757
+ // only for the run it starts (the way back when the global CLI is gone).
1758
+ // Any other `astryx`, even one the scan cannot trace to the package, such
1759
+ // as the shim of Volta, asdf or mise, may well run the CLI, so doctor stays
1760
+ // quiet here and cli-on-path reports what it can; never a false "gone".
1761
+ // The reinstalled copy refreshes the text too.
1762
+ const rows = ctx.searchPathCommands;
1763
+ if (command === COMPUTER_COMMAND && rows && !rows.some(r => !r.runner)) {
1764
+ return {
1765
+ ...base,
1766
+ message: `${status} It tells agents to run \`${COMPUTER_COMMAND}\`, but no \`${COMPUTER_COMMAND}\` command is on the search path, so agents that read it find no CLI to run (as when a restart removes the CLI installed for the computer and keeps the home folder).${linkNote}`,
1767
+ fix: recorded
1768
+ ? `Run \`${recorded}\`, the step the skill names for when \`${COMPUTER_COMMAND}\` is not found.`
1769
+ : `Install it for the computer: \`${COMPUTER_INSTALL_STEP}\`.`,
1770
+ };
1771
+ }
1772
+ return {
1773
+ ...base,
1774
+ message: `${status}${linkNote}`,
1775
+ ...(!current || missing.length > 0 || dangling.length > 0 ? {fix} : {}),
1776
+ };
1777
+ }
1778
+
1779
+ /**
1780
+ * The integrations installed for the computer (spec AST-081 FR12). Outside a
1781
+ * project: each one that loads, and a warning for each that is skipped
1782
+ * because its peer ranges exclude the answering release, or fails to load.
1783
+ * @param {ComputerContext} ctx
1784
+ * @returns {DoctorCheck}
1785
+ */
1786
+ export function checkComputerIntegrations(ctx) {
1787
+ const found = ctx.computerIntegrations ?? [];
1788
+ const loaded = new Set(ctx.loadedComputerIntegrations ?? []);
1789
+ const base = {id: 'computer-integrations', label: 'Integrations installed for this computer'};
1790
+ if (found.length === 0) {
1791
+ return {
1792
+ ...base,
1793
+ status: 'info',
1794
+ message: 'No integration is installed beside the CLI installed for this computer.',
1795
+ };
1796
+ }
1797
+ const label = (/** @type {{name: string, version?: string}} */ c) => (c.version ? `${c.name}@${c.version}` : c.name);
1798
+ const own = new Set(ctx.folderIntegrations ?? []);
1799
+ const shadowed = found.filter(c => own.has(c.name));
1800
+ const rest = found.filter(c => !own.has(c.name));
1801
+ const used = rest.filter(c => !c.incompatible && loaded.has(c.name));
1802
+ const skipped = rest.filter(c => c.incompatible);
1803
+ const failed = rest.filter(c => !c.incompatible && !loaded.has(c.name));
1804
+ const notes = [
1805
+ used.length > 0 ? `Used outside a project: ${used.map(label).join(', ')}.` : '',
1806
+ ...shadowed.map(c => `${label(c)} is not used here: this folder loads its own copy of ${c.name}.`),
1807
+ ...skipped.map(c => `Skipped ${label(c)}: ${c.incompatible}.`),
1808
+ ...failed.map(c => `${label(c)} could not be loaded.`),
1809
+ ].filter(Boolean);
1810
+ const fixes = [
1811
+ ...skipped.map(c => `install a release of ${c.name} that supports this CLI`),
1812
+ ...failed.map(c => `run \`${formatCliCommand(`astryx doctor integration validate ${c.name}`, ctx.cwd)}\``),
1813
+ ];
1814
+ return {
1815
+ ...base,
1816
+ status: skipped.length + failed.length > 0 ? 'warn' : 'pass',
1817
+ message: notes.join(' '),
1818
+ ...(fixes.length > 0 ? {fix: `To fix: ${fixes.join('; ')}.`} : {}),
1819
+ };
1820
+ }
1821
+
1822
+ /**
1823
+ * Inside a project, the integrations installed for the computer that the
1824
+ * project does not install: available, never used (spec AST-081 FR12).
1825
+ * Always info.
1826
+ * @param {{installed: Set<string>, candidates: import('../../foundation/integrations/computer-integrations.mjs').ComputerIntegrationCandidate[], install: ((name: string) => string) | null, elsewhere?: boolean}} input
1827
+ * `install`: the command that adds a package with the project's package
1828
+ * manager (read by the adapter); null when the folder belongs to no
1829
+ * project, or `elsewhere`: its package.json is not the project's, where the
1830
+ * fix points at the project's folder
1831
+ * @returns {DoctorCheck}
1832
+ */
1833
+ export function checkComputerIntegrationsAvailable({installed, candidates, install, elsewhere = false}) {
1834
+ const available = candidates.filter(c => !installed.has(c.name));
1835
+ const base = {id: 'computer-integrations', label: 'Integrations installed for this computer', status: /** @type {const} */ ('info')};
1836
+ if (available.length === 0) {
1837
+ return {...base, message: 'No integration installed for this computer is missing from this project.'};
1838
+ }
1839
+ const add = install;
1840
+ return {
1841
+ ...base,
1842
+ message: `Installed for this computer but not in this project, so not used here: ${available.map(c => c.name).join(', ')}.`,
1843
+ ...(add
1844
+ ? {fix: `To use one in this project, run ${available.map(c => `\`${add(c.name)}\``).join(' or ')}.`}
1845
+ : elsewhere
1846
+ ? {fix: NO_PROJECT_FIX}
1847
+ : {}),
1848
+ };
1849
+ }
1850
+
1851
+ /**
1852
+ * The checks doctor runs outside a project, in order.
1853
+ * @type {Array<(ctx: ComputerContext) => DoctorCheck>}
1854
+ */
1855
+ export const COMPUTER_CHECKS = [
1856
+ ctx =>
1857
+ checkNodeVersion({
1858
+ cwd: '',
1859
+ nodeVersion: ctx.nodeVersion,
1860
+ coreDir: null,
1861
+ configPath: null,
1862
+ }),
1863
+ checkAnsweringRelease,
1864
+ checkNewestRelease,
1865
+ checkCliOnPath,
1866
+ checkComputerIntegrations,
1867
+ checkComputerAgentText,
1868
+ ];
1869
+
1870
+ /**
1871
+ * Doctor outside a project: the computer, never the folder.
1872
+ * @param {string} cwd
1873
+ * @returns {Promise<DoctorReport>}
1874
+ */
1875
+ async function runComputerChecks(cwd) {
1876
+ /** @type {ComputerContext} */
1877
+ const ctx = {
1878
+ nodeVersion: process.versions.node,
1879
+ cwd,
1880
+ ...releaseFacts(cwd, {project: false}),
1881
+ };
1882
+ Object.assign(ctx, await computerIntegrationsOutside(cwd));
1883
+ const checks = COMPUTER_CHECKS.map(fn => fn(ctx));
1884
+ const summary = {pass: 0, warn: 0, fail: 0, info: 0};
1885
+ for (const c of checks) summary[c.status] += 1;
1886
+ return {checks, summary};
1887
+ }
1888
+
1281
1889
  /**
1282
1890
  * Ordered list of synchronous check functions. Append here to add a check.
1283
1891
  * (checkConfig is async and is awaited separately by {@link runChecks}.)
@@ -1294,6 +1902,7 @@ export const SYNC_CHECKS = [
1294
1902
  checkAgentDocs,
1295
1903
  checkPeerDeps,
1296
1904
  checkPackageManager,
1905
+ checkCliCommand,
1297
1906
  ];
1298
1907
 
1299
1908
  /**
@@ -1305,7 +1914,8 @@ export const SYNC_CHECKS = [
1305
1914
  */
1306
1915
  export async function runChecks(options = {}) {
1307
1916
  const cwd = options.cwd ?? process.cwd();
1308
- const coreDir = findCoreDir(cwd);
1917
+ if (outsideProject(cwd)) return runComputerChecks(cwd);
1918
+ const coreDir = findProjectCoreDir(cwd);
1309
1919
  // findConfigPath throws when multiple config files coexist. That's a
1310
1920
  // misconfiguration doctor exists to report — catch it and surface it through
1311
1921
  // checkConfig as a FAIL rather than crashing the whole diagnostic engine.
@@ -1339,7 +1949,9 @@ export async function runChecks(options = {}) {
1339
1949
  /** @type {import('../../foundation/config/project.mjs').Project | null} */
1340
1950
  let project = null;
1341
1951
  try {
1342
- project = await Project.load(cwd);
1952
+ // Doctor judges the project, so it reads only the Core the project
1953
+ // installed, never a carried one (spec AST-081 FR3).
1954
+ project = await Project.load(cwd, {core: 'project'});
1343
1955
  } catch (err) {
1344
1956
  // A project the CLI cannot load leaves the checks that need it
1345
1957
  // skipped. The reason is kept: those checks and the config
@@ -1442,6 +2054,8 @@ export async function runChecks(options = {}) {
1442
2054
  cwd,
1443
2055
  nodeVersion: process.versions.node,
1444
2056
  coreDir,
2057
+ ownCli: projectHasOwnCli(cwd),
2058
+ projectCore: projectCoreVersion(cwd),
1445
2059
  configPath,
1446
2060
  integrations,
1447
2061
  docsCatalog,
@@ -1466,6 +2080,31 @@ export async function runChecks(options = {}) {
1466
2080
  checks.push(await checkConfig(ctx));
1467
2081
  }
1468
2082
  }
2083
+ const facts = releaseFacts(cwd, {project: true});
2084
+ checks.push(
2085
+ checkNewestRelease({
2086
+ nodeVersion: ctx.nodeVersion,
2087
+ answeredBy: facts.answeredBy,
2088
+ latest: facts.latest,
2089
+ projectLag: facts.projectLag,
2090
+ lastNoticeSkip: facts.lastNoticeSkip,
2091
+ lastCheckFailure: facts.lastCheckFailure,
2092
+ stateLock: facts.stateLock,
2093
+ }),
2094
+ );
2095
+ checks.push(
2096
+ checkCliOnPath({
2097
+ nodeVersion: ctx.nodeVersion,
2098
+ answeredBy: facts.answeredBy,
2099
+ latest: null,
2100
+ searchPathCommands: facts.searchPathCommands,
2101
+ }),
2102
+ );
2103
+ {
2104
+ const loadedNames = (project?.loadedIntegrations ?? []).map((/** @type {{name: string}} */ i) => i.name);
2105
+ const candidates = await computerIntegrationsToAdd(cwd, loadedNames);
2106
+ checks.push(checkComputerIntegrationsAvailable({installed: new Set(loadedNames), candidates, install: installCommandFor(cwd), elsewhere: insideProjectElsewhere(cwd)}));
2107
+ }
1469
2108
  checks.push(await checkAuthoringDocs(ctx));
1470
2109
  checks.push(await checkCliDocs(ctx));
1471
2110
  checks.push(await checkDocsTree(ctx));