@astryxdesign/cli 0.7.2-canary.fe2c655 → 0.7.3-canary.02f5fe4

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 (694) hide show
  1. package/CHANGELOG.md +119 -0
  2. package/README.md +216 -162
  3. package/api/build/_adapter.d.mts +66 -0
  4. package/api/build/_adapter.mjs +139 -4
  5. package/api/build/blocks/blocks.d.mts +92 -0
  6. package/api/build/blocks/blocks.mjs +528 -0
  7. package/api/build/blocks/blocks.test.mjs +238 -0
  8. package/api/build/blocks/blocks.weights.json +1 -0
  9. package/api/build/build.doc.mjs +4 -0
  10. package/api/build/build.mjs +25 -6
  11. package/api/build/build.type.mjs +1 -1
  12. package/api/build/help/help.mjs +1 -1
  13. package/api/build/kit/answer.d.mts +18 -0
  14. package/api/build/kit/answer.mjs +195 -0
  15. package/api/build/kit/answer.test.mjs +108 -0
  16. package/api/build/kit/kind.d.mts +25 -0
  17. package/api/build/kit/kind.mjs +170 -0
  18. package/api/build/kit/kit.d.mts +2 -4
  19. package/api/build/kit/kit.mjs +8 -406
  20. package/api/build/kit/rank.d.mts +3 -32
  21. package/api/build/kit/rank.mjs +25 -342
  22. package/api/build/kit/rank.test.mjs +2 -1
  23. package/api/build/kit/signals.d.mts +79 -0
  24. package/api/build/kit/signals.mjs +295 -0
  25. package/api/build/kit/start.d.mts +32 -0
  26. package/api/build/kit/start.mjs +163 -0
  27. package/api/build/kit/words.d.mts +72 -0
  28. package/api/build/kit/words.mjs +202 -0
  29. package/api/build/tree.d.mts +20 -0
  30. package/api/build/tree.mjs +53 -0
  31. package/api/build/tree.test.mjs +185 -0
  32. package/api/build/tree.weights.json +1 -0
  33. package/api/component/_adapter.d.mts +72 -1
  34. package/api/component/_adapter.mjs +251 -23
  35. package/api/component/component-replacement.test.mjs +1 -0
  36. package/api/component/component.doc.mjs +6 -1
  37. package/api/component/component.mjs +97 -16
  38. package/api/component/component.test.mjs +95 -0
  39. package/api/component/doc-links.test.mjs +386 -0
  40. package/api/component/integration-export-name.test.mjs +644 -0
  41. package/api/component/list/list.mjs +42 -4
  42. package/api/discover/_adapter.d.mts +131 -12
  43. package/api/discover/_adapter.mjs +520 -34
  44. package/api/discover/_adapter.test.mjs +641 -17
  45. package/api/discover/_catalog-view.d.mts +16 -5
  46. package/api/discover/_catalog-view.mjs +32 -8
  47. package/api/discover/detail/doc/doc.mjs +9 -1
  48. package/api/discover/discover.d.mts +11 -0
  49. package/api/discover/discover.doc.mjs +9 -7
  50. package/api/discover/discover.mjs +120 -19
  51. package/api/discover/discover.type.d.mts +20 -7
  52. package/api/discover/discover.type.mjs +17 -7
  53. package/api/discover/list/list.d.mts +4 -3
  54. package/api/discover/list/list.mjs +17 -10
  55. package/api/docs/_adapter.d.mts +76 -2
  56. package/api/docs/_adapter.mjs +630 -34
  57. package/api/docs/docs-homes.test.mjs +127 -0
  58. package/api/docs/docs.doc.mjs +5 -4
  59. package/api/docs/docs.mjs +2 -1
  60. package/api/docs/docs.test.mjs +39 -20
  61. package/api/docs/integration-tree.test.mjs +42 -20
  62. package/api/docs/integrationDocs.test.mjs +125 -1
  63. package/api/docs/list/list.mjs +12 -2
  64. package/api/docs/namespace-section.test.mjs +6 -6
  65. package/api/docs/reference-blocks.test.mjs +3 -5
  66. package/api/doctor/_adapter.d.mts +128 -0
  67. package/api/doctor/_adapter.mjs +253 -4
  68. package/api/doctor/doctor.d.mts +217 -0
  69. package/api/doctor/doctor.doc.mjs +3 -2
  70. package/api/doctor/doctor.mjs +691 -52
  71. package/api/doctor/doctor.test.mjs +267 -18
  72. package/api/doctor/release-checks.test.mjs +156 -0
  73. package/api/doctor/theme-checks.mjs +17 -4
  74. package/api/doctor/theme-checks.test.mjs +223 -7
  75. package/api/error.d.mts +10 -0
  76. package/api/error.mjs +25 -0
  77. package/api/gap-report/gap-report.d.mts +3 -0
  78. package/api/gap-report/gap-report.doc.mjs +11 -3
  79. package/api/gap-report/gap-report.mjs +219 -52
  80. package/api/gap-report/gap-report.test.mjs +233 -0
  81. package/api/gap-report/gap-report.type.d.mts +41 -0
  82. package/api/gap-report/gap-report.type.mjs +18 -0
  83. package/api/hook/_adapter.mjs +3 -11
  84. package/api/hook/hook.doc.mjs +4 -0
  85. package/api/hook/hook.mjs +1 -1
  86. package/api/hook/list/list.d.mts +1 -1
  87. package/api/index.d.mts +2 -0
  88. package/api/index.mjs +2 -0
  89. package/api/init/_adapter.d.mts +22 -0
  90. package/api/init/_adapter.mjs +59 -0
  91. package/api/init/init.d.mts +7 -5
  92. package/api/init/init.doc.mjs +45 -10
  93. package/api/init/init.mjs +28 -4
  94. package/api/init/init.test.mjs +18 -2
  95. package/api/init/init.type.d.mts +50 -5
  96. package/api/init/init.type.mjs +39 -5
  97. package/api/init/remove/remove.mjs +17 -4
  98. package/api/init/remove/remove.test.mjs +114 -2
  99. package/api/init/run/run.d.mts +1 -1
  100. package/api/init/run/run.mjs +86 -12
  101. package/api/init/user/user.d.mts +14 -0
  102. package/api/init/user/user.mjs +87 -0
  103. package/api/init/user/user.test.mjs +232 -0
  104. package/api/integration/_adapter.d.mts +53 -0
  105. package/api/integration/_adapter.mjs +132 -0
  106. package/api/integration/add-contribution.component-names.test.mjs +66 -0
  107. package/api/integration/add-contribution.mjs +8 -4
  108. package/api/integration/add-contribution.test.mjs +4 -2
  109. package/api/integration/authoring-checks.mjs +192 -14
  110. package/api/integration/authoring-checks.test.mjs +1028 -0
  111. package/api/integration/authoring-checks.type.d.mts +143 -0
  112. package/api/integration/authoring-checks.type.mjs +74 -2
  113. package/api/integration/check/check.d.mts +96 -0
  114. package/api/integration/check/check.mjs +357 -0
  115. package/api/integration/check/check.test.mjs +489 -0
  116. package/api/integration/integration-check.type.d.mts +118 -0
  117. package/api/integration/integration-check.type.mjs +84 -0
  118. package/api/integration/integrationCheck.doc.d.mts +10 -0
  119. package/api/integration/integrationCheck.doc.mjs +73 -0
  120. package/api/integration/integrationComponentConflicts.doc.mjs +45 -6
  121. package/api/integration/integrationDocConflicts.doc.mjs +22 -5
  122. package/api/integration/pack-check.mjs +486 -123
  123. package/api/integration/pack-check.test.mjs +817 -10
  124. package/api/integration/validate-integration-case-disk.test.mjs +111 -0
  125. package/api/integration/validate-integration-fixes.test.mjs +73 -5
  126. package/api/integration/validate-integration-gaps.test.mjs +1975 -0
  127. package/api/integration/validate-integration.d.mts +23 -2
  128. package/api/integration/validate-integration.mjs +358 -45
  129. package/api/integration/validate-integration.test.mjs +88 -8
  130. package/api/integration/validate-integration.type.d.mts +105 -0
  131. package/api/integration/validate-integration.type.mjs +79 -1
  132. package/api/integration/validate-unread-theme-folders.test.mjs +3 -2
  133. package/api/integration/validateIntegration.doc.mjs +19 -3
  134. package/api/search/ORDERING.md +28 -0
  135. package/api/search/_adapter.d.mts +91 -0
  136. package/api/search/_adapter.mjs +172 -0
  137. package/api/search/_adapter.test.mjs +173 -0
  138. package/api/search/_candidate-score.d.mts +40 -0
  139. package/api/search/_candidate-score.mjs +238 -0
  140. package/api/search/_cards.json +6098 -0
  141. package/api/search/_compound.d.mts +50 -0
  142. package/api/search/_compound.mjs +170 -0
  143. package/api/search/_doc-searcher.d.mts +4 -0
  144. package/api/search/_doc-searcher.mjs +23 -0
  145. package/api/search/_doc-searcher.test.mjs +36 -0
  146. package/api/search/_doc-searcher.weights.json +1 -0
  147. package/api/search/_doc-text.d.mts +30 -0
  148. package/api/search/_doc-text.mjs +87 -0
  149. package/api/search/_entries.d.mts +63 -0
  150. package/api/search/_entries.mjs +232 -0
  151. package/api/search/_entries.test.mjs +48 -0
  152. package/api/search/_features.d.mts +35 -0
  153. package/api/search/_features.mjs +155 -0
  154. package/api/search/_gather-components.d.mts +26 -0
  155. package/api/search/_gather-components.mjs +284 -0
  156. package/api/search/_gather-docs.d.mts +20 -0
  157. package/api/search/_gather-docs.mjs +254 -0
  158. package/api/search/_gather-hooks.d.mts +12 -0
  159. package/api/search/_gather-hooks.mjs +52 -0
  160. package/api/search/_gather-templates.d.mts +25 -0
  161. package/api/search/_gather-templates.mjs +107 -0
  162. package/api/search/_lexical.d.mts +135 -0
  163. package/api/search/_lexical.mjs +351 -0
  164. package/api/search/_main-searcher.d.mts +19 -0
  165. package/api/search/_main-searcher.mjs +115 -0
  166. package/api/search/_main-searcher.test.mjs +106 -0
  167. package/api/search/_main-searcher.weights.json +1 -0
  168. package/api/search/_module-doc.d.mts +32 -0
  169. package/api/search/_module-doc.mjs +78 -0
  170. package/api/search/_order.d.mts +32 -0
  171. package/api/search/_order.mjs +162 -0
  172. package/api/search/_order.test.mjs +72 -0
  173. package/api/search/_priority.d.mts +50 -0
  174. package/api/search/_priority.mjs +139 -0
  175. package/api/search/_query-words.d.mts +25 -0
  176. package/api/search/_query-words.mjs +92 -0
  177. package/api/search/_query.d.mts +65 -0
  178. package/api/search/_query.mjs +176 -0
  179. package/api/search/_result.d.mts +97 -0
  180. package/api/search/_result.mjs +150 -0
  181. package/api/search/_score.d.mts +60 -0
  182. package/api/search/_score.mjs +283 -0
  183. package/api/search/_synonyms.d.mts +21 -0
  184. package/api/search/_synonyms.mjs +67 -0
  185. package/api/search/_template-searcher.d.mts +4 -0
  186. package/api/search/_template-searcher.mjs +23 -0
  187. package/api/search/_template-searcher.test.mjs +40 -0
  188. package/api/search/_template-searcher.weights.json +1 -0
  189. package/api/search/_titles.d.mts +33 -0
  190. package/api/search/_titles.mjs +65 -0
  191. package/api/search/_words.d.mts +64 -0
  192. package/api/search/_words.mjs +157 -0
  193. package/api/search/search.d.mts +22 -236
  194. package/api/search/search.doc.mjs +14 -7
  195. package/api/search/search.domain-priority.test.mjs +108 -18
  196. package/api/search/search.integration-check.test.mjs +24 -0
  197. package/api/search/search.mjs +59 -1904
  198. package/api/search/search.test.mjs +193 -15
  199. package/api/search/search.type.d.mts +20 -2
  200. package/api/search/search.type.mjs +12 -2
  201. package/api/search/tree.d.mts +15 -0
  202. package/api/search/tree.mjs +40 -0
  203. package/api/search/tree.test.mjs +66 -0
  204. package/api/search/tree.weights.json +1 -0
  205. package/api/swizzle/_adapter.mjs +2 -2
  206. package/api/swizzle/copy/copy.mjs +29 -15
  207. package/api/template/template-integration.test.mjs +1 -1
  208. package/api/template/template.mjs +5 -1
  209. package/api/template/template.test.mjs +120 -3
  210. package/api/theme/_adapter.d.mts +16 -0
  211. package/api/theme/_adapter.mjs +32 -5
  212. package/api/theme/build/build.d.mts +2 -1
  213. package/api/theme/build/build.family.test.mjs +64 -0
  214. package/api/theme/build/build.mjs +250 -38
  215. package/api/theme/build/build.packed-old-core.test.mjs +21 -3
  216. package/api/theme/build/font-warning.mjs +5 -3
  217. package/api/theme/build/font-warning.test.mjs +2 -1
  218. package/api/theme/remove/remove.test.mjs +2 -2
  219. package/api/theme/targets/targets.mjs +3 -10
  220. package/api/theme/themeBuild.doc.mjs +4 -4
  221. package/api/theme/themeTargets.doc.mjs +4 -0
  222. package/api/theme/use/use.test.mjs +1 -1
  223. package/api/upgrade/_adapter.d.mts +97 -11
  224. package/api/upgrade/_adapter.mjs +315 -32
  225. package/api/upgrade/registry/registry.d.mts +10 -4
  226. package/api/upgrade/registry/registry.mjs +33 -16
  227. package/api/upgrade/registry/registry.test.mjs +85 -0
  228. package/api/upgrade/run/cli-behind-core.test.mjs +438 -0
  229. package/api/upgrade/run/run.d.mts +4 -2
  230. package/api/upgrade/run/run.mjs +48 -4
  231. package/api/upgrade/run/run.test.mjs +417 -3
  232. package/api/upgrade/status/status.d.mts +21 -5
  233. package/api/upgrade/status/status.mjs +45 -8
  234. package/api/upgrade/upgrade.doc.mjs +16 -8
  235. package/api/upgrade/upgrade.mjs +18 -3
  236. package/api/upgrade/upgrade.test.mjs +194 -0
  237. package/api/upgrade/upgrade.type.d.mts +28 -3
  238. package/api/upgrade/upgrade.type.mjs +24 -7
  239. package/assets/codemods/__tests__/registry.test.mjs +1 -0
  240. package/assets/codemods/registry.mjs +1 -0
  241. package/assets/codemods/runner.mjs +7 -1
  242. package/assets/codemods/transforms/v0.7.0/__tests__/validate-data-token-ownership.test.mjs +11 -0
  243. package/assets/codemods/transforms/v0.7.0/validate-data-token-ownership.mjs +3 -3
  244. package/assets/codemods/transforms/v0.7.2/__tests__/migrate-list-edge-compensation-to-edge.test.mjs +120 -0
  245. package/assets/codemods/transforms/v0.7.2/index.mjs +20 -0
  246. package/assets/codemods/transforms/v0.7.2/migrate-list-edge-compensation-to-edge.mjs +198 -0
  247. package/assets/discover/official-packages.json +367 -0
  248. package/assets/docs/README.md +10 -0
  249. package/assets/docs/author-a-theme.doc.mjs +5 -5
  250. package/assets/docs/browser-support.doc.mjs +1 -1
  251. package/assets/docs/getting-started.doc.mjs +26 -6
  252. package/assets/docs/is-my-code-good.doc.mjs +287 -0
  253. package/assets/docs/styling-overview.doc.mjs +1 -1
  254. package/assets/docs/theme.doc.dense.mjs +1 -1
  255. package/assets/docs/theme.doc.mjs +2 -2
  256. package/assets/docs/theme.doc.zh.mjs +1 -1
  257. package/assets/docs/tree/add-a-component.doc.mjs +22 -2
  258. package/assets/docs/tree/add-a-theme.doc.mjs +14 -4
  259. package/assets/docs/tree/add-a-topic.doc.mjs +54 -9
  260. package/assets/docs/tree/agent-guidance.doc.mjs +19 -5
  261. package/assets/docs/tree/api.doc.mjs +6 -0
  262. package/assets/docs/tree/authoring.doc.mjs +61 -0
  263. package/assets/docs/tree/block-template.doc.mjs +8 -20
  264. package/assets/docs/tree/build-the-template.doc.mjs +1 -1
  265. package/assets/docs/tree/building-blocks.doc.mjs +6 -2
  266. package/assets/docs/tree/check-the-props.doc.mjs +201 -0
  267. package/assets/docs/tree/check-your-docs.doc.mjs +69 -11
  268. package/assets/docs/tree/checks.doc.mjs +32 -24
  269. package/assets/docs/tree/ci-and-containers.doc.mjs +101 -0
  270. package/assets/docs/tree/cli-command.doc.mjs +142 -0
  271. package/assets/docs/tree/cli.doc.mjs +7 -1
  272. package/assets/docs/tree/codemods.doc.mjs +17 -7
  273. package/assets/docs/tree/commands.doc.mjs +7 -1
  274. package/assets/docs/tree/component-family.doc.mjs +8 -6
  275. package/assets/docs/tree/component-imports.doc.mjs +21 -3
  276. package/assets/docs/tree/components.doc.mjs +7 -1
  277. package/assets/docs/tree/computer-agent-guide.doc.mjs +160 -0
  278. package/assets/docs/tree/computer-integrations.doc.mjs +93 -0
  279. package/assets/docs/tree/configuration.doc.mjs +8 -4
  280. package/assets/docs/tree/debug-and-gap-reports.doc.mjs +41 -22
  281. package/assets/docs/tree/define-the-theme.doc.mjs +7 -6
  282. package/assets/docs/tree/describe-the-component.doc.mjs +3 -3
  283. package/assets/docs/tree/docs.doc.mjs +9 -3
  284. package/assets/docs/tree/document-the-template.doc.mjs +1 -1
  285. package/assets/docs/tree/document-the-theme.doc.mjs +4 -4
  286. package/assets/docs/tree/export-template-assets.doc.mjs +1 -1
  287. package/assets/docs/tree/extend-or-replace.doc.mjs +19 -9
  288. package/assets/docs/{authoring.doc.mjs → tree/field-reference.doc.mjs} +4 -3
  289. package/assets/docs/tree/fonts-and-assets.doc.mjs +3 -3
  290. package/assets/docs/tree/generate-a-palette.doc.mjs +8 -6
  291. package/assets/docs/tree/global-integrations.doc.mjs +124 -0
  292. package/assets/docs/tree/global.doc.mjs +131 -0
  293. package/assets/docs/tree/grade-template-with-agent.doc.mjs +5 -5
  294. package/assets/docs/tree/help.doc.mjs +2 -2
  295. package/assets/docs/tree/host-entry.doc.mjs +128 -0
  296. package/assets/docs/tree/install-once.doc.mjs +105 -0
  297. package/assets/docs/tree/layout-spacing.doc.mjs +2 -3
  298. package/assets/docs/tree/layout.doc.mjs +6 -6
  299. package/assets/docs/tree/links.doc.mjs +7 -6
  300. package/assets/docs/tree/migration-setup.doc.mjs +3 -2
  301. package/assets/docs/tree/migration-verification.doc.mjs +0 -1
  302. package/assets/docs/tree/migration.doc.mjs +0 -1
  303. package/assets/docs/tree/monorepos.doc.mjs +84 -0
  304. package/assets/docs/tree/package-and-test.doc.mjs +1 -1
  305. package/assets/docs/tree/page-template.doc.mjs +4 -8
  306. package/assets/docs/tree/problems.doc.mjs +205 -0
  307. package/assets/docs/tree/project-global-or-both.doc.mjs +131 -0
  308. package/assets/docs/tree/publishing.doc.mjs +6 -6
  309. package/assets/docs/tree/quick-start.doc.mjs +54 -28
  310. package/assets/docs/tree/reference.doc.mjs +192 -0
  311. package/assets/docs/tree/replace-a-core-component.doc.mjs +15 -7
  312. package/assets/docs/tree/replace-a-core-template.doc.mjs +10 -12
  313. package/assets/docs/tree/scaffold.doc.mjs +1 -1
  314. package/assets/docs/tree/sections-and-placement.doc.mjs +9 -11
  315. package/assets/docs/tree/see-it-in-an-app.doc.mjs +17 -5
  316. package/assets/docs/tree/ship.doc.mjs +2 -2
  317. package/assets/docs/tree/short-and-findable.doc.mjs +9 -8
  318. package/assets/docs/tree/single-component.doc.mjs +15 -6
  319. package/assets/docs/tree/start-a-template.doc.mjs +18 -8
  320. package/assets/docs/tree/subcomponent.doc.mjs +6 -6
  321. package/assets/docs/tree/template-assets.doc.mjs +2 -2
  322. package/assets/docs/tree/template-doc-overview.doc.mjs +6 -17
  323. package/assets/docs/tree/template-fonts.doc.mjs +1 -1
  324. package/assets/docs/tree/template-grading-rubric.doc.mjs +17 -38
  325. package/assets/docs/tree/template-icons.doc.mjs +1 -1
  326. package/assets/docs/tree/template-images-media.doc.mjs +1 -1
  327. package/assets/docs/tree/template-styles.doc.mjs +2 -2
  328. package/assets/docs/tree/templates.doc.mjs +7 -1
  329. package/assets/docs/tree/test-in-an-app.doc.mjs +7 -7
  330. package/assets/docs/tree/test-template-in-app.doc.mjs +5 -5
  331. package/assets/docs/tree/themes.doc.mjs +5 -1
  332. package/assets/docs/tree/tokens-and-setup.doc.mjs +19 -15
  333. package/assets/docs/tree/tokens-data.doc.mjs +2 -2
  334. package/assets/docs/tree/troubleshooting.doc.mjs +29 -13
  335. package/assets/docs/tree/type-scale.doc.mjs +3 -3
  336. package/assets/docs/tree/uninstall.doc.mjs +109 -0
  337. package/assets/docs/tree/updates.doc.mjs +92 -0
  338. package/assets/docs/tree/upgrading.doc.mjs +6 -6
  339. package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +15 -3
  340. package/assets/docs/tree/verify-packed-template.doc.mjs +3 -3
  341. package/assets/docs/tree/versioning.doc.mjs +14 -9
  342. package/assets/docs/tree/who-answers.doc.mjs +119 -0
  343. package/assets/docs/tree/write-good-templates.doc.mjs +1 -1
  344. package/assets/docs/tree/write-the-template-file.doc.mjs +7 -6
  345. package/assets/docs/use-a-theme.doc.mjs +2 -2
  346. package/assets/docs/use-integrations.doc.mjs +120 -0
  347. package/assets/docs/working-with-ai.doc.mjs +70 -13
  348. package/assets/templates/themes/butter/butterTheme.ts +4 -2
  349. package/assets/templates/themes/stone/stoneTheme.ts +4 -2
  350. package/assets/templates/themes/y2k/y2kTheme.ts +4 -2
  351. package/assets/theme.template.ts +1 -1
  352. package/authoring/codemod/codemod.doc.mjs +7 -3
  353. package/authoring/config/config.doc.mjs +9 -6
  354. package/authoring/config/type.ts +4 -2
  355. package/authoring/debug/parse.d.mts +5 -5
  356. package/authoring/discover/discover.doc.mjs +7 -1
  357. package/authoring/doctypes/_schema.d.mts +8 -3
  358. package/authoring/doctypes/_schema.mjs +1 -0
  359. package/authoring/doctypes/base/graph-fields.doc.mjs +6 -4
  360. package/authoring/doctypes/command/command.doc.mjs +20 -12
  361. package/authoring/doctypes/command/type.ts +14 -0
  362. package/authoring/doctypes/component/component.doc.mjs +30 -27
  363. package/authoring/doctypes/component/type.ts +21 -8
  364. package/authoring/doctypes/enum/enum.doc.mjs +7 -4
  365. package/authoring/doctypes/function/function.doc.mjs +9 -8
  366. package/authoring/doctypes/hook/hook.doc.mjs +5 -4
  367. package/authoring/doctypes/hook/type.ts +4 -0
  368. package/authoring/doctypes/namespace/namespace.doc.mjs +7 -6
  369. package/authoring/doctypes/reference/reference.doc.mjs +12 -11
  370. package/authoring/doctypes/schema/schema.doc.mjs +10 -10
  371. package/authoring/doctypes/template/integration-template.doc.d.mts +11 -0
  372. package/authoring/doctypes/template/integration-template.doc.mjs +49 -0
  373. package/authoring/doctypes/template/parse.d.mts +6 -2
  374. package/authoring/doctypes/template/parse.mjs +3 -1
  375. package/authoring/doctypes/template/template.doc.mjs +3 -3
  376. package/authoring/doctypes/template/type.ts +22 -0
  377. package/authoring/doctypes/theme/theme.doc.mjs +2 -2
  378. package/authoring/gap-report/gap-report.doc.mjs +2 -2
  379. package/authoring/identity/identity.doc.mjs +3 -3
  380. package/authoring/index.d.ts +2 -0
  381. package/authoring/integration/integration.doc.mjs +30 -6
  382. package/authoring/integration/parse.test.mjs +21 -0
  383. package/authoring/integration/schema.d.mts +16 -3
  384. package/authoring/integration/schema.mjs +34 -2
  385. package/authoring/integration/type.ts +13 -1
  386. package/clients/cli/bin/astryx.mjs +11 -65
  387. package/clients/cli/cli-command.test.mjs +166 -0
  388. package/clients/cli/command-result-coverage.test.mjs +6 -5
  389. package/clients/cli/commands/blog.doc.mjs +1 -0
  390. package/clients/cli/commands/build-theme.batch-report.test.mjs +17 -3
  391. package/clients/cli/commands/build-theme.color-scheme.test.mjs +1 -1
  392. package/clients/cli/commands/build-theme.font-warning.test.mjs +3 -3
  393. package/clients/cli/commands/build-theme.path-safety.test.mjs +224 -6
  394. package/clients/cli/commands/build-theme.registry.test.mjs +5 -2
  395. package/clients/cli/commands/build.doc.mjs +1 -0
  396. package/clients/cli/commands/build.exit-codes-doc.test.mjs +4 -0
  397. package/clients/cli/commands/cli-postinstall.test.mjs +49 -1
  398. package/clients/cli/commands/component.doc.mjs +1 -0
  399. package/clients/cli/commands/{component/index.mjs → component.mjs} +37 -37
  400. package/clients/cli/commands/component.test.mjs +6 -6
  401. package/clients/cli/commands/discover.broken-integration.test.mjs +11 -7
  402. package/clients/cli/commands/discover.doc.mjs +9 -1
  403. package/clients/cli/commands/discover.mjs +144 -93
  404. package/clients/cli/commands/discover.no-source.test.mjs +448 -33
  405. package/clients/cli/commands/discover.sources.test.mjs +20 -1
  406. package/clients/cli/commands/docs.doc.mjs +3 -2
  407. package/clients/cli/commands/docs.mjs +50 -20
  408. package/clients/cli/commands/docs.test.mjs +16 -16
  409. package/clients/cli/commands/doctor-integration-components.doc.mjs +19 -4
  410. package/clients/cli/commands/doctor-integration-docs.doc.mjs +20 -5
  411. package/clients/cli/commands/doctor-integration-templates.doc.mjs +1 -0
  412. package/clients/cli/commands/doctor-integration-validate.doc.mjs +22 -2
  413. package/clients/cli/commands/doctor-integration.test.mjs +936 -3
  414. package/clients/cli/commands/doctor.doc.mjs +12 -2
  415. package/clients/cli/commands/doctor.mjs +332 -7
  416. package/clients/cli/commands/doctor.test.mjs +75 -4
  417. package/clients/cli/commands/gap-report.doc.mjs +15 -2
  418. package/clients/cli/commands/gap-report.mjs +24 -2
  419. package/clients/cli/commands/gap-report.test.mjs +79 -0
  420. package/clients/cli/commands/hook.doc.mjs +1 -0
  421. package/clients/cli/commands/{hook/index.mjs → hook.mjs} +22 -22
  422. package/clients/cli/commands/import-hint-correctness.test.mjs +1 -1
  423. package/clients/cli/commands/init.behavior.test.mjs +235 -1
  424. package/clients/cli/commands/init.doc.mjs +55 -10
  425. package/clients/cli/commands/init.flag-help.test.mjs +1 -0
  426. package/clients/cli/commands/init.mjs +2 -2
  427. package/clients/cli/commands/integration-add.doc.mjs +7 -0
  428. package/clients/cli/commands/integration-authoring.test.mjs +49 -3
  429. package/clients/cli/commands/integration-check.doc.mjs +93 -0
  430. package/clients/cli/commands/integration-check.test.mjs +489 -0
  431. package/clients/cli/commands/integration-pack.doc.mjs +8 -1
  432. package/clients/cli/commands/integration-verify.doc.mjs +8 -1
  433. package/clients/cli/commands/integration.doc.mjs +6 -2
  434. package/clients/cli/commands/integration.mjs +119 -7
  435. package/clients/cli/commands/json-contract.test.mjs +11 -2
  436. package/clients/cli/commands/manifest.doc.mjs +2 -1
  437. package/clients/cli/commands/manifest.mjs +67 -0
  438. package/clients/cli/commands/outside-project.test.mjs +874 -0
  439. package/clients/cli/commands/postinstall.doc.mjs +23 -0
  440. package/clients/cli/commands/postinstall.mjs +31 -0
  441. package/clients/cli/commands/search.doc.mjs +1 -0
  442. package/clients/cli/commands/search.mjs +16 -11
  443. package/clients/cli/commands/search.test.mjs +429 -296
  444. package/clients/cli/commands/setup-nudge.test.mjs +180 -2
  445. package/clients/cli/commands/swizzle.doc.mjs +7 -0
  446. package/clients/cli/commands/swizzle.mjs +5 -3
  447. package/clients/cli/commands/swizzle.routing.test.mjs +19 -2
  448. package/clients/cli/commands/template.doc.mjs +1 -0
  449. package/clients/cli/commands/text-json-parity.test.mjs +5 -0
  450. package/clients/cli/commands/theme-add-lifecycle.test.mjs +3 -3
  451. package/clients/cli/commands/theme-add.doc.mjs +8 -1
  452. package/clients/cli/commands/theme-build.doc.mjs +3 -2
  453. package/clients/cli/commands/theme-eject.doc.mjs +1 -0
  454. package/clients/cli/commands/theme-list.behavior.test.mjs +6 -0
  455. package/clients/cli/commands/theme-list.doc.mjs +1 -0
  456. package/clients/cli/commands/theme-palette-generate.doc.mjs +1 -0
  457. package/clients/cli/commands/theme-remove.doc.mjs +7 -0
  458. package/clients/cli/commands/theme-targets.doc.mjs +1 -0
  459. package/clients/cli/commands/theme-template.doc.mjs +1 -0
  460. package/clients/cli/commands/theme-use.doc.mjs +7 -0
  461. package/clients/cli/commands/{build-theme.mjs → theme.mjs} +27 -14
  462. package/clients/cli/commands/upgrade.config-ordering.test.mjs +2 -1
  463. package/clients/cli/commands/upgrade.doc.mjs +26 -3
  464. package/clients/cli/commands/upgrade.file-protection.test.mjs +3 -2
  465. package/clients/cli/commands/upgrade.hook-output.test.mjs +2 -1
  466. package/clients/cli/commands/upgrade.mjs +9 -2
  467. package/clients/cli/commands/upgrade.older-cli.test.mjs +160 -0
  468. package/clients/cli/commands/upgrade.subfolder-path.test.mjs +97 -0
  469. package/clients/cli/index.mjs +156 -100
  470. package/clients/cli/lib/cli-error.mjs +4 -1
  471. package/clients/cli/lib/component-format.mjs +1 -1
  472. package/clients/cli/lib/component-format.test.mjs +1 -1
  473. package/clients/cli/lib/define-command.mjs +14 -9
  474. package/clients/cli/lib/exit-codes.test.mjs +5 -0
  475. package/clients/cli/lib/json-shim.mjs +3 -1
  476. package/clients/cli/lib/manifest.d.ts +9 -0
  477. package/clients/cli/lib/manifest.mjs +17 -1
  478. package/clients/cli/lib/manifest.test.mjs +37 -0
  479. package/clients/cli/lib/welcome-box.mjs +50 -0
  480. package/clients/cli/run.mjs +108 -0
  481. package/foundation/agent-docs/agent-doc-state.d.mts +67 -13
  482. package/foundation/agent-docs/agent-doc-state.mjs +123 -14
  483. package/foundation/agent-docs/agent-docs.d.mts +104 -15
  484. package/foundation/agent-docs/agent-docs.mjs +1028 -114
  485. package/foundation/agent-docs/agent-docs.path-safety.test.mjs +1239 -17
  486. package/foundation/agent-docs/agent-docs.test.mjs +683 -39
  487. package/foundation/agent-docs/computer-agent-text.d.mts +170 -0
  488. package/foundation/agent-docs/computer-agent-text.mjs +750 -0
  489. package/foundation/agent-docs/computer-agent-text.test.mjs +699 -0
  490. package/foundation/config/project.d.mts +35 -3
  491. package/foundation/config/project.mjs +243 -26
  492. package/foundation/config/project.test.mjs +122 -0
  493. package/foundation/config/theme-state.mjs +5 -4
  494. package/foundation/discovery/authoring-self-docs.d.mts +7 -0
  495. package/foundation/discovery/authoring-self-docs.mjs +17 -4
  496. package/foundation/discovery/authoring-self-docs.test.mjs +15 -3
  497. package/foundation/discovery/authoring-surface.d.mts +1 -1
  498. package/foundation/discovery/authoring-surface.mjs +2 -2
  499. package/foundation/discovery/authoring-surface.test.mjs +1 -1
  500. package/foundation/discovery/cli-self-docs.d.mts +2 -1
  501. package/foundation/discovery/cli-self-docs.mjs +22 -4
  502. package/foundation/discovery/cli-self-docs.test.mjs +3 -2
  503. package/foundation/discovery/component-discovery.d.mts +227 -10
  504. package/foundation/discovery/component-discovery.export-names.test.mjs +760 -0
  505. package/foundation/discovery/component-discovery.mjs +940 -57
  506. package/foundation/discovery/component-discovery.test.mjs +96 -0
  507. package/foundation/discovery/component-loader.d.mts +1 -1
  508. package/foundation/discovery/component-replacement.mjs +20 -1
  509. package/foundation/discovery/component-replacement.test.mjs +40 -0
  510. package/foundation/discovery/copied-passages.d.mts +216 -0
  511. package/foundation/discovery/copied-passages.mjs +720 -0
  512. package/foundation/discovery/copied-passages.test.mjs +124 -0
  513. package/foundation/discovery/doc-consistency.d.mts +34 -0
  514. package/foundation/discovery/doc-consistency.mjs +513 -0
  515. package/foundation/discovery/doc-consistency.test.mjs +347 -0
  516. package/foundation/discovery/doc-fluff.d.mts +112 -0
  517. package/foundation/discovery/doc-fluff.mjs +328 -0
  518. package/foundation/discovery/doc-fluff.test.mjs +142 -0
  519. package/foundation/discovery/doc-types.d.mts +157 -0
  520. package/foundation/discovery/doc-types.mjs +1231 -0
  521. package/foundation/discovery/doc-types.test.mjs +744 -0
  522. package/foundation/discovery/docs-discovery.d.mts +88 -0
  523. package/foundation/discovery/docs-discovery.mjs +97 -3
  524. package/foundation/discovery/integration-doc-files.d.mts +22 -0
  525. package/foundation/discovery/integration-doc-files.mjs +231 -0
  526. package/foundation/discovery/integration-doc-files.test.mjs +246 -0
  527. package/foundation/discovery/module-exports.d.mts +68 -0
  528. package/foundation/discovery/module-exports.mjs +609 -31
  529. package/foundation/discovery/module-exports.test.mjs +172 -0
  530. package/foundation/discovery/package-tsconfig.d.mts +14 -0
  531. package/foundation/discovery/package-tsconfig.mjs +228 -0
  532. package/foundation/discovery/package-tsconfig.test.mjs +210 -0
  533. package/foundation/discovery/project-typescript.d.mts +32 -0
  534. package/foundation/discovery/project-typescript.mjs +89 -0
  535. package/foundation/discovery/prop-drift.d.mts +297 -0
  536. package/foundation/discovery/prop-drift.mjs +2428 -0
  537. package/foundation/discovery/prop-drift.test.mjs +2337 -0
  538. package/foundation/discovery/runner-allowlist.d.mts +38 -0
  539. package/foundation/discovery/runner-allowlist.mjs +51 -0
  540. package/foundation/discovery/runner-commands.d.mts +77 -0
  541. package/foundation/discovery/runner-commands.mjs +294 -0
  542. package/foundation/discovery/runner-commands.test.mjs +128 -0
  543. package/foundation/discovery/stray-pointers.d.mts +52 -0
  544. package/foundation/discovery/stray-pointers.mjs +293 -0
  545. package/foundation/discovery/stray-pointers.test.mjs +86 -0
  546. package/foundation/discovery/template-adapter.mjs +92 -6
  547. package/foundation/discovery/template-needs.mjs +6 -46
  548. package/foundation/discovery/theme-discovery.mjs +147 -3
  549. package/foundation/discovery/theme-imports.mjs +8 -4
  550. package/foundation/discovery/theme-imports.test.mjs +1 -1
  551. package/foundation/discovery/type-match.d.mts +53 -0
  552. package/foundation/discovery/type-match.mjs +660 -0
  553. package/foundation/discovery/type-match.test.mjs +169 -0
  554. package/foundation/discovery/type-text.d.mts +68 -0
  555. package/foundation/discovery/type-text.mjs +658 -0
  556. package/foundation/discovery/type-text.test.mjs +196 -0
  557. package/foundation/discovery/unresolved-references.d.mts +62 -0
  558. package/foundation/discovery/unresolved-references.mjs +790 -0
  559. package/foundation/discovery/unresolved-references.test.mjs +567 -0
  560. package/foundation/doc-compiler/doc-loads.test.mjs +84 -10
  561. package/foundation/doc-compiler/inputs.mjs +27 -1
  562. package/foundation/doc-compiler/inputs.test.mjs +0 -1
  563. package/foundation/doc-compiler/links.d.mts +41 -5
  564. package/foundation/doc-compiler/links.mjs +167 -28
  565. package/foundation/doc-compiler/links.test.mjs +97 -4
  566. package/foundation/doc-compiler/read.d.mts +1 -1
  567. package/foundation/doc-compiler/read.mjs +59 -1
  568. package/foundation/doc-compiler/read.test.mjs +11 -2
  569. package/foundation/doc-compiler/split-topic-keywords.test.mjs +3 -2
  570. package/foundation/doc-compiler/tree.d.mts +32 -6
  571. package/foundation/doc-compiler/tree.mjs +107 -44
  572. package/foundation/doc-compiler/tree.test.mjs +14 -7
  573. package/foundation/env/cli-command.d.mts +83 -0
  574. package/foundation/env/cli-command.mjs +152 -0
  575. package/foundation/env/cli-command.test.mjs +129 -0
  576. package/foundation/env/node-version.test.mjs +15 -3
  577. package/foundation/env/package-manager.d.mts +93 -9
  578. package/foundation/env/package-manager.global.test.mjs +66 -0
  579. package/foundation/env/package-manager.mjs +184 -13
  580. package/foundation/env/package-manager.test.mjs +341 -15
  581. package/foundation/env/project-scope.d.mts +9 -0
  582. package/foundation/env/project-scope.mjs +33 -0
  583. package/foundation/env/search-path.d.mts +42 -0
  584. package/foundation/env/search-path.mjs +205 -0
  585. package/foundation/env/search-path.test.mjs +122 -0
  586. package/foundation/env/user-dirs.d.mts +39 -0
  587. package/foundation/env/user-dirs.mjs +93 -0
  588. package/foundation/env/user-dirs.test.mjs +72 -0
  589. package/foundation/env/user-settings.d.mts +46 -0
  590. package/foundation/env/user-settings.mjs +106 -0
  591. package/foundation/env/user-settings.test.mjs +64 -0
  592. package/foundation/fs/file-protection.mjs +15 -1
  593. package/foundation/fs/module-loader.d.mts +2 -1
  594. package/foundation/fs/module-loader.mjs +60 -4
  595. package/foundation/fs/module-loader.stdout.test.mjs +3 -2
  596. package/foundation/fs/module-loader.test.mjs +194 -0
  597. package/foundation/fs/path-compare.d.mts +33 -0
  598. package/foundation/fs/path-compare.mjs +73 -0
  599. package/foundation/fs/paths.d.mts +122 -4
  600. package/foundation/fs/paths.mjs +675 -29
  601. package/foundation/fs/paths.test.mjs +439 -54
  602. package/foundation/fs/read-record.d.mts +18 -0
  603. package/foundation/fs/read-record.mjs +131 -0
  604. package/foundation/fs/read-record.test.mjs +77 -0
  605. package/foundation/integrations/autolink.d.mts +2 -1
  606. package/foundation/integrations/autolink.mjs +4 -13
  607. package/foundation/integrations/cli-peer-version.d.mts +36 -0
  608. package/foundation/integrations/cli-peer-version.mjs +132 -0
  609. package/foundation/integrations/cli-peer-version.test.mjs +354 -0
  610. package/foundation/integrations/computer-integrations.d.mts +81 -0
  611. package/foundation/integrations/computer-integrations.mjs +345 -0
  612. package/foundation/integrations/computer-integrations.test.mjs +292 -0
  613. package/foundation/integrations/contribution-inventory.d.mts +26 -20
  614. package/foundation/integrations/contribution-inventory.mjs +97 -8
  615. package/foundation/integrations/exports-subpaths.d.mts +57 -0
  616. package/foundation/integrations/exports-subpaths.mjs +113 -0
  617. package/foundation/integrations/integration-warnings.mjs +3 -2
  618. package/foundation/integrations/integration-warnings.test.mjs +8 -9
  619. package/foundation/integrations/integrations.d.mts +51 -1
  620. package/foundation/integrations/integrations.mjs +106 -4
  621. package/foundation/integrations/issue.ts +6 -0
  622. package/foundation/integrations/public-imports.d.mts +118 -0
  623. package/foundation/integrations/public-imports.mjs +1769 -0
  624. package/foundation/integrations/public-imports.test.mjs +1793 -0
  625. package/foundation/integrations/validate-contributions.d.mts +117 -0
  626. package/foundation/integrations/validate-contributions.mjs +1209 -14
  627. package/foundation/release/agent-text-worker.d.mts +4 -0
  628. package/foundation/release/agent-text-worker.mjs +52 -0
  629. package/foundation/release/cache.d.mts +310 -0
  630. package/foundation/release/cache.mjs +1216 -0
  631. package/foundation/release/cache.test.mjs +923 -0
  632. package/foundation/release/choose.d.mts +207 -0
  633. package/foundation/release/choose.mjs +677 -0
  634. package/foundation/release/choose.test.mjs +427 -0
  635. package/foundation/release/core-worker.d.mts +4 -0
  636. package/foundation/release/core-worker.mjs +38 -0
  637. package/foundation/release/global-state.d.mts +263 -0
  638. package/foundation/release/global-state.mjs +474 -0
  639. package/foundation/release/global-state.test.mjs +381 -0
  640. package/foundation/release/handoff.d.mts +79 -0
  641. package/foundation/release/handoff.e2e.test.mjs +278 -0
  642. package/foundation/release/handoff.mjs +524 -0
  643. package/foundation/release/lock-lease.d.mts +23 -0
  644. package/foundation/release/lock-lease.mjs +80 -0
  645. package/foundation/release/notices.d.mts +307 -0
  646. package/foundation/release/notices.mjs +707 -0
  647. package/foundation/release/notices.test.mjs +586 -0
  648. package/foundation/release/npm-guard.d.mts +4 -0
  649. package/foundation/release/npm-guard.mjs +126 -0
  650. package/foundation/release/project.d.mts +141 -0
  651. package/foundation/release/project.mjs +466 -0
  652. package/foundation/release/project.test.mjs +288 -0
  653. package/foundation/release/refresh-worker.d.mts +4 -0
  654. package/foundation/release/refresh-worker.mjs +27 -0
  655. package/foundation/release/run-context.d.mts +47 -0
  656. package/foundation/release/run-context.mjs +163 -0
  657. package/foundation/release/update-notice-env.d.mts +28 -0
  658. package/foundation/release/update-notice-env.mjs +39 -0
  659. package/foundation/response/base.d.ts +26 -3
  660. package/foundation/response/error-codes.d.mts +3 -1
  661. package/foundation/response/error-codes.doc.mjs +12 -0
  662. package/foundation/response/error-codes.mjs +17 -0
  663. package/foundation/response/error-codes.test.mjs +2 -0
  664. package/foundation/response/json-contract.test.mjs +7 -2
  665. package/foundation/response/json.mjs +4 -1
  666. package/foundation/response/response-types.doc.mjs +22 -11
  667. package/foundation/response/response-types.doc.test.mjs +46 -0
  668. package/foundation/response/response.doc.mjs +3 -3
  669. package/foundation/route/route.d.mts +285 -0
  670. package/foundation/route/route.mjs +324 -0
  671. package/foundation/route/route.test.mjs +153 -0
  672. package/foundation/runtime/hosted-runtime.test.mjs +403 -0
  673. package/foundation/runtime/locked-versions.d.mts +32 -0
  674. package/foundation/runtime/locked-versions.mjs +65 -0
  675. package/foundation/runtime/package-root.d.mts +91 -0
  676. package/foundation/runtime/package-root.mjs +174 -0
  677. package/foundation/runtime/package-set.d.mts +62 -0
  678. package/foundation/runtime/package-set.mjs +130 -0
  679. package/foundation/runtime/package-set.test.mjs +432 -0
  680. package/foundation/text/string-utils.d.mts +2 -1
  681. package/foundation/text/string-utils.mjs +16 -5
  682. package/foundation/text/suggest-order.d.mts +72 -0
  683. package/foundation/text/suggest-order.mjs +363 -0
  684. package/foundation/text/suggest-order.test.mjs +259 -0
  685. package/foundation/xle/browser-imports.test.mjs +74 -0
  686. package/foundation/xle/registry.mjs +4 -0
  687. package/foundation/xle/registry.test.mjs +28 -0
  688. package/host/door-conformance.test.mjs +274 -0
  689. package/host/index.d.ts +116 -0
  690. package/host/index.mjs +295 -0
  691. package/host/index.test.mjs +302 -0
  692. package/package.json +16 -11
  693. package/scripts/postinstall.mjs +24 -5
  694. package/assets/docs/tree/integrations.doc.mjs +0 -40
@@ -1,12 +1,124 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
2
  import {describe, it, expect, beforeEach, afterEach, vi} from 'vitest';
3
- import * as fs from 'node:fs'; import * as path from 'node:path';
3
+ import * as fs from 'node:fs'; import * as path from 'node:path'; import * as os from 'node:os';
4
4
  import {init} from '../init.mjs'; import {remove} from './remove.mjs'; import {logger} from '../../logger.mjs';
5
5
  let dir;
6
- beforeEach(() => { dir = fs.mkdtempSync(path.join(process.cwd(), '.astryx-rm-')); fs.writeFileSync(path.join(dir,'package.json'),JSON.stringify({name:'c',version:'1.0.0'})); });
6
+ beforeEach(() => { dir = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-rm-')); fs.writeFileSync(path.join(dir,'package.json'),JSON.stringify({name:'c',version:'1.0.0'})); });
7
7
  afterEach(() => { fs.rmSync(dir,{recursive:true,force:true}); logger.setSilent(true); });
8
8
  describe('init.remove says whether anything was removed', () => {
9
9
  it('false on clean', async () => { const r=await remove({cwd:dir}); expect(r.type).toBe('init.remove'); expect(r.data.removed).toBe(false); });
10
10
  it('true then false', async () => { await init({features:['agents']},{cwd:dir}); expect(fs.readFileSync(path.join(dir,'AGENTS.md'),'utf8')).toContain('<!-- ASTRYX:START -->'); const a=await remove({cwd:dir}); expect(a.data.removed).toBe(true); const b=await remove({cwd:dir}); expect(b.data.removed).toBe(false); });
11
11
  it('human line', async () => { const l=[]; const s=vi.spyOn(console,'log').mockImplementation((...a)=>l.push(a.join(' '))); logger.setSilent(false); try{await remove({cwd:dir});}finally{logger.setSilent(true);s.mockRestore();} expect(l.join('\n')).toContain('Nothing to remove'); });
12
12
  });
13
+
14
+ describe('init.remove acts where init writes, the project root, and in the folder it runs in', () => {
15
+ /** @type {string} */
16
+ let project;
17
+ beforeEach(() => {
18
+ project = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-rm-root-'));
19
+ fs.writeFileSync(
20
+ path.join(project, 'package.json'),
21
+ JSON.stringify({name: 'app', dependencies: {'@astryxdesign/core': '1.0.0'}}),
22
+ );
23
+ fs.mkdirSync(path.join(project, 'src', 'app'), {recursive: true});
24
+ });
25
+ afterEach(() => { fs.rmSync(project, {recursive: true, force: true}); });
26
+ const block = '<!-- ASTRYX:START -->\nAstryx v0.7.0 · old\n<!-- ASTRYX:END -->\n';
27
+
28
+ it('removes from a subfolder what init wrote at the project root', async () => {
29
+ const sub = path.join(project, 'src', 'app');
30
+ const written = await init({features: ['agents']}, {cwd: sub});
31
+ expect(written.data.docsWritten).toEqual(['../../AGENTS.md', '../../GEMINI.md']);
32
+
33
+ const r = await remove({cwd: sub});
34
+ expect(r).toEqual({type: 'init.remove', data: {removed: true}});
35
+ // Both files held only what init wrote, so they are deleted.
36
+ expect(fs.existsSync(path.join(project, 'AGENTS.md'))).toBe(false);
37
+ expect(fs.existsSync(path.join(project, 'GEMINI.md'))).toBe(false);
38
+ expect((await remove({cwd: sub})).data.removed).toBe(false);
39
+ });
40
+
41
+ it('removes a block an earlier version left in the subfolder, and keeps the files\' own text', async () => {
42
+ const sub = path.join(project, 'src', 'app');
43
+ fs.writeFileSync(path.join(project, 'AGENTS.md'), `# Agents\n\nRoot rules.\n\n${block}`);
44
+ fs.writeFileSync(path.join(sub, 'AGENTS.md'), `# App\n\nApp rules.\n\n${block}`);
45
+
46
+ expect((await remove({cwd: sub})).data.removed).toBe(true);
47
+ expect(fs.readFileSync(path.join(project, 'AGENTS.md'), 'utf8').trimEnd()).toBe('# Agents\n\nRoot rules.');
48
+ expect(fs.readFileSync(path.join(sub, 'AGENTS.md'), 'utf8').trimEnd()).toBe('# App\n\nApp rules.');
49
+ });
50
+
51
+ it('run at the project root, leaves a subfolder alone', async () => {
52
+ const sub = path.join(project, 'src', 'app');
53
+ fs.writeFileSync(path.join(project, 'AGENTS.md'), `# Agents\n\n${block}`);
54
+ fs.writeFileSync(path.join(sub, 'AGENTS.md'), `# App\n\n${block}`);
55
+
56
+ expect((await remove({cwd: project})).data.removed).toBe(true);
57
+ expect(fs.readFileSync(path.join(sub, 'AGENTS.md'), 'utf8')).toContain('<!-- ASTRYX:START -->');
58
+ });
59
+
60
+ it('below the root, leaves a root agent file linked outside the project with a note, and removes the rest (0.7.x exited 0)', async () => {
61
+ // A workspace member whose CLAUDE.md links to the monorepo's.
62
+ const monorepo = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-rm-mono-'));
63
+ try {
64
+ const web = path.join(monorepo, 'apps', 'web');
65
+ const sub = path.join(web, 'src');
66
+ fs.mkdirSync(sub, {recursive: true});
67
+ fs.writeFileSync(
68
+ path.join(web, 'package.json'),
69
+ JSON.stringify({name: 'web', dependencies: {'@astryxdesign/core': '1.0.0'}}),
70
+ );
71
+ fs.writeFileSync(path.join(monorepo, 'CLAUDE.md'), `# Shared\n\n${block}`);
72
+ fs.symlinkSync('../../CLAUDE.md', path.join(web, 'CLAUDE.md'));
73
+ fs.writeFileSync(path.join(web, 'AGENTS.md'), `# Web\n\n${block}`);
74
+ fs.writeFileSync(path.join(sub, 'AGENTS.md'), `# Src\n\n${block}`);
75
+ const warned = [];
76
+ const warn = vi.spyOn(console, 'error').mockImplementation((...a) => warned.push(a.join(' ')));
77
+ logger.setSilent(false);
78
+ let r;
79
+ try {
80
+ r = await remove({cwd: sub});
81
+ } finally {
82
+ logger.setSilent(true);
83
+ warn.mockRestore();
84
+ }
85
+ expect(r).toEqual({type: 'init.remove', data: {removed: true}});
86
+ expect(fs.readFileSync(path.join(web, 'AGENTS.md'), 'utf8').trimEnd()).toBe('# Web');
87
+ expect(fs.readFileSync(path.join(sub, 'AGENTS.md'), 'utf8').trimEnd()).toBe('# Src');
88
+ // The file outside the project keeps its block.
89
+ expect(fs.readFileSync(path.join(monorepo, 'CLAUDE.md'), 'utf8')).toContain('<!-- ASTRYX:START -->');
90
+ expect(warned.join('\n')).toMatch(/Note: Left as it is: \.\.\/CLAUDE\.md \(/u);
91
+ } finally {
92
+ fs.rmSync(monorepo, {recursive: true, force: true});
93
+ }
94
+ });
95
+
96
+ it('at the root, a root agent file linked outside the project still refuses the run', async () => {
97
+ const outside = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-rm-outside-'));
98
+ try {
99
+ fs.writeFileSync(path.join(outside, 'CLAUDE.md'), `# Shared\n\n${block}`);
100
+ fs.symlinkSync(path.join(outside, 'CLAUDE.md'), path.join(project, 'CLAUDE.md'));
101
+ fs.writeFileSync(path.join(project, 'AGENTS.md'), `# Agents\n\n${block}`);
102
+ await expect(remove({cwd: project})).rejects.toMatchObject({code: 'ERR_PATH_TRAVERSAL'});
103
+ expect(fs.readFileSync(path.join(project, 'AGENTS.md'), 'utf8')).toContain('<!-- ASTRYX:START -->');
104
+ } finally {
105
+ fs.rmSync(outside, {recursive: true, force: true});
106
+ }
107
+ });
108
+
109
+ it('a link in the subfolder that escapes it refuses the run before anything is written', async () => {
110
+ const sub = path.join(project, 'src', 'app');
111
+ const outside = fs.mkdtempSync(path.join(os.tmpdir(), 'astryx-rm-outside-'));
112
+ try {
113
+ fs.writeFileSync(path.join(project, 'AGENTS.md'), `# Agents\n\n${block}`);
114
+ fs.writeFileSync(path.join(outside, 'CLAUDE.md'), `# Shared\n\n${block}`);
115
+ fs.symlinkSync(path.join(outside, 'CLAUDE.md'), path.join(sub, 'CLAUDE.md'));
116
+
117
+ await expect(remove({cwd: sub})).rejects.toMatchObject({code: 'ERR_PATH_TRAVERSAL'});
118
+ expect(fs.readFileSync(path.join(project, 'AGENTS.md'), 'utf8')).toContain('<!-- ASTRYX:START -->');
119
+ expect(fs.readFileSync(path.join(outside, 'CLAUDE.md'), 'utf8')).toContain('<!-- ASTRYX:START -->');
120
+ } finally {
121
+ fs.rmSync(outside, {recursive: true, force: true});
122
+ }
123
+ });
124
+ });
@@ -8,7 +8,7 @@
8
8
  * installed built themes and imports their production and font stylesheets. The
9
9
  * app imports that module once and passes its default to <Theme>.
10
10
  *
11
- * @param {string} invocation install-aware CLI invocation stem (e.g. `npx astryx`, `pnpm exec astryx`, or `npx @astryxdesign/cli` for one-off runs)
11
+ * @param {string} invocation install-aware CLI invocation stem (e.g. `pnpm exec astryx`, `yarn astryx`, or `npx @astryxdesign/cli` for one-off runs)
12
12
  * @returns {string[]} ordered list of human-facing lines
13
13
  */
14
14
  export function getNextSteps(invocation: string): string[];
@@ -4,10 +4,12 @@
4
4
  * @file init.run leaf — non-interactive project setup + feature installer.
5
5
  *
6
6
  * `run(options, ctx)` is the default / `--features` / `--all` path behind
7
- * `astryx init`. It installs the AGENTS.md/CLAUDE.md agent-docs cheat sheet,
8
- * points at the theme + page-building workflows, and (optionally) scaffolds a
9
- * starter template — with NO prompts, so it behaves identically for humans,
10
- * agents, CI, and piped I/O. It performs the side effects and returns an
7
+ * `astryx init`. It installs the agent-docs cheat sheet at the project root so
8
+ * every agent reads it (AGENTS.md, a copy in the project's Claude Code file, and
9
+ * a GEMINI.md import), points at the theme + page-building workflows, and
10
+ * (optionally) scaffolds a starter template — with NO prompts, so it behaves
11
+ * identically for humans, agents, CI, and piped I/O. It performs the side
12
+ * effects and returns an
11
13
  * `init.run` receipt. Hard errors (unknown feature/template) throw AstryxError
12
14
  * with a stable code. Human output is emitted through the shared `logger`
13
15
  * (silent by default) so the CLI keeps its exact plain output and a
@@ -19,9 +21,13 @@ import * as fs from 'node:fs';
19
21
  import {CLI_ROOT} from '../../../foundation/fs/paths.mjs';
20
22
  import {PathSafetyError, assertWithin} from '../../../foundation/fs/path-safety.mjs';
21
23
  import {getCliInvocation} from '../../../foundation/env/package-manager.mjs';
24
+ import {isOutsideProject} from '../../../foundation/env/project-scope.mjs';
22
25
  import {
26
+ AgentDocsHomeError,
27
+ assertAgentDocsTarget,
23
28
  installAgentDocs,
24
29
  renderAgentDocsBlock,
30
+ agentDocsTargetDir,
25
31
  } from '../../../foundation/agent-docs/agent-docs.mjs';
26
32
  import {themeTemplate} from '../../theme/template/template.mjs';
27
33
  import {listTemplates} from '../../template/template.mjs';
@@ -39,7 +45,7 @@ const VALID_AGENTS = ['claude', 'cursor', 'codex', 'hermes', 'muse', 'all'];
39
45
  * installed built themes and imports their production and font stylesheets. The
40
46
  * app imports that module once and passes its default to <Theme>.
41
47
  *
42
- * @param {string} invocation install-aware CLI invocation stem (e.g. `npx astryx`, `pnpm exec astryx`, or `npx @astryxdesign/cli` for one-off runs)
48
+ * @param {string} invocation install-aware CLI invocation stem (e.g. `pnpm exec astryx`, `yarn astryx`, or `npx @astryxdesign/cli` for one-off runs)
43
49
  * @returns {string[]} ordered list of human-facing lines
44
50
  */
45
51
  export function getNextSteps(invocation) {
@@ -50,11 +56,11 @@ export function getNextSteps(invocation) {
50
56
  ` (run \`${invocation} doctor\` to verify)`,
51
57
  " 2. Import base styles: import '@astryxdesign/core/reset.css'",
52
58
  " and import '@astryxdesign/core/astryx.css'",
53
- " 3. Import components: import { Button } from '@astryxdesign/core'",
59
+ " 3. Import components: import { Button } from '@astryxdesign/core/Button'",
54
60
  ' 4. Add and wire a theme:',
55
61
  ' npm install @astryxdesign/theme-neutral',
56
62
  ` ${invocation} theme add neutral`,
57
- " import { Theme } from '@astryxdesign/core'",
63
+ " import { Theme } from '@astryxdesign/core/theme'",
58
64
  " import { themes, defaultThemeSlug } from './astryx-themes'",
59
65
  ' <Theme theme={themes[defaultThemeSlug]}>...</Theme>',
60
66
  ` Extend an imported theme to customize it. Run \`${invocation} theme eject <slug>\` only to fork source.`,
@@ -63,6 +69,24 @@ export function getNextSteps(invocation) {
63
69
  ];
64
70
  }
65
71
 
72
+ /**
73
+ * Whether two paths name one folder, links resolved.
74
+ * @param {string} a
75
+ * @param {string} b
76
+ * @returns {boolean}
77
+ */
78
+ function sameFolder(a, b) {
79
+ /** @param {string} p */
80
+ const real = p => {
81
+ try {
82
+ return fs.realpathSync(p);
83
+ } catch {
84
+ return path.resolve(p);
85
+ }
86
+ };
87
+ return real(a) === real(b);
88
+ }
89
+
66
90
  /**
67
91
  * Install the agent-docs block; records the outcome (or soft error) on `data`.
68
92
  * Mirrors the CLI's historical soft-error policy: a PathSafetyError surfaces its
@@ -91,14 +115,46 @@ async function applyAgents(cwd, options, invocation, data) {
91
115
  ? options.agentDocsPath
92
116
  : [options.agentDocsPath]
93
117
  : undefined;
94
- const renderedBlock = await renderAgentDocsBlock(cwd);
95
- const written = installAgentDocs(cwd, {
118
+ // The docs belong at the project root, not in the subfolder init ran in;
119
+ // with no project above, they go where init ran, as before. Explicit
120
+ // --agent-docs-path entries stay relative to where init ran.
121
+ const root = agentDocsTargetDir(cwd);
122
+ const targetDir = paths ? cwd : root;
123
+ // With explicit paths, installAgentDocs checks each file's own folder.
124
+ if (!paths) assertAgentDocsTarget(targetDir);
125
+ const renderedBlock = await renderAgentDocsBlock(root);
126
+ /** @type {string[]} */
127
+ const skipped = [];
128
+ const files = installAgentDocs(targetDir, {
96
129
  agent: options.agent,
97
130
  paths,
98
131
  renderedBlock,
132
+ onSkipped: (rel, reason) => skipped.push(`${rel} (${reason})`),
133
+ // Below the project root, 0.7.1 wrote in the folder init ran in, so a
134
+ // project-root file that escapes the project is skipped and reported,
135
+ // not refused.
136
+ refuseEscapes: sameFolder(targetDir, cwd),
99
137
  });
138
+ // Each file is reported from where init ran, as before.
139
+ const written =
140
+ path.resolve(targetDir) === path.resolve(cwd)
141
+ ? files
142
+ : files.map(rel =>
143
+ path.relative(cwd, path.join(targetDir, rel)).split(path.sep).join('/'),
144
+ );
100
145
  data.docsWritten = written;
101
146
  logger.log(`[ok] AI agent docs installed -> ${written.join(', ')}`);
147
+ // A file 0.7.1 did not write (GEMINI.md, or a project-root file when init
148
+ // runs below the root) that resolves outside the project is left as it
149
+ // is, reported in docsError with one note; the exit code stays 0. A file
150
+ // 0.7.1 wrote whose path escapes the project still fails the run (below).
151
+ if (skipped.length > 0) {
152
+ const message =
153
+ `Left as ${skipped.length === 1 ? 'it is' : 'they are'}: ${skipped.join('; ')}. ` +
154
+ 'To have init write the Astryx block there, make it a regular file inside the project, or copy the block from AGENTS.md by hand.';
155
+ logger.warn(`Note: ${message}`);
156
+ data.docsError = {kind: 'install-failed', message};
157
+ }
102
158
  } catch (err) {
103
159
  // PathSafetyError carries a precise, user-actionable message — surface it
104
160
  // (and flag exit 1) instead of the generic "could not install" warning so
@@ -108,6 +164,14 @@ async function applyAgents(cwd, options, invocation, data) {
108
164
  data.docsError = {kind: 'path-safety', message: err.message};
109
165
  return;
110
166
  }
167
+ // The home folder, and a settings folder in it outside a project, are
168
+ // refused: nothing is written, the reason is printed as a note, and the
169
+ // exit code stays 0.
170
+ if (err instanceof AgentDocsHomeError) {
171
+ logger.warn(`Note: ${err.message}`);
172
+ data.docsError = {kind: 'install-failed', message: err.message};
173
+ return;
174
+ }
111
175
  logger.error(
112
176
  `Could not install agent docs. Try again with \`${invocation} init --features agents\`.`,
113
177
  );
@@ -140,8 +204,11 @@ function applyTheme(cwd, invocation, data) {
140
204
  data.themeTemplate = 'failed';
141
205
  logger.error('Could not write the theme template.');
142
206
  }
207
+ // Outside a project there is no app to add a theme to (spec AST-081 FR3).
143
208
  logger.log(
144
- ` Edit the blank template for a new theme, or run \`${invocation} theme add <slug>\` to import an installed built theme (\`${invocation} theme list\` to browse). Extend an imported theme for ordinary customization; use \`${invocation} theme eject <slug>\` only to fork source.`,
209
+ isOutsideProject(cwd)
210
+ ? ` Edit the blank template for a new theme, or run \`${invocation} theme eject <slug>\` to start from the source of a built theme (\`${invocation} theme list\` to browse).`
211
+ : ` Edit the blank template for a new theme, or run \`${invocation} theme add <slug>\` to import an installed built theme (\`${invocation} theme list\` to browse). Extend an imported theme for ordinary customization; use \`${invocation} theme eject <slug>\` only to fork source.`,
145
212
  );
146
213
  }
147
214
 
@@ -276,7 +343,10 @@ export async function run(options = {}, {cwd = process.cwd()} = {}) {
276
343
  }
277
344
 
278
345
  // No flags: TTY-free default — install the AI agent cheat sheet with NO
279
- // prompts, then print the getting-started guidance.
346
+ // prompts, then print the getting-started guidance. Outside a project the
347
+ // steps would set up a project the folder does not have, so they are left
348
+ // out (spec AST-081 FR3).
349
+ const outside = isOutsideProject(cwd);
280
350
  /** @type {import('../init.type.mjs').InitRunData} */
281
351
  const data = {
282
352
  mode: 'default',
@@ -288,13 +358,17 @@ export async function run(options = {}, {cwd = process.cwd()} = {}) {
288
358
  themeTemplatePath: null,
289
359
  template: null,
290
360
  templatePath: null,
291
- nextSteps: true,
361
+ nextSteps: !outside,
292
362
  };
293
363
  await applyAgents(cwd, options, invocation, data);
294
364
  logger.log('');
295
365
  logger.log(
296
366
  ` Tip: \`${invocation} init --all\` also points you to the theme and page-building workflows.`,
297
367
  );
368
+ if (outside) {
369
+ logger.log('');
370
+ return {type: 'init.run', data};
371
+ }
298
372
  for (const line of getNextSteps(invocation)) logger.log(line);
299
373
  return {type: 'init.run', data};
300
374
  }
@@ -0,0 +1,14 @@
1
+ // @generated by scripts/sync-api-types.mjs from the JSDoc in api/**/*.mjs.
2
+ // DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
3
+
4
+ /**
5
+ * Write or remove the computer-level agent text and return an `init.user`
6
+ * receipt. Progress is emitted through the shared `logger` (silent by
7
+ * default); warnings go to its warn channel.
8
+ *
9
+ * @param {{remove?: boolean}} [options]
10
+ * @returns {Promise<import('../init.type.mjs').InitUserResponse>}
11
+ */
12
+ export function user({ remove }?: {
13
+ remove?: boolean;
14
+ }): Promise<import("../init.type.mjs").InitUserResponse>;
@@ -0,0 +1,87 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file init.user leaf — the `--user` path behind `astryx init`.
5
+ *
6
+ * `user(options)` writes, refreshes, or (with `remove`) removes the
7
+ * computer-level agent text: one skill file in the home folder, linked into
8
+ * the skill folders agents read (spec AST-081 FR7, FR8). It records the
9
+ * setting and the links it made in the settings file, so the global copy,
10
+ * which keeps the text current in the background, does the same, or, after
11
+ * `remove`, writes nothing. The adapter does every file access. It works in any
12
+ * folder and never touches a project's agent docs. A file without the Astryx
13
+ * markers, one a person edited, or a symlinked one is kept as it is. When no
14
+ * `astryx` on the computer's search path runs the Astryx CLI, the text is
15
+ * still written and the receipt warns that the CLI should be installed for
16
+ * the computer.
17
+ *
18
+ * @input `remove`; the home folder, search path, and settings of
19
+ * the computer
20
+ * @output an `init.user` receipt
21
+ * @position packages/cli/api/init — dispatched by init.mjs
22
+ */
23
+
24
+ import {
25
+ COMPUTER_COMMAND,
26
+ COMPUTER_INSTALL_STEP,
27
+ applyComputerAgentText,
28
+ displayHomePath,
29
+ } from '../_adapter.mjs';
30
+ import {logger} from '../../logger.mjs';
31
+
32
+ /**
33
+ * @param {string} file
34
+ * @param {string} home
35
+ * @returns {string}
36
+ */
37
+ const display = (file, home) => displayHomePath(file, home);
38
+
39
+ /** @type {Record<import('../init.type.mjs').InitUserAction, string>} */
40
+ const ACTION_LINES = {
41
+ written: '[ok] Wrote',
42
+ linked: '[ok] Linked',
43
+ unchanged: '[ok] Already current:',
44
+ removed: '[ok] Removed',
45
+ absent: '[ok] Nothing to remove:',
46
+ kept: '[skip] Kept',
47
+ };
48
+
49
+ /** @type {Record<'edited' | 'not-astryx', string>} */
50
+ const KEPT_REASONS = {
51
+ edited: '(edited since Astryx wrote it; delete it to get the current text):',
52
+ 'not-astryx': '(not created by Astryx):',
53
+ };
54
+
55
+ /**
56
+ * Write or remove the computer-level agent text and return an `init.user`
57
+ * receipt. Progress is emitted through the shared `logger` (silent by
58
+ * default); warnings go to its warn channel.
59
+ *
60
+ * @param {{remove?: boolean}} [options]
61
+ * @returns {Promise<import('../init.type.mjs').InitUserResponse>}
62
+ */
63
+ export async function user({remove = false} = {}) {
64
+ const {home, command, files, cliOnSearchPath} = applyComputerAgentText({
65
+ remove,
66
+ });
67
+ /** @type {string[]} */
68
+ const warnings = [];
69
+ // A host's door is its own command; only the plain `astryx` needs a copy of
70
+ // the CLI installed for the computer.
71
+ if (!remove && command === COMPUTER_COMMAND && !cliOnSearchPath) {
72
+ warnings.push(
73
+ `No \`${COMPUTER_COMMAND}\` command on this computer runs the Astryx CLI, so agents cannot run the commands this text names. Install the CLI for the computer: ${COMPUTER_INSTALL_STEP}`,
74
+ );
75
+ }
76
+ for (const file of files) {
77
+ const reason = file.reason ? ` ${KEPT_REASONS[file.reason]}` : '';
78
+ logger.log(
79
+ `${ACTION_LINES[file.action]}${reason || (file.action === 'kept' ? ':' : '')} ${display(file.path, home)}`,
80
+ );
81
+ }
82
+ for (const warning of warnings) logger.warn(`[warn] ${warning}`);
83
+ return {
84
+ type: 'init.user',
85
+ data: {files, command, warnings},
86
+ };
87
+ }
@@ -0,0 +1,232 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * @file `init({user})` and `astryx init --user`: the `init.user` receipt, the
5
+ * install warning, the refused combinations, and that it touches nothing in
6
+ * the folder it runs in. Every test uses a temporary home folder and search
7
+ * path.
8
+ */
9
+
10
+ import * as fs from 'node:fs';
11
+ import * as os from 'node:os';
12
+ import * as path from 'node:path';
13
+ import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest';
14
+ import {init} from '../init.mjs';
15
+ import {user} from './user.mjs';
16
+ import {runWithCliDoor} from '../../../foundation/env/cli-command.mjs';
17
+ import {renderComputerAgentText} from '../../../foundation/agent-docs/computer-agent-text.mjs';
18
+ import {runCli} from '../../../test-utils/run-cli.mjs';
19
+ import {settingsFile} from '../../../foundation/env/user-settings.mjs';
20
+ import {readGlobalState} from '../../../foundation/release/global-state.mjs';
21
+ import {cliVersion} from '../../../foundation/release/run-context.mjs';
22
+
23
+ /** @type {string[]} */
24
+ const dirs = [];
25
+ /** @param {string} label */
26
+ function tmp(label) {
27
+ const dir = fs.mkdtempSync(
28
+ path.join(os.tmpdir(), `astryx-init-user-${label}-`),
29
+ );
30
+ dirs.push(dir);
31
+ return dir;
32
+ }
33
+
34
+ let home = '';
35
+ let emptyPath = '';
36
+ beforeEach(() => {
37
+ home = tmp('home');
38
+ emptyPath = tmp('path');
39
+ vi.stubEnv('HOME', home);
40
+ vi.stubEnv('USERPROFILE', home);
41
+ vi.stubEnv('PATH', emptyPath);
42
+ // The settings file and the cache live under this home folder too.
43
+ vi.stubEnv('XDG_CONFIG_HOME', path.join(home, '.config'));
44
+ vi.stubEnv('XDG_CACHE_HOME', path.join(home, '.cache'));
45
+ vi.stubEnv('APPDATA', path.join(home, 'AppData', 'Roaming'));
46
+ vi.stubEnv('LOCALAPPDATA', path.join(home, 'AppData', 'Local'));
47
+ });
48
+
49
+ /** The settings file under the temporary home folder. */
50
+ const settings = () => {
51
+ try {
52
+ return JSON.parse(fs.readFileSync(settingsFile(), 'utf8'));
53
+ } catch {
54
+ return null;
55
+ }
56
+ };
57
+ afterEach(() => {
58
+ vi.unstubAllEnvs();
59
+ while (dirs.length) {
60
+ fs.rmSync(/** @type {string} */ (dirs.pop()), {
61
+ recursive: true,
62
+ force: true,
63
+ });
64
+ }
65
+ });
66
+
67
+ const FILE = path.join('.agents', 'skills', 'astryx', 'SKILL.md');
68
+ const CLAUDE = path.join('.claude', 'skills', 'astryx');
69
+
70
+ /** Put a linked Astryx CLI first on the search path. */
71
+ function installCliOnPath() {
72
+ const prefix = tmp('prefix');
73
+ const pkg = path.join(prefix, 'lib', 'node_modules', '@astryxdesign', 'cli');
74
+ fs.mkdirSync(pkg, {recursive: true});
75
+ fs.writeFileSync(
76
+ path.join(pkg, 'package.json'),
77
+ '{"name":"@astryxdesign/cli"}',
78
+ );
79
+ fs.writeFileSync(path.join(pkg, 'bin.mjs'), '#!/usr/bin/env node\n');
80
+ fs.chmodSync(path.join(pkg, 'bin.mjs'), 0o755);
81
+ fs.mkdirSync(path.join(prefix, 'bin'));
82
+ fs.symlinkSync(path.join(pkg, 'bin.mjs'), path.join(prefix, 'bin', 'astryx'));
83
+ vi.stubEnv('PATH', path.join(prefix, 'bin'));
84
+ }
85
+
86
+ describe('init({user: true})', () => {
87
+ it('writes the file, links Claude Code to it, and returns the init.user receipt, warning when no astryx runs the CLI', async () => {
88
+ const r = await init({user: true}, {cwd: tmp('cwd')});
89
+ expect(r.type).toBe('init.user');
90
+ if (r.type !== 'init.user') return;
91
+ expect(r.data.files).toEqual([
92
+ {path: path.join(home, FILE), action: 'written'},
93
+ {path: path.join(home, CLAUDE), action: 'linked'},
94
+ ]);
95
+ expect(r.data.command).toBe('astryx');
96
+ expect(r.data.warnings).toHaveLength(1);
97
+ expect(r.data.warnings[0]).toContain('npm i -g @astryxdesign/cli');
98
+ expect(fs.readFileSync(path.join(home, FILE), 'utf8')).toBe(
99
+ renderComputerAgentText({command: 'astryx'}),
100
+ );
101
+ expect(fs.readFileSync(path.join(home, CLAUDE, 'SKILL.md'), 'utf8')).toBe(
102
+ renderComputerAgentText({command: 'astryx'}),
103
+ );
104
+ });
105
+
106
+ it.skipIf(process.platform === 'win32')(
107
+ 'does not warn when the astryx on the search path is the Astryx CLI',
108
+ async () => {
109
+ installCliOnPath();
110
+ const r = await user();
111
+ expect(r.data.warnings).toEqual([]);
112
+ },
113
+ );
114
+
115
+ it("names the host's door and does not ask for a global install", async () => {
116
+ const r = await runWithCliDoor('acme astryx', () => user());
117
+ expect(r.data.command).toBe('acme astryx');
118
+ expect(r.data.warnings).toEqual([]);
119
+ expect(fs.readFileSync(path.join(home, FILE), 'utf8')).toContain(
120
+ "`acme astryx search '<what you need>'`",
121
+ );
122
+ });
123
+
124
+ it('works inside a project and writes nothing in it', async () => {
125
+ const dir = tmp('project');
126
+ fs.writeFileSync(
127
+ path.join(dir, 'package.json'),
128
+ '{"name":"app","dependencies":{"@astryxdesign/core":"*"}}',
129
+ );
130
+ const before = fs.readdirSync(dir).sort();
131
+ const r = await init({user: true}, {cwd: dir});
132
+ expect(r.type).toBe('init.user');
133
+ expect(fs.readdirSync(dir).sort()).toEqual(before);
134
+ });
135
+
136
+ it('records the links it made, and the release that wrote the text', async () => {
137
+ await init({user: true});
138
+ expect(settings()).toEqual({
139
+ computerAgentText: {install: true, links: [path.join(home, CLAUDE)]},
140
+ });
141
+ expect(readGlobalState().agentText).toEqual({writtenBy: cliVersion()});
142
+ });
143
+
144
+ it('keeps every other setting when it records the choice', async () => {
145
+ fs.mkdirSync(path.dirname(settingsFile()), {recursive: true});
146
+ fs.writeFileSync(settingsFile(), '{"other": 1}');
147
+ await init({user: true, removeAgents: true});
148
+ expect(settings()).toEqual({
149
+ other: 1,
150
+ computerAgentText: {install: false, links: []},
151
+ });
152
+ });
153
+
154
+ it('with removeAgents removes the file and the links it made, and records that the text is not wanted', async () => {
155
+ await init({user: true});
156
+ const r = await init({user: true, removeAgents: true});
157
+ expect(r.type).toBe('init.user');
158
+ if (r.type !== 'init.user') return;
159
+ expect(r.data.files).toEqual([
160
+ {path: path.join(home, FILE), action: 'removed'},
161
+ {path: path.join(home, CLAUDE), action: 'removed'},
162
+ ]);
163
+ expect(r.data.warnings).toEqual([]);
164
+ expect(fs.existsSync(path.join(home, FILE))).toBe(false);
165
+ expect(fs.existsSync(path.join(home, CLAUDE))).toBe(false);
166
+ expect(settings()).toEqual({
167
+ computerAgentText: {install: false, links: []},
168
+ });
169
+ });
170
+
171
+ it.each([
172
+ [{features: 'agents'}, '--features'],
173
+ [{all: true}, '--all'],
174
+ [{agentDocsPath: 'X.md'}, '--agent-docs-path'],
175
+ [{agent: 'claude'}, '--agent'],
176
+ [{removeAgents: true, agent: 'claude'}, '--agent'],
177
+ ])(
178
+ 'refuses %o with ERR_INVALID_ARGUMENT and writes nothing',
179
+ async (extra, flag) => {
180
+ await expect(init({user: true, ...extra})).rejects.toMatchObject({
181
+ code: 'ERR_INVALID_ARGUMENT',
182
+ message: expect.stringContaining(flag),
183
+ });
184
+ expect(fs.existsSync(path.join(home, '.agents'))).toBe(false);
185
+ expect(fs.existsSync(path.join(home, '.claude'))).toBe(false);
186
+ },
187
+ );
188
+ });
189
+
190
+ describe('astryx init --user', () => {
191
+ it('prints the file, the link, and the install warning in text mode', async () => {
192
+ const r = await runCli(['init', '--user'], tmp('cwd'));
193
+ expect(r.status).toBe(0);
194
+ expect(r.stdout).toContain(`Wrote ~${path.sep}${FILE}`);
195
+ expect(r.stdout).toContain(`Linked ~${path.sep}${CLAUDE}`);
196
+ expect(r.stderr).toContain('npm i -g @astryxdesign/cli');
197
+ });
198
+
199
+ it('emits the init.user envelope under --json', async () => {
200
+ const r = await runCli(['--json', 'init', '--user'], tmp('cwd'));
201
+ expect(r.status).toBe(0);
202
+ const env = JSON.parse(r.stdout);
203
+ expect(env.type).toBe('init.user');
204
+ expect(
205
+ env.data.files.map((/** @type {{action: string}} */ f) => f.action),
206
+ ).toEqual(['written', 'linked']);
207
+ expect(env.data.command).toBe('astryx');
208
+ });
209
+
210
+ it('names a file a person edited as kept, and why', async () => {
211
+ await runCli(['init', '--user'], tmp('cwd'));
212
+ const file = path.join(home, FILE);
213
+ fs.appendFileSync(file, 'my note\n');
214
+ const r = await runCli(['init', '--user'], tmp('cwd'));
215
+ expect(r.status).toBe(0);
216
+ expect(r.stdout).toContain(
217
+ `Kept (edited since Astryx wrote it; delete it to get the current text): ~${path.sep}${FILE}`,
218
+ );
219
+ expect(fs.readFileSync(file, 'utf8')).toMatch(/my note\n$/u);
220
+ });
221
+
222
+ it('exits 1 with ERR_INVALID_ARGUMENT when combined with --all or --agent', async () => {
223
+ for (const extra of [['--all'], ['--agent', 'claude']]) {
224
+ const r = await runCli(
225
+ ['--json', 'init', '--user', ...extra],
226
+ tmp('cwd'),
227
+ );
228
+ expect(r.status).toBe(1);
229
+ expect(JSON.parse(r.stdout).code).toBe('ERR_INVALID_ARGUMENT');
230
+ }
231
+ });
232
+ });