@astryxdesign/cli 0.7.2 → 0.7.3-canary.0bf23bd

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 (337) hide show
  1. package/README.md +104 -71
  2. package/api/build/_adapter.d.mts +58 -0
  3. package/api/build/_adapter.mjs +115 -3
  4. package/api/build/blocks/blocks.d.mts +42 -0
  5. package/api/build/blocks/blocks.mjs +169 -0
  6. package/api/build/blocks/blocks.test.mjs +108 -0
  7. package/api/build/blocks/blocks.weights.json +1 -0
  8. package/api/build/build.mjs +25 -6
  9. package/api/build/kit/answer.d.mts +14 -0
  10. package/api/build/kit/answer.mjs +85 -0
  11. package/api/build/kit/answer.test.mjs +38 -0
  12. package/api/build/kit/kind.d.mts +25 -0
  13. package/api/build/kit/kind.mjs +170 -0
  14. package/api/build/kit/kit.d.mts +2 -4
  15. package/api/build/kit/kit.mjs +8 -398
  16. package/api/build/kit/rank.d.mts +3 -32
  17. package/api/build/kit/rank.mjs +25 -342
  18. package/api/build/kit/rank.test.mjs +2 -1
  19. package/api/build/kit/signals.d.mts +60 -0
  20. package/api/build/kit/signals.mjs +284 -0
  21. package/api/build/kit/start.d.mts +32 -0
  22. package/api/build/kit/start.mjs +163 -0
  23. package/api/build/kit/words.d.mts +72 -0
  24. package/api/build/kit/words.mjs +202 -0
  25. package/api/build/tree.d.mts +20 -0
  26. package/api/build/tree.mjs +52 -0
  27. package/api/build/tree.test.mjs +177 -0
  28. package/api/build/tree.weights.json +1 -0
  29. package/api/component/component.mjs +17 -3
  30. package/api/component/component.test.mjs +15 -0
  31. package/api/component/doc-links.test.mjs +2 -2
  32. package/api/discover/_adapter.mjs +36 -9
  33. package/api/discover/_adapter.test.mjs +42 -0
  34. package/api/docs/_adapter.d.mts +2 -2
  35. package/api/docs/_adapter.mjs +78 -23
  36. package/api/docs/docs-homes.test.mjs +127 -0
  37. package/api/docs/docs.doc.mjs +3 -3
  38. package/api/docs/docs.test.mjs +35 -19
  39. package/api/docs/integration-tree.test.mjs +20 -20
  40. package/api/docs/integrationDocs.test.mjs +1 -1
  41. package/api/docs/namespace-section.test.mjs +6 -6
  42. package/api/docs/reference-blocks.test.mjs +2 -2
  43. package/api/doctor/doctor.mjs +102 -24
  44. package/api/doctor/doctor.test.mjs +37 -12
  45. package/api/hook/hook.mjs +1 -1
  46. package/api/init/init.doc.mjs +20 -6
  47. package/api/init/init.test.mjs +18 -2
  48. package/api/init/init.type.d.mts +3 -3
  49. package/api/init/init.type.mjs +3 -3
  50. package/api/init/remove/remove.mjs +17 -4
  51. package/api/init/remove/remove.test.mjs +114 -2
  52. package/api/init/run/run.mjs +69 -6
  53. package/api/integration/_adapter.d.mts +8 -0
  54. package/api/integration/_adapter.mjs +21 -1
  55. package/api/integration/authoring-checks.mjs +83 -9
  56. package/api/integration/authoring-checks.test.mjs +220 -0
  57. package/api/integration/pack-check.mjs +123 -4
  58. package/api/integration/pack-check.test.mjs +87 -0
  59. package/api/integration/validate-integration-case-disk.test.mjs +111 -0
  60. package/api/integration/validate-integration-gaps.test.mjs +1964 -0
  61. package/api/integration/validate-integration.d.mts +11 -2
  62. package/api/integration/validate-integration.mjs +250 -32
  63. package/api/integration/validate-integration.type.d.mts +59 -1
  64. package/api/integration/validate-integration.type.mjs +46 -2
  65. package/api/integration/validateIntegration.doc.mjs +1 -1
  66. package/api/search/ORDERING.md +28 -0
  67. package/api/search/_adapter.d.mts +82 -2
  68. package/api/search/_adapter.mjs +157 -10
  69. package/api/search/_adapter.test.mjs +173 -0
  70. package/api/search/_candidate-score.d.mts +40 -0
  71. package/api/search/_candidate-score.mjs +238 -0
  72. package/api/search/_cards.json +6098 -0
  73. package/api/search/_compound.d.mts +50 -0
  74. package/api/search/_compound.mjs +170 -0
  75. package/api/search/_doc-searcher.d.mts +4 -0
  76. package/api/search/_doc-searcher.mjs +23 -0
  77. package/api/search/_doc-searcher.test.mjs +36 -0
  78. package/api/search/_doc-searcher.weights.json +1 -0
  79. package/api/search/_doc-text.d.mts +30 -0
  80. package/api/search/_doc-text.mjs +87 -0
  81. package/api/search/_entries.d.mts +52 -0
  82. package/api/search/_entries.mjs +214 -0
  83. package/api/search/_features.d.mts +35 -0
  84. package/api/search/_features.mjs +155 -0
  85. package/api/search/_gather-components.d.mts +26 -0
  86. package/api/search/_gather-components.mjs +284 -0
  87. package/api/search/_gather-docs.d.mts +20 -0
  88. package/api/search/_gather-docs.mjs +254 -0
  89. package/api/search/_gather-hooks.d.mts +12 -0
  90. package/api/search/_gather-hooks.mjs +52 -0
  91. package/api/search/_gather-templates.d.mts +25 -0
  92. package/api/search/_gather-templates.mjs +107 -0
  93. package/api/search/_lexical.d.mts +135 -0
  94. package/api/search/_lexical.mjs +351 -0
  95. package/api/search/_main-searcher.d.mts +19 -0
  96. package/api/search/_main-searcher.mjs +115 -0
  97. package/api/search/_main-searcher.test.mjs +106 -0
  98. package/api/search/_main-searcher.weights.json +1 -0
  99. package/api/search/_module-doc.d.mts +32 -0
  100. package/api/search/_module-doc.mjs +78 -0
  101. package/api/search/_order.d.mts +32 -0
  102. package/api/search/_order.mjs +162 -0
  103. package/api/search/_order.test.mjs +72 -0
  104. package/api/search/_priority.d.mts +50 -0
  105. package/api/search/_priority.mjs +139 -0
  106. package/api/search/_query-words.d.mts +25 -0
  107. package/api/search/_query-words.mjs +92 -0
  108. package/api/search/_query.d.mts +65 -0
  109. package/api/search/_query.mjs +176 -0
  110. package/api/search/_result.d.mts +97 -0
  111. package/api/search/_result.mjs +150 -0
  112. package/api/search/_score.d.mts +60 -0
  113. package/api/search/_score.mjs +283 -0
  114. package/api/search/_synonyms.d.mts +21 -0
  115. package/api/search/_synonyms.mjs +67 -0
  116. package/api/search/_template-searcher.d.mts +4 -0
  117. package/api/search/_template-searcher.mjs +23 -0
  118. package/api/search/_template-searcher.test.mjs +40 -0
  119. package/api/search/_template-searcher.weights.json +1 -0
  120. package/api/search/_titles.d.mts +33 -0
  121. package/api/search/_titles.mjs +65 -0
  122. package/api/search/_words.d.mts +64 -0
  123. package/api/search/_words.mjs +157 -0
  124. package/api/search/search.d.mts +22 -305
  125. package/api/search/search.doc.mjs +8 -6
  126. package/api/search/search.mjs +56 -2482
  127. package/api/search/search.test.mjs +14 -14
  128. package/api/search/search.type.d.mts +6 -1
  129. package/api/search/search.type.mjs +6 -1
  130. package/api/search/tree.d.mts +15 -0
  131. package/api/search/tree.mjs +40 -0
  132. package/api/search/tree.test.mjs +66 -0
  133. package/api/search/tree.weights.json +1 -0
  134. package/api/theme/build/build.packed-old-core.test.mjs +1 -0
  135. package/api/upgrade/_adapter.d.mts +57 -9
  136. package/api/upgrade/_adapter.mjs +198 -28
  137. package/api/upgrade/registry/registry.d.mts +10 -4
  138. package/api/upgrade/registry/registry.mjs +33 -16
  139. package/api/upgrade/registry/registry.test.mjs +85 -0
  140. package/api/upgrade/run/run.d.mts +4 -2
  141. package/api/upgrade/run/run.mjs +13 -2
  142. package/api/upgrade/run/run.test.mjs +417 -3
  143. package/api/upgrade/upgrade.doc.mjs +10 -5
  144. package/api/upgrade/upgrade.mjs +13 -2
  145. package/api/upgrade/upgrade.test.mjs +194 -0
  146. package/api/upgrade/upgrade.type.d.mts +5 -3
  147. package/api/upgrade/upgrade.type.mjs +8 -6
  148. package/assets/codemods/transforms/v0.7.0/__tests__/validate-data-token-ownership.test.mjs +11 -0
  149. package/assets/codemods/transforms/v0.7.0/validate-data-token-ownership.mjs +3 -3
  150. package/assets/docs/author-a-theme.doc.mjs +2 -2
  151. package/assets/docs/is-my-code-good.doc.mjs +287 -0
  152. package/assets/docs/tree/add-a-component.doc.mjs +1 -1
  153. package/assets/docs/tree/add-a-theme.doc.mjs +1 -1
  154. package/assets/docs/tree/add-a-topic.doc.mjs +2 -2
  155. package/assets/docs/tree/agent-guidance.doc.mjs +1 -1
  156. package/assets/docs/tree/api.doc.mjs +6 -0
  157. package/assets/docs/tree/{integrations.doc.mjs → authoring.doc.mjs} +15 -6
  158. package/assets/docs/tree/block-template.doc.mjs +1 -1
  159. package/assets/docs/tree/build-the-template.doc.mjs +1 -1
  160. package/assets/docs/tree/building-blocks.doc.mjs +2 -2
  161. package/assets/docs/tree/check-the-props.doc.mjs +1 -1
  162. package/assets/docs/tree/check-your-docs.doc.mjs +2 -2
  163. package/assets/docs/tree/checks.doc.mjs +2 -2
  164. package/assets/docs/tree/cli-command.doc.mjs +1 -1
  165. package/assets/docs/tree/cli.doc.mjs +1 -1
  166. package/assets/docs/tree/codemods.doc.mjs +1 -1
  167. package/assets/docs/tree/component-family.doc.mjs +1 -1
  168. package/assets/docs/tree/component-imports.doc.mjs +1 -1
  169. package/assets/docs/tree/components.doc.mjs +1 -1
  170. package/assets/docs/tree/configuration.doc.mjs +1 -1
  171. package/assets/docs/tree/debug-and-gap-reports.doc.mjs +1 -1
  172. package/assets/docs/tree/define-the-theme.doc.mjs +1 -1
  173. package/assets/docs/tree/describe-the-component.doc.mjs +1 -1
  174. package/assets/docs/tree/docs.doc.mjs +1 -1
  175. package/assets/docs/tree/document-the-template.doc.mjs +1 -1
  176. package/assets/docs/tree/document-the-theme.doc.mjs +1 -1
  177. package/assets/docs/tree/export-template-assets.doc.mjs +1 -1
  178. package/assets/docs/tree/extend-or-replace.doc.mjs +1 -1
  179. package/assets/docs/{authoring.doc.mjs → tree/field-reference.doc.mjs} +4 -3
  180. package/assets/docs/tree/fonts-and-assets.doc.mjs +1 -1
  181. package/assets/docs/tree/generate-a-palette.doc.mjs +1 -1
  182. package/assets/docs/tree/global-integrations.doc.mjs +2 -2
  183. package/assets/docs/tree/grade-template-with-agent.doc.mjs +4 -4
  184. package/assets/docs/tree/help.doc.mjs +2 -2
  185. package/assets/docs/tree/host-entry.doc.mjs +128 -0
  186. package/assets/docs/tree/links.doc.mjs +1 -1
  187. package/assets/docs/tree/package-and-test.doc.mjs +1 -1
  188. package/assets/docs/tree/page-template.doc.mjs +1 -1
  189. package/assets/docs/tree/publishing.doc.mjs +1 -1
  190. package/assets/docs/tree/quick-start.doc.mjs +1 -1
  191. package/assets/docs/tree/replace-a-core-component.doc.mjs +1 -1
  192. package/assets/docs/tree/replace-a-core-template.doc.mjs +1 -1
  193. package/assets/docs/tree/sections-and-placement.doc.mjs +1 -1
  194. package/assets/docs/tree/see-it-in-an-app.doc.mjs +1 -1
  195. package/assets/docs/tree/ship.doc.mjs +2 -2
  196. package/assets/docs/tree/short-and-findable.doc.mjs +1 -1
  197. package/assets/docs/tree/single-component.doc.mjs +1 -1
  198. package/assets/docs/tree/start-a-template.doc.mjs +1 -1
  199. package/assets/docs/tree/subcomponent.doc.mjs +1 -1
  200. package/assets/docs/tree/template-assets.doc.mjs +1 -1
  201. package/assets/docs/tree/template-doc-overview.doc.mjs +2 -2
  202. package/assets/docs/tree/template-fonts.doc.mjs +1 -1
  203. package/assets/docs/tree/template-grading-rubric.doc.mjs +17 -38
  204. package/assets/docs/tree/template-icons.doc.mjs +1 -1
  205. package/assets/docs/tree/template-images-media.doc.mjs +1 -1
  206. package/assets/docs/tree/template-styles.doc.mjs +1 -1
  207. package/assets/docs/tree/templates.doc.mjs +1 -1
  208. package/assets/docs/tree/test-in-an-app.doc.mjs +1 -1
  209. package/assets/docs/tree/test-template-in-app.doc.mjs +1 -1
  210. package/assets/docs/tree/themes.doc.mjs +1 -1
  211. package/assets/docs/tree/tokens-data.doc.mjs +2 -2
  212. package/assets/docs/tree/troubleshooting.doc.mjs +1 -1
  213. package/assets/docs/tree/upgrading.doc.mjs +1 -1
  214. package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +1 -1
  215. package/assets/docs/tree/verify-packed-template.doc.mjs +1 -1
  216. package/assets/docs/tree/versioning.doc.mjs +1 -1
  217. package/assets/docs/tree/write-good-templates.doc.mjs +1 -1
  218. package/assets/docs/tree/write-the-template-file.doc.mjs +1 -1
  219. package/assets/docs/use-integrations.doc.mjs +120 -0
  220. package/assets/docs/working-with-ai.doc.mjs +6 -2
  221. package/assets/templates/themes/butter/butterTheme.ts +4 -2
  222. package/assets/templates/themes/stone/stoneTheme.ts +4 -2
  223. package/assets/templates/themes/y2k/y2kTheme.ts +4 -2
  224. package/authoring/debug/parse.d.mts +1 -1
  225. package/authoring/doctypes/schema/schema.doc.mjs +1 -1
  226. package/authoring/integration/integration.doc.mjs +1 -1
  227. package/authoring/integration/type.ts +1 -1
  228. package/clients/cli/bin/astryx.mjs +7 -78
  229. package/clients/cli/command-result-coverage.test.mjs +6 -5
  230. package/clients/cli/commands/build-theme.color-scheme.test.mjs +1 -1
  231. package/clients/cli/commands/{component/index.mjs → component.mjs} +37 -37
  232. package/clients/cli/commands/component.test.mjs +6 -6
  233. package/clients/cli/commands/docs.doc.mjs +2 -2
  234. package/clients/cli/commands/docs.mjs +1 -1
  235. package/clients/cli/commands/docs.test.mjs +14 -14
  236. package/clients/cli/commands/doctor.mjs +30 -0
  237. package/clients/cli/commands/doctor.test.mjs +65 -0
  238. package/clients/cli/commands/{hook/index.mjs → hook.mjs} +22 -22
  239. package/clients/cli/commands/import-hint-correctness.test.mjs +1 -1
  240. package/clients/cli/commands/init.behavior.test.mjs +230 -1
  241. package/clients/cli/commands/init.doc.mjs +28 -9
  242. package/clients/cli/commands/init.flag-help.test.mjs +1 -0
  243. package/clients/cli/commands/init.mjs +2 -2
  244. package/clients/cli/commands/integration-check.test.mjs +48 -0
  245. package/clients/cli/commands/integration.doc.mjs +1 -1
  246. package/clients/cli/commands/manifest.doc.mjs +1 -1
  247. package/clients/cli/commands/manifest.mjs +67 -0
  248. package/clients/cli/commands/outside-project.test.mjs +2 -1
  249. package/clients/cli/commands/postinstall.doc.mjs +23 -0
  250. package/clients/cli/commands/postinstall.mjs +31 -0
  251. package/clients/cli/commands/setup-nudge.test.mjs +61 -0
  252. package/clients/cli/commands/{build-theme.mjs → theme.mjs} +1 -1
  253. package/clients/cli/commands/upgrade.doc.mjs +4 -2
  254. package/clients/cli/commands/upgrade.mjs +9 -2
  255. package/clients/cli/commands/upgrade.subfolder-path.test.mjs +97 -0
  256. package/clients/cli/index.mjs +29 -86
  257. package/clients/cli/lib/component-format.mjs +1 -1
  258. package/clients/cli/lib/define-command.mjs +12 -7
  259. package/clients/cli/lib/exit-codes.test.mjs +5 -0
  260. package/clients/cli/lib/manifest.test.mjs +12 -0
  261. package/clients/cli/lib/welcome-box.mjs +50 -0
  262. package/clients/cli/run.mjs +108 -0
  263. package/foundation/agent-docs/agent-doc-state.d.mts +20 -7
  264. package/foundation/agent-docs/agent-doc-state.mjs +35 -8
  265. package/foundation/agent-docs/agent-docs.d.mts +100 -12
  266. package/foundation/agent-docs/agent-docs.mjs +1012 -100
  267. package/foundation/agent-docs/agent-docs.path-safety.test.mjs +1239 -17
  268. package/foundation/agent-docs/agent-docs.test.mjs +683 -39
  269. package/foundation/config/project.mjs +20 -22
  270. package/foundation/config/project.test.mjs +56 -0
  271. package/foundation/discovery/authoring-self-docs.d.mts +7 -0
  272. package/foundation/discovery/authoring-self-docs.mjs +16 -4
  273. package/foundation/discovery/authoring-self-docs.test.mjs +15 -3
  274. package/foundation/discovery/authoring-surface.d.mts +1 -1
  275. package/foundation/discovery/authoring-surface.mjs +2 -2
  276. package/foundation/discovery/authoring-surface.test.mjs +1 -1
  277. package/foundation/discovery/cli-self-docs.d.mts +2 -1
  278. package/foundation/discovery/cli-self-docs.mjs +4 -3
  279. package/foundation/discovery/cli-self-docs.test.mjs +2 -2
  280. package/foundation/discovery/component-discovery.d.mts +37 -0
  281. package/foundation/discovery/component-discovery.mjs +80 -5
  282. package/foundation/discovery/component-loader.d.mts +1 -1
  283. package/foundation/discovery/docs-discovery.mjs +14 -1
  284. package/foundation/discovery/module-exports.d.mts +41 -0
  285. package/foundation/discovery/module-exports.mjs +253 -0
  286. package/foundation/discovery/runner-allowlist.d.mts +2 -4
  287. package/foundation/discovery/runner-allowlist.mjs +2 -4
  288. package/foundation/discovery/runner-commands.d.mts +77 -0
  289. package/foundation/discovery/runner-commands.mjs +294 -0
  290. package/foundation/discovery/runner-commands.test.mjs +128 -0
  291. package/foundation/discovery/stray-pointers.test.mjs +1 -1
  292. package/foundation/discovery/template-adapter.mjs +18 -1
  293. package/foundation/discovery/template-needs.mjs +1 -44
  294. package/foundation/discovery/theme-discovery.mjs +147 -3
  295. package/foundation/doc-compiler/doc-loads.test.mjs +33 -9
  296. package/foundation/doc-compiler/read.mjs +59 -1
  297. package/foundation/doc-compiler/read.test.mjs +11 -2
  298. package/foundation/doc-compiler/split-topic-keywords.test.mjs +3 -2
  299. package/foundation/doc-compiler/tree.d.mts +32 -6
  300. package/foundation/doc-compiler/tree.mjs +77 -43
  301. package/foundation/doc-compiler/tree.test.mjs +7 -7
  302. package/foundation/fs/paths.d.mts +70 -8
  303. package/foundation/fs/paths.mjs +556 -38
  304. package/foundation/fs/paths.test.mjs +430 -54
  305. package/foundation/fs/read-record.d.mts +18 -0
  306. package/foundation/fs/read-record.mjs +131 -0
  307. package/foundation/fs/read-record.test.mjs +77 -0
  308. package/foundation/integrations/autolink.d.mts +2 -1
  309. package/foundation/integrations/autolink.mjs +4 -13
  310. package/foundation/integrations/computer-integrations.d.mts +6 -3
  311. package/foundation/integrations/computer-integrations.mjs +38 -9
  312. package/foundation/integrations/contribution-inventory.d.mts +15 -20
  313. package/foundation/integrations/contribution-inventory.mjs +41 -5
  314. package/foundation/integrations/integrations.d.mts +27 -1
  315. package/foundation/integrations/integrations.mjs +61 -2
  316. package/foundation/integrations/validate-contributions.d.mts +115 -0
  317. package/foundation/integrations/validate-contributions.mjs +1168 -13
  318. package/foundation/release/handoff.d.mts +23 -4
  319. package/foundation/release/handoff.mjs +175 -8
  320. package/foundation/release/run-context.mjs +14 -0
  321. package/foundation/response/response-types.doc.mjs +1 -1
  322. package/foundation/route/route.d.mts +285 -0
  323. package/foundation/route/route.mjs +324 -0
  324. package/foundation/route/route.test.mjs +153 -0
  325. package/foundation/runtime/hosted-runtime.test.mjs +403 -0
  326. package/foundation/runtime/locked-versions.d.mts +32 -0
  327. package/foundation/runtime/locked-versions.mjs +65 -0
  328. package/foundation/runtime/package-root.d.mts +91 -0
  329. package/foundation/runtime/package-root.mjs +174 -0
  330. package/foundation/runtime/package-set.d.mts +62 -0
  331. package/foundation/runtime/package-set.mjs +130 -0
  332. package/foundation/runtime/package-set.test.mjs +432 -0
  333. package/host/door-conformance.test.mjs +274 -0
  334. package/host/index.d.ts +116 -0
  335. package/host/index.mjs +295 -0
  336. package/host/index.test.mjs +302 -0
  337. package/package.json +14 -9
@@ -246,7 +246,7 @@ describe('integration docs in the docs tree', () => {
246
246
  });
247
247
  scaffold({
248
248
  ...kit(),
249
- 'unorganized.doc.mjs': {...NAMESPACE, name: 'unorganized', title: 'Mine', summary: 'Mine.'},
249
+ 'guides.doc.mjs': {...NAMESPACE, name: 'guides', title: 'Mine', summary: 'Mine.'},
250
250
  'use-a-theme.doc.mjs': {...NAMESPACE, name: 'use-a-theme', title: 'Acme use-a-theme', summary: 'Acme use-a-theme.'},
251
251
  'cli.doc.mjs': flat('cli'),
252
252
  });
@@ -254,26 +254,26 @@ describe('integration docs in the docs tree', () => {
254
254
  expect(listed.meta.namespaces).toEqual(
255
255
  expect.arrayContaining([
256
256
  expect.objectContaining({topic: 'cli', package: '@astryxdesign/cli'}),
257
- expect.objectContaining({topic: 'unorganized', package: '@astryxdesign/cli'}),
257
+ expect.objectContaining({topic: 'guides', package: '@astryxdesign/cli'}),
258
258
  ]),
259
259
  );
260
- expect(listed.meta.namespaces.filter(n => ['use-a-theme', 'unorganized'].includes(n.topic) && n.package === '@acme/kit')).toEqual([]);
260
+ expect(listed.meta.namespaces.filter(n => ['use-a-theme', 'guides'].includes(n.topic) && n.package === '@acme/kit')).toEqual([]);
261
261
  expect(listed.data.map(entry => entry.topic)).not.toContain('cli');
262
262
  // Each name opens the CLI's doc, and a CLI topic keeps its home.
263
263
  expect((await docs('cli', undefined, {cwd: tmpDir})).data).toMatchObject({route: 'cli', package: '@astryxdesign/cli'});
264
- expect((await docs('unorganized', undefined, {cwd: tmpDir})).data).toMatchObject({route: 'unorganized', package: '@astryxdesign/cli'});
264
+ expect((await docs('guides', undefined, {cwd: tmpDir})).data).toMatchObject({route: 'guides', package: '@astryxdesign/cli'});
265
265
  const tokens = await docs('use-a-theme', undefined, {cwd: tmpDir});
266
266
  expect(tokens.type).toBe('docs.detail');
267
- expect(tokens.data.links.up).toBe('astryx docs unorganized');
267
+ expect(tokens.data.links.up).toBe('astryx docs guides');
268
268
  // The project doctor and the package doctor both name the claims.
269
269
  const check = await checkDocsTree({docsCatalog: await loadDocsCatalog(tmpDir)});
270
270
  expect(check.status).toBe('warn');
271
- expect(check.message).toContain('takes the route "unorganized", which the CLI\'s own docs keep');
271
+ expect(check.message).toContain('takes the route "guides", which the CLI\'s own docs keep');
272
272
  expect(check.message).toContain('takes the route "use-a-theme"');
273
273
  expect(check.message).toContain('both have the route "cli"');
274
274
  const result = await integrationDocConflicts('@acme/kit', {cwd: tmpDir});
275
275
  const graph = result.data.issues.filter(issue => issue.code === 'invalid_doc_graph').map(issue => issue.message).join('\n');
276
- expect(graph).toContain('takes the route "unorganized"');
276
+ expect(graph).toContain('takes the route "guides"');
277
277
  expect(graph).toContain('takes the route "use-a-theme"');
278
278
  expect(graph).toContain('route "cli"');
279
279
  }, SLOW);
@@ -294,16 +294,16 @@ describe('integration docs in the docs tree', () => {
294
294
  const nested = path.join(tmpDir, 'node_modules', '@acme', 'kit', 'docs', 'nested');
295
295
  fs.mkdirSync(nested, {recursive: true});
296
296
  fs.writeFileSync(
297
- path.join(nested, 'caps-unorganized.doc.mjs'),
298
- `export const docs = ${JSON.stringify(flat('Unorganized'), null, 2)};\n`,
297
+ path.join(nested, 'caps-guides.doc.mjs'),
298
+ `export const docs = ${JSON.stringify(flat('Guides'), null, 2)};\n`,
299
299
  );
300
300
  fs.writeFileSync(
301
301
  path.join(nested, 'caps-use-a-theme.doc.mjs'),
302
302
  `export const docs = ${JSON.stringify(flat('USE-A-THEME'), null, 2)};\n`,
303
303
  );
304
304
  expect((await docs('cli', undefined, {cwd: tmpDir})).data).toMatchObject({route: 'cli', package: '@astryxdesign/cli'});
305
- expect((await docs('unorganized', undefined, {cwd: tmpDir})).data).toMatchObject({route: 'unorganized', package: '@astryxdesign/cli'});
306
- expect((await docs('theme', undefined, {cwd: tmpDir, index: true})).data.links.up).toBe('astryx docs unorganized');
305
+ expect((await docs('guides', undefined, {cwd: tmpDir})).data).toMatchObject({route: 'guides', package: '@astryxdesign/cli'});
306
+ expect((await docs('theme', undefined, {cwd: tmpDir, index: true})).data.links.up).toBe('astryx docs guides');
307
307
  const listed = await docs(undefined, undefined, {cwd: tmpDir});
308
308
  expect(listed.data.map(entry => entry.topic.toLowerCase())).not.toContain('cli');
309
309
  // Search does not offer a topic whose route another doc owns.
@@ -314,10 +314,10 @@ describe('integration docs in the docs tree', () => {
314
314
  const check = await checkDocsTree({docsCatalog: await loadDocsCatalog(tmpDir)});
315
315
  expect(check.status).toBe('warn');
316
316
  expect(check.message).toContain('route "CLI"');
317
- expect(check.message).toContain('takes the route "Unorganized"');
317
+ expect(check.message).toContain('takes the route "Guides"');
318
318
  // `USE-A-THEME` is the CLI's topic `use-a-theme` in another spelling: the CLI's wins.
319
319
  const tokens = await docs('USE-A-THEME', undefined, {cwd: tmpDir, index: true});
320
- expect(tokens.data).toMatchObject({name: 'use-a-theme', links: {up: 'astryx docs unorganized'}});
320
+ expect(tokens.data).toMatchObject({name: 'use-a-theme', links: {up: 'astryx docs guides'}});
321
321
  expect(tokens.data.title).not.toBe('Acme USE-A-THEME');
322
322
  const result = await integrationDocConflicts('@acme/kit', {cwd: tmpDir});
323
323
  const graph = result.data.issues
@@ -325,7 +325,7 @@ describe('integration docs in the docs tree', () => {
325
325
  .map(issue => issue.message)
326
326
  .join('\n');
327
327
  expect(graph).toContain('route "CLI"');
328
- expect(graph).toContain('takes the route "Unorganized"');
328
+ expect(graph).toContain('takes the route "Guides"');
329
329
  }, SLOW);
330
330
 
331
331
  it("keeps a replaced name for the topic that replaces it, over another integration's namespace", async () => {
@@ -420,9 +420,9 @@ describe('integration docs in the docs tree', () => {
420
420
  scaffold({
421
421
  ...kit(),
422
422
  'a-cli.doc.mjs': flat('cli'),
423
- 'a-unorganized.doc.mjs': flat('Unorganized'),
423
+ 'a-guides.doc.mjs': flat('Guides'),
424
424
  'z-b2.doc.mjs': flat('b2', {replaces: 'cli'}),
425
- 'z-u2.doc.mjs': flat('u2', {replaces: 'Unorganized'}),
425
+ 'z-u2.doc.mjs': flat('u2', {replaces: 'Guides'}),
426
426
  });
427
427
  // Another package carries the chain on: c3 replaces b2, which replaced cli.
428
428
  const three = path.join(tmpDir, 'node_modules', '@acme', 'three');
@@ -440,8 +440,8 @@ describe('integration docs in the docs tree', () => {
440
440
  for (const name of ['cli', 'CLI']) {
441
441
  expect((await docs(name, undefined, {cwd: tmpDir})).data).toMatchObject({route: 'cli', package: '@astryxdesign/cli'});
442
442
  }
443
- for (const name of ['unorganized', 'UNORGANIZED']) {
444
- expect((await docs(name, undefined, {cwd: tmpDir})).data).toMatchObject({route: 'unorganized', package: '@astryxdesign/cli'});
443
+ for (const name of ['guides', 'GUIDES']) {
444
+ expect((await docs(name, undefined, {cwd: tmpDir})).data).toMatchObject({route: 'guides', package: '@astryxdesign/cli'});
445
445
  }
446
446
  // The replacement still answers to its own names.
447
447
  for (const name of ['b2', 'c3']) {
@@ -456,7 +456,7 @@ describe('integration docs in the docs tree', () => {
456
456
  const check = await checkDocsTree({docsCatalog: await loadDocsCatalog(tmpDir)});
457
457
  expect(check.status).toBe('warn');
458
458
  expect(check.message).toContain('The topic "c3" answers to "cli" through replaces');
459
- expect(check.message).toContain('The topic "u2" answers to "unorganized" through replaces');
459
+ expect(check.message).toContain('The topic "u2" answers to "guides" through replaces');
460
460
  const inPackage = await integrationDocConflicts('@acme/kit', {cwd: tmpDir});
461
461
  const graph = inPackage.data.issues
462
462
  .filter(issue => issue.code === 'invalid_doc_graph')
@@ -519,7 +519,7 @@ describe('integration docs in the docs tree', () => {
519
519
  });
520
520
  const {data} = await docs('theme', 'acme-theming-notes', {cwd: tmpDir});
521
521
  // `generic:integrations` is the extension's own guide, never the CLI's
522
- // `cli/integrations`; a provider-qualified link reaches the CLI's topic.
522
+ // `authoring`; a provider-qualified link reaches the CLI's topic.
523
523
  expect(data.content[0].text).toBe(
524
524
  'See `astryx docs acme/integrations`, and `astryx docs tokens` for the tokens.',
525
525
  );
@@ -447,7 +447,7 @@ describe('integration-contributed topics', () => {
447
447
  // places it.
448
448
  expect(top).toContain('troubleshooting');
449
449
  expect(
450
- top.some(name => /^cli\/integrations\/(?:.+\/)?troubleshooting$/.test(name)),
450
+ top.some(name => /^authoring\/(?:.+\/)?troubleshooting$/.test(name)),
451
451
  top.join(', '),
452
452
  ).toBe(true);
453
453
  }, SLOW);
@@ -157,13 +157,13 @@ describe('a section read on a namespace', () => {
157
157
  expect(names).toContain('layout/side-panels');
158
158
  expect(names.every(name => name.startsWith('layout/'))).toBe(true);
159
159
  // At any depth: guides under nested namespaces are named, namespaces are not.
160
- const cli = await docs('cli', 'zzzz-nope').catch(e => e);
161
- expect(cli.code).toBe('ERR_UNKNOWN_SECTION');
162
- expect(cli.suggestions.map(s => s.name)).toContain(
163
- 'cli/integrations/quick-start',
160
+ const authoring = await docs('authoring', 'zzzz-nope').catch(e => e);
161
+ expect(authoring.code).toBe('ERR_UNKNOWN_SECTION');
162
+ expect(authoring.suggestions.map(s => s.name)).toContain(
163
+ 'authoring/building-blocks/components/add-a-component',
164
164
  );
165
- expect(cli.suggestions.map(s => s.name)).not.toContain(
166
- 'cli/integrations',
165
+ expect(authoring.suggestions.map(s => s.name)).not.toContain(
166
+ 'authoring/building-blocks',
167
167
  );
168
168
  },
169
169
  SLOW,
@@ -300,11 +300,11 @@ describe('a reference block that includes what it names', () => {
300
300
  });
301
301
 
302
302
  describe('what a reference block names', () => {
303
- it('resolves a CLI authoring schema by identity, to its section of `astryx docs authoring`', async () => {
303
+ it('resolves a CLI authoring schema by identity, to its section of the field reference', async () => {
304
304
  const resolve = await linkResolver(builtinCatalog(), CLI_PROVIDER_ID);
305
305
  expect(await resolve('schema:integration')).toMatchObject({
306
306
  id: createDocId(CLI_PROVIDER_ID, 'schema', 'integration'),
307
- route: 'authoring',
307
+ route: 'authoring/field-reference',
308
308
  title: 'Astryx Integration',
309
309
  command: 'astryx docs authoring integration',
310
310
  });
@@ -28,7 +28,15 @@ import * as fs from 'node:fs';
28
28
  import * as path from 'node:path';
29
29
 
30
30
  import {MIN_NODE_VERSION, isNodeVersionSupported} from '../../foundation/env/node-version.mjs';
31
- import {AGENT_DOC_PATHS} from '../../foundation/agent-docs/agent-doc-state.mjs';
31
+ import {
32
+ AGENT_DOC_PATHS,
33
+ holdsAstryxBlock,
34
+ } from '../../foundation/agent-docs/agent-doc-state.mjs';
35
+ import {
36
+ agentDocsTargetDir,
37
+ assertAgentDocsTarget,
38
+ resolveAgentDocsRoot,
39
+ } from '../../foundation/agent-docs/agent-docs.mjs';
32
40
  import {CLI_ROOT, findProjectCoreDir, findInstalledPackage} from '../../foundation/fs/paths.mjs';
33
41
  import {explainPackageManager, formatCliCommand, getCliInvocation, guessCliInvocation} from '../../foundation/env/package-manager.mjs';
34
42
  import {chooseDeclaredCliCommand, declaredCliCommandsOf} from '../../foundation/env/cli-command.mjs';
@@ -610,6 +618,21 @@ export function checkConfiguredIntegrations(ctx) {
610
618
  };
611
619
  }
612
620
 
621
+ /**
622
+ * Why init writes no agent docs for `cwd`, or null when it does: the folder
623
+ * it would write to is the home folder or the top of a settings folder in it.
624
+ * @param {string} cwd
625
+ * @returns {string | null}
626
+ */
627
+ function agentDocsRefusal(cwd) {
628
+ try {
629
+ assertAgentDocsTarget(agentDocsTargetDir(cwd));
630
+ return null;
631
+ } catch (err) {
632
+ return err instanceof Error && err.name === 'AgentDocsHomeError' ? err.message : null;
633
+ }
634
+ }
635
+
613
636
  /**
614
637
  * Check 7 — agent docs exist and contain the Astryx section markers.
615
638
  * @param {DoctorContext} ctx
@@ -617,8 +640,15 @@ export function checkConfiguredIntegrations(ctx) {
617
640
  */
618
641
  export function checkAgentDocs(ctx) {
619
642
  // Every file init can write (one shared list), looked for in the working
620
- // directory and in the project root: run from src/, the docs sit at the root.
621
- const dirs = [...new Set([ctx.cwd, projectRootOf(ctx)])];
643
+ // directory, the project root, and the folder init writes them to: run from
644
+ // src/ or a nested package, the docs sit at the root.
645
+ const dirs = [
646
+ ...new Set(
647
+ [ctx.cwd, projectRootOf(ctx), resolveAgentDocsRoot(ctx.cwd) ?? ctx.cwd].map(dir =>
648
+ path.resolve(dir),
649
+ ),
650
+ ),
651
+ ];
622
652
  const present = dirs.flatMap(dir =>
623
653
  AGENT_DOC_PATHS.map(rel => path.join(dir, rel)).filter(abs =>
624
654
  fs.existsSync(abs),
@@ -626,6 +656,15 @@ export function checkAgentDocs(ctx) {
626
656
  );
627
657
  /** @param {string} abs */
628
658
  const shown = abs => path.relative(ctx.cwd, abs) || path.basename(abs);
659
+ // In the home folder, or at the top of a settings folder in it, init
660
+ // writes no agent docs (INV31), so the fix is moving the project or a hand
661
+ // edit, never init.
662
+ const refusal = agentDocsRefusal(ctx.cwd);
663
+ /** @param {string} initFix */
664
+ const fixFor = initFix =>
665
+ refusal
666
+ ? `${refusal} Move the project into a folder of its own, or edit the Astryx block in its agent file by hand.`
667
+ : initFix;
629
668
 
630
669
  if (present.length === 0) {
631
670
  return {
@@ -633,29 +672,64 @@ export function checkAgentDocs(ctx) {
633
672
  label: 'AI agent docs',
634
673
  status: 'info',
635
674
  message: `No agent docs found: looked for ${AGENT_DOC_PATHS.join(', ')} in ${dirs.map(dir => path.relative(ctx.cwd, dir) || '.').join(' and ')}.`,
636
- fix: `Generate agent docs with \`${getCliInvocation(ctx.cwd)} init --features agents\`.`,
675
+ fix: fixFor(
676
+ `Generate agent docs with \`${getCliInvocation(ctx.cwd)} init --features agents\`.`,
677
+ ),
637
678
  };
638
679
  }
639
680
 
640
- const withMarkers = present.filter(abs => {
681
+ /** @param {string} abs */
682
+ const read = abs => {
641
683
  try {
642
- const content = fs.readFileSync(abs, 'utf-8');
643
- return (
644
- (content.includes('<!-- ASTRYX:START -->') || content.includes('<!-- XDS:START -->')) &&
645
- (content.includes('<!-- ASTRYX:END -->') || content.includes('<!-- XDS:END -->'))
646
- );
684
+ return fs.readFileSync(abs, 'utf-8');
647
685
  } catch {
648
- return false;
686
+ return '';
649
687
  }
688
+ };
689
+ const withMarkers = present.filter(abs => {
690
+ const content = read(abs);
691
+ return (
692
+ (content.includes('<!-- ASTRYX:START -->') || content.includes('<!-- XDS:START -->')) &&
693
+ (content.includes('<!-- ASTRYX:END -->') || content.includes('<!-- XDS:END -->')) &&
694
+ holdsAstryxBlock(content)
695
+ );
650
696
  });
651
697
 
698
+ // A managed block that only imports another file (init's GEMINI.md import
699
+ // of AGENTS.md) is broken when that file holds no block: the agents that
700
+ // read either file get no Astryx docs.
701
+ const dangling = present.flatMap(abs => {
702
+ const match = /<!-- ASTRYX:START -->\s*@(\S+)\s*<!-- ASTRYX:END -->/.exec(read(abs));
703
+ if (match == null) return [];
704
+ const target = path.resolve(path.dirname(abs), match[1]);
705
+ return holdsAstryxBlock(read(target)) ? [] : [{abs, target}];
706
+ });
707
+ if (dangling.length > 0) {
708
+ return {
709
+ id: 'agent-docs',
710
+ label: 'AI agent docs',
711
+ status: 'fail',
712
+ message: dangling
713
+ .map(
714
+ ({abs, target}) =>
715
+ `${shown(abs)} imports ${shown(target)}, which holds no Astryx block, so agents that read them get no Astryx docs.`,
716
+ )
717
+ .join(' '),
718
+ fix: fixFor(
719
+ `Write the block again with \`${getCliInvocation(ctx.cwd)} init --features agents\`.`,
720
+ ),
721
+ };
722
+ }
723
+
652
724
  if (withMarkers.length === 0) {
653
725
  return {
654
726
  id: 'agent-docs',
655
727
  label: 'AI agent docs',
656
728
  status: 'warn',
657
729
  message: `Agent docs present (${present.map(shown).join(', ')}) but no Astryx section markers found.`,
658
- fix: `Add the Astryx section to your agent docs with \`${getCliInvocation(ctx.cwd)} init --features agents\`.`,
730
+ fix: fixFor(
731
+ `Add the Astryx section to your agent docs with \`${getCliInvocation(ctx.cwd)} init --features agents\`.`,
732
+ ),
659
733
  };
660
734
  }
661
735
 
@@ -974,16 +1048,20 @@ function nameTypes(names) {
974
1048
  }
975
1049
 
976
1050
  /**
977
- * The section keys `astryx docs authoring --index` lists, read the way that
978
- * command reads them.
1051
+ * The section keys the field reference lists (`astryx docs
1052
+ * authoring/field-reference --index`), read the way that command reads them.
979
1053
  * @returns {Promise<{keys: Set<string>} | {keys: null, error: string}>}
980
1054
  */
981
1055
  async function authoringTopicKeys() {
982
1056
  try {
1057
+ const {AUTHORING_REFERENCE_ROUTE} =
1058
+ await import('../../foundation/discovery/authoring-self-docs.mjs');
983
1059
  const catalog = DocsCatalog.fromBuiltins();
984
- const entry = catalog.resolve('authoring');
985
- if (!entry) return {keys: null, error: 'it is not a built-in topic'};
986
- const index = indexView(await lowerTopic(catalog, entry));
1060
+ const node = (await projectTree(catalog)).get(AUTHORING_REFERENCE_ROUTE);
1061
+ if (!node?.ref?.topicFile) {
1062
+ return {keys: null, error: 'the docs tree has no field reference'};
1063
+ }
1064
+ const index = indexView(await lowerTopic(catalog, guideEntry(node)));
987
1065
  return {keys: new Set(index.sections.map(section => section.id))};
988
1066
  } catch (err) {
989
1067
  return {
@@ -1002,7 +1080,7 @@ function surfaceProblems(surface) {
1002
1080
  const problems = [];
1003
1081
  if (surface.types === 0 && surface.untraced.length === 0) {
1004
1082
  problems.push(
1005
- `@astryxdesign/cli/authoring exports no types, so nothing was compared with \`${formatCliCommand('docs authoring')}\``,
1083
+ `@astryxdesign/cli/authoring exports no types, so nothing was compared with \`${formatCliCommand('docs authoring/field-reference')}\``,
1006
1084
  );
1007
1085
  }
1008
1086
  for (const {module, names, reason, source, key} of surface.unreadable) {
@@ -1015,7 +1093,7 @@ function surfaceProblems(surface) {
1015
1093
  ? `${source} does not load`
1016
1094
  : `${source} renders section "${key}", which the topic's index does not list`;
1017
1095
  problems.push(
1018
- `${nameTypes(names)} from ${module} ${names.length === 1 ? 'has' : 'have'} no doc in \`${formatCliCommand('docs authoring')}\`: ${why}`,
1096
+ `${nameTypes(names)} from ${module} ${names.length === 1 ? 'has' : 'have'} no doc in \`${formatCliCommand('docs authoring/field-reference')}\`: ${why}`,
1019
1097
  );
1020
1098
  }
1021
1099
  for (const {name, reason} of surface.untraced) {
@@ -1056,7 +1134,7 @@ export async function checkAuthoringDocs(_ctx, options = {}) {
1056
1134
  const audit = await auditAuthoringSelfDocs({root, sources});
1057
1135
  const problems = [
1058
1136
  ...audit.unreachable.map(
1059
- source => `${source} is not in \`${formatCliCommand('docs authoring')}\``,
1137
+ source => `${source} is not in \`${formatCliCommand('docs authoring/field-reference')}\``,
1060
1138
  ),
1061
1139
  ...audit.failed.map(
1062
1140
  ({source, error}) => `${source} failed to load: ${error}`,
@@ -1072,7 +1150,7 @@ export async function checkAuthoringDocs(_ctx, options = {}) {
1072
1150
  : {keys: options.topicKeys};
1073
1151
  const surface = [
1074
1152
  ...('error' in topic
1075
- ? [`\`${formatCliCommand('docs authoring')}\` could not be read: ${topic.error}`]
1153
+ ? [`\`${formatCliCommand('docs authoring/field-reference')}\` could not be read: ${topic.error}`]
1076
1154
  : []),
1077
1155
  ...surfaceProblems(
1078
1156
  await auditAuthoringSurface({root, sources, topicKeys: topic.keys}),
@@ -1096,7 +1174,7 @@ export async function checkAuthoringDocs(_ctx, options = {}) {
1096
1174
  id,
1097
1175
  label,
1098
1176
  status: 'pass',
1099
- message: `All ${audit.sections} authoring schemas are readable in \`${formatCliCommand('docs authoring')}\`.`,
1177
+ message: `All ${audit.sections} authoring schemas are readable in \`${formatCliCommand('docs authoring/field-reference')}\`.`,
1100
1178
  };
1101
1179
  } catch (err) {
1102
1180
  return {
@@ -1164,7 +1242,7 @@ export async function checkCliDocs(_ctx, options = {}) {
1164
1242
  id,
1165
1243
  label,
1166
1244
  status: 'pass',
1167
- message: `All ${audit.docs} CLI docs are readable: ${audit.tree} in the \`${formatCliCommand('docs cli')}\` tree and ${audit.authoring} in \`${formatCliCommand('docs authoring')}\`.`,
1245
+ message: `All ${audit.docs} CLI docs are readable: ${audit.tree} in the \`${formatCliCommand('docs cli')}\` tree and ${audit.authoring} in \`${formatCliCommand('docs authoring/field-reference')}\`.`,
1168
1246
  };
1169
1247
  } catch (err) {
1170
1248
  return {
@@ -1281,7 +1359,7 @@ export async function checkDocsProgressiveDisclosure(ctx) {
1281
1359
  // A guide the docs tree places is a topic read by its route; it is held
1282
1360
  // to the same budget as every flat topic.
1283
1361
  const guides = [...(await projectTree(catalog)).nodes.values()]
1284
- // A flat topic in the Unorganized level is checked above, as a topic.
1362
+ // A flat topic in a topic level is checked above, as a topic.
1285
1363
  .filter(node => node.kind === 'generic' && !node.ref?.flatTopic)
1286
1364
  .map(guideEntry);
1287
1365
  for (const entry of [...catalog.entries(), ...guides]) {
@@ -652,7 +652,7 @@ describe('checkAuthoringDocs', () => {
652
652
  it('passes when every authoring self-doc is reachable and fits one read', async () => {
653
653
  const c = await checkAuthoringDocs();
654
654
  expect(c).toMatchObject({id: 'authoring-docs', status: 'pass'});
655
- expect(c.message).toContain(formatCliCommand('docs authoring'));
655
+ expect(c.message).toContain(formatCliCommand('docs authoring/field-reference'));
656
656
  }, SLOW);
657
657
  });
658
658
 
@@ -680,7 +680,7 @@ describe('checkCliDocs', () => {
680
680
  id: 'cli-docs',
681
681
  label: 'CLI docs',
682
682
  status: 'pass',
683
- message: `All ${audit.docs} CLI docs are readable: ${audit.tree} in the \`${formatCliCommand('docs cli')}\` tree and ${audit.authoring} in \`${formatCliCommand('docs authoring')}\`.`,
683
+ message: `All ${audit.docs} CLI docs are readable: ${audit.tree} in the \`${formatCliCommand('docs cli')}\` tree and ${audit.authoring} in \`${formatCliCommand('docs authoring/field-reference')}\`.`,
684
684
  });
685
685
  },
686
686
  SLOW,
@@ -790,7 +790,7 @@ describe('checkDocsTree', () => {
790
790
  describe('checkAuthoringDocs against the public authoring surface', () => {
791
791
  const NAMESPACE = 'doctypes/namespace/namespace.doc.mjs';
792
792
  const NAMESPACE_TYPES =
793
- `NamespaceDoc, NamespaceProviderScope, NamespaceSlotAcceptance, NamespaceSlot, NamespaceAdoptionSource, NamespaceAdoptionRule from doctypes/namespace/type.ts have no doc in \`${formatCliCommand('docs authoring')}\``;
793
+ `NamespaceDoc, NamespaceProviderScope, NamespaceSlotAcceptance, NamespaceSlot, NamespaceAdoptionSource, NamespaceAdoptionRule from doctypes/namespace/type.ts have no doc in \`${formatCliCommand('docs authoring/field-reference')}\``;
794
794
  const SURFACE_FIX =
795
795
  'Put a self-doc beside each module whose types @astryxdesign/cli/authoring exports and list it in AUTHORING_SELF_DOCS; export what each listed self-doc documents, or remove that self-doc.';
796
796
 
@@ -820,7 +820,7 @@ describe('checkAuthoringDocs against the public authoring surface', () => {
820
820
  id: 'authoring-docs',
821
821
  label: 'Authoring docs',
822
822
  status: 'pass',
823
- message: `All ${AUTHORING_SELF_DOCS.length} authoring schemas are readable in \`${formatCliCommand('docs authoring')}\`.`,
823
+ message: `All ${AUTHORING_SELF_DOCS.length} authoring schemas are readable in \`${formatCliCommand('docs authoring/field-reference')}\`.`,
824
824
  });
825
825
  },
826
826
  SLOW,
@@ -849,7 +849,7 @@ describe('checkAuthoringDocs against the public authoring surface', () => {
849
849
  });
850
850
  expect(c.status).toBe('fail');
851
851
  expect(c.message).toBe(
852
- `2 problems: ${NAMESPACE} is not in \`${formatCliCommand('docs authoring')}\`; ${NAMESPACE_TYPES}: ${NAMESPACE} is not listed in AUTHORING_SELF_DOCS`,
852
+ `2 problems: ${NAMESPACE} is not in \`${formatCliCommand('docs authoring/field-reference')}\`; ${NAMESPACE_TYPES}: ${NAMESPACE} is not listed in AUTHORING_SELF_DOCS`,
853
853
  );
854
854
  },
855
855
  SLOW,
@@ -858,7 +858,7 @@ describe('checkAuthoringDocs against the public authoring surface', () => {
858
858
  it(
859
859
  "fails when the topic's index no longer exposes the NamespaceDoc section",
860
860
  async () => {
861
- const index = await docs('authoring', undefined, {index: true});
861
+ const index = await docs('authoring/field-reference', undefined, {index: true});
862
862
  const topicKeys = new Set(
863
863
  index.data.sections
864
864
  .map((/** @type {any} */ s) => s.id)
@@ -947,7 +947,7 @@ describe('checkAuthoringDocs against the public authoring surface', () => {
947
947
  expect(c.fix).toBe(SURFACE_FIX);
948
948
  expect(c.message).toMatch(
949
949
  new RegExp(
950
- `^[A-Za-z, ]+ from ${escape(module)} have no doc in \`${escape(formatCliCommand('docs authoring'))}\`: no self-doc sits beside ${escape(module)}$`,
950
+ `^[A-Za-z, ]+ from ${escape(module)} have no doc in \`${escape(formatCliCommand('docs authoring/field-reference'))}\`: no self-doc sits beside ${escape(module)}$`,
951
951
  ),
952
952
  );
953
953
  for (const name of names) {
@@ -990,7 +990,7 @@ describe('checkAuthoringDocs against the public authoring surface', () => {
990
990
  expect(await checkAuthoringDocs()).toMatchObject({
991
991
  status: 'fail',
992
992
  message:
993
- `\`${formatCliCommand('docs authoring')}\` could not be read: no built-in docs`,
993
+ `\`${formatCliCommand('docs authoring/field-reference')}\` could not be read: no built-in docs`,
994
994
  fix: SURFACE_FIX,
995
995
  });
996
996
  } finally {
@@ -1011,7 +1011,7 @@ describe('checkAuthoringDocs against the public authoring surface', () => {
1011
1011
  const c = await checkAuthoringDocs(undefined, {root});
1012
1012
  expect(c.status).toBe('fail');
1013
1013
  expect(c.message).toContain(
1014
- `@astryxdesign/cli/authoring exports no types, so nothing was compared with \`${formatCliCommand('docs authoring')}\``,
1014
+ `@astryxdesign/cli/authoring exports no types, so nothing was compared with \`${formatCliCommand('docs authoring/field-reference')}\``,
1015
1015
  );
1016
1016
  },
1017
1017
  SLOW,
@@ -1317,8 +1317,12 @@ describe('a configured integration that cannot load fails the gate', () => {
1317
1317
  'astryx.config.mjs': "export default { integrations: ['@acme/widgets'] };\n",
1318
1318
  };
1319
1319
 
1320
- /** Run doctor on a project under the working directory. */
1321
- async function report(files) {
1320
+ /**
1321
+ * Run doctor on a project under the working directory, in its folder `at`.
1322
+ * @param {Record<string, string>} files
1323
+ * @param {string} [at]
1324
+ */
1325
+ async function report(files, at = '.') {
1322
1326
  const dir = fs.mkdtempSync(path.join(process.cwd(), '.astryx-doctor-gate-'));
1323
1327
  tmpDirs.push(dir);
1324
1328
  for (const [rel, content] of Object.entries(files)) {
@@ -1326,7 +1330,7 @@ describe('a configured integration that cannot load fails the gate', () => {
1326
1330
  fs.mkdirSync(path.dirname(abs), {recursive: true});
1327
1331
  fs.writeFileSync(abs, content);
1328
1332
  }
1329
- const {checks, summary} = (await doctor({cwd: dir})).data;
1333
+ const {checks, summary} = (await doctor({cwd: path.join(dir, at)})).data;
1330
1334
  return {by: Object.fromEntries(checks.map(c => [c.id, c])), summary};
1331
1335
  }
1332
1336
 
@@ -1354,6 +1358,27 @@ describe('a configured integration that cannot load fails the gate', () => {
1354
1358
  SLOW,
1355
1359
  );
1356
1360
 
1361
+ it(
1362
+ 'passes in a workspace member whose configured integration is hoisted to the root',
1363
+ async () => {
1364
+ const {by} = await report(
1365
+ {
1366
+ 'package.json': JSON.stringify({name: 'root', private: true, workspaces: ['apps/*']}),
1367
+ ...CORE_STUB,
1368
+ ...widgets(),
1369
+ 'apps/web/package.json': '{"name":"web"}',
1370
+ 'apps/web/astryx.config.mjs': "export default { integrations: ['@acme/widgets'] };\n",
1371
+ },
1372
+ 'apps/web',
1373
+ );
1374
+ expect(by['configured-integrations'].status).toBe('pass');
1375
+ expect(by['configured-integrations'].message).toBe(
1376
+ '1 integration named in astryx.config loaded: @acme/widgets.',
1377
+ );
1378
+ },
1379
+ SLOW,
1380
+ );
1381
+
1357
1382
  it(
1358
1383
  'counts the package being authored when its own config names it',
1359
1384
  async () => {
package/api/hook/hook.mjs CHANGED
@@ -43,7 +43,7 @@ export async function hook(name, options = {}) {
43
43
  zh = false,
44
44
  } = options;
45
45
 
46
- // Default detail level mirrors the CLI (see commands/hook/index.mjs):
46
+ // Default detail level mirrors the CLI (see commands/hook.mjs):
47
47
  // single-hook views default to 'full', list-style views (--list,
48
48
  // --category, or no name) default to 'brief' (scannable name lists).
49
49
  // Keeping this in sync with the CLI is what the API↔CLI parity test checks.
@@ -17,11 +17,22 @@ export const doc = {
17
17
  'Non-interactive project setup: install agent docs and point at the theme + build workflows.',
18
18
  description:
19
19
  'Sets a project up with NO prompts, so it behaves identically for humans, ' +
20
- 'agents, CI, and piped I/O. By default it installs the AGENTS.md/CLAUDE.md ' +
21
- 'agent-docs cheat sheet, including guidance from configured integrations, and ' +
20
+ 'agents, CI, and piped I/O. By default it writes the agent-docs cheat sheet, ' +
21
+ 'including guidance from configured integrations, at the project root (the nearest ' +
22
+ 'package.json that declares an @astryxdesign package, else the nearest package.json, ' +
23
+ 'searching no higher than the repository root): to AGENTS.md, ' +
24
+ "to the project's Claude Code file (CLAUDE.md, else .claude/CLAUDE.md; when CLAUDE.md " +
25
+ 'exists, a block in .claude/CLAUDE.md is removed, unless the project ignores CLAUDE.md, in which case a ' +
26
+ '.claude/CLAUDE.md the project commits keeps a copy), and as a ' +
27
+ 'one-line import of AGENTS.md in GEMINI.md unless GEMINI.md already imports it. ' +
28
+ 'In the home folder, or in a settings folder in it outside a project, it writes none: every ' +
29
+ 'folder of the home folder whose name starts with a dot is a settings folder, and such a folder itself never ' +
30
+ 'counts as a project, even with a package.json; a folder below it with a package.json does. ' +
31
+ 'The managed block is Astryx-owned: it is written, refreshed, and removed whatever ignore rules ' +
32
+ 'or generated markers say, and the text around it is never changed. It then ' +
22
33
  "prints getting-started guidance; `features` / `all` print only the chosen features' " +
23
34
  'guidance instead (the theme feature also writes the annotated theme template). ' +
24
- 'With `removeAgents` it removes the managed agent-docs block instead of installing. ' +
35
+ 'With `removeAgents` it removes the managed agent-docs block, at the project root and in `ctx.cwd`, instead of installing. ' +
25
36
  'Outside a project it writes the same files but prints no getting-started steps and no step that adds a theme to an app, since those set up a project the folder does not have. ' +
26
37
  "With `user` it sets up the computer instead of the folder: it writes the computer-level agent text, one skill file at `~/.agents/skills/astryx/SKILL.md`, linked into the skill folders of agents that read only their own (`~/.claude/skills` always; Kiro, Windsurf, Letta, Xcode's coding assistant, and `~/.config/agents/skills` when that folder exists), with a managed copy for Continue, whose scanner skips links,, telling agents anywhere on the computer that Astryx is there and when to use it; with `user` and `removeAgents` it removes the file and the links it made and records that the text is turned off. " +
27
38
  'The CLI installed for the computer refreshes the file in the background after each upgrade; package install scripts, project setup, and upgrade never write it.' +
@@ -46,7 +57,7 @@ export const doc = {
46
57
  name: 'options.removeAgents',
47
58
  type: 'boolean',
48
59
  description:
49
- 'Remove the managed agent-docs block instead of installing. With `user`, remove the computer-level agent text instead.',
60
+ 'Remove the managed agent-docs block, at the project root and in `ctx.cwd`, instead of installing. With `user`, remove the computer-level agent text instead.',
50
61
  },
51
62
  {
52
63
  name: 'options.user',
@@ -62,7 +73,10 @@ export const doc = {
62
73
  name: 'options.agent',
63
74
  type: 'string',
64
75
  description:
65
- 'Agent preset: claude, cursor, codex, hermes, muse, all. Used only when the agents feature runs. Refused with `user`.',
76
+ 'Agent preset: claude, cursor, codex, hermes, muse, all. `all` writes every existing agent doc except GEMINI.md, ' +
77
+ 'which keeps its one-line import of AGENTS.md. With claude and all, when CLAUDE.md gets the block, a block in ' +
78
+ '.claude/CLAUDE.md is removed, unless the project ignores CLAUDE.md, in which case a .claude/CLAUDE.md the ' +
79
+ 'project commits gets a copy too. Used only when the agents feature runs. Refused with `user`.',
66
80
  },
67
81
  {
68
82
  name: 'options.agentDocsPath',
@@ -122,7 +136,7 @@ export const doc = {
122
136
  },
123
137
  {
124
138
  code: 'ERR_PATH_TRAVERSAL',
125
- when: 'the template output path resolves outside cwd (for example through a symlinked src directory), or, with removeAgents, a managed agent-docs file resolves outside cwd through a symlink',
139
+ when: 'the template output path resolves outside cwd (for example through a symlinked src directory), or, with removeAgents, a managed agent-docs file in cwd, or at the project root when run there, resolves outside that folder through a symlink (run below the root, a project-root file is left as it is with a note)',
126
140
  },
127
141
  ],
128
142
  examples: [