@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
@@ -0,0 +1,77 @@
1
+ // @generated by scripts/sync-api-types.mjs from the JSDoc in foundation/**/*.mjs.
2
+ // DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
3
+
4
+ /**
5
+ * Each runner command in a text, as written, with the bare command to write
6
+ * instead, in order.
7
+ *
8
+ * @param {string} text
9
+ * @returns {{written: string, bare: string, download: boolean}[]}
10
+ */
11
+ export function runnerCommandsIn(text: string): {
12
+ written: string;
13
+ bare: string;
14
+ download: boolean;
15
+ }[];
16
+ /**
17
+ * An Astryx command a topic writes with a package runner in front.
18
+ *
19
+ * @typedef {object} RunnerCommand
20
+ * @property {string} topic
21
+ * @property {string | null} section the title of the section it is in
22
+ * @property {string} file the doc file
23
+ * @property {number | null} line the file line that holds it, when found
24
+ * @property {string} command the command as written
25
+ * @property {string} message the bare command to write instead
26
+ */
27
+ /**
28
+ * Runner commands in a package's topics.
29
+ *
30
+ * @param {{records: {name: string, path: string}[], guides?: {name: string, ref?: {topicFile?: string}}[]}} discovered
31
+ * the package's docs, as discoverIntegrationDocs returns them
32
+ * @returns {Promise<RunnerCommand[]>}
33
+ */
34
+ export function findRunnerCommands(discovered: {
35
+ records: {
36
+ name: string;
37
+ path: string;
38
+ }[];
39
+ guides?: {
40
+ name: string;
41
+ ref?: {
42
+ topicFile?: string;
43
+ };
44
+ }[];
45
+ }): Promise<RunnerCommand[]>;
46
+ /**
47
+ * The CLI's top-level commands, the words that make a project runner's
48
+ * `astryx <word>` a command; runner-commands.test.mjs keeps the list equal to
49
+ * the command docs.
50
+ */
51
+ export const CLI_COMMANDS: readonly string[];
52
+ /**
53
+ * An Astryx command a topic writes with a package runner in front.
54
+ */
55
+ export type RunnerCommand = {
56
+ topic: string;
57
+ /**
58
+ * the title of the section it is in
59
+ */
60
+ section: string | null;
61
+ /**
62
+ * the doc file
63
+ */
64
+ file: string;
65
+ /**
66
+ * the file line that holds it, when found
67
+ */
68
+ line: number | null;
69
+ /**
70
+ * the command as written
71
+ */
72
+ command: string;
73
+ /**
74
+ * the bare command to write instead
75
+ */
76
+ message: string;
77
+ };
@@ -0,0 +1,294 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /**
4
+ * Runner commands in docs: an Astryx command written with a package runner in
5
+ * front. Docs write the bare `astryx <command>` and the CLI shows it with the
6
+ * reader's runner, so a written runner is wrong for every reader on another
7
+ * package manager.
8
+ *
9
+ * - A download runner (`npx`, `pnpm dlx`, `yarn dlx`, `bunx`, `bun x`,
10
+ * `npm exec`) with the unscoped name `astryx`, with or without an
11
+ * `@<version>` after it, always counts, even with nothing after it: it
12
+ * fetches an unrelated package of that name.
13
+ * - A project runner (`pnpm exec`, `pnpm`, `yarn`) counts when one of the
14
+ * CLI's commands follows on the same line (`pnpm exec astryx docs`). Named
15
+ * alone, or before a word that is not a command (`yarn astryx runs the
16
+ * copy…`), it is left alone.
17
+ * - A runner and what follows it are read within one line; flags between a
18
+ * download runner and the name are read past.
19
+ * - The scoped package (`npx @astryxdesign/cli init`) never counts: it is the
20
+ * one form that runs before the CLI is installed, for install steps.
21
+ * - Other tools' commands (`npx prettier`) never count.
22
+ */
23
+
24
+ import fs from 'node:fs';
25
+
26
+ import {loadTopicModule} from './docs-discovery.mjs';
27
+
28
+ /**
29
+ * @param {string} file
30
+ * @returns {Promise<any>} the doc, or null for one that does not load (the
31
+ * load problem is reported elsewhere)
32
+ */
33
+ async function loadQuietly(file) {
34
+ try {
35
+ return await loadTopicModule(file);
36
+ } catch {
37
+ return null;
38
+ }
39
+ }
40
+
41
+ const DOWNLOAD_RUNNERS = [
42
+ 'pnpm dlx',
43
+ 'yarn dlx',
44
+ 'npm exec --',
45
+ 'npm exec',
46
+ 'npx',
47
+ 'bunx',
48
+ 'bun x',
49
+ ];
50
+ const PROJECT_RUNNERS = ['pnpm exec', 'pnpm', 'yarn'];
51
+
52
+ /**
53
+ * The CLI's top-level commands, the words that make a project runner's
54
+ * `astryx <word>` a command; runner-commands.test.mjs keeps the list equal to
55
+ * the command docs.
56
+ */
57
+ export const CLI_COMMANDS = Object.freeze([
58
+ 'blog',
59
+ 'build',
60
+ 'component',
61
+ 'discover',
62
+ 'docs',
63
+ 'doctor',
64
+ 'gap-report',
65
+ 'help',
66
+ 'hook',
67
+ 'init',
68
+ 'integration',
69
+ 'manifest',
70
+ 'postinstall',
71
+ 'search',
72
+ 'swizzle',
73
+ 'template',
74
+ 'theme',
75
+ 'upgrade',
76
+ ]);
77
+
78
+ /** Blanks within one line. */
79
+ const BLANK = '[ \\t]+';
80
+
81
+ /** @param {readonly string[]} words */
82
+ const alternatives = words =>
83
+ words.map(each => each.replace(/ /g, BLANK)).join('|');
84
+
85
+ /** After `astryx`: the name ends here (a sentence's period may follow). */
86
+ const NAME_END = '(?![\\w@/-]|\\.\\w)';
87
+
88
+ const DOWNLOAD_COMMAND = new RegExp(
89
+ `(?<![\\w@/.-])(?:${alternatives(DOWNLOAD_RUNNERS)})(?:${BLANK}--?[a-z][\\w-]*)*${BLANK}astryx(?:@[\\w^~-]+(?:\\.[\\w^~-]+)*)?${NAME_END}(?:${BLANK}(${alternatives(CLI_COMMANDS)})(?![\\w-]))?`,
90
+ 'g',
91
+ );
92
+ const PROJECT_COMMAND = new RegExp(
93
+ `(?<![\\w@/.-])(?:${alternatives(PROJECT_RUNNERS)})${BLANK}astryx${BLANK}(${alternatives(CLI_COMMANDS)})(?![\\w-])`,
94
+ 'g',
95
+ );
96
+
97
+ /**
98
+ * Each runner command in a text, as written, with the bare command to write
99
+ * instead, in order.
100
+ *
101
+ * @param {string} text
102
+ * @returns {{written: string, bare: string, download: boolean}[]}
103
+ */
104
+ export function runnerCommandsIn(text) {
105
+ const found = [
106
+ ...[...String(text).matchAll(DOWNLOAD_COMMAND)].map(match => ({
107
+ match,
108
+ download: true,
109
+ })),
110
+ ...[...String(text).matchAll(PROJECT_COMMAND)].map(match => ({
111
+ match,
112
+ download: false,
113
+ })),
114
+ ];
115
+ /** @type {{at: number, written: string, bare: string, download: boolean}[]} */
116
+ const unique = [];
117
+ for (const {match, download} of found) {
118
+ if (unique.some(each => each.at === match.index)) continue;
119
+ unique.push({
120
+ at: match.index,
121
+ written: match[0],
122
+ bare: match[1] ? `astryx ${match[1]}` : 'astryx',
123
+ download,
124
+ });
125
+ }
126
+ return unique
127
+ .sort((a, b) => a.at - b.at)
128
+ .map(({written, bare, download}) => ({written, bare, download}));
129
+ }
130
+
131
+ /**
132
+ * Every string in a doc, with the title of the section it is in. Code counts:
133
+ * most commands sit in code blocks.
134
+ *
135
+ * @param {unknown} doc
136
+ * @returns {{text: string, section: string | null}[]}
137
+ */
138
+ function allStrings(doc) {
139
+ /** @type {{text: string, section: string | null}[]} */
140
+ const strings = [];
141
+ // A doc that refers back to itself is walked once.
142
+ const visited = new WeakSet();
143
+ /**
144
+ * @param {unknown} value
145
+ * @param {string | null} section
146
+ */
147
+ const walk = (value, section) => {
148
+ if (typeof value === 'string') {
149
+ strings.push({text: value, section});
150
+ } else if (value != null && typeof value === 'object') {
151
+ if (visited.has(value)) return;
152
+ visited.add(value);
153
+ if (Array.isArray(value)) {
154
+ for (const item of value) walk(item, section);
155
+ return;
156
+ }
157
+ const record = /** @type {Record<string, unknown>} */ (value);
158
+ for (const [key, field] of Object.entries(record)) {
159
+ if (key === 'sections' && Array.isArray(field)) {
160
+ for (const each of field) {
161
+ walk(each, typeof each?.title === 'string' ? each.title : section);
162
+ }
163
+ } else {
164
+ walk(field, section);
165
+ }
166
+ }
167
+ }
168
+ };
169
+ walk(doc, null);
170
+ return strings;
171
+ }
172
+
173
+ /**
174
+ * Where a file next holds the text, at or after a cursor (a 0-based line and
175
+ * column), else the first place it holds it, else null. A doc's strings are
176
+ * read in file order, so moving the cursor past each match puts a command the
177
+ * topic writes twice on its own line.
178
+ *
179
+ * @param {string[]} lines
180
+ * @param {string} written
181
+ * @param {{line: number, column: number}} cursor
182
+ * @returns {{line: number, column: number} | null}
183
+ */
184
+ function locate(lines, written, cursor) {
185
+ for (let at = cursor.line; at < lines.length; at++) {
186
+ const column = lines[at].indexOf(
187
+ written,
188
+ at === cursor.line ? cursor.column : 0,
189
+ );
190
+ if (column >= 0) return {line: at, column};
191
+ }
192
+ const first = lines.findIndex(line => line.includes(written));
193
+ return first < 0
194
+ ? null
195
+ : {line: first, column: lines[first].indexOf(written)};
196
+ }
197
+
198
+ /**
199
+ * An Astryx command a topic writes with a package runner in front.
200
+ *
201
+ * @typedef {object} RunnerCommand
202
+ * @property {string} topic
203
+ * @property {string | null} section the title of the section it is in
204
+ * @property {string} file the doc file
205
+ * @property {number | null} line the file line that holds it, when found
206
+ * @property {string} command the command as written
207
+ * @property {string} message the bare command to write instead
208
+ */
209
+
210
+ /**
211
+ * Runner commands in a package's topics.
212
+ *
213
+ * @param {{records: {name: string, path: string}[], guides?: {name: string, ref?: {topicFile?: string}}[]}} discovered
214
+ * the package's docs, as discoverIntegrationDocs returns them
215
+ * @returns {Promise<RunnerCommand[]>}
216
+ */
217
+ export async function findRunnerCommands(discovered) {
218
+ const topics = [
219
+ ...discovered.records.map(record => ({
220
+ name: record.name,
221
+ file: record.path,
222
+ })),
223
+ ...(discovered.guides ?? []).map(guide => ({
224
+ name: guide.name,
225
+ file: guide.ref?.topicFile,
226
+ })),
227
+ ];
228
+ /** @type {RunnerCommand[]} */
229
+ const found = [];
230
+ for (const {name, file} of topics) {
231
+ const doc = file ? await loadQuietly(file) : null;
232
+ if (!doc || !file) continue;
233
+ /** @type {string[]} */
234
+ let lines = [];
235
+ try {
236
+ lines = fs.readFileSync(file, 'utf8').split('\n');
237
+ } catch {
238
+ // The doc loaded, so only the line number is lost.
239
+ }
240
+ let cursor = {line: 0, column: 0};
241
+ for (const {text, section} of allStrings(doc)) {
242
+ const commands = runnerCommandsIn(text);
243
+ if (commands.length === 0) continue;
244
+ // A one-line string's own literal places it, wherever the walk reached
245
+ // it (a section in a constant above the doc object, say); else the
246
+ // cursor search, in file order.
247
+ const forms = [`'${text}'`, `"${text}"`, JSON.stringify(text)];
248
+ const holds = (/** @type {string} */ line) =>
249
+ forms.some(form => line.includes(form));
250
+ // On the cursor's own line, only a copy at or after the cursor counts:
251
+ // the copy just matched still sits before it.
252
+ const after = text.includes('\n')
253
+ ? -1
254
+ : lines.findIndex((line, at) =>
255
+ at > cursor.line
256
+ ? holds(line)
257
+ : at === cursor.line &&
258
+ forms.some(form => line.indexOf(form, cursor.column) >= 0),
259
+ );
260
+ const literal =
261
+ after >= 0 || text.includes('\n') ? after : lines.findIndex(holds);
262
+ const column = after >= 0 && after === cursor.line ? cursor.column : 0;
263
+ let from = literal >= 0 ? {line: literal, column} : cursor;
264
+ for (const {written, bare, download} of commands) {
265
+ const place = locate(lines, written, from);
266
+ if (place) {
267
+ from = {line: place.line, column: place.column + written.length};
268
+ if (
269
+ place.line > cursor.line ||
270
+ (place.line === cursor.line && place.column >= cursor.column)
271
+ ) {
272
+ cursor = from;
273
+ }
274
+ }
275
+ const line = place ? place.line + 1 : null;
276
+ const where = section ? ` (section "${section}")` : '';
277
+ found.push({
278
+ topic: name,
279
+ section,
280
+ file,
281
+ line,
282
+ command: written,
283
+ message:
284
+ `"${name}"${where} writes \`${written}\`${line == null ? '' : ` (line ${line})`}. ` +
285
+ `Write \`${bare}\`; the CLI shows it with your project's runner` +
286
+ (download
287
+ ? ', and a download runner with the bare name fetches an unrelated package called astryx.'
288
+ : '.'),
289
+ });
290
+ }
291
+ }
292
+ }
293
+ return found;
294
+ }
@@ -0,0 +1,128 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ import fs from 'node:fs';
4
+ import os from 'node:os';
5
+ import path from 'node:path';
6
+ import {fileURLToPath} from 'node:url';
7
+ import {describe, expect, it} from 'vitest';
8
+
9
+ import {
10
+ CLI_COMMANDS,
11
+ findRunnerCommands,
12
+ runnerCommandsIn,
13
+ } from './runner-commands.mjs';
14
+
15
+ const COMMAND_DOCS = path.resolve(
16
+ path.dirname(fileURLToPath(import.meta.url)),
17
+ '../../clients/cli/commands',
18
+ );
19
+
20
+ describe('runnerCommandsIn', () => {
21
+ it('finds an Astryx command after a project runner, with the bare command', () => {
22
+ expect(runnerCommandsIn('Run `pnpm exec astryx docs theme`.')).toEqual([
23
+ {written: 'pnpm exec astryx docs', bare: 'astryx docs', download: false},
24
+ ]);
25
+ expect(runnerCommandsIn('yarn astryx component Button')).toEqual([
26
+ {
27
+ written: 'yarn astryx component',
28
+ bare: 'astryx component',
29
+ download: false,
30
+ },
31
+ ]);
32
+ });
33
+
34
+ it('always flags the unscoped name after a download runner, even alone', () => {
35
+ expect(runnerCommandsIn('npx astryx doctor; or `bunx astryx`')).toEqual([
36
+ {written: 'npx astryx doctor', bare: 'astryx doctor', download: true},
37
+ {written: 'bunx astryx', bare: 'astryx', download: true},
38
+ ]);
39
+ });
40
+
41
+ it('leaves the scoped package, a project runner named alone, and other tools', () => {
42
+ expect(
43
+ runnerCommandsIn(
44
+ 'Install with `npx @astryxdesign/cli init`. Run it through Yarn (`yarn astryx`). Format with `npx prettier --write`. `npx astryx-tools init`.',
45
+ ),
46
+ ).toEqual([]);
47
+ });
48
+
49
+ it('flags `bun x`, a versioned name, and a name that ends a sentence', () => {
50
+ expect(
51
+ runnerCommandsIn(
52
+ 'Run `bun x astryx doctor`, `npx astryx@latest init` or `npx astryx@0.7.1 docs`. Or just npx astryx.',
53
+ ),
54
+ ).toEqual([
55
+ {written: 'bun x astryx doctor', bare: 'astryx doctor', download: true},
56
+ {
57
+ written: 'npx astryx@latest init',
58
+ bare: 'astryx init',
59
+ download: true,
60
+ },
61
+ {written: 'npx astryx@0.7.1 docs', bare: 'astryx docs', download: true},
62
+ {written: 'npx astryx', bare: 'astryx', download: true},
63
+ ]);
64
+ expect(
65
+ runnerCommandsIn('Edit astryx.config, not npx astryx.config.'),
66
+ ).toEqual([]);
67
+ });
68
+
69
+ it('reads a runner and its command within one line, and needs a CLI command after a project runner', () => {
70
+ expect(
71
+ runnerCommandsIn(
72
+ 'Through Yarn, yarn astryx runs the copy in node_modules.\n```\npnpm astryx\nyarn astryx\n```',
73
+ ),
74
+ ).toEqual([]);
75
+ expect(runnerCommandsIn('npx astryx runs an unrelated package')).toEqual([
76
+ {written: 'npx astryx', bare: 'astryx', download: true},
77
+ ]);
78
+ expect(runnerCommandsIn('pnpm\nastryx docs')).toEqual([]);
79
+ expect(
80
+ runnerCommandsIn('Run `npx -y astryx doctor` or `npx --yes astryx`.'),
81
+ ).toEqual([
82
+ {written: 'npx -y astryx doctor', bare: 'astryx doctor', download: true},
83
+ {written: 'npx --yes astryx', bare: 'astryx', download: true},
84
+ ]);
85
+ });
86
+ });
87
+
88
+ describe('findRunnerCommands', () => {
89
+ it('reads a topic that refers back to itself once, without crashing', async () => {
90
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'runner-self-'));
91
+ const file = path.join(dir, 'acme-setup.doc.mjs');
92
+ fs.writeFileSync(
93
+ file,
94
+ [
95
+ 'const section = {',
96
+ " title: 'Overview',",
97
+ " content: [{type: 'prose', text: 'Run `yarn astryx docs` first.'}],",
98
+ '};',
99
+ "const doc = {type: 'generic', name: 'acme-setup', sections: [section]};",
100
+ 'section.self = section;',
101
+ 'doc.again = doc;',
102
+ 'export default doc;',
103
+ '',
104
+ ].join('\n'),
105
+ );
106
+
107
+ const found = await findRunnerCommands({
108
+ records: [{name: 'acme-setup', path: file}],
109
+ });
110
+
111
+ expect(
112
+ found.map(({command, section, line}) => ({command, section, line})),
113
+ ).toEqual([{command: 'yarn astryx docs', section: 'Overview', line: 3}]);
114
+ });
115
+ });
116
+
117
+ describe('CLI_COMMANDS', () => {
118
+ it('names every top-level command the CLI documents, and nothing else', () => {
119
+ const documented = fs
120
+ .readdirSync(COMMAND_DOCS)
121
+ .filter(file => file.endsWith('.doc.mjs'))
122
+ .map(file => file.slice(0, -'.doc.mjs'.length))
123
+ .filter(name => !name.includes('-') || name === 'gap-report');
124
+ expect([...CLI_COMMANDS].sort()).toEqual(
125
+ [...new Set([...documented, 'help'])].sort(),
126
+ );
127
+ });
128
+ });
@@ -16,7 +16,7 @@ describe('pointersIn', () => {
16
16
  it('leaves a walkthrough alone: more words, a flag, or a route', () => {
17
17
  expect(
18
18
  pointersIn(
19
- 'Run `astryx docs theme overview`, `astryx docs --json`, `astryx docs cli/integrations`, or `astryx docs`.',
19
+ 'Run `astryx docs theme overview`, `astryx docs --json`, `astryx docs authoring/quick-start`, or `astryx docs`.',
20
20
  ),
21
21
  ).toEqual([]);
22
22
  });
@@ -1192,6 +1192,23 @@ function reportSamePackageReplacements(pkg, autolinked, templates, errors) {
1192
1192
  }
1193
1193
  }
1194
1194
 
1195
+ /**
1196
+ * The fix for a template doc with no same-stem `.tsx`: rename a same-stem
1197
+ * source written in another extension, or add the `.tsx`.
1198
+ * @param {string} docPath
1199
+ * @param {string} sourcePath the `.tsx` it needs
1200
+ */
1201
+ function missingTemplateSourceFix(docPath, sourcePath) {
1202
+ const source = path.basename(sourcePath);
1203
+ const stem = source.slice(0, -'.tsx'.length);
1204
+ const other = ['.jsx', '.js', '.ts', '.mjs']
1205
+ .map(extension => `${stem}${extension}`)
1206
+ .find(file => fs.existsSync(path.join(path.dirname(sourcePath), file)));
1207
+ return other
1208
+ ? `Found ${other}, but a template source must be a .tsx file. Fix: rename ${other} to ${source}.`
1209
+ : `Fix: add ${source} beside ${path.basename(docPath)}, with the template as its default export.`;
1210
+ }
1211
+
1195
1212
  /**
1196
1213
  * Discover the templates contributed by a SINGLE integration. Same per-template
1197
1214
  * rules as {@link discoverIntegrationTemplates} (same-stem source required,
@@ -1225,7 +1242,7 @@ export async function discoverIntegrationTemplatesForOne(integration) {
1225
1242
  errors.push({
1226
1243
  package: pkgLabel,
1227
1244
  template: id,
1228
- message: `Template "${id}" is missing its same-stem source file ${path.basename(sourcePath)}.`,
1245
+ message: `Template "${id}" is missing its same-stem source file ${path.basename(sourcePath)}. ${missingTemplateSourceFix(docPath, sourcePath)}`,
1229
1246
  });
1230
1247
  // A replacement this unusable template declares still counts, so its
1231
1248
  // target fails closed instead of going to a sibling.
@@ -18,7 +18,7 @@
18
18
  import * as fs from 'node:fs';
19
19
  import * as path from 'node:path';
20
20
  import module from 'node:module';
21
- import {CLI_ROOT} from '../fs/paths.mjs';
21
+ import {CLI_ROOT, findWorkspaceRoot} from '../fs/paths.mjs';
22
22
  import {detectStylingSystem} from '../agent-docs/agent-docs.mjs';
23
23
  import {
24
24
  decidePackageManagerIn,
@@ -71,49 +71,6 @@ const ADD_VERB = {
71
71
  npm: 'npm install',
72
72
  };
73
73
 
74
- /**
75
- * Whether a package.json declares a workspace (`workspaces` field).
76
- * @param {string} dir
77
- * @returns {{exists: boolean, declaresWorkspace: boolean}}
78
- */
79
- function readPackageJson(dir) {
80
- const pkgPath = path.join(dir, 'package.json');
81
- if (!fs.existsSync(pkgPath)) return {exists: false, declaresWorkspace: false};
82
- try {
83
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
84
- return {exists: true, declaresWorkspace: pkg.workspaces != null};
85
- } catch {
86
- return {exists: true, declaresWorkspace: false};
87
- }
88
- }
89
-
90
- /**
91
- * The workspace root (AST-048 FR15): the nearest directory at or above the
92
- * target that declares a workspace, by `pnpm-workspace.yaml` or a package.json
93
- * `workspaces` field; outside a workspace, the nearest package.json's
94
- * directory; with no package.json, the target itself.
95
- * @param {string} target
96
- * @returns {string}
97
- */
98
- function findWorkspaceRoot(target) {
99
- let dir = path.resolve(target);
100
- /** @type {string | null} */
101
- let nearestPackage = null;
102
- for (;;) {
103
- const pkg = readPackageJson(dir);
104
- if (
105
- fs.existsSync(path.join(dir, 'pnpm-workspace.yaml')) ||
106
- pkg.declaresWorkspace
107
- ) {
108
- return dir;
109
- }
110
- if (pkg.exists && nearestPackage == null) nearestPackage = dir;
111
- const parent = path.dirname(dir);
112
- if (parent === dir) return nearestPackage ?? path.resolve(target);
113
- dir = parent;
114
- }
115
- }
116
-
117
74
  /**
118
75
  * The project's package manager for an install command (AST-048 FR17, FR19):
119
76
  * the nearest directory from the target up to the workspace root that has a