@astryxdesign/cli 0.7.2-canary.fdd384f → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (450) hide show
  1. package/CHANGELOG.md +119 -0
  2. package/README.md +169 -149
  3. package/api/build/_adapter.d.mts +8 -0
  4. package/api/build/_adapter.mjs +12 -1
  5. package/api/build/build.doc.mjs +4 -0
  6. package/api/build/kit/kit.mjs +3 -11
  7. package/api/component/_adapter.d.mts +31 -0
  8. package/api/component/_adapter.mjs +119 -11
  9. package/api/component/component.doc.mjs +6 -1
  10. package/api/component/component.mjs +60 -10
  11. package/api/component/doc-links.test.mjs +386 -0
  12. package/api/discover/_adapter.d.mts +132 -12
  13. package/api/discover/_adapter.mjs +488 -27
  14. package/api/discover/_adapter.test.mjs +599 -17
  15. package/api/discover/_catalog-view.d.mts +16 -5
  16. package/api/discover/_catalog-view.mjs +32 -8
  17. package/api/discover/detail/doc/doc.mjs +9 -1
  18. package/api/discover/discover.d.mts +11 -0
  19. package/api/discover/discover.doc.mjs +9 -7
  20. package/api/discover/discover.mjs +121 -19
  21. package/api/discover/discover.type.d.mts +20 -7
  22. package/api/discover/discover.type.mjs +17 -7
  23. package/api/discover/list/list.d.mts +4 -3
  24. package/api/discover/list/list.mjs +17 -10
  25. package/api/docs/_adapter.d.mts +63 -0
  26. package/api/docs/_adapter.mjs +312 -18
  27. package/api/docs/docs.doc.mjs +2 -1
  28. package/api/docs/docs.mjs +2 -1
  29. package/api/docs/docs.test.mjs +5 -2
  30. package/api/docs/integration-tree.test.mjs +22 -0
  31. package/api/docs/list/list.mjs +12 -2
  32. package/api/docs/reference-blocks.test.mjs +1 -3
  33. package/api/doctor/_adapter.d.mts +108 -0
  34. package/api/doctor/_adapter.mjs +223 -4
  35. package/api/doctor/doctor.d.mts +199 -0
  36. package/api/doctor/doctor.doc.mjs +3 -2
  37. package/api/doctor/doctor.mjs +530 -34
  38. package/api/doctor/doctor.test.mjs +180 -14
  39. package/api/doctor/release-checks.test.mjs +156 -0
  40. package/api/doctor/theme-checks.mjs +16 -3
  41. package/api/doctor/theme-checks.test.mjs +223 -7
  42. package/api/error.d.mts +10 -0
  43. package/api/error.mjs +25 -0
  44. package/api/gap-report/gap-report.d.mts +3 -0
  45. package/api/gap-report/gap-report.doc.mjs +11 -3
  46. package/api/gap-report/gap-report.mjs +152 -48
  47. package/api/gap-report/gap-report.test.mjs +233 -0
  48. package/api/gap-report/gap-report.type.d.mts +41 -0
  49. package/api/gap-report/gap-report.type.mjs +18 -0
  50. package/api/hook/_adapter.mjs +3 -11
  51. package/api/hook/hook.doc.mjs +4 -0
  52. package/api/hook/list/list.d.mts +1 -1
  53. package/api/init/_adapter.d.mts +22 -0
  54. package/api/init/_adapter.mjs +59 -0
  55. package/api/init/init.d.mts +7 -5
  56. package/api/init/init.doc.mjs +28 -7
  57. package/api/init/init.mjs +28 -4
  58. package/api/init/init.type.d.mts +48 -3
  59. package/api/init/init.type.mjs +37 -3
  60. package/api/init/run/run.d.mts +1 -1
  61. package/api/init/run/run.mjs +15 -4
  62. package/api/init/user/user.d.mts +14 -0
  63. package/api/init/user/user.mjs +87 -0
  64. package/api/init/user/user.test.mjs +232 -0
  65. package/api/integration/_adapter.d.mts +33 -0
  66. package/api/integration/_adapter.mjs +58 -0
  67. package/api/integration/authoring-checks.mjs +100 -5
  68. package/api/integration/authoring-checks.test.mjs +769 -0
  69. package/api/integration/authoring-checks.type.d.mts +134 -0
  70. package/api/integration/authoring-checks.type.mjs +60 -1
  71. package/api/integration/integrationCheck.doc.mjs +1 -1
  72. package/api/integration/integrationComponentConflicts.doc.mjs +11 -3
  73. package/api/integration/integrationDocConflicts.doc.mjs +22 -5
  74. package/api/integration/pack-check.mjs +303 -13
  75. package/api/integration/pack-check.test.mjs +371 -10
  76. package/api/integration/validate-integration-fixes.test.mjs +67 -0
  77. package/api/integration/validate-integration.d.mts +8 -0
  78. package/api/integration/validate-integration.mjs +55 -9
  79. package/api/integration/validate-integration.test.mjs +77 -8
  80. package/api/integration/validate-integration.type.d.mts +8 -0
  81. package/api/integration/validate-integration.type.mjs +3 -1
  82. package/api/integration/validate-unread-theme-folders.test.mjs +3 -2
  83. package/api/integration/validateIntegration.doc.mjs +4 -2
  84. package/api/search/_adapter.d.mts +11 -0
  85. package/api/search/_adapter.mjs +25 -0
  86. package/api/search/search.d.mts +73 -13
  87. package/api/search/search.doc.mjs +8 -3
  88. package/api/search/search.domain-priority.test.mjs +108 -18
  89. package/api/search/search.mjs +541 -52
  90. package/api/search/search.test.mjs +143 -1
  91. package/api/search/search.type.d.mts +14 -1
  92. package/api/search/search.type.mjs +6 -1
  93. package/api/swizzle/_adapter.mjs +2 -2
  94. package/api/swizzle/copy/copy.mjs +1 -1
  95. package/api/template/template-integration.test.mjs +1 -1
  96. package/api/theme/_adapter.d.mts +16 -0
  97. package/api/theme/_adapter.mjs +32 -5
  98. package/api/theme/build/build.d.mts +2 -1
  99. package/api/theme/build/build.mjs +70 -14
  100. package/api/theme/build/build.packed-old-core.test.mjs +20 -3
  101. package/api/theme/build/font-warning.mjs +5 -3
  102. package/api/theme/build/font-warning.test.mjs +2 -1
  103. package/api/theme/remove/remove.test.mjs +2 -2
  104. package/api/theme/targets/targets.mjs +3 -10
  105. package/api/theme/themeTargets.doc.mjs +4 -0
  106. package/api/theme/use/use.test.mjs +1 -1
  107. package/api/upgrade/_adapter.d.mts +40 -2
  108. package/api/upgrade/_adapter.mjs +118 -5
  109. package/api/upgrade/run/cli-behind-core.test.mjs +438 -0
  110. package/api/upgrade/run/run.mjs +35 -2
  111. package/api/upgrade/status/status.d.mts +21 -5
  112. package/api/upgrade/status/status.mjs +45 -8
  113. package/api/upgrade/upgrade.doc.mjs +6 -3
  114. package/api/upgrade/upgrade.mjs +5 -1
  115. package/api/upgrade/upgrade.type.d.mts +23 -0
  116. package/api/upgrade/upgrade.type.mjs +16 -1
  117. package/assets/codemods/__tests__/registry.test.mjs +1 -0
  118. package/assets/codemods/registry.mjs +1 -0
  119. package/assets/codemods/runner.mjs +7 -1
  120. package/assets/codemods/transforms/next/index.mjs +1 -11
  121. package/assets/codemods/transforms/v0.7.2/index.mjs +20 -0
  122. package/assets/discover/official-packages.json +367 -0
  123. package/assets/docs/author-a-theme.doc.mjs +2 -2
  124. package/assets/docs/browser-support.doc.mjs +1 -1
  125. package/assets/docs/getting-started.doc.mjs +23 -3
  126. package/assets/docs/styling-overview.doc.mjs +1 -1
  127. package/assets/docs/theme.doc.mjs +1 -1
  128. package/assets/docs/tree/add-a-component.doc.mjs +12 -2
  129. package/assets/docs/tree/add-a-theme.doc.mjs +13 -3
  130. package/assets/docs/tree/add-a-topic.doc.mjs +54 -9
  131. package/assets/docs/tree/agent-guidance.doc.mjs +19 -5
  132. package/assets/docs/tree/block-template.doc.mjs +6 -18
  133. package/assets/docs/tree/build-the-template.doc.mjs +1 -1
  134. package/assets/docs/tree/building-blocks.doc.mjs +4 -0
  135. package/assets/docs/tree/check-the-props.doc.mjs +8 -3
  136. package/assets/docs/tree/check-your-docs.doc.mjs +30 -13
  137. package/assets/docs/tree/checks.doc.mjs +17 -17
  138. package/assets/docs/tree/ci-and-containers.doc.mjs +88 -0
  139. package/assets/docs/tree/cli-command.doc.mjs +142 -0
  140. package/assets/docs/tree/cli.doc.mjs +6 -0
  141. package/assets/docs/tree/codemods.doc.mjs +17 -7
  142. package/assets/docs/tree/commands.doc.mjs +7 -1
  143. package/assets/docs/tree/component-family.doc.mjs +3 -5
  144. package/assets/docs/tree/component-imports.doc.mjs +13 -3
  145. package/assets/docs/tree/components.doc.mjs +7 -1
  146. package/assets/docs/tree/computer-agent-guide.doc.mjs +160 -0
  147. package/assets/docs/tree/computer-integrations.doc.mjs +93 -0
  148. package/assets/docs/tree/configuration.doc.mjs +7 -3
  149. package/assets/docs/tree/debug-and-gap-reports.doc.mjs +41 -22
  150. package/assets/docs/tree/define-the-theme.doc.mjs +6 -5
  151. package/assets/docs/tree/describe-the-component.doc.mjs +2 -2
  152. package/assets/docs/tree/docs.doc.mjs +9 -3
  153. package/assets/docs/tree/document-the-template.doc.mjs +1 -1
  154. package/assets/docs/tree/document-the-theme.doc.mjs +3 -3
  155. package/assets/docs/tree/export-template-assets.doc.mjs +1 -1
  156. package/assets/docs/tree/extend-or-replace.doc.mjs +19 -9
  157. package/assets/docs/tree/fonts-and-assets.doc.mjs +2 -2
  158. package/assets/docs/tree/generate-a-palette.doc.mjs +7 -5
  159. package/assets/docs/tree/global-integrations.doc.mjs +124 -0
  160. package/assets/docs/tree/global.doc.mjs +131 -0
  161. package/assets/docs/tree/grade-template-with-agent.doc.mjs +5 -5
  162. package/assets/docs/tree/install-once.doc.mjs +105 -0
  163. package/assets/docs/tree/integrations.doc.mjs +13 -1
  164. package/assets/docs/tree/layout-spacing.doc.mjs +2 -3
  165. package/assets/docs/tree/layout.doc.mjs +6 -6
  166. package/assets/docs/tree/links.doc.mjs +6 -5
  167. package/assets/docs/tree/migration-setup.doc.mjs +3 -2
  168. package/assets/docs/tree/migration-verification.doc.mjs +0 -1
  169. package/assets/docs/tree/migration.doc.mjs +0 -1
  170. package/assets/docs/tree/monorepos.doc.mjs +84 -0
  171. package/assets/docs/tree/package-and-test.doc.mjs +1 -1
  172. package/assets/docs/tree/page-template.doc.mjs +3 -7
  173. package/assets/docs/tree/problems.doc.mjs +205 -0
  174. package/assets/docs/tree/project-global-or-both.doc.mjs +131 -0
  175. package/assets/docs/tree/publishing.doc.mjs +6 -6
  176. package/assets/docs/tree/quick-start.doc.mjs +52 -26
  177. package/assets/docs/tree/reference.doc.mjs +192 -0
  178. package/assets/docs/tree/replace-a-core-component.doc.mjs +15 -7
  179. package/assets/docs/tree/replace-a-core-template.doc.mjs +9 -11
  180. package/assets/docs/tree/scaffold.doc.mjs +1 -1
  181. package/assets/docs/tree/sections-and-placement.doc.mjs +9 -11
  182. package/assets/docs/tree/see-it-in-an-app.doc.mjs +17 -5
  183. package/assets/docs/tree/short-and-findable.doc.mjs +9 -8
  184. package/assets/docs/tree/single-component.doc.mjs +14 -6
  185. package/assets/docs/tree/start-a-template.doc.mjs +18 -8
  186. package/assets/docs/tree/subcomponent.doc.mjs +6 -6
  187. package/assets/docs/tree/template-assets.doc.mjs +2 -2
  188. package/assets/docs/tree/template-doc-overview.doc.mjs +6 -17
  189. package/assets/docs/tree/template-fonts.doc.mjs +1 -1
  190. package/assets/docs/tree/template-icons.doc.mjs +1 -1
  191. package/assets/docs/tree/template-images-media.doc.mjs +1 -1
  192. package/assets/docs/tree/template-styles.doc.mjs +2 -2
  193. package/assets/docs/tree/templates.doc.mjs +7 -1
  194. package/assets/docs/tree/test-in-an-app.doc.mjs +7 -7
  195. package/assets/docs/tree/test-template-in-app.doc.mjs +5 -5
  196. package/assets/docs/tree/themes.doc.mjs +4 -0
  197. package/assets/docs/tree/tokens-and-setup.doc.mjs +19 -15
  198. package/assets/docs/tree/troubleshooting.doc.mjs +22 -14
  199. package/assets/docs/tree/uninstall.doc.mjs +109 -0
  200. package/assets/docs/tree/updates.doc.mjs +92 -0
  201. package/assets/docs/tree/upgrading.doc.mjs +6 -6
  202. package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +14 -2
  203. package/assets/docs/tree/verify-packed-template.doc.mjs +3 -3
  204. package/assets/docs/tree/versioning.doc.mjs +9 -9
  205. package/assets/docs/tree/who-answers.doc.mjs +119 -0
  206. package/assets/docs/tree/write-the-template-file.doc.mjs +5 -5
  207. package/assets/docs/working-with-ai.doc.mjs +23 -9
  208. package/authoring/codemod/codemod.doc.mjs +7 -3
  209. package/authoring/config/config.doc.mjs +9 -6
  210. package/authoring/config/type.ts +4 -2
  211. package/authoring/debug/parse.d.mts +5 -5
  212. package/authoring/discover/discover.doc.mjs +7 -1
  213. package/authoring/doctypes/_schema.d.mts +8 -3
  214. package/authoring/doctypes/_schema.mjs +1 -0
  215. package/authoring/doctypes/base/graph-fields.doc.mjs +6 -4
  216. package/authoring/doctypes/command/command.doc.mjs +20 -12
  217. package/authoring/doctypes/command/type.ts +14 -0
  218. package/authoring/doctypes/component/component.doc.mjs +29 -26
  219. package/authoring/doctypes/component/type.ts +15 -5
  220. package/authoring/doctypes/enum/enum.doc.mjs +7 -4
  221. package/authoring/doctypes/function/function.doc.mjs +9 -8
  222. package/authoring/doctypes/hook/hook.doc.mjs +5 -4
  223. package/authoring/doctypes/hook/type.ts +4 -0
  224. package/authoring/doctypes/namespace/namespace.doc.mjs +7 -6
  225. package/authoring/doctypes/reference/reference.doc.mjs +12 -11
  226. package/authoring/doctypes/schema/schema.doc.mjs +9 -9
  227. package/authoring/doctypes/template/template.doc.mjs +2 -2
  228. package/authoring/doctypes/theme/theme.doc.mjs +2 -2
  229. package/authoring/gap-report/gap-report.doc.mjs +2 -2
  230. package/authoring/identity/identity.doc.mjs +3 -3
  231. package/authoring/index.d.ts +1 -0
  232. package/authoring/integration/integration.doc.mjs +22 -5
  233. package/authoring/integration/parse.test.mjs +11 -0
  234. package/authoring/integration/schema.d.mts +14 -3
  235. package/authoring/integration/schema.mjs +33 -2
  236. package/authoring/integration/type.ts +8 -0
  237. package/clients/cli/bin/astryx.mjs +20 -3
  238. package/clients/cli/cli-command.test.mjs +166 -0
  239. package/clients/cli/commands/blog.doc.mjs +1 -0
  240. package/clients/cli/commands/build-theme.batch-report.test.mjs +14 -2
  241. package/clients/cli/commands/build-theme.font-warning.test.mjs +3 -3
  242. package/clients/cli/commands/build-theme.mjs +25 -12
  243. package/clients/cli/commands/build.doc.mjs +1 -0
  244. package/clients/cli/commands/build.exit-codes-doc.test.mjs +4 -0
  245. package/clients/cli/commands/cli-postinstall.test.mjs +49 -1
  246. package/clients/cli/commands/component.doc.mjs +1 -0
  247. package/clients/cli/commands/discover.broken-integration.test.mjs +11 -7
  248. package/clients/cli/commands/discover.doc.mjs +9 -1
  249. package/clients/cli/commands/discover.mjs +144 -93
  250. package/clients/cli/commands/discover.no-source.test.mjs +448 -33
  251. package/clients/cli/commands/discover.sources.test.mjs +20 -1
  252. package/clients/cli/commands/docs.doc.mjs +1 -0
  253. package/clients/cli/commands/docs.mjs +49 -19
  254. package/clients/cli/commands/docs.test.mjs +2 -2
  255. package/clients/cli/commands/doctor-integration-components.doc.mjs +7 -3
  256. package/clients/cli/commands/doctor-integration-docs.doc.mjs +20 -5
  257. package/clients/cli/commands/doctor-integration-templates.doc.mjs +1 -0
  258. package/clients/cli/commands/doctor-integration-validate.doc.mjs +13 -2
  259. package/clients/cli/commands/doctor-integration.test.mjs +52 -4
  260. package/clients/cli/commands/doctor.doc.mjs +12 -2
  261. package/clients/cli/commands/doctor.mjs +102 -7
  262. package/clients/cli/commands/doctor.test.mjs +10 -4
  263. package/clients/cli/commands/gap-report.doc.mjs +15 -2
  264. package/clients/cli/commands/gap-report.mjs +24 -2
  265. package/clients/cli/commands/gap-report.test.mjs +79 -0
  266. package/clients/cli/commands/hook.doc.mjs +1 -0
  267. package/clients/cli/commands/init.behavior.test.mjs +5 -0
  268. package/clients/cli/commands/init.doc.mjs +29 -3
  269. package/clients/cli/commands/integration-add.doc.mjs +7 -0
  270. package/clients/cli/commands/integration-authoring.test.mjs +49 -3
  271. package/clients/cli/commands/integration-check.doc.mjs +10 -2
  272. package/clients/cli/commands/integration-check.test.mjs +175 -1
  273. package/clients/cli/commands/integration-pack.doc.mjs +8 -1
  274. package/clients/cli/commands/integration-verify.doc.mjs +8 -1
  275. package/clients/cli/commands/integration.mjs +15 -9
  276. package/clients/cli/commands/json-contract.test.mjs +11 -2
  277. package/clients/cli/commands/manifest.doc.mjs +1 -0
  278. package/clients/cli/commands/outside-project.test.mjs +774 -0
  279. package/clients/cli/commands/search.doc.mjs +1 -0
  280. package/clients/cli/commands/search.mjs +16 -11
  281. package/clients/cli/commands/search.test.mjs +429 -296
  282. package/clients/cli/commands/setup-nudge.test.mjs +102 -2
  283. package/clients/cli/commands/swizzle.doc.mjs +7 -0
  284. package/clients/cli/commands/swizzle.mjs +5 -3
  285. package/clients/cli/commands/swizzle.routing.test.mjs +19 -2
  286. package/clients/cli/commands/template.doc.mjs +1 -0
  287. package/clients/cli/commands/theme-add-lifecycle.test.mjs +3 -3
  288. package/clients/cli/commands/theme-add.doc.mjs +8 -1
  289. package/clients/cli/commands/theme-build.doc.mjs +1 -0
  290. package/clients/cli/commands/theme-eject.doc.mjs +1 -0
  291. package/clients/cli/commands/theme-list.behavior.test.mjs +6 -0
  292. package/clients/cli/commands/theme-list.doc.mjs +1 -0
  293. package/clients/cli/commands/theme-palette-generate.doc.mjs +1 -0
  294. package/clients/cli/commands/theme-remove.doc.mjs +7 -0
  295. package/clients/cli/commands/theme-targets.doc.mjs +1 -0
  296. package/clients/cli/commands/theme-template.doc.mjs +1 -0
  297. package/clients/cli/commands/theme-use.doc.mjs +7 -0
  298. package/clients/cli/commands/upgrade.config-ordering.test.mjs +2 -1
  299. package/clients/cli/commands/upgrade.doc.mjs +22 -1
  300. package/clients/cli/commands/upgrade.file-protection.test.mjs +3 -2
  301. package/clients/cli/commands/upgrade.hook-output.test.mjs +2 -1
  302. package/clients/cli/commands/upgrade.older-cli.test.mjs +160 -0
  303. package/clients/cli/index.mjs +111 -16
  304. package/clients/cli/lib/cli-error.mjs +4 -1
  305. package/clients/cli/lib/component-format.test.mjs +1 -1
  306. package/clients/cli/lib/define-command.mjs +2 -2
  307. package/clients/cli/lib/json-shim.mjs +3 -1
  308. package/clients/cli/lib/manifest.d.ts +9 -0
  309. package/clients/cli/lib/manifest.mjs +17 -1
  310. package/clients/cli/lib/manifest.test.mjs +25 -0
  311. package/foundation/agent-docs/agent-doc-state.d.mts +35 -6
  312. package/foundation/agent-docs/agent-doc-state.mjs +66 -6
  313. package/foundation/agent-docs/agent-docs.d.mts +4 -3
  314. package/foundation/agent-docs/agent-docs.mjs +11 -9
  315. package/foundation/agent-docs/computer-agent-text.d.mts +133 -0
  316. package/foundation/agent-docs/computer-agent-text.mjs +623 -0
  317. package/foundation/agent-docs/computer-agent-text.test.mjs +602 -0
  318. package/foundation/config/project.d.mts +35 -3
  319. package/foundation/config/project.mjs +145 -8
  320. package/foundation/config/project.test.mjs +66 -0
  321. package/foundation/config/theme-state.mjs +5 -4
  322. package/foundation/discovery/authoring-self-docs.mjs +1 -1
  323. package/foundation/discovery/cli-self-docs.mjs +20 -3
  324. package/foundation/discovery/cli-self-docs.test.mjs +3 -2
  325. package/foundation/discovery/component-replacement.mjs +2 -1
  326. package/foundation/discovery/copied-passages.d.mts +216 -0
  327. package/foundation/discovery/copied-passages.mjs +720 -0
  328. package/foundation/discovery/copied-passages.test.mjs +124 -0
  329. package/foundation/discovery/doc-consistency.d.mts +34 -0
  330. package/foundation/discovery/doc-consistency.mjs +513 -0
  331. package/foundation/discovery/doc-consistency.test.mjs +347 -0
  332. package/foundation/discovery/doc-fluff.d.mts +112 -0
  333. package/foundation/discovery/doc-fluff.mjs +328 -0
  334. package/foundation/discovery/doc-fluff.test.mjs +142 -0
  335. package/foundation/discovery/doc-types.d.mts +1 -1
  336. package/foundation/discovery/doc-types.mjs +15 -8
  337. package/foundation/discovery/doc-types.test.mjs +94 -2
  338. package/foundation/discovery/docs-discovery.d.mts +7 -0
  339. package/foundation/discovery/docs-discovery.mjs +26 -2
  340. package/foundation/discovery/project-typescript.d.mts +11 -2
  341. package/foundation/discovery/project-typescript.mjs +32 -4
  342. package/foundation/discovery/prop-drift.d.mts +26 -7
  343. package/foundation/discovery/prop-drift.mjs +173 -8
  344. package/foundation/discovery/prop-drift.test.mjs +120 -2
  345. package/foundation/discovery/runner-allowlist.d.mts +40 -0
  346. package/foundation/discovery/runner-allowlist.mjs +53 -0
  347. package/foundation/discovery/stray-pointers.d.mts +52 -0
  348. package/foundation/discovery/stray-pointers.mjs +293 -0
  349. package/foundation/discovery/stray-pointers.test.mjs +86 -0
  350. package/foundation/discovery/template-needs.mjs +5 -2
  351. package/foundation/discovery/theme-imports.mjs +8 -4
  352. package/foundation/discovery/theme-imports.test.mjs +1 -1
  353. package/foundation/discovery/type-text.mjs +1 -1
  354. package/foundation/discovery/type-text.test.mjs +1 -1
  355. package/foundation/doc-compiler/doc-loads.test.mjs +42 -2
  356. package/foundation/doc-compiler/inputs.mjs +4 -1
  357. package/foundation/doc-compiler/links.d.mts +32 -0
  358. package/foundation/doc-compiler/links.mjs +163 -26
  359. package/foundation/doc-compiler/links.test.mjs +97 -4
  360. package/foundation/doc-compiler/read.d.mts +1 -1
  361. package/foundation/doc-compiler/tree.mjs +30 -1
  362. package/foundation/doc-compiler/tree.test.mjs +8 -1
  363. package/foundation/env/cli-command.d.mts +83 -0
  364. package/foundation/env/cli-command.mjs +152 -0
  365. package/foundation/env/cli-command.test.mjs +129 -0
  366. package/foundation/env/node-version.test.mjs +15 -3
  367. package/foundation/env/package-manager.d.mts +93 -9
  368. package/foundation/env/package-manager.global.test.mjs +66 -0
  369. package/foundation/env/package-manager.mjs +184 -13
  370. package/foundation/env/package-manager.test.mjs +341 -15
  371. package/foundation/env/project-scope.d.mts +9 -0
  372. package/foundation/env/project-scope.mjs +33 -0
  373. package/foundation/env/search-path.d.mts +42 -0
  374. package/foundation/env/search-path.mjs +205 -0
  375. package/foundation/env/search-path.test.mjs +122 -0
  376. package/foundation/env/user-dirs.d.mts +39 -0
  377. package/foundation/env/user-dirs.mjs +93 -0
  378. package/foundation/env/user-dirs.test.mjs +72 -0
  379. package/foundation/env/user-settings.d.mts +46 -0
  380. package/foundation/env/user-settings.mjs +106 -0
  381. package/foundation/env/user-settings.test.mjs +64 -0
  382. package/foundation/fs/file-protection.mjs +15 -1
  383. package/foundation/fs/module-loader.d.mts +2 -1
  384. package/foundation/fs/module-loader.mjs +60 -4
  385. package/foundation/fs/module-loader.stdout.test.mjs +3 -2
  386. package/foundation/fs/module-loader.test.mjs +194 -0
  387. package/foundation/fs/path-compare.d.mts +33 -0
  388. package/foundation/fs/path-compare.mjs +73 -0
  389. package/foundation/fs/paths.d.mts +57 -1
  390. package/foundation/fs/paths.mjs +131 -3
  391. package/foundation/fs/paths.test.mjs +10 -1
  392. package/foundation/integrations/computer-integrations.d.mts +78 -0
  393. package/foundation/integrations/computer-integrations.mjs +316 -0
  394. package/foundation/integrations/computer-integrations.test.mjs +292 -0
  395. package/foundation/integrations/contribution-inventory.d.mts +11 -0
  396. package/foundation/integrations/contribution-inventory.mjs +50 -2
  397. package/foundation/integrations/integration-warnings.mjs +3 -2
  398. package/foundation/integrations/integration-warnings.test.mjs +8 -9
  399. package/foundation/integrations/integrations.d.mts +25 -1
  400. package/foundation/integrations/integrations.mjs +46 -3
  401. package/foundation/integrations/issue.ts +6 -0
  402. package/foundation/integrations/validate-contributions.d.mts +2 -0
  403. package/foundation/integrations/validate-contributions.mjs +41 -1
  404. package/foundation/release/agent-text-worker.d.mts +4 -0
  405. package/foundation/release/agent-text-worker.mjs +52 -0
  406. package/foundation/release/cache.d.mts +310 -0
  407. package/foundation/release/cache.mjs +1216 -0
  408. package/foundation/release/cache.test.mjs +920 -0
  409. package/foundation/release/choose.d.mts +207 -0
  410. package/foundation/release/choose.mjs +677 -0
  411. package/foundation/release/choose.test.mjs +427 -0
  412. package/foundation/release/core-worker.d.mts +4 -0
  413. package/foundation/release/core-worker.mjs +38 -0
  414. package/foundation/release/global-state.d.mts +259 -0
  415. package/foundation/release/global-state.mjs +472 -0
  416. package/foundation/release/global-state.test.mjs +381 -0
  417. package/foundation/release/handoff.d.mts +60 -0
  418. package/foundation/release/handoff.e2e.test.mjs +278 -0
  419. package/foundation/release/handoff.mjs +357 -0
  420. package/foundation/release/lock-lease.d.mts +23 -0
  421. package/foundation/release/lock-lease.mjs +80 -0
  422. package/foundation/release/notices.d.mts +253 -0
  423. package/foundation/release/notices.mjs +596 -0
  424. package/foundation/release/notices.test.mjs +565 -0
  425. package/foundation/release/npm-guard.d.mts +4 -0
  426. package/foundation/release/npm-guard.mjs +126 -0
  427. package/foundation/release/project.d.mts +141 -0
  428. package/foundation/release/project.mjs +466 -0
  429. package/foundation/release/project.test.mjs +288 -0
  430. package/foundation/release/refresh-worker.d.mts +4 -0
  431. package/foundation/release/refresh-worker.mjs +27 -0
  432. package/foundation/release/run-context.d.mts +47 -0
  433. package/foundation/release/run-context.mjs +149 -0
  434. package/foundation/release/update-notice-env.d.mts +28 -0
  435. package/foundation/release/update-notice-env.mjs +39 -0
  436. package/foundation/response/base.d.ts +26 -3
  437. package/foundation/response/error-codes.d.mts +3 -1
  438. package/foundation/response/error-codes.doc.mjs +12 -0
  439. package/foundation/response/error-codes.mjs +17 -0
  440. package/foundation/response/error-codes.test.mjs +2 -0
  441. package/foundation/response/json-contract.test.mjs +7 -2
  442. package/foundation/response/json.mjs +4 -1
  443. package/foundation/response/response-types.doc.mjs +17 -11
  444. package/foundation/response/response-types.doc.test.mjs +46 -0
  445. package/foundation/response/response.doc.mjs +3 -3
  446. package/foundation/xle/browser-imports.test.mjs +74 -0
  447. package/package.json +11 -11
  448. package/scripts/postinstall.mjs +24 -5
  449. /package/assets/codemods/transforms/{next → v0.7.2}/__tests__/migrate-list-edge-compensation-to-edge.test.mjs +0 -0
  450. /package/assets/codemods/transforms/{next → v0.7.2}/migrate-list-edge-compensation-to-edge.mjs +0 -0
@@ -0,0 +1,602 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file The computer-level agent text (spec AST-081 FR7, FR8): its two
5
+ * renderings, version-free and naming only stable commands; the one file it
6
+ * writes and the links it makes from the one table; what it keeps; exact
7
+ * removal; the search-path check behind its warning; and the rule that only
8
+ * `init --user` and the background step write it. Every test uses a temporary
9
+ * home folder.
10
+ */
11
+
12
+ import * as fs from 'node:fs';
13
+ import * as os from 'node:os';
14
+ import * as path from 'node:path';
15
+ import {spawnSync} from 'node:child_process';
16
+ import {createHash} from 'node:crypto';
17
+ import {fileURLToPath} from 'node:url';
18
+ import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest';
19
+ import {
20
+ SKILL_LINKS,
21
+ STABLE_COMMANDS,
22
+ astryxOnSearchPath,
23
+ computerSkillFile,
24
+ computerSkillLinks,
25
+ inspectComputerAgentText,
26
+ removeComputerAgentText,
27
+ renderComputerAgentText,
28
+ writeComputerAgentText,
29
+ } from './computer-agent-text.mjs';
30
+ import {MARKER_END, MARKER_START} from './agent-doc-state.mjs';
31
+ import {runWithCliDoor} from '../env/cli-command.mjs';
32
+ import {runCli} from '../../test-utils/run-cli.mjs';
33
+ import {parse as parseYaml} from 'yaml';
34
+
35
+ const HERE = path.dirname(fileURLToPath(import.meta.url));
36
+ const CLI_ROOT = path.resolve(HERE, '../..');
37
+ const CORE_ROOT = path.resolve(CLI_ROOT, '../core');
38
+
39
+ /**
40
+ * An Astryx file as some other release would write it: the given text with
41
+ * its hash line recorded.
42
+ * @param {string} text
43
+ * @returns {string}
44
+ */
45
+ function unedited(text) {
46
+ const hash = createHash('sha256').update(text).digest('hex');
47
+ return text.replace(
48
+ `${MARKER_START}\n`,
49
+ `${MARKER_START}\n<!-- astryx-managed sha256:${hash} -->\n`,
50
+ );
51
+ }
52
+
53
+ /** @type {string[]} */
54
+ const dirs = [];
55
+ /** @param {string} label */
56
+ function tmp(label) {
57
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), `astryx-user-${label}-`));
58
+ dirs.push(dir);
59
+ return dir;
60
+ }
61
+
62
+ let home = '';
63
+ beforeEach(() => {
64
+ home = tmp('home');
65
+ vi.stubEnv('HOME', home);
66
+ vi.stubEnv('USERPROFILE', home);
67
+ });
68
+ afterEach(() => {
69
+ vi.unstubAllEnvs();
70
+ while (dirs.length) {
71
+ fs.rmSync(/** @type {string} */ (dirs.pop()), {
72
+ recursive: true,
73
+ force: true,
74
+ });
75
+ }
76
+ });
77
+
78
+ const FILE = () => computerSkillFile(home);
79
+ const FOLDER = () => path.dirname(FILE());
80
+ const CLAUDE = () => path.join(home, '.claude', 'skills', 'astryx');
81
+
82
+ /** @param {string} at */
83
+ const isLinkToFolder = at =>
84
+ fs.lstatSync(at).isSymbolicLink() &&
85
+ fs.realpathSync(at) === fs.realpathSync(FOLDER());
86
+
87
+ describe('renderComputerAgentText', () => {
88
+ it('a skill named astryx with a one-sentence when-to-use description, in strict YAML', () => {
89
+ {
90
+ const text = renderComputerAgentText({command: 'astryx'});
91
+ const [, front] = text.split('---\n');
92
+ const meta = parseYaml(front);
93
+ expect(meta.name).toBe('astryx');
94
+ expect(meta.description).toMatch(/design system and UI library/);
95
+ expect(meta.description).toMatch(/building or changing UI/);
96
+ expect(meta.description).toMatch(/@astryxdesign/);
97
+ expect(
98
+ meta.description.split(/[.!?](\s|$)/u).filter(Boolean),
99
+ ).toHaveLength(1);
100
+ expect(text).toContain(MARKER_START);
101
+ expect(text).toContain(MARKER_END);
102
+ expect(text).toContain(
103
+ 'Astryx, a design system and UI library, is available on this computer.',
104
+ );
105
+ expect(text).toContain(
106
+ 'Use it when you build or change UI, pick components, or style or theme an app.',
107
+ );
108
+ expect(text).toContain("Start with `astryx search '<what you need>'`.");
109
+ }
110
+ });
111
+
112
+ it('is version-free and names only stable commands', () => {
113
+ {
114
+ const text = renderComputerAgentText({command: 'astryx'});
115
+ // No version string anywhere: not a release, a Node range, or a tag.
116
+ expect(text).not.toMatch(/\bv?\d+\.\d+(\.\d+)?\b/u);
117
+ expect(text).not.toMatch(/@(latest|next|canary)\b/u);
118
+ const commands = [...text.matchAll(/`astryx ([a-z-]+)/gu)].map(m => m[1]);
119
+ expect(commands.length).toBeGreaterThan(0);
120
+ expect(commands.filter(c => !STABLE_COMMANDS.includes(c))).toEqual([]);
121
+ }
122
+ });
123
+
124
+ it('stays short, and tells agents to read docs through the CLI', () => {
125
+ const text = renderComputerAgentText({command: 'astryx'});
126
+ expect(text.split('\n').length).toBeLessThanOrEqual(15);
127
+ expect(text).toContain(
128
+ "Then read `astryx component <Name>` or `astryx docs <topic>`, not the package's files.",
129
+ );
130
+ });
131
+
132
+ it("uses the host's door when one is set", () => {
133
+ const text = runWithCliDoor('acme astryx', () => renderComputerAgentText());
134
+ expect(text).toContain("`acme astryx search '<what you need>'`");
135
+ expect(text).not.toMatch(/`astryx /u);
136
+ });
137
+ });
138
+
139
+ describe('the link table', () => {
140
+ it('is data: each entry names a skills folder, the folder it needs, and link or copy; Claude Code always', () => {
141
+ expect(SKILL_LINKS[0]).toEqual({
142
+ skills: path.join('.claude', 'skills'),
143
+ when: null,
144
+ mode: 'link',
145
+ });
146
+ for (const place of SKILL_LINKS) {
147
+ expect(path.isAbsolute(place.skills)).toBe(false);
148
+ expect(place.skills.endsWith('skills')).toBe(true);
149
+ expect(['link', 'copy']).toContain(place.mode);
150
+ }
151
+ // Continue's scanner skips links, so it is the one copy.
152
+ expect(
153
+ SKILL_LINKS.filter(p => p.mode === 'copy').map(p => p.skills),
154
+ ).toEqual([path.join('.continue', 'skills')]);
155
+ // Never a store another tool manages.
156
+ for (const place of SKILL_LINKS) {
157
+ expect(place.skills).not.toBe(path.join('.codex', 'skills'));
158
+ expect(place.skills).not.toBe(path.join('.config', 'muse', 'skills'));
159
+ }
160
+ expect(computerSkillLinks(home).map(l => l.wanted)).toEqual(
161
+ SKILL_LINKS.map(l => l.when == null),
162
+ );
163
+ });
164
+ });
165
+
166
+ describe('links and the copy', () => {
167
+ it.skipIf(process.platform === 'win32')(
168
+ 'links with a relative target',
169
+ () => {
170
+ writeComputerAgentText({home, command: 'astryx'});
171
+ expect(fs.readlinkSync(CLAUDE())).toBe(
172
+ path.join('..', '..', '.agents', 'skills', 'astryx'),
173
+ );
174
+ },
175
+ );
176
+
177
+ it('links ~/.config/agents/skills only when that folder already exists', () => {
178
+ const kimi = path.join(home, '.config', 'agents', 'skills');
179
+ expect(writeComputerAgentText({home, command: 'astryx'}).links).toEqual([
180
+ CLAUDE(),
181
+ ]);
182
+ fs.mkdirSync(kimi, {recursive: true});
183
+ expect(writeComputerAgentText({home, command: 'astryx'}).links).toEqual([
184
+ CLAUDE(),
185
+ path.join(kimi, 'astryx'),
186
+ ]);
187
+ });
188
+
189
+ it('writes, refreshes, and removes a managed copy for Continue, whose scanner skips links', () => {
190
+ fs.mkdirSync(path.join(home, '.continue'));
191
+ const copy = path.join(home, '.continue', 'skills', 'astryx');
192
+ const {rows, links} = writeComputerAgentText({home, command: 'astryx'});
193
+ expect(rows).toContainEqual({path: copy, action: 'written'});
194
+ expect(links).not.toContain(copy);
195
+ expect(fs.lstatSync(copy).isSymbolicLink()).toBe(false);
196
+ expect(fs.readFileSync(path.join(copy, 'SKILL.md'), 'utf8')).toBe(
197
+ fs.readFileSync(FILE(), 'utf8'),
198
+ );
199
+ expect(
200
+ writeComputerAgentText({home, command: 'astryx'}).rows,
201
+ ).toContainEqual({
202
+ path: copy,
203
+ action: 'unchanged',
204
+ });
205
+ expect(removeComputerAgentText({home, links})).toContainEqual({
206
+ path: copy,
207
+ action: 'removed',
208
+ });
209
+ expect(fs.existsSync(copy)).toBe(false);
210
+ });
211
+
212
+ it.skipIf(process.platform === 'win32')(
213
+ 'links from a ~/.claude that is itself a link to a dotfiles folder, with a target that resolves',
214
+ () => {
215
+ const dotfiles = path.join(home, 'dotfiles', '.claude');
216
+ fs.mkdirSync(dotfiles, {recursive: true});
217
+ fs.symlinkSync(dotfiles, path.join(home, '.claude'), 'dir');
218
+
219
+ const {rows, links} = writeComputerAgentText({home, command: 'astryx'});
220
+
221
+ expect(rows).toContainEqual({path: CLAUDE(), action: 'linked'});
222
+ // The system resolves the link from the dotfiles folder, and it reaches
223
+ // the skill folder.
224
+ expect(isLinkToFolder(CLAUDE())).toBe(true);
225
+ expect(fs.readFileSync(path.join(CLAUDE(), 'SKILL.md'), 'utf8')).toBe(
226
+ fs.readFileSync(FILE(), 'utf8'),
227
+ );
228
+ expect(
229
+ inspectComputerAgentText({home, command: 'astryx', links}),
230
+ ).toContainEqual({
231
+ path: CLAUDE(),
232
+ state: 'linked',
233
+ link: true,
234
+ });
235
+ },
236
+ );
237
+
238
+ it.skipIf(process.platform === 'win32')(
239
+ 'reports a link the system cannot follow as dangling, even when its text names the folder from the path',
240
+ () => {
241
+ const dotfiles = path.join(home, 'dotfiles', '.claude');
242
+ fs.mkdirSync(path.join(dotfiles, 'skills'), {recursive: true});
243
+ fs.symlinkSync(dotfiles, path.join(home, '.claude'), 'dir');
244
+ writeComputerAgentText({home, command: 'astryx'});
245
+ // A link as 0.7.x wrote it: relative to ~/.claude/skills, which the
246
+ // system reads from ~/dotfiles/.claude/skills, so it dangles.
247
+ fs.rmSync(CLAUDE());
248
+ fs.symlinkSync(
249
+ path.relative(path.dirname(CLAUDE()), FOLDER()),
250
+ CLAUDE(),
251
+ 'dir',
252
+ );
253
+ expect(fs.existsSync(path.join(CLAUDE(), 'SKILL.md'))).toBe(false);
254
+
255
+ const rows = inspectComputerAgentText({
256
+ home,
257
+ command: 'astryx',
258
+ links: [CLAUDE()],
259
+ });
260
+ expect(rows.find(row => row.path === CLAUDE())?.state).toBe('dangling');
261
+ // init --user remakes it so that it resolves.
262
+ expect(
263
+ writeComputerAgentText({home, command: 'astryx'}).rows,
264
+ ).toContainEqual({
265
+ path: CLAUDE(),
266
+ action: 'linked',
267
+ });
268
+ expect(isLinkToFolder(CLAUDE())).toBe(true);
269
+ },
270
+ );
271
+
272
+ it.skipIf(process.platform === 'win32')(
273
+ 'writes the managed copy into a ~/.continue that is itself a link',
274
+ () => {
275
+ const real = path.join(home, 'dotfiles', '.continue');
276
+ fs.mkdirSync(real, {recursive: true});
277
+ fs.symlinkSync(real, path.join(home, '.continue'), 'dir');
278
+ const copy = path.join(home, '.continue', 'skills', 'astryx');
279
+
280
+ const {rows} = writeComputerAgentText({home, command: 'astryx'});
281
+
282
+ expect(rows).toContainEqual({path: copy, action: 'written'});
283
+ expect(
284
+ fs.readFileSync(
285
+ path.join(real, 'skills', 'astryx', 'SKILL.md'),
286
+ 'utf8',
287
+ ),
288
+ ).toBe(fs.readFileSync(FILE(), 'utf8'));
289
+ },
290
+ );
291
+
292
+ it('reports a link whose folder is missing as dangling', () => {
293
+ const {links} = writeComputerAgentText({home, command: 'astryx'});
294
+ fs.rmSync(FOLDER(), {recursive: true});
295
+ expect(inspectComputerAgentText({home, command: 'astryx', links})).toEqual([
296
+ {path: FILE(), state: 'absent'},
297
+ {path: CLAUDE(), state: 'dangling', link: true},
298
+ ]);
299
+ });
300
+ });
301
+
302
+ describe('write, refresh, keep, remove', () => {
303
+ it('writes one file, links Claude Code to its folder, then reports both unchanged', () => {
304
+ const first = writeComputerAgentText({home, command: 'astryx'});
305
+ expect(first.rows).toEqual([
306
+ {path: FILE(), action: 'written'},
307
+ {path: CLAUDE(), action: 'linked'},
308
+ ]);
309
+ expect(first.links).toEqual([CLAUDE()]);
310
+ expect(isLinkToFolder(CLAUDE())).toBe(true);
311
+ // An agent reading the linked folder reads the one file.
312
+ expect(fs.readFileSync(path.join(CLAUDE(), 'SKILL.md'), 'utf8')).toBe(
313
+ fs.readFileSync(FILE(), 'utf8'),
314
+ );
315
+ expect(writeComputerAgentText({home, command: 'astryx'}).rows).toEqual([
316
+ {path: FILE(), action: 'unchanged'},
317
+ {path: CLAUDE(), action: 'unchanged'},
318
+ ]);
319
+ });
320
+
321
+ it("links another agent's folder only when that agent's folder exists", () => {
322
+ fs.mkdirSync(path.join(home, '.kiro'));
323
+ const {links} = writeComputerAgentText({home, command: 'astryx'});
324
+ expect(links).toEqual([
325
+ CLAUDE(),
326
+ path.join(home, '.kiro', 'skills', 'astryx'),
327
+ ]);
328
+ expect(fs.existsSync(path.join(home, '.continue'))).toBe(false);
329
+ });
330
+
331
+ it('refreshes only the file; the links follow', () => {
332
+ writeComputerAgentText({home, command: 'astryx'});
333
+ fs.writeFileSync(
334
+ FILE(),
335
+ unedited(
336
+ `---\nname: astryx\n---\n\n${MARKER_START}\nold\n${MARKER_END}\n`,
337
+ ),
338
+ );
339
+ expect(inspectComputerAgentText({home, command: 'astryx'})[0].state).toBe(
340
+ 'differs',
341
+ );
342
+ expect(writeComputerAgentText({home, command: 'astryx'}).rows).toEqual([
343
+ {path: FILE(), action: 'written'},
344
+ {path: CLAUDE(), action: 'unchanged'},
345
+ ]);
346
+ expect(fs.readFileSync(path.join(CLAUDE(), 'SKILL.md'), 'utf8')).toBe(
347
+ renderComputerAgentText({command: 'astryx'}),
348
+ );
349
+ });
350
+
351
+ it('keeps an edited file, a file without the markers, and anything at a link target Astryx did not create', () => {
352
+ fs.mkdirSync(FOLDER(), {recursive: true});
353
+ fs.writeFileSync(FILE(), 'my own notes\n');
354
+ fs.mkdirSync(CLAUDE(), {recursive: true});
355
+ fs.writeFileSync(path.join(CLAUDE(), 'SKILL.md'), 'my own skill\n');
356
+ const {rows, links} = writeComputerAgentText({home, command: 'astryx'});
357
+ expect(rows).toEqual([
358
+ {path: FILE(), action: 'kept', reason: 'not-astryx'},
359
+ {path: CLAUDE(), action: 'kept', reason: 'not-astryx'},
360
+ ]);
361
+ expect(links).toEqual([]);
362
+ expect(fs.readFileSync(FILE(), 'utf8')).toBe('my own notes\n');
363
+ expect(
364
+ inspectComputerAgentText({home, command: 'astryx'}).map(e => e.state),
365
+ ).toEqual(['foreign', 'foreign']);
366
+
367
+ fs.writeFileSync(
368
+ FILE(),
369
+ `${renderComputerAgentText({command: 'astryx'})}edit\n`,
370
+ );
371
+ expect(writeComputerAgentText({home, command: 'astryx'}).rows[0]).toEqual({
372
+ path: FILE(),
373
+ action: 'kept',
374
+ reason: 'edited',
375
+ });
376
+ });
377
+
378
+ it('keeps a link that points somewhere else', () => {
379
+ const elsewhere = tmp('elsewhere');
380
+ fs.mkdirSync(path.dirname(CLAUDE()), {recursive: true});
381
+ fs.symlinkSync(elsewhere, CLAUDE(), 'dir');
382
+ const {rows} = writeComputerAgentText({home, command: 'astryx'});
383
+ expect(rows[1]).toEqual({
384
+ path: CLAUDE(),
385
+ action: 'kept',
386
+ reason: 'not-astryx',
387
+ });
388
+ expect(fs.realpathSync(CLAUDE())).toBe(fs.realpathSync(elsewhere));
389
+ });
390
+
391
+ it('replaces an unedited copy an earlier release left at a link target with the link', () => {
392
+ fs.mkdirSync(CLAUDE(), {recursive: true});
393
+ fs.writeFileSync(
394
+ path.join(CLAUDE(), 'SKILL.md'),
395
+ unedited(
396
+ `---\nname: astryx\n---\n\n${MARKER_START}\nold\n${MARKER_END}\n`,
397
+ ),
398
+ );
399
+ expect(writeComputerAgentText({home, command: 'astryx'}).rows[1]).toEqual({
400
+ path: CLAUDE(),
401
+ action: 'linked',
402
+ });
403
+ expect(isLinkToFolder(CLAUDE())).toBe(true);
404
+ });
405
+
406
+ it('removes the file and exactly the links it made, then reports the file absent', () => {
407
+ const {links} = writeComputerAgentText({home, command: 'astryx'});
408
+ // A link someone else made to the same folder is not Astryx's to remove.
409
+ const theirs = path.join(home, 'mine', 'astryx');
410
+ fs.mkdirSync(path.dirname(theirs), {recursive: true});
411
+ fs.symlinkSync(FOLDER(), theirs, 'dir');
412
+ expect(removeComputerAgentText({home, links})).toEqual([
413
+ {path: FILE(), action: 'removed'},
414
+ {path: CLAUDE(), action: 'removed'},
415
+ ]);
416
+ expect(fs.existsSync(CLAUDE())).toBe(false);
417
+ expect(fs.existsSync(FOLDER())).toBe(false);
418
+ expect(fs.lstatSync(theirs).isSymbolicLink()).toBe(true);
419
+ expect(removeComputerAgentText({home, links: []})).toEqual([
420
+ {path: FILE(), action: 'absent'},
421
+ ]);
422
+ });
423
+
424
+ it('keeps an edited file on remove, and a recorded link someone repointed', () => {
425
+ const {links} = writeComputerAgentText({home, command: 'astryx'});
426
+ fs.unlinkSync(CLAUDE());
427
+ fs.symlinkSync(tmp('other'), CLAUDE(), 'dir');
428
+ fs.appendFileSync(FILE(), 'edit\n');
429
+ expect(removeComputerAgentText({home, links})).toEqual([
430
+ {path: FILE(), action: 'kept', reason: 'edited'},
431
+ ]);
432
+ expect(fs.existsSync(FILE())).toBe(true);
433
+ expect(fs.lstatSync(CLAUDE()).isSymbolicLink()).toBe(true);
434
+ });
435
+ });
436
+
437
+ describe.skipIf(process.platform === 'win32')('astryxOnSearchPath', () => {
438
+ /** @param {string} file @param {string} content */
439
+ function executable(file, content) {
440
+ fs.mkdirSync(path.dirname(file), {recursive: true});
441
+ fs.writeFileSync(file, content);
442
+ fs.chmodSync(file, 0o755);
443
+ }
444
+
445
+ /** A global prefix with the CLI package linked into its bin folder. */
446
+ function linkedCli() {
447
+ const prefix = tmp('prefix');
448
+ const pkg = path.join(
449
+ prefix,
450
+ 'lib',
451
+ 'node_modules',
452
+ '@astryxdesign',
453
+ 'cli',
454
+ );
455
+ fs.mkdirSync(pkg, {recursive: true});
456
+ fs.writeFileSync(
457
+ path.join(pkg, 'package.json'),
458
+ '{"name":"@astryxdesign/cli","version":"0.0.0"}',
459
+ );
460
+ executable(path.join(pkg, 'bin.mjs'), '#!/usr/bin/env node\n');
461
+ fs.mkdirSync(path.join(prefix, 'bin'));
462
+ fs.symlinkSync(
463
+ path.join(pkg, 'bin.mjs'),
464
+ path.join(prefix, 'bin', 'astryx'),
465
+ );
466
+ return path.join(prefix, 'bin');
467
+ }
468
+
469
+ it('finds a linked CLI', () => {
470
+ expect(
471
+ astryxOnSearchPath({searchPath: linkedCli(), platform: 'linux'}),
472
+ ).toBe(true);
473
+ });
474
+
475
+ it("finds a package manager's shim that starts the CLI", () => {
476
+ const bin = tmp('shim');
477
+ executable(
478
+ path.join(bin, 'astryx'),
479
+ '#!/bin/sh\nexec node "$basedir/global/5/node_modules/@astryxdesign/cli/clients/cli/bin/astryx.mjs" "$@"\n',
480
+ );
481
+ expect(astryxOnSearchPath({searchPath: bin, platform: 'linux'})).toBe(true);
482
+ });
483
+
484
+ it('is false with no astryx, or when the first astryx is another program', () => {
485
+ expect(
486
+ astryxOnSearchPath({searchPath: tmp('empty'), platform: 'linux'}),
487
+ ).toBe(false);
488
+ const other = tmp('other');
489
+ executable(path.join(other, 'astryx'), '#!/bin/sh\necho other\n');
490
+ expect(
491
+ astryxOnSearchPath({
492
+ searchPath: [other, linkedCli()].join(path.delimiter),
493
+ platform: 'linux',
494
+ }),
495
+ ).toBe(false);
496
+ });
497
+
498
+ it('skips a file that cannot run', () => {
499
+ const bin = tmp('noexec');
500
+ fs.writeFileSync(path.join(bin, 'astryx'), 'not a program');
501
+ expect(
502
+ astryxOnSearchPath({
503
+ searchPath: [bin, linkedCli()].join(path.delimiter),
504
+ platform: 'linux',
505
+ }),
506
+ ).toBe(true);
507
+ });
508
+ });
509
+
510
+ describe('only `init --user` writes the computer-level text (FR7)', () => {
511
+ /** @returns {string[]} the skill paths present in the temp home */
512
+ const present = () =>
513
+ [
514
+ computerSkillFile(home),
515
+ path.join(home, '.claude', 'skills', 'astryx'),
516
+ ].filter(p => fs.existsSync(p));
517
+
518
+ /** A project that installs this release's Core. */
519
+ function project() {
520
+ const dir = tmp('project');
521
+ fs.writeFileSync(
522
+ path.join(dir, 'package.json'),
523
+ JSON.stringify({
524
+ name: 'app',
525
+ private: true,
526
+ dependencies: {'@astryxdesign/core': '*'},
527
+ }),
528
+ );
529
+ fs.mkdirSync(path.join(dir, 'node_modules', '@astryxdesign'), {
530
+ recursive: true,
531
+ });
532
+ fs.symlinkSync(
533
+ CORE_ROOT,
534
+ path.join(dir, 'node_modules', '@astryxdesign', 'core'),
535
+ 'dir',
536
+ );
537
+ return dir;
538
+ }
539
+
540
+ it('no source outside init --user (its adapter) and the background step imports the write or remove functions', () => {
541
+ /** @type {string[]} */
542
+ const importers = [];
543
+ /** @param {string} dir */
544
+ const walk = dir => {
545
+ for (const entry of fs.readdirSync(dir, {withFileTypes: true})) {
546
+ if (entry.name === 'node_modules' || entry.name.startsWith('.'))
547
+ continue;
548
+ const file = path.join(dir, entry.name);
549
+ if (entry.isDirectory()) walk(file);
550
+ else if (
551
+ /\.m?[jt]s$/.test(entry.name) &&
552
+ !/\.test\.m?[jt]s$/.test(entry.name) &&
553
+ // Generated declarations (sync:api-types) import nothing.
554
+ !/\.d\.m?ts$/.test(entry.name)
555
+ ) {
556
+ const text = fs.readFileSync(file, 'utf8');
557
+ if (
558
+ /\b(writeComputerAgentText|removeComputerAgentText)\b/.test(text)
559
+ ) {
560
+ importers.push(path.relative(CLI_ROOT, file));
561
+ }
562
+ }
563
+ }
564
+ };
565
+ for (const top of [
566
+ 'api',
567
+ 'clients',
568
+ 'foundation',
569
+ 'scripts',
570
+ 'authoring',
571
+ ]) {
572
+ walk(path.join(CLI_ROOT, top));
573
+ }
574
+ expect(importers.sort()).toEqual(
575
+ [
576
+ path.join('api', 'init', '_adapter.mjs'),
577
+ path.join('foundation', 'agent-docs', 'computer-agent-text.mjs'),
578
+ path.join('foundation', 'release', 'agent-text-worker.mjs'),
579
+ ].sort(),
580
+ );
581
+ });
582
+
583
+ it('install (postinstall), project init, and upgrade write none of it', async () => {
584
+ const dir = project();
585
+ const postinstall = spawnSync(
586
+ process.execPath,
587
+ [path.join(CLI_ROOT, 'scripts', 'postinstall.mjs')],
588
+ {
589
+ env: {...process.env, HOME: home, USERPROFILE: home, INIT_CWD: dir},
590
+ encoding: 'utf8',
591
+ },
592
+ );
593
+ expect(postinstall.status).toBe(0);
594
+ expect((await runCli(['--json', 'init'], dir)).status).toBe(0);
595
+ expect((await runCli(['--json', 'init', '--all'], dir)).status).toBe(0);
596
+ await runCli(['--json', 'upgrade'], dir);
597
+ await runCli(['--json', 'init'], tmp('outside'));
598
+ expect(present()).toEqual([]);
599
+ expect(fs.existsSync(path.join(home, '.agents'))).toBe(false);
600
+ expect(fs.existsSync(path.join(home, '.claude'))).toBe(false);
601
+ }, 60_000);
602
+ });
@@ -1,6 +1,24 @@
1
1
  // @generated by scripts/sync-api-types.mjs from the JSDoc in foundation/**/*.mjs.
2
2
  // DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
3
3
 
4
+ /**
5
+ * The computer-level integrations (spec AST-081 FR12), read from package.json
6
+ * files only. Outside a project, those installed beside the running CLI;
7
+ * inside one, where the project's own CLI may be the copy that answers, those
8
+ * installed beside the CLI installed for the computer (see globalCliRoot).
9
+ * @param {string} [cwd]
10
+ * @returns {import('../integrations/computer-integrations.mjs').ComputerIntegrationCandidate[]}
11
+ */
12
+ export function computerIntegrationCandidates(cwd?: string): import("../integrations/computer-integrations.mjs").ComputerIntegrationCandidate[];
13
+ /**
14
+ * The computer-level integrations a project could add (spec AST-081 FR12):
15
+ * those it does not install, whose peer ranges admit the release that
16
+ * answers, and whose manifest loads.
17
+ * @param {string} cwd
18
+ * @param {Set<string>} installed package names the project loads or declares
19
+ * @returns {Promise<import('../integrations/computer-integrations.mjs').ComputerIntegrationCandidate[]>}
20
+ */
21
+ export function availableComputerIntegrations(cwd: string, installed: Set<string>): Promise<import("../integrations/computer-integrations.mjs").ComputerIntegrationCandidate[]>;
4
22
  /**
5
23
  * Find astryx.config.{ts,mjs,js} as a sibling of the nearest package.json.
6
24
  * Returns the absolute path, or null if none is present. Throws if multiple
@@ -37,13 +55,18 @@ export class Project {
37
55
  * and loads the configured integrations. Discovery is NOT run here — it is
38
56
  * lazy and memoized on the returned instance.
39
57
  *
58
+ * `core: 'project'` reads only the project's installed Core, never the Core
59
+ * the answering release carries: the commands that judge or change a
60
+ * project (`doctor`, `swizzle`, `upgrade`) load it so (spec AST-081 FR3).
61
+ *
40
62
  * @param {string} [cwd]
41
- * @param {{cache?: import('./config-cache.mjs').ConfigCache, fresh?: boolean}} [options]
63
+ * @param {{cache?: import('./config-cache.mjs').ConfigCache, fresh?: boolean, core?: 'any' | 'project'}} [options]
42
64
  * @returns {Promise<Project>}
43
65
  */
44
- static load(cwd?: string, { cache, fresh }?: {
66
+ static load(cwd?: string, { cache, fresh, core }?: {
45
67
  cache?: import("./config-cache.mjs").ConfigCache;
46
68
  fresh?: boolean;
69
+ core?: "any" | "project";
47
70
  }): Promise<Project>;
48
71
  /**
49
72
  * @param {object} init
@@ -54,8 +77,9 @@ export class Project {
54
77
  * @param {import('../integrations/integrations.mjs').LoadedIntegration[]} init.loadedIntegrations
55
78
  * @param {import('./config-cache.mjs').ConfigCache} init.cache
56
79
  * @param {string} init.hash config content hash
80
+ * @param {'any' | 'project'} [init.core] which Core it reads
57
81
  */
58
- constructor({ cwd, configPath, config, integrations, loadedIntegrations, cache, hash, }: {
82
+ constructor({ cwd, configPath, config, integrations, loadedIntegrations, cache, hash, core, }: {
59
83
  cwd: string;
60
84
  configPath: string | null;
61
85
  config: import("../../authoring/config/type").AstryxConfig;
@@ -63,6 +87,7 @@ export class Project {
63
87
  loadedIntegrations: import("../integrations/integrations.mjs").LoadedIntegration[];
64
88
  cache: import("./config-cache.mjs").ConfigCache;
65
89
  hash: string;
90
+ core?: "project" | "any" | undefined;
66
91
  });
67
92
  /**
68
93
  * The validated config surface (same data loadConfig returned, minus the
@@ -92,6 +117,13 @@ export class Project {
92
117
  * @returns {string|null}
93
118
  */
94
119
  get configPath(): string | null;
120
+ /**
121
+ * The Core this project reads: its installed Core, else, unless it was
122
+ * loaded with `core: 'project'`, the Core the answering release carries
123
+ * (spec AST-081 FR3). Null when there is none.
124
+ * @returns {string | null}
125
+ */
126
+ coreDir(): string | null;
95
127
  /**
96
128
  * Core + integration component ownership records. Wraps
97
129
  * discoverOwnedComponents for core and discoverIntegrationComponents (via