@astryxdesign/cli 0.7.2-canary.fe2c655 → 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 (509) hide show
  1. package/CHANGELOG.md +119 -0
  2. package/README.md +175 -154
  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/help/help.mjs +1 -1
  7. package/api/build/kit/kit.mjs +3 -11
  8. package/api/component/_adapter.d.mts +72 -1
  9. package/api/component/_adapter.mjs +212 -18
  10. package/api/component/component.doc.mjs +6 -1
  11. package/api/component/component.mjs +82 -15
  12. package/api/component/component.test.mjs +80 -0
  13. package/api/component/doc-links.test.mjs +386 -0
  14. package/api/component/integration-export-name.test.mjs +644 -0
  15. package/api/component/list/list.mjs +42 -4
  16. package/api/discover/_adapter.d.mts +132 -12
  17. package/api/discover/_adapter.mjs +488 -27
  18. package/api/discover/_adapter.test.mjs +599 -17
  19. package/api/discover/_catalog-view.d.mts +16 -5
  20. package/api/discover/_catalog-view.mjs +32 -8
  21. package/api/discover/detail/doc/doc.mjs +9 -1
  22. package/api/discover/discover.d.mts +11 -0
  23. package/api/discover/discover.doc.mjs +9 -7
  24. package/api/discover/discover.mjs +121 -19
  25. package/api/discover/discover.type.d.mts +20 -7
  26. package/api/discover/discover.type.mjs +17 -7
  27. package/api/discover/list/list.d.mts +4 -3
  28. package/api/discover/list/list.mjs +17 -10
  29. package/api/docs/_adapter.d.mts +74 -0
  30. package/api/docs/_adapter.mjs +553 -16
  31. package/api/docs/docs.doc.mjs +2 -1
  32. package/api/docs/docs.mjs +2 -1
  33. package/api/docs/docs.test.mjs +5 -2
  34. package/api/docs/integration-tree.test.mjs +22 -0
  35. package/api/docs/integrationDocs.test.mjs +124 -0
  36. package/api/docs/list/list.mjs +12 -2
  37. package/api/docs/reference-blocks.test.mjs +1 -3
  38. package/api/doctor/_adapter.d.mts +108 -0
  39. package/api/doctor/_adapter.mjs +223 -4
  40. package/api/doctor/doctor.d.mts +199 -0
  41. package/api/doctor/doctor.doc.mjs +3 -2
  42. package/api/doctor/doctor.mjs +530 -34
  43. package/api/doctor/doctor.test.mjs +180 -14
  44. package/api/doctor/release-checks.test.mjs +156 -0
  45. package/api/doctor/theme-checks.mjs +17 -4
  46. package/api/doctor/theme-checks.test.mjs +223 -7
  47. package/api/error.d.mts +10 -0
  48. package/api/error.mjs +25 -0
  49. package/api/gap-report/gap-report.d.mts +3 -0
  50. package/api/gap-report/gap-report.doc.mjs +11 -3
  51. package/api/gap-report/gap-report.mjs +219 -52
  52. package/api/gap-report/gap-report.test.mjs +233 -0
  53. package/api/gap-report/gap-report.type.d.mts +41 -0
  54. package/api/gap-report/gap-report.type.mjs +18 -0
  55. package/api/hook/_adapter.mjs +3 -11
  56. package/api/hook/hook.doc.mjs +4 -0
  57. package/api/hook/list/list.d.mts +1 -1
  58. package/api/index.d.mts +2 -0
  59. package/api/index.mjs +2 -0
  60. package/api/init/_adapter.d.mts +22 -0
  61. package/api/init/_adapter.mjs +59 -0
  62. package/api/init/init.d.mts +7 -5
  63. package/api/init/init.doc.mjs +28 -7
  64. package/api/init/init.mjs +28 -4
  65. package/api/init/init.type.d.mts +48 -3
  66. package/api/init/init.type.mjs +37 -3
  67. package/api/init/run/run.d.mts +1 -1
  68. package/api/init/run/run.mjs +17 -6
  69. package/api/init/user/user.d.mts +14 -0
  70. package/api/init/user/user.mjs +87 -0
  71. package/api/init/user/user.test.mjs +232 -0
  72. package/api/integration/_adapter.d.mts +45 -0
  73. package/api/integration/_adapter.mjs +112 -0
  74. package/api/integration/add-contribution.component-names.test.mjs +66 -0
  75. package/api/integration/add-contribution.mjs +8 -4
  76. package/api/integration/add-contribution.test.mjs +4 -2
  77. package/api/integration/authoring-checks.mjs +110 -6
  78. package/api/integration/authoring-checks.test.mjs +808 -0
  79. package/api/integration/authoring-checks.type.d.mts +143 -0
  80. package/api/integration/authoring-checks.type.mjs +74 -2
  81. package/api/integration/check/check.d.mts +96 -0
  82. package/api/integration/check/check.mjs +357 -0
  83. package/api/integration/check/check.test.mjs +489 -0
  84. package/api/integration/integration-check.type.d.mts +118 -0
  85. package/api/integration/integration-check.type.mjs +84 -0
  86. package/api/integration/integrationCheck.doc.d.mts +10 -0
  87. package/api/integration/integrationCheck.doc.mjs +73 -0
  88. package/api/integration/integrationComponentConflicts.doc.mjs +45 -6
  89. package/api/integration/integrationDocConflicts.doc.mjs +22 -5
  90. package/api/integration/pack-check.mjs +364 -120
  91. package/api/integration/pack-check.test.mjs +730 -10
  92. package/api/integration/validate-integration-fixes.test.mjs +67 -0
  93. package/api/integration/validate-integration.d.mts +8 -0
  94. package/api/integration/validate-integration.mjs +63 -9
  95. package/api/integration/validate-integration.test.mjs +88 -8
  96. package/api/integration/validate-integration.type.d.mts +16 -0
  97. package/api/integration/validate-integration.type.mjs +14 -1
  98. package/api/integration/validate-unread-theme-folders.test.mjs +3 -2
  99. package/api/integration/validateIntegration.doc.mjs +19 -3
  100. package/api/search/_adapter.d.mts +11 -0
  101. package/api/search/_adapter.mjs +25 -0
  102. package/api/search/search.d.mts +82 -13
  103. package/api/search/search.doc.mjs +8 -3
  104. package/api/search/search.domain-priority.test.mjs +108 -18
  105. package/api/search/search.integration-check.test.mjs +24 -0
  106. package/api/search/search.mjs +645 -64
  107. package/api/search/search.test.mjs +179 -1
  108. package/api/search/search.type.d.mts +14 -1
  109. package/api/search/search.type.mjs +6 -1
  110. package/api/swizzle/_adapter.mjs +2 -2
  111. package/api/swizzle/copy/copy.mjs +29 -15
  112. package/api/template/template-integration.test.mjs +1 -1
  113. package/api/template/template.test.mjs +120 -3
  114. package/api/theme/_adapter.d.mts +16 -0
  115. package/api/theme/_adapter.mjs +32 -5
  116. package/api/theme/build/build.d.mts +2 -1
  117. package/api/theme/build/build.family.test.mjs +64 -0
  118. package/api/theme/build/build.mjs +250 -38
  119. package/api/theme/build/build.packed-old-core.test.mjs +20 -3
  120. package/api/theme/build/font-warning.mjs +5 -3
  121. package/api/theme/build/font-warning.test.mjs +2 -1
  122. package/api/theme/remove/remove.test.mjs +2 -2
  123. package/api/theme/targets/targets.mjs +3 -10
  124. package/api/theme/themeBuild.doc.mjs +4 -4
  125. package/api/theme/themeTargets.doc.mjs +4 -0
  126. package/api/theme/use/use.test.mjs +1 -1
  127. package/api/upgrade/_adapter.d.mts +40 -2
  128. package/api/upgrade/_adapter.mjs +118 -5
  129. package/api/upgrade/run/cli-behind-core.test.mjs +438 -0
  130. package/api/upgrade/run/run.mjs +35 -2
  131. package/api/upgrade/status/status.d.mts +21 -5
  132. package/api/upgrade/status/status.mjs +45 -8
  133. package/api/upgrade/upgrade.doc.mjs +6 -3
  134. package/api/upgrade/upgrade.mjs +5 -1
  135. package/api/upgrade/upgrade.type.d.mts +23 -0
  136. package/api/upgrade/upgrade.type.mjs +16 -1
  137. package/assets/codemods/__tests__/registry.test.mjs +1 -0
  138. package/assets/codemods/registry.mjs +1 -0
  139. package/assets/codemods/runner.mjs +7 -1
  140. package/assets/codemods/transforms/v0.7.2/__tests__/migrate-list-edge-compensation-to-edge.test.mjs +120 -0
  141. package/assets/codemods/transforms/v0.7.2/index.mjs +20 -0
  142. package/assets/codemods/transforms/v0.7.2/migrate-list-edge-compensation-to-edge.mjs +198 -0
  143. package/assets/discover/official-packages.json +367 -0
  144. package/assets/docs/README.md +10 -0
  145. package/assets/docs/author-a-theme.doc.mjs +3 -3
  146. package/assets/docs/browser-support.doc.mjs +1 -1
  147. package/assets/docs/getting-started.doc.mjs +26 -6
  148. package/assets/docs/styling-overview.doc.mjs +1 -1
  149. package/assets/docs/theme.doc.dense.mjs +1 -1
  150. package/assets/docs/theme.doc.mjs +2 -2
  151. package/assets/docs/theme.doc.zh.mjs +1 -1
  152. package/assets/docs/tree/add-a-component.doc.mjs +22 -2
  153. package/assets/docs/tree/add-a-theme.doc.mjs +13 -3
  154. package/assets/docs/tree/add-a-topic.doc.mjs +54 -9
  155. package/assets/docs/tree/agent-guidance.doc.mjs +19 -5
  156. package/assets/docs/tree/block-template.doc.mjs +8 -20
  157. package/assets/docs/tree/build-the-template.doc.mjs +1 -1
  158. package/assets/docs/tree/building-blocks.doc.mjs +4 -0
  159. package/assets/docs/tree/check-the-props.doc.mjs +201 -0
  160. package/assets/docs/tree/check-your-docs.doc.mjs +69 -11
  161. package/assets/docs/tree/checks.doc.mjs +32 -24
  162. package/assets/docs/tree/ci-and-containers.doc.mjs +88 -0
  163. package/assets/docs/tree/cli-command.doc.mjs +142 -0
  164. package/assets/docs/tree/cli.doc.mjs +6 -0
  165. package/assets/docs/tree/codemods.doc.mjs +17 -7
  166. package/assets/docs/tree/commands.doc.mjs +7 -1
  167. package/assets/docs/tree/component-family.doc.mjs +8 -6
  168. package/assets/docs/tree/component-imports.doc.mjs +21 -3
  169. package/assets/docs/tree/components.doc.mjs +7 -1
  170. package/assets/docs/tree/computer-agent-guide.doc.mjs +160 -0
  171. package/assets/docs/tree/computer-integrations.doc.mjs +93 -0
  172. package/assets/docs/tree/configuration.doc.mjs +7 -3
  173. package/assets/docs/tree/debug-and-gap-reports.doc.mjs +41 -22
  174. package/assets/docs/tree/define-the-theme.doc.mjs +6 -5
  175. package/assets/docs/tree/describe-the-component.doc.mjs +3 -3
  176. package/assets/docs/tree/docs.doc.mjs +9 -3
  177. package/assets/docs/tree/document-the-template.doc.mjs +1 -1
  178. package/assets/docs/tree/document-the-theme.doc.mjs +3 -3
  179. package/assets/docs/tree/export-template-assets.doc.mjs +1 -1
  180. package/assets/docs/tree/extend-or-replace.doc.mjs +19 -9
  181. package/assets/docs/tree/fonts-and-assets.doc.mjs +2 -2
  182. package/assets/docs/tree/generate-a-palette.doc.mjs +7 -5
  183. package/assets/docs/tree/global-integrations.doc.mjs +124 -0
  184. package/assets/docs/tree/global.doc.mjs +131 -0
  185. package/assets/docs/tree/grade-template-with-agent.doc.mjs +5 -5
  186. package/assets/docs/tree/install-once.doc.mjs +105 -0
  187. package/assets/docs/tree/integrations.doc.mjs +13 -1
  188. package/assets/docs/tree/layout-spacing.doc.mjs +2 -3
  189. package/assets/docs/tree/layout.doc.mjs +6 -6
  190. package/assets/docs/tree/links.doc.mjs +7 -6
  191. package/assets/docs/tree/migration-setup.doc.mjs +3 -2
  192. package/assets/docs/tree/migration-verification.doc.mjs +0 -1
  193. package/assets/docs/tree/migration.doc.mjs +0 -1
  194. package/assets/docs/tree/monorepos.doc.mjs +84 -0
  195. package/assets/docs/tree/package-and-test.doc.mjs +1 -1
  196. package/assets/docs/tree/page-template.doc.mjs +4 -8
  197. package/assets/docs/tree/problems.doc.mjs +205 -0
  198. package/assets/docs/tree/project-global-or-both.doc.mjs +131 -0
  199. package/assets/docs/tree/publishing.doc.mjs +6 -6
  200. package/assets/docs/tree/quick-start.doc.mjs +53 -27
  201. package/assets/docs/tree/reference.doc.mjs +192 -0
  202. package/assets/docs/tree/replace-a-core-component.doc.mjs +15 -7
  203. package/assets/docs/tree/replace-a-core-template.doc.mjs +10 -12
  204. package/assets/docs/tree/scaffold.doc.mjs +1 -1
  205. package/assets/docs/tree/sections-and-placement.doc.mjs +9 -11
  206. package/assets/docs/tree/see-it-in-an-app.doc.mjs +17 -5
  207. package/assets/docs/tree/short-and-findable.doc.mjs +9 -8
  208. package/assets/docs/tree/single-component.doc.mjs +15 -6
  209. package/assets/docs/tree/start-a-template.doc.mjs +18 -8
  210. package/assets/docs/tree/subcomponent.doc.mjs +6 -6
  211. package/assets/docs/tree/template-assets.doc.mjs +2 -2
  212. package/assets/docs/tree/template-doc-overview.doc.mjs +6 -17
  213. package/assets/docs/tree/template-fonts.doc.mjs +1 -1
  214. package/assets/docs/tree/template-icons.doc.mjs +1 -1
  215. package/assets/docs/tree/template-images-media.doc.mjs +1 -1
  216. package/assets/docs/tree/template-styles.doc.mjs +2 -2
  217. package/assets/docs/tree/templates.doc.mjs +7 -1
  218. package/assets/docs/tree/test-in-an-app.doc.mjs +7 -7
  219. package/assets/docs/tree/test-template-in-app.doc.mjs +5 -5
  220. package/assets/docs/tree/themes.doc.mjs +4 -0
  221. package/assets/docs/tree/tokens-and-setup.doc.mjs +19 -15
  222. package/assets/docs/tree/troubleshooting.doc.mjs +29 -13
  223. package/assets/docs/tree/type-scale.doc.mjs +3 -3
  224. package/assets/docs/tree/uninstall.doc.mjs +109 -0
  225. package/assets/docs/tree/updates.doc.mjs +92 -0
  226. package/assets/docs/tree/upgrading.doc.mjs +6 -6
  227. package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +14 -2
  228. package/assets/docs/tree/verify-packed-template.doc.mjs +3 -3
  229. package/assets/docs/tree/versioning.doc.mjs +10 -9
  230. package/assets/docs/tree/who-answers.doc.mjs +119 -0
  231. package/assets/docs/tree/write-the-template-file.doc.mjs +7 -6
  232. package/assets/docs/use-a-theme.doc.mjs +2 -2
  233. package/assets/docs/working-with-ai.doc.mjs +66 -13
  234. package/assets/theme.template.ts +1 -1
  235. package/authoring/codemod/codemod.doc.mjs +7 -3
  236. package/authoring/config/config.doc.mjs +9 -6
  237. package/authoring/config/type.ts +4 -2
  238. package/authoring/debug/parse.d.mts +5 -5
  239. package/authoring/discover/discover.doc.mjs +7 -1
  240. package/authoring/doctypes/_schema.d.mts +8 -3
  241. package/authoring/doctypes/_schema.mjs +1 -0
  242. package/authoring/doctypes/base/graph-fields.doc.mjs +6 -4
  243. package/authoring/doctypes/command/command.doc.mjs +20 -12
  244. package/authoring/doctypes/command/type.ts +14 -0
  245. package/authoring/doctypes/component/component.doc.mjs +30 -27
  246. package/authoring/doctypes/component/type.ts +21 -8
  247. package/authoring/doctypes/enum/enum.doc.mjs +7 -4
  248. package/authoring/doctypes/function/function.doc.mjs +9 -8
  249. package/authoring/doctypes/hook/hook.doc.mjs +5 -4
  250. package/authoring/doctypes/hook/type.ts +4 -0
  251. package/authoring/doctypes/namespace/namespace.doc.mjs +7 -6
  252. package/authoring/doctypes/reference/reference.doc.mjs +12 -11
  253. package/authoring/doctypes/schema/schema.doc.mjs +9 -9
  254. package/authoring/doctypes/template/integration-template.doc.d.mts +11 -0
  255. package/authoring/doctypes/template/integration-template.doc.mjs +49 -0
  256. package/authoring/doctypes/template/parse.d.mts +6 -2
  257. package/authoring/doctypes/template/parse.mjs +3 -1
  258. package/authoring/doctypes/template/template.doc.mjs +3 -3
  259. package/authoring/doctypes/template/type.ts +22 -0
  260. package/authoring/doctypes/theme/theme.doc.mjs +2 -2
  261. package/authoring/gap-report/gap-report.doc.mjs +2 -2
  262. package/authoring/identity/identity.doc.mjs +3 -3
  263. package/authoring/index.d.ts +2 -0
  264. package/authoring/integration/integration.doc.mjs +30 -6
  265. package/authoring/integration/parse.test.mjs +21 -0
  266. package/authoring/integration/schema.d.mts +16 -3
  267. package/authoring/integration/schema.mjs +34 -2
  268. package/authoring/integration/type.ts +12 -0
  269. package/clients/cli/bin/astryx.mjs +20 -3
  270. package/clients/cli/cli-command.test.mjs +166 -0
  271. package/clients/cli/commands/blog.doc.mjs +1 -0
  272. package/clients/cli/commands/build-theme.batch-report.test.mjs +17 -3
  273. package/clients/cli/commands/build-theme.font-warning.test.mjs +3 -3
  274. package/clients/cli/commands/build-theme.mjs +26 -13
  275. package/clients/cli/commands/build-theme.path-safety.test.mjs +224 -6
  276. package/clients/cli/commands/build-theme.registry.test.mjs +5 -2
  277. package/clients/cli/commands/build.doc.mjs +1 -0
  278. package/clients/cli/commands/build.exit-codes-doc.test.mjs +4 -0
  279. package/clients/cli/commands/cli-postinstall.test.mjs +49 -1
  280. package/clients/cli/commands/component.doc.mjs +1 -0
  281. package/clients/cli/commands/discover.broken-integration.test.mjs +11 -7
  282. package/clients/cli/commands/discover.doc.mjs +9 -1
  283. package/clients/cli/commands/discover.mjs +144 -93
  284. package/clients/cli/commands/discover.no-source.test.mjs +448 -33
  285. package/clients/cli/commands/discover.sources.test.mjs +20 -1
  286. package/clients/cli/commands/docs.doc.mjs +1 -0
  287. package/clients/cli/commands/docs.mjs +49 -19
  288. package/clients/cli/commands/docs.test.mjs +2 -2
  289. package/clients/cli/commands/doctor-integration-components.doc.mjs +19 -4
  290. package/clients/cli/commands/doctor-integration-docs.doc.mjs +20 -5
  291. package/clients/cli/commands/doctor-integration-templates.doc.mjs +1 -0
  292. package/clients/cli/commands/doctor-integration-validate.doc.mjs +22 -2
  293. package/clients/cli/commands/doctor-integration.test.mjs +888 -3
  294. package/clients/cli/commands/doctor.doc.mjs +12 -2
  295. package/clients/cli/commands/doctor.mjs +292 -7
  296. package/clients/cli/commands/doctor.test.mjs +10 -4
  297. package/clients/cli/commands/gap-report.doc.mjs +15 -2
  298. package/clients/cli/commands/gap-report.mjs +24 -2
  299. package/clients/cli/commands/gap-report.test.mjs +79 -0
  300. package/clients/cli/commands/hook.doc.mjs +1 -0
  301. package/clients/cli/commands/init.behavior.test.mjs +5 -0
  302. package/clients/cli/commands/init.doc.mjs +29 -3
  303. package/clients/cli/commands/integration-add.doc.mjs +7 -0
  304. package/clients/cli/commands/integration-authoring.test.mjs +49 -3
  305. package/clients/cli/commands/integration-check.doc.mjs +93 -0
  306. package/clients/cli/commands/integration-check.test.mjs +441 -0
  307. package/clients/cli/commands/integration-pack.doc.mjs +8 -1
  308. package/clients/cli/commands/integration-verify.doc.mjs +8 -1
  309. package/clients/cli/commands/integration.doc.mjs +6 -2
  310. package/clients/cli/commands/integration.mjs +119 -7
  311. package/clients/cli/commands/json-contract.test.mjs +11 -2
  312. package/clients/cli/commands/manifest.doc.mjs +1 -0
  313. package/clients/cli/commands/outside-project.test.mjs +774 -0
  314. package/clients/cli/commands/search.doc.mjs +1 -0
  315. package/clients/cli/commands/search.mjs +16 -11
  316. package/clients/cli/commands/search.test.mjs +429 -296
  317. package/clients/cli/commands/setup-nudge.test.mjs +102 -2
  318. package/clients/cli/commands/swizzle.doc.mjs +7 -0
  319. package/clients/cli/commands/swizzle.mjs +5 -3
  320. package/clients/cli/commands/swizzle.routing.test.mjs +19 -2
  321. package/clients/cli/commands/template.doc.mjs +1 -0
  322. package/clients/cli/commands/text-json-parity.test.mjs +5 -0
  323. package/clients/cli/commands/theme-add-lifecycle.test.mjs +3 -3
  324. package/clients/cli/commands/theme-add.doc.mjs +8 -1
  325. package/clients/cli/commands/theme-build.doc.mjs +3 -2
  326. package/clients/cli/commands/theme-eject.doc.mjs +1 -0
  327. package/clients/cli/commands/theme-list.behavior.test.mjs +6 -0
  328. package/clients/cli/commands/theme-list.doc.mjs +1 -0
  329. package/clients/cli/commands/theme-palette-generate.doc.mjs +1 -0
  330. package/clients/cli/commands/theme-remove.doc.mjs +7 -0
  331. package/clients/cli/commands/theme-targets.doc.mjs +1 -0
  332. package/clients/cli/commands/theme-template.doc.mjs +1 -0
  333. package/clients/cli/commands/theme-use.doc.mjs +7 -0
  334. package/clients/cli/commands/upgrade.config-ordering.test.mjs +2 -1
  335. package/clients/cli/commands/upgrade.doc.mjs +22 -1
  336. package/clients/cli/commands/upgrade.file-protection.test.mjs +3 -2
  337. package/clients/cli/commands/upgrade.hook-output.test.mjs +2 -1
  338. package/clients/cli/commands/upgrade.older-cli.test.mjs +160 -0
  339. package/clients/cli/index.mjs +112 -16
  340. package/clients/cli/lib/cli-error.mjs +4 -1
  341. package/clients/cli/lib/component-format.test.mjs +1 -1
  342. package/clients/cli/lib/define-command.mjs +2 -2
  343. package/clients/cli/lib/json-shim.mjs +3 -1
  344. package/clients/cli/lib/manifest.d.ts +9 -0
  345. package/clients/cli/lib/manifest.mjs +17 -1
  346. package/clients/cli/lib/manifest.test.mjs +25 -0
  347. package/foundation/agent-docs/agent-doc-state.d.mts +35 -6
  348. package/foundation/agent-docs/agent-doc-state.mjs +66 -6
  349. package/foundation/agent-docs/agent-docs.d.mts +4 -3
  350. package/foundation/agent-docs/agent-docs.mjs +11 -9
  351. package/foundation/agent-docs/computer-agent-text.d.mts +133 -0
  352. package/foundation/agent-docs/computer-agent-text.mjs +623 -0
  353. package/foundation/agent-docs/computer-agent-text.test.mjs +602 -0
  354. package/foundation/config/project.d.mts +35 -3
  355. package/foundation/config/project.mjs +226 -7
  356. package/foundation/config/project.test.mjs +66 -0
  357. package/foundation/config/theme-state.mjs +5 -4
  358. package/foundation/discovery/authoring-self-docs.mjs +2 -1
  359. package/foundation/discovery/cli-self-docs.mjs +20 -3
  360. package/foundation/discovery/cli-self-docs.test.mjs +3 -2
  361. package/foundation/discovery/component-discovery.d.mts +190 -10
  362. package/foundation/discovery/component-discovery.export-names.test.mjs +760 -0
  363. package/foundation/discovery/component-discovery.mjs +864 -56
  364. package/foundation/discovery/component-discovery.test.mjs +96 -0
  365. package/foundation/discovery/component-replacement.mjs +20 -1
  366. package/foundation/discovery/component-replacement.test.mjs +40 -0
  367. package/foundation/discovery/copied-passages.d.mts +216 -0
  368. package/foundation/discovery/copied-passages.mjs +720 -0
  369. package/foundation/discovery/copied-passages.test.mjs +124 -0
  370. package/foundation/discovery/doc-consistency.d.mts +34 -0
  371. package/foundation/discovery/doc-consistency.mjs +513 -0
  372. package/foundation/discovery/doc-consistency.test.mjs +347 -0
  373. package/foundation/discovery/doc-fluff.d.mts +112 -0
  374. package/foundation/discovery/doc-fluff.mjs +328 -0
  375. package/foundation/discovery/doc-fluff.test.mjs +142 -0
  376. package/foundation/discovery/doc-types.d.mts +157 -0
  377. package/foundation/discovery/doc-types.mjs +1231 -0
  378. package/foundation/discovery/doc-types.test.mjs +744 -0
  379. package/foundation/discovery/docs-discovery.d.mts +88 -0
  380. package/foundation/discovery/docs-discovery.mjs +83 -2
  381. package/foundation/discovery/integration-doc-files.d.mts +22 -0
  382. package/foundation/discovery/integration-doc-files.mjs +231 -0
  383. package/foundation/discovery/integration-doc-files.test.mjs +246 -0
  384. package/foundation/discovery/module-exports.d.mts +27 -0
  385. package/foundation/discovery/module-exports.mjs +356 -31
  386. package/foundation/discovery/module-exports.test.mjs +172 -0
  387. package/foundation/discovery/package-tsconfig.d.mts +14 -0
  388. package/foundation/discovery/package-tsconfig.mjs +228 -0
  389. package/foundation/discovery/package-tsconfig.test.mjs +210 -0
  390. package/foundation/discovery/project-typescript.d.mts +32 -0
  391. package/foundation/discovery/project-typescript.mjs +89 -0
  392. package/foundation/discovery/prop-drift.d.mts +297 -0
  393. package/foundation/discovery/prop-drift.mjs +2428 -0
  394. package/foundation/discovery/prop-drift.test.mjs +2337 -0
  395. package/foundation/discovery/runner-allowlist.d.mts +40 -0
  396. package/foundation/discovery/runner-allowlist.mjs +53 -0
  397. package/foundation/discovery/stray-pointers.d.mts +52 -0
  398. package/foundation/discovery/stray-pointers.mjs +293 -0
  399. package/foundation/discovery/stray-pointers.test.mjs +86 -0
  400. package/foundation/discovery/template-adapter.mjs +74 -5
  401. package/foundation/discovery/template-needs.mjs +5 -2
  402. package/foundation/discovery/theme-imports.mjs +8 -4
  403. package/foundation/discovery/theme-imports.test.mjs +1 -1
  404. package/foundation/discovery/type-match.d.mts +53 -0
  405. package/foundation/discovery/type-match.mjs +660 -0
  406. package/foundation/discovery/type-match.test.mjs +169 -0
  407. package/foundation/discovery/type-text.d.mts +68 -0
  408. package/foundation/discovery/type-text.mjs +658 -0
  409. package/foundation/discovery/type-text.test.mjs +196 -0
  410. package/foundation/discovery/unresolved-references.d.mts +62 -0
  411. package/foundation/discovery/unresolved-references.mjs +790 -0
  412. package/foundation/discovery/unresolved-references.test.mjs +567 -0
  413. package/foundation/doc-compiler/doc-loads.test.mjs +51 -1
  414. package/foundation/doc-compiler/inputs.mjs +27 -1
  415. package/foundation/doc-compiler/inputs.test.mjs +0 -1
  416. package/foundation/doc-compiler/links.d.mts +41 -5
  417. package/foundation/doc-compiler/links.mjs +167 -28
  418. package/foundation/doc-compiler/links.test.mjs +97 -4
  419. package/foundation/doc-compiler/read.d.mts +1 -1
  420. package/foundation/doc-compiler/tree.mjs +30 -1
  421. package/foundation/doc-compiler/tree.test.mjs +8 -1
  422. package/foundation/env/cli-command.d.mts +83 -0
  423. package/foundation/env/cli-command.mjs +152 -0
  424. package/foundation/env/cli-command.test.mjs +129 -0
  425. package/foundation/env/node-version.test.mjs +15 -3
  426. package/foundation/env/package-manager.d.mts +93 -9
  427. package/foundation/env/package-manager.global.test.mjs +66 -0
  428. package/foundation/env/package-manager.mjs +184 -13
  429. package/foundation/env/package-manager.test.mjs +341 -15
  430. package/foundation/env/project-scope.d.mts +9 -0
  431. package/foundation/env/project-scope.mjs +33 -0
  432. package/foundation/env/search-path.d.mts +42 -0
  433. package/foundation/env/search-path.mjs +205 -0
  434. package/foundation/env/search-path.test.mjs +122 -0
  435. package/foundation/env/user-dirs.d.mts +39 -0
  436. package/foundation/env/user-dirs.mjs +93 -0
  437. package/foundation/env/user-dirs.test.mjs +72 -0
  438. package/foundation/env/user-settings.d.mts +46 -0
  439. package/foundation/env/user-settings.mjs +106 -0
  440. package/foundation/env/user-settings.test.mjs +64 -0
  441. package/foundation/fs/file-protection.mjs +15 -1
  442. package/foundation/fs/module-loader.d.mts +2 -1
  443. package/foundation/fs/module-loader.mjs +60 -4
  444. package/foundation/fs/module-loader.stdout.test.mjs +3 -2
  445. package/foundation/fs/module-loader.test.mjs +194 -0
  446. package/foundation/fs/path-compare.d.mts +33 -0
  447. package/foundation/fs/path-compare.mjs +73 -0
  448. package/foundation/fs/paths.d.mts +57 -1
  449. package/foundation/fs/paths.mjs +131 -3
  450. package/foundation/fs/paths.test.mjs +10 -1
  451. package/foundation/integrations/computer-integrations.d.mts +78 -0
  452. package/foundation/integrations/computer-integrations.mjs +316 -0
  453. package/foundation/integrations/computer-integrations.test.mjs +292 -0
  454. package/foundation/integrations/contribution-inventory.d.mts +11 -0
  455. package/foundation/integrations/contribution-inventory.mjs +56 -3
  456. package/foundation/integrations/integration-warnings.mjs +3 -2
  457. package/foundation/integrations/integration-warnings.test.mjs +8 -9
  458. package/foundation/integrations/integrations.d.mts +25 -1
  459. package/foundation/integrations/integrations.mjs +46 -3
  460. package/foundation/integrations/issue.ts +6 -0
  461. package/foundation/integrations/validate-contributions.d.mts +2 -0
  462. package/foundation/integrations/validate-contributions.mjs +41 -1
  463. package/foundation/release/agent-text-worker.d.mts +4 -0
  464. package/foundation/release/agent-text-worker.mjs +52 -0
  465. package/foundation/release/cache.d.mts +310 -0
  466. package/foundation/release/cache.mjs +1216 -0
  467. package/foundation/release/cache.test.mjs +920 -0
  468. package/foundation/release/choose.d.mts +207 -0
  469. package/foundation/release/choose.mjs +677 -0
  470. package/foundation/release/choose.test.mjs +427 -0
  471. package/foundation/release/core-worker.d.mts +4 -0
  472. package/foundation/release/core-worker.mjs +38 -0
  473. package/foundation/release/global-state.d.mts +259 -0
  474. package/foundation/release/global-state.mjs +472 -0
  475. package/foundation/release/global-state.test.mjs +381 -0
  476. package/foundation/release/handoff.d.mts +60 -0
  477. package/foundation/release/handoff.e2e.test.mjs +278 -0
  478. package/foundation/release/handoff.mjs +357 -0
  479. package/foundation/release/lock-lease.d.mts +23 -0
  480. package/foundation/release/lock-lease.mjs +80 -0
  481. package/foundation/release/notices.d.mts +253 -0
  482. package/foundation/release/notices.mjs +596 -0
  483. package/foundation/release/notices.test.mjs +565 -0
  484. package/foundation/release/npm-guard.d.mts +4 -0
  485. package/foundation/release/npm-guard.mjs +126 -0
  486. package/foundation/release/project.d.mts +141 -0
  487. package/foundation/release/project.mjs +466 -0
  488. package/foundation/release/project.test.mjs +288 -0
  489. package/foundation/release/refresh-worker.d.mts +4 -0
  490. package/foundation/release/refresh-worker.mjs +27 -0
  491. package/foundation/release/run-context.d.mts +47 -0
  492. package/foundation/release/run-context.mjs +149 -0
  493. package/foundation/release/update-notice-env.d.mts +28 -0
  494. package/foundation/release/update-notice-env.mjs +39 -0
  495. package/foundation/response/base.d.ts +26 -3
  496. package/foundation/response/error-codes.d.mts +3 -1
  497. package/foundation/response/error-codes.doc.mjs +12 -0
  498. package/foundation/response/error-codes.mjs +17 -0
  499. package/foundation/response/error-codes.test.mjs +2 -0
  500. package/foundation/response/json-contract.test.mjs +7 -2
  501. package/foundation/response/json.mjs +4 -1
  502. package/foundation/response/response-types.doc.mjs +22 -11
  503. package/foundation/response/response-types.doc.test.mjs +46 -0
  504. package/foundation/response/response.doc.mjs +3 -3
  505. package/foundation/xle/browser-imports.test.mjs +74 -0
  506. package/foundation/xle/registry.mjs +4 -0
  507. package/foundation/xle/registry.test.mjs +28 -0
  508. package/package.json +11 -11
  509. package/scripts/postinstall.mjs +24 -5
@@ -18,8 +18,11 @@ export const doc = {
18
18
  'Runs read-only project health diagnostics by default: Node version, @astryxdesign/core ' +
19
19
  'install and version alignment, theme packages, generated app themes when present, earlier copied-theme migration, config, integrations (every one astryx.config names must load, linked without a config entry, ' +
20
20
  'provider identity, contribution issues), agent docs, core peer dependencies, package manager, ' +
21
- "and the docs the CLI reads. It writes nothing, but loading astryx.config runs that file's code. " +
22
- 'The `integration` subcommands provide authoring checks for one integration package.',
21
+ "the docs the CLI reads, the newest release the computer knows of, with the install and upgrade commands when the project's release is older, and every `astryx` command on the search path, warning when the first one is not the Astryx CLI or predates the hand-off. It writes nothing, but loading astryx.config runs that file's code. " +
22
+ 'The `integration` subcommands provide authoring checks for one integration package. ' +
23
+ 'Outside a project it checks the computer instead: the Node version, the release that answered, ' +
24
+ 'the newest release this computer knows of, with the command that upgrades the CLI installed for the computer when it is older, the astryx commands on the search path, the integrations installed for the computer (warning about one it skips), and whether the computer-level agent text (`astryx init --user`) is present, matches this release, was edited, or is turned off (info only). ' +
25
+ 'It never fails a check because the folder has no project.',
23
26
  fn: 'doctor',
24
27
  subcommands: ['integration'],
25
28
  examples: [
@@ -42,9 +45,16 @@ export const doc = {
42
45
  cli: 'astryx doctor integration docs @acme/widgets',
43
46
  },
44
47
  ],
48
+ outsideProject: 'own',
45
49
  exitCodes: [
46
50
  {code: 0, when: 'no checks failed (warnings are allowed)'},
47
51
  {code: 1, when: 'one or more checks failed'},
48
52
  ],
49
53
  related: ['init', 'upgrade'],
54
+ notes: [
55
+ {
56
+ type: 'prose',
57
+ text: "In global mode: outside a project, doctor checks your computer instead of a folder: `node-version`, `answering-release` (the version that answered), `newest-release`, `cli-on-path` (every astryx command on your PATH), `computer-integrations` (integrations installed globally), and `computer-agent-text` (the agent guide). It never fails a check because the folder has no project. In a project, the project's version of Astryx runs it, whichever astryx command you start. See {@link namespace:global} and {@link generic:who-answers}.",
58
+ },
59
+ ],
50
60
  };
@@ -38,6 +38,8 @@ import {doc as integrationDocConflictsFn} from '../../../api/integration/integra
38
38
  import {NO_RESULT_SET} from '../../../foundation/debug/index.mjs';
39
39
  import {cliError} from '../lib/cli-error.mjs';
40
40
  import {ERROR_CODES} from '../../../foundation/response/error-codes.mjs';
41
+ import {formatCliCommand} from '../../../foundation/env/package-manager.mjs';
42
+ import {isOutsideProject} from '../../../foundation/env/project-scope.mjs';
41
43
 
42
44
  const STATUS = {
43
45
  pass: '[ok]',
@@ -92,12 +94,91 @@ function issueBlocks(issues) {
92
94
  return [
93
95
  section('Integration issues'),
94
96
  records(issues, {
95
- fields: ['severity', 'code', 'message'],
97
+ fields: ['severity', 'code', 'message', 'fix', 'file', 'line'],
96
98
  format: {severity: statusToken},
97
99
  }),
98
100
  ];
99
101
  }
100
102
 
103
+ /**
104
+ * The docs checked against the authoring types: a visible line when it was
105
+ * skipped, the findings (all warnings), the files it could not check, or an
106
+ * [ok] (never after a failed check).
107
+ * @param {import('../../../api/integration/validate-integration.type.mjs').DocTypesReport | undefined} report
108
+ * @param {boolean} failed
109
+ */
110
+ function docTypesBlocks(report, failed) {
111
+ if (!report) return [];
112
+ if (
113
+ report.status === 'checked' &&
114
+ report.docs === 0 &&
115
+ report.findings.length === 0 &&
116
+ report.unchecked.length === 0
117
+ ) {
118
+ return [
119
+ text('[info] No doc files to type-check.'),
120
+ ...(report.fixtures
121
+ ? [text(`[info] ${report.fixtures} test fixture doc(s) not checked.`)]
122
+ : []),
123
+ ];
124
+ }
125
+ if (report.status === 'skipped') {
126
+ return [text(`[skip] Docs not type-checked: ${report.reason}`)];
127
+ }
128
+ const blocks = [
129
+ section(
130
+ `Checking ${report.docs} doc(s) against the authoring types${report.typescript ? ` (TypeScript ${report.typescript})` : ''}`,
131
+ ),
132
+ ];
133
+ if (report.findings.length > 0) {
134
+ const files = new Set(report.findings.map(f => f.doc)).size;
135
+ blocks.push(
136
+ records(report.findings, {
137
+ fields: [
138
+ 'severity',
139
+ 'doc',
140
+ 'line',
141
+ 'export',
142
+ 'type',
143
+ 'field',
144
+ 'problem',
145
+ 'message',
146
+ 'fix',
147
+ ],
148
+ format: {severity: statusToken},
149
+ }),
150
+ text(
151
+ `${report.findings.length} doc field(s) in ${files} file(s) do not match the authoring types. ` +
152
+ 'These are warnings; `astryx docs authoring` lists every field.',
153
+ ),
154
+ );
155
+ }
156
+ if (report.unchecked.length > 0) {
157
+ blocks.push(
158
+ text(`[info] Not type-checked: ${report.unchecked.length} doc file(s):`),
159
+ records(report.unchecked, {fields: ['doc', 'reason']}),
160
+ );
161
+ }
162
+ if (report.fixtures) {
163
+ blocks.push(
164
+ text(`[info] ${report.fixtures} test fixture doc(s) not checked.`),
165
+ );
166
+ }
167
+ if (report.findings.length === 0 && report.docs > 0) {
168
+ // After a failed check an [ok] reads as a pass, but the result still shows.
169
+ blocks.push(
170
+ text(
171
+ failed
172
+ ? `[info] No doc field differs from its authoring type (${report.docs} checked).`
173
+ : report.unchecked.length > 0
174
+ ? `[ok] Every doc that was checked matches its authoring type (${report.docs} checked).`
175
+ : `[ok] Every doc matches its authoring type (${report.docs} checked).`,
176
+ ),
177
+ );
178
+ }
179
+ return blocks;
180
+ }
181
+
101
182
  /**
102
183
  * @param {import('../../../api/integration/validate-integration.type.mjs').ValidateIntegrationResponse['data']} data
103
184
  */
@@ -110,15 +191,19 @@ function printIntegrationValidation(data) {
110
191
  ...(data.issues.length > 0
111
192
  ? [
112
193
  records(data.issues, {
113
- fields: ['severity', 'code', 'message'],
194
+ fields: ['severity', 'code', 'message', 'fix', 'file', 'line'],
114
195
  format: {severity: statusToken},
115
196
  }),
116
197
  ]
117
198
  : [text('[ok] No integration issues found.')]),
199
+ ...(data.notes ?? []).map(note =>
200
+ text(`note (${note.code}): ${note.message}`),
201
+ ),
118
202
  text(
119
203
  `Summary: ${errors} error${errors === 1 ? '' : 's'}, ` +
120
204
  `${warnings} warning${warnings === 1 ? '' : 's'}`,
121
205
  ),
206
+ ...docTypesBlocks(data.docTypes, errors > 0),
122
207
  );
123
208
  }
124
209
 
@@ -173,6 +258,101 @@ function printTemplateConflicts(data) {
173
258
  emit(...output);
174
259
  }
175
260
 
261
+ /**
262
+ * The documented-props comparison, with every field its JSON carries: a
263
+ * visible line when it was skipped, the findings (all warnings), the
264
+ * components it could not read, and how many were compared with which
265
+ * TypeScript, as an [ok] when nothing differs (never after a failed check).
266
+ * Then how many documented prop types were judged and which were not.
267
+ * @param {import('../../../api/integration/authoring-checks.type.mjs').PropDriftReport | undefined} report
268
+ * @param {boolean} failed
269
+ */
270
+ function propDriftBlocks(report, failed) {
271
+ if (!report) return [];
272
+ if (report.status === 'skipped') {
273
+ return [text(`[skip] Props not compared: ${report.reason}`)];
274
+ }
275
+ const blocks = [];
276
+ if (report.findings.length > 0) {
277
+ // Two docs of one name are two components.
278
+ const components = new Set(
279
+ report.findings.map(f => `${f.doc}\0${f.component}`),
280
+ ).size;
281
+ blocks.push(
282
+ records(report.findings, {
283
+ fields: [
284
+ 'severity',
285
+ 'component',
286
+ 'prop',
287
+ 'kind',
288
+ 'message',
289
+ 'doc',
290
+ 'source',
291
+ 'docType',
292
+ 'codeType',
293
+ 'docRequired',
294
+ 'codeRequired',
295
+ 'docDefault',
296
+ 'codeDefault',
297
+ 'fix',
298
+ ],
299
+ format: {severity: statusToken},
300
+ }),
301
+ text(
302
+ `${report.findings.length} prop(s) differ between the docs and the code in ${components} of ${report.components} component(s) compared (TypeScript ${report.typescript}). ` +
303
+ 'These are warnings: update the doc, or the code if the doc is right.',
304
+ ),
305
+ );
306
+ }
307
+ if (report.unchecked.length > 0) {
308
+ blocks.push(
309
+ text(
310
+ `[info] Props not compared for ${report.unchecked.length} documented component(s):`,
311
+ ),
312
+ records(report.unchecked, {fields: ['component', 'doc', 'reason']}),
313
+ );
314
+ }
315
+ const notChecked = report.propsNotChecked ?? [];
316
+ if (notChecked.length > 0) {
317
+ blocks.push(
318
+ text(
319
+ `[info] Props not checked for a doc entry in ${notChecked.length} component(s):`,
320
+ ),
321
+ records(notChecked, {fields: ['component', 'doc', 'reason', 'props']}),
322
+ );
323
+ }
324
+ if (report.findings.length === 0) {
325
+ const apart =
326
+ notChecked.length > 0 ? ', apart from the props not checked' : '';
327
+ blocks.push(
328
+ text(
329
+ !failed && report.components > 0
330
+ ? `[ok] Documented prop names and required flags match the code in ${report.components} component(s)${apart} (TypeScript ${report.typescript}).`
331
+ : `[info] Props compared in ${report.components} component(s) (TypeScript ${report.typescript}).`,
332
+ ),
333
+ );
334
+ }
335
+ if (report.typesSkipped) {
336
+ blocks.push(text(`[skip] Prop types not judged: ${report.typesSkipped}.`));
337
+ return blocks;
338
+ }
339
+ const notCompared = report.typesNotCompared.flatMap(entry => entry.props);
340
+ blocks.push(
341
+ text(
342
+ `[info] Documented prop types judged: ${report.typesCompared}; not compared: ${notCompared.length}. ` +
343
+ 'A type is judged when the doc writes it the same as the source (exactly, if it contains `readonly`), or when both are primitives, literals, arrays and unions of those, or names for them.',
344
+ ),
345
+ );
346
+ if (notCompared.length > 0) {
347
+ blocks.push(
348
+ records(report.typesNotCompared, {
349
+ fields: ['component', 'doc', 'reason', 'props'],
350
+ }),
351
+ );
352
+ }
353
+ return blocks;
354
+ }
355
+
176
356
  /**
177
357
  * @param {import('../../../api/integration/authoring-checks.type.mjs').IntegrationComponentConflictResponse['data']} data
178
358
  */
@@ -186,7 +366,8 @@ function printComponentConflicts(data) {
186
366
  // An [ok] after a failed check reads as a pass: say nothing it could not check.
187
367
  const failed = data.issues.some(issue => issue.severity === 'error');
188
368
  if (data.conflicts.length === 0) {
189
- if (!failed) output.push(text('[ok] No component names conflict with Core.'));
369
+ if (!failed)
370
+ output.push(text('[ok] No component names conflict with Core.'));
190
371
  } else {
191
372
  output.push(
192
373
  records(data.conflicts, {
@@ -205,6 +386,7 @@ function printComponentConflicts(data) {
205
386
  ),
206
387
  );
207
388
  }
389
+ output.push(...propDriftBlocks(data.propDrift, failed));
208
390
  emit(...output);
209
391
  }
210
392
 
@@ -216,7 +398,16 @@ function printDocConflicts(data) {
216
398
  section(
217
399
  `Checking integration docs: ${integrationLabel(data) ?? '(local package)'}`,
218
400
  ),
219
- ...issueBlocks(data.issues),
401
+ // A copied passage or fluff is listed below with its detail, so its
402
+ // issue is not printed twice.
403
+ ...issueBlocks(
404
+ data.issues.filter(
405
+ issue =>
406
+ issue.code !== 'copied_passage' &&
407
+ issue.code !== 'doc_fluff' &&
408
+ issue.code !== 'stray_doc_pointer',
409
+ ),
410
+ ),
220
411
  ];
221
412
  if (
222
413
  !data.issues.some(
@@ -239,9 +430,99 @@ function printDocConflicts(data) {
239
430
  }),
240
431
  );
241
432
  }
433
+ const copied = data.copiedPassages ?? [];
434
+ if (copied.length === 0) {
435
+ if (!data.issues.some(issue => issue.severity === 'error')) {
436
+ output.push(text('[ok] No topic copies a passage from another.'));
437
+ }
438
+ } else {
439
+ output.push(
440
+ text(
441
+ `[warn] ${copied.length} copied passage(s) (copied_passage): two topics say the same thing word for word. Keep the text in one and link to it from the other.`,
442
+ ),
443
+ records(copied, {
444
+ fields: [
445
+ 'topic',
446
+ 'section',
447
+ 'line',
448
+ 'otherTopic',
449
+ 'otherOwner',
450
+ 'owner',
451
+ 'text',
452
+ 'words',
453
+ 'passages',
454
+ 'message',
455
+ ],
456
+ }),
457
+ );
458
+ }
459
+ const fluff = data.fluff ?? [];
460
+ if (fluff.length === 0) {
461
+ if (!data.issues.some(issue => issue.severity === 'error')) {
462
+ output.push(text('[ok] No filler phrases or low-density topics.'));
463
+ }
464
+ } else {
465
+ output.push(
466
+ text(
467
+ `[warn] ${fluff.length} fluff finding(s) (doc_fluff): words that add length, not meaning. Cut each one.`,
468
+ ),
469
+ records(fluff, {
470
+ fields: [
471
+ 'topic',
472
+ 'kind',
473
+ 'phrase',
474
+ 'section',
475
+ 'file',
476
+ 'line',
477
+ 'density',
478
+ 'message',
479
+ ],
480
+ }),
481
+ );
482
+ }
483
+ const stray = data.strayPointers ?? [];
484
+ if (stray.length === 0) {
485
+ if (!data.issues.some(issue => issue.severity === 'error')) {
486
+ output.push(text('[ok] Every pointer to a doc is a link.'));
487
+ }
488
+ } else {
489
+ output.push(
490
+ text(
491
+ `[warn] ${stray.length} stray doc pointer(s) (stray_doc_pointer): a written \`astryx docs <name>\` where a link would be checked. Replace each with the link shown.`,
492
+ ),
493
+ records(stray, {
494
+ fields: [
495
+ 'topic',
496
+ 'section',
497
+ 'file',
498
+ 'line',
499
+ 'pointer',
500
+ 'link',
501
+ 'message',
502
+ ],
503
+ }),
504
+ );
505
+ }
242
506
  emit(...output);
243
507
  }
244
508
 
509
+ /**
510
+ * Print one `doctor integration` check's result as its own subcommand prints
511
+ * it. `astryx integration check` prints each check it runs through this.
512
+ * @param {import('../../../api/integration/integration-check.type.mjs').IntegrationCheckResponse} response
513
+ */
514
+ export function printIntegrationCheckResponse(response) {
515
+ if (response.type === 'integration.validate') {
516
+ printIntegrationValidation(response.data);
517
+ } else if (response.type === 'integration.component-conflicts') {
518
+ printComponentConflicts(response.data);
519
+ } else if (response.type === 'integration.template-conflicts') {
520
+ printTemplateConflicts(response.data);
521
+ } else {
522
+ printDocConflicts(response.data);
523
+ }
524
+ }
525
+
245
526
  /** @param {import('commander').Command} program */
246
527
  async function runProjectDoctor(program) {
247
528
  const report = await runChecks();
@@ -265,7 +546,7 @@ async function runIntegrationValidation(program, pkg) {
265
546
  noPackage
266
547
  ? 'Nothing was validated: no package.json found. Run this command inside a package directory.'
267
548
  : 'Nothing was validated: no astryx.integration.* found next to package.json. ' +
268
- 'To validate an installed integration: astryx doctor integration validate <package>',
549
+ `To validate an installed integration: ${formatCliCommand('doctor integration validate <package>')}`,
269
550
  ),
270
551
  );
271
552
  } else printIntegrationValidation(result.data);
@@ -288,10 +569,14 @@ async function runAuthoringCheck(program, pkg, kind) {
288
569
 
289
570
  if (program.opts().json) jsonOut(result);
290
571
  else if (!result.data.validated) {
572
+ // Outside a project no integration is installed to check by name
573
+ // (spec AST-081 FR3).
291
574
  emit(
292
575
  text(
293
- 'Nothing was checked: no astryx.integration.* found next to package.json. ' +
294
- `To check an installed integration: astryx doctor integration ${kind} <package>`,
576
+ isOutsideProject()
577
+ ? 'Nothing was checked: no astryx.integration.* found next to package.json, and this folder is outside a project.'
578
+ : 'Nothing was checked: no astryx.integration.* found next to package.json. ' +
579
+ `To check an installed integration: ${formatCliCommand(`doctor integration ${kind} <package>`)}`,
295
580
  ),
296
581
  );
297
582
  } else if (kind === 'templates') {
@@ -20,6 +20,7 @@ import {
20
20
  checkPackageManager,
21
21
  } from '../../../api/doctor/doctor.mjs';
22
22
  import {MIN_NODE_VERSION} from '../../../foundation/env/node-version.mjs';
23
+ import {CLI_VERSION, linkThisCliAsProjectCli} from '../../../test-utils/release-fixtures.mjs';
23
24
 
24
25
  let tmpDir;
25
26
  let logCalls;
@@ -101,7 +102,7 @@ describe('doctor — individual checks', () => {
101
102
  });
102
103
 
103
104
  it('app themes: reports unmanaged state as one INFO check', async () => {
104
- fs.writeFileSync(path.join(tmpDir, 'package.json'), '{"name":"app"}\n');
105
+ fs.writeFileSync(path.join(tmpDir, 'package.json'), '{"name":"app","dependencies":{"@astryxdesign/core":"*"}}\n');
105
106
  const result = await runChecks({cwd: tmpDir});
106
107
  const themeChecks = result.checks.filter(check =>
107
108
  check.id.startsWith('theme-'),
@@ -176,13 +177,16 @@ describe('doctor — individual checks', () => {
176
177
  it('agent-docs: the fix names the scoped package when the project has no local CLI', () => {
177
178
  fs.writeFileSync(path.join(tmpDir, 'package-lock.json'), '{}');
178
179
  const res = checkAgentDocs({cwd: tmpDir});
179
- expect(res.fix).toContain('`npx @astryxdesign/cli init --features agents`');
180
+ expect(res.fix).toContain(
181
+ `\`npx @astryxdesign/cli@${CLI_VERSION} init --features agents\``,
182
+ );
180
183
  });
181
184
 
182
185
  it('agent-docs: the fix keeps `npx astryx` when the project has a local CLI', () => {
183
186
  fs.writeFileSync(path.join(tmpDir, 'package-lock.json'), '{}');
184
187
  fs.mkdirSync(path.join(tmpDir, 'node_modules', '.bin'), {recursive: true});
185
188
  fs.writeFileSync(path.join(tmpDir, 'node_modules', '.bin', 'astryx'), '');
189
+ linkThisCliAsProjectCli(tmpDir);
186
190
  const res = checkAgentDocs({cwd: tmpDir});
187
191
  expect(res.fix).toContain('`npx astryx init --features agents`');
188
192
  });
@@ -301,7 +305,8 @@ describe('doctor — runChecks / report', () => {
301
305
  expect(total).toBe(report.checks.length);
302
306
  });
303
307
 
304
- it('reports a FAIL for core when run in a bare directory', async () => {
308
+ it('reports a FAIL for core in a project that declares Core but installs none', async () => {
309
+ fs.writeFileSync(path.join(tmpDir, 'package.json'), '{"name":"app","dependencies":{"@astryxdesign/core":"*"}}\n');
305
310
  const report = await runChecks({cwd: tmpDir});
306
311
  expect(find(report.checks, 'core-installed').status).toBe('fail');
307
312
  expect(report.summary.fail).toBeGreaterThan(0);
@@ -346,7 +351,8 @@ describe('doctor — command', () => {
346
351
  process.exitCode = prevExit;
347
352
  });
348
353
 
349
- it('sets exit code 1 when a check FAILs (bare dir, no @astryxdesign/core)', async () => {
354
+ it('sets exit code 1 when a check FAILs (Core declared, not installed)', async () => {
355
+ fs.writeFileSync(path.join(tmpDir, 'package.json'), '{"name":"app","dependencies":{"@astryxdesign/core":"*"}}\n');
350
356
  const prevCwd = process.cwd();
351
357
  const prevExit = process.exitCode;
352
358
  process.exitCode = undefined;
@@ -16,6 +16,8 @@ export const doc = {
16
16
  'Reports a missing component, variant, layout, styling, accessibility, API, or documentation capability to the package that owns it: the --package you name, else the integration whose component replaces it, else the one package that provides the component, else Core. ' +
17
17
  'Every configured handler receives the report: the project config handler first, then each integration handler in config order. Public handlers run only with --confirm-public; internal handlers always run. A failing handler does not stop the others. ' +
18
18
  "With no handler it files a GitHub issue for the owning package, only with --confirm-public (without it nothing is sent), or returns the package's issues URL when that is not on GitHub. " +
19
+ "With --to <package>, the report goes only to that integration's issues URL instead, under the same --confirm-public rule. " +
20
+ 'When loaded integrations name their own issues URLs, the output lists every destination: an agent asks the person where to file and passes their choice with --to; it never picks a package itself, and without a choice the default is used. ' +
19
21
  'The report records whether an agent or a person ran it.',
20
22
  fn: 'gapReport',
21
23
  args: [
@@ -55,13 +57,19 @@ export const doc = {
55
57
  flag: '--confirm-public',
56
58
  param: 'options.confirmPublic',
57
59
  description:
58
- 'Allow public delivery: public handlers run, and with no handler it files a GitHub issue with your gh login',
60
+ 'Allow public delivery: public handlers run, and a GitHub issues URL gets an issue filed with your gh login',
61
+ },
62
+ {
63
+ flag: '--to <package>',
64
+ param: 'options.to',
65
+ description:
66
+ "Send the report only to this loaded integration's issues URL, as the person chose; the destinations the output lists are the choices",
59
67
  },
60
68
  {
61
69
  flag: '--list-categories',
62
70
  param: 'options.listCategories',
63
71
  description:
64
- 'List valid report categories without filing; the component and the other gap-report options are refused with ERR_INVALID_ARGUMENT',
72
+ 'List valid report categories, and the destinations when there is more than one, without filing; the component and the other gap-report options are refused with ERR_INVALID_ARGUMENT',
65
73
  },
66
74
  ],
67
75
  examples: [
@@ -70,7 +78,12 @@ export const doc = {
70
78
  label: 'Prepare a report (nothing public happens without --confirm-public)',
71
79
  cli: "astryx gap-report Button --category missing_variant --reason 'Need a compact size'",
72
80
  },
81
+ {
82
+ label: 'File where the person chose',
83
+ cli: "astryx gap-report Button --category missing_variant --reason 'Need a compact size' --to @acme/widgets",
84
+ },
73
85
  ],
86
+ outsideProject: 'same',
74
87
  exitCodes: [
75
88
  {
76
89
  code: 0,
@@ -7,7 +7,7 @@
7
7
  import {gapReport as gapReportApi} from '../../../api/gap-report/gap-report.mjs';
8
8
  import {NO_RESULT_SET, resultSet} from '../../../foundation/debug/index.mjs';
9
9
  import {jsonOut} from '../../../foundation/response/json.mjs';
10
- import {emit, record, records, section} from '../formatters/index.mjs';
10
+ import {emit, record, records, section, text} from '../formatters/index.mjs';
11
11
  import {cliError} from '../lib/cli-error.mjs';
12
12
  import {defineCommand} from '../lib/define-command.mjs';
13
13
  import {doc as gapReportCommand} from './gap-report.doc.mjs';
@@ -19,7 +19,7 @@ export function registerGapReport(program) {
19
19
  fn: gapReportFn,
20
20
  action: async (
21
21
  /** @type {string|undefined} */ component,
22
- /** @type {{category?: string, reason?: string, additionalContext?: string, package?: string, confirmPublic?: boolean, listCategories?: boolean}} */ options,
22
+ /** @type {{category?: string, reason?: string, additionalContext?: string, package?: string, confirmPublic?: boolean, listCategories?: boolean, to?: string}} */ options,
23
23
  ) => {
24
24
  /** @type {Awaited<ReturnType<typeof gapReportApi>>} */
25
25
  let result;
@@ -35,6 +35,7 @@ export function registerGapReport(program) {
35
35
  package: options.package,
36
36
  confirmPublic: options.confirmPublic,
37
37
  listCategories: options.listCategories,
38
+ to: options.to,
38
39
  });
39
40
  } catch (error) {
40
41
  const err =
@@ -78,6 +79,9 @@ export function registerGapReport(program) {
78
79
  section('Gap report categories'),
79
80
  records(result.data, {fields: ['value', 'label']}),
80
81
  );
82
+ if (result.meta) {
83
+ emit(...destinationBlocks(result.meta.destinations, result.meta.ask));
84
+ }
81
85
  return answered;
82
86
  }
83
87
 
@@ -119,8 +123,26 @@ export function registerGapReport(program) {
119
123
  );
120
124
  }
121
125
 
126
+ if (result.data.destinations && result.data.ask) {
127
+ emit(...destinationBlocks(result.data.destinations, result.data.ask));
128
+ }
129
+
122
130
  if (isFailure) process.exitCode = 1;
123
131
  return answered;
124
132
  },
125
133
  });
126
134
  }
135
+
136
+ /**
137
+ * The destinations a report can go to, then the line telling an agent to ask
138
+ * the person (architecture:cli-surface INV36).
139
+ * @param {import('../../../api/gap-report/gap-report.type.mjs').GapReportDestination[]} destinations
140
+ * @param {string} ask
141
+ */
142
+ function destinationBlocks(destinations, ask) {
143
+ return [
144
+ section('Destinations'),
145
+ records(destinations, {fields: ['to', 'default', 'issuesUrl']}),
146
+ text(ask),
147
+ ];
148
+ }
@@ -331,3 +331,82 @@ describe('gap-report control docs', () => {
331
331
  ).rejects.toMatchObject({code: 'ERR_INVALID_ARGUMENT'});
332
332
  });
333
333
  });
334
+
335
+ describe('gap-report destinations in text', () => {
336
+ it('lists the destinations and tells the agent to ask the person', async () => {
337
+ const cwd = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-gap-dest-'));
338
+ try {
339
+ fs.writeFileSync(
340
+ path.join(cwd, 'package.json'),
341
+ JSON.stringify({name: 'fixture-app'}),
342
+ );
343
+ const pkg = path.join(cwd, 'node_modules', '@test', 'widgets');
344
+ fs.mkdirSync(pkg, {recursive: true});
345
+ fs.writeFileSync(
346
+ path.join(pkg, 'package.json'),
347
+ JSON.stringify({name: '@test/widgets', version: '1.0.0'}),
348
+ );
349
+ fs.writeFileSync(
350
+ path.join(pkg, 'astryx.integration.mjs'),
351
+ "export default {issuesUrl: 'https://tracker.example/widgets'};\n",
352
+ );
353
+ fs.writeFileSync(
354
+ path.join(cwd, 'astryx.config.mjs'),
355
+ `export default {
356
+ integrations: ['@test/widgets'],
357
+ gapReport: {audience: 'internal', handle: () => ({status: 'filed', message: 'shared'})},
358
+ };\n`,
359
+ );
360
+
361
+ const listed = await runCli(['gap-report', '--list-categories'], {cwd});
362
+ expect(listed.status).toBe(0);
363
+ expect(listed.stdout).toContain('Destinations');
364
+ expect(listed.stdout).toMatch(/^default:\s+true$/m);
365
+ expect(listed.stdout).toMatch(/^to:\s+@test\/widgets$/m);
366
+ expect(listed.stdout).toMatch(
367
+ /^issuesUrl:\s+https:\/\/tracker\.example\/widgets$/m,
368
+ );
369
+ expect(listed.stdout).toMatch(/ask the person/i);
370
+
371
+ const filed = await runCli(
372
+ [
373
+ 'gap-report',
374
+ 'Button',
375
+ '--category',
376
+ 'docs_gap',
377
+ '--reason',
378
+ 'Need a keyboard example',
379
+ ],
380
+ {cwd},
381
+ );
382
+ expect(filed.status).toBe(0);
383
+ expect(filed.stdout).toContain('Destinations');
384
+ expect(filed.stdout).toMatch(/ask the person/i);
385
+
386
+ const chosen = await runCli(
387
+ [
388
+ 'gap-report',
389
+ 'Button',
390
+ '--category',
391
+ 'docs_gap',
392
+ '--reason',
393
+ 'Need a keyboard example',
394
+ '--to',
395
+ '@test/widgets',
396
+ '--json',
397
+ ],
398
+ {cwd},
399
+ );
400
+ expect(chosen.status).toBe(0);
401
+ expect(JSON.parse(chosen.stdout).data.deliveries).toEqual([
402
+ expect.objectContaining({
403
+ handler: 'issuesUrl',
404
+ status: 'routed_only',
405
+ url: 'https://tracker.example/widgets',
406
+ }),
407
+ ]);
408
+ } finally {
409
+ fs.rmSync(cwd, {recursive: true, force: true});
410
+ }
411
+ });
412
+ });
@@ -40,6 +40,7 @@ export const doc = {
40
40
  {label: 'Browse the catalog', cli: 'astryx hook --list'},
41
41
  {label: 'One hook as JSON', cli: 'astryx hook useFocusTrap --json'},
42
42
  ],
43
+ outsideProject: 'same',
43
44
  exitCodes: [
44
45
  {code: 0, when: 'success'},
45
46
  {
@@ -39,6 +39,11 @@ const exists = rel => fs.existsSync(path.join(tmpDir, rel));
39
39
 
40
40
  describe('astryx init — default (no flags)', () => {
41
41
  it('installs the tool-agnostic AGENTS.md cheat sheet and prints next steps', async () => {
42
+ // Inside a project; outside one the steps are left out (outside-project.test.mjs).
43
+ fs.writeFileSync(
44
+ path.join(tmpDir, 'package.json'),
45
+ '{"name":"app","dependencies":{"@astryxdesign/core":"*"}}',
46
+ );
42
47
  const {status, stdout} = await runCli(['init'], {cwd: tmpDir});
43
48
  expect(status).toBe(0);
44
49
  expect(exists('AGENTS.md')).toBe(true);