@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
@@ -0,0 +1,160 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file `astryx upgrade` with a CLI older than the installed Core: the human
5
+ * output never says the code is up to date, warns once with the same upgrade
6
+ * pinned to the CLI release that matches the Core, and keeps the exit status;
7
+ * `--json` carries the same facts in `cliBehindCore`. Pasted anywhere, the
8
+ * printed command runs that upgrade in the same project.
9
+ */
10
+
11
+ import {describe, it, expect, beforeEach, afterEach} from 'vitest';
12
+ import {spawnSync} from 'node:child_process';
13
+ import * as fs from 'node:fs';
14
+ import * as os from 'node:os';
15
+ import * as path from 'node:path';
16
+ import {runCli} from '../../../test-utils/run-cli.mjs';
17
+ import {CLI_ROOT} from '../../../foundation/fs/paths.mjs';
18
+ import {
19
+ commandInFolder,
20
+ getDlxPrefix,
21
+ quoteCommandArgument,
22
+ } from '../../../foundation/env/package-manager.mjs';
23
+
24
+ const CLI_VERSION = JSON.parse(
25
+ fs.readFileSync(path.join(CLI_ROOT, 'package.json'), 'utf8'),
26
+ ).version;
27
+ const [MAJOR, MINOR] = CLI_VERSION.split('.').map(Number);
28
+ const NEWER_CORE = `${MAJOR}.${MINOR + 1}.0`;
29
+ const NON_ASCII = /[\u0080-\uFFFF]/;
30
+
31
+ let tmpDir;
32
+
33
+ /** @param {string} rel @param {string} body */
34
+ function write(rel, body) {
35
+ const file = path.join(tmpDir, rel);
36
+ fs.mkdirSync(path.dirname(file), {recursive: true});
37
+ fs.writeFileSync(file, body);
38
+ }
39
+
40
+ beforeEach(() => {
41
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-upgrade-older-cli-'));
42
+ write('package.json', JSON.stringify({name: 'consumer', private: true}));
43
+ write(
44
+ 'node_modules/@astryxdesign/core/package.json',
45
+ JSON.stringify({name: '@astryxdesign/core', version: NEWER_CORE}),
46
+ );
47
+ write('src/index.ts', 'export const x = 1;\n');
48
+ });
49
+
50
+ afterEach(() => {
51
+ fs.rmSync(tmpDir, {recursive: true, force: true});
52
+ });
53
+
54
+ /**
55
+ * The pinned command the CLI prints for this project. It names the folder the
56
+ * CLI ran in and the source directory as the CLI resolved them.
57
+ * @param {...string} rest the arguments after `--path <dir>`
58
+ */
59
+ function pinned(...rest) {
60
+ const project = fs.realpathSync(tmpDir);
61
+ return commandInFolder(
62
+ project,
63
+ [
64
+ getDlxPrefix(tmpDir),
65
+ `@astryxdesign/cli@${NEWER_CORE}`,
66
+ 'upgrade',
67
+ '--from',
68
+ CLI_VERSION,
69
+ '--path',
70
+ quoteCommandArgument(path.join(project, 'src')),
71
+ ...rest,
72
+ ].join(' '),
73
+ );
74
+ }
75
+
76
+ /**
77
+ * Stand-ins for the package runners: each drops its own words and the pinned
78
+ * package, then runs this CLI with the rest of the command.
79
+ */
80
+ const RUNS_THIS_CLI = [
81
+ ['npx', 1],
82
+ ['bunx', 1],
83
+ ['pnpm', 2],
84
+ ['yarn', 2],
85
+ ]
86
+ .map(
87
+ ([name, words]) =>
88
+ `${name}() { shift ${words}; "$0" ${quoteCommandArgument(path.join(CLI_ROOT, 'clients', 'cli', 'bin', 'astryx.mjs'))} "$@"; }`,
89
+ )
90
+ .join('; ');
91
+
92
+ describe('astryx upgrade with a CLI older than the installed Core', () => {
93
+ it('says it cannot finish and prints the upgrade pinned to the matching CLI', async () => {
94
+ const r = await runCli(['upgrade', '--from', CLI_VERSION, '--apply'], {
95
+ cwd: tmpDir,
96
+ });
97
+ const output = `${r.stdout}\n${r.stderr}`;
98
+
99
+ expect(r.status).toBe(0);
100
+ expect(output).not.toMatch(/up to date/i);
101
+ expect(output).not.toContain('No codemods available');
102
+ expect(r.stderr).toContain(
103
+ `This CLI (${CLI_VERSION}) is older than the installed @astryxdesign/core (${NEWER_CORE})`,
104
+ );
105
+ // The command has a line of its own, so it copies exactly.
106
+ expect(r.stderr).toContain(
107
+ `the CLI that matches your Core:\n ${pinned('--apply')}\n`,
108
+ );
109
+ expect(r.stdout.trim().split('\n').at(-1)).toBe('Upgrade incomplete');
110
+ expect(output.split('\n').filter(line => NON_ASCII.test(line))).toEqual([]);
111
+ });
112
+
113
+ it('carries cliBehindCore in --json with the same exit status', async () => {
114
+ const r = await runCli(['--json', 'upgrade', '--from', CLI_VERSION], {
115
+ cwd: tmpDir,
116
+ });
117
+ const parsed = JSON.parse(r.stdout);
118
+
119
+ expect(r.status).toBe(0);
120
+ expect(parsed.type).toBe('upgrade.status');
121
+ expect(parsed.data.status).toBe('no_codemods');
122
+ expect(parsed.data.cliBehindCore).toEqual({
123
+ cliVersion: CLI_VERSION,
124
+ command: pinned(),
125
+ });
126
+ });
127
+
128
+ // Where an upgrade runs decides what it finds: the installed Core is read
129
+ // from that folder, and --path must stay inside it. Printed without its
130
+ // folder, the command exited 1 when pasted in a subfolder
131
+ // (ERR_PATH_TRAVERSAL) or in the folder above (ERR_VERSION_DETECT).
132
+ it.skipIf(process.platform === 'win32')(
133
+ 'prints a command that upgrades the same project wherever it is pasted',
134
+ async () => {
135
+ write('src/deep/index.ts', 'export const y = 2;\n');
136
+ const r = await runCli(['--json', 'upgrade', '--from', CLI_VERSION], {
137
+ cwd: tmpDir,
138
+ });
139
+ const {command} = JSON.parse(r.stdout).data.cliBehindCore;
140
+
141
+ for (const pastedIn of [
142
+ path.join(tmpDir, 'src', 'deep'),
143
+ path.dirname(tmpDir),
144
+ ]) {
145
+ const ran = spawnSync(
146
+ '/bin/sh',
147
+ ['-c', `${RUNS_THIS_CLI}; ${command}`, process.execPath],
148
+ {cwd: pastedIn, encoding: 'utf8'},
149
+ );
150
+
151
+ expect(ran.status, ran.stderr).toBe(0);
152
+ expect(ran.stdout).toContain(`Installed target: ${NEWER_CORE}`);
153
+ expect(ran.stdout).toContain(
154
+ `Scanning ${path.join(fs.realpathSync(tmpDir), 'src')} `,
155
+ );
156
+ }
157
+ },
158
+ 30_000,
159
+ );
160
+ });
@@ -16,8 +16,14 @@ import {Command, Option} from 'commander';
16
16
  import {fileURLToPath} from 'node:url';
17
17
  import * as fs from 'node:fs';
18
18
  import * as path from 'node:path';
19
- import {getCliInvocation} from '../../foundation/env/package-manager.mjs';
19
+ import {formatCliCommand, getCliInvocation} from '../../foundation/env/package-manager.mjs';
20
+ import {hasRecordedProject} from '../../foundation/env/cli-command.mjs';
21
+ import {isOutsideProject} from '../../foundation/env/project-scope.mjs';
20
22
  import {API_VERSION, setJsonMode} from '../../foundation/response/json.mjs';
23
+ import {answeredBy, runChoice} from '../../foundation/release/run-context.mjs';
24
+ import {addCliCommand} from '../../foundation/release/notices.mjs';
25
+ import {projectRootOf} from '../../foundation/release/choose.mjs';
26
+ import {readReleaseFacts} from '../../foundation/release/project.mjs';
21
27
  import {buildManifest} from './lib/manifest.mjs';
22
28
  import {cliError} from './lib/cli-error.mjs';
23
29
  import {emit, section, text, records} from './formatters/index.mjs';
@@ -26,7 +32,9 @@ import {levenshteinDistance} from '../../foundation/text/string-utils.mjs';
26
32
  import {installJsonShim} from './lib/json-shim.mjs';
27
33
  import {addDocHelp, markReportsResult} from './lib/define-command.mjs';
28
34
  import {doc as manifestDoc} from './commands/manifest.doc.mjs';
29
- import {isAstryxInitialized} from '../../foundation/agent-docs/agent-docs.mjs';
35
+ import {isAstryxInitialized, isAstryxPromptInReach} from '../../foundation/agent-docs/agent-docs.mjs';
36
+ import {CLI_ROOT} from '../../foundation/fs/paths.mjs';
37
+ import {setupNudge} from '../../foundation/agent-docs/agent-doc-state.mjs';
30
38
  import * as debug from '../../foundation/debug/index.mjs';
31
39
 
32
40
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -63,7 +71,7 @@ export function handleVersionJsonPreflight(argv = process.argv.slice(2)) {
63
71
  // reports the same shape Commander's own `--version` path does.
64
72
  debug.recordCommandResult(debug.NO_RESULT_SET);
65
73
  debug.setOutcome('ok', {exitCode: 0});
66
- console.log(JSON.stringify({apiVersion: API_VERSION, type: 'version', data: {version: pkg.version}}, null, 2));
74
+ console.log(JSON.stringify({apiVersion: API_VERSION, type: 'version', data: {version: pkg.version}, meta: {answeredBy: answeredBy()}}, null, 2));
67
75
  process.exit(0);
68
76
  }
69
77
 
@@ -98,6 +106,7 @@ export const JSON_SUPPORTED = new Set([
98
106
  'theme targets',
99
107
  'theme palette generate',
100
108
  'integration add',
109
+ 'integration check',
101
110
  'integration verify',
102
111
  'integration pack',
103
112
  'upgrade',
@@ -184,6 +193,31 @@ export async function loadProjectDebugHandler() {
184
193
  }
185
194
  }
186
195
 
196
+ /**
197
+ * Load the project's integrations before a help run, so help prints commands
198
+ * with the `cliCommand` they declare (spec:AST-081 FR5). Help is printed while
199
+ * Commander parses, before any hook, so the preAction load cannot reach it.
200
+ * `Project.load` reads astryx.config and each integration's manifest; it does
201
+ * no discovery. Skipped when the debug preload already loaded the project, and
202
+ * outside a package. Called from the bin before Commander parses.
203
+ *
204
+ * @param {string[]} [argv] arguments after the binary.
205
+ * @returns {Promise<void>}
206
+ */
207
+ export async function loadProjectForHelp(argv = process.argv.slice(2)) {
208
+ const help =
209
+ argv.includes('--help') || argv.includes('-h') || argv[0] === 'help';
210
+ if (!help) return;
211
+ try {
212
+ const cwd = process.cwd();
213
+ if (hasRecordedProject(cwd)) return;
214
+ const {Project} = await import('../../foundation/config/project.mjs');
215
+ await Project.load(cwd);
216
+ } catch {
217
+ // Help prints with the package-manager guess.
218
+ }
219
+ }
220
+
187
221
  /**
188
222
  * Hand the whole invocation to the debug recorder: which command ran, its
189
223
  * positional arguments by name, its options and where each value came from,
@@ -276,6 +310,26 @@ const commands = [
276
310
  ];
277
311
 
278
312
  const SETUP_NUDGE_EXEMPT = new Set(['init', 'agent-docs']);
313
+
314
+ /**
315
+ * Is `cwd` inside the CLI's own source checkout while the CLI runs from it?
316
+ * An installed CLI always lives under `node_modules`; a CLI running from source
317
+ * does not, and its checkout is the pnpm workspace that holds it. Developing
318
+ * Astryx is not an app to set up, so the setup nudge stays quiet there.
319
+ * @param {string} cwd
320
+ * @returns {boolean}
321
+ */
322
+ function inOwnSourceCheckout(cwd) {
323
+ if (CLI_ROOT.split(path.sep).includes('node_modules')) return false;
324
+ let checkout = CLI_ROOT;
325
+ while (!fs.existsSync(path.join(checkout, 'pnpm-workspace.yaml'))) {
326
+ const parent = path.dirname(checkout);
327
+ if (parent === checkout) return false;
328
+ checkout = parent;
329
+ }
330
+ const rel = path.relative(checkout, path.resolve(cwd));
331
+ return rel !== '..' && !rel.startsWith(`..${path.sep}`) && !path.isAbsolute(rel);
332
+ }
279
333
  /** An integration package's manifest: the package is not an app to set up. */
280
334
  const INTEGRATION_MANIFEST_FILES = [
281
335
  'astryx.integration.ts',
@@ -307,7 +361,8 @@ export async function createProgram() {
307
361
  // kit in clients/cli/formatters.
308
362
  program.addHelpText(
309
363
  'after',
310
- '\n' +
364
+ // A function, so the example names the invocation when help is printed.
365
+ () => '\n' +
311
366
  [
312
367
  section(
313
368
  'Output format',
@@ -330,9 +385,13 @@ export async function createProgram() {
330
385
  {fields: ['block', 'shape']},
331
386
  ),
332
387
  text(
333
- 'Grep a field across records, e.g. astryx search button | grep "^command:". ' +
388
+ `Grep a field across records, e.g. ${formatCliCommand('search button')} | grep "^command:". ` +
334
389
  'Errors/warnings go to stderr; use --json for structured parsing.',
335
390
  ),
391
+ text(
392
+ "Global mode: in a project, the project's own CLI answers (this copy hands the run to it); a copy installed for the computer checks for updates once a day and keeps an agent skill in your home folder. " +
393
+ `See ${formatCliCommand('docs global')}.`,
394
+ ),
336
395
  ]
337
396
  .map(block => block.toString())
338
397
  .join('\n\n'),
@@ -429,6 +488,7 @@ export async function createProgram() {
429
488
  // Enriched, self-describing surface (the full manifest payload).
430
489
  manifest,
431
490
  },
491
+ meta: {answeredBy: answeredBy()},
432
492
  }, null, 2));
433
493
  debug.recordCommandResult(
434
494
  debug.resultSet({
@@ -525,10 +585,29 @@ export async function createProgram() {
525
585
  process.exit(1);
526
586
  });
527
587
 
588
+ /**
589
+ * Load the project's integrations before any command runs, so every command
590
+ * it prints starts with the `cliCommand` they declare (the picker reads them
591
+ * synchronously; see foundation/env/cli-command.mjs). Skipped when this
592
+ * process already loaded the project, such as the bin's debug preload. A
593
+ * project that cannot load is the command's to report, never this hook's.
594
+ */
595
+ program.hook('preAction', async () => {
596
+ try {
597
+ const cwd = process.cwd();
598
+ if (hasRecordedProject(cwd)) return;
599
+ const {Project} = await import('../../foundation/config/project.mjs');
600
+ await Project.load(cwd);
601
+ } catch {
602
+ // Best-effort: the picker falls back to its package-manager guess.
603
+ }
604
+ });
605
+
528
606
  /**
529
607
  * Enforcement layer 3 — setup nudge. If this project hasn't run `astryx init`
530
- * yet (no Astryx marker in any agent-doc file — see isAstryxInitialized), remind
531
- * the user/agent that setup is missing.
608
+ * yet (no Astryx marker in any agent-doc file between the project and its
609
+ * repository root — see isAstryxPromptInReach), remind the user/agent that
610
+ * setup is missing.
532
611
  *
533
612
  * Uses `preAction` (not postAction) so it fires for EVERY valid command — even
534
613
  * ones whose action errors or calls process.exit (postAction is skipped then).
@@ -538,7 +617,9 @@ export async function createProgram() {
538
617
  * consumers parse stdout, not stderr — a stderr nudge would not reach them anyway.
539
618
  * The core/cli postinstall layers already nudge at install time regardless of
540
619
  * --json; a machine-readable nudge could later be an envelope field. Also skipped
541
- * for the installer commands themselves and outside a project (no package.json).
620
+ * for the installer commands themselves, outside a project (no package.json,
621
+ * or one that neither installs nor declares Astryx), and inside the CLI's own
622
+ * source checkout when it runs from there.
542
623
  */
543
624
  program.hook('preAction', (thisCommand, actionCommand) => {
544
625
  try {
@@ -546,15 +627,30 @@ export async function createProgram() {
546
627
  if (SETUP_NUDGE_EXEMPT.has(actionCommand.name())) return;
547
628
  const cwd = process.cwd();
548
629
  if (!fs.existsSync(path.join(cwd, 'package.json'))) return; // not a project
630
+ // A package.json that neither installs nor declares Astryx is outside a
631
+ // project: `init` would set up a project the folder does not have
632
+ // (spec AST-081 FR3).
633
+ if (isOutsideProject(cwd)) return;
549
634
  // An integration package is not an app: `init` is not its next step.
550
635
  if (INTEGRATION_MANIFEST_FILES.some(file => fs.existsSync(path.join(cwd, file)))) return;
551
- if (isAstryxInitialized(cwd)) return; // already set up — stay quiet
552
- // Same wording as the core/cli postinstall nudges. #4151's getCliInvocation()
553
- // renders the correct form for THIS project — scoped `npx @astryxdesign/cli`
554
- // one-off, or `<pm> astryx` when installed — never the bare `npx astryx` footgun.
555
- console.error(
556
- `\nNext step: run \`${getCliInvocation(cwd)} init\` to finish setup and install the Astryx agent prompt.`,
557
- );
636
+ if (inOwnSourceCheckout(cwd)) return; // developing Astryx itself, not an app
637
+ if (isAstryxPromptInReach(cwd)) return; // already set up — stay quiet
638
+ // Same wording as the core/cli postinstall nudges. getCliInvocation()
639
+ // renders the correct form for THIS project — an integration's cliCommand,
640
+ // scoped `npx @astryxdesign/cli` one-off, or `<pm> astryx` when installed —
641
+ // never npx with the unscoped name, which fetches an unrelated package.
642
+ // A project with no CLI of its own, answered by another release than its
643
+ // Core's: add the CLI at Core's version first, as the daily hint says,
644
+ // then run that CLI's init (spec AST-081 FR2).
645
+ const choice = runChoice(cwd);
646
+ if (choice.copy === 'starting' && choice.coreVersion && choice.coreVersion !== choice.version) {
647
+ const root = projectRootOf(readReleaseFacts(cwd)) ?? cwd;
648
+ console.error(
649
+ setupNudge(getCliInvocation(root, {installed: true}), {before: addCliCommand(root, choice.coreVersion)}).trimEnd(),
650
+ );
651
+ return;
652
+ }
653
+ console.error(setupNudge(getCliInvocation(cwd)).trimEnd());
558
654
  } catch {
559
655
  // Never let the nudge break a command.
560
656
  }
@@ -607,7 +703,7 @@ export async function createProgram() {
607
703
  );
608
704
  if (program.opts().json) {
609
705
  process.__xdsJsonHandled = true;
610
- console.log(JSON.stringify({apiVersion: API_VERSION, type: 'manifest', data: manifest}, null, 2));
706
+ console.log(JSON.stringify({apiVersion: API_VERSION, type: 'manifest', data: manifest, meta: {answeredBy: answeredBy()}}, null, 2));
611
707
  return;
612
708
  }
613
709
  // Human-readable summary as greppable records (agents should use --json).
@@ -53,6 +53,7 @@
53
53
  import {isJsonMode, jsonError as _jsonError, humanWarn} from '../../../foundation/response/json.mjs';
54
54
  import {ERROR_CODES} from '../../../foundation/response/error-codes.mjs';
55
55
  import {setOutcome} from '../../../foundation/debug/index.mjs';
56
+ import {formatCliCommand} from '../../../foundation/env/package-manager.mjs';
56
57
 
57
58
  /**
58
59
  * Suggestion object — matches the shape used by API errors and the JSON
@@ -122,7 +123,9 @@ export function cliError(message, options = {}) {
122
123
  if (suggestions?.length) {
123
124
  console.error('');
124
125
  for (const s of suggestions) {
125
- const tag = s.reason ? ` ${s.name} (${s.reason})` : ` ${s.name}`;
126
+ // A suggested command stays bare in JSON and is prefixed here.
127
+ const name = /^astryx(\s|$)/.test(s.name) ? formatCliCommand(s.name) : s.name;
128
+ const tag = s.reason ? ` ${name} (${s.reason})` : ` ${name}`;
126
129
  console.error(tag);
127
130
  }
128
131
  }
@@ -67,7 +67,7 @@ describe('formatFull sub-component rendering', () => {
67
67
  expect(out).not.toContain('undefined');
68
68
  // Points the reader at the sub-component's own docs instead of a blank.
69
69
  expect(out).toMatch(
70
- /(?:astryx|@astryxdesign\/cli) component XDSRadioListItem/,
70
+ /(?:astryx|@astryxdesign\/cli(?:@\S+)?) component XDSRadioListItem/,
71
71
  );
72
72
  });
73
73
 
@@ -180,8 +180,8 @@ export function defineCommand(parent, doc, {fn, action} = {}) {
180
180
  */
181
181
  export function addDocHelp(cmd, doc) {
182
182
  addExitCodesHelp(cmd, doc.exitCodes);
183
- // Rendered when help is shown, so the run prefix (npx astryx, pnpm astryx,
184
- // ...) is looked up then, not on every start.
183
+ // Rendered when help is shown, so the run prefix (pnpm exec astryx, yarn
184
+ // astryx, ...) is looked up then, not on every start.
185
185
  cmd.addHelpText('after', () => {
186
186
  const examples = (doc.examples ?? []).flatMap(({label, cli}) => [
187
187
  ...(label ? [` # ${label}`] : []),
@@ -42,6 +42,7 @@
42
42
  */
43
43
 
44
44
  import {API_VERSION, isJsonMode, toErrorEnvelope} from '../../../foundation/response/json.mjs';
45
+ import {answeredBy} from '../../../foundation/release/run-context.mjs';
45
46
  import {ERROR_CODES} from '../../../foundation/response/error-codes.mjs';
46
47
  import {setCommand, setOutcome, recordHelp} from '../../../foundation/debug/index.mjs';
47
48
 
@@ -84,7 +85,7 @@ function jsonActive() {
84
85
  * subcommand (e.g. `astryx component --help --json`).
85
86
  *
86
87
  * @param {import('commander').Command} cmd
87
- * @returns {{apiVersion: number, type: 'help', data: object}}
88
+ * @returns {{apiVersion: number, type: 'help', data: object, meta: {answeredBy: {cli: string, core: string | null, copy: 'project' | 'starting' | 'matched' | 'carried' | 'fallback'}}}}
88
89
  */
89
90
  export function buildHelpEnvelope(cmd) {
90
91
  // Reconstruct the fully-qualified command name (e.g. "astryx theme build").
@@ -119,6 +120,7 @@ export function buildHelpEnvelope(cmd) {
119
120
  options,
120
121
  subcommands,
121
122
  },
123
+ meta: {answeredBy: answeredBy()},
122
124
  };
123
125
  }
124
126
 
@@ -50,6 +50,13 @@ export interface ManifestCommand {
50
50
  examples?: string[];
51
51
  /** Documented exit codes: the process exit status and when it occurs. */
52
52
  exitCodes?: {code: number; when: string}[];
53
+ /**
54
+ * What the command does in a folder outside any project: `same` gives the
55
+ * answer it gives in a new project; `refuse` writes nothing and fails, naming
56
+ * the app project or integration package it needs; `own` does something
57
+ * else, which its docs state. Absent on a group that only holds subcommands.
58
+ */
59
+ outsideProject?: 'same' | 'refuse' | 'own';
53
60
  /** Nested subcommands (e.g. `theme build` under `theme`). */
54
61
  subcommands?: ManifestCommand[];
55
62
  }
@@ -64,6 +71,8 @@ export interface CLIManifest {
64
71
  commands: ManifestCommand[];
65
72
  /** Sorted list of fully-qualified command names that support `--json`. */
66
73
  jsonSupported: string[];
74
+ /** What a run may do beyond its command (the hand-off, the daily update check, the agent skill), and the docs topic that explains it. */
75
+ globalMode: {docs: string; summary: string};
67
76
  /** Flat index of response `type` discriminators keyed by command name. */
68
77
  responseTypes: Record<string, string[]>;
69
78
  }
@@ -9,7 +9,8 @@
9
9
  *
10
10
  * Its per-command facts come from the command's own definitions: names,
11
11
  * arguments and options from Commander metadata (program.commands, cmd.options,
12
- * cmd.registeredArguments, cmd.description()); examples from its CommandDoc;
12
+ * cmd.registeredArguments, cmd.description()); examples, exit codes, and what
13
+ * it does outside a project from its CommandDoc;
13
14
  * response `type` discriminators from the returns of the FunctionDoc it wraps
14
15
  * (both attached by `defineCommand`); `--json` support from the JSON_SUPPORTED
15
16
  * allowlist in index.mjs. The hand-kept lists are CLI_LAYER_RESPONSE_TYPES,
@@ -39,6 +40,16 @@ import {API_VERSION} from '../../../foundation/response/json.mjs';
39
40
  import {commandDocsOf} from './define-command.mjs';
40
41
  import {doc as manifestDoc} from '../commands/manifest.doc.mjs';
41
42
 
43
+ /**
44
+ * What a run may do beyond the command it was asked for (spec AST-081;
45
+ * `spec:AST-017` FR14), and where it is explained.
46
+ */
47
+ export const GLOBAL_MODE = Object.freeze({
48
+ docs: 'astryx docs global',
49
+ summary:
50
+ "In a folder that belongs to a project, the project's own CLI answers: this copy hands the run to it. In a project with no CLI of its own, and outside any project, this copy answers. A copy installed for the computer checks the registry for a newer release once a day in the background, and keeps one agent skill file in the home folder.",
51
+ });
52
+
42
53
  /**
43
54
  * Envelopes the CLI layer builds itself, so the wrapped FunctionDoc cannot
44
55
  * declare them: `manifest` wraps no API function, `theme build` batches several
@@ -194,6 +205,10 @@ function describeCommand(cmd, root, jsonSupported) {
194
205
  }));
195
206
  if (exitCodes.length > 0) entry.exitCodes = exitCodes;
196
207
 
208
+ // What the command does outside a project (spec AST-081 FR4). Every command
209
+ // declares it in its CommandDoc; a group that only holds subcommands does not.
210
+ if (docs?.doc.outsideProject) entry.outsideProject = docs.doc.outsideProject;
211
+
197
212
  // Sort subcommands by name for a stable, agent-facing contract — the same
198
213
  // guarantee the top-level command list makes. Otherwise Commander
199
214
  // registration order leaks into the manifest and a pure reorder of
@@ -265,6 +280,7 @@ export function buildManifest(program, opts = {}) {
265
280
  globalOptions: describeGlobalOptions(program),
266
281
  commands,
267
282
  jsonSupported: [...jsonSupported].sort(),
283
+ globalMode: GLOBAL_MODE,
268
284
  // Flat index of every response `type` discriminator the CLI can emit,
269
285
  // keyed by command — lets an agent know what to expect back per call.
270
286
  responseTypes: Object.fromEntries(
@@ -307,3 +307,28 @@ describe('manifest: text projection', () => {
307
307
  for (const key of textKeys) expect(jsonKeys).toContain(key);
308
308
  });
309
309
  });
310
+
311
+ describe('global mode is discoverable (spec:AST-017 FR14)', () => {
312
+ it('the manifest names what a run may do beyond its command, and the docs topic that explains it', () => {
313
+ expect(manifest.globalMode.docs).toBe('astryx docs global');
314
+ for (const word of ['hands the run', 'once a day', 'agent skill']) {
315
+ expect(manifest.globalMode.summary).toContain(word);
316
+ }
317
+ });
318
+
319
+ it('--help carries one line pointing to the global mode docs', () => {
320
+ const help = program.helpInformation() + '';
321
+ let after = '';
322
+ program.emit('afterHelp', {
323
+ error: false,
324
+ write: (/** @type {string} */ s) => {
325
+ after += s;
326
+ },
327
+ });
328
+ const lines = `${help}${after}`
329
+ .split('\n')
330
+ .filter(line => line.startsWith('Global mode:'));
331
+ expect(lines).toHaveLength(1);
332
+ expect(lines[0]).toMatch(/docs global\.$/u);
333
+ });
334
+ });
@@ -20,6 +20,34 @@ export function discoverAgentDocs(targetDir: string): string[];
20
20
  * @returns {boolean}
21
21
  */
22
22
  export function isAstryxInitialized(targetDir?: string): boolean;
23
+ /**
24
+ * Does an agent working in `startDir` already have the Astryx prompt in reach?
25
+ *
26
+ * Coding agents read the agent docs between the repository root and the folder
27
+ * they work in. So inside a git repository, init's block in any folder from
28
+ * `startDir` up to the repository root counts: a workspace package of a
29
+ * monorepo initialized at its root is set up. Outside a repository only
30
+ * `startDir` itself is checked, as {@link isAstryxInitialized} does. Both the
31
+ * per-command nudge and the postinstall nudges ask this.
32
+ *
33
+ * @param {string} [startDir=process.cwd()]
34
+ * @returns {boolean}
35
+ */
36
+ export function isAstryxPromptInReach(startDir?: string): boolean;
37
+ /**
38
+ * The one-line next-step every setup layer prints. Kept here so layer 1
39
+ * (core), layer 2 (cli postinstall) and layer 3 (each command) can never word
40
+ * it differently. Each layer passes the stem from the CLI's picker when it can
41
+ * load it.
42
+ *
43
+ * @param {string} [invocation] the CLI invocation stem, e.g. `pnpm exec astryx`
44
+ * @param {{before?: string | null}} [options] `before`: a command to run
45
+ * first, such as the one that adds the CLI to a project that has none
46
+ * @returns {string}
47
+ */
48
+ export function setupNudge(invocation?: string, { before }?: {
49
+ before?: string | null;
50
+ }): string;
23
51
  /**
24
52
  * Pure decision: should a postinstall print the setup nudge? Shared by BOTH
25
53
  * postinstall layers, so the matrix cannot drift between them, and unit-testable
@@ -61,11 +89,12 @@ export const AGENT_DOC_PATHS: string[];
61
89
  */
62
90
  export const INIT_MARKERS: string[];
63
91
  /**
64
- * The one-line next-step both postinstall nudges print. Kept here so layer 1
65
- * (core) and layer 2 (cli) can never word it differently.
66
- *
67
- * Scoped package form (`@astryxdesign/cli`) — always resolves to us, even before
68
- * the CLI is installed. Bare `npx astryx` would fetch an unrelated look-alike
92
+ * The invocation the setup nudge falls back to when the CLI's picker
93
+ * (`getCliInvocation`) cannot be loaded. Scoped package form
94
+ * (`@astryxdesign/cli`) — always resolves to us, even before the CLI is
95
+ * installed. npx with the unscoped name would fetch an unrelated look-alike
69
96
  * package (see PR #4151).
70
97
  */
71
- export const SETUP_NUDGE: "\nNext step: run `npx @astryxdesign/cli init` to finish setup and install the Astryx agent prompt.\n\n";
98
+ export const SETUP_INVOCATION: "npx @astryxdesign/cli";
99
+ /** The nudge with the fallback invocation. */
100
+ export const SETUP_NUDGE: string;