@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
@@ -6,30 +6,30 @@
6
6
  * Global options: --detail full|compact|brief, --lang en|zh|dense
7
7
  */
8
8
 
9
- import {findCoreDir} from '../../../../foundation/fs/paths.mjs';
9
+ import {findCoreDir} from '../../../foundation/fs/paths.mjs';
10
10
  import {
11
11
  resolveImportPath,
12
- } from '../../../../foundation/discovery/component-discovery.mjs';
12
+ } from '../../../foundation/discovery/component-discovery.mjs';
13
13
  import {
14
14
  formatFull,
15
15
  formatCompact,
16
16
  formatBrief,
17
17
  formatProps,
18
18
  formatBriefAll,
19
- } from '../../lib/component-format.mjs';
20
- import {resolveTheme} from '../../lib/resolve-theme.mjs';
21
- import {getCliInvocation} from '../../../../foundation/env/package-manager.mjs';
22
- import {jsonOut} from '../../../../foundation/response/json.mjs';
23
- import {emit, section, text, list, record, records, code} from '../../formatters/index.mjs';
24
- import {cliError} from '../../lib/cli-error.mjs';
25
- import {defineCommand} from '../../lib/define-command.mjs';
26
- import {resultSet} from '../../../../foundation/debug/index.mjs';
27
- import {ERROR_CODES} from '../../../../foundation/response/error-codes.mjs';
28
- import {component as componentApi} from '../../../../api/component/component.mjs';
29
- import {Project} from '../../../../foundation/config/project.mjs';
30
- import {warnOnIntegrationIssues} from '../../../../foundation/integrations/integration-warnings.mjs';
31
- import {doc as componentCommand} from '../component.doc.mjs';
32
- import {doc as componentFn} from '../../../../api/component/component.doc.mjs';
19
+ } from '../lib/component-format.mjs';
20
+ import {resolveTheme} from '../lib/resolve-theme.mjs';
21
+ import {getCliInvocation} from '../../../foundation/env/package-manager.mjs';
22
+ import {jsonOut} from '../../../foundation/response/json.mjs';
23
+ import {emit, section, text, list, record, records, code} from '../formatters/index.mjs';
24
+ import {cliError} from '../lib/cli-error.mjs';
25
+ import {defineCommand} from '../lib/define-command.mjs';
26
+ import {resultSet} from '../../../foundation/debug/index.mjs';
27
+ import {ERROR_CODES} from '../../../foundation/response/error-codes.mjs';
28
+ import {component as componentApi} from '../../../api/component/component.mjs';
29
+ import {Project} from '../../../foundation/config/project.mjs';
30
+ import {warnOnIntegrationIssues} from '../../../foundation/integrations/integration-warnings.mjs';
31
+ import {doc as componentCommand} from './component.doc.mjs';
32
+ import {doc as componentFn} from '../../../api/component/component.doc.mjs';
33
33
 
34
34
  /**
35
35
  * The api layer's component() widens its return to `{type: string, data: unknown}`,
@@ -37,13 +37,13 @@ import {doc as componentFn} from '../../../../api/component/component.doc.mjs';
37
37
  * src/types/component to get narrowing + typed data.
38
38
  *
39
39
  * @typedef {(
40
- * | import('../../../../api/component/component.type.mjs').ComponentListResponse
41
- * | import('../../../../api/component/component.type.mjs').ComponentBatchResponse
42
- * | import('../../../../api/component/component.type.mjs').ComponentDetailResponse
43
- * | import('../../../../api/component/component.type.mjs').ComponentDetailPropsResponse
44
- * | import('../../../../api/component/component.type.mjs').ComponentDetailSourceResponse
45
- * | import('../../../../api/component/component.type.mjs').ComponentDetailShowcaseResponse
46
- * | import('../../../../api/component/component.type.mjs').ComponentDetailBlocksResponse
40
+ * | import('../../../api/component/component.type.mjs').ComponentListResponse
41
+ * | import('../../../api/component/component.type.mjs').ComponentBatchResponse
42
+ * | import('../../../api/component/component.type.mjs').ComponentDetailResponse
43
+ * | import('../../../api/component/component.type.mjs').ComponentDetailPropsResponse
44
+ * | import('../../../api/component/component.type.mjs').ComponentDetailSourceResponse
45
+ * | import('../../../api/component/component.type.mjs').ComponentDetailShowcaseResponse
46
+ * | import('../../../api/component/component.type.mjs').ComponentDetailBlocksResponse
47
47
  * )} ComponentResult
48
48
  */
49
49
 
@@ -57,7 +57,7 @@ import {doc as componentFn} from '../../../../api/component/component.doc.mjs';
57
57
  * the thing that resolved (the component) is not the thing being counted.
58
58
  *
59
59
  * @param {ComponentResult} result
60
- * @returns {import('../../../../foundation/debug/command-result.mjs').CommandResult}
60
+ * @returns {import('../../../foundation/debug/command-result.mjs').CommandResult}
61
61
  */
62
62
  function summarize(result) {
63
63
  switch (result.type) {
@@ -90,11 +90,11 @@ function summarize(result) {
90
90
 
91
91
  /**
92
92
  * Project one single-component API result through the existing formatter kit.
93
- * @param {import('../../../../api/component/component.type.mjs').ComponentSingleResponse} result
93
+ * @param {import('../../../api/component/component.type.mjs').ComponentSingleResponse} result
94
94
  * @param {string} requestedName
95
95
  * @param {'full'|'compact'|'brief'} detail
96
96
  * @param {Awaited<ReturnType<typeof resolveTheme>>} themeData
97
- * @returns {import('../../formatters/index.mjs').Block[]}
97
+ * @returns {import('../formatters/index.mjs').Block[]}
98
98
  */
99
99
  function componentDetailBlocks(result, requestedName, detail, themeData) {
100
100
  const requested = (requestedName.split('/').pop() ?? requestedName).replace(
@@ -111,7 +111,7 @@ function componentDetailBlocks(result, requestedName, detail, themeData) {
111
111
  : requested;
112
112
  switch (result.type) {
113
113
  case 'component.detail': {
114
- /** @type {import('../../formatters/index.mjs').Block[]} */
114
+ /** @type {import('../formatters/index.mjs').Block[]} */
115
115
  const out = [record({package: result.package})];
116
116
  if (result.data.parentDoc) {
117
117
  out.push(record(result.data, {fields: ['parentDoc']}));
@@ -150,7 +150,7 @@ function componentDetailBlocks(result, requestedName, detail, themeData) {
150
150
  /** @param {{package: string}} block @param {string} label */
151
151
  const owned = (block, label) =>
152
152
  block.package === result.package ? label : `${label} (${block.package})`;
153
- /** @type {import('../../formatters/index.mjs').Block[]} */
153
+ /** @type {import('../formatters/index.mjs').Block[]} */
154
154
  const out = [record({package: result.package})];
155
155
  if (showcase) {
156
156
  out.push(
@@ -243,7 +243,7 @@ export function registerComponent(program) {
243
243
  lang, zh, dense,
244
244
  }));
245
245
  } catch (e) {
246
- const err = /** @type {import('../../../../api/error.mjs').AstryxError} */ (e);
246
+ const err = /** @type {import('../../../api/error.mjs').AstryxError} */ (e);
247
247
  return cliError(err.message, {suggestions: err.suggestions, code: err.code});
248
248
  }
249
249
 
@@ -326,7 +326,7 @@ export function registerComponent(program) {
326
326
  // description), grouped by category. Fields mirror the JSON keys.
327
327
  const groups = result.data.components;
328
328
  const entries = Object.entries(groups);
329
- /** @type {import('../../formatters/index.mjs').Block[]} */
329
+ /** @type {import('../formatters/index.mjs').Block[]} */
330
330
  const out = [];
331
331
  for (const [cat, items] of entries) {
332
332
  // Skip the synthetic group header when there's only one ungrouped category
@@ -362,7 +362,7 @@ export function registerComponent(program) {
362
362
  nameCounts.set(item.name, set);
363
363
  }
364
364
  }
365
- /** @param {import('../../../../api/component/component.type.mjs').ComponentListEntry} item */
365
+ /** @param {import('../../../api/component/component.type.mjs').ComponentListEntry} item */
366
366
  const importCell = item => {
367
367
  // Use a precomputed import when the API supplies one (integration
368
368
  // components carry it); only fall back to the core resolver for
@@ -392,7 +392,7 @@ export function registerComponent(program) {
392
392
  }
393
393
 
394
394
  case 'component.batch': {
395
- /** @type {import('../../formatters/index.mjs').Block[]} */
395
+ /** @type {import('../formatters/index.mjs').Block[]} */
396
396
  const out = [
397
397
  section('Component batch'),
398
398
  record({count: result.data.count}),
@@ -474,8 +474,8 @@ export function registerComponent(program) {
474
474
 
475
475
  // Re-export lib functions for backward compatibility
476
476
  // (agent-docs.mjs, tests, and generate-skill-doc.sh import from here)
477
- export {discoverComponents, discoverExternalComponentsGrouped, findComponentReadme, findComponentSource, findExternalComponentDoc, resolveImportPath} from '../../../../foundation/discovery/component-discovery.mjs';
478
- export {discoverExternalPackages} from '../../../../foundation/fs/paths.mjs';
479
- export {loadDocs} from '../../../../foundation/discovery/component-loader.mjs';
480
- export {formatFull, formatCompact, formatBrief, formatProps, formatBriefAll} from '../../lib/component-format.mjs';
481
- export {levenshteinDistance, findClosestComponents, searchComponents} from '../../../../foundation/text/string-utils.mjs';
477
+ export {discoverComponents, discoverExternalComponentsGrouped, findComponentReadme, findComponentSource, findExternalComponentDoc, resolveImportPath} from '../../../foundation/discovery/component-discovery.mjs';
478
+ export {discoverExternalPackages} from '../../../foundation/fs/paths.mjs';
479
+ export {loadDocs} from '../../../foundation/discovery/component-loader.mjs';
480
+ export {formatFull, formatCompact, formatBrief, formatProps, formatBriefAll} from '../lib/component-format.mjs';
481
+ export {levenshteinDistance, findClosestComponents, searchComponents} from '../../../foundation/text/string-utils.mjs';
@@ -12,7 +12,7 @@ import {
12
12
  findComponentSource,
13
13
  levenshteinDistance,
14
14
  findClosestComponents,
15
- } from './component/index.mjs';
15
+ } from './component.mjs';
16
16
  import {runCli} from '../../../test-utils/run-cli.mjs';
17
17
 
18
18
  const REPO_ROOT = path.resolve(import.meta.dirname, '../../../../..');
@@ -528,7 +528,7 @@ describe('findExternalComponentDoc', () => {
528
528
  describe('searchComponents', () => {
529
529
  it('finds Collapsible when searching "accordion"', async () => {
530
530
  const {searchComponents, discoverComponents} =
531
- await import('./component/index.mjs');
531
+ await import('./component.mjs');
532
532
  const components = discoverComponents('packages/core');
533
533
  const results = await searchComponents(
534
534
  'accordion',
@@ -542,7 +542,7 @@ describe('searchComponents', () => {
542
542
 
543
543
  it('finds Dialog when searching "modal"', async () => {
544
544
  const {searchComponents, discoverComponents} =
545
- await import('./component/index.mjs');
545
+ await import('./component.mjs');
546
546
  const components = discoverComponents('packages/core');
547
547
  const results = await searchComponents(
548
548
  'modal',
@@ -556,7 +556,7 @@ describe('searchComponents', () => {
556
556
 
557
557
  it('returns multiple matches for ambiguous terms like "select"', async () => {
558
558
  const {searchComponents, discoverComponents} =
559
- await import('./component/index.mjs');
559
+ await import('./component.mjs');
560
560
  const components = discoverComponents('packages/core');
561
561
  const results = await searchComponents(
562
562
  'select',
@@ -571,7 +571,7 @@ describe('searchComponents', () => {
571
571
 
572
572
  it('finds exact name matches with score 100', async () => {
573
573
  const {searchComponents, discoverComponents} =
574
- await import('./component/index.mjs');
574
+ await import('./component.mjs');
575
575
  const components = discoverComponents('packages/core');
576
576
  const results = await searchComponents(
577
577
  'Button',
@@ -584,7 +584,7 @@ describe('searchComponents', () => {
584
584
 
585
585
  it('returns empty array for complete gibberish', async () => {
586
586
  const {searchComponents, discoverComponents} =
587
- await import('./component/index.mjs');
587
+ await import('./component.mjs');
588
588
  const components = discoverComponents('packages/core');
589
589
  const results = await searchComponents(
590
590
  'zzzzzzzzzzz',
@@ -58,10 +58,10 @@ export const doc = {
58
58
  {label: 'Every CLI doc, one line each', cli: 'astryx docs cli --depth all'},
59
59
  {
60
60
  label: 'The integration guides as one read',
61
- cli: 'astryx docs cli/integrations --depth all --detail full',
61
+ cli: 'astryx docs authoring --depth all --detail full',
62
62
  },
63
63
  {label: 'One API function', cli: 'astryx docs cli/api/functions/search'},
64
- {label: 'A whole guide', cli: 'astryx docs cli/integrations/quick-start --full'},
64
+ {label: 'A whole guide', cli: 'astryx docs authoring/quick-start --full'},
65
65
  ],
66
66
  outsideProject: 'same',
67
67
  exitCodes: [
@@ -535,7 +535,7 @@ function emitNode(node, detail, run) {
535
535
  text(
536
536
  [
537
537
  // A child's route is its parent's route and its name, except a flat
538
- // topic in the Unorganized level, which keeps its own name.
538
+ // topic in a topic level, which keeps its own name.
539
539
  node.slots.every(slot =>
540
540
  slot.children.every(child => child.route.startsWith(`${node.route}/`)),
541
541
  )
@@ -227,7 +227,7 @@ describe('the docs tree, one level at a time', () => {
227
227
  it('wraps namespace summaries without discarding searchable words', async () => {
228
228
  const {status, stdout} = await runCli([
229
229
  'docs',
230
- 'cli/integrations/building-blocks/templates',
230
+ 'authoring/building-blocks/templates',
231
231
  ]);
232
232
  expect(status).toBe(0);
233
233
  expect(stdout).toMatch(/^start-a-template +Help others build apps faster/m);
@@ -274,8 +274,8 @@ describe('the docs tree, one level at a time', () => {
274
274
  }, SLOW);
275
275
 
276
276
  it('reads the integration guides by their routes, and not by the old name', async () => {
277
- // cli/integrations is a namespace: one level, its guides by slot.
278
- const level = await runCli(['docs', 'cli/integrations']);
277
+ // authoring is a namespace: one level, its guides by slot.
278
+ const level = await runCli(['docs', 'authoring']);
279
279
  expect(level.status).toBe(0);
280
280
  expect(level.stdout).toMatch(
281
281
  /^quick-start +Create a new integration package/m,
@@ -283,42 +283,42 @@ describe('the docs tree, one level at a time', () => {
283
283
  expect(level.stdout).toMatch(/^building-blocks +/m);
284
284
  const guide = await runCli([
285
285
  'docs',
286
- 'cli/integrations/building-blocks/codemods',
286
+ 'authoring/building-blocks/codemods',
287
287
  '--index',
288
288
  ]);
289
289
  expect(guide.status).toBe(0);
290
290
  expect(guide.stdout).toMatch(
291
- /Read one section: .*docs cli\/integrations\/building-blocks\/codemods <section>/,
291
+ /Read one section: .*docs authoring\/building-blocks\/codemods <section>/,
292
292
  );
293
293
  // A bare read is one level too: the sections, and how to read it all.
294
294
  const bare = await runCli([
295
295
  'docs',
296
- 'cli/integrations/building-blocks/codemods',
296
+ 'authoring/building-blocks/codemods',
297
297
  ]);
298
298
  expect(bare.status).toBe(0);
299
299
  expect(bare.stdout).toBe(guide.stdout);
300
300
  expect(bare.stdout).toMatch(
301
- /Read everything: +.*docs cli\/integrations\/building-blocks\/codemods --full$/m,
301
+ /Read everything: +.*docs authoring\/building-blocks\/codemods --full$/m,
302
302
  );
303
303
  const one = await runCli([
304
304
  'docs',
305
- 'cli/integrations/building-blocks/codemods',
305
+ 'authoring/building-blocks/codemods',
306
306
  'which-codemods-run',
307
307
  ]);
308
308
  expect(one.status).toBe(0);
309
309
  expect(one.stdout).toMatch(/^## Choose when a codemod runs$/m);
310
310
  expect(one.stdout).toMatch(
311
- /^Up: .*docs cli\/integrations\/building-blocks\/codemods --index$/m,
311
+ /^Up: .*docs authoring\/building-blocks\/codemods --index$/m,
312
312
  );
313
313
  expect(one.stdout).toMatch(
314
- /^Previous: .*docs cli\/integrations\/building-blocks\/codemods write-the-transform$/m,
314
+ /^Previous: .*docs authoring\/building-blocks\/codemods write-the-transform$/m,
315
315
  );
316
316
  expect(one.stdout).toMatch(
317
- /^Next: .*docs cli\/integrations\/building-blocks\/codemods run-codemods-in-an-app$/m,
317
+ /^Next: .*docs authoring\/building-blocks\/codemods run-codemods-in-an-app$/m,
318
318
  );
319
319
  const full = await runCli([
320
320
  'docs',
321
- 'cli/integrations/building-blocks/codemods',
321
+ 'authoring/building-blocks/codemods',
322
322
  '--full',
323
323
  ]);
324
324
  expect(full.status).toBe(0);
@@ -343,11 +343,11 @@ describe('the docs tree, one level at a time', () => {
343
343
  type: 'docs.detail.section',
344
344
  data: {id: 'side-panels', title: 'Side panels'},
345
345
  });
346
- const missing = await runCli(['docs', 'cli', 'zzzz-nope', '--json']);
346
+ const missing = await runCli(['docs', 'authoring', 'zzzz-nope', '--json']);
347
347
  expect(missing.status).toBe(1);
348
348
  const error = JSON.parse(missing.stdout);
349
349
  expect(error).toMatchObject({code: 'ERR_UNKNOWN_SECTION'});
350
- expect(error.suggestions.map(s => s.name)).toContain('cli/integrations/quick-start');
350
+ expect(error.suggestions.map(s => s.name)).toContain('authoring/quick-start');
351
351
  }, SLOW);
352
352
  });
353
353
 
@@ -19,7 +19,10 @@ import {Command} from 'commander';
19
19
  import {discoverCoreTemplates} from '../../../foundation/discovery/template-adapter.mjs';
20
20
  import {discoverOwnedComponents} from '../../../foundation/discovery/component-discovery.mjs';
21
21
  import {discoverBuiltinTopics} from '../../../foundation/discovery/docs-discovery.mjs';
22
- import {findCoreDir, findProjectCoreDir} from '../../../foundation/fs/paths.mjs';
22
+ import {
23
+ findCoreDir,
24
+ findProjectCoreDir,
25
+ } from '../../../foundation/fs/paths.mjs';
23
26
  import {registerDoctor} from './doctor.mjs';
24
27
  import {expectRecordsCarry} from '../../../test-utils/text-projection.mjs';
25
28
 
@@ -674,6 +677,51 @@ describe('doctor integration — command', () => {
674
677
  }
675
678
  });
676
679
 
680
+ it('validate prints the public-import findings as listed only, and still exits 0', async () => {
681
+ writeIntegration({id: 'hero', name: 'Hero', type: 'block'});
682
+ fs.writeFileSync(
683
+ path.join(tmpDir, 'package.json'),
684
+ JSON.stringify({name: '@acme/widgets', version: '1.2.3', exports: {}}),
685
+ );
686
+ process.chdir(tmpDir);
687
+
688
+ await createProgram().parseAsync([
689
+ 'node',
690
+ 'astryx',
691
+ 'doctor',
692
+ 'integration',
693
+ 'validate',
694
+ ]);
695
+
696
+ const output = logCalls.join('\n');
697
+ expect(output).toContain('note (template_import_not_exported):');
698
+ expect(output).toContain('Fix:');
699
+ expect(output).toContain('[ok] No integration issues found.');
700
+ expect(process.exitCode).toBeUndefined();
701
+ });
702
+
703
+ it('validate lists, as not checked, the imports it cannot judge when package.json has no name', async () => {
704
+ writeIntegration({id: 'hero', name: 'Hero', type: 'block'});
705
+ fs.writeFileSync(
706
+ path.join(tmpDir, 'package.json'),
707
+ JSON.stringify({version: '1.2.3'}),
708
+ );
709
+ process.chdir(tmpDir);
710
+
711
+ await createProgram().parseAsync([
712
+ 'node',
713
+ 'astryx',
714
+ 'doctor',
715
+ 'integration',
716
+ 'validate',
717
+ ]);
718
+
719
+ const output = logCalls.join('\n');
720
+ expect(output).toMatch(/\[info\] Imports not checked: 1:/);
721
+ expect(output).toContain('package.json has no "name"');
722
+ expect(process.exitCode).toBeUndefined();
723
+ });
724
+
677
725
  it('templates warns on a Core id and writes the exact --package command', async () => {
678
726
  const [core] = await discoverCoreTemplates();
679
727
  expect(core).toBeDefined();
@@ -21,7 +21,7 @@ export const doc = {
21
21
  "the docs the CLI reads, the newest release the computer knows of, with the install and upgrade commands when the project's release is older, and every `astryx` command on the search path, warning when the first one is not the Astryx CLI or predates the hand-off. It writes nothing, but loading astryx.config runs that file's code. " +
22
22
  'The `integration` subcommands provide authoring checks for one integration package. ' +
23
23
  'Outside a project it checks the computer instead: the Node version, the release that answered, ' +
24
- 'the newest release this computer knows of, with the command that upgrades the CLI installed for the computer when it is older, the astryx commands on the search path, the integrations installed for the computer (warning about one it skips), and whether the computer-level agent text (`astryx init --user`) is present, matches this release, was edited, or is turned off (info only). ' +
24
+ "the newest release this computer knows of, with the command that upgrades the CLI installed for the computer when it is older, the astryx commands on the search path, the integrations installed for the computer (warning about one it skips), and whether the computer-level agent text (`astryx init --user`) is present, matches this release, was edited, or is turned off (info only); when that text names `astryx` and no `astryx` command is on the search path, apart from a package runner's own copy, it says so and names the install step the text records, or npm's when it records none. " +
25
25
  'It never fails a check because the folder has no project.',
26
26
  fn: 'doctor',
27
27
  subcommands: ['integration'],
@@ -179,6 +179,34 @@ function docTypesBlocks(report, failed) {
179
179
  return blocks;
180
180
  }
181
181
 
182
+ /**
183
+ * The component doc names that could not be judged against their sources.
184
+ * @param {import('../../../api/integration/validate-integration.type.mjs').DocNamesReport | undefined} report
185
+ */
186
+ function docNamesBlocks(report) {
187
+ if (!report || report.unchecked.length === 0) return [];
188
+ return [
189
+ text(
190
+ `[info] Component names not checked: ${report.unchecked.length} doc(s) whose source exports this check cannot read:`,
191
+ ),
192
+ records(report.unchecked, {fields: ['file', 'name', 'reason']}),
193
+ ];
194
+ }
195
+
196
+ /**
197
+ * The block component names this check could not judge.
198
+ * @param {import('../../../api/integration/validate-integration.type.mjs').BlockComponentsReport | undefined} report
199
+ */
200
+ function blockComponentsBlocks(report) {
201
+ if (!report || report.unchecked.length === 0) return [];
202
+ return [
203
+ text(
204
+ `[info] Block component names not checked: ${report.unchecked.length} name(s) this check could not judge:`,
205
+ ),
206
+ records(report.unchecked, {fields: ['file', 'name', 'reason']}),
207
+ ];
208
+ }
209
+
182
210
  /**
183
211
  * @param {import('../../../api/integration/validate-integration.type.mjs').ValidateIntegrationResponse['data']} data
184
212
  */
@@ -204,6 +232,18 @@ function printIntegrationValidation(data) {
204
232
  `${warnings} warning${warnings === 1 ? '' : 's'}`,
205
233
  ),
206
234
  ...docTypesBlocks(data.docTypes, errors > 0),
235
+ ...docNamesBlocks(data.docNames),
236
+ ...blockComponentsBlocks(data.blockComponents),
237
+ ...(data.publicImports && data.publicImports.unchecked.length > 0
238
+ ? [
239
+ text(
240
+ `[info] Imports not checked: ${data.publicImports.unchecked.length}:`,
241
+ ),
242
+ records(data.publicImports.unchecked, {
243
+ fields: ['file', 'specifier', 'reason'],
244
+ }),
245
+ ]
246
+ : []),
207
247
  );
208
248
  }
209
249
 
@@ -191,6 +191,31 @@ describe('doctor — individual checks', () => {
191
191
  expect(res.fix).toContain('`npx astryx init --features agents`');
192
192
  });
193
193
 
194
+ it('agent-docs: at the top of a home settings folder, the fix moves the project or edits by hand, never init', () => {
195
+ const saved = {HOME: process.env.HOME, USERPROFILE: process.env.USERPROFILE};
196
+ process.env.HOME = tmpDir;
197
+ process.env.USERPROFILE = tmpDir;
198
+ try {
199
+ const project = path.join(tmpDir, '.dotfiles');
200
+ fs.mkdirSync(project);
201
+ fs.writeFileSync(path.join(project, 'package.json'), '{"name":"dots"}');
202
+ for (const content of [null, '# Agents\nno markers here\n']) {
203
+ if (content != null) fs.writeFileSync(path.join(project, 'AGENTS.md'), content);
204
+ const res = checkAgentDocs({cwd: project});
205
+ expect(res.fix).toContain('Agent docs are not written into the home folder');
206
+ expect(res.fix).toContain(
207
+ 'Move the project into a folder of its own, or edit the Astryx block in its agent file by hand.',
208
+ );
209
+ expect(res.fix).not.toContain('init --features agents');
210
+ }
211
+ } finally {
212
+ for (const [key, value] of Object.entries(saved)) {
213
+ if (value === undefined) delete process.env[key];
214
+ else process.env[key] = value;
215
+ }
216
+ }
217
+ });
218
+
194
219
  it('agent-docs: WARN when docs exist without XDS markers', () => {
195
220
  fs.writeFileSync(path.join(tmpDir, 'AGENTS.md'), '# AGENTS\nno markers here');
196
221
  const res = checkAgentDocs({cwd: tmpDir});
@@ -221,6 +246,25 @@ describe('doctor — individual checks', () => {
221
246
  );
222
247
  });
223
248
 
249
+ it('agent-docs: PASS from a nested package, reading the root that declares Astryx', () => {
250
+ fs.writeFileSync(
251
+ path.join(tmpDir, 'package.json'),
252
+ JSON.stringify({dependencies: {'@astryxdesign/core': '0.7.0'}}),
253
+ );
254
+ fs.writeFileSync(
255
+ path.join(tmpDir, 'AGENTS.md'),
256
+ '<!-- ASTRYX:START -->\nstuff\n<!-- ASTRYX:END -->\n',
257
+ );
258
+ const functions = path.join(tmpDir, 'functions');
259
+ fs.mkdirSync(path.join(functions, 'src'), {recursive: true});
260
+ fs.writeFileSync(path.join(functions, 'package.json'), '{"name":"functions"}');
261
+
262
+ expect(checkAgentDocs({cwd: path.join(functions, 'src')})).toMatchObject({
263
+ status: 'pass',
264
+ message: `Astryx agent docs section present in ${path.join('..', '..', 'AGENTS.md')}.`,
265
+ });
266
+ });
267
+
224
268
  it('agent-docs: reads every file init can write, Hermes included', () => {
225
269
  fs.writeFileSync(
226
270
  path.join(tmpDir, 'HERMES.md'),
@@ -232,6 +276,27 @@ describe('doctor — individual checks', () => {
232
276
  });
233
277
  });
234
278
 
279
+ it('agent-docs: FAIL when GEMINI.md imports an AGENTS.md that holds no block', () => {
280
+ const geminiImport = '<!-- ASTRYX:START -->\n@./AGENTS.md\n<!-- ASTRYX:END -->\n';
281
+ fs.writeFileSync(path.join(tmpDir, 'GEMINI.md'), geminiImport);
282
+ expect(checkAgentDocs({cwd: tmpDir})).toMatchObject({
283
+ status: 'fail',
284
+ message: expect.stringMatching(/^GEMINI\.md imports AGENTS\.md, which holds no Astryx block/),
285
+ fix: expect.stringMatching(/init --features agents/),
286
+ });
287
+
288
+ // A copy elsewhere does not mend it: Codex and Gemini CLI still get none.
289
+ fs.writeFileSync(path.join(tmpDir, 'CLAUDE.md'), '<!-- ASTRYX:START -->\nstuff\n<!-- ASTRYX:END -->\n');
290
+ fs.writeFileSync(path.join(tmpDir, 'AGENTS.md'), '# Agents\n');
291
+ expect(checkAgentDocs({cwd: tmpDir}).status).toBe('fail');
292
+
293
+ fs.writeFileSync(path.join(tmpDir, 'AGENTS.md'), '<!-- ASTRYX:START -->\nstuff\n<!-- ASTRYX:END -->\n');
294
+ expect(checkAgentDocs({cwd: tmpDir})).toMatchObject({
295
+ status: 'pass',
296
+ message: 'Astryx agent docs section present in AGENTS.md, CLAUDE.md.',
297
+ });
298
+ });
299
+
235
300
  it('agent-docs: INFO names what it looked for and where', () => {
236
301
  const res = checkAgentDocs({cwd: tmpDir});
237
302
  expect(res.status).toBe('info');
@@ -11,17 +11,17 @@ import {
11
11
  formatHookCompact,
12
12
  formatHookBrief,
13
13
  formatHookParams,
14
- } from '../../lib/hook-format.mjs';
15
- import {getCliInvocation} from '../../../../foundation/env/package-manager.mjs';
16
- import {jsonOut} from '../../../../foundation/response/json.mjs';
17
- import {emit, section, text, list, records, code, record} from '../../formatters/index.mjs';
18
- import {cliError} from '../../lib/cli-error.mjs';
19
- import {defineCommand} from '../../lib/define-command.mjs';
20
- import {resultSet} from '../../../../foundation/debug/index.mjs';
21
- import {ERROR_CODES} from '../../../../foundation/response/error-codes.mjs';
22
- import {hook as hookApi} from '../../../../api/hook/hook.mjs';
23
- import {doc as hookCommand} from '../hook.doc.mjs';
24
- import {doc as hookFn} from '../../../../api/hook/hook.doc.mjs';
14
+ } from '../lib/hook-format.mjs';
15
+ import {getCliInvocation} from '../../../foundation/env/package-manager.mjs';
16
+ import {jsonOut} from '../../../foundation/response/json.mjs';
17
+ import {emit, section, text, list, records, code, record} from '../formatters/index.mjs';
18
+ import {cliError} from '../lib/cli-error.mjs';
19
+ import {defineCommand} from '../lib/define-command.mjs';
20
+ import {resultSet} from '../../../foundation/debug/index.mjs';
21
+ import {ERROR_CODES} from '../../../foundation/response/error-codes.mjs';
22
+ import {hook as hookApi} from '../../../api/hook/hook.mjs';
23
+ import {doc as hookCommand} from './hook.doc.mjs';
24
+ import {doc as hookFn} from '../../../api/hook/hook.doc.mjs';
25
25
 
26
26
  /**
27
27
  * The api layer's hook() widens its return to `{type: string, data: unknown}`,
@@ -29,9 +29,9 @@ import {doc as hookFn} from '../../../../api/hook/hook.doc.mjs';
29
29
  * the colocated api/hook/hook.type.mjs to get narrowing + typed data.
30
30
  *
31
31
  * @typedef {(
32
- * | import('../../../../api/hook/hook.type.mjs').HookListResponse
33
- * | import('../../../../api/hook/hook.type.mjs').HookDetailResponse
34
- * | import('../../../../api/hook/hook.type.mjs').HookDetailParamsResponse
32
+ * | import('../../../api/hook/hook.type.mjs').HookListResponse
33
+ * | import('../../../api/hook/hook.type.mjs').HookDetailResponse
34
+ * | import('../../../api/hook/hook.type.mjs').HookDetailParamsResponse
35
35
  * )} HookResult
36
36
  */
37
37
 
@@ -40,7 +40,7 @@ import {doc as hookFn} from '../../../../api/hook/hook.doc.mjs';
40
40
  * count is every hook across the groups — names or docs, depending on detail.
41
41
  *
42
42
  * @param {HookResult} result
43
- * @returns {import('../../../../foundation/debug/command-result.mjs').CommandResult}
43
+ * @returns {import('../../../foundation/debug/command-result.mjs').CommandResult}
44
44
  */
45
45
  function summarize(result) {
46
46
  if (result.type === 'hook.list') {
@@ -90,7 +90,7 @@ export function registerHook(program) {
90
90
  lang, zh,
91
91
  }));
92
92
  } catch (e) {
93
- const err = /** @type {import('../../../../api/error.mjs').AstryxError} */ (e);
93
+ const err = /** @type {import('../../../api/error.mjs').AstryxError} */ (e);
94
94
  return cliError(err.message, {suggestions: err.suggestions, code: err.code});
95
95
  }
96
96
 
@@ -112,7 +112,7 @@ export function registerHook(program) {
112
112
  // The whole view is one markdown document: a `## <category>` heading
113
113
  // over each category's concatenated hook docs.
114
114
  const groups = result.data.components;
115
- /** @type {import('../../formatters/index.mjs').Block[]} */
115
+ /** @type {import('../formatters/index.mjs').Block[]} */
116
116
  const out = [];
117
117
  for (const [cat, items] of Object.entries(groups)) {
118
118
  const body = items
@@ -130,7 +130,7 @@ export function registerHook(program) {
130
130
  // --detail compact — one record (name + description) per hook,
131
131
  // grouped by category.
132
132
  const groups = result.data.components;
133
- /** @type {import('../../formatters/index.mjs').Block[]} */
133
+ /** @type {import('../formatters/index.mjs').Block[]} */
134
134
  const out = [];
135
135
  for (const [cat, items] of Object.entries(groups)) {
136
136
  out.push(section(cat), records(items, {fields: ['name', 'description']}));
@@ -147,7 +147,7 @@ export function registerHook(program) {
147
147
  const [cat, hookNames] = Object.entries(groups)[0];
148
148
  emit(section(`${cat}:`), list(hookNames));
149
149
  } else {
150
- /** @type {import('../../formatters/index.mjs').Block[]} */
150
+ /** @type {import('../formatters/index.mjs').Block[]} */
151
151
  const out = [];
152
152
  for (const [category, hookNames] of Object.entries(groups)) {
153
153
  out.push(section(category), list(hookNames));
@@ -197,12 +197,12 @@ export function registerHook(program) {
197
197
  }
198
198
 
199
199
  // Re-export lib functions for external consumers
200
- export {discoverHooks, findHookDoc, getAllHookNames} from '../../../../foundation/discovery/hook-discovery.mjs';
201
- export {loadDocs} from '../../../../foundation/discovery/component-loader.mjs';
200
+ export {discoverHooks, findHookDoc, getAllHookNames} from '../../../foundation/discovery/hook-discovery.mjs';
201
+ export {loadDocs} from '../../../foundation/discovery/component-loader.mjs';
202
202
  export {
203
203
  formatHookFull,
204
204
  formatHookCompact,
205
205
  formatHookBrief,
206
206
  formatHookBriefAll,
207
207
  formatHookParams,
208
- } from '../../lib/hook-format.mjs';
208
+ } from '../lib/hook-format.mjs';