@astryxdesign/cli 0.7.2 → 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 (373) hide show
  1. package/README.md +104 -71
  2. package/api/build/_adapter.d.mts +58 -0
  3. package/api/build/_adapter.mjs +127 -3
  4. package/api/build/blocks/blocks.d.mts +92 -0
  5. package/api/build/blocks/blocks.mjs +528 -0
  6. package/api/build/blocks/blocks.test.mjs +238 -0
  7. package/api/build/blocks/blocks.weights.json +1 -0
  8. package/api/build/build.mjs +25 -6
  9. package/api/build/build.type.mjs +1 -1
  10. package/api/build/kit/answer.d.mts +18 -0
  11. package/api/build/kit/answer.mjs +195 -0
  12. package/api/build/kit/answer.test.mjs +108 -0
  13. package/api/build/kit/kind.d.mts +25 -0
  14. package/api/build/kit/kind.mjs +170 -0
  15. package/api/build/kit/kit.d.mts +2 -4
  16. package/api/build/kit/kit.mjs +8 -398
  17. package/api/build/kit/rank.d.mts +3 -32
  18. package/api/build/kit/rank.mjs +25 -342
  19. package/api/build/kit/rank.test.mjs +2 -1
  20. package/api/build/kit/signals.d.mts +79 -0
  21. package/api/build/kit/signals.mjs +295 -0
  22. package/api/build/kit/start.d.mts +32 -0
  23. package/api/build/kit/start.mjs +163 -0
  24. package/api/build/kit/words.d.mts +72 -0
  25. package/api/build/kit/words.mjs +202 -0
  26. package/api/build/tree.d.mts +20 -0
  27. package/api/build/tree.mjs +53 -0
  28. package/api/build/tree.test.mjs +185 -0
  29. package/api/build/tree.weights.json +1 -0
  30. package/api/component/_adapter.mjs +39 -5
  31. package/api/component/component-replacement.test.mjs +1 -0
  32. package/api/component/component.mjs +17 -3
  33. package/api/component/component.test.mjs +15 -0
  34. package/api/component/doc-links.test.mjs +2 -2
  35. package/api/discover/_adapter.d.mts +1 -2
  36. package/api/discover/_adapter.mjs +42 -17
  37. package/api/discover/_adapter.test.mjs +42 -0
  38. package/api/discover/discover.mjs +0 -1
  39. package/api/docs/_adapter.d.mts +2 -2
  40. package/api/docs/_adapter.mjs +82 -23
  41. package/api/docs/docs-homes.test.mjs +127 -0
  42. package/api/docs/docs.doc.mjs +3 -3
  43. package/api/docs/docs.test.mjs +35 -19
  44. package/api/docs/integration-tree.test.mjs +20 -20
  45. package/api/docs/integrationDocs.test.mjs +1 -1
  46. package/api/docs/namespace-section.test.mjs +6 -6
  47. package/api/docs/reference-blocks.test.mjs +2 -2
  48. package/api/doctor/_adapter.d.mts +33 -13
  49. package/api/doctor/_adapter.mjs +55 -25
  50. package/api/doctor/doctor.d.mts +23 -5
  51. package/api/doctor/doctor.mjs +183 -40
  52. package/api/doctor/doctor.test.mjs +95 -12
  53. package/api/hook/hook.mjs +1 -1
  54. package/api/init/init.doc.mjs +20 -6
  55. package/api/init/init.test.mjs +18 -2
  56. package/api/init/init.type.d.mts +3 -3
  57. package/api/init/init.type.mjs +3 -3
  58. package/api/init/remove/remove.mjs +17 -4
  59. package/api/init/remove/remove.test.mjs +114 -2
  60. package/api/init/run/run.mjs +69 -6
  61. package/api/integration/_adapter.d.mts +8 -0
  62. package/api/integration/_adapter.mjs +21 -1
  63. package/api/integration/authoring-checks.mjs +83 -9
  64. package/api/integration/authoring-checks.test.mjs +220 -0
  65. package/api/integration/pack-check.mjs +123 -4
  66. package/api/integration/pack-check.test.mjs +87 -0
  67. package/api/integration/validate-integration-case-disk.test.mjs +111 -0
  68. package/api/integration/validate-integration-fixes.test.mjs +6 -5
  69. package/api/integration/validate-integration-gaps.test.mjs +1975 -0
  70. package/api/integration/validate-integration.d.mts +15 -2
  71. package/api/integration/validate-integration.mjs +298 -39
  72. package/api/integration/validate-integration.type.d.mts +90 -1
  73. package/api/integration/validate-integration.type.mjs +67 -2
  74. package/api/integration/validateIntegration.doc.mjs +1 -1
  75. package/api/search/ORDERING.md +28 -0
  76. package/api/search/_adapter.d.mts +82 -2
  77. package/api/search/_adapter.mjs +157 -10
  78. package/api/search/_adapter.test.mjs +173 -0
  79. package/api/search/_candidate-score.d.mts +40 -0
  80. package/api/search/_candidate-score.mjs +238 -0
  81. package/api/search/_cards.json +6098 -0
  82. package/api/search/_compound.d.mts +50 -0
  83. package/api/search/_compound.mjs +170 -0
  84. package/api/search/_doc-searcher.d.mts +4 -0
  85. package/api/search/_doc-searcher.mjs +23 -0
  86. package/api/search/_doc-searcher.test.mjs +36 -0
  87. package/api/search/_doc-searcher.weights.json +1 -0
  88. package/api/search/_doc-text.d.mts +30 -0
  89. package/api/search/_doc-text.mjs +87 -0
  90. package/api/search/_entries.d.mts +63 -0
  91. package/api/search/_entries.mjs +232 -0
  92. package/api/search/_entries.test.mjs +48 -0
  93. package/api/search/_features.d.mts +35 -0
  94. package/api/search/_features.mjs +155 -0
  95. package/api/search/_gather-components.d.mts +26 -0
  96. package/api/search/_gather-components.mjs +284 -0
  97. package/api/search/_gather-docs.d.mts +20 -0
  98. package/api/search/_gather-docs.mjs +254 -0
  99. package/api/search/_gather-hooks.d.mts +12 -0
  100. package/api/search/_gather-hooks.mjs +52 -0
  101. package/api/search/_gather-templates.d.mts +25 -0
  102. package/api/search/_gather-templates.mjs +107 -0
  103. package/api/search/_lexical.d.mts +135 -0
  104. package/api/search/_lexical.mjs +351 -0
  105. package/api/search/_main-searcher.d.mts +19 -0
  106. package/api/search/_main-searcher.mjs +115 -0
  107. package/api/search/_main-searcher.test.mjs +106 -0
  108. package/api/search/_main-searcher.weights.json +1 -0
  109. package/api/search/_module-doc.d.mts +32 -0
  110. package/api/search/_module-doc.mjs +78 -0
  111. package/api/search/_order.d.mts +32 -0
  112. package/api/search/_order.mjs +162 -0
  113. package/api/search/_order.test.mjs +72 -0
  114. package/api/search/_priority.d.mts +50 -0
  115. package/api/search/_priority.mjs +139 -0
  116. package/api/search/_query-words.d.mts +25 -0
  117. package/api/search/_query-words.mjs +92 -0
  118. package/api/search/_query.d.mts +65 -0
  119. package/api/search/_query.mjs +176 -0
  120. package/api/search/_result.d.mts +97 -0
  121. package/api/search/_result.mjs +150 -0
  122. package/api/search/_score.d.mts +60 -0
  123. package/api/search/_score.mjs +283 -0
  124. package/api/search/_synonyms.d.mts +21 -0
  125. package/api/search/_synonyms.mjs +67 -0
  126. package/api/search/_template-searcher.d.mts +4 -0
  127. package/api/search/_template-searcher.mjs +23 -0
  128. package/api/search/_template-searcher.test.mjs +40 -0
  129. package/api/search/_template-searcher.weights.json +1 -0
  130. package/api/search/_titles.d.mts +33 -0
  131. package/api/search/_titles.mjs +65 -0
  132. package/api/search/_words.d.mts +64 -0
  133. package/api/search/_words.mjs +157 -0
  134. package/api/search/search.d.mts +22 -305
  135. package/api/search/search.doc.mjs +8 -6
  136. package/api/search/search.mjs +56 -2482
  137. package/api/search/search.test.mjs +14 -14
  138. package/api/search/search.type.d.mts +6 -1
  139. package/api/search/search.type.mjs +6 -1
  140. package/api/search/tree.d.mts +15 -0
  141. package/api/search/tree.mjs +40 -0
  142. package/api/search/tree.test.mjs +66 -0
  143. package/api/search/tree.weights.json +1 -0
  144. package/api/template/template.mjs +5 -1
  145. package/api/theme/build/build.packed-old-core.test.mjs +1 -0
  146. package/api/upgrade/_adapter.d.mts +57 -9
  147. package/api/upgrade/_adapter.mjs +198 -28
  148. package/api/upgrade/registry/registry.d.mts +10 -4
  149. package/api/upgrade/registry/registry.mjs +33 -16
  150. package/api/upgrade/registry/registry.test.mjs +85 -0
  151. package/api/upgrade/run/run.d.mts +4 -2
  152. package/api/upgrade/run/run.mjs +13 -2
  153. package/api/upgrade/run/run.test.mjs +417 -3
  154. package/api/upgrade/upgrade.doc.mjs +10 -5
  155. package/api/upgrade/upgrade.mjs +13 -2
  156. package/api/upgrade/upgrade.test.mjs +194 -0
  157. package/api/upgrade/upgrade.type.d.mts +5 -3
  158. package/api/upgrade/upgrade.type.mjs +8 -6
  159. package/assets/codemods/transforms/v0.7.0/__tests__/validate-data-token-ownership.test.mjs +11 -0
  160. package/assets/codemods/transforms/v0.7.0/validate-data-token-ownership.mjs +3 -3
  161. package/assets/docs/author-a-theme.doc.mjs +2 -2
  162. package/assets/docs/is-my-code-good.doc.mjs +287 -0
  163. package/assets/docs/tree/add-a-component.doc.mjs +1 -1
  164. package/assets/docs/tree/add-a-theme.doc.mjs +1 -1
  165. package/assets/docs/tree/add-a-topic.doc.mjs +2 -2
  166. package/assets/docs/tree/agent-guidance.doc.mjs +1 -1
  167. package/assets/docs/tree/api.doc.mjs +6 -0
  168. package/assets/docs/tree/{integrations.doc.mjs → authoring.doc.mjs} +15 -6
  169. package/assets/docs/tree/block-template.doc.mjs +1 -1
  170. package/assets/docs/tree/build-the-template.doc.mjs +1 -1
  171. package/assets/docs/tree/building-blocks.doc.mjs +2 -2
  172. package/assets/docs/tree/check-the-props.doc.mjs +1 -1
  173. package/assets/docs/tree/check-your-docs.doc.mjs +2 -2
  174. package/assets/docs/tree/checks.doc.mjs +3 -3
  175. package/assets/docs/tree/ci-and-containers.doc.mjs +13 -0
  176. package/assets/docs/tree/cli-command.doc.mjs +1 -1
  177. package/assets/docs/tree/cli.doc.mjs +1 -1
  178. package/assets/docs/tree/codemods.doc.mjs +1 -1
  179. package/assets/docs/tree/component-family.doc.mjs +1 -1
  180. package/assets/docs/tree/component-imports.doc.mjs +1 -1
  181. package/assets/docs/tree/components.doc.mjs +1 -1
  182. package/assets/docs/tree/configuration.doc.mjs +1 -1
  183. package/assets/docs/tree/debug-and-gap-reports.doc.mjs +1 -1
  184. package/assets/docs/tree/define-the-theme.doc.mjs +1 -1
  185. package/assets/docs/tree/describe-the-component.doc.mjs +1 -1
  186. package/assets/docs/tree/docs.doc.mjs +1 -1
  187. package/assets/docs/tree/document-the-template.doc.mjs +1 -1
  188. package/assets/docs/tree/document-the-theme.doc.mjs +1 -1
  189. package/assets/docs/tree/export-template-assets.doc.mjs +1 -1
  190. package/assets/docs/tree/extend-or-replace.doc.mjs +1 -1
  191. package/assets/docs/{authoring.doc.mjs → tree/field-reference.doc.mjs} +4 -3
  192. package/assets/docs/tree/fonts-and-assets.doc.mjs +1 -1
  193. package/assets/docs/tree/generate-a-palette.doc.mjs +1 -1
  194. package/assets/docs/tree/global-integrations.doc.mjs +2 -2
  195. package/assets/docs/tree/grade-template-with-agent.doc.mjs +4 -4
  196. package/assets/docs/tree/help.doc.mjs +2 -2
  197. package/assets/docs/tree/host-entry.doc.mjs +128 -0
  198. package/assets/docs/tree/links.doc.mjs +1 -1
  199. package/assets/docs/tree/package-and-test.doc.mjs +1 -1
  200. package/assets/docs/tree/page-template.doc.mjs +1 -1
  201. package/assets/docs/tree/publishing.doc.mjs +1 -1
  202. package/assets/docs/tree/quick-start.doc.mjs +1 -1
  203. package/assets/docs/tree/replace-a-core-component.doc.mjs +1 -1
  204. package/assets/docs/tree/replace-a-core-template.doc.mjs +1 -1
  205. package/assets/docs/tree/sections-and-placement.doc.mjs +1 -1
  206. package/assets/docs/tree/see-it-in-an-app.doc.mjs +1 -1
  207. package/assets/docs/tree/ship.doc.mjs +2 -2
  208. package/assets/docs/tree/short-and-findable.doc.mjs +1 -1
  209. package/assets/docs/tree/single-component.doc.mjs +1 -1
  210. package/assets/docs/tree/start-a-template.doc.mjs +1 -1
  211. package/assets/docs/tree/subcomponent.doc.mjs +1 -1
  212. package/assets/docs/tree/template-assets.doc.mjs +1 -1
  213. package/assets/docs/tree/template-doc-overview.doc.mjs +2 -2
  214. package/assets/docs/tree/template-fonts.doc.mjs +1 -1
  215. package/assets/docs/tree/template-grading-rubric.doc.mjs +17 -38
  216. package/assets/docs/tree/template-icons.doc.mjs +1 -1
  217. package/assets/docs/tree/template-images-media.doc.mjs +1 -1
  218. package/assets/docs/tree/template-styles.doc.mjs +1 -1
  219. package/assets/docs/tree/templates.doc.mjs +1 -1
  220. package/assets/docs/tree/test-in-an-app.doc.mjs +1 -1
  221. package/assets/docs/tree/test-template-in-app.doc.mjs +1 -1
  222. package/assets/docs/tree/themes.doc.mjs +1 -1
  223. package/assets/docs/tree/tokens-data.doc.mjs +2 -2
  224. package/assets/docs/tree/troubleshooting.doc.mjs +1 -1
  225. package/assets/docs/tree/upgrading.doc.mjs +1 -1
  226. package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +1 -1
  227. package/assets/docs/tree/verify-packed-template.doc.mjs +1 -1
  228. package/assets/docs/tree/versioning.doc.mjs +5 -1
  229. package/assets/docs/tree/write-good-templates.doc.mjs +1 -1
  230. package/assets/docs/tree/write-the-template-file.doc.mjs +1 -1
  231. package/assets/docs/use-integrations.doc.mjs +120 -0
  232. package/assets/docs/working-with-ai.doc.mjs +6 -2
  233. package/assets/templates/themes/butter/butterTheme.ts +4 -2
  234. package/assets/templates/themes/stone/stoneTheme.ts +4 -2
  235. package/assets/templates/themes/y2k/y2kTheme.ts +4 -2
  236. package/authoring/debug/parse.d.mts +1 -1
  237. package/authoring/doctypes/schema/schema.doc.mjs +1 -1
  238. package/authoring/integration/integration.doc.mjs +1 -1
  239. package/authoring/integration/type.ts +1 -1
  240. package/clients/cli/bin/astryx.mjs +7 -78
  241. package/clients/cli/command-result-coverage.test.mjs +6 -5
  242. package/clients/cli/commands/build-theme.color-scheme.test.mjs +1 -1
  243. package/clients/cli/commands/{component/index.mjs → component.mjs} +37 -37
  244. package/clients/cli/commands/component.test.mjs +6 -6
  245. package/clients/cli/commands/docs.doc.mjs +2 -2
  246. package/clients/cli/commands/docs.mjs +1 -1
  247. package/clients/cli/commands/docs.test.mjs +14 -14
  248. package/clients/cli/commands/doctor-integration.test.mjs +49 -1
  249. package/clients/cli/commands/doctor.doc.mjs +1 -1
  250. package/clients/cli/commands/doctor.mjs +40 -0
  251. package/clients/cli/commands/doctor.test.mjs +65 -0
  252. package/clients/cli/commands/{hook/index.mjs → hook.mjs} +22 -22
  253. package/clients/cli/commands/import-hint-correctness.test.mjs +1 -1
  254. package/clients/cli/commands/init.behavior.test.mjs +230 -1
  255. package/clients/cli/commands/init.doc.mjs +28 -9
  256. package/clients/cli/commands/init.flag-help.test.mjs +1 -0
  257. package/clients/cli/commands/init.mjs +2 -2
  258. package/clients/cli/commands/integration-check.test.mjs +48 -0
  259. package/clients/cli/commands/integration.doc.mjs +1 -1
  260. package/clients/cli/commands/manifest.doc.mjs +1 -1
  261. package/clients/cli/commands/manifest.mjs +67 -0
  262. package/clients/cli/commands/outside-project.test.mjs +101 -1
  263. package/clients/cli/commands/postinstall.doc.mjs +23 -0
  264. package/clients/cli/commands/postinstall.mjs +31 -0
  265. package/clients/cli/commands/setup-nudge.test.mjs +78 -0
  266. package/clients/cli/commands/{build-theme.mjs → theme.mjs} +1 -1
  267. package/clients/cli/commands/upgrade.doc.mjs +4 -2
  268. package/clients/cli/commands/upgrade.mjs +9 -2
  269. package/clients/cli/commands/upgrade.subfolder-path.test.mjs +97 -0
  270. package/clients/cli/index.mjs +54 -94
  271. package/clients/cli/lib/component-format.mjs +1 -1
  272. package/clients/cli/lib/define-command.mjs +12 -7
  273. package/clients/cli/lib/exit-codes.test.mjs +5 -0
  274. package/clients/cli/lib/manifest.test.mjs +12 -0
  275. package/clients/cli/lib/welcome-box.mjs +50 -0
  276. package/clients/cli/run.mjs +108 -0
  277. package/foundation/agent-docs/agent-doc-state.d.mts +32 -7
  278. package/foundation/agent-docs/agent-doc-state.mjs +58 -9
  279. package/foundation/agent-docs/agent-docs.d.mts +100 -12
  280. package/foundation/agent-docs/agent-docs.mjs +1017 -105
  281. package/foundation/agent-docs/agent-docs.path-safety.test.mjs +1239 -17
  282. package/foundation/agent-docs/agent-docs.test.mjs +683 -39
  283. package/foundation/agent-docs/computer-agent-text.d.mts +39 -2
  284. package/foundation/agent-docs/computer-agent-text.mjs +135 -8
  285. package/foundation/agent-docs/computer-agent-text.test.mjs +99 -2
  286. package/foundation/config/project.mjs +20 -22
  287. package/foundation/config/project.test.mjs +56 -0
  288. package/foundation/discovery/authoring-self-docs.d.mts +7 -0
  289. package/foundation/discovery/authoring-self-docs.mjs +16 -4
  290. package/foundation/discovery/authoring-self-docs.test.mjs +15 -3
  291. package/foundation/discovery/authoring-surface.d.mts +1 -1
  292. package/foundation/discovery/authoring-surface.mjs +2 -2
  293. package/foundation/discovery/authoring-surface.test.mjs +1 -1
  294. package/foundation/discovery/cli-self-docs.d.mts +2 -1
  295. package/foundation/discovery/cli-self-docs.mjs +4 -3
  296. package/foundation/discovery/cli-self-docs.test.mjs +2 -2
  297. package/foundation/discovery/component-discovery.d.mts +37 -0
  298. package/foundation/discovery/component-discovery.mjs +80 -5
  299. package/foundation/discovery/component-loader.d.mts +1 -1
  300. package/foundation/discovery/docs-discovery.mjs +14 -1
  301. package/foundation/discovery/module-exports.d.mts +41 -0
  302. package/foundation/discovery/module-exports.mjs +253 -0
  303. package/foundation/discovery/runner-allowlist.d.mts +2 -4
  304. package/foundation/discovery/runner-allowlist.mjs +2 -4
  305. package/foundation/discovery/runner-commands.d.mts +77 -0
  306. package/foundation/discovery/runner-commands.mjs +294 -0
  307. package/foundation/discovery/runner-commands.test.mjs +128 -0
  308. package/foundation/discovery/stray-pointers.test.mjs +1 -1
  309. package/foundation/discovery/template-adapter.mjs +18 -1
  310. package/foundation/discovery/template-needs.mjs +1 -44
  311. package/foundation/discovery/theme-discovery.mjs +147 -3
  312. package/foundation/doc-compiler/doc-loads.test.mjs +33 -9
  313. package/foundation/doc-compiler/read.mjs +59 -1
  314. package/foundation/doc-compiler/read.test.mjs +11 -2
  315. package/foundation/doc-compiler/split-topic-keywords.test.mjs +3 -2
  316. package/foundation/doc-compiler/tree.d.mts +32 -6
  317. package/foundation/doc-compiler/tree.mjs +77 -43
  318. package/foundation/doc-compiler/tree.test.mjs +7 -7
  319. package/foundation/fs/paths.d.mts +70 -8
  320. package/foundation/fs/paths.mjs +556 -38
  321. package/foundation/fs/paths.test.mjs +430 -54
  322. package/foundation/fs/read-record.d.mts +18 -0
  323. package/foundation/fs/read-record.mjs +131 -0
  324. package/foundation/fs/read-record.test.mjs +77 -0
  325. package/foundation/integrations/autolink.d.mts +2 -1
  326. package/foundation/integrations/autolink.mjs +4 -13
  327. package/foundation/integrations/cli-peer-version.d.mts +36 -0
  328. package/foundation/integrations/cli-peer-version.mjs +132 -0
  329. package/foundation/integrations/cli-peer-version.test.mjs +354 -0
  330. package/foundation/integrations/computer-integrations.d.mts +6 -3
  331. package/foundation/integrations/computer-integrations.mjs +38 -9
  332. package/foundation/integrations/contribution-inventory.d.mts +15 -20
  333. package/foundation/integrations/contribution-inventory.mjs +41 -5
  334. package/foundation/integrations/exports-subpaths.d.mts +57 -0
  335. package/foundation/integrations/exports-subpaths.mjs +113 -0
  336. package/foundation/integrations/integrations.d.mts +27 -1
  337. package/foundation/integrations/integrations.mjs +61 -2
  338. package/foundation/integrations/public-imports.d.mts +118 -0
  339. package/foundation/integrations/public-imports.mjs +1769 -0
  340. package/foundation/integrations/public-imports.test.mjs +1793 -0
  341. package/foundation/integrations/validate-contributions.d.mts +115 -0
  342. package/foundation/integrations/validate-contributions.mjs +1168 -13
  343. package/foundation/release/cache.test.mjs +24 -21
  344. package/foundation/release/global-state.d.mts +8 -4
  345. package/foundation/release/global-state.mjs +4 -2
  346. package/foundation/release/handoff.d.mts +23 -4
  347. package/foundation/release/handoff.mjs +175 -8
  348. package/foundation/release/notices.d.mts +62 -8
  349. package/foundation/release/notices.mjs +136 -25
  350. package/foundation/release/notices.test.mjs +21 -0
  351. package/foundation/release/run-context.mjs +14 -0
  352. package/foundation/response/response-types.doc.mjs +1 -1
  353. package/foundation/route/route.d.mts +285 -0
  354. package/foundation/route/route.mjs +324 -0
  355. package/foundation/route/route.test.mjs +153 -0
  356. package/foundation/runtime/hosted-runtime.test.mjs +403 -0
  357. package/foundation/runtime/locked-versions.d.mts +32 -0
  358. package/foundation/runtime/locked-versions.mjs +65 -0
  359. package/foundation/runtime/package-root.d.mts +91 -0
  360. package/foundation/runtime/package-root.mjs +174 -0
  361. package/foundation/runtime/package-set.d.mts +62 -0
  362. package/foundation/runtime/package-set.mjs +130 -0
  363. package/foundation/runtime/package-set.test.mjs +432 -0
  364. package/foundation/text/string-utils.d.mts +2 -1
  365. package/foundation/text/string-utils.mjs +16 -5
  366. package/foundation/text/suggest-order.d.mts +72 -0
  367. package/foundation/text/suggest-order.mjs +363 -0
  368. package/foundation/text/suggest-order.test.mjs +259 -0
  369. package/host/door-conformance.test.mjs +274 -0
  370. package/host/index.d.ts +116 -0
  371. package/host/index.mjs +295 -0
  372. package/host/index.test.mjs +302 -0
  373. package/package.json +14 -9
@@ -802,8 +802,28 @@ describe('stopped and damaged installs heal', () => {
802
802
  });
803
803
  });
804
804
 
805
+ /** @param {number} pid */
806
+ const alive = pid => {
807
+ try {
808
+ process.kill(pid, 0);
809
+ return true;
810
+ } catch {
811
+ return false;
812
+ }
813
+ };
814
+
815
+ /** @param {() => boolean} done @param {number} ms */
816
+ const waitFor = async (done, ms) => {
817
+ const end = Date.now() + ms;
818
+ while (Date.now() < end) {
819
+ if (done()) return true;
820
+ await new Promise(r => setTimeout(r, 100));
821
+ }
822
+ return done();
823
+ };
824
+
805
825
  describe('runNpm time limit', () => {
806
- it('ends a package manager that ignores SIGTERM, leaving no process behind', () => {
826
+ it('ends a package manager that ignores SIGTERM, leaving no process behind', async () => {
807
827
  const marker = tempDir('npm-pid');
808
828
  const pidFile = path.join(marker, 'pid');
809
829
  const result = runNpm([], {
@@ -819,30 +839,13 @@ describe('runNpm time limit', () => {
819
839
  });
820
840
  expect(/** @type {any} */ (result.error)?.code).toBe('ETIMEDOUT');
821
841
  const pid = Number(fs.readFileSync(pidFile, 'utf-8'));
822
- expect(() => process.kill(pid, 0)).toThrow();
842
+ // The guard sends SIGKILL and exits at once; the killed process can still
843
+ // exist for a moment (the signal in flight, or awaiting reaping).
844
+ expect(await waitFor(() => !alive(pid), 5_000)).toBe(true);
823
845
  });
824
846
  });
825
847
 
826
848
  describe('no install outlives its run', () => {
827
- /** @param {number} pid */
828
- const alive = pid => {
829
- try {
830
- process.kill(pid, 0);
831
- return true;
832
- } catch {
833
- return false;
834
- }
835
- };
836
- /** @param {() => boolean} done @param {number} ms */
837
- const waitFor = async (done, ms) => {
838
- const end = Date.now() + ms;
839
- while (Date.now() < end) {
840
- if (done()) return true;
841
- await new Promise(r => setTimeout(r, 100));
842
- }
843
- return done();
844
- };
845
-
846
849
  it('ends npm when the run that started it is killed outright, and removes its process-id record', async () => {
847
850
  const dir = tempDir('orphan');
848
851
  const pidFile = path.join(dir, 'npm-pid');
@@ -20,9 +20,11 @@
20
20
  */
21
21
  /**
22
22
  * Why an update notice did not print: `already-shown` (it printed within the
23
- * day) or `no-newer` (nothing newer is known). Recorded only while the run
23
+ * day), `no-newer` (nothing newer is known), or `no-project` (the folder
24
+ * installs Core but belongs to no Astryx project, so no install command
25
+ * fits). Recorded only while the run
24
26
  * holds the state file's lock.
25
- * @typedef {{reason: 'already-shown' | 'no-newer', at: number}} NoticeSkip
27
+ * @typedef {{reason: 'already-shown' | 'no-newer' | 'no-project', at: number}} NoticeSkip
26
28
  */
27
29
  /**
28
30
  * The state file.
@@ -242,11 +244,13 @@ export type GlobalState = {
242
244
  };
243
245
  /**
244
246
  * Why an update notice did not print: `already-shown` (it printed within the
245
- * day) or `no-newer` (nothing newer is known). Recorded only while the run
247
+ * day), `no-newer` (nothing newer is known), or `no-project` (the folder
248
+ * installs Core but belongs to no Astryx project, so no install command
249
+ * fits). Recorded only while the run
246
250
  * holds the state file's lock.
247
251
  */
248
252
  export type NoticeSkip = {
249
- reason: "already-shown" | "no-newer";
253
+ reason: "already-shown" | "no-newer" | "no-project";
250
254
  at: number;
251
255
  };
252
256
  /**
@@ -68,9 +68,11 @@ export const CHECK_INTERVAL_MS = 24 * 60 * 60_000;
68
68
 
69
69
  /**
70
70
  * Why an update notice did not print: `already-shown` (it printed within the
71
- * day) or `no-newer` (nothing newer is known). Recorded only while the run
71
+ * day), `no-newer` (nothing newer is known), or `no-project` (the folder
72
+ * installs Core but belongs to no Astryx project, so no install command
73
+ * fits). Recorded only while the run
72
74
  * holds the state file's lock.
73
- * @typedef {{reason: 'already-shown' | 'no-newer', at: number}} NoticeSkip
75
+ * @typedef {{reason: 'already-shown' | 'no-newer' | 'no-project', at: number}} NoticeSkip
74
76
  */
75
77
 
76
78
  /**
@@ -10,6 +10,14 @@
10
10
  * @returns {string}
11
11
  */
12
12
  export function whoAnsweredLine(choice: import("./choose.mjs").ReleaseChoice, computer?: boolean): string;
13
+ /**
14
+ * The line for a run this copy answers in place of the project's own CLI,
15
+ * which a host declined, when this copy is that CLI's release (FR2).
16
+ * @param {string} version
17
+ * @param {string} reason the reason, as {@link declinedReasonText} words it
18
+ * @returns {string}
19
+ */
20
+ export function inPlaceOfProjectLine(version: string, reason: string): string;
13
21
  /**
14
22
  * The line for a run handed to the project's own CLI.
15
23
  * @param {string} projectVersion
@@ -41,15 +49,26 @@ export function progressText(event: "install" | "wait", version: string): string
41
49
  * @returns {string}
42
50
  */
43
51
  export function fallbackLine(wanted: string, reason: string, answering: string): string;
52
+ /**
53
+ * The fallback warning's reason for a project CLI a host declined.
54
+ * @param {string} reason the host's own words
55
+ * @returns {string}
56
+ */
57
+ export function declinedReasonText(reason: string): string;
44
58
  /**
45
59
  * Choose who answers and hand off when it is not this copy.
46
- * @param {{cliRoot: string, argv: string[]}} input argv after `node astryx`
47
- * @returns {Promise<void>} resolves when this copy answers
60
+ * @param {{cliRoot: string, argv: string[], declinedProjectCli?: string}} input
61
+ * argv after `node astryx`; the reason a programmatic host declined the
62
+ * project's own CLI, when it did
63
+ * @returns {Promise<import('../../host/index.js').HostedCliResult | undefined>}
64
+ * resolves with nothing when this copy answers, and with that release's
65
+ * result when another package set's host entry answered a hosted run
48
66
  */
49
- export function startRun({ cliRoot, argv }: {
67
+ export function startRun({ cliRoot, argv, declinedProjectCli }: {
50
68
  cliRoot: string;
51
69
  argv: string[];
52
- }): Promise<void>;
70
+ declinedProjectCli?: string;
71
+ }): Promise<import("../../host/index.js").HostedCliResult | undefined>;
53
72
  /**
54
73
  * How long a hand-off waits after an interrupt before passing it on to a CLI
55
74
  * that is still running. An interrupt sent to the whole process group (a
@@ -19,9 +19,26 @@
19
19
  * (notices.mjs); a global copy also starts the daily update check, which
20
20
  * installs nothing (global-state.mjs).
21
21
  *
22
- * @input the running copy, argv, the working folder
23
- * @output returns when this copy answers; otherwise never returns
24
- * @position foundation/release — called once by clients/cli/bin/astryx.mjs.
22
+ * A programmatic host that found the project's own CLI and did not run it
23
+ * passes the reason (spec AST-081 FR1, FR2). That run is never handed to the
24
+ * project's CLI: this copy answers in its place when it is that CLI's release,
25
+ * saying so in a line of its own; a package set of the host's whose CLI is
26
+ * that release answers through its own host entry; and otherwise this copy
27
+ * answers as for a release that cannot be obtained, with the warning naming
28
+ * the reason.
29
+ *
30
+ * A hosted run installs nothing (architecture:cli-surface INV30): a release
31
+ * other than this copy comes only from the first of the host's package sets
32
+ * that qualifies. When that CLI ships a host entry, the entry runs in this
33
+ * process with the same inputs, discover sources included, and the host's
34
+ * call settles with its result; otherwise its bin takes the run. With no such
35
+ * set, this copy answers, announced.
36
+ *
37
+ * @input the running copy, argv, the working folder, and a host's
38
+ * declined-project-CLI reason
39
+ * @output returns when this copy answers, or with another release's result
40
+ * when its host entry answered; never returns after a hand-off to a bin
41
+ * @position foundation/release — called once per run by clients/cli/run.mjs.
25
42
  */
26
43
 
27
44
  import {spawn} from 'node:child_process';
@@ -32,8 +49,10 @@ import {
32
49
  isGlobalCopy,
33
50
  namesWhoAnswered,
34
51
  packageRootOf,
52
+ projectRootOf,
35
53
  runningCopy,
36
54
  } from './choose.mjs';
55
+ import {readReleaseFacts} from './project.mjs';
37
56
  import {ensureRelease, userCacheDir} from './cache.mjs';
38
57
  import {
39
58
  maybeFetchCore,
@@ -43,6 +62,8 @@ import {
43
62
  import {recordRunChoice} from './run-context.mjs';
44
63
  import {noticeLines} from './notices.mjs';
45
64
  import {cliDoor, runWithCliDoor} from '../env/cli-command.mjs';
65
+ import {hostedRun} from '../runtime/package-root.mjs';
66
+ import {qualifyingRoot} from '../runtime/package-set.mjs';
46
67
 
47
68
  /** Where each release's notes are published. */
48
69
 
@@ -63,6 +84,17 @@ export function whoAnsweredLine(choice, computer = false) {
63
84
  : `astryx: answered by CLI ${choice.version} with Core ${core}, outside a project`;
64
85
  }
65
86
 
87
+ /**
88
+ * The line for a run this copy answers in place of the project's own CLI,
89
+ * which a host declined, when this copy is that CLI's release (FR2).
90
+ * @param {string} version
91
+ * @param {string} reason the reason, as {@link declinedReasonText} words it
92
+ * @returns {string}
93
+ */
94
+ export function inPlaceOfProjectLine(version, reason) {
95
+ return `astryx: answered by CLI ${version}, the release of this project's CLI, in its place (${reason})`;
96
+ }
97
+
66
98
  /**
67
99
  * The line for a run handed to the project's own CLI.
68
100
  * @param {string} projectVersion
@@ -131,16 +163,32 @@ function failUnavailable(message, json) {
131
163
  process.exit(1);
132
164
  }
133
165
 
166
+ /**
167
+ * The fallback warning's reason for a project CLI a host declined.
168
+ * @param {string} reason the host's own words
169
+ * @returns {string}
170
+ */
171
+ export function declinedReasonText(reason) {
172
+ return `project CLI skipped: ${reason}`;
173
+ }
174
+
134
175
  /**
135
176
  * Choose who answers and hand off when it is not this copy.
136
- * @param {{cliRoot: string, argv: string[]}} input argv after `node astryx`
137
- * @returns {Promise<void>} resolves when this copy answers
177
+ * @param {{cliRoot: string, argv: string[], declinedProjectCli?: string}} input
178
+ * argv after `node astryx`; the reason a programmatic host declined the
179
+ * project's own CLI, when it did
180
+ * @returns {Promise<import('../../host/index.js').HostedCliResult | undefined>}
181
+ * resolves with nothing when this copy answers, and with that release's
182
+ * result when another package set's host entry answered a hosted run
138
183
  */
139
- export async function startRun({cliRoot, argv}) {
184
+ export async function startRun({cliRoot, argv, declinedProjectCli}) {
140
185
  const json = argv.includes('--json');
141
186
  const self = runningCopy(cliRoot);
142
187
  const cacheDir = userCacheDir();
143
- const global = isGlobalCopy(cliRoot, cacheDir);
188
+ // A host carries its own CLI and keeps it current itself: a hosted copy
189
+ // runs none of the global copy's background work, and installs nothing.
190
+ const hosted = hostedRun();
191
+ const global = !hosted && isGlobalCopy(cliRoot, cacheDir);
144
192
  if (global) {
145
193
  try {
146
194
  maybeStartCheck();
@@ -189,7 +237,46 @@ export async function startRun({cliRoot, argv}) {
189
237
  };
190
238
  }
191
239
 
192
- if (choice.unavailable && choice.copy === 'project') {
240
+ // A host declined the project's own CLI (FR2): the run is never handed to
241
+ // it. The release equal to that CLI's version answers in its place when
242
+ // this copy is that release, or when a package set of the host's holds it
243
+ // with a host entry; otherwise this copy answers, announced as for a release
244
+ // that cannot be obtained, naming the reason.
245
+ let declined = null;
246
+ /** Set when this copy answers in place of the declined CLI, its release. */
247
+ let inPlace = null;
248
+ if (
249
+ declinedProjectCli &&
250
+ choice.copy === 'project' &&
251
+ choice.answer !== 'self'
252
+ ) {
253
+ if (choice.version === self.version) {
254
+ // Still the project CLI's release (`copy` stays `project`), never the
255
+ // release matched to the project's Core, which can be another.
256
+ choice = {...choice, answer: 'self', bin: null, unavailable: null};
257
+ inPlace = declinedReasonText(declinedProjectCli);
258
+ } else {
259
+ const served = hosted
260
+ ? qualifyingRoot({
261
+ release: choice.version,
262
+ projectDir: projectDirOf(process.cwd()),
263
+ roots: hosted.roots,
264
+ needsHostEntry: true,
265
+ })
266
+ : null;
267
+ if (served && 'root' in served && served.hostEntry) {
268
+ const answered = await answerInPackageSet(
269
+ served.hostEntry,
270
+ served.root,
271
+ {argv, declinedProjectCli},
272
+ );
273
+ if (answered) return answered;
274
+ }
275
+ declined = declinedReasonText(declinedProjectCli);
276
+ }
277
+ }
278
+
279
+ if (!declined && choice.unavailable && choice.copy === 'project') {
193
280
  failUnavailable(
194
281
  `This project's CLI ${choice.version} could not be started: ${choice.unavailable}. Reinstall the project's dependencies.`,
195
282
  json,
@@ -245,6 +332,11 @@ export async function startRun({cliRoot, argv}) {
245
332
  });
246
333
  };
247
334
 
335
+ if (declined) {
336
+ await fallBack(declined);
337
+ return;
338
+ }
339
+
248
340
  if (choice.unavailable) {
249
341
  await fallBack(choice.unavailable);
250
342
  return;
@@ -256,6 +348,8 @@ export async function startRun({cliRoot, argv}) {
256
348
  // line; a hint after the command, once a day, says how to match it.
257
349
  if (!json && (choice.copy === 'matched' || choice.copy === 'carried'))
258
350
  console.error(whoAnsweredLine(choice, computer));
351
+ else if (!json && inPlace)
352
+ console.error(inPlaceOfProjectLine(choice.version, inPlace));
259
353
  process.once('exit', code => {
260
354
  if (code === 0) notify();
261
355
  });
@@ -263,6 +357,32 @@ export async function startRun({cliRoot, argv}) {
263
357
  }
264
358
 
265
359
  let bin = choice.bin;
360
+ if (!bin && hosted) {
361
+ const served = qualifyingRoot({
362
+ release: choice.version,
363
+ projectDir: projectDirOf(process.cwd()),
364
+ roots: hosted.roots,
365
+ });
366
+ if (!('root' in served)) {
367
+ await fallBack(served.reason);
368
+ return;
369
+ }
370
+ if (served.hostEntry) {
371
+ const answered = await answerInPackageSet(served.hostEntry, served.root, {
372
+ argv,
373
+ });
374
+ if (answered) return answered;
375
+ }
376
+ // A hosted run installs nothing (INV30): a set whose CLI starts through
377
+ // neither its host entry nor a bin leaves this copy to answer.
378
+ if (!served.bin) {
379
+ await fallBack(
380
+ `the CLI ${choice.version} in the package set at ${served.root} could not be started`,
381
+ );
382
+ return;
383
+ }
384
+ bin = served.bin;
385
+ }
266
386
  if (!bin) {
267
387
  const release = ensureRelease(choice.version, {
268
388
  cacheDir,
@@ -295,6 +415,53 @@ export async function startRun({cliRoot, argv}) {
295
415
  */
296
416
  export const INTERRUPT_GRACE_MS = 5000;
297
417
 
418
+ /**
419
+ * The project folder a run in `cwd` belongs to, or null outside a project.
420
+ * @param {string} cwd
421
+ * @returns {string | null}
422
+ */
423
+ function projectDirOf(cwd) {
424
+ try {
425
+ return projectRootOf(readReleaseFacts(cwd));
426
+ } catch {
427
+ return null;
428
+ }
429
+ }
430
+
431
+ /**
432
+ * Hand a hosted run over to the CLI of one of the host's package sets,
433
+ * through that CLI's own host entry, in this process, with the inputs this run
434
+ * got: the arguments, the folder, the command name, the approved integrations,
435
+ * the discover sources, and the reason the host declined the project's CLI.
436
+ * Its roots are that package set alone. That CLI prints its own who-answered
437
+ * line and notices, and the host's call settles with its result.
438
+ * @param {string} hostEntry the package set's `host/index.mjs`
439
+ * @param {string} root the package set
440
+ * @param {{argv: string[], declinedProjectCli?: string}} run
441
+ * @returns {Promise<import('../../host/index.js').HostedCliResult | null>} null
442
+ * when the entry cannot be loaded, before anything ran
443
+ */
444
+ async function answerInPackageSet(hostEntry, root, {argv, declinedProjectCli}) {
445
+ const hosted = hostedRun();
446
+ if (!hosted?.loadHostEntry) return null;
447
+ let answer;
448
+ try {
449
+ answer = await hosted.loadHostEntry(hostEntry);
450
+ } catch {
451
+ return null;
452
+ }
453
+ const door = cliDoor();
454
+ return answer({
455
+ argv,
456
+ cwd: process.cwd(),
457
+ roots: [root],
458
+ integrations: [...hosted.integrations],
459
+ discoverSources: [...hosted.discoverSources],
460
+ ...(door ? {cliCommand: door} : {}),
461
+ ...(declinedProjectCli ? {declinedProjectCli} : {}),
462
+ });
463
+ }
464
+
298
465
  /**
299
466
  * Run `bin` as this run and end with its exit status.
300
467
  * @param {string} bin
@@ -91,24 +91,71 @@ export function globalBehind({ self, stateDir }: {
91
91
  };
92
92
  stateDir?: string;
93
93
  }): GlobalBehind | null;
94
+ /**
95
+ * The command that adds a package to the project of a run in `cwd`, with the
96
+ * project's package manager (spec AST-081 FR12). Null when `cwd` belongs to
97
+ * no project `spec:AST-040` FR9 defines, and in a folder inside the project
98
+ * whose package.json is not the project's (see {@link installFolder}): a run
99
+ * there loads integrations from that package.json, so a package added to the
100
+ * project would stay unused there, and the advice is
101
+ * {@link RUN_FROM_PROJECT}.
102
+ * @param {string} cwd
103
+ * @returns {((name: string) => string) | null}
104
+ */
105
+ export function projectAddCommand(cwd: string): ((name: string) => string) | null;
94
106
  /**
95
107
  * The commands that bring a project's own CLI and Core to one release, with
96
108
  * the project's package manager: when the CLI is newer, Core up to it and then
97
109
  * the codemods from Core's release; otherwise the CLI to Core's release.
110
+ * Null in a folder that belongs to no project, or whose package.json is not
111
+ * the project's (see {@link installFolder}).
98
112
  * @param {{cwd: string, cli: string, core: string}} options
99
- * @returns {string}
113
+ * @returns {string | null}
100
114
  */
101
115
  export function alignCliAndCore({ cwd, cli, core }: {
102
116
  cwd: string;
103
117
  cli: string;
104
118
  core: string;
105
- }): string;
119
+ }): string | null;
120
+ /**
121
+ * Where a command that adds an Astryx package runs, for a run in `cwd`: in
122
+ * the project `spec:AST-040` FR9 defines, which stops at a repository root
123
+ * and never counts the home folder (spec AST-081 FR2, FR10). Null when `cwd`
124
+ * belongs to no such project, so no command adds anything. When `cwd`'s
125
+ * nearest package.json is not the project's, as in a tool folder that
126
+ * installs its own Core, a package manager would add to the tool's
127
+ * package.json, so `moved` says the command runs in the project's folder.
128
+ * @param {string} cwd
129
+ * @returns {{dir: string, moved: boolean} | null}
130
+ */
131
+ export function installFolder(cwd: string): {
132
+ dir: string;
133
+ moved: boolean;
134
+ } | null;
135
+ /**
136
+ * The command that adds the CLI at Core's version, for a run in `cwd` whose
137
+ * Core is `core`, with the project's package manager in the project root.
138
+ * `command` is null in a folder inside the project whose package.json is not
139
+ * the project's (see {@link installFolder}), where the advice is
140
+ * {@link RUN_FROM_PROJECT}. Null when the folder belongs to no project.
141
+ * @param {{cwd: string, core: string}} options
142
+ * @returns {{root: string, core: string, command: string | null} | null}
143
+ */
144
+ export function cliInstallAdvice({ cwd, core }: {
145
+ cwd: string;
146
+ core: string;
147
+ }): {
148
+ root: string;
149
+ core: string;
150
+ command: string | null;
151
+ } | null;
106
152
  /**
107
153
  * The hint for a project with no CLI of its own, answered by `answered` while
108
154
  * its Core is `core`: the command that adds the CLI at Core's version, with
109
- * the project's package manager.
155
+ * the project's package manager (see {@link cliInstallAdvice}). Null when no
156
+ * command fits, or when the project the command adds to already matches.
110
157
  * @param {{cwd: string, answered: string, core: string}} options
111
- * @returns {{root: string, line: string}}
158
+ * @returns {{root: string, line: string} | null}
112
159
  */
113
160
  export function noCliHint({ cwd, answered, core }: {
114
161
  cwd: string;
@@ -117,15 +164,16 @@ export function noCliHint({ cwd, answered, core }: {
117
164
  }): {
118
165
  root: string;
119
166
  line: string;
120
- };
167
+ } | null;
121
168
  /**
122
169
  * The hint's text.
123
170
  * @param {string} answered
124
171
  * @param {string} core
125
- * @param {string} command the command that adds the CLI at Core's version
172
+ * @param {string | null} command the command that adds the CLI at Core's
173
+ * version; null in a folder whose package.json is not the project's
126
174
  * @returns {string}
127
175
  */
128
- export function noCliHintLine(answered: string, core: string, command: string): string;
176
+ export function noCliHintLine(answered: string, core: string, command: string | null): string;
129
177
  /**
130
178
  * The command that installs the CLI at `version` in the project at `root`,
131
179
  * with its package manager.
@@ -140,7 +188,7 @@ export function addCliCommand(root: string, version: string): string;
140
188
  * @param {{cwd: string, current: string, stateDir?: string}} options
141
189
  * @returns {ProjectLag | null}
142
190
  */
143
- export function projectLag({ cwd, current, stateDir }: {
191
+ export function projectLag({ cwd, current: given, stateDir }: {
144
192
  cwd: string;
145
193
  current: string;
146
194
  stateDir?: string;
@@ -207,6 +255,12 @@ export function noticeLines({ cwd, self, choice, json, stateDir, cacheDir, now,
207
255
  }): string[];
208
256
  /** A notice prints at most once in this long. */
209
257
  export const NOTICE_INTERVAL_MS: number;
258
+ /**
259
+ * What to tell a run in a folder inside a project whose package.json is not
260
+ * the project's: run from the project's folder, since adding there would make
261
+ * the folder a project of its own (spec AST-081 FR2).
262
+ */
263
+ export const RUN_FROM_PROJECT: "Run this from the Astryx project's folder, or declare an Astryx package in this folder's package.json to make it a project of its own.";
210
264
  export type GlobalBehind = {
211
265
  /**
212
266
  * the global copy's version