@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
@@ -1,6 +1,35 @@
1
1
  // @generated by scripts/sync-api-types.mjs from the JSDoc in foundation/**/*.mjs.
2
2
  // DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
3
3
 
4
+ /**
5
+ * What to do when the `astryx` command the text names is not found, as on a
6
+ * machine whose home folder outlives its system folders: the skill file
7
+ * survives a restart while the CLI installed for the computer does not.
8
+ * @param {string} install the install step
9
+ * @returns {string}
10
+ */
11
+ export function computerRestoreLine(install: string): string;
12
+ /**
13
+ * The install step the text names. `judge`: the step a file is judged by, the
14
+ * one it records first, so every copy of one release, two global installs
15
+ * included, finds the same text current. `write`: the step a write records,
16
+ * the global copy's own first, since only the CLI installed for the computer
17
+ * chooses it. Either way npm's when there is no other.
18
+ * @param {{cliRoot?: string, home?: string, for?: 'judge' | 'write'}} [options]
19
+ * @returns {string}
20
+ */
21
+ export function computerInstallStep({ cliRoot, home, for: use, }?: {
22
+ cliRoot?: string;
23
+ home?: string;
24
+ for?: "judge" | "write";
25
+ }): string;
26
+ /**
27
+ * The install step a skill file's text records, or null when it records
28
+ * none.
29
+ * @param {string} content
30
+ * @returns {string | null}
31
+ */
32
+ export function skillInstallStepOf(content: string): string | null;
4
33
  /**
5
34
  * A home-folder path as a person reads it: `~/.claude/skills/astryx`.
6
35
  * @param {string} file
@@ -32,13 +61,21 @@ export function computerSkillLinks(home?: string): Array<{
32
61
  * @returns {string}
33
62
  */
34
63
  export function computerAgentCommand(): string;
64
+ /**
65
+ * The command a skill file's text names: the one in its "Start with" line,
66
+ * or null when the text has none.
67
+ * @param {string} content
68
+ * @returns {string | null}
69
+ */
70
+ export function skillCommandOf(content: string): string | null;
35
71
  /**
36
72
  * The full skill file.
37
- * @param {{command?: string}} [options]
73
+ * @param {{command?: string, install?: string}} [options]
38
74
  * @returns {string}
39
75
  */
40
- export function renderComputerAgentText({ command, }?: {
76
+ export function renderComputerAgentText({ command, install, }?: {
41
77
  command?: string;
78
+ install?: string;
42
79
  }): string;
43
80
  /**
44
81
  * Read-only: the state of the skill file against this release's rendering,
@@ -43,7 +43,17 @@ import {createHash} from 'node:crypto';
43
43
  import {cliDoor} from '../env/cli-command.mjs';
44
44
  import {astryxCommandsOnSearchPath} from '../env/search-path.mjs';
45
45
  import {samePath} from '../fs/path-compare.mjs';
46
- import {MARKER_START, MARKER_END} from './agent-doc-state.mjs';
46
+ import {CLI_ROOT} from '../fs/paths.mjs';
47
+ import {CLI_PACKAGE} from '../env/package-manager.mjs';
48
+ import {isGlobalCopy} from '../release/choose.mjs';
49
+ import {globalUpgradeCommand} from '../release/notices.mjs';
50
+ import {
51
+ MARKER_START,
52
+ MARKER_END,
53
+ SELF_CHECK_CSS,
54
+ WORKFLOW_STEPS,
55
+ WORKFLOW_TITLE,
56
+ } from './agent-doc-state.mjs';
47
57
 
48
58
  /** The command the computer-level text uses when no host door is set. */
49
59
  export const COMPUTER_COMMAND = 'astryx';
@@ -51,6 +61,83 @@ export const COMPUTER_COMMAND = 'astryx';
51
61
  /** The install step for the CLI copy the computer-level text relies on. */
52
62
  export const COMPUTER_INSTALL_STEP = 'npm i -g @astryxdesign/cli';
53
63
 
64
+ /**
65
+ * What to do when the `astryx` command the text names is not found, as on a
66
+ * machine whose home folder outlives its system folders: the skill file
67
+ * survives a restart while the CLI installed for the computer does not.
68
+ * @param {string} install the install step
69
+ * @returns {string}
70
+ */
71
+ export function computerRestoreLine(install) {
72
+ return `If \`${COMPUTER_COMMAND}\` is not found, run \`${install}\` first.`;
73
+ }
74
+
75
+ /**
76
+ * The step the CLI installed for the computer names for itself: the command
77
+ * that installs it again with the same package manager into the same folder,
78
+ * as the update notice names it but with no version. Null for any other copy.
79
+ * @param {string} [cliRoot]
80
+ * @returns {string | null}
81
+ */
82
+ function ownInstallStep(cliRoot = CLI_ROOT) {
83
+ try {
84
+ if (isGlobalCopy(cliRoot)) {
85
+ return globalUpgradeCommand(cliRoot).replace(
86
+ `${CLI_PACKAGE}@latest`,
87
+ CLI_PACKAGE,
88
+ );
89
+ }
90
+ } catch {
91
+ // A copy whose install cannot be read names none of its own.
92
+ }
93
+ return null;
94
+ }
95
+
96
+ /**
97
+ * The step the skill file in `home` records, or null.
98
+ * @param {string} home
99
+ * @returns {string | null}
100
+ */
101
+ function recordedInstallStep(home) {
102
+ try {
103
+ return skillInstallStepOf(fs.readFileSync(computerSkillFile(home), 'utf8'));
104
+ } catch {
105
+ return null;
106
+ }
107
+ }
108
+
109
+ /**
110
+ * The install step the text names. `judge`: the step a file is judged by, the
111
+ * one it records first, so every copy of one release, two global installs
112
+ * included, finds the same text current. `write`: the step a write records,
113
+ * the global copy's own first, since only the CLI installed for the computer
114
+ * chooses it. Either way npm's when there is no other.
115
+ * @param {{cliRoot?: string, home?: string, for?: 'judge' | 'write'}} [options]
116
+ * @returns {string}
117
+ */
118
+ export function computerInstallStep({
119
+ cliRoot = CLI_ROOT,
120
+ home = os.homedir(),
121
+ for: use = 'write',
122
+ } = {}) {
123
+ const own = ownInstallStep(cliRoot);
124
+ const recorded = recordedInstallStep(home);
125
+ return (
126
+ (use === 'judge' ? (recorded ?? own) : (own ?? recorded)) ??
127
+ COMPUTER_INSTALL_STEP
128
+ );
129
+ }
130
+
131
+ /**
132
+ * The install step a skill file's text records, or null when it records
133
+ * none.
134
+ * @param {string} content
135
+ * @returns {string | null}
136
+ */
137
+ export function skillInstallStepOf(content) {
138
+ return /is not found, run `(.+?)` first\./u.exec(content)?.[1] ?? null;
139
+ }
140
+
54
141
  /** The skill folder, relative to the home folder, that holds the one file. */
55
142
  const SKILL_FOLDER = path.join('.agents', 'skills', 'astryx');
56
143
 
@@ -164,27 +251,52 @@ export function computerAgentCommand() {
164
251
  }
165
252
 
166
253
  /**
167
- * The body lines, with `run` as the command.
254
+ * The body lines, with `run` as the command and `install` as the step that
255
+ * installs it.
168
256
  * @param {string} run
257
+ * @param {string} install
169
258
  * @returns {string[]}
170
259
  */
171
- function bodyLines(run) {
260
+ function bodyLines(run, install) {
172
261
  return [
173
262
  'Astryx, a design system and UI library, is available on this computer.',
174
263
  'Use it when you build or change UI, pick components, or style or theme an app.',
175
264
  `Its docs, components, templates, and themes are all reachable through \`${run}\`. Start with \`${run} search '<what you need>'\`.`,
176
265
  `Then read \`${run} component <Name>\` or \`${run} docs <topic>\`, not the package's files.`,
177
266
  "In a project, the project's version of Astryx answers automatically.",
267
+ // How to build, which discovery alone leaves to the agent's own page
268
+ // route: in a project, from the template, with the project agent docs'
269
+ // own workflow lines.
270
+ // A project that already installs Core gets the CLI at that version, as
271
+ // the CLI's own hint for that state says, never a newer CLI beside it.
272
+ `To build UI, work in a project folder (create one if there is none). Add \`@astryxdesign/core\` and \`@astryxdesign/cli\` to a new project or one without \`@astryxdesign/core\`; to a project that already has \`@astryxdesign/core\`, add \`@astryxdesign/cli\` at the same version. Run \`${run} init\` there and follow the agent docs it writes.`,
273
+ WORKFLOW_TITLE,
274
+ ...WORKFLOW_STEPS.map(step => step.replaceAll('`astryx ', `\`${run} `)),
275
+ `Before you deliver, re-read each file; ${SELF_CHECK_CSS}. Then build the project and deliver that build, not a description of it.`,
276
+ // A host's door is the host's to restore; only the global command has an
277
+ // install step to name.
278
+ ...(run === COMPUTER_COMMAND ? [computerRestoreLine(install)] : []),
178
279
  ];
179
280
  }
180
281
 
282
+ /**
283
+ * The command a skill file's text names: the one in its "Start with" line,
284
+ * or null when the text has none.
285
+ * @param {string} content
286
+ * @returns {string | null}
287
+ */
288
+ export function skillCommandOf(content) {
289
+ return /Start with `(.+?) search '/u.exec(content)?.[1] ?? null;
290
+ }
291
+
181
292
  /**
182
293
  * The full skill file.
183
- * @param {{command?: string}} [options]
294
+ * @param {{command?: string, install?: string}} [options]
184
295
  * @returns {string}
185
296
  */
186
297
  export function renderComputerAgentText({
187
298
  command = computerAgentCommand(),
299
+ install = COMPUTER_INSTALL_STEP,
188
300
  } = {}) {
189
301
  return withHash(
190
302
  [
@@ -196,7 +308,7 @@ export function renderComputerAgentText({
196
308
  '---',
197
309
  '',
198
310
  MARKER_START,
199
- ...bodyLines(command),
311
+ ...bodyLines(command, install),
200
312
  MARKER_END,
201
313
  '',
202
314
  ].join('\n'),
@@ -404,7 +516,10 @@ export function inspectComputerAgentText({
404
516
  command,
405
517
  links = [],
406
518
  } = {}) {
407
- const expected = renderComputerAgentText({command});
519
+ const expected = renderComputerAgentText({
520
+ command,
521
+ install: computerInstallStep({home, for: 'judge'}),
522
+ });
408
523
  const folder = path.join(home, SKILL_FOLDER);
409
524
  const made = new Set(links);
410
525
  return [
@@ -517,12 +632,24 @@ function writeCopy(at, expected) {
517
632
  * @returns {{rows: import('../../api/init/init.type.mjs').InitUserFile[], links: string[]}}
518
633
  */
519
634
  export function writeComputerAgentText({home = os.homedir(), command} = {}) {
520
- const expected = renderComputerAgentText({command});
635
+ // Judged by the step the file records, so a file another copy of this
636
+ // release wrote stays as it is; a write records this copy's own step.
637
+ const judged = renderComputerAgentText({
638
+ command,
639
+ install: computerInstallStep({home, for: 'judge'}),
640
+ });
521
641
  const file = computerSkillFile(home);
522
642
  const folder = path.dirname(file);
523
643
  /** @type {import('../../api/init/init.type.mjs').InitUserFile[]} */
524
644
  const rows = [];
525
- const state = stateOf(file, expected);
645
+ const state = stateOf(file, judged);
646
+ const expected =
647
+ state === 'current'
648
+ ? judged
649
+ : renderComputerAgentText({
650
+ command,
651
+ install: computerInstallStep({home, for: 'write'}),
652
+ });
526
653
  if (state === 'foreign' || state === 'edited') {
527
654
  rows.push({path: file, action: 'kept', reason: keptReason(state)});
528
655
  } else if (state === 'current') {
@@ -24,10 +24,18 @@ import {
24
24
  computerSkillLinks,
25
25
  inspectComputerAgentText,
26
26
  removeComputerAgentText,
27
+ computerInstallStep,
27
28
  renderComputerAgentText,
29
+ skillInstallStepOf,
28
30
  writeComputerAgentText,
29
31
  } from './computer-agent-text.mjs';
30
- import {MARKER_END, MARKER_START} from './agent-doc-state.mjs';
32
+ import {
33
+ MARKER_END,
34
+ MARKER_START,
35
+ SELF_CHECK_CSS,
36
+ WORKFLOW_STEPS,
37
+ WORKFLOW_TITLE,
38
+ } from './agent-doc-state.mjs';
31
39
  import {runWithCliDoor} from '../env/cli-command.mjs';
32
40
  import {runCli} from '../../test-utils/run-cli.mjs';
33
41
  import {parse as parseYaml} from 'yaml';
@@ -123,17 +131,106 @@ describe('renderComputerAgentText', () => {
123
131
 
124
132
  it('stays short, and tells agents to read docs through the CLI', () => {
125
133
  const text = renderComputerAgentText({command: 'astryx'});
126
- expect(text.split('\n').length).toBeLessThanOrEqual(15);
134
+ expect(text.split('\n').length).toBeLessThanOrEqual(22);
127
135
  expect(text).toContain(
128
136
  "Then read `astryx component <Name>` or `astryx docs <topic>`, not the package's files.",
129
137
  );
130
138
  });
131
139
 
140
+ // Discovery alone leaves the build to the agent's own page route, which
141
+ // may not install packages; the skill names the route and the project
142
+ // agent docs' own workflow lines.
143
+ it('gives the build workflow: a project with Astryx installed and init run, the shared template steps, then check and deliver the build', () => {
144
+ const text = renderComputerAgentText({command: 'astryx'});
145
+ expect(text).toContain(
146
+ 'To build UI, work in a project folder (create one if there is none). Add `@astryxdesign/core` and `@astryxdesign/cli` to a new project or one without `@astryxdesign/core`; to a project that already has `@astryxdesign/core`, add `@astryxdesign/cli` at the same version. Run `astryx init` there and follow the agent docs it writes.',
147
+ );
148
+ for (const line of [WORKFLOW_TITLE, ...WORKFLOW_STEPS]) {
149
+ expect(text).toContain(line);
150
+ }
151
+ expect(text).toContain(`re-read each file; ${SELF_CHECK_CSS}.`);
152
+ expect(text).toContain('deliver that build');
153
+ });
154
+
132
155
  it("uses the host's door when one is set", () => {
133
156
  const text = runWithCliDoor('acme astryx', () => renderComputerAgentText());
134
157
  expect(text).toContain("`acme astryx search '<what you need>'`");
135
158
  expect(text).not.toMatch(/`astryx /u);
136
159
  });
160
+
161
+ // A machine can keep the home folder and lose the CLI installed for the
162
+ // computer, so the skill says how to get it back, with no version.
163
+ it('names the install step for when `astryx` is not found, and only for the global command', () => {
164
+ expect(
165
+ renderComputerAgentText({
166
+ command: 'astryx',
167
+ install: 'npm i -g @astryxdesign/cli',
168
+ }),
169
+ ).toContain(
170
+ 'If `astryx` is not found, run `npm i -g @astryxdesign/cli` first.',
171
+ );
172
+ // It repeats the step that installed the copy writing it, which doctor
173
+ // reads back.
174
+ const bun = renderComputerAgentText({
175
+ command: 'astryx',
176
+ install: 'bun add -g @astryxdesign/cli',
177
+ });
178
+ expect(bun).toContain(
179
+ 'If `astryx` is not found, run `bun add -g @astryxdesign/cli` first.',
180
+ );
181
+ expect(skillInstallStepOf(bun)).toBe('bun add -g @astryxdesign/cli');
182
+ });
183
+
184
+ // Rendering is pure: its default step never depends on the skill in the
185
+ // home folder of the computer the test runs on.
186
+ it("renders npm's step by default, whatever skill the caller's home folder holds", () => {
187
+ const home = tmp('caller-home');
188
+ const file = computerSkillFile(home);
189
+ fs.mkdirSync(path.dirname(file), {recursive: true});
190
+ fs.writeFileSync(
191
+ file,
192
+ renderComputerAgentText({
193
+ command: 'astryx',
194
+ install: 'pnpm add -g @astryxdesign/cli',
195
+ }),
196
+ );
197
+ const saved = process.env.HOME;
198
+ process.env.HOME = home;
199
+ try {
200
+ expect(renderComputerAgentText({command: 'astryx'})).toContain(
201
+ 'run `npm i -g @astryxdesign/cli` first.',
202
+ );
203
+ } finally {
204
+ process.env.HOME = saved;
205
+ }
206
+ });
207
+
208
+ // Only the CLI installed for the computer chooses the step; every other
209
+ // copy of the release, such as a project's own CLI, keeps the one the file
210
+ // records, so it neither reports the file as different nor rewrites it.
211
+ it('keeps the step the file records for a copy that is not installed for the computer', () => {
212
+ const home = tmp('home');
213
+ const file = computerSkillFile(home);
214
+ fs.mkdirSync(path.dirname(file), {recursive: true});
215
+ const pnpm = renderComputerAgentText({
216
+ command: 'astryx',
217
+ install: 'pnpm add -g @astryxdesign/cli',
218
+ });
219
+ fs.writeFileSync(file, pnpm);
220
+ expect(computerInstallStep({home})).toBe('pnpm add -g @astryxdesign/cli');
221
+ expect(inspectComputerAgentText({home, command: 'astryx'})[0].state).toBe(
222
+ 'current',
223
+ );
224
+ writeComputerAgentText({home, command: 'astryx'});
225
+ expect(fs.readFileSync(file, 'utf8')).toBe(pnpm);
226
+ // No file yet: npm's step.
227
+ expect(computerInstallStep({home: tmp('empty-home')})).toBe(
228
+ 'npm i -g @astryxdesign/cli',
229
+ );
230
+ expect(renderComputerAgentText({command: 'acme astryx'})).not.toContain(
231
+ 'npm i -g',
232
+ );
233
+ });
137
234
  });
138
235
 
139
236
  describe('the link table', () => {
@@ -40,7 +40,7 @@ import {parseConfig} from '../../authoring/config/parse.mjs';
40
40
  import {
41
41
  loadIntegrations,
42
42
  loadLocalIntegration,
43
- resolvePackageDir,
43
+ resolveInstalledIntegrationDir,
44
44
  } from '../integrations/integrations.mjs';
45
45
  import {loadAutolinkCandidates} from '../integrations/autolink.mjs';
46
46
  import {
@@ -65,7 +65,7 @@ import {
65
65
  discoverValidIntegrationComponents,
66
66
  } from '../discovery/component-discovery.mjs';
67
67
  import {resolveComponentReplacements} from '../discovery/component-replacement.mjs';
68
- import {CLI_ROOT, findCoreDir, findProjectCoreDir} from '../fs/paths.mjs';
68
+ import {CLI_ROOT, findCoreDir, findPackageRoot, findProjectCoreDir} from '../fs/paths.mjs';
69
69
  import {
70
70
  applyTemplateReplacements,
71
71
  effectiveTemplateDiscovery,
@@ -89,6 +89,10 @@ import {
89
89
  } from '../../assets/codemods/integration-discovery.mjs';
90
90
  import {validateLoadedIntegration} from '../integrations/validate-contributions.mjs';
91
91
  import {recordDeclaredCliCommands} from '../env/cli-command.mjs';
92
+ import {
93
+ hostedRun,
94
+ recordResolvedIntegrations,
95
+ } from '../runtime/package-root.mjs';
92
96
  import {
93
97
  InMemoryConfigCache,
94
98
  cacheKey,
@@ -121,9 +125,16 @@ function errorMessage(err) {
121
125
  */
122
126
  export function computerIntegrationCandidates(cwd = process.cwd()) {
123
127
  try {
124
- const cliRoot = isOutsideProject(cwd) ? CLI_ROOT : globalCliRoot(cwd);
128
+ const outside = isOutsideProject(cwd);
129
+ const cliRoot = outside ? CLI_ROOT : globalCliRoot(cwd);
125
130
  return cliRoot
126
- ? scanComputerIntegrations({cliRoot, release: cliVersion()})
131
+ ? scanComputerIntegrations({
132
+ cliRoot,
133
+ release: cliVersion(),
134
+ // Outside a project, the integrations a host approves load from the
135
+ // package set that serves the run too (cli-surface INV30).
136
+ approved: outside ? (hostedRun()?.integrations ?? []) : [],
137
+ })
127
138
  : [];
128
139
  } catch {
129
140
  return [];
@@ -212,7 +223,7 @@ async function loadConfiguredIntegrations(specs, {cwd, fresh}) {
212
223
  /** @type {string|undefined} */
213
224
  let packageDir;
214
225
  try {
215
- packageDir = resolvePackageDir(spec, cwd);
226
+ packageDir = resolveInstalledIntegrationDir(spec, cwd);
216
227
  } catch {
217
228
  // Not a bare package name: there is no install location to record.
218
229
  }
@@ -315,22 +326,6 @@ export const CONFIG_BASENAMES = [
315
326
  export const DEFAULT_ISSUES_URL =
316
327
  'https://github.com/facebook/astryx/issues/new';
317
328
 
318
- /**
319
- * Find the directory of the nearest package.json walking up from startDir.
320
- * @param {string} startDir
321
- * @returns {string|null}
322
- */
323
- function findPackageRoot(startDir) {
324
- let dir = startDir;
325
- for (let i = 0; i < 50; i++) {
326
- if (fs.existsSync(path.join(dir, 'package.json'))) return dir;
327
- const parent = path.dirname(dir);
328
- if (parent === dir) break;
329
- dir = parent;
330
- }
331
- return null;
332
- }
333
-
334
329
  /**
335
330
  * Whether this project accepts `debug` handlers contributed by the
336
331
  * integrations it loads.
@@ -495,7 +490,8 @@ export class Project {
495
490
  // The project root: the config's directory when there is one (findConfigPath
496
491
  // resolves the config as a sibling of the nearest package.json, so the two
497
492
  // agree), otherwise that package.json's directory. Dependencies are declared
498
- // there, and node_modules sits there.
493
+ // there, and installed packages resolve from there up the node_modules
494
+ // chain.
499
495
  const projectDir = configPath
500
496
  ? path.dirname(configPath)
501
497
  : (findPackageRoot(cwd) ?? cwd);
@@ -580,6 +576,8 @@ export class Project {
580
576
  : []),
581
577
  ]);
582
578
  loadedIntegrations = resolution.integrations;
579
+ // A hosted run returns each package it resolved (cli-surface INV30).
580
+ recordResolvedIntegrations(loadedIntegrations);
583
581
  // The picker (getCliInvocation) is synchronous, so the commands these
584
582
  // integrations declare are recorded here, where they become known.
585
583
  recordDeclaredCliCommands(projectDir, loadedIntegrations);
@@ -291,6 +291,32 @@ describe('Project.load', () => {
291
291
  });
292
292
  });
293
293
 
294
+ describe('Project.issues and a near-miss manifest key', () => {
295
+ it.each([
296
+ [
297
+ 'alone, while the loaded integration fills providerId from the package name',
298
+ {providerID: '@acme/widgets'},
299
+ 'Fix: rename "providerID" to "providerId" in astryx.integration.mjs.',
300
+ ],
301
+ [
302
+ 'beside the key it suggests',
303
+ {providerID: '@acme/widgets', providerId: '@acme/widgets'},
304
+ 'Fix: remove "providerID" from astryx.integration.mjs; it already sets "providerId".',
305
+ ],
306
+ ])(
307
+ 'reads the keys the manifest declares: providerID %s',
308
+ async (_label, unknownKeys, fix) => {
309
+ scaffold({unknownKeys});
310
+ const project = await Project.load(tmpDir);
311
+ const keys = (await project.issues()).filter(
312
+ issue => issue.code === 'unknown_manifest_key',
313
+ );
314
+ expect(keys).toHaveLength(1);
315
+ expect(keys[0].message).toContain(fix);
316
+ },
317
+ );
318
+ });
319
+
294
320
  describe('Project.load and the cliCommand an integration declares', () => {
295
321
  it('records it, so the picker prints commands with it', async () => {
296
322
  scaffold({cliCommand: 'acme astryx'});
@@ -830,6 +856,36 @@ describe('Project loads each integration in isolation', () => {
830
856
  });
831
857
  });
832
858
 
859
+ describe('Project finds configured integrations a workspace hoists', () => {
860
+ // npm, Yarn and Bun workspaces install a member's dependencies at the
861
+ // workspace root, so the member's own node_modules does not hold them.
862
+ it('loads an integration named in a member config from the workspace root', async () => {
863
+ scaffold();
864
+ fs.writeFileSync(
865
+ path.join(tmpDir, 'package.json'),
866
+ JSON.stringify({name: 'root', private: true, workspaces: ['apps/*']}),
867
+ );
868
+ fs.rmSync(path.join(tmpDir, 'astryx.config.mjs'));
869
+ const member = path.join(tmpDir, 'apps', 'web');
870
+ fs.mkdirSync(member, {recursive: true});
871
+ fs.writeFileSync(path.join(member, 'package.json'), JSON.stringify({name: 'web'}));
872
+ fs.writeFileSync(
873
+ path.join(member, 'astryx.config.mjs'),
874
+ `export default ${JSON.stringify({integrations: ['@acme/widgets']})};\n`,
875
+ );
876
+
877
+ const project = await Project.load(member);
878
+
879
+ const widgets = (await project.components()).filter(
880
+ component => component.package === '@acme/widgets',
881
+ );
882
+ expect(widgets.map(component => component.name)).toEqual(['Widget']);
883
+ expect(await project.issues()).not.toContainEqual(
884
+ expect.objectContaining({package: '@acme/widgets', code: 'integration_error'}),
885
+ );
886
+ });
887
+ });
888
+
833
889
  describe('Project.issuesUrl routing', () => {
834
890
  it('routes core to config.issuesUrl, falling back to the default', async () => {
835
891
  scaffold({issuesUrl: 'https://example.com/core'});
@@ -83,5 +83,12 @@ export function auditAuthoringSelfDocs({ root, sources, budget, }?: {
83
83
  }>;
84
84
  /** The directory the self-docs live under. */
85
85
  export const AUTHORING_ROOT: string;
86
+ /** The guide that holds every authoring schema, one section each. */
87
+ export const AUTHORING_REFERENCE_NAME: "field-reference";
88
+ /**
89
+ * Its route. `astryx docs authoring <name>` reads one of its sections through
90
+ * the authoring namespace (spec:AST-046 FR6).
91
+ */
92
+ export const AUTHORING_REFERENCE_ROUTE: "authoring/field-reference";
86
93
  /** Every authoring self-doc, relative to {@link AUTHORING_ROOT}, in reading order. */
87
94
  export const AUTHORING_SELF_DOCS: string[];
@@ -9,7 +9,7 @@
9
9
  * `authoring` reference topic with one section per self-doc, and an audit
10
10
  * naming any self-doc the topic cannot reach, any that fails to load, and any
11
11
  * section over the docs output budget.
12
- * @position Read by assets/docs/authoring.doc.mjs (the topic) and by Doctor
12
+ * @position Read by assets/docs/tree/field-reference.doc.mjs (the guide) and by Doctor
13
13
  * (the audit). Lives in foundation because authoring/ holds only contracts.
14
14
  * A new `*.doc.mjs` under authoring/ must be added to the list, or Doctor and
15
15
  * the self-doc tests fail.
@@ -27,6 +27,15 @@ import {
27
27
  /** The directory the self-docs live under. */
28
28
  export const AUTHORING_ROOT = path.join(CLI_ROOT, 'authoring');
29
29
 
30
+ /** The guide that holds every authoring schema, one section each. */
31
+ export const AUTHORING_REFERENCE_NAME = 'field-reference';
32
+
33
+ /**
34
+ * Its route. `astryx docs authoring <name>` reads one of its sections through
35
+ * the authoring namespace (spec:AST-046 FR6).
36
+ */
37
+ export const AUTHORING_REFERENCE_ROUTE = `authoring/${AUTHORING_REFERENCE_NAME}`;
38
+
30
39
  /** Every authoring self-doc, relative to {@link AUTHORING_ROOT}, in reading order. */
31
40
  export const AUTHORING_SELF_DOCS = [
32
41
  'integration/integration.doc.mjs',
@@ -189,11 +198,14 @@ export function selfDocSection(doc) {
189
198
  */
190
199
  export function buildAuthoringReferenceDoc(docs) {
191
200
  return /** @type {any} */ ({
192
- name: 'authoring',
193
- title: 'Authoring Reference',
201
+ type: 'generic',
202
+ name: AUTHORING_REFERENCE_NAME,
203
+ placement: {parent: 'namespace:authoring', slot: 'reference', order: 10},
204
+ title: 'Field reference',
194
205
  category: 'guide',
206
+ keywords: ['authoring reference', 'field reference', 'every field'],
195
207
  description:
196
- 'Every file an integration author writes, field by field: the integration manifest, astryx.config, codemods, identity, and each doc type. The step-by-step guides are under `astryx docs cli/integrations`.',
208
+ 'Every file an integration author writes, field by field: the integration manifest, astryx.config, codemods, identity, and each doc type. The guides that use them are the rest of {@link namespace:authoring}.',
197
209
  sections: docs.map(selfDocSection),
198
210
  });
199
211
  }
@@ -38,20 +38,32 @@ describe('authoring self-docs', () => {
38
38
  });
39
39
  });
40
40
 
41
- it('builds a valid topic with one section per self-doc', async () => {
42
- const topic = await buildAuthoringTopic();
41
+ it('builds a valid guide with one section per self-doc, placed under authoring', async () => {
42
+ const {placement, ...topic} = await buildAuthoringTopic();
43
43
  expect(problemsInTopic(topic)).toEqual([]);
44
44
  expect(topic.sections).toHaveLength(AUTHORING_SELF_DOCS.length);
45
+ expect(placement).toEqual({
46
+ parent: 'namespace:authoring',
47
+ slot: 'reference',
48
+ order: 10,
49
+ });
45
50
  });
46
51
 
47
52
  it(
48
53
  'is readable progressively through the docs API',
49
54
  async () => {
50
- const index = await docs('authoring', undefined, {index: true});
55
+ const index = await docs('authoring/field-reference', undefined, {
56
+ index: true,
57
+ });
51
58
  expect(index.type).toBe('docs.index');
52
59
  expect(index.data.sections.map(s => s.id)).toContain('integration');
60
+ // The namespace answers a section read from the field reference, as
61
+ // `astryx docs authoring <name>` always has.
53
62
  const section = await docs('authoring', 'integration');
54
63
  expect(section.data.title).toBe('Astryx Integration');
64
+ expect(
65
+ (await docs('authoring/field-reference', 'integration')).data,
66
+ ).toEqual(section.data);
55
67
  expect(section.data.content.some(block => block.type === 'table')).toBe(
56
68
  true,
57
69
  );
@@ -30,7 +30,7 @@ export function tracePublicAuthoringTypes(root?: string): {
30
30
  * document something public: a public type declared beside it, and, when its
31
31
  * title is the name of a type declared beside it, that type.
32
32
  * @param {{root?: string, sources?: string[], topicKeys?: Set<string> | null}} [options]
33
- * `topicKeys` are the keys `astryx docs authoring --index` lists; null skips
33
+ * `topicKeys` are the keys `astryx docs authoring/field-reference --index` lists; null skips
34
34
  * that comparison.
35
35
  * @returns {Promise<{
36
36
  * types: number,