@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,292 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file Computer-level integrations (spec AST-081 FR12): which install root
5
+ * counts, which packages in it are integrations, and the peer-range check.
6
+ */
7
+
8
+ import {afterEach, describe, expect, it} from 'vitest';
9
+ import * as fs from 'node:fs';
10
+ import * as path from 'node:path';
11
+ import {
12
+ computerInstallRoot,
13
+ computerInstallRoots,
14
+ loadComputerIntegrations,
15
+ peerProblem,
16
+ scanComputerIntegrations,
17
+ } from './computer-integrations.mjs';
18
+ import {
19
+ cleanupFixtures,
20
+ tempDir,
21
+ writeJson,
22
+ } from '../../test-utils/release-fixtures.mjs';
23
+
24
+ afterEach(cleanupFixtures);
25
+
26
+ /**
27
+ * A package at `dir`, with a manifest when `manifest` is set.
28
+ * @param {string} dir
29
+ * @param {Record<string, unknown>} pkg
30
+ * @param {string | null} [manifest]
31
+ */
32
+ function pkgAt(dir, pkg, manifest = 'export default {};\n') {
33
+ writeJson(path.join(dir, 'package.json'), pkg);
34
+ if (manifest != null) {
35
+ fs.writeFileSync(path.join(dir, 'astryx.integration.mjs'), manifest);
36
+ }
37
+ return dir;
38
+ }
39
+
40
+ /** An npm-style global folder (no package.json) holding the CLI and others. */
41
+ function npmGlobal() {
42
+ const root = path.join(tempDir('prefix'), 'lib');
43
+ const modules = path.join(root, 'node_modules');
44
+ const cli = pkgAt(
45
+ path.join(modules, '@astryxdesign', 'cli'),
46
+ {name: '@astryxdesign/cli', version: '0.8.0'},
47
+ null,
48
+ );
49
+ pkgAt(path.join(modules, '@astryxdesign', 'core'), {
50
+ name: '@astryxdesign/core',
51
+ version: '0.8.0',
52
+ });
53
+ pkgAt(path.join(modules, '@acme', 'widgets'), {
54
+ name: '@acme/widgets',
55
+ version: '1.2.0',
56
+ description: 'Acme widgets',
57
+ });
58
+ pkgAt(path.join(modules, 'plain-tool'), {name: 'plain-tool'}, null);
59
+ pkgAt(path.join(modules, 'zeta-kit'), {
60
+ name: 'zeta-kit',
61
+ version: '2.0.0',
62
+ peerDependencies: {'@astryxdesign/core': '>=9.0.0'},
63
+ });
64
+ return {root, cli};
65
+ }
66
+
67
+ describe('computerInstallRoot', () => {
68
+ it('is the folder whose node_modules holds the CLI; none for a runner cache or a folder outside node_modules', () => {
69
+ const {root, cli} = npmGlobal();
70
+ expect(computerInstallRoot(cli)).toBe(fs.realpathSync(root));
71
+ const runner = pkgAt(
72
+ path.join(
73
+ tempDir('npx'),
74
+ '_npx',
75
+ 'ab12',
76
+ 'node_modules',
77
+ '@astryxdesign',
78
+ 'cli',
79
+ ),
80
+ {name: '@astryxdesign/cli', version: '0.8.0'},
81
+ null,
82
+ );
83
+ expect(computerInstallRoot(runner)).toBeNull();
84
+ expect(computerInstallRoot(tempDir('checkout'))).toBeNull();
85
+ });
86
+
87
+ it('is none for a release in the per-user release cache', () => {
88
+ const cacheDir = tempDir('cache');
89
+ const cli = pkgAt(
90
+ path.join(
91
+ cacheDir,
92
+ 'releases',
93
+ '0.8.0',
94
+ 'node_modules',
95
+ '@astryxdesign',
96
+ 'cli',
97
+ ),
98
+ {name: '@astryxdesign/cli', version: '0.8.0'},
99
+ null,
100
+ );
101
+ expect(computerInstallRoot(cli, {cacheDir})).toBeNull();
102
+ });
103
+ });
104
+
105
+ describe('computerInstallRoots', () => {
106
+ it("is pnpm's shared global folder before pnpm 11", () => {
107
+ const g5 = path.join(tempDir('pnpm'), 'global', '5');
108
+ const cli = pkgAt(
109
+ path.join(
110
+ g5,
111
+ '.pnpm',
112
+ '@astryxdesign+cli@0.8.0',
113
+ 'node_modules',
114
+ '@astryxdesign',
115
+ 'cli',
116
+ ),
117
+ {name: '@astryxdesign/cli', version: '0.8.0'},
118
+ null,
119
+ );
120
+ expect(computerInstallRoots(cli)).toEqual([fs.realpathSync(g5)]);
121
+ });
122
+
123
+ it('is every pnpm global folder since pnpm 11, when one of them installs this CLI', () => {
124
+ const home = tempDir('pnpm-home');
125
+ const cli = pkgAt(
126
+ path.join(
127
+ home,
128
+ 'store',
129
+ 'v11',
130
+ 'links',
131
+ '@astryxdesign',
132
+ 'cli',
133
+ '0.8.0',
134
+ 'h',
135
+ 'node_modules',
136
+ '@astryxdesign',
137
+ 'cli',
138
+ ),
139
+ {name: '@astryxdesign/cli', version: '0.8.0'},
140
+ null,
141
+ );
142
+ const a = path.join(home, 'global', 'v11', 'a');
143
+ const b = path.join(home, 'global', 'v11', 'b');
144
+ writeJson(path.join(b, 'package.json'), {
145
+ dependencies: {'@acme/widgets': '1.0.0'},
146
+ });
147
+ // No global folder installs this CLI yet: only its own install root.
148
+ expect(computerInstallRoots(cli)).toHaveLength(1);
149
+ writeJson(path.join(a, 'package.json'), {
150
+ dependencies: {'@astryxdesign/cli': '0.8.0'},
151
+ });
152
+ fs.mkdirSync(path.join(a, 'node_modules', '@astryxdesign'), {
153
+ recursive: true,
154
+ });
155
+ fs.symlinkSync(cli, path.join(a, 'node_modules', '@astryxdesign', 'cli'));
156
+ expect(computerInstallRoots(cli)).toEqual([a, b]);
157
+ });
158
+
159
+ it("is every pnpm global folder when the CLI's files sit inside its own global folder (pnpm 12)", () => {
160
+ const globalDir = path.join(tempDir('pnpm12'), 'global', 'v11');
161
+ const a = path.join(globalDir, 'a');
162
+ const b = path.join(globalDir, 'b');
163
+ writeJson(path.join(a, 'package.json'), {
164
+ dependencies: {'@astryxdesign/cli': '0.8.0'},
165
+ });
166
+ const cli = pkgAt(
167
+ path.join(
168
+ a,
169
+ 'node_modules',
170
+ '.pnpm',
171
+ '@astryxdesign+cli@0.8.0',
172
+ 'node_modules',
173
+ '@astryxdesign',
174
+ 'cli',
175
+ ),
176
+ {name: '@astryxdesign/cli', version: '0.8.0'},
177
+ null,
178
+ );
179
+ fs.mkdirSync(path.join(a, 'node_modules', '@astryxdesign'), {
180
+ recursive: true,
181
+ });
182
+ fs.symlinkSync(cli, path.join(a, 'node_modules', '@astryxdesign', 'cli'));
183
+ writeJson(path.join(b, 'package.json'), {
184
+ dependencies: {'@acme/widgets': '1.0.0'},
185
+ });
186
+ pkgAt(path.join(b, 'node_modules', '@acme', 'widgets'), {
187
+ name: '@acme/widgets',
188
+ version: '1.0.0',
189
+ });
190
+ expect(computerInstallRoots(cli)).toEqual([
191
+ fs.realpathSync(a),
192
+ fs.realpathSync(b),
193
+ ]);
194
+ expect(
195
+ scanComputerIntegrations({cliRoot: cli, release: '0.8.0'}).map(
196
+ c => c.name,
197
+ ),
198
+ ).toEqual(['@acme/widgets']);
199
+ });
200
+ });
201
+
202
+ describe('scanComputerIntegrations', () => {
203
+ it('lists every package beside the CLI that ships a manifest, never the CLI or Core, and marks an incompatible one', () => {
204
+ const {cli} = npmGlobal();
205
+ const found = scanComputerIntegrations({cliRoot: cli, release: '0.8.0'});
206
+ expect(found.map(c => [c.name, c.version, c.incompatible])).toEqual([
207
+ ['@acme/widgets', '1.2.0', null],
208
+ [
209
+ 'zeta-kit',
210
+ '2.0.0',
211
+ "it needs @astryxdesign/core >=9.0.0, and this computer's CLI is 0.8.0",
212
+ ],
213
+ ]);
214
+ expect(found[0].description).toBe('Acme widgets');
215
+ });
216
+
217
+ it("reads a door's package set from the root's declared dependencies", () => {
218
+ const root = tempDir('runtime');
219
+ writeJson(path.join(root, 'package.json'), {
220
+ name: 'runtime',
221
+ private: true,
222
+ dependencies: {
223
+ '@astryxdesign/cli': '0.8.0',
224
+ '@astryxdesign/core': '0.8.0',
225
+ '@acme/meta-kit': '3.0.0',
226
+ },
227
+ });
228
+ const modules = path.join(root, 'node_modules');
229
+ const cli = pkgAt(
230
+ path.join(modules, '@astryxdesign', 'cli'),
231
+ {name: '@astryxdesign/cli', version: '0.8.0'},
232
+ null,
233
+ );
234
+ pkgAt(path.join(modules, '@acme', 'meta-kit'), {
235
+ name: '@acme/meta-kit',
236
+ version: '3.0.0',
237
+ });
238
+ // Installed but not declared: not part of the set.
239
+ pkgAt(path.join(modules, 'stray-kit'), {name: 'stray-kit'});
240
+ expect(
241
+ scanComputerIntegrations({cliRoot: cli, release: '0.8.0'}).map(
242
+ c => c.name,
243
+ ),
244
+ ).toEqual(['@acme/meta-kit']);
245
+ });
246
+
247
+ it('lists nothing for a copy with no install root', () => {
248
+ expect(
249
+ scanComputerIntegrations({
250
+ cliRoot: tempDir('checkout'),
251
+ release: '0.8.0',
252
+ }),
253
+ ).toEqual([]);
254
+ });
255
+ });
256
+
257
+ describe('peerProblem', () => {
258
+ it('checks the Core and CLI peer ranges against the answering release', () => {
259
+ expect(peerProblem({}, '0.8.0')).toBeNull();
260
+ expect(
261
+ peerProblem(
262
+ {peerDependencies: {'@astryxdesign/core': '^0.8.0'}},
263
+ '0.8.2',
264
+ ),
265
+ ).toBeNull();
266
+ expect(
267
+ peerProblem(
268
+ {peerDependencies: {'@astryxdesign/cli': '>=0.9.0'}},
269
+ '0.8.2',
270
+ ),
271
+ ).toBe(
272
+ "it needs @astryxdesign/cli >=0.9.0, and this computer's CLI is 0.8.2",
273
+ );
274
+ expect(
275
+ peerProblem({peerDependencies: {react: '>=99'}}, '0.8.2'),
276
+ ).toBeNull();
277
+ });
278
+ });
279
+
280
+ describe('loadComputerIntegrations', () => {
281
+ it("loads the compatible ones, marked as the computer's, and leaves out excluded and incompatible ones", async () => {
282
+ const {cli} = npmGlobal();
283
+ const found = scanComputerIntegrations({cliRoot: cli, release: '0.8.0'});
284
+ const loaded = await loadComputerIntegrations(found);
285
+ expect(loaded.map(c => [c.spec, c.integration?.__computer])).toEqual([
286
+ ['@acme/widgets', true],
287
+ ]);
288
+ expect(
289
+ await loadComputerIntegrations(found, {exclude: ['@acme/widgets']}),
290
+ ).toEqual([]);
291
+ });
292
+ });
@@ -95,6 +95,17 @@ export function compareIdentities(local: ContributionIdentities, packed: Contrib
95
95
  * @returns {string[]} unregistered PascalCase names
96
96
  */
97
97
  export function findSourceOnlyCandidates(componentsRoot: string | undefined, registeredNames: string[]): string[];
98
+ /**
99
+ * Find `.tsx` files directly in a templates root that have no same-stem
100
+ * template metadata, so template discovery never lists them. Like the
101
+ * component check, it scans only direct children of the root: a file in a
102
+ * subfolder may be a helper a template imports. A file with a dotted stem (a
103
+ * test, story, or similar companion) is never a candidate.
104
+ *
105
+ * @param {string|undefined} templatesRoot absolute path
106
+ * @returns {string[]} basenames of the unpaired sources, sorted
107
+ */
108
+ export function findTemplateSourceOnlyCandidates(templatesRoot: string | undefined): string[];
98
109
  export type FileInventoryRoot = {
99
110
  kind: "themes" | "components" | "templates" | "codemods" | "docs";
100
111
  /**
@@ -248,11 +248,16 @@ export async function collectIdentities(loaded) {
248
248
  if (loaded.components && fs.existsSync(loaded.components)) {
249
249
  try {
250
250
  const records = discoverIntegrationComponents(loaded) ?? [];
251
+ // A family doc backs several records: load it, and report it, once.
252
+ const loads = new Map();
251
253
  for (const record of records) {
254
+ const first = !loads.has(record.docPath);
255
+ if (first) loads.set(record.docPath, loadComponentDoc(record.docPath));
252
256
  try {
253
- await loadComponentDoc(record.docPath);
257
+ await loads.get(record.docPath);
254
258
  identities.components.push(record.name);
255
259
  } catch (err) {
260
+ if (!first) continue;
256
261
  errors.push({
257
262
  kind: 'components',
258
263
  message: `${path.relative(loaded.components, record.docPath)}: ${
@@ -314,8 +319,11 @@ export async function collectIdentities(loaded) {
314
319
 
315
320
  if (loaded.docs && fs.existsSync(loaded.docs)) {
316
321
  try {
317
- const {records, guides, errors: docErrors} =
318
- await discoverIntegrationDocs(loaded);
322
+ const {
323
+ records,
324
+ guides,
325
+ errors: docErrors,
326
+ } = await discoverIntegrationDocs(loaded);
319
327
  for (const docError of docErrors) {
320
328
  errors.push({kind: 'docs', message: docError.message});
321
329
  }
@@ -498,3 +506,48 @@ export function findSourceOnlyCandidates(componentsRoot, registeredNames) {
498
506
  }
499
507
  return candidates.sort();
500
508
  }
509
+
510
+ /**
511
+ * Metadata suffixes that pair a template source with its spec: the canonical
512
+ * `.doc.*` family and the released `.template.*` compatibility family.
513
+ */
514
+ const TEMPLATE_DOC_SUFFIXES = [
515
+ '.doc.ts',
516
+ '.doc.mjs',
517
+ '.doc.js',
518
+ '.template.ts',
519
+ '.template.mjs',
520
+ '.template.js',
521
+ ];
522
+
523
+ /** A `.tsx` source and its stem, which may hold a dot (`landing.page`). */
524
+ const TSX_SOURCE = /^(.+)\.tsx$/u;
525
+
526
+ /** A test, spec or story stem: never a template. */
527
+ const NOT_A_TEMPLATE = /\.(?:test|spec|stories)$/u;
528
+
529
+ /**
530
+ * Find `.tsx` files directly in a templates root that have no same-stem
531
+ * template metadata, so template discovery never lists them. Like the
532
+ * component check, it scans only direct children of the root: a file in a
533
+ * subfolder may be a helper a template imports. A file with a dotted stem (a
534
+ * test, story, or similar companion) is never a candidate.
535
+ *
536
+ * @param {string|undefined} templatesRoot absolute path
537
+ * @returns {string[]} basenames of the unpaired sources, sorted
538
+ */
539
+ export function findTemplateSourceOnlyCandidates(templatesRoot) {
540
+ if (!templatesRoot || !fs.existsSync(templatesRoot)) return [];
541
+ /** @type {string[]} */
542
+ const candidates = [];
543
+ for (const entry of fs.readdirSync(templatesRoot, {withFileTypes: true})) {
544
+ if (!entry.isFile() && !entry.isSymbolicLink()) continue;
545
+ const match = entry.name.match(TSX_SOURCE);
546
+ if (!match || NOT_A_TEMPLATE.test(match[1])) continue;
547
+ const paired = TEMPLATE_DOC_SUFFIXES.some(suffix =>
548
+ fs.existsSync(path.join(templatesRoot, `${match[1]}${suffix}`)),
549
+ );
550
+ if (!paired) candidates.push(entry.name);
551
+ }
552
+ return candidates.sort();
553
+ }
@@ -21,6 +21,7 @@
21
21
  */
22
22
 
23
23
  import {validateLoadedIntegration} from './validate-contributions.mjs';
24
+ import {formatCliCommand} from '../env/package-manager.mjs';
24
25
 
25
26
  /**
26
27
  * For each configured (already-loaded) integration, compute its issues using
@@ -81,8 +82,8 @@ export async function warnOnIntegrationIssues(source, {json = false} = {}) {
81
82
 
82
83
  for (const [pkg, count] of issueCounts) {
83
84
  const command = fromProject
84
- ? 'astryx doctor'
85
- : `astryx doctor integration validate ${pkg}`;
85
+ ? formatCliCommand('doctor')
86
+ : formatCliCommand(`doctor integration validate ${pkg}`);
86
87
  console.error(
87
88
  `Warning: ${pkg} has ${count} integration issue(s). Run: ${command}`,
88
89
  );
@@ -61,9 +61,8 @@ describe('warnOnIntegrationIssues', () => {
61
61
  await warnOnIntegrationIssues([integration], {json: false});
62
62
 
63
63
  expect(errLines).toHaveLength(1);
64
- expect(errLines[0]).toBe(
65
- 'Warning: @acme/widgets has 1 integration issue(s). ' +
66
- 'Run: astryx doctor integration validate @acme/widgets',
64
+ expect(errLines[0]).toMatch(
65
+ /^Warning: @acme\/widgets has 1 integration issue\(s\)\. Run: .+ doctor integration validate @acme\/widgets$/,
67
66
  );
68
67
  });
69
68
 
@@ -81,9 +80,10 @@ describe('warnOnIntegrationIssues', () => {
81
80
 
82
81
  await warnOnIntegrationIssues(project, {json: false});
83
82
 
84
- expect(errLines).toEqual([
85
- 'Warning: @acme/later has 1 integration issue(s). Run: astryx doctor',
86
- ]);
83
+ expect(errLines).toHaveLength(1);
84
+ expect(errLines[0]).toMatch(
85
+ /^Warning: @acme\/later has 1 integration issue\(s\)\. Run: .+ doctor$/,
86
+ );
87
87
  });
88
88
 
89
89
  it('emits nothing in --json mode (keeps stdout/JSON clean)', async () => {
@@ -103,9 +103,8 @@ describe('warnOnIntegrationIssues', () => {
103
103
  await warnOnIntegrationIssues([integration], {json: false});
104
104
 
105
105
  expect(errLines).toHaveLength(1);
106
- expect(errLines[0]).toBe(
107
- 'Warning: @acme/stale has 1 integration issue(s). ' +
108
- 'Run: astryx doctor integration validate @acme/stale',
106
+ expect(errLines[0]).toMatch(
107
+ /^Warning: @acme\/stale has 1 integration issue\(s\)\. Run: .+ doctor integration validate @acme\/stale$/,
109
108
  );
110
109
  });
111
110
 
@@ -45,7 +45,7 @@ export function findManifestPaths(dir: string): string[];
45
45
  * @param {string} file absolute manifest path
46
46
  * @param {string} [label] used in error messages
47
47
  * @param {{fresh?: boolean}} [options]
48
- * @returns {Promise<{manifest: import('../../authoring/integration/type').AstryxIntegration, unknownKeys: string[], debug?: import('../../authoring/debug/type').DebugEventHandler, gapReport?: import('../../authoring/gap-report/type').GapReportHandler, gapReportError?: string, discover?: import('../../authoring/discover/type').DiscoverSource, discoverError?: string, agentDocsError?: string}>}
48
+ * @returns {Promise<{manifest: import('../../authoring/integration/type').AstryxIntegration, unknownKeys: string[], debug?: import('../../authoring/debug/type').DebugEventHandler, gapReport?: import('../../authoring/gap-report/type').GapReportHandler, gapReportError?: string, discover?: import('../../authoring/discover/type').DiscoverSource, discoverError?: string, agentDocsError?: string, cliCommandError?: string}>}
49
49
  */
50
50
  export function loadManifest(file: string, label?: string, { fresh }?: {
51
51
  fresh?: boolean;
@@ -58,6 +58,7 @@ export function loadManifest(file: string, label?: string, { fresh }?: {
58
58
  discover?: import("../../authoring/discover/type").DiscoverSource;
59
59
  discoverError?: string;
60
60
  agentDocsError?: string;
61
+ cliCommandError?: string;
61
62
  }>;
62
63
  /**
63
64
  * Load and validate a manifest module's default export against the integration
@@ -126,6 +127,10 @@ export function loadIntegrations(specs?: string[], { cwd, fresh, resolveProvider
126
127
  * @property {string} [themes]
127
128
  * @property {string} [issuesUrl]
128
129
  * @property {{append?: readonly string[]}} [agentDocs]
130
+ * @property {string} [cliCommand] the command this package's users run the
131
+ * CLI with; read by the picker in foundation/env (see cli-command.mjs)
132
+ * @property {string} [__cliCommandError] why a declared `cliCommand` was
133
+ * ignored; the rest of the manifest still contributes
129
134
  * @property {string} [__agentDocsError] contribution-specific validation
130
135
  * failure; other manifest contributions remain available
131
136
  * @property {string} __spec
@@ -145,6 +150,9 @@ export function loadIntegrations(specs?: string[], { cwd, fresh, resolveProvider
145
150
  * @property {boolean} [__autolinked] loaded because the project declares the
146
151
  * package as a dependency and it ships a manifest, with no astryx.config
147
152
  * entry naming it — see foundation/integrations/autolink.mjs
153
+ * @property {boolean} [__computer] loaded because it is installed beside the
154
+ * running CLI and the folder is outside a project (spec AST-081 FR12) — see
155
+ * foundation/integrations/computer-integrations.mjs
148
156
  * @property {boolean} [__local] loaded from the package containing the current
149
157
  * working directory, so an author sees the exact bytes they are editing
150
158
  * @property {string} [__dependencyField] for an autolinked integration, the
@@ -186,6 +194,16 @@ export type LoadedIntegration = {
186
194
  agentDocs?: {
187
195
  append?: readonly string[];
188
196
  } | undefined;
197
+ /**
198
+ * the command this package's users run the
199
+ * CLI with; read by the picker in foundation/env (see cli-command.mjs)
200
+ */
201
+ cliCommand?: string | undefined;
202
+ /**
203
+ * why a declared `cliCommand` was
204
+ * ignored; the rest of the manifest still contributes
205
+ */
206
+ __cliCommandError?: string | undefined;
189
207
  /**
190
208
  * contribution-specific validation
191
209
  * failure; other manifest contributions remain available
@@ -226,6 +244,12 @@ export type LoadedIntegration = {
226
244
  * entry naming it — see foundation/integrations/autolink.mjs
227
245
  */
228
246
  __autolinked?: boolean | undefined;
247
+ /**
248
+ * loaded because it is installed beside the
249
+ * running CLI and the folder is outside a project (spec AST-081 FR12) — see
250
+ * foundation/integrations/computer-integrations.mjs
251
+ */
252
+ __computer?: boolean | undefined;
229
253
  /**
230
254
  * loaded from the package containing the current
231
255
  * working directory, so an author sees the exact bytes they are editing
@@ -6,7 +6,8 @@
6
6
  * Integrations are PACKAGE NAMES listed in astryx.config.{ts,mjs,js}. Each
7
7
  * package declares a single conventional root manifest sibling to its
8
8
  * package.json — astryx.integration.{ts,mjs,js} — which contributes
9
- * components/templates/codemods/docs/themes roots and an optional issuesUrl.
9
+ * components/templates/codemods/docs/themes roots, an optional issuesUrl, and
10
+ * an optional cliCommand.
10
11
  * Stable provider identity and package metadata come from package.json, not the
11
12
  * manifest.
12
13
  *
@@ -24,6 +25,7 @@ import {normalizeProviderId} from '../identity/provider-identity.mjs';
24
25
  // the loader can isolate an invalid optional contribution from valid roots.
25
26
  import {
26
27
  parseAgentDocsField,
28
+ parseCliCommandField,
27
29
  parseIntegrationBase,
28
30
  unknownIntegrationKeys,
29
31
  } from '../../authoring/integration/schema.mjs';
@@ -51,6 +53,10 @@ import {resolveProviders} from './provider-resolution.mjs';
51
53
  * @property {string} [themes]
52
54
  * @property {string} [issuesUrl]
53
55
  * @property {{append?: readonly string[]}} [agentDocs]
56
+ * @property {string} [cliCommand] the command this package's users run the
57
+ * CLI with; read by the picker in foundation/env (see cli-command.mjs)
58
+ * @property {string} [__cliCommandError] why a declared `cliCommand` was
59
+ * ignored; the rest of the manifest still contributes
54
60
  * @property {string} [__agentDocsError] contribution-specific validation
55
61
  * failure; other manifest contributions remain available
56
62
  * @property {string} __spec
@@ -70,6 +76,9 @@ import {resolveProviders} from './provider-resolution.mjs';
70
76
  * @property {boolean} [__autolinked] loaded because the project declares the
71
77
  * package as a dependency and it ships a manifest, with no astryx.config
72
78
  * entry naming it — see foundation/integrations/autolink.mjs
79
+ * @property {boolean} [__computer] loaded because it is installed beside the
80
+ * running CLI and the folder is outside a project (spec AST-081 FR12) — see
81
+ * foundation/integrations/computer-integrations.mjs
73
82
  * @property {boolean} [__local] loaded from the package containing the current
74
83
  * working directory, so an author sees the exact bytes they are editing
75
84
  * @property {string} [__dependencyField] for an autolinked integration, the
@@ -204,7 +213,7 @@ function parseDiscoverSourceExport(value, label) {
204
213
  * @param {string} file absolute manifest path
205
214
  * @param {string} [label] used in error messages
206
215
  * @param {{fresh?: boolean}} [options]
207
- * @returns {Promise<{manifest: import('../../authoring/integration/type').AstryxIntegration, unknownKeys: string[], debug?: import('../../authoring/debug/type').DebugEventHandler, gapReport?: import('../../authoring/gap-report/type').GapReportHandler, gapReportError?: string, discover?: import('../../authoring/discover/type').DiscoverSource, discoverError?: string, agentDocsError?: string}>}
216
+ * @returns {Promise<{manifest: import('../../authoring/integration/type').AstryxIntegration, unknownKeys: string[], debug?: import('../../authoring/debug/type').DebugEventHandler, gapReport?: import('../../authoring/gap-report/type').GapReportHandler, gapReportError?: string, discover?: import('../../authoring/discover/type').DiscoverSource, discoverError?: string, agentDocsError?: string, cliCommandError?: string}>}
208
217
  */
209
218
  export async function loadManifest(
210
219
  file,
@@ -239,8 +248,31 @@ export async function loadManifest(
239
248
  }
240
249
  }
241
250
 
251
+ /** @type {string | undefined} */
252
+ let cliCommand;
253
+ /** @type {string | undefined} */
254
+ let cliCommandError;
255
+ const rawCliCommand =
256
+ raw != null && typeof raw === 'object' && !Array.isArray(raw)
257
+ ? /** @type {{cliCommand?: unknown}} */ (raw).cliCommand
258
+ : undefined;
259
+ if (rawCliCommand !== undefined) {
260
+ try {
261
+ cliCommand = parseCliCommandField(
262
+ rawCliCommand,
263
+ `${label} field "cliCommand"`,
264
+ );
265
+ } catch (err) {
266
+ cliCommandError = err instanceof Error ? err.message : String(err);
267
+ }
268
+ }
269
+
242
270
  return {
243
- manifest: agentDocs == null ? baseManifest : {...baseManifest, agentDocs},
271
+ manifest: {
272
+ ...baseManifest,
273
+ ...(agentDocs == null ? {} : {agentDocs}),
274
+ ...(cliCommand == null ? {} : {cliCommand}),
275
+ },
244
276
  unknownKeys: unknownIntegrationKeys(raw),
245
277
  debug:
246
278
  typeof mod?.debug === 'function'
@@ -253,6 +285,7 @@ export async function loadManifest(
253
285
  discover: discover.source,
254
286
  discoverError: discover.error,
255
287
  agentDocsError,
288
+ cliCommandError,
256
289
  };
257
290
  }
258
291
 
@@ -375,6 +408,8 @@ export async function loadLocalIntegration(packageDir, {fresh = false} = {}) {
375
408
  let discoverError;
376
409
  /** @type {string | undefined} */
377
410
  let agentDocsError;
411
+ /** @type {string | undefined} */
412
+ let cliCommandError;
378
413
  try {
379
414
  ({
380
415
  manifest,
@@ -385,6 +420,7 @@ export async function loadLocalIntegration(packageDir, {fresh = false} = {}) {
385
420
  discover: discoverSource,
386
421
  discoverError,
387
422
  agentDocsError,
423
+ cliCommandError,
388
424
  } = await loadManifest(manifestFile, `Integration ${spec}`, {fresh}));
389
425
  } catch (err) {
390
426
  return {
@@ -422,6 +458,8 @@ export async function loadLocalIntegration(packageDir, {fresh = false} = {}) {
422
458
  themes: resolveRoot(manifest.themes),
423
459
  issuesUrl: manifest.issuesUrl,
424
460
  agentDocs: manifest.agentDocs,
461
+ cliCommand: manifest.cliCommand,
462
+ __cliCommandError: cliCommandError,
425
463
  __agentDocsError: agentDocsError,
426
464
  __unknownKeys: unknownKeys,
427
465
  __debug: debugHandler,
@@ -486,6 +524,8 @@ export async function loadIntegrations(
486
524
  let discoverError;
487
525
  /** @type {string | undefined} */
488
526
  let agentDocsError;
527
+ /** @type {string | undefined} */
528
+ let cliCommandError;
489
529
  try {
490
530
  ({
491
531
  manifest,
@@ -496,6 +536,7 @@ export async function loadIntegrations(
496
536
  discover: discoverSource,
497
537
  discoverError,
498
538
  agentDocsError,
539
+ cliCommandError,
499
540
  } = await loadManifest(manifestFile, `Integration ${spec}`, {fresh}));
500
541
  } catch (err) {
501
542
  // A manifest that throws on import or fails schema validation must NOT take
@@ -540,6 +581,8 @@ export async function loadIntegrations(
540
581
  themes: resolveRoot(manifest.themes),
541
582
  issuesUrl: manifest.issuesUrl,
542
583
  agentDocs: manifest.agentDocs,
584
+ cliCommand: manifest.cliCommand,
585
+ __cliCommandError: cliCommandError,
543
586
  __agentDocsError: agentDocsError,
544
587
  __unknownKeys: unknownKeys,
545
588
  __debug: debugHandler,
@@ -15,4 +15,10 @@ export interface AstryxIntegrationIssue {
15
15
  code: string;
16
16
  severity: 'warning' | 'error';
17
17
  message: string;
18
+ /** What to change, ready to paste, when the check knows it. */
19
+ fix?: string;
20
+ /** The file the issue is in, relative to the package. */
21
+ file?: string;
22
+ /** The 1-based line in `file`, when the check can locate it. */
23
+ line?: number;
18
24
  }
@@ -38,6 +38,8 @@ export function validateLoadedIntegration(loaded: LoadedIntegration): Promise<Is
38
38
  */
39
39
  /** Stable issue code for an invalid optional agent-doc contribution. */
40
40
  export const INVALID_AGENT_DOCS: "invalid_agent_docs";
41
+ /** Stable issue code for a `cliCommand` that was ignored as invalid. */
42
+ export const INVALID_CLI_COMMAND: "invalid_cli_command";
41
43
  /** Stable issue code for a package whose provider ID is already claimed. */
42
44
  export const DUPLICATE_PROVIDER: "duplicate_provider";
43
45
  export type Issue = import("./issue").AstryxIntegrationIssue;