@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
@@ -43,6 +43,16 @@ import {
43
43
  resolvePackageDir,
44
44
  } from '../integrations/integrations.mjs';
45
45
  import {loadAutolinkCandidates} from '../integrations/autolink.mjs';
46
+ import {
47
+ computerInstallRoots,
48
+ loadComputerIntegrations,
49
+ scanComputerIntegrations,
50
+ } from '../integrations/computer-integrations.mjs';
51
+ import {relativeWithin, samePath} from '../fs/path-compare.mjs';
52
+ import {isOutsideProject} from '../env/project-scope.mjs';
53
+ import {cliVersion} from '../release/run-context.mjs';
54
+ import {isGlobalCopy} from '../release/choose.mjs';
55
+ import {astryxCommandsOnSearchPath} from '../env/search-path.mjs';
46
56
  import {resolveProviders} from '../integrations/provider-resolution.mjs';
47
57
  import {
48
58
  setProject as setDebugProject,
@@ -55,11 +65,12 @@ import {
55
65
  discoverValidIntegrationComponents,
56
66
  } from '../discovery/component-discovery.mjs';
57
67
  import {resolveComponentReplacements} from '../discovery/component-replacement.mjs';
58
- import {findCoreDir} from '../fs/paths.mjs';
68
+ import {CLI_ROOT, findCoreDir, findProjectCoreDir} from '../fs/paths.mjs';
59
69
  import {
60
70
  applyTemplateReplacements,
61
71
  effectiveTemplateDiscovery,
62
72
  discoverAllUnresolved as discoverTemplates,
73
+ discoverCoreTemplates,
63
74
  discoverIntegrationTemplatesForOne,
64
75
  } from '../discovery/template-adapter.mjs';
65
76
  import {
@@ -77,6 +88,7 @@ import {
77
88
  selectIntegrationCodemods,
78
89
  } from '../../assets/codemods/integration-discovery.mjs';
79
90
  import {validateLoadedIntegration} from '../integrations/validate-contributions.mjs';
91
+ import {recordDeclaredCliCommands} from '../env/cli-command.mjs';
80
92
  import {
81
93
  InMemoryConfigCache,
82
94
  cacheKey,
@@ -99,6 +111,80 @@ function errorMessage(err) {
99
111
  return message == null ? String(err) : String(message);
100
112
  }
101
113
 
114
+ /**
115
+ * The computer-level integrations (spec AST-081 FR12), read from package.json
116
+ * files only. Outside a project, those installed beside the running CLI;
117
+ * inside one, where the project's own CLI may be the copy that answers, those
118
+ * installed beside the CLI installed for the computer (see globalCliRoot).
119
+ * @param {string} [cwd]
120
+ * @returns {import('../integrations/computer-integrations.mjs').ComputerIntegrationCandidate[]}
121
+ */
122
+ export function computerIntegrationCandidates(cwd = process.cwd()) {
123
+ try {
124
+ const cliRoot = isOutsideProject(cwd) ? CLI_ROOT : globalCliRoot(cwd);
125
+ return cliRoot
126
+ ? scanComputerIntegrations({cliRoot, release: cliVersion()})
127
+ : [];
128
+ } catch {
129
+ return [];
130
+ }
131
+ }
132
+
133
+ /**
134
+ * The CLI package whose install root holds the computer-level integrations
135
+ * for a folder inside a project: this copy when it was installed outside the
136
+ * project (installed for the computer, or in a tool's package set), else the
137
+ * first `astryx` on the search path that is installed for the computer. A
138
+ * project's own CLI and a release from the per-user cache are neither.
139
+ * @param {string} cwd
140
+ * @returns {string | null}
141
+ */
142
+ function globalCliRoot(cwd) {
143
+ let folder = path.resolve(cwd);
144
+ try {
145
+ folder = fs.realpathSync(folder);
146
+ } catch {
147
+ // A folder that cannot be resolved is compared as written.
148
+ }
149
+ const own = computerInstallRoots(CLI_ROOT);
150
+ if (
151
+ own.length > 0 &&
152
+ !own.some(
153
+ root => samePath(root, folder) || relativeWithin(root, folder) != null,
154
+ )
155
+ ) {
156
+ return CLI_ROOT;
157
+ }
158
+ for (const command of astryxCommandsOnSearchPath()) {
159
+ if (command.cliRoot && isGlobalCopy(command.cliRoot)) {
160
+ return command.cliRoot;
161
+ }
162
+ }
163
+ return null;
164
+ }
165
+
166
+ /**
167
+ * The computer-level integrations a project could add (spec AST-081 FR12):
168
+ * those it does not install, whose peer ranges admit the release that
169
+ * answers, and whose manifest loads.
170
+ * @param {string} cwd
171
+ * @param {Set<string>} installed package names the project loads or declares
172
+ * @returns {Promise<import('../integrations/computer-integrations.mjs').ComputerIntegrationCandidate[]>}
173
+ */
174
+ export async function availableComputerIntegrations(cwd, installed) {
175
+ const candidates = computerIntegrationCandidates(cwd).filter(
176
+ c => !c.incompatible && !installed.has(c.name),
177
+ );
178
+ if (candidates.length === 0) return [];
179
+ const loaded = await loadComputerIntegrations(candidates);
180
+ const usable = new Set(
181
+ loaded.flatMap(entry =>
182
+ entry.integration && !entry.integration.__loadError ? [entry.spec] : [],
183
+ ),
184
+ );
185
+ return candidates.filter(c => usable.has(c.name));
186
+ }
187
+
102
188
  /**
103
189
  * Load the configured integrations one at a time, so a package that cannot
104
190
  * load at all (not installed, no manifest, more than one manifest, not a bare
@@ -337,6 +423,12 @@ export class Project {
337
423
  #cache;
338
424
  /** @type {string} */
339
425
  #hash;
426
+ /**
427
+ * Which Core the project reads: `any`, its installed Core else the Core the
428
+ * answering release carries, or `project`, its installed Core only.
429
+ * @type {'any' | 'project'}
430
+ */
431
+ #core = 'any';
340
432
  /** @type {ProjectIntegrationIssue[]} */
341
433
  #issues = [];
342
434
  /**
@@ -359,6 +451,7 @@ export class Project {
359
451
  * @param {import('../integrations/integrations.mjs').LoadedIntegration[]} init.loadedIntegrations
360
452
  * @param {import('./config-cache.mjs').ConfigCache} init.cache
361
453
  * @param {string} init.hash config content hash
454
+ * @param {'any' | 'project'} [init.core] which Core it reads
362
455
  */
363
456
  constructor({
364
457
  cwd,
@@ -368,7 +461,9 @@ export class Project {
368
461
  loadedIntegrations,
369
462
  cache,
370
463
  hash,
464
+ core = 'any',
371
465
  }) {
466
+ this.#core = core;
372
467
  this.#cwd = cwd;
373
468
  this.#configPath = configPath;
374
469
  this.#config = config;
@@ -383,11 +478,15 @@ export class Project {
383
478
  * and loads the configured integrations. Discovery is NOT run here — it is
384
479
  * lazy and memoized on the returned instance.
385
480
  *
481
+ * `core: 'project'` reads only the project's installed Core, never the Core
482
+ * the answering release carries: the commands that judge or change a
483
+ * project (`doctor`, `swizzle`, `upgrade`) load it so (spec AST-081 FR3).
484
+ *
386
485
  * @param {string} [cwd]
387
- * @param {{cache?: import('./config-cache.mjs').ConfigCache, fresh?: boolean}} [options]
486
+ * @param {{cache?: import('./config-cache.mjs').ConfigCache, fresh?: boolean, core?: 'any' | 'project'}} [options]
388
487
  * @returns {Promise<Project>}
389
488
  */
390
- static async load(cwd = process.cwd(), {cache, fresh = false} = {}) {
489
+ static async load(cwd = process.cwd(), {cache, fresh = false, core = 'any'} = {}) {
391
490
  const resolvedCache = fresh
392
491
  ? new InMemoryConfigCache()
393
492
  : (cache ?? new InMemoryConfigCache());
@@ -439,7 +538,26 @@ export class Project {
439
538
  // existing consumer command doubles as the author's preview. A local copy
440
539
  // replaces the same installed package in place, preserving configured
441
540
  // precedence while making the source being edited authoritative.
442
- const localIntegration = await loadLocalIntegrationSafely(projectDir, fresh);
541
+ const localIntegration = await loadLocalIntegrationSafely(
542
+ projectDir,
543
+ fresh,
544
+ );
545
+
546
+ // Outside a project, the integrations installed beside the running CLI
547
+ // load too (spec AST-081 FR12); a package the folder already loads keeps
548
+ // its own copy. Inside a project they never load.
549
+ const computer = isOutsideProject(cwd)
550
+ ? await loadComputerIntegrations(computerIntegrationCandidates(cwd), {
551
+ fresh,
552
+ exclude: [
553
+ ...loadedIntegrations.map(integration => integration.name),
554
+ ...autolinked.flatMap(candidate =>
555
+ candidate.integration ? [candidate.integration.name] : [],
556
+ ),
557
+ ...(localIntegration ? [localIntegration.name] : []),
558
+ ],
559
+ })
560
+ : [];
443
561
 
444
562
  // Autolinked and local packages can claim a provider ID that a configured
445
563
  // package already holds, so identity is resolved once over the final set.
@@ -450,6 +568,7 @@ export class Project {
450
568
  spec: integration.__spec,
451
569
  })),
452
570
  ...autolinked,
571
+ ...computer,
453
572
  ...(localIntegration
454
573
  ? [
455
574
  {
@@ -461,6 +580,9 @@ export class Project {
461
580
  : []),
462
581
  ]);
463
582
  loadedIntegrations = resolution.integrations;
583
+ // The picker (getCliInvocation) is synchronous, so the commands these
584
+ // integrations declare are recorded here, where they become known.
585
+ recordDeclaredCliCommands(projectDir, loadedIntegrations);
464
586
 
465
587
  // The debug recorder resolves its settings synchronously, long before any
466
588
  // command gets here, so this is where a project's `debug` block gets a
@@ -499,6 +621,7 @@ export class Project {
499
621
  loadedIntegrations,
500
622
  cache: resolvedCache,
501
623
  hash,
624
+ core,
502
625
  });
503
626
  PROVIDER_LEDGERS.set(project, resolution.ledger);
504
627
  return project;
@@ -633,6 +756,18 @@ export class Project {
633
756
  }
634
757
  }
635
758
 
759
+ /**
760
+ * The Core this project reads: its installed Core, else, unless it was
761
+ * loaded with `core: 'project'`, the Core the answering release carries
762
+ * (spec AST-081 FR3). Null when there is none.
763
+ * @returns {string | null}
764
+ */
765
+ coreDir() {
766
+ return this.#core === 'project'
767
+ ? findProjectCoreDir(this.#cwd)
768
+ : findCoreDir(this.#cwd);
769
+ }
770
+
636
771
  /**
637
772
  * Core + integration component ownership records. Wraps
638
773
  * discoverOwnedComponents for core and discoverIntegrationComponents (via
@@ -644,7 +779,7 @@ export class Project {
644
779
  */
645
780
  async components() {
646
781
  return this.#memo('components', async () => {
647
- const coreDir = findCoreDir(this.#cwd);
782
+ const coreDir = this.coreDir();
648
783
  /** @type {Array<{name: string, package: string, group: string|null, docPath: string|null, sourcePath: string|null, issuesUrl: string|undefined}>} */
649
784
  const records = [];
650
785
 
@@ -695,7 +830,7 @@ export class Project {
695
830
  async componentReplacements() {
696
831
  return this.#memo('componentReplacements', async () => {
697
832
  const replacements = await resolveComponentReplacements(
698
- findCoreDir(this.#cwd),
833
+ this.coreDir(),
699
834
  this.#loadedIntegrations,
700
835
  );
701
836
  for (const finding of replacements.findings) {
@@ -821,6 +956,85 @@ export class Project {
821
956
  });
822
957
  }
823
958
 
959
+ /**
960
+ * The components and templates this project reads, as links name them: by
961
+ * owner provider id, kind, and name (a template by its id).
962
+ * @returns {Promise<import('../discovery/docs-discovery.mjs').LinkableContributions>}
963
+ */
964
+ async #linkableContributions() {
965
+ /** @type {Map<string, string>} */
966
+ const providers = new Map([[CORE_PACKAGE, CORE_PACKAGE]]);
967
+ for (const integration of this.#loadedIntegrations) {
968
+ if (integration?.name) {
969
+ providers.set(
970
+ integration.name,
971
+ integration.providerId ?? integration.name,
972
+ );
973
+ }
974
+ }
975
+ const providerOf = (/** @type {string | undefined} */ pkg) =>
976
+ providers.get(pkg ?? CORE_PACKAGE) ?? pkg ?? CORE_PACKAGE;
977
+ // An active replacement answers to the Core name it replaces, so a link
978
+ // to that Core component has to select Core (spec:AST-035 FR11).
979
+ /** @type {Map<string, string>} */
980
+ const replacing = new Map();
981
+ if (this.#loadedIntegrations.length > 0) {
982
+ const {active} = await resolveComponentReplacements(
983
+ this.coreDir(),
984
+ this.#loadedIntegrations,
985
+ );
986
+ for (const each of active) {
987
+ replacing.set(`${each.package}\0${each.name}`, each.target);
988
+ }
989
+ }
990
+ const components = (await this.components()).map(record => {
991
+ const replaces = replacing.get(`${record.package}\0${record.name}`);
992
+ return {
993
+ providerId: providerOf(record.package),
994
+ package: record.package,
995
+ name: record.name,
996
+ docPath: record.docPath,
997
+ ...(replaces == null ? {} : {replaces}),
998
+ };
999
+ });
1000
+ /** @param {any} template @param {string} pkg */
1001
+ const linkable = (template, pkg) => ({
1002
+ providerId: providerOf(pkg),
1003
+ package: pkg,
1004
+ type: template.type,
1005
+ id: template.dirName,
1006
+ name: template.name,
1007
+ ...(template.displayName == null
1008
+ ? {}
1009
+ : {displayName: template.displayName}),
1010
+ ...(template.description == null
1011
+ ? {}
1012
+ : {description: template.description}),
1013
+ ...(typeof template.replaces === 'string'
1014
+ ? {replaces: template.replaces}
1015
+ : {}),
1016
+ });
1017
+ /** @param {any} template */
1018
+ const isPageOrBlock = template =>
1019
+ template.type === 'page' || template.type === 'block';
1020
+ // Every Core template, a replaced one too, since `--package
1021
+ // @astryxdesign/core` still opens it; everyone else's as unqualified
1022
+ // lookups see them, an active replacement with the id it takes over.
1023
+ const templates = [
1024
+ ...(await discoverCoreTemplates())
1025
+ .filter(isPageOrBlock)
1026
+ .map(template => linkable(template, CORE_PACKAGE)),
1027
+ .../** @type {any[]} */ (await this.templates())
1028
+ .filter(
1029
+ template =>
1030
+ isPageOrBlock(template) &&
1031
+ (template.package ?? CORE_PACKAGE) !== CORE_PACKAGE,
1032
+ )
1033
+ .map(template => linkable(template, template.package)),
1034
+ ];
1035
+ return {components, templates};
1036
+ }
1037
+
824
1038
  /**
825
1039
  * The CLI's own doc topics plus the ones the configured integrations
826
1040
  * contribute, resolved into one catalog: additions, replacements (with the
@@ -838,6 +1052,8 @@ export class Project {
838
1052
  async docs() {
839
1053
  return this.#memo('docs', async () => {
840
1054
  const catalog = DocsCatalog.fromBuiltins();
1055
+ // Links name component, page, and block docs too (spec:AST-047 FR9).
1056
+ catalog.setContributions(() => this.#linkableContributions());
841
1057
  let rank = 0;
842
1058
 
843
1059
  for (const integration of this.#loadedIntegrations) {
@@ -861,7 +1077,10 @@ export class Project {
861
1077
  // nothing and a reader gets the topic it was meant to replace.
862
1078
  if (errors.length > 0) {
863
1079
  for (const e of errors) {
864
- catalog.addIssue({package: integration.name ?? pkg, message: e.message});
1080
+ catalog.addIssue({
1081
+ package: integration.name ?? pkg,
1082
+ message: e.message,
1083
+ });
865
1084
  }
866
1085
  continue;
867
1086
  }
@@ -2,9 +2,11 @@
2
2
 
3
3
  import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest';
4
4
  import * as fs from 'node:fs';
5
+ import * as os from 'node:os';
5
6
  import * as path from 'node:path';
6
7
  import {Project, DEFAULT_ISSUES_URL, findConfigPath} from './project.mjs';
7
8
  import {InMemoryConfigCache} from './config-cache.mjs';
9
+ import {getCliInvocation} from '../env/package-manager.mjs';
8
10
  import * as componentDiscovery from '../discovery/component-discovery.mjs';
9
11
  import {discoverCoreTemplates} from '../discovery/template-adapter.mjs';
10
12
 
@@ -31,6 +33,7 @@ function scaffold({
31
33
  docs = null,
32
34
  unknownKeys = null,
33
35
  agentDocs = null,
36
+ cliCommand = null,
34
37
  integrationIssuesUrl = 'https://example.com/widgets/issues',
35
38
  } = {}) {
36
39
  fs.writeFileSync(
@@ -57,6 +60,7 @@ function scaffold({
57
60
  if (withCodemods) manifest.codemods = './codemods';
58
61
  if (docs) manifest.docs = './docs';
59
62
  if (agentDocs != null) manifest.agentDocs = agentDocs;
63
+ if (cliCommand != null) manifest.cliCommand = cliCommand;
60
64
  if (unknownKeys) Object.assign(manifest, unknownKeys);
61
65
  if (integrationIssuesUrl) manifest.issuesUrl = integrationIssuesUrl;
62
66
  fs.writeFileSync(
@@ -287,6 +291,68 @@ describe('Project.load', () => {
287
291
  });
288
292
  });
289
293
 
294
+ describe('Project.load and the cliCommand an integration declares', () => {
295
+ it('records it, so the picker prints commands with it', async () => {
296
+ scaffold({cliCommand: 'acme astryx'});
297
+ const project = await Project.load(tmpDir);
298
+ expect(project.loadedIntegrations[0].cliCommand).toBe('acme astryx');
299
+ expect(getCliInvocation(tmpDir)).toBe('acme astryx');
300
+ });
301
+
302
+ it('ignores an invalid one with a warning and keeps the rest of the package', async () => {
303
+ scaffold({cliCommand: ' acme astryx\n'});
304
+ const project = await Project.load(tmpDir);
305
+ const [widgets] = project.loadedIntegrations;
306
+ expect(widgets.__loadError).toBeUndefined();
307
+ expect(widgets.cliCommand).toBeUndefined();
308
+ expect(widgets.components).toBeDefined();
309
+ expect(getCliInvocation(tmpDir)).not.toContain('acme astryx');
310
+ const issues = await project.issues();
311
+ expect(issues).toContainEqual(
312
+ expect.objectContaining({
313
+ code: 'invalid_cli_command',
314
+ severity: 'warning',
315
+ }),
316
+ );
317
+ });
318
+
319
+ it('drops a previous load once the declaration is gone', async () => {
320
+ scaffold({cliCommand: 'acme astryx'});
321
+ await Project.load(tmpDir);
322
+ scaffold();
323
+ await Project.load(tmpDir, {fresh: true});
324
+ expect(getCliInvocation(tmpDir)).not.toBe('acme astryx');
325
+ });
326
+ });
327
+
328
+ describe("Project.load({core: 'project'})", () => {
329
+ it('reads only the installed Core, never the Core the answering release carries', async () => {
330
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-project-core-'));
331
+ try {
332
+ fs.writeFileSync(
333
+ path.join(dir, 'package.json'),
334
+ JSON.stringify({name: 'a', dependencies: {'@astryxdesign/core': '^0.7.0'}}),
335
+ );
336
+ const any = await Project.load(dir);
337
+ const own = await Project.load(dir, {core: 'project'});
338
+ // The CLI in this repository carries the Core beside it.
339
+ expect(any.coreDir()).not.toBeNull();
340
+ expect(own.coreDir()).toBeNull();
341
+ expect((await own.components()).filter(c => c.package === '@astryxdesign/core')).toEqual([]);
342
+
343
+ const core = path.join(dir, 'node_modules', '@astryxdesign', 'core');
344
+ fs.mkdirSync(core, {recursive: true});
345
+ fs.writeFileSync(
346
+ path.join(core, 'package.json'),
347
+ JSON.stringify({name: '@astryxdesign/core', version: '0.7.0'}),
348
+ );
349
+ expect((await Project.load(dir, {core: 'project'})).coreDir()).toBe(fs.realpathSync(core));
350
+ } finally {
351
+ fs.rmSync(dir, {recursive: true, force: true});
352
+ }
353
+ });
354
+ });
355
+
290
356
  describe('findConfigPath', () => {
291
357
  it('rejects multiple config files at the same root', () => {
292
358
  fs.writeFileSync(
@@ -16,6 +16,7 @@
16
16
  import * as fs from 'node:fs';
17
17
  import * as path from 'node:path';
18
18
  import {assertWithin, PathSafetyError} from '../fs/path-safety.mjs';
19
+ import {formatCliCommand} from '../env/package-manager.mjs';
19
20
 
20
21
  export const LOCAL_THEME_ROOT = './src/themes';
21
22
  export const THEME_MODULE_BASENAME = 'astryx-themes';
@@ -106,7 +107,7 @@ export function themeModuleCandidates(projectDir) {
106
107
  function validThemeRecord(value, label) {
107
108
  if (!isRecord(value) || !isRecord(value.themes)) {
108
109
  throw new ThemeStateError(
109
- `${label} must carry a static ${THEME_RECORD_MARKER} header. Regenerate it with \`astryx theme add <slug>\`.`,
110
+ `${label} must carry a static ${THEME_RECORD_MARKER} header. Regenerate it with \`${formatCliCommand('theme add <slug>')}\`.`,
110
111
  );
111
112
  }
112
113
  const keys = Object.keys(value);
@@ -139,7 +140,7 @@ function validThemeRecord(value, label) {
139
140
  !THEME_SLUG_RE.test(value.defaultThemeSlug)
140
141
  ) {
141
142
  throw new ThemeStateError(
142
- `${label} defaultThemeSlug must be a lowercase kebab-case slug. Regenerate it with \`astryx theme use <slug>\`.`,
143
+ `${label} defaultThemeSlug must be a lowercase kebab-case slug. Regenerate it with \`${formatCliCommand('theme use <slug>')}\`.`,
143
144
  );
144
145
  }
145
146
  return {themes, defaultSlug: value.defaultThemeSlug};
@@ -159,7 +160,7 @@ export function parseThemeModuleRecord(source, label = 'theme module') {
159
160
  .filter(line => /^\s*\*\s*@astryx-theme-record\s+/u.test(line));
160
161
  if (recordLines.length !== 1) {
161
162
  throw new ThemeStateError(
162
- `${label} must carry exactly one static ${THEME_RECORD_MARKER} header. Regenerate it with \`astryx theme add <slug>\`.`,
163
+ `${label} must carry exactly one static ${THEME_RECORD_MARKER} header. Regenerate it with \`${formatCliCommand('theme add <slug>')}\`.`,
163
164
  );
164
165
  }
165
166
  const payload = recordLines[0].replace(
@@ -318,7 +319,7 @@ export function readThemeState(cwd = process.cwd()) {
318
319
  export function renderThemeModule(themes, defaultSlug, typescript) {
319
320
  if (!themes.some(theme => theme.slug === defaultSlug)) {
320
321
  throw new ThemeStateError(
321
- `Default theme "${defaultSlug}" is not added. Run \`astryx theme add ${defaultSlug}\` first.`,
322
+ `Default theme "${defaultSlug}" is not added. Run \`${formatCliCommand(`theme add ${defaultSlug}`)}\` first.`,
322
323
  );
323
324
  }
324
325
  /** @type {string[]} */
@@ -46,6 +46,7 @@ export const AUTHORING_SELF_DOCS = [
46
46
  'doctypes/reference/reference.doc.mjs',
47
47
  'doctypes/schema/schema.doc.mjs',
48
48
  'doctypes/template/template.doc.mjs',
49
+ 'doctypes/template/integration-template.doc.mjs',
49
50
  'doctypes/theme/theme.doc.mjs',
50
51
  ];
51
52
 
@@ -192,7 +193,7 @@ export function buildAuthoringReferenceDoc(docs) {
192
193
  title: 'Authoring Reference',
193
194
  category: 'guide',
194
195
  description:
195
- 'Every file an integration author writes, field by field: the integration manifest, astryx.config, codemods, identity, and each doc type.',
196
+ 'Every file an integration author writes, field by field: the integration manifest, astryx.config, codemods, identity, and each doc type. The step-by-step guides are under `astryx docs cli/integrations`.',
196
197
  sections: docs.map(selfDocSection),
197
198
  });
198
199
  }
@@ -25,6 +25,7 @@ import {
25
25
  oversizedDocSections,
26
26
  } from './docs-output-budget.mjs';
27
27
  import {routeSegment} from './docs-section-key.mjs';
28
+ import {formatCliCommand} from '../env/package-manager.mjs';
28
29
 
29
30
  /** The directories, relative to the CLI root, that hold the CLI's own docs. */
30
31
  export const CLI_SELF_DOC_DIRS = [
@@ -187,6 +188,19 @@ function sentence(text, more) {
187
188
  return more && text !== '' && !/[.!?:]$/.test(text) ? `${text}.` : text;
188
189
  }
189
190
 
191
+ /**
192
+ * The line a command doc prints for what it does outside a project (spec
193
+ * AST-081 FR4). A group that only holds subcommands declares nothing and
194
+ * prints nothing.
195
+ * @type {Record<string, string>}
196
+ */
197
+ const OUTSIDE_PROJECT_TEXT = {
198
+ same: "Outside a project (`same`): it answers as it would in a new project on this release, reading this release's Core, as it does in a project with nothing installed: the one installed beside the CLI, else one in the per-user cache, which a global install downloads in the background after a command run online (in CI, the first command that needs it downloads it).",
199
+ refuse:
200
+ 'Outside a project (`refuse`): it writes nothing and fails, naming the app project or integration package it needs.',
201
+ own: 'Outside a project (`own`): it does what the description above says it does outside a project.',
202
+ };
203
+
190
204
  /** @param {any[]} notes */
191
205
  function noteBlocks(notes) {
192
206
  return (notes ?? []).filter(note => TOPIC_BLOCKS.has(note?.type));
@@ -251,6 +265,8 @@ function commandSection(cmd, index) {
251
265
  rows: exitCodes.map((/** @type {any} */ e) => [String(e.code), e.when]),
252
266
  });
253
267
  }
268
+ const outside = OUTSIDE_PROJECT_TEXT[cmd.outsideProject];
269
+ if (outside) content.push({type: 'prose', text: outside});
254
270
  // Links name docs by identity; the route in each command is derived when
255
271
  // the doc is read (spec:AST-047 FR9).
256
272
  const subcommands = (cmd.subcommands ?? []).filter(
@@ -318,7 +334,8 @@ function functionSection(fn, index) {
318
334
  // A Default column only when some parameter declares a default, so a
319
335
  // function with none keeps a three-column table.
320
336
  const defaults = params.some(
321
- (/** @type {any} */ p) => typeof p.default === 'string' && p.default !== '',
337
+ (/** @type {any} */ p) =>
338
+ typeof p.default === 'string' && p.default !== '',
322
339
  );
323
340
  content.push({
324
341
  type: 'table',
@@ -475,13 +492,13 @@ export async function auditCliSelfDocs({
475
492
  else {
476
493
  misfiled.push({
477
494
  source,
478
- message: `${source} has namespace "authoring", but \`astryx docs authoring\` does not list it`,
495
+ message: `${source} has namespace "authoring", but \`${formatCliCommand('docs authoring')}\` does not list it`,
479
496
  });
480
497
  }
481
498
  } else if (inAuthoring) {
482
499
  misfiled.push({
483
500
  source,
484
- message: `${source} is read in \`astryx docs authoring\`, but its namespace is "${namespace}"`,
501
+ message: `${source} is read in \`${formatCliCommand('docs authoring')}\`, but its namespace is "${namespace}"`,
485
502
  });
486
503
  } else {
487
504
  treeDocs.push(doc);
@@ -25,6 +25,7 @@ import {
25
25
  discoverCliSelfDocSources,
26
26
  } from './cli-self-docs.mjs';
27
27
  import {routeSegment} from './docs-section-key.mjs';
28
+ import {formatCliCommand} from '../env/package-manager.mjs';
28
29
 
29
30
  const SLOW = 60_000;
30
31
 
@@ -145,8 +146,8 @@ describe('auditCliSelfDocs', () => {
145
146
  });
146
147
  const result = await audit(root, ['y/y.doc.mjs', 'z/z.doc.mjs']);
147
148
  expect(result.misfiled.map(entry => entry.message)).toEqual([
148
- 'authoring/x/x.doc.mjs has namespace "authoring", but `astryx docs authoring` does not list it',
149
- 'authoring/y/y.doc.mjs is read in `astryx docs authoring`, but its namespace is "cli/api"',
149
+ `authoring/x/x.doc.mjs has namespace "authoring", but \`${formatCliCommand('docs authoring')}\` does not list it`,
150
+ `authoring/y/y.doc.mjs is read in \`${formatCliCommand('docs authoring')}\`, but its namespace is "cli/api"`,
150
151
  ]);
151
152
  expect(result.authoring).toBe(1);
152
153
  expect(result.tree).toBe(0);