@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
@@ -16,9 +16,24 @@ import {describe, it, expect, beforeEach, afterEach} from 'vitest';
16
16
  import * as fs from 'node:fs';
17
17
  import * as path from 'node:path';
18
18
  import * as os from 'node:os';
19
- import {isAstryxInitialized} from '../../../foundation/agent-docs/agent-docs.mjs';
19
+ import {fileURLToPath} from 'node:url';
20
+ import {
21
+ isAstryxInitialized,
22
+ isAstryxPromptInReach,
23
+ } from '../../../foundation/agent-docs/agent-docs.mjs';
20
24
  import {runCli} from '../../../test-utils/run-cli.mjs';
21
25
 
26
+ /** This source checkout: the tests run the CLI from it. */
27
+ const REPO = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../../..');
28
+
29
+ /** Is `dir` inside a git repository? The temp folder normally is not. */
30
+ function insideRepository(dir) {
31
+ for (let d = path.resolve(dir); ; d = path.dirname(d)) {
32
+ if (fs.existsSync(path.join(d, '.git'))) return true;
33
+ if (path.dirname(d) === d) return false;
34
+ }
35
+ }
36
+
22
37
  const MARKER = '<!-- ASTRYX:START -->';
23
38
  const NUDGE = /finish setup and install the Astryx agent prompt/;
24
39
 
@@ -61,8 +76,44 @@ describe('isAstryxInitialized — centralized setup check (one place)', () => {
61
76
  });
62
77
  });
63
78
 
79
+ // Agents read the agent docs between the repository root and the folder they
80
+ // work in, so a monorepo initialized at its root is set up in every package.
81
+ describe('isAstryxPromptInReach — up to the repository root', () => {
82
+ it('finds init\'s block at the repository root from a workspace package', () => {
83
+ fs.mkdirSync(path.join(tmp, '.git'));
84
+ write('AGENTS.md', MARKER);
85
+ write('packages/app/package.json', '{"name":"app"}');
86
+ expect(isAstryxPromptInReach(path.join(tmp, 'packages/app'))).toBe(true);
87
+ });
88
+
89
+ it('treats a worktree .git file as the repository root', () => {
90
+ write('.git', 'gitdir: /elsewhere\n');
91
+ write('CLAUDE.md', MARKER);
92
+ write('packages/app/package.json', '{"name":"app"}');
93
+ expect(isAstryxPromptInReach(path.join(tmp, 'packages/app'))).toBe(true);
94
+ });
95
+
96
+ it('does not look above the repository root', () => {
97
+ write('AGENTS.md', MARKER);
98
+ fs.mkdirSync(path.join(tmp, 'repo/.git'), {recursive: true});
99
+ write('repo/pkg/package.json', '{"name":"pkg"}');
100
+ expect(isAstryxPromptInReach(path.join(tmp, 'repo/pkg'))).toBe(false);
101
+ });
102
+
103
+ it.skipIf(insideRepository(os.tmpdir()))(
104
+ 'checks only the folder itself outside a repository',
105
+ () => {
106
+ write('AGENTS.md', MARKER);
107
+ write('pkg/package.json', '{"name":"pkg"}');
108
+ expect(isAstryxPromptInReach(tmp)).toBe(true);
109
+ expect(isAstryxPromptInReach(path.join(tmp, 'pkg'))).toBe(false);
110
+ },
111
+ );
112
+ });
113
+
64
114
  describe('enforcement layer 3 — per-command setup nudge', () => {
65
- const asProject = () => write('package.json', '{"name":"t"}');
115
+ const asProject = () =>
116
+ write('package.json', '{"name":"t","dependencies":{"@astryxdesign/core":"*"}}');
66
117
 
67
118
  it('nudges on stderr after a command when not set up', async () => {
68
119
  asProject();
@@ -70,6 +121,41 @@ describe('enforcement layer 3 — per-command setup nudge', () => {
70
121
  expect(r.stderr).toMatch(NUDGE);
71
122
  });
72
123
 
124
+ it.each([
125
+ ['in the home folder', ''],
126
+ ['at the top of a settings folder in it', '.dotfiles'],
127
+ ])('stays quiet %s, where init writes no agent docs', async (_label, rel) => {
128
+ const saved = {HOME: process.env.HOME, USERPROFILE: process.env.USERPROFILE};
129
+ process.env.HOME = tmp;
130
+ process.env.USERPROFILE = tmp;
131
+ try {
132
+ write(path.join(rel, 'package.json'), '{"name":"t"}');
133
+ const r = await runCli(['docs', 'tokens'], path.join(tmp, rel));
134
+ expect(r.stderr).not.toMatch(NUDGE);
135
+ } finally {
136
+ for (const [key, value] of Object.entries(saved)) {
137
+ if (value === undefined) delete process.env[key];
138
+ else process.env[key] = value;
139
+ }
140
+ }
141
+ });
142
+
143
+ it("in a project with only Core of another release, names adding the CLI at Core's version first, as the daily hint does", async () => {
144
+ write(
145
+ 'package.json',
146
+ '{"name":"t","dependencies":{"@astryxdesign/core":"0.0.1"}}',
147
+ );
148
+ write(
149
+ 'node_modules/@astryxdesign/core/package.json',
150
+ '{"name":"@astryxdesign/core","version":"0.0.1"}',
151
+ );
152
+ write('package-lock.json', '{}');
153
+ const r = await runCli(['docs', 'tokens'], tmp);
154
+ expect(r.stderr).toContain(
155
+ 'Next step: run `npm install @astryxdesign/cli@0.0.1`, then `npx astryx init` to finish setup and install the Astryx agent prompt.',
156
+ );
157
+ });
158
+
73
159
  it('is suppressed in --json (machine mode stays clean), stdout valid JSON', async () => {
74
160
  asProject();
75
161
  const r = await runCli(['--json', 'docs', 'tokens'], tmp);
@@ -89,14 +175,106 @@ describe('enforcement layer 3 — per-command setup nudge', () => {
89
175
  expect((await runCli(['docs', 'tokens'], tmp)).stderr).not.toMatch(NUDGE);
90
176
  });
91
177
 
178
+ it('is quiet outside a project (a package.json that declares no Astryx package)', async () => {
179
+ write('package.json', '{"name":"t"}');
180
+ expect((await runCli(['docs', 'tokens'], tmp)).stderr).not.toMatch(NUDGE);
181
+ });
182
+
92
183
  it('is quiet in an integration package, which is not an app', async () => {
93
184
  asProject();
94
185
  write('astryx.integration.mjs', 'export default {};\n');
95
186
  expect((await runCli(['docs', 'tokens'], tmp)).stderr).not.toMatch(NUDGE);
96
187
  });
97
188
 
189
+ it('is quiet in a nested package once init has written the docs at the project root', async () => {
190
+ // init run in functions/ writes at the root that declares Astryx.
191
+ write('package.json', JSON.stringify({name: 'app', dependencies: {'@astryxdesign/core': '0.7.0'}}));
192
+ write('AGENTS.md', MARKER);
193
+ write('functions/package.json', '{"name":"functions"}');
194
+ const r = await runCli(['docs', 'tokens'], path.join(tmp, 'functions'));
195
+ expect(r.stderr).not.toMatch(NUDGE);
196
+ });
197
+
198
+ it('nudges in a nested package while the project root has no docs', async () => {
199
+ write('package.json', JSON.stringify({name: 'app', dependencies: {'@astryxdesign/core': '0.7.0'}}));
200
+ write('functions/package.json', '{"name":"functions"}');
201
+ const r = await runCli(['docs', 'tokens'], path.join(tmp, 'functions'));
202
+ expect(r.stderr).toMatch(NUDGE);
203
+ });
204
+
205
+ // A nested package.json that is not the project's (spec AST-040 FR9): the
206
+ // reminder sends the person to the project's folder, never adds the CLI to
207
+ // the nested package.json (spec AST-081 FR2).
208
+ it("in a nested package whose package.json is not the project's, says to run from the project's folder", async () => {
209
+ write('package.json', JSON.stringify({name: 'app', dependencies: {'@astryxdesign/core': '0.7.0'}}));
210
+ write('package-lock.json', '{}');
211
+ write('functions/package.json', '{"name":"functions"}');
212
+ const r = await runCli(['docs', 'tokens'], path.join(tmp, 'functions'));
213
+ // Only `cd` comes first, so the app has no CLI of its own yet: the
214
+ // command is the scoped package at the answering release, never the bare
215
+ // `astryx` name a package runner would fetch from the registry.
216
+ const {version} = JSON.parse(fs.readFileSync(new URL('../../../package.json', import.meta.url), 'utf8'));
217
+ expect(r.stderr.trim()).toBe(
218
+ `Next step: run \`cd ${tmp}\`, then \`npx @astryxdesign/cli@${version} init\` to finish setup and install the Astryx agent prompt.`,
219
+ );
220
+ });
221
+
222
+ // Inside a git repository the project-root check above adds nothing to the
223
+ // walk up to the repository root: the same outcomes as without it.
224
+ it('inside a repository, is quiet in a nested package once init has written the docs at the project root', async () => {
225
+ fs.mkdirSync(path.join(tmp, '.git'));
226
+ write('package.json', JSON.stringify({name: 'app', dependencies: {'@astryxdesign/core': '0.7.0'}}));
227
+ write('AGENTS.md', MARKER);
228
+ write('functions/package.json', '{"name":"functions"}');
229
+ const r = await runCli(['docs', 'tokens'], path.join(tmp, 'functions'));
230
+ expect(r.stderr).not.toMatch(NUDGE);
231
+ });
232
+
233
+ it('inside a repository, still nudges when the only docs lie above the repository root', async () => {
234
+ write('package.json', JSON.stringify({name: 'outer', dependencies: {'@astryxdesign/core': '0.7.0'}}));
235
+ write('AGENTS.md', MARKER);
236
+ fs.mkdirSync(path.join(tmp, 'repo', '.git'), {recursive: true});
237
+ write('repo/package.json', '{"name":"repo"}');
238
+ // It declares Astryx: a folder that declares nothing is outside a
239
+ // project, where no reminder prints (spec AST-081 FR3).
240
+ write(
241
+ 'repo/functions/package.json',
242
+ JSON.stringify({name: 'functions', dependencies: {'@astryxdesign/core': '0.7.0'}}),
243
+ );
244
+ const r = await runCli(['docs', 'tokens'], path.join(tmp, 'repo', 'functions'));
245
+ expect(r.stderr).toMatch(NUDGE);
246
+ });
247
+
98
248
  it('does not nudge for the installer command itself', async () => {
99
249
  asProject();
100
250
  expect((await runCli(['init'], tmp)).stderr).not.toMatch(NUDGE);
101
251
  });
252
+
253
+ it('is quiet in a workspace package when init\'s block is at the repository root', async () => {
254
+ fs.mkdirSync(path.join(tmp, '.git'));
255
+ write('AGENTS.md', MARKER);
256
+ write('packages/app/package.json', '{"name":"app","dependencies":{"@astryxdesign/core":"*"}}');
257
+ const r = await runCli(['docs', 'tokens'], path.join(tmp, 'packages/app'));
258
+ expect(r.stderr).not.toMatch(NUDGE);
259
+ });
260
+
261
+ it('still nudges in a workspace package with no block up to the repository root', async () => {
262
+ fs.mkdirSync(path.join(tmp, '.git'));
263
+ write('AGENTS.md', 'project notes, no astryx block');
264
+ write('packages/app/package.json', '{"name":"app","dependencies":{"@astryxdesign/core":"*"}}');
265
+ const r = await runCli(['docs', 'tokens'], path.join(tmp, 'packages/app'));
266
+ expect(r.stderr).toMatch(NUDGE);
267
+ });
268
+
269
+ // The tests run the CLI from this checkout, so a project inside it is the
270
+ // contributor flow. The same project in the temp folder nudges (above).
271
+ it('is quiet inside the source checkout the CLI runs from', async () => {
272
+ const inside = fs.mkdtempSync(path.join(REPO, '.astryx-setup-nudge-'));
273
+ try {
274
+ fs.writeFileSync(path.join(inside, 'package.json'), '{"name":"t","dependencies":{"@astryxdesign/core":"*"}}');
275
+ expect((await runCli(['docs', 'tokens'], inside)).stderr).not.toMatch(NUDGE);
276
+ } finally {
277
+ fs.rmSync(inside, {recursive: true, force: true});
278
+ }
279
+ });
102
280
  });
@@ -57,6 +57,7 @@ export const doc = {
57
57
  cli: 'astryx swizzle SideNav --package @astryxdesign/core',
58
58
  },
59
59
  ],
60
+ outsideProject: 'refuse',
60
61
  exitCodes: [
61
62
  {code: 0, when: 'success'},
62
63
  {
@@ -65,4 +66,10 @@ export const doc = {
65
66
  },
66
67
  ],
67
68
  related: ['component', 'discover', 'upgrade'],
69
+ notes: [
70
+ {
71
+ type: 'prose',
72
+ text: 'In global mode: outside a project, swizzle writes nothing and stops with `ERR_CORE_NOT_FOUND`: "Could not find @astryxdesign/core package. Make sure you are inside the design system monorepo or have @astryxdesign/core installed." In a project, the project\'s version of Astryx runs it, whichever astryx command you start. See {@link namespace:global} and {@link generic:problems}.',
73
+ },
74
+ ],
68
75
  };
@@ -91,14 +91,16 @@ export function registerSwizzle(program) {
91
91
  ),
92
92
  ];
93
93
 
94
- // StyleX build requirement — swizzled StyleX source renders unstyled with
95
- // no error unless the consumer's build runs a StyleX compiler.
94
+ // StyleX build requirement — swizzled StyleX source throws on import
95
+ // ("Unexpected 'stylex.create' call at runtime") unless the consumer's
96
+ // build runs a StyleX compiler, so the page does not render at all.
96
97
  if (usesStyleX) {
97
98
  out.push(
98
99
  text(
99
100
  [
100
101
  `${WARN} These components use StyleX and require a StyleX compiler in your build.`,
101
- ' Without one they render unstyled (no error). See setup per framework:',
102
+ ` Without one, importing them throws "Unexpected 'stylex.create' call at runtime"`,
103
+ ' and the page does not render. See setup per framework:',
102
104
  ` ${run} docs styling`,
103
105
  ' Next.js note: the StyleX Babel plugin disables SWC and breaks next/font -',
104
106
  ' use an SWC-based StyleX transform instead (covered in the guide).',
@@ -15,8 +15,16 @@ import {describe, it, expect, beforeEach, afterEach} from 'vitest';
15
15
  import * as fs from 'node:fs';
16
16
  import * as path from 'node:path';
17
17
  import * as os from 'node:os';
18
+ import {createRequire} from 'node:module';
19
+ import {fileURLToPath} from 'node:url';
18
20
  import {runCli} from '../../../test-utils/run-cli.mjs';
19
21
 
22
+ /** The StyleX runtime that Core ships with, resolved the way Core resolves it. */
23
+ const CORE_PACKAGE = path.resolve(
24
+ path.dirname(fileURLToPath(import.meta.url)),
25
+ '../../../../core/package.json',
26
+ );
27
+
20
28
  /**
21
29
  * Build a fake @astryxdesign/core under <project>/node_modules with a single
22
30
  * swizzleable Button component (bare `Button.tsx`, no doc).
@@ -303,10 +311,19 @@ describe('swizzle — StyleX build setup note (#3373)', () => {
303
311
  const humanResult = await runCli(['swizzle', 'Styled', '-f'], project);
304
312
  expect(humanResult.code).toBe(0);
305
313
  expect(humanResult.stdout).toMatch(/StyleX compiler/i);
306
- expect(humanResult.stdout).toMatch(/unstyled/i);
314
+ expect(humanResult.stdout).toMatch(/importing them throws "Unexpected 'stylex\.create' call at runtime"/);
315
+ expect(humanResult.stdout).not.toMatch(/unstyled/i);
307
316
  expect(humanResult.stdout).toMatch(/next\/font/i);
308
317
  // The fixture has no `astryx` bin installed, so the note names the scoped package.
309
- expect(humanResult.stdout).toMatch(/@astryxdesign\/cli docs styling/);
318
+ expect(humanResult.stdout).toMatch(/@astryxdesign\/cli@\S+ docs styling/);
319
+ });
320
+
321
+ // The note quotes what StyleX does without a compiler; this keeps it true.
322
+ it('matches what the StyleX runtime does when styles are not compiled', () => {
323
+ const stylex = createRequire(CORE_PACKAGE)('@stylexjs/stylex');
324
+ expect(() => stylex.create({root: {color: 'red'}})).toThrow(
325
+ /Unexpected 'stylex\.create' call at runtime/,
326
+ );
310
327
  });
311
328
 
312
329
  it('does not print the StyleX note for components without StyleX', async () => {
@@ -102,6 +102,7 @@ export const doc = {
102
102
  cli: 'astryx template --cdn public/demo.html',
103
103
  },
104
104
  ],
105
+ outsideProject: 'same',
105
106
  exitCodes: [
106
107
  {
107
108
  code: 0,
@@ -341,6 +341,11 @@ const CASES = [
341
341
  args: ['doctor', 'integration', 'validate'],
342
342
  skipFieldChecks: true,
343
343
  },
344
+ {
345
+ name: 'integration check',
346
+ args: ['integration', 'check'],
347
+ skipFieldChecks: true,
348
+ },
344
349
  {
345
350
  name: 'doctor integration components',
346
351
  args: ['doctor', 'integration', 'components'],
@@ -20,7 +20,7 @@ beforeEach(() => {
20
20
  JSON.stringify({
21
21
  name: 'app',
22
22
  private: true,
23
- dependencies: {'@acme/themes': '^1.0.0'},
23
+ dependencies: {'@acme/themes': '^1.0.0', '@astryxdesign/core': '*'},
24
24
  }),
25
25
  );
26
26
  const packageDir = path.join(tmpDir, 'node_modules', '@acme', 'themes');
@@ -99,7 +99,7 @@ describe('theme add cleanup lifecycle', () => {
99
99
  change: {action: 'add', slug: 'ocean', changed: true},
100
100
  },
101
101
  });
102
- expect(payload.meta).toBeUndefined();
102
+ expect(Object.keys(payload.meta)).toEqual(['answeredBy']);
103
103
  });
104
104
 
105
105
  it('keeps --import as an accepted no-op with the same response', async () => {
@@ -151,7 +151,7 @@ describe('theme add cleanup lifecycle', () => {
151
151
  expect(result.status, result.stderr).toBe(0);
152
152
  const payload = JSON.parse(result.stdout);
153
153
  expect(payload).toMatchObject({type: 'theme.list'});
154
- expect(payload.meta).toBeUndefined();
154
+ expect(Object.keys(payload.meta)).toEqual(['answeredBy']);
155
155
  expect(payload.data.some(theme => theme.slug === 'broken')).toBe(false);
156
156
  expect(Object.keys(payload.data[0]).sort()).toEqual([
157
157
  'description',
@@ -11,7 +11,7 @@ export const doc = {
11
11
  summary:
12
12
  "Add a built theme to the app. To copy a theme's source as an editable fork, run `theme eject <slug> [path] [--overwrite]`.",
13
13
  description:
14
- "Records the theme in the generated app module and imports its built module and stylesheets. --import remains accepted as a compatibility no-op. To copy a theme's source as an editable fork, run `theme eject <slug> [path] [--overwrite]`. A bare command or --list delegates to themeListCopySources(), whose released JSON bytes stay unchanged while its text names the current add and eject commands; use --package when owners share a slug.",
14
+ "Records the theme in the generated app module and imports its built module and stylesheets. --import remains accepted as a compatibility no-op. To copy a theme's source as an editable fork, run `theme eject <slug> [path] [--overwrite]`. A bare command or --list delegates to themeListCopySources(), whose released JSON bytes stay unchanged while its text names the current add and eject commands; use --package when owners share a slug. Outside a project, a bare command or --list lists the available themes as in a new project; given a slug it writes nothing and fails, naming the app project it needs (ERR_THEME_INVALID), or with ERR_UNKNOWN_THEME when no theme has that slug.",
15
15
  fn: 'themeAdd',
16
16
  args: [{name: 'slug', param: 'slug', required: false}],
17
17
  options: [
@@ -38,6 +38,7 @@ export const doc = {
38
38
  cli: 'astryx theme add ocean --package @acme/themes',
39
39
  },
40
40
  ],
41
+ outsideProject: 'own',
41
42
  exitCodes: [
42
43
  {code: 0, when: 'the theme is listed or added'},
43
44
  {
@@ -46,4 +47,10 @@ export const doc = {
46
47
  },
47
48
  ],
48
49
  related: ['theme list', 'theme remove', 'theme use', 'theme eject'],
50
+ notes: [
51
+ {
52
+ type: 'prose',
53
+ text: 'In global mode: outside a project, theme add with no slug lists the themes available, as in a new project; with a slug it writes nothing and stops with `ERR_THEME_INVALID`: "No package.json found. Run this command inside an app project." In a project, the project\'s version of Astryx runs it, whichever astryx command you start. See {@link namespace:global} and {@link generic:problems}.',
54
+ },
55
+ ],
49
56
  };
@@ -30,7 +30,7 @@ export const doc = {
30
30
  {
31
31
  flag: '--family',
32
32
  description:
33
- 'Build the selected extension family into one keyed CSS, JS, and declaration set. Requires --family-key; refused with --out or --watch (ERR_THEME_INVALID)',
33
+ 'Build the selected extension family into one keyed CSS, JS, and declaration set, written beside the family root theme file, which must be inside the working directory to build (ERR_PATH_TRAVERSAL); --check only reads, so it compares outputs anywhere. Requires --family-key; refused with --out or --watch (ERR_THEME_INVALID)',
34
34
  },
35
35
  {
36
36
  flag: '--family-key <key>',
@@ -41,7 +41,7 @@ export const doc = {
41
41
  flag: '-o, --out <path>',
42
42
  param: 'options.out',
43
43
  description:
44
- 'Output CSS file path; the .js and .d.ts are written beside it (default: <theme name>.css next to the source). A relative path must stay inside the working directory. Single theme only: refused with more than one file or with --family (ERR_THEME_INVALID)',
44
+ 'Output CSS file path, relative or absolute; the .js and .d.ts are written beside it (default: <theme name>.css next to the source). A build writes only inside the working directory, the defaults beside the source too (ERR_PATH_TRAVERSAL); --check writes nothing, so it compares outputs anywhere and refuses only a relative path that escapes the working directory. Single theme only: refused with more than one file or with --family (ERR_THEME_INVALID)',
45
45
  },
46
46
  {
47
47
  flag: '--icons-specifier <specifier>',
@@ -83,6 +83,7 @@ export const doc = {
83
83
  cli: 'astryx theme build ./src/themes/ocean.ts --icons-specifier ./icons.mjs',
84
84
  },
85
85
  ],
86
+ outsideProject: 'same',
86
87
  exitCodes: [
87
88
  {
88
89
  code: 0,
@@ -36,6 +36,7 @@ export const doc = {
36
36
  cli: 'astryx theme eject ocean src/themes/my-ocean --package @acme/themes',
37
37
  },
38
38
  ],
39
+ outsideProject: 'same',
39
40
  exitCodes: [
40
41
  {code: 0, when: 'all source and descriptor files are written'},
41
42
  {
@@ -24,6 +24,12 @@ afterEach(() => {
24
24
 
25
25
  describe('astryx theme list', () => {
26
26
  it('lists bundled themes and points at import, eject, and discover', async () => {
27
+ // Inside a project; outside one there is no app to import into
28
+ // (outside-project.test.mjs).
29
+ fs.writeFileSync(
30
+ path.join(tmpDir, 'package.json'),
31
+ '{"name":"app","dependencies":{"@astryxdesign/core":"*"}}',
32
+ );
27
33
  const {status, stdout} = await runCli(['theme', 'list'], {cwd: tmpDir});
28
34
 
29
35
  expect(status).toBe(0);
@@ -33,6 +33,7 @@ export const doc = {
33
33
  cli: 'astryx theme list --package @acme/themes',
34
34
  },
35
35
  ],
36
+ outsideProject: 'same',
36
37
  exitCodes: [
37
38
  {code: 0, when: 'success'},
38
39
  {
@@ -57,6 +57,7 @@ export const doc = {
57
57
  cli: 'astryx theme palette generate themes/ocean/palette.config.json --out themes/ocean/tokens/ocean.palette.ts --preview themes/ocean/tokens/ocean.palette.html',
58
58
  },
59
59
  ],
60
+ outsideProject: 'same',
60
61
  exitCodes: [
61
62
  {
62
63
  code: 0,
@@ -14,9 +14,16 @@ export const doc = {
14
14
  fn: 'themeRemove',
15
15
  args: [{name: 'slug', param: 'slug', required: true}],
16
16
  examples: [{label: 'Remove an added theme', cli: 'astryx theme remove ocean'}],
17
+ outsideProject: 'refuse',
17
18
  exitCodes: [
18
19
  {code: 0, when: 'the module is regenerated without the theme'},
19
20
  {code: 1, when: 'the theme is not added, is the default, or state is invalid'},
20
21
  ],
21
22
  related: ['theme add', 'theme use', 'theme list'],
23
+ notes: [
24
+ {
25
+ type: 'prose',
26
+ text: "In global mode: outside a project, theme remove writes nothing and stops with `ERR_THEME_INVALID`: \"No package.json found. Run this command inside an app project.\" In a project, the project's version of Astryx runs it, whichever astryx command you start. See {@link namespace:global} and {@link generic:problems}.",
27
+ },
28
+ ],
22
29
  };
@@ -30,6 +30,7 @@ export const doc = {
30
30
  {label: 'Search keys', cli: 'astryx theme targets thumb'},
31
31
  {label: 'For a lint or an audit script', cli: 'astryx --json theme targets'},
32
32
  ],
33
+ outsideProject: 'same',
33
34
  exitCodes: [
34
35
  {code: 0, when: 'success'},
35
36
  {code: 1, when: 'a filter matches no target, or core cannot be resolved'},
@@ -35,6 +35,7 @@ export const doc = {
35
35
  {label: 'Write it at the project root', cli: 'astryx theme template'},
36
36
  {label: 'Somewhere else', cli: 'astryx theme template src/themes/starter.ts'},
37
37
  ],
38
+ outsideProject: 'same',
38
39
  exitCodes: [
39
40
  {code: 0, when: 'success, including when an existing file was left untouched'},
40
41
  {code: 1, when: 'the target path escapes the project, or the file cannot be written'},
@@ -14,9 +14,16 @@ export const doc = {
14
14
  fn: 'themeUse',
15
15
  args: [{name: 'slug', param: 'slug', required: true}],
16
16
  examples: [{label: 'Choose the default theme', cli: 'astryx theme use ocean'}],
17
+ outsideProject: 'refuse',
17
18
  exitCodes: [
18
19
  {code: 0, when: 'the module is regenerated with the selected default'},
19
20
  {code: 1, when: 'the theme is not added or state is invalid'},
20
21
  ],
21
22
  related: ['theme add', 'theme remove', 'theme list'],
23
+ notes: [
24
+ {
25
+ type: 'prose',
26
+ text: "In global mode: outside a project, theme use writes nothing and stops with `ERR_THEME_INVALID`: \"No package.json found. Run this command inside an app project.\" In a project, the project's version of Astryx runs it, whichever astryx command you start. See {@link namespace:global} and {@link generic:problems}.",
27
+ },
28
+ ],
22
29
  };
@@ -34,6 +34,7 @@ import * as path from 'node:path';
34
34
  import {fileURLToPath} from 'node:url';
35
35
  import {spawn} from 'node:child_process';
36
36
  import {getCliInvocation} from '../../../foundation/env/package-manager.mjs';
37
+ import {isOutsideProject} from '../../../foundation/env/project-scope.mjs';
37
38
  import {jsonOut} from '../../../foundation/response/json.mjs';
38
39
  import {
39
40
  emit,
@@ -95,7 +96,7 @@ import {doc as themePaletteGenerateFn} from '../../../api/theme/themePaletteGene
95
96
 
96
97
  /**
97
98
  * Path to this CLI's real entry (clients/cli/bin/astryx.mjs), resolved from
98
- * this module's location (clients/cli/commands/build-theme.mjs → ../bin/
99
+ * this module's location (clients/cli/commands/theme.mjs → ../bin/
99
100
  * astryx.mjs). Used to re-invoke `theme build` as a child process in watch mode.
100
101
  */
101
102
  function resolveCliBin() {
@@ -224,10 +225,13 @@ async function runThemeBuildWatch(entries, options) {
224
225
  emit(text(watchingLine));
225
226
 
226
227
  await new Promise((/** @type {(value?: void) => void} */ resolve) => {
228
+ let stopping = false;
227
229
  /** @param {NodeJS.Signals} signal */
228
230
  const stop = async signal => {
229
- process.removeListener('SIGINT', stop);
230
- process.removeListener('SIGTERM', stop);
231
+ // A second interrupt while stopping (a terminal's Ctrl-C and the same
232
+ // signal passed on by the copy that started the run) changes nothing.
233
+ if (stopping) return;
234
+ stopping = true;
231
235
  stopped = true;
232
236
  queued.clear();
233
237
  for (const d of debounces.values()) clearTimeout(d);
@@ -237,10 +241,12 @@ async function runThemeBuildWatch(entries, options) {
237
241
  rebuildChild?.kill(signal);
238
242
  await rebuildDone;
239
243
  emit(text('\nStopped watching.'));
244
+ process.removeListener('SIGINT', stop);
245
+ process.removeListener('SIGTERM', stop);
240
246
  resolve();
241
247
  };
242
- process.once('SIGINT', stop);
243
- process.once('SIGTERM', stop);
248
+ process.on('SIGINT', stop);
249
+ process.on('SIGTERM', stop);
244
250
  });
245
251
  }
246
252
 
@@ -283,14 +289,24 @@ function printThemeAddList(themes) {
283
289
  return theme.description ? [head, theme.description] : head;
284
290
  }),
285
291
  ),
286
- text(
287
- `Import one: ${run} theme add <slug> [--package <package>]\n` +
288
- `Fork source: ${run} theme eject <slug> [target-path]`,
289
- ),
292
+ text(themeNextSteps(run)),
290
293
  more,
291
294
  );
292
295
  }
293
296
 
297
+ /**
298
+ * What to do with a listed theme. Outside a project there is no app to add one
299
+ * to, so only the fork step is named (spec AST-081 FR3).
300
+ * @param {string} run the CLI invocation
301
+ * @returns {string}
302
+ */
303
+ function themeNextSteps(run) {
304
+ const fork = `Fork source: ${run} theme eject <slug> [target-path]`;
305
+ return isOutsideProject()
306
+ ? fork
307
+ : `Import one: ${run} theme add <slug> [--package <package>]\n${fork}`;
308
+ }
309
+
294
310
  /**
295
311
  * Print every field in the `theme.list` entries. Duplicate slugs stay separate
296
312
  * because each record names its owner.
@@ -325,10 +341,7 @@ function printThemeList(themes, unmigratedCopies = []) {
325
341
  default: (/** @type {boolean} */ value) => (value ? 'yes' : 'no'),
326
342
  },
327
343
  }),
328
- text(
329
- `Import one: ${run} theme add <slug> [--package <package>]\n` +
330
- `Fork source: ${run} theme eject <slug> [target-path]`,
331
- ),
344
+ text(themeNextSteps(run)),
332
345
  more,
333
346
  );
334
347
  }
@@ -397,7 +410,7 @@ function printThemeApp(result) {
397
410
  emit(
398
411
  text('Wire the generated module once in the app root:'),
399
412
  code(
400
- "import {Theme} from '@astryxdesign/core';\n" +
413
+ "import {Theme} from '@astryxdesign/core/theme';\n" +
401
414
  `import {themes, defaultThemeSlug} from './${stem}';\n\n` +
402
415
  '<Theme theme={themes[defaultThemeSlug]}>\n <App />\n</Theme>',
403
416
  ),
@@ -30,6 +30,7 @@ import {Command} from 'commander';
30
30
  import {registerUpgrade} from './upgrade.mjs';
31
31
  import {Project} from '../../../foundation/config/project.mjs';
32
32
  import {generateCompressedIndex} from '../../../foundation/agent-docs/agent-docs.mjs';
33
+ import {CLI_VERSION} from '../../../test-utils/release-fixtures.mjs';
33
34
 
34
35
  let tmpDir;
35
36
  let originalCwd;
@@ -189,7 +190,7 @@ describe('upgrade — core codemods run before config load', () => {
189
190
  expect(out).toMatch(/fails strict validation/i);
190
191
  // The fixture has no `astryx` bin installed, so the command names the scoped package.
191
192
  expect(out).toContain(
192
- '@astryxdesign/cli upgrade --from 0.1.2 --codemod migrate-layout-components-to-experimental --apply',
193
+ `@astryxdesign/cli@${CLI_VERSION} upgrade --from 0.1.2 --codemod migrate-layout-components-to-experimental --apply`,
193
194
  );
194
195
  expect(exitCode).not.toBe(1);
195
196
  });