@astryxdesign/cli 0.7.2-canary.fdd384f → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (450) hide show
  1. package/CHANGELOG.md +119 -0
  2. package/README.md +169 -149
  3. package/api/build/_adapter.d.mts +8 -0
  4. package/api/build/_adapter.mjs +12 -1
  5. package/api/build/build.doc.mjs +4 -0
  6. package/api/build/kit/kit.mjs +3 -11
  7. package/api/component/_adapter.d.mts +31 -0
  8. package/api/component/_adapter.mjs +119 -11
  9. package/api/component/component.doc.mjs +6 -1
  10. package/api/component/component.mjs +60 -10
  11. package/api/component/doc-links.test.mjs +386 -0
  12. package/api/discover/_adapter.d.mts +132 -12
  13. package/api/discover/_adapter.mjs +488 -27
  14. package/api/discover/_adapter.test.mjs +599 -17
  15. package/api/discover/_catalog-view.d.mts +16 -5
  16. package/api/discover/_catalog-view.mjs +32 -8
  17. package/api/discover/detail/doc/doc.mjs +9 -1
  18. package/api/discover/discover.d.mts +11 -0
  19. package/api/discover/discover.doc.mjs +9 -7
  20. package/api/discover/discover.mjs +121 -19
  21. package/api/discover/discover.type.d.mts +20 -7
  22. package/api/discover/discover.type.mjs +17 -7
  23. package/api/discover/list/list.d.mts +4 -3
  24. package/api/discover/list/list.mjs +17 -10
  25. package/api/docs/_adapter.d.mts +63 -0
  26. package/api/docs/_adapter.mjs +312 -18
  27. package/api/docs/docs.doc.mjs +2 -1
  28. package/api/docs/docs.mjs +2 -1
  29. package/api/docs/docs.test.mjs +5 -2
  30. package/api/docs/integration-tree.test.mjs +22 -0
  31. package/api/docs/list/list.mjs +12 -2
  32. package/api/docs/reference-blocks.test.mjs +1 -3
  33. package/api/doctor/_adapter.d.mts +108 -0
  34. package/api/doctor/_adapter.mjs +223 -4
  35. package/api/doctor/doctor.d.mts +199 -0
  36. package/api/doctor/doctor.doc.mjs +3 -2
  37. package/api/doctor/doctor.mjs +530 -34
  38. package/api/doctor/doctor.test.mjs +180 -14
  39. package/api/doctor/release-checks.test.mjs +156 -0
  40. package/api/doctor/theme-checks.mjs +16 -3
  41. package/api/doctor/theme-checks.test.mjs +223 -7
  42. package/api/error.d.mts +10 -0
  43. package/api/error.mjs +25 -0
  44. package/api/gap-report/gap-report.d.mts +3 -0
  45. package/api/gap-report/gap-report.doc.mjs +11 -3
  46. package/api/gap-report/gap-report.mjs +152 -48
  47. package/api/gap-report/gap-report.test.mjs +233 -0
  48. package/api/gap-report/gap-report.type.d.mts +41 -0
  49. package/api/gap-report/gap-report.type.mjs +18 -0
  50. package/api/hook/_adapter.mjs +3 -11
  51. package/api/hook/hook.doc.mjs +4 -0
  52. package/api/hook/list/list.d.mts +1 -1
  53. package/api/init/_adapter.d.mts +22 -0
  54. package/api/init/_adapter.mjs +59 -0
  55. package/api/init/init.d.mts +7 -5
  56. package/api/init/init.doc.mjs +28 -7
  57. package/api/init/init.mjs +28 -4
  58. package/api/init/init.type.d.mts +48 -3
  59. package/api/init/init.type.mjs +37 -3
  60. package/api/init/run/run.d.mts +1 -1
  61. package/api/init/run/run.mjs +15 -4
  62. package/api/init/user/user.d.mts +14 -0
  63. package/api/init/user/user.mjs +87 -0
  64. package/api/init/user/user.test.mjs +232 -0
  65. package/api/integration/_adapter.d.mts +33 -0
  66. package/api/integration/_adapter.mjs +58 -0
  67. package/api/integration/authoring-checks.mjs +100 -5
  68. package/api/integration/authoring-checks.test.mjs +769 -0
  69. package/api/integration/authoring-checks.type.d.mts +134 -0
  70. package/api/integration/authoring-checks.type.mjs +60 -1
  71. package/api/integration/integrationCheck.doc.mjs +1 -1
  72. package/api/integration/integrationComponentConflicts.doc.mjs +11 -3
  73. package/api/integration/integrationDocConflicts.doc.mjs +22 -5
  74. package/api/integration/pack-check.mjs +303 -13
  75. package/api/integration/pack-check.test.mjs +371 -10
  76. package/api/integration/validate-integration-fixes.test.mjs +67 -0
  77. package/api/integration/validate-integration.d.mts +8 -0
  78. package/api/integration/validate-integration.mjs +55 -9
  79. package/api/integration/validate-integration.test.mjs +77 -8
  80. package/api/integration/validate-integration.type.d.mts +8 -0
  81. package/api/integration/validate-integration.type.mjs +3 -1
  82. package/api/integration/validate-unread-theme-folders.test.mjs +3 -2
  83. package/api/integration/validateIntegration.doc.mjs +4 -2
  84. package/api/search/_adapter.d.mts +11 -0
  85. package/api/search/_adapter.mjs +25 -0
  86. package/api/search/search.d.mts +73 -13
  87. package/api/search/search.doc.mjs +8 -3
  88. package/api/search/search.domain-priority.test.mjs +108 -18
  89. package/api/search/search.mjs +541 -52
  90. package/api/search/search.test.mjs +143 -1
  91. package/api/search/search.type.d.mts +14 -1
  92. package/api/search/search.type.mjs +6 -1
  93. package/api/swizzle/_adapter.mjs +2 -2
  94. package/api/swizzle/copy/copy.mjs +1 -1
  95. package/api/template/template-integration.test.mjs +1 -1
  96. package/api/theme/_adapter.d.mts +16 -0
  97. package/api/theme/_adapter.mjs +32 -5
  98. package/api/theme/build/build.d.mts +2 -1
  99. package/api/theme/build/build.mjs +70 -14
  100. package/api/theme/build/build.packed-old-core.test.mjs +20 -3
  101. package/api/theme/build/font-warning.mjs +5 -3
  102. package/api/theme/build/font-warning.test.mjs +2 -1
  103. package/api/theme/remove/remove.test.mjs +2 -2
  104. package/api/theme/targets/targets.mjs +3 -10
  105. package/api/theme/themeTargets.doc.mjs +4 -0
  106. package/api/theme/use/use.test.mjs +1 -1
  107. package/api/upgrade/_adapter.d.mts +40 -2
  108. package/api/upgrade/_adapter.mjs +118 -5
  109. package/api/upgrade/run/cli-behind-core.test.mjs +438 -0
  110. package/api/upgrade/run/run.mjs +35 -2
  111. package/api/upgrade/status/status.d.mts +21 -5
  112. package/api/upgrade/status/status.mjs +45 -8
  113. package/api/upgrade/upgrade.doc.mjs +6 -3
  114. package/api/upgrade/upgrade.mjs +5 -1
  115. package/api/upgrade/upgrade.type.d.mts +23 -0
  116. package/api/upgrade/upgrade.type.mjs +16 -1
  117. package/assets/codemods/__tests__/registry.test.mjs +1 -0
  118. package/assets/codemods/registry.mjs +1 -0
  119. package/assets/codemods/runner.mjs +7 -1
  120. package/assets/codemods/transforms/next/index.mjs +1 -11
  121. package/assets/codemods/transforms/v0.7.2/index.mjs +20 -0
  122. package/assets/discover/official-packages.json +367 -0
  123. package/assets/docs/author-a-theme.doc.mjs +2 -2
  124. package/assets/docs/browser-support.doc.mjs +1 -1
  125. package/assets/docs/getting-started.doc.mjs +23 -3
  126. package/assets/docs/styling-overview.doc.mjs +1 -1
  127. package/assets/docs/theme.doc.mjs +1 -1
  128. package/assets/docs/tree/add-a-component.doc.mjs +12 -2
  129. package/assets/docs/tree/add-a-theme.doc.mjs +13 -3
  130. package/assets/docs/tree/add-a-topic.doc.mjs +54 -9
  131. package/assets/docs/tree/agent-guidance.doc.mjs +19 -5
  132. package/assets/docs/tree/block-template.doc.mjs +6 -18
  133. package/assets/docs/tree/build-the-template.doc.mjs +1 -1
  134. package/assets/docs/tree/building-blocks.doc.mjs +4 -0
  135. package/assets/docs/tree/check-the-props.doc.mjs +8 -3
  136. package/assets/docs/tree/check-your-docs.doc.mjs +30 -13
  137. package/assets/docs/tree/checks.doc.mjs +17 -17
  138. package/assets/docs/tree/ci-and-containers.doc.mjs +88 -0
  139. package/assets/docs/tree/cli-command.doc.mjs +142 -0
  140. package/assets/docs/tree/cli.doc.mjs +6 -0
  141. package/assets/docs/tree/codemods.doc.mjs +17 -7
  142. package/assets/docs/tree/commands.doc.mjs +7 -1
  143. package/assets/docs/tree/component-family.doc.mjs +3 -5
  144. package/assets/docs/tree/component-imports.doc.mjs +13 -3
  145. package/assets/docs/tree/components.doc.mjs +7 -1
  146. package/assets/docs/tree/computer-agent-guide.doc.mjs +160 -0
  147. package/assets/docs/tree/computer-integrations.doc.mjs +93 -0
  148. package/assets/docs/tree/configuration.doc.mjs +7 -3
  149. package/assets/docs/tree/debug-and-gap-reports.doc.mjs +41 -22
  150. package/assets/docs/tree/define-the-theme.doc.mjs +6 -5
  151. package/assets/docs/tree/describe-the-component.doc.mjs +2 -2
  152. package/assets/docs/tree/docs.doc.mjs +9 -3
  153. package/assets/docs/tree/document-the-template.doc.mjs +1 -1
  154. package/assets/docs/tree/document-the-theme.doc.mjs +3 -3
  155. package/assets/docs/tree/export-template-assets.doc.mjs +1 -1
  156. package/assets/docs/tree/extend-or-replace.doc.mjs +19 -9
  157. package/assets/docs/tree/fonts-and-assets.doc.mjs +2 -2
  158. package/assets/docs/tree/generate-a-palette.doc.mjs +7 -5
  159. package/assets/docs/tree/global-integrations.doc.mjs +124 -0
  160. package/assets/docs/tree/global.doc.mjs +131 -0
  161. package/assets/docs/tree/grade-template-with-agent.doc.mjs +5 -5
  162. package/assets/docs/tree/install-once.doc.mjs +105 -0
  163. package/assets/docs/tree/integrations.doc.mjs +13 -1
  164. package/assets/docs/tree/layout-spacing.doc.mjs +2 -3
  165. package/assets/docs/tree/layout.doc.mjs +6 -6
  166. package/assets/docs/tree/links.doc.mjs +6 -5
  167. package/assets/docs/tree/migration-setup.doc.mjs +3 -2
  168. package/assets/docs/tree/migration-verification.doc.mjs +0 -1
  169. package/assets/docs/tree/migration.doc.mjs +0 -1
  170. package/assets/docs/tree/monorepos.doc.mjs +84 -0
  171. package/assets/docs/tree/package-and-test.doc.mjs +1 -1
  172. package/assets/docs/tree/page-template.doc.mjs +3 -7
  173. package/assets/docs/tree/problems.doc.mjs +205 -0
  174. package/assets/docs/tree/project-global-or-both.doc.mjs +131 -0
  175. package/assets/docs/tree/publishing.doc.mjs +6 -6
  176. package/assets/docs/tree/quick-start.doc.mjs +52 -26
  177. package/assets/docs/tree/reference.doc.mjs +192 -0
  178. package/assets/docs/tree/replace-a-core-component.doc.mjs +15 -7
  179. package/assets/docs/tree/replace-a-core-template.doc.mjs +9 -11
  180. package/assets/docs/tree/scaffold.doc.mjs +1 -1
  181. package/assets/docs/tree/sections-and-placement.doc.mjs +9 -11
  182. package/assets/docs/tree/see-it-in-an-app.doc.mjs +17 -5
  183. package/assets/docs/tree/short-and-findable.doc.mjs +9 -8
  184. package/assets/docs/tree/single-component.doc.mjs +14 -6
  185. package/assets/docs/tree/start-a-template.doc.mjs +18 -8
  186. package/assets/docs/tree/subcomponent.doc.mjs +6 -6
  187. package/assets/docs/tree/template-assets.doc.mjs +2 -2
  188. package/assets/docs/tree/template-doc-overview.doc.mjs +6 -17
  189. package/assets/docs/tree/template-fonts.doc.mjs +1 -1
  190. package/assets/docs/tree/template-icons.doc.mjs +1 -1
  191. package/assets/docs/tree/template-images-media.doc.mjs +1 -1
  192. package/assets/docs/tree/template-styles.doc.mjs +2 -2
  193. package/assets/docs/tree/templates.doc.mjs +7 -1
  194. package/assets/docs/tree/test-in-an-app.doc.mjs +7 -7
  195. package/assets/docs/tree/test-template-in-app.doc.mjs +5 -5
  196. package/assets/docs/tree/themes.doc.mjs +4 -0
  197. package/assets/docs/tree/tokens-and-setup.doc.mjs +19 -15
  198. package/assets/docs/tree/troubleshooting.doc.mjs +22 -14
  199. package/assets/docs/tree/uninstall.doc.mjs +109 -0
  200. package/assets/docs/tree/updates.doc.mjs +92 -0
  201. package/assets/docs/tree/upgrading.doc.mjs +6 -6
  202. package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +14 -2
  203. package/assets/docs/tree/verify-packed-template.doc.mjs +3 -3
  204. package/assets/docs/tree/versioning.doc.mjs +9 -9
  205. package/assets/docs/tree/who-answers.doc.mjs +119 -0
  206. package/assets/docs/tree/write-the-template-file.doc.mjs +5 -5
  207. package/assets/docs/working-with-ai.doc.mjs +23 -9
  208. package/authoring/codemod/codemod.doc.mjs +7 -3
  209. package/authoring/config/config.doc.mjs +9 -6
  210. package/authoring/config/type.ts +4 -2
  211. package/authoring/debug/parse.d.mts +5 -5
  212. package/authoring/discover/discover.doc.mjs +7 -1
  213. package/authoring/doctypes/_schema.d.mts +8 -3
  214. package/authoring/doctypes/_schema.mjs +1 -0
  215. package/authoring/doctypes/base/graph-fields.doc.mjs +6 -4
  216. package/authoring/doctypes/command/command.doc.mjs +20 -12
  217. package/authoring/doctypes/command/type.ts +14 -0
  218. package/authoring/doctypes/component/component.doc.mjs +29 -26
  219. package/authoring/doctypes/component/type.ts +15 -5
  220. package/authoring/doctypes/enum/enum.doc.mjs +7 -4
  221. package/authoring/doctypes/function/function.doc.mjs +9 -8
  222. package/authoring/doctypes/hook/hook.doc.mjs +5 -4
  223. package/authoring/doctypes/hook/type.ts +4 -0
  224. package/authoring/doctypes/namespace/namespace.doc.mjs +7 -6
  225. package/authoring/doctypes/reference/reference.doc.mjs +12 -11
  226. package/authoring/doctypes/schema/schema.doc.mjs +9 -9
  227. package/authoring/doctypes/template/template.doc.mjs +2 -2
  228. package/authoring/doctypes/theme/theme.doc.mjs +2 -2
  229. package/authoring/gap-report/gap-report.doc.mjs +2 -2
  230. package/authoring/identity/identity.doc.mjs +3 -3
  231. package/authoring/index.d.ts +1 -0
  232. package/authoring/integration/integration.doc.mjs +22 -5
  233. package/authoring/integration/parse.test.mjs +11 -0
  234. package/authoring/integration/schema.d.mts +14 -3
  235. package/authoring/integration/schema.mjs +33 -2
  236. package/authoring/integration/type.ts +8 -0
  237. package/clients/cli/bin/astryx.mjs +20 -3
  238. package/clients/cli/cli-command.test.mjs +166 -0
  239. package/clients/cli/commands/blog.doc.mjs +1 -0
  240. package/clients/cli/commands/build-theme.batch-report.test.mjs +14 -2
  241. package/clients/cli/commands/build-theme.font-warning.test.mjs +3 -3
  242. package/clients/cli/commands/build-theme.mjs +25 -12
  243. package/clients/cli/commands/build.doc.mjs +1 -0
  244. package/clients/cli/commands/build.exit-codes-doc.test.mjs +4 -0
  245. package/clients/cli/commands/cli-postinstall.test.mjs +49 -1
  246. package/clients/cli/commands/component.doc.mjs +1 -0
  247. package/clients/cli/commands/discover.broken-integration.test.mjs +11 -7
  248. package/clients/cli/commands/discover.doc.mjs +9 -1
  249. package/clients/cli/commands/discover.mjs +144 -93
  250. package/clients/cli/commands/discover.no-source.test.mjs +448 -33
  251. package/clients/cli/commands/discover.sources.test.mjs +20 -1
  252. package/clients/cli/commands/docs.doc.mjs +1 -0
  253. package/clients/cli/commands/docs.mjs +49 -19
  254. package/clients/cli/commands/docs.test.mjs +2 -2
  255. package/clients/cli/commands/doctor-integration-components.doc.mjs +7 -3
  256. package/clients/cli/commands/doctor-integration-docs.doc.mjs +20 -5
  257. package/clients/cli/commands/doctor-integration-templates.doc.mjs +1 -0
  258. package/clients/cli/commands/doctor-integration-validate.doc.mjs +13 -2
  259. package/clients/cli/commands/doctor-integration.test.mjs +52 -4
  260. package/clients/cli/commands/doctor.doc.mjs +12 -2
  261. package/clients/cli/commands/doctor.mjs +102 -7
  262. package/clients/cli/commands/doctor.test.mjs +10 -4
  263. package/clients/cli/commands/gap-report.doc.mjs +15 -2
  264. package/clients/cli/commands/gap-report.mjs +24 -2
  265. package/clients/cli/commands/gap-report.test.mjs +79 -0
  266. package/clients/cli/commands/hook.doc.mjs +1 -0
  267. package/clients/cli/commands/init.behavior.test.mjs +5 -0
  268. package/clients/cli/commands/init.doc.mjs +29 -3
  269. package/clients/cli/commands/integration-add.doc.mjs +7 -0
  270. package/clients/cli/commands/integration-authoring.test.mjs +49 -3
  271. package/clients/cli/commands/integration-check.doc.mjs +10 -2
  272. package/clients/cli/commands/integration-check.test.mjs +175 -1
  273. package/clients/cli/commands/integration-pack.doc.mjs +8 -1
  274. package/clients/cli/commands/integration-verify.doc.mjs +8 -1
  275. package/clients/cli/commands/integration.mjs +15 -9
  276. package/clients/cli/commands/json-contract.test.mjs +11 -2
  277. package/clients/cli/commands/manifest.doc.mjs +1 -0
  278. package/clients/cli/commands/outside-project.test.mjs +774 -0
  279. package/clients/cli/commands/search.doc.mjs +1 -0
  280. package/clients/cli/commands/search.mjs +16 -11
  281. package/clients/cli/commands/search.test.mjs +429 -296
  282. package/clients/cli/commands/setup-nudge.test.mjs +102 -2
  283. package/clients/cli/commands/swizzle.doc.mjs +7 -0
  284. package/clients/cli/commands/swizzle.mjs +5 -3
  285. package/clients/cli/commands/swizzle.routing.test.mjs +19 -2
  286. package/clients/cli/commands/template.doc.mjs +1 -0
  287. package/clients/cli/commands/theme-add-lifecycle.test.mjs +3 -3
  288. package/clients/cli/commands/theme-add.doc.mjs +8 -1
  289. package/clients/cli/commands/theme-build.doc.mjs +1 -0
  290. package/clients/cli/commands/theme-eject.doc.mjs +1 -0
  291. package/clients/cli/commands/theme-list.behavior.test.mjs +6 -0
  292. package/clients/cli/commands/theme-list.doc.mjs +1 -0
  293. package/clients/cli/commands/theme-palette-generate.doc.mjs +1 -0
  294. package/clients/cli/commands/theme-remove.doc.mjs +7 -0
  295. package/clients/cli/commands/theme-targets.doc.mjs +1 -0
  296. package/clients/cli/commands/theme-template.doc.mjs +1 -0
  297. package/clients/cli/commands/theme-use.doc.mjs +7 -0
  298. package/clients/cli/commands/upgrade.config-ordering.test.mjs +2 -1
  299. package/clients/cli/commands/upgrade.doc.mjs +22 -1
  300. package/clients/cli/commands/upgrade.file-protection.test.mjs +3 -2
  301. package/clients/cli/commands/upgrade.hook-output.test.mjs +2 -1
  302. package/clients/cli/commands/upgrade.older-cli.test.mjs +160 -0
  303. package/clients/cli/index.mjs +111 -16
  304. package/clients/cli/lib/cli-error.mjs +4 -1
  305. package/clients/cli/lib/component-format.test.mjs +1 -1
  306. package/clients/cli/lib/define-command.mjs +2 -2
  307. package/clients/cli/lib/json-shim.mjs +3 -1
  308. package/clients/cli/lib/manifest.d.ts +9 -0
  309. package/clients/cli/lib/manifest.mjs +17 -1
  310. package/clients/cli/lib/manifest.test.mjs +25 -0
  311. package/foundation/agent-docs/agent-doc-state.d.mts +35 -6
  312. package/foundation/agent-docs/agent-doc-state.mjs +66 -6
  313. package/foundation/agent-docs/agent-docs.d.mts +4 -3
  314. package/foundation/agent-docs/agent-docs.mjs +11 -9
  315. package/foundation/agent-docs/computer-agent-text.d.mts +133 -0
  316. package/foundation/agent-docs/computer-agent-text.mjs +623 -0
  317. package/foundation/agent-docs/computer-agent-text.test.mjs +602 -0
  318. package/foundation/config/project.d.mts +35 -3
  319. package/foundation/config/project.mjs +145 -8
  320. package/foundation/config/project.test.mjs +66 -0
  321. package/foundation/config/theme-state.mjs +5 -4
  322. package/foundation/discovery/authoring-self-docs.mjs +1 -1
  323. package/foundation/discovery/cli-self-docs.mjs +20 -3
  324. package/foundation/discovery/cli-self-docs.test.mjs +3 -2
  325. package/foundation/discovery/component-replacement.mjs +2 -1
  326. package/foundation/discovery/copied-passages.d.mts +216 -0
  327. package/foundation/discovery/copied-passages.mjs +720 -0
  328. package/foundation/discovery/copied-passages.test.mjs +124 -0
  329. package/foundation/discovery/doc-consistency.d.mts +34 -0
  330. package/foundation/discovery/doc-consistency.mjs +513 -0
  331. package/foundation/discovery/doc-consistency.test.mjs +347 -0
  332. package/foundation/discovery/doc-fluff.d.mts +112 -0
  333. package/foundation/discovery/doc-fluff.mjs +328 -0
  334. package/foundation/discovery/doc-fluff.test.mjs +142 -0
  335. package/foundation/discovery/doc-types.d.mts +1 -1
  336. package/foundation/discovery/doc-types.mjs +15 -8
  337. package/foundation/discovery/doc-types.test.mjs +94 -2
  338. package/foundation/discovery/docs-discovery.d.mts +7 -0
  339. package/foundation/discovery/docs-discovery.mjs +26 -2
  340. package/foundation/discovery/project-typescript.d.mts +11 -2
  341. package/foundation/discovery/project-typescript.mjs +32 -4
  342. package/foundation/discovery/prop-drift.d.mts +26 -7
  343. package/foundation/discovery/prop-drift.mjs +173 -8
  344. package/foundation/discovery/prop-drift.test.mjs +120 -2
  345. package/foundation/discovery/runner-allowlist.d.mts +40 -0
  346. package/foundation/discovery/runner-allowlist.mjs +53 -0
  347. package/foundation/discovery/stray-pointers.d.mts +52 -0
  348. package/foundation/discovery/stray-pointers.mjs +293 -0
  349. package/foundation/discovery/stray-pointers.test.mjs +86 -0
  350. package/foundation/discovery/template-needs.mjs +5 -2
  351. package/foundation/discovery/theme-imports.mjs +8 -4
  352. package/foundation/discovery/theme-imports.test.mjs +1 -1
  353. package/foundation/discovery/type-text.mjs +1 -1
  354. package/foundation/discovery/type-text.test.mjs +1 -1
  355. package/foundation/doc-compiler/doc-loads.test.mjs +42 -2
  356. package/foundation/doc-compiler/inputs.mjs +4 -1
  357. package/foundation/doc-compiler/links.d.mts +32 -0
  358. package/foundation/doc-compiler/links.mjs +163 -26
  359. package/foundation/doc-compiler/links.test.mjs +97 -4
  360. package/foundation/doc-compiler/read.d.mts +1 -1
  361. package/foundation/doc-compiler/tree.mjs +30 -1
  362. package/foundation/doc-compiler/tree.test.mjs +8 -1
  363. package/foundation/env/cli-command.d.mts +83 -0
  364. package/foundation/env/cli-command.mjs +152 -0
  365. package/foundation/env/cli-command.test.mjs +129 -0
  366. package/foundation/env/node-version.test.mjs +15 -3
  367. package/foundation/env/package-manager.d.mts +93 -9
  368. package/foundation/env/package-manager.global.test.mjs +66 -0
  369. package/foundation/env/package-manager.mjs +184 -13
  370. package/foundation/env/package-manager.test.mjs +341 -15
  371. package/foundation/env/project-scope.d.mts +9 -0
  372. package/foundation/env/project-scope.mjs +33 -0
  373. package/foundation/env/search-path.d.mts +42 -0
  374. package/foundation/env/search-path.mjs +205 -0
  375. package/foundation/env/search-path.test.mjs +122 -0
  376. package/foundation/env/user-dirs.d.mts +39 -0
  377. package/foundation/env/user-dirs.mjs +93 -0
  378. package/foundation/env/user-dirs.test.mjs +72 -0
  379. package/foundation/env/user-settings.d.mts +46 -0
  380. package/foundation/env/user-settings.mjs +106 -0
  381. package/foundation/env/user-settings.test.mjs +64 -0
  382. package/foundation/fs/file-protection.mjs +15 -1
  383. package/foundation/fs/module-loader.d.mts +2 -1
  384. package/foundation/fs/module-loader.mjs +60 -4
  385. package/foundation/fs/module-loader.stdout.test.mjs +3 -2
  386. package/foundation/fs/module-loader.test.mjs +194 -0
  387. package/foundation/fs/path-compare.d.mts +33 -0
  388. package/foundation/fs/path-compare.mjs +73 -0
  389. package/foundation/fs/paths.d.mts +57 -1
  390. package/foundation/fs/paths.mjs +131 -3
  391. package/foundation/fs/paths.test.mjs +10 -1
  392. package/foundation/integrations/computer-integrations.d.mts +78 -0
  393. package/foundation/integrations/computer-integrations.mjs +316 -0
  394. package/foundation/integrations/computer-integrations.test.mjs +292 -0
  395. package/foundation/integrations/contribution-inventory.d.mts +11 -0
  396. package/foundation/integrations/contribution-inventory.mjs +50 -2
  397. package/foundation/integrations/integration-warnings.mjs +3 -2
  398. package/foundation/integrations/integration-warnings.test.mjs +8 -9
  399. package/foundation/integrations/integrations.d.mts +25 -1
  400. package/foundation/integrations/integrations.mjs +46 -3
  401. package/foundation/integrations/issue.ts +6 -0
  402. package/foundation/integrations/validate-contributions.d.mts +2 -0
  403. package/foundation/integrations/validate-contributions.mjs +41 -1
  404. package/foundation/release/agent-text-worker.d.mts +4 -0
  405. package/foundation/release/agent-text-worker.mjs +52 -0
  406. package/foundation/release/cache.d.mts +310 -0
  407. package/foundation/release/cache.mjs +1216 -0
  408. package/foundation/release/cache.test.mjs +920 -0
  409. package/foundation/release/choose.d.mts +207 -0
  410. package/foundation/release/choose.mjs +677 -0
  411. package/foundation/release/choose.test.mjs +427 -0
  412. package/foundation/release/core-worker.d.mts +4 -0
  413. package/foundation/release/core-worker.mjs +38 -0
  414. package/foundation/release/global-state.d.mts +259 -0
  415. package/foundation/release/global-state.mjs +472 -0
  416. package/foundation/release/global-state.test.mjs +381 -0
  417. package/foundation/release/handoff.d.mts +60 -0
  418. package/foundation/release/handoff.e2e.test.mjs +278 -0
  419. package/foundation/release/handoff.mjs +357 -0
  420. package/foundation/release/lock-lease.d.mts +23 -0
  421. package/foundation/release/lock-lease.mjs +80 -0
  422. package/foundation/release/notices.d.mts +253 -0
  423. package/foundation/release/notices.mjs +596 -0
  424. package/foundation/release/notices.test.mjs +565 -0
  425. package/foundation/release/npm-guard.d.mts +4 -0
  426. package/foundation/release/npm-guard.mjs +126 -0
  427. package/foundation/release/project.d.mts +141 -0
  428. package/foundation/release/project.mjs +466 -0
  429. package/foundation/release/project.test.mjs +288 -0
  430. package/foundation/release/refresh-worker.d.mts +4 -0
  431. package/foundation/release/refresh-worker.mjs +27 -0
  432. package/foundation/release/run-context.d.mts +47 -0
  433. package/foundation/release/run-context.mjs +149 -0
  434. package/foundation/release/update-notice-env.d.mts +28 -0
  435. package/foundation/release/update-notice-env.mjs +39 -0
  436. package/foundation/response/base.d.ts +26 -3
  437. package/foundation/response/error-codes.d.mts +3 -1
  438. package/foundation/response/error-codes.doc.mjs +12 -0
  439. package/foundation/response/error-codes.mjs +17 -0
  440. package/foundation/response/error-codes.test.mjs +2 -0
  441. package/foundation/response/json-contract.test.mjs +7 -2
  442. package/foundation/response/json.mjs +4 -1
  443. package/foundation/response/response-types.doc.mjs +17 -11
  444. package/foundation/response/response-types.doc.test.mjs +46 -0
  445. package/foundation/response/response.doc.mjs +3 -3
  446. package/foundation/xle/browser-imports.test.mjs +74 -0
  447. package/package.json +11 -11
  448. package/scripts/postinstall.mjs +24 -5
  449. /package/assets/codemods/transforms/{next → v0.7.2}/__tests__/migrate-list-edge-compensation-to-edge.test.mjs +0 -0
  450. /package/assets/codemods/transforms/{next → v0.7.2}/migrate-list-edge-compensation-to-edge.mjs +0 -0
@@ -0,0 +1,677 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file Which release answers a run (spec AST-081 FR2, FR3, FR6, FR9).
5
+ *
6
+ * As released, the project decides who answers (DEC-10):
7
+ *
8
+ * - **In a project that installs its own CLI**, that CLI answers, whatever its
9
+ * version: every other copy hands the run to it, `astryx upgrade` included.
10
+ * - **In a project with no CLI of its own** (only Core, or nothing installed
11
+ * yet), the copy that started the run answers, as it always has; a hint,
12
+ * once a day, names the command that adds the CLI at Core's version
13
+ * (notices.mjs).
14
+ * - **When the project's own CLI cannot start**, the release equal to the
15
+ * project's Core answers (the matched release): the copy that started the
16
+ * run when it is that release, else the release in the per-user cache,
17
+ * installed first. When it cannot be obtained, the copy that started the
18
+ * run answers, announced (handoff.mjs).
19
+ * - **Outside a project**, every copy answers itself, the global copy
20
+ * included; a newer release is announced, never installed (notices.mjs).
21
+ *
22
+ * Core deciding over the project's own CLI, and the matched release for a
23
+ * project with only Core, are the next minor release's change.
24
+ *
25
+ * A copy never hands a run to itself, so a run is handed off at most once:
26
+ * the copy it reaches makes the same choice and finds itself.
27
+ *
28
+ * @input the folder, the running copy
29
+ * @output a {@link ReleaseChoice}
30
+ * @position foundation/release — called once per run by handoff.mjs.
31
+ */
32
+
33
+ import * as fs from 'node:fs';
34
+ import * as os from 'node:os';
35
+ import * as path from 'node:path';
36
+ import {
37
+ readReleaseFacts,
38
+ readJson,
39
+ CORE_PACKAGE_NAME,
40
+ CLI_PACKAGE_NAME,
41
+ } from './project.mjs';
42
+ import {
43
+ cliBinOf,
44
+ isInReleaseCache,
45
+ isReleaseVersion,
46
+ resolvePublishedVersion,
47
+ userCacheDir,
48
+ } from './cache.mjs';
49
+ import {relativeWithin, samePath} from '../fs/path-compare.mjs';
50
+
51
+ /**
52
+ * The copy that is running.
53
+ * @typedef {object} RunningCopy
54
+ * @property {string} version its package version
55
+ * @property {string} root its package folder
56
+ */
57
+
58
+ /**
59
+ * Who answers the run.
60
+ * @typedef {object} ReleaseChoice
61
+ * @property {'project' | 'starting' | 'matched' | 'carried' | 'fallback'} copy
62
+ * the kind of copy that answers (`meta.answeredBy.copy`): `project`, the
63
+ * project's own CLI; `starting`, the copy that started the run, in a project
64
+ * with no CLI of its own; `matched`, the release equal to the project's
65
+ * Core, when the project's own CLI cannot start; `carried`, outside a
66
+ * project; `fallback`, the copy that started the run when the matched
67
+ * release could not be obtained (spec AST-081 FR2, FR5)
68
+ * @property {'self' | 'project-cli' | 'release'} answer `self`: this copy;
69
+ * `project-cli`: hand off to the project's CLI; `release`: hand off to the
70
+ * cached release `version`
71
+ * @property {string} version the release that answers
72
+ * @property {string | null} bin the entry file to hand off to (`project-cli`
73
+ * and a cached `release` that is already installed)
74
+ * @property {string | null} coreVersion the Core the answering release pairs
75
+ * with, when known now
76
+ * @property {boolean} inProject
77
+ * @property {string | null} unavailable why the release cannot answer, when it
78
+ * cannot
79
+ */
80
+
81
+ /**
82
+ * @param {string} p
83
+ * @returns {string}
84
+ */
85
+ function real(p) {
86
+ try {
87
+ return fs.realpathSync(p);
88
+ } catch {
89
+ return path.resolve(p);
90
+ }
91
+ }
92
+
93
+ /** Folder-name patterns of package-manager global installs that hold a package.json. */
94
+ const GLOBAL_OWNER_PATTERNS = [
95
+ // pnpm: `<pnpm home>/global/5` before pnpm 10, `<pnpm home>/global/v11/<id>` since.
96
+ /[\\/]global[\\/]v?\d+(?:[\\/][^\\/]+)?$/,
97
+ // Yarn 1: `~/.config/yarn/global`, or `%LOCALAPPDATA%\Yarn\Data\global` on Windows.
98
+ /[\\/]yarn[\\/](?:data[\\/])?global$/i,
99
+ // Bun: `<BUN_INSTALL>/install/global`, `~/.bun/install/global` by default.
100
+ /[\\/]install[\\/]global$/i,
101
+ ];
102
+
103
+ /**
104
+ * Where a global install's settings come from: the home folder, the
105
+ * environment, and the platform. Defaults to this process's.
106
+ * @typedef {{home?: string, env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform}} GlobalWhere
107
+ */
108
+
109
+ /**
110
+ * The value a line `<key> <value>` or `<key>=<value>` sets in a package
111
+ * manager's settings file (`.yarnrc`, `.npmrc`, `bunfig.toml`), unquoted, or
112
+ * null.
113
+ * @param {string} file
114
+ * @param {string} key
115
+ * @returns {string | null}
116
+ */
117
+ function settingIn(file, key) {
118
+ let text;
119
+ try {
120
+ text = fs.readFileSync(file, 'utf8');
121
+ } catch {
122
+ return null;
123
+ }
124
+ const escaped = key.replace(/[.*+?^${}()|[\]\\]/gu, '\\$&');
125
+ const match = new RegExp(
126
+ `^\\s*${escaped}\\s*(?:=|\\s)\\s*(.+?)\\s*$`,
127
+ 'mu',
128
+ ).exec(text);
129
+ if (!match) return null;
130
+ return match[1].replace(/^(["'])(.*)\1$/u, '$2');
131
+ }
132
+
133
+ /**
134
+ * An absolute folder from a setting, with `~` for the home folder; null for a
135
+ * relative or empty one.
136
+ * @param {string | null | undefined} value
137
+ * @param {string} home
138
+ * @param {NodeJS.Platform} platform
139
+ * @returns {string | null}
140
+ */
141
+ function absoluteFolder(value, home, platform) {
142
+ if (!value) return null;
143
+ const paths = platform === 'win32' ? path.win32 : path.posix;
144
+ const expanded = value.startsWith('~')
145
+ ? paths.join(home, value.slice(1))
146
+ : value;
147
+ return paths.isAbsolute(expanded) ? paths.normalize(expanded) : null;
148
+ }
149
+
150
+ /**
151
+ * The global folders the package managers' own settings name on this
152
+ * computer, read the way each package manager reads them, never guessed from
153
+ * a folder's layout:
154
+ * - Bun: `$BUN_INSTALL/install/global`, else `~/.bun/install/global`, and a
155
+ * `globalDir` in `~/.bunfig.toml` or `$XDG_CONFIG_HOME/.bunfig.toml`.
156
+ * - Yarn 1: the `global-folder` setting (`YARN_GLOBAL_FOLDER`,
157
+ * `npm_config_global_folder`, or `global-folder` in `~/.yarnrc`), else
158
+ * `<data folder>/global`: `%LOCALAPPDATA%\Yarn\Data` on Windows,
159
+ * `$XDG_DATA_HOME/yarn`, else `~/.config/yarn`.
160
+ * - pnpm: `<PNPM_HOME>/global`, else `<data folder>/global` (`$XDG_DATA_HOME/pnpm`,
161
+ * else `%LOCALAPPDATA%\pnpm`, `~/Library/pnpm`, or `~/.local/share/pnpm`), and a
162
+ * `global-dir` in `~/.npmrc` or pnpm's `rc` file; a global install is a
163
+ * numbered folder below it.
164
+ * @param {GlobalWhere} [where]
165
+ * @returns {Array<{manager: 'bun' | 'yarn' | 'pnpm', folder: string}>}
166
+ */
167
+ export function configuredGlobalFolders(where = {}) {
168
+ const {
169
+ home = os.homedir(),
170
+ env = process.env,
171
+ platform = process.platform,
172
+ } = where;
173
+ const paths = platform === 'win32' ? path.win32 : path.posix;
174
+ const abs = (/** @type {string | null | undefined} */ value) =>
175
+ absoluteFolder(value, home, platform);
176
+ const xdgConfig = abs(env.XDG_CONFIG_HOME) ?? paths.join(home, '.config');
177
+ const xdgData = abs(env.XDG_DATA_HOME);
178
+ const localAppData =
179
+ abs(env.LOCALAPPDATA) ?? paths.join(home, 'AppData', 'Local');
180
+ /** @type {Array<{manager: 'bun' | 'yarn' | 'pnpm', folder: string}>} */
181
+ const out = [];
182
+ // Bun
183
+ out.push({
184
+ manager: 'bun',
185
+ folder: paths.join(
186
+ abs(env.BUN_INSTALL) ?? paths.join(home, '.bun'),
187
+ 'install',
188
+ 'global',
189
+ ),
190
+ });
191
+ for (const file of [
192
+ paths.join(home, '.bunfig.toml'),
193
+ paths.join(xdgConfig, '.bunfig.toml'),
194
+ ]) {
195
+ const dir = abs(settingIn(file, 'globalDir'));
196
+ if (dir) out.push({manager: 'bun', folder: dir});
197
+ }
198
+ // Yarn 1
199
+ const yarnSetting =
200
+ abs(env.YARN_GLOBAL_FOLDER) ??
201
+ abs(env.npm_config_global_folder) ??
202
+ abs(settingIn(paths.join(home, '.yarnrc'), 'global-folder'));
203
+ const yarnData =
204
+ platform === 'win32'
205
+ ? paths.join(localAppData, 'Yarn', 'Data')
206
+ : xdgData
207
+ ? paths.join(xdgData, 'yarn')
208
+ : paths.join(home, '.config', 'yarn');
209
+ out.push({
210
+ manager: 'yarn',
211
+ folder: yarnSetting ?? paths.join(yarnData, 'global'),
212
+ });
213
+ // pnpm
214
+ // pnpm's data folder: PNPM_HOME, else $XDG_DATA_HOME/pnpm on any platform,
215
+ // else the platform's own.
216
+ const pnpmHome =
217
+ abs(env.PNPM_HOME) ??
218
+ (xdgData
219
+ ? paths.join(xdgData, 'pnpm')
220
+ : platform === 'win32'
221
+ ? paths.join(localAppData, 'pnpm')
222
+ : platform === 'darwin'
223
+ ? paths.join(home, 'Library', 'pnpm')
224
+ : paths.join(home, '.local', 'share', 'pnpm'));
225
+ out.push({manager: 'pnpm', folder: paths.join(pnpmHome, 'global')});
226
+ for (const file of [
227
+ paths.join(home, '.npmrc'),
228
+ paths.join(xdgConfig, 'pnpm', 'rc'),
229
+ ]) {
230
+ const dir = abs(settingIn(file, 'global-dir'));
231
+ if (dir) out.push({manager: 'pnpm', folder: dir});
232
+ }
233
+ return out;
234
+ }
235
+
236
+ /**
237
+ * The package manager's global folder that holds the CLI at `root`, when the
238
+ * package manager's own settings name that folder (configuredGlobalFolders);
239
+ * null otherwise. A folder that only looks like one, such as an app made with
240
+ * `yarn add` alone or one under a folder named `install/global`, is not.
241
+ * @param {string} root the CLI package's real folder
242
+ * @param {GlobalWhere} [where]
243
+ * @returns {{manager: 'bun' | 'pnpm' | 'yarn', folder: string} | null}
244
+ */
245
+ export function globalFolderOf(root, where = {}) {
246
+ const place = installPlaceOf(root);
247
+ if (place.runner || !place.owner) return null;
248
+ const owner = place.owner;
249
+ const platform = where.platform ?? process.platform;
250
+ for (const known of configuredGlobalFolders(where)) {
251
+ // Compared as real folders, so a linked home folder reads the same.
252
+ const folder = real(known.folder);
253
+ if (known.manager === 'pnpm') {
254
+ // `<global-dir>/<n>` before pnpm 11, `<global-dir>/v<n>/<id>` since.
255
+ const rel = relativeWithin(folder, owner, platform);
256
+ if (rel !== null && /^v?\d+(?:[\\/][^\\/]+)?$/u.test(rel)) {
257
+ return {manager: 'pnpm', folder: owner};
258
+ }
259
+ } else if (samePath(folder, owner, platform)) {
260
+ return {manager: known.manager, folder: owner};
261
+ }
262
+ }
263
+ return null;
264
+ }
265
+
266
+ /** Path markers of package runners' temporary installs. */
267
+ const RUNNER_CACHE =
268
+ /[\\/]_npx[\\/]|[\\/]dlx[-\\/]|[\\/]\.bun[\\/]install[\\/]cache[\\/]|[\\/]bunx-|[\\/]xfs-/i;
269
+
270
+ /**
271
+ * Where an installed CLI package lives: its owner, the folder that holds the
272
+ * `node_modules` it is in (above pnpm's `.pnpm` store), and whether that owner
273
+ * is named like a package manager's default global folder (`knownGlobal`, a
274
+ * hint only: isGlobalCopy decides from the package managers' settings). Pure:
275
+ * reads no file, and
276
+ * accepts either separator, so Windows paths read the same on every platform.
277
+ * @param {string} root the package's real folder
278
+ * @returns {{runner: boolean, owner: string | null, knownGlobal: boolean}}
279
+ */
280
+ export function installPlaceOf(root) {
281
+ if (RUNNER_CACHE.test(root)) {
282
+ return {runner: true, owner: null, knownGlobal: false};
283
+ }
284
+ const sep = /^[A-Za-z]:\\|^\\\\/.test(root) ? '\\' : path.sep;
285
+ const parts = root.split(/[\\/]/);
286
+ const at = parts.lastIndexOf('node_modules');
287
+ if (at < 1) return {runner: false, owner: null, knownGlobal: false};
288
+ let owner = parts.slice(0, at).join(sep) || sep;
289
+ // pnpm keeps the real folder in `<owner>/node_modules/.pnpm/<id>/node_modules`,
290
+ // or, for its global folder before pnpm 11, in `<owner>/.pnpm/<id>/node_modules`.
291
+ const pnpmAt = parts.lastIndexOf('.pnpm');
292
+ if (pnpmAt > 0 && pnpmAt < at) {
293
+ owner =
294
+ parts
295
+ .slice(0, parts[pnpmAt - 1] === 'node_modules' ? pnpmAt - 1 : pnpmAt)
296
+ .join(sep) || sep;
297
+ }
298
+ return {
299
+ runner: false,
300
+ owner,
301
+ knownGlobal: GLOBAL_OWNER_PATTERNS.some(re => re.test(owner)),
302
+ };
303
+ }
304
+
305
+ /**
306
+ * Whether `root` is inside a pnpm store's `links` folder (`<store>/v<N>/links`),
307
+ * where the global virtual store keeps its packages.
308
+ * @param {string} root
309
+ * @returns {boolean}
310
+ */
311
+ function inPnpmStoreLinks(root) {
312
+ const parts = root.split(/[\\/]/);
313
+ const at = parts.lastIndexOf('links');
314
+ return at >= 2 && /^v\d+$/u.test(parts[at - 1]);
315
+ }
316
+
317
+ /**
318
+ * Whether a global folder of pnpm's, as its settings name it, installs the
319
+ * CLI package at `root`: `<global-dir>/v<N>/<id>/node_modules/@astryxdesign/cli`
320
+ * (or `<global-dir>/<N>/node_modules/…` before pnpm 11) resolves to it.
321
+ * @param {string} root the CLI package's real folder
322
+ * @param {GlobalWhere} where
323
+ * @returns {boolean}
324
+ */
325
+ function linkedFromPnpmGlobal(root, where) {
326
+ const platform = where.platform ?? process.platform;
327
+ const cliIn = (/** @type {string} */ dir) =>
328
+ path.join(dir, 'node_modules', '@astryxdesign', 'cli');
329
+ for (const known of configuredGlobalFolders(where)) {
330
+ if (known.manager !== 'pnpm') continue;
331
+ /** @type {string[]} */
332
+ let versions;
333
+ try {
334
+ versions = fs.readdirSync(known.folder);
335
+ } catch {
336
+ continue;
337
+ }
338
+ for (const version of versions.filter(v => /^v?\d+$/u.test(v))) {
339
+ const dir = path.join(known.folder, version);
340
+ /** @type {string[]} */
341
+ let ids = [];
342
+ try {
343
+ ids = fs.readdirSync(dir);
344
+ } catch {
345
+ // Not a folder.
346
+ }
347
+ for (const candidate of [dir, ...ids.map(id => path.join(dir, id))]) {
348
+ if (samePath(real(cliIn(candidate)), root, platform)) return true;
349
+ }
350
+ }
351
+ }
352
+ return false;
353
+ }
354
+
355
+ /**
356
+ * Is the running copy the CLI package installed for the computer
357
+ * (`npm i -g`, `pnpm add -g`, `yarn global add`, `bun add -g`, Volta)?
358
+ *
359
+ * Read from where the package lives: in a `node_modules` folder whose owner
360
+ * holds no package.json (npm's global folder), or whose owner is a global
361
+ * folder a package manager's own settings name (globalFolderOf). A copy in a
362
+ * project, in a package runner's temporary install, or in the release cache
363
+ * is not, and when unsure the answer is no: a copy that is not global runs no
364
+ * update check and writes no agent skill.
365
+ *
366
+ * @param {string} cliRoot
367
+ * @param {string} [cacheDir]
368
+ * @param {GlobalWhere} [where]
369
+ * @returns {boolean}
370
+ */
371
+ export function isGlobalCopy(cliRoot, cacheDir = userCacheDir(), where = {}) {
372
+ const root = real(cliRoot);
373
+ const place = installPlaceOf(root);
374
+ if (place.runner || !place.owner || isInReleaseCache(root, cacheDir)) {
375
+ return false;
376
+ }
377
+ if (globalFolderOf(root, where)) return true;
378
+ // pnpm's global virtual store keeps every package, a project's own CLI
379
+ // included, in `<store>/v<N>/links/…`, whose folders hold no package.json:
380
+ // global only when a pnpm global folder the settings name links to it.
381
+ if (inPnpmStoreLinks(root)) return linkedFromPnpmGlobal(root, where);
382
+ // npm's global folder holds no package.json; a project always has one.
383
+ return !fs.existsSync(path.join(place.owner, 'package.json'));
384
+ }
385
+
386
+ /**
387
+ * The Core release a project records, without asking the registry: the Core
388
+ * the folder reads, else the version its lockfile records, else an exact
389
+ * declared version; null for a range.
390
+ * @param {import('./project.mjs').ReleaseFacts} facts
391
+ * @returns {string | null}
392
+ */
393
+ export function coreVersionOf(facts) {
394
+ if (facts.core) return facts.core.version;
395
+ if (isReleaseVersion(facts.lockedCoreVersion)) {
396
+ return /** @type {string} */ (facts.lockedCoreVersion);
397
+ }
398
+ if (facts.declaredCoreRange) {
399
+ return exactOrRange(CORE_PACKAGE_NAME, facts.declaredCoreRange).version;
400
+ }
401
+ return null;
402
+ }
403
+
404
+ /**
405
+ * The release that matches a project's facts, before any registry question.
406
+ * @param {import('./project.mjs').ReleaseFacts} facts
407
+ * @returns {{version: string | null, range: {name: string, spec: string} | null}}
408
+ */
409
+ export function matchedVersionOf(facts) {
410
+ if (facts.core) return {version: facts.core.version, range: null};
411
+ if (isReleaseVersion(facts.lockedCoreVersion)) {
412
+ return {version: facts.lockedCoreVersion, range: null};
413
+ }
414
+ if (facts.declaredCoreRange) {
415
+ return exactOrRange(CORE_PACKAGE_NAME, facts.declaredCoreRange);
416
+ }
417
+ if (facts.cli) return {version: facts.cli.version, range: null};
418
+ if (isReleaseVersion(facts.lockedCliVersion)) {
419
+ return {version: facts.lockedCliVersion, range: null};
420
+ }
421
+ if (facts.declaredCliRange) {
422
+ return exactOrRange(CLI_PACKAGE_NAME, facts.declaredCliRange);
423
+ }
424
+ return {version: null, range: null};
425
+ }
426
+
427
+ /**
428
+ * @param {string} name
429
+ * @param {string} spec
430
+ * @returns {{version: string | null, range: {name: string, spec: string} | null}}
431
+ */
432
+ function exactOrRange(name, spec) {
433
+ const trimmed = spec.trim().replace(/^=/, '').replace(/^v/, '');
434
+ if (isReleaseVersion(trimmed)) return {version: trimmed, range: null};
435
+ return {version: null, range: {name, spec}};
436
+ }
437
+
438
+ /**
439
+ * Choose who answers a run started by `self` in `cwd`.
440
+ *
441
+ * A command that acts on the computer rather than the folder is chosen as
442
+ * outside a project wherever it runs (`computer`), so a project's older CLI
443
+ * never answers it.
444
+ *
445
+ * @param {{cwd: string, self: RunningCopy, cacheDir?: string, resolve?: typeof resolvePublishedVersion, computer?: boolean}} input
446
+ * @returns {ReleaseChoice}
447
+ */
448
+ export function chooseRelease({
449
+ cwd,
450
+ self,
451
+ resolve = resolvePublishedVersion,
452
+ computer = false,
453
+ }) {
454
+ const facts = computer ? OUTSIDE : readReleaseFacts(cwd);
455
+ const selfReal = real(self.root);
456
+
457
+ if (!facts.inProject) {
458
+ return {
459
+ copy: 'carried',
460
+ answer: 'self',
461
+ version: self.version,
462
+ bin: null,
463
+ coreVersion: null,
464
+ inProject: false,
465
+ unavailable: null,
466
+ };
467
+ }
468
+
469
+ // A project that installs its own CLI is answered by that CLI, whatever its
470
+ // version, and a project with none by the copy that started the run, as
471
+ // released (FR2, DEC-10; `spec:AST-017` FR12). Only a project whose own CLI
472
+ // cannot start gets the Core-matched release.
473
+ if (!facts.cli) {
474
+ // A copy that lives inside the project itself, such as a workspace's own
475
+ // CLI package beside its Core package, is the project's own CLI.
476
+ const inside = isInside(selfReal, projectRootOf(facts));
477
+ return {
478
+ copy: inside ? 'project' : 'starting',
479
+ answer: 'self',
480
+ version: self.version,
481
+ bin: null,
482
+ coreVersion: coreVersionOf(facts),
483
+ inProject: true,
484
+ unavailable: null,
485
+ };
486
+ }
487
+ {
488
+ if (samePath(real(facts.cli.dir), selfReal)) {
489
+ return {
490
+ copy: 'project',
491
+ answer: 'self',
492
+ version: facts.cli.version,
493
+ bin: null,
494
+ coreVersion: facts.core?.version ?? null,
495
+ inProject: true,
496
+ unavailable: null,
497
+ };
498
+ }
499
+ const bin = cliBinOf(facts.cli.dir);
500
+ if (bin) {
501
+ return {
502
+ copy: 'project',
503
+ answer: 'project-cli',
504
+ version: facts.cli.version,
505
+ bin,
506
+ coreVersion: facts.core?.version ?? null,
507
+ inProject: true,
508
+ unavailable: null,
509
+ };
510
+ }
511
+ }
512
+ let {version, range} = matchedVersionOf(facts);
513
+ if (!version && range) {
514
+ version = resolve(range.name, range.spec);
515
+ if (!version) {
516
+ return {
517
+ copy: 'matched',
518
+ answer: 'release',
519
+ version: `${range.name}@${range.spec}`,
520
+ bin: null,
521
+ coreVersion: null,
522
+ inProject: true,
523
+ unavailable: `no published release of ${range.name} matching "${range.spec}" could be found`,
524
+ };
525
+ }
526
+ }
527
+
528
+ const coreVersion = facts.core?.version ?? version;
529
+ if (!version || version === self.version) {
530
+ // A copy that lives inside the project itself, such as a workspace's own
531
+ // CLI package beside its Core package, is the project's own CLI.
532
+ if (isInside(selfReal, projectRootOf(facts))) {
533
+ return {
534
+ copy: 'project',
535
+ answer: 'self',
536
+ version: self.version,
537
+ bin: null,
538
+ coreVersion: facts.core?.version ?? null,
539
+ inProject: true,
540
+ unavailable: null,
541
+ };
542
+ }
543
+ return {
544
+ copy: 'matched',
545
+ answer: 'self',
546
+ version: self.version,
547
+ bin: null,
548
+ coreVersion,
549
+ inProject: true,
550
+ unavailable: null,
551
+ };
552
+ }
553
+ return {
554
+ copy: 'matched',
555
+ answer: 'release',
556
+ version,
557
+ bin: null,
558
+ coreVersion,
559
+ inProject: true,
560
+ unavailable: null,
561
+ };
562
+ }
563
+
564
+ /**
565
+ * The folder a project's facts point at: the declaring package.json's folder,
566
+ * else the folder that holds the Core (the one above its node_modules, or the
567
+ * workspace root above `packages/core`).
568
+ * @param {import('./project.mjs').ReleaseFacts} facts
569
+ * @returns {string | null}
570
+ */
571
+ export function projectRootOf(facts) {
572
+ if (facts.declaringDir) return facts.declaringDir;
573
+ const dir = facts.core?.dir ?? facts.cli?.dir;
574
+ if (!dir) return null;
575
+ const parts = path.resolve(dir).split(path.sep);
576
+ const at = parts.indexOf('node_modules');
577
+ if (at > 0) return parts.slice(0, at).join(path.sep) || path.sep;
578
+ return path.dirname(path.dirname(dir));
579
+ }
580
+
581
+ /**
582
+ * Is `child` inside `parent` (both real paths), outside any node_modules
583
+ * folder in between? A package installed into a project's node_modules is
584
+ * found as the project's CLI already; this covers a copy that is part of the
585
+ * project's own source, such as a workspace package.
586
+ * @param {string} child
587
+ * @param {string | null} parent
588
+ * @returns {boolean}
589
+ */
590
+ function isInside(child, parent) {
591
+ if (!parent) return false;
592
+ const rel = relativeWithin(real(parent), child);
593
+ if (rel === null) return false;
594
+ return !rel.split(/[\\/]/).includes('node_modules');
595
+ }
596
+
597
+ /** The facts of a folder outside any project. */
598
+ const OUTSIDE = Object.freeze({
599
+ inProject: false,
600
+ core: null,
601
+ cli: null,
602
+ declaringDir: null,
603
+ declaredCoreRange: null,
604
+ declaredCliRange: null,
605
+ lockedCoreVersion: null,
606
+ lockedCliVersion: null,
607
+ });
608
+
609
+ /** Options that take a value, so their value is not the command or topic. */
610
+ const VALUE_OPTIONS = new Set(['--lang', '--detail', '--depth']);
611
+
612
+ /**
613
+ * Is this run a command that acts on the computer rather than the folder it
614
+ * runs in? `astryx init --user`, which writes the computer-level agent text,
615
+ * and `astryx docs global[/…]`, the docs of global mode itself, which the copy
616
+ * that started the run answers, so they are readable inside a project whose
617
+ * release predates them (spec AST-081 FR8, FR9).
618
+ * @param {string[]} argv arguments after `astryx`
619
+ * @returns {boolean}
620
+ */
621
+ export function isComputerLevel(argv) {
622
+ const words = [];
623
+ for (let i = 0; i < argv.length && words.length < 2; i++) {
624
+ const arg = argv[i];
625
+ if (VALUE_OPTIONS.has(arg)) {
626
+ i += 1;
627
+ continue;
628
+ }
629
+ if (!arg.startsWith('-')) words.push(arg);
630
+ }
631
+ const [command, topic] = words;
632
+ if (command === 'init') return argv.includes('--user');
633
+ return (
634
+ command === 'docs' &&
635
+ typeof topic === 'string' &&
636
+ (topic === 'global' || topic.startsWith('global/'))
637
+ );
638
+ }
639
+
640
+ /**
641
+ * The running copy, from its package folder.
642
+ * @param {string} root
643
+ * @returns {RunningCopy}
644
+ */
645
+ export function runningCopy(root) {
646
+ const pkg = readJson(path.join(root, 'package.json'));
647
+ return {version: String(pkg?.version ?? '0.0.0'), root};
648
+ }
649
+
650
+ /**
651
+ * Does the package at `cliDir` print its own who-answered line? Releases that
652
+ * carry this module do; older ones answer silently, so the copy that hands a
653
+ * run to them prints the line for them.
654
+ * @param {string} cliDir
655
+ * @returns {boolean}
656
+ */
657
+ export function namesWhoAnswered(cliDir) {
658
+ return fs.existsSync(
659
+ path.join(cliDir, 'foundation', 'release', 'choose.mjs'),
660
+ );
661
+ }
662
+
663
+ /**
664
+ * The CLI package folder an entry file belongs to.
665
+ * @param {string} bin
666
+ * @returns {string}
667
+ */
668
+ export function packageRootOf(bin) {
669
+ let dir = path.dirname(real(bin));
670
+ for (;;) {
671
+ if (readJson(path.join(dir, 'package.json'))?.name === CLI_PACKAGE_NAME)
672
+ return dir;
673
+ const parent = path.dirname(dir);
674
+ if (parent === dir) return path.dirname(real(bin));
675
+ dir = parent;
676
+ }
677
+ }