@astryxdesign/cli 0.6.3 → 0.6.4-canary.06c8fa3

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 (670) hide show
  1. package/CHANGELOG.md +194 -0
  2. package/README.md +121 -85
  3. package/api/blog/blog.doc.mjs +1 -0
  4. package/api/build/_adapter.d.mts +50 -0
  5. package/api/build/_adapter.mjs +60 -0
  6. package/api/build/build.doc.mjs +16 -9
  7. package/api/build/build.test.mjs +197 -8
  8. package/api/build/build.type.d.mts +91 -2
  9. package/api/build/build.type.mjs +52 -8
  10. package/api/build/help/help.d.mts +12 -5
  11. package/api/build/help/help.mjs +69 -6
  12. package/api/build/kit/kit.d.mts +4 -1
  13. package/api/build/kit/kit.mjs +165 -49
  14. package/api/build/kit/rank.d.mts +44 -0
  15. package/api/build/kit/rank.mjs +432 -0
  16. package/api/build/kit/rank.test.mjs +196 -0
  17. package/api/component/_adapter.d.mts +31 -12
  18. package/api/component/_adapter.mjs +79 -15
  19. package/api/component/component.d.mts +6 -3
  20. package/api/component/component.doc.mjs +36 -13
  21. package/api/component/component.mjs +339 -22
  22. package/api/component/component.test.mjs +38 -0
  23. package/api/component/component.type.d.mts +47 -11
  24. package/api/component/component.type.mjs +76 -24
  25. package/api/component/detail/blocks/blocks.d.mts +2 -1
  26. package/api/component/detail/blocks/blocks.mjs +4 -3
  27. package/api/component/list/list.d.mts +0 -5
  28. package/api/component/list/list.mjs +40 -11
  29. package/api/discover/_adapter.d.mts +114 -6
  30. package/api/discover/_adapter.mjs +372 -17
  31. package/api/discover/_adapter.test.mjs +215 -0
  32. package/api/discover/_catalog-view.d.mts +115 -0
  33. package/api/discover/_catalog-view.mjs +203 -0
  34. package/api/discover/_catalog-view.test.mjs +128 -0
  35. package/api/discover/detail/detail.d.mts +18 -6
  36. package/api/discover/detail/detail.mjs +67 -13
  37. package/api/discover/detail/detail.test.mjs +85 -0
  38. package/api/discover/detail/item/item.d.mts +26 -0
  39. package/api/discover/detail/item/item.mjs +78 -0
  40. package/api/discover/detail/item/item.test.mjs +73 -0
  41. package/api/discover/discover.d.mts +3 -9
  42. package/api/discover/discover.doc.mjs +62 -18
  43. package/api/discover/discover.mjs +220 -36
  44. package/api/discover/discover.test.mjs +11 -2
  45. package/api/discover/discover.type.d.mts +150 -11
  46. package/api/discover/discover.type.mjs +107 -17
  47. package/api/discover/list/list.d.mts +20 -6
  48. package/api/discover/list/list.mjs +45 -12
  49. package/api/discover/list/list.test.mjs +46 -0
  50. package/api/discover/search/search.d.mts +18 -16
  51. package/api/discover/search/search.mjs +102 -56
  52. package/api/discover/search/search.test.mjs +144 -10
  53. package/api/docs/_adapter.d.mts +272 -41
  54. package/api/docs/_adapter.mjs +985 -108
  55. package/api/docs/compiled-topics.test.mjs +78 -0
  56. package/api/docs/detail/detail.mjs +22 -63
  57. package/api/docs/detail/section/section.d.mts +1 -1
  58. package/api/docs/detail/section/section.mjs +54 -19
  59. package/api/docs/detail/section/section.test.mjs +50 -0
  60. package/api/docs/docs.d.mts +10 -3
  61. package/api/docs/docs.doc.mjs +55 -16
  62. package/api/docs/docs.mjs +53 -10
  63. package/api/docs/docs.test.mjs +166 -4
  64. package/api/docs/docs.type.d.mts +221 -5
  65. package/api/docs/docs.type.mjs +153 -11
  66. package/api/docs/index/index.d.mts +18 -0
  67. package/api/docs/index/index.mjs +40 -0
  68. package/api/docs/index/index.test.mjs +62 -0
  69. package/api/docs/integration-tree.test.mjs +555 -0
  70. package/api/docs/integrationDocs.test.mjs +114 -8
  71. package/api/docs/list/list.mjs +28 -12
  72. package/api/docs/node/node.d.mts +43 -0
  73. package/api/docs/node/node.mjs +192 -0
  74. package/api/docs/reference-blocks.test.mjs +406 -0
  75. package/api/doctor/doctor.d.mts +104 -1
  76. package/api/doctor/doctor.doc.mjs +1 -0
  77. package/api/doctor/doctor.mjs +635 -7
  78. package/api/doctor/doctor.test.mjs +732 -11
  79. package/api/gap-report/gap-report.doc.mjs +8 -4
  80. package/api/hook/_adapter.mjs +19 -5
  81. package/api/hook/hook.doc.mjs +1 -0
  82. package/api/hook/hook.type.d.mts +3 -3
  83. package/api/hook/hook.type.mjs +11 -11
  84. package/api/hook/list/list.d.mts +2 -2
  85. package/api/hook/list/list.mjs +69 -17
  86. package/api/index.d.mts +2 -3
  87. package/api/index.mjs +5 -4
  88. package/api/init/init.doc.mjs +6 -1
  89. package/api/init/init.test.mjs +41 -1
  90. package/api/init/remove/remove.mjs +1 -1
  91. package/api/init/run/run.mjs +20 -10
  92. package/api/integration/add-contribution.component-names.test.mjs +120 -0
  93. package/api/integration/add-contribution.d.mts +2 -1
  94. package/api/integration/add-contribution.mjs +130 -15
  95. package/api/integration/add-contribution.test.mjs +258 -7
  96. package/api/integration/add-helpers.d.mts +5 -2
  97. package/api/integration/add-helpers.mjs +36 -9
  98. package/api/integration/add-theme.mjs +34 -64
  99. package/api/integration/add-theme.test.mjs +105 -21
  100. package/api/integration/authoring-checks.mjs +138 -28
  101. package/api/integration/authoring-checks.test.mjs +179 -7
  102. package/api/integration/authoring-checks.type.mjs +6 -1
  103. package/api/integration/integration-authoring.type.d.mts +3 -1
  104. package/api/integration/integration-authoring.type.mjs +2 -0
  105. package/api/integration/integration-block-exports.test.mjs +10 -6
  106. package/api/integration/integrationAdd.doc.mjs +14 -4
  107. package/api/integration/integrationAddAgentDoc.doc.mjs +1 -0
  108. package/api/integration/integrationAddCodemod.doc.mjs +1 -0
  109. package/api/integration/integrationAddComponent.doc.mjs +2 -1
  110. package/api/integration/integrationAddDoc.doc.mjs +8 -1
  111. package/api/integration/integrationAddTemplate.doc.mjs +1 -0
  112. package/api/integration/integrationAddTheme.doc.mjs +6 -5
  113. package/api/integration/integrationComponentConflicts.doc.mjs +1 -0
  114. package/api/integration/integrationDocConflicts.doc.mjs +2 -1
  115. package/api/integration/integrationPackCheck.doc.mjs +2 -1
  116. package/api/integration/integrationTemplateConflicts.doc.d.mts +3 -0
  117. package/api/integration/integrationTemplateConflicts.doc.mjs +4 -0
  118. package/api/integration/pack-check.lifecycle-output.test.mjs +105 -0
  119. package/api/integration/pack-check.mjs +111 -10
  120. package/api/integration/pack-check.test.mjs +387 -47
  121. package/api/integration/pack-check.type.d.mts +26 -2
  122. package/api/integration/pack-check.type.mjs +14 -1
  123. package/api/integration/summarizeIssues.doc.mjs +1 -0
  124. package/api/integration/template-conflict-compatibility.test.mjs +73 -0
  125. package/api/integration/validate-integration-fixes.test.mjs +1389 -0
  126. package/api/integration/validate-integration.mjs +52 -102
  127. package/api/integration/validate-integration.test.mjs +179 -26
  128. package/api/integration/validate-unread-theme-folders.test.mjs +110 -0
  129. package/api/integration/validateIntegration.doc.mjs +3 -2
  130. package/api/json/assertResponse.doc.mjs +1 -0
  131. package/api/json/envelope-types.test.mjs +76 -0
  132. package/api/json/index.ts +2 -1
  133. package/api/json/isError.doc.mjs +1 -0
  134. package/api/json/parseResponse.doc.mjs +3 -2
  135. package/api/search/search-return-type.test.mjs +54 -0
  136. package/api/search/search.d.mts +62 -11
  137. package/api/search/search.doc.mjs +8 -2
  138. package/api/search/search.mjs +471 -83
  139. package/api/search/search.test.mjs +142 -1
  140. package/api/search/search.type.d.mts +15 -3
  141. package/api/search/search.type.mjs +5 -2
  142. package/api/swizzle/copy/copy.mjs +28 -11
  143. package/api/swizzle/swizzle.doc.mjs +2 -1
  144. package/api/swizzle/swizzle.type.d.mts +2 -2
  145. package/api/swizzle/swizzle.type.mjs +2 -2
  146. package/api/template/copy/copy.mjs +17 -23
  147. package/api/template/copy/copy.test.mjs +17 -0
  148. package/api/template/list/list.mjs +1 -0
  149. package/api/template/table-floating-bulk-actions.test.mjs +66 -0
  150. package/api/template/template-integration.test.mjs +1008 -3
  151. package/api/template/template-suffix.test.mjs +41 -21
  152. package/api/template/template.d.mts +1 -1
  153. package/api/template/template.doc.mjs +30 -8
  154. package/api/template/template.mjs +46 -9
  155. package/api/template/template.type.d.mts +12 -14
  156. package/api/template/template.type.mjs +15 -14
  157. package/api/theme/_adapter.d.mts +2 -3
  158. package/api/theme/_adapter.mjs +4 -5
  159. package/api/theme/add/add.binary.test.mjs +84 -0
  160. package/api/theme/add/add.mjs +31 -22
  161. package/api/theme/add/add.rollback.test.mjs +158 -0
  162. package/api/theme/add/add.staging.test.mjs +83 -0
  163. package/api/theme/add/add.test.mjs +14 -1
  164. package/api/theme/build/build.family.test.mjs +7 -12
  165. package/api/theme/build/build.mjs +140 -59
  166. package/api/theme/build/build.public-component-vars.test.mjs +1 -1
  167. package/api/theme/build/build.receipt-doc.test.mjs +111 -0
  168. package/api/theme/build/build.rollback.test.mjs +148 -0
  169. package/api/theme/build/build.test.mjs +127 -0
  170. package/api/theme/build/font-warning.mjs +3 -3
  171. package/api/theme/build/font-warning.test.mjs +5 -2
  172. package/api/theme/generateTonalPalette.doc.mjs +1 -0
  173. package/api/theme/integration-themes.test.mjs +39 -28
  174. package/api/theme/list/list.test.mjs +19 -20
  175. package/api/theme/listThemes.doc.mjs +6 -5
  176. package/api/theme/palette/generate/generate.mjs +8 -3
  177. package/api/theme/palette/generate/generate.test.mjs +96 -0
  178. package/api/theme/palette/generate/generator.d.mts +10 -13
  179. package/api/theme/palette/generate/generator.mjs +15 -4
  180. package/api/theme/palette/generate/generator.test.mjs +10 -0
  181. package/api/theme/template/template.mjs +11 -2
  182. package/api/theme/template/template.test.mjs +20 -0
  183. package/api/theme/theme.type.d.mts +170 -11
  184. package/api/theme/theme.type.mjs +94 -27
  185. package/api/theme/themeAdd.doc.mjs +4 -3
  186. package/api/theme/themeBuild.doc.mjs +8 -4
  187. package/api/theme/themeList.doc.mjs +6 -3
  188. package/api/theme/themeListAvailable.doc.mjs +5 -3
  189. package/api/theme/themePaletteGenerate.doc.mjs +1 -0
  190. package/api/theme/themeTargets.doc.mjs +1 -0
  191. package/api/theme/themeTemplate.doc.mjs +6 -2
  192. package/api/upgrade/_adapter.d.mts +32 -5
  193. package/api/upgrade/_adapter.mjs +139 -22
  194. package/api/upgrade/list/list.mjs +2 -1
  195. package/api/upgrade/list/list.test.mjs +73 -0
  196. package/api/upgrade/project-context.test.mjs +272 -0
  197. package/api/upgrade/provider-agreement.test.mjs +152 -0
  198. package/api/upgrade/run/files-changed.test.mjs +111 -0
  199. package/api/upgrade/run/run.mjs +359 -60
  200. package/api/upgrade/status/status.mjs +2 -2
  201. package/api/upgrade/upgrade.doc.mjs +12 -5
  202. package/api/upgrade/upgrade.type.d.mts +43 -5
  203. package/api/upgrade/upgrade.type.mjs +29 -13
  204. package/assets/codemods/__tests__/registry.test.mjs +1 -0
  205. package/assets/codemods/__tests__/runner.test.mjs +332 -8
  206. package/assets/codemods/file-count.test.mjs +163 -0
  207. package/assets/codemods/integration-discovery.mjs +48 -4
  208. package/assets/codemods/integration-discovery.test.mjs +73 -0
  209. package/assets/codemods/integration-runner.mjs +59 -7
  210. package/assets/codemods/integration-runner.protection.test.mjs +153 -0
  211. package/assets/codemods/registry.mjs +1 -0
  212. package/assets/codemods/run-codemod.mjs +177 -34
  213. package/assets/codemods/runner.mjs +353 -104
  214. package/assets/codemods/term-log.mjs +32 -8
  215. package/assets/codemods/term-log.test.mjs +19 -1
  216. package/assets/codemods/transform-prop.mjs +109 -0
  217. package/assets/codemods/transform-prop.test.mjs +95 -0
  218. package/assets/codemods/transforms/v0.0.14/__tests__/rename-status-variants.test.mjs +86 -165
  219. package/assets/codemods/transforms/v0.0.14/rename-status-variants.mjs +72 -210
  220. package/assets/codemods/transforms/v0.1.8/__tests__/rename-avatar-size-scale.test.mjs +83 -115
  221. package/assets/codemods/transforms/v0.1.8/rename-avatar-size-scale.mjs +57 -186
  222. package/assets/codemods/transforms/v0.3.0/__tests__/unwrap-authoring-factories.test.mjs +27 -5
  223. package/assets/codemods/transforms/v0.3.0/unwrap-authoring-factories.mjs +20 -5
  224. package/assets/codemods/transforms/v0.6.0/__tests__/next-codemods.test.mjs +47 -0
  225. package/assets/codemods/transforms/v0.6.0/rename-resizable-pixel-bounds.mjs +57 -10
  226. package/assets/codemods/transforms/v0.6.4/__tests__/migrate-native-picker-to-presentation.test.mjs +63 -0
  227. package/assets/codemods/transforms/v0.6.4/__tests__/migrate-theme-catalog-to-descriptors.test.mjs +220 -0
  228. package/assets/codemods/transforms/v0.6.4/index.mjs +31 -0
  229. package/assets/codemods/transforms/v0.6.4/migrate-native-picker-to-presentation.mjs +148 -0
  230. package/assets/codemods/transforms/v0.6.4/migrate-theme-catalog-to-descriptors.mjs +141 -0
  231. package/assets/docs/README.md +9 -0
  232. package/assets/docs/authoring.doc.mjs +14 -0
  233. package/assets/docs/getting-started.doc.mjs +2 -2
  234. package/assets/docs/internationalization.doc.mjs +7 -5
  235. package/assets/docs/layout.doc.dense.mjs +2 -2
  236. package/assets/docs/layout.doc.mjs +1 -1
  237. package/assets/docs/principles.doc.mjs +6 -6
  238. package/assets/docs/styling-libraries.doc.mjs +4 -4
  239. package/assets/docs/styling.doc.mjs +4 -4
  240. package/assets/docs/theme.doc.mjs +5 -5
  241. package/assets/docs/tokens.doc.mjs +1 -1
  242. package/assets/docs/tree/api.doc.mjs +30 -0
  243. package/assets/docs/tree/cli.doc.mjs +23 -0
  244. package/assets/docs/tree/commands.doc.mjs +25 -0
  245. package/assets/docs/tree/component-lookups.doc.mjs +149 -0
  246. package/assets/docs/{cli-integrations.doc.mjs → tree/integrations.doc.mjs} +144 -26
  247. package/assets/docs/tree/integrations.test.mjs +62 -0
  248. package/assets/docs/tree/writing-docs.doc.mjs +286 -0
  249. package/assets/docs/working-with-ai.doc.mjs +4 -4
  250. package/assets/templates/blocks/components/CheckboxList/CheckboxListSelectAllPattern.doc.mjs +1 -1
  251. package/assets/templates/blocks/components/CheckboxList/CheckboxListSelectAllPattern.tsx +1 -3
  252. package/assets/templates/blocks/components/DateInput/DateInputDateRange.tsx +1 -1
  253. package/assets/templates/blocks/components/InternationalizationProvider/InternationalizationProvider01ShippedLocale.tsx +1 -1
  254. package/assets/templates/blocks/components/Item/ItemDocumentTabs.doc.mjs +14 -0
  255. package/assets/templates/blocks/components/Item/ItemDocumentTabs.tsx +100 -0
  256. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaInlineRail.doc.mjs +14 -0
  257. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaInlineRail.tsx +61 -0
  258. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaOverscrollChaining.doc.mjs +14 -0
  259. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaOverscrollChaining.tsx +126 -0
  260. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaShowcase.doc.mjs +15 -0
  261. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaShowcase.tsx +86 -0
  262. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyGroupHeaders.doc.mjs +14 -0
  263. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyGroupHeaders.tsx +99 -0
  264. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyPassthrough.doc.mjs +14 -0
  265. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyPassthrough.tsx +122 -0
  266. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaTwoAxisBoard.doc.mjs +14 -0
  267. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaTwoAxisBoard.tsx +95 -0
  268. package/assets/templates/blocks/components/Table/TableBulkActionsTable.doc.mjs +14 -0
  269. package/assets/templates/blocks/components/Table/TableBulkActionsTable.tsx +71 -0
  270. package/assets/templates/blocks/components/Table/TableFloatingBulkActionsTable.doc.mjs +19 -0
  271. package/assets/templates/blocks/components/Table/TableFloatingBulkActionsTable.tsx +139 -0
  272. package/assets/templates/blocks/components/TimeInput/TimeInputConstrained.tsx +1 -0
  273. package/assets/templates/blocks/components/Timer/TimerFormats.doc.mjs +14 -0
  274. package/assets/templates/blocks/components/Timer/TimerFormats.tsx +34 -0
  275. package/assets/templates/blocks/components/Timer/TimerInline.doc.mjs +14 -0
  276. package/assets/templates/blocks/components/Timer/TimerInline.tsx +14 -0
  277. package/assets/templates/blocks/components/Timer/TimerShowcase.doc.mjs +13 -0
  278. package/assets/templates/blocks/components/Timer/TimerShowcase.tsx +47 -0
  279. package/assets/templates/blocks/components/Timer/TimerTypography.doc.mjs +14 -0
  280. package/assets/templates/blocks/components/Timer/TimerTypography.tsx +31 -0
  281. package/assets/templates/blocks/components/Toolbar/ToolbarTableFilter.doc.mjs +19 -3
  282. package/assets/templates/blocks/components/Toolbar/ToolbarTableFilter.tsx +383 -65
  283. package/assets/templates/pages/table-tree/page.tsx +1704 -0
  284. package/assets/templates/pages/table-tree/template.doc.mjs +12 -0
  285. package/assets/templates/themes/butter/butterTheme.doc.mjs +11 -0
  286. package/assets/templates/themes/chocolate/chocolateTheme.doc.mjs +11 -0
  287. package/assets/templates/themes/gothic/gothicTheme.doc.mjs +11 -0
  288. package/assets/templates/themes/matcha/matchaTheme.doc.mjs +11 -0
  289. package/assets/templates/themes/neutral/neutralTheme.doc.mjs +11 -0
  290. package/assets/templates/themes/stone/stoneTheme.doc.mjs +11 -0
  291. package/assets/templates/themes/y2k/y2kTheme.doc.mjs +11 -0
  292. package/authoring/_shared/contract.ts +22 -0
  293. package/authoring/codemod/codemod.doc.mjs +7 -2
  294. package/authoring/codemod/parse.d.mts +8 -8
  295. package/authoring/codemod/parse.mjs +8 -6
  296. package/authoring/codemod/type.ts +12 -0
  297. package/authoring/config/config.doc.mjs +11 -3
  298. package/authoring/config/debug-composition.test.mjs +92 -0
  299. package/authoring/config/parse.d.mts +15 -13
  300. package/authoring/config/parse.mjs +27 -8
  301. package/authoring/config/parse.test.mjs +8 -0
  302. package/authoring/config/type.ts +31 -8
  303. package/authoring/debug/debug.doc.d.mts +11 -0
  304. package/authoring/debug/debug.doc.mjs +182 -0
  305. package/authoring/debug/parse.d.mts +8 -8
  306. package/authoring/debug/parse.mjs +3 -3
  307. package/authoring/discover/discover.doc.d.mts +13 -0
  308. package/authoring/discover/discover.doc.mjs +138 -0
  309. package/authoring/discover/parse.d.mts +24 -0
  310. package/authoring/discover/parse.mjs +128 -0
  311. package/authoring/discover/parse.test.mjs +124 -0
  312. package/authoring/discover/type.ts +87 -0
  313. package/authoring/doctypes/_schema.d.mts +790 -23
  314. package/authoring/doctypes/_schema.mjs +543 -39
  315. package/authoring/doctypes/base/graph-fields.doc.d.mts +9 -0
  316. package/authoring/doctypes/base/graph-fields.doc.mjs +64 -0
  317. package/authoring/doctypes/base/type.ts +41 -0
  318. package/authoring/doctypes/command/command.doc.mjs +5 -4
  319. package/authoring/doctypes/command/parse.d.mts +2 -2
  320. package/authoring/doctypes/command/parse.mjs +1 -1
  321. package/authoring/doctypes/command/type.ts +6 -5
  322. package/authoring/doctypes/component/component.doc.mjs +6 -3
  323. package/authoring/doctypes/component/parse.d.mts +2 -2
  324. package/authoring/doctypes/component/parse.mjs +1 -1
  325. package/authoring/doctypes/component/type.ts +6 -5
  326. package/authoring/doctypes/doctypes-new.test.mjs +48 -6
  327. package/authoring/doctypes/enum/enum.doc.mjs +1 -1
  328. package/authoring/doctypes/enum/parse.d.mts +2 -2
  329. package/authoring/doctypes/enum/parse.mjs +1 -1
  330. package/authoring/doctypes/enum/type.ts +4 -2
  331. package/authoring/doctypes/function/function.doc.mjs +7 -2
  332. package/authoring/doctypes/function/parse.d.mts +2 -2
  333. package/authoring/doctypes/function/parse.mjs +1 -1
  334. package/authoring/doctypes/function/type.ts +9 -4
  335. package/authoring/doctypes/hook/hook.doc.mjs +4 -0
  336. package/authoring/doctypes/hook/parse.d.mts +2 -2
  337. package/authoring/doctypes/hook/parse.mjs +1 -1
  338. package/authoring/doctypes/hook/type.ts +5 -4
  339. package/authoring/doctypes/legacy.d.mts +8 -6
  340. package/authoring/doctypes/legacy.mjs +5 -4
  341. package/authoring/doctypes/load-contract.test.mjs +233 -0
  342. package/authoring/doctypes/namespace/namespace.doc.d.mts +9 -0
  343. package/authoring/doctypes/namespace/namespace.doc.mjs +128 -0
  344. package/authoring/doctypes/namespace/parse.d.mts +12 -0
  345. package/authoring/doctypes/namespace/parse.mjs +25 -0
  346. package/authoring/doctypes/namespace/parse.test.mjs +163 -0
  347. package/authoring/doctypes/namespace/type.ts +74 -0
  348. package/authoring/doctypes/parse.d.mts +22 -18
  349. package/authoring/doctypes/parse.mjs +22 -11
  350. package/authoring/doctypes/parse.test.mjs +77 -3
  351. package/authoring/doctypes/reference/parse.d.mts +2 -2
  352. package/authoring/doctypes/reference/parse.mjs +8 -5
  353. package/authoring/doctypes/reference/reference.doc.mjs +48 -6
  354. package/authoring/doctypes/reference/type.ts +70 -7
  355. package/authoring/doctypes/schema/parse.d.mts +2 -2
  356. package/authoring/doctypes/schema/parse.mjs +1 -1
  357. package/authoring/doctypes/schema/schema.doc.mjs +1 -1
  358. package/authoring/doctypes/schema/type.ts +4 -4
  359. package/authoring/doctypes/template/parse.d.mts +94 -1
  360. package/authoring/doctypes/template/parse.mjs +40 -2
  361. package/authoring/doctypes/template/parse.test.mjs +26 -2
  362. package/authoring/doctypes/template/template.doc.mjs +13 -3
  363. package/authoring/doctypes/template/type.ts +13 -2
  364. package/authoring/doctypes/theme/parse.d.mts +35 -0
  365. package/authoring/doctypes/theme/parse.mjs +76 -0
  366. package/authoring/doctypes/theme/theme.doc.d.mts +9 -0
  367. package/authoring/doctypes/theme/theme.doc.mjs +79 -0
  368. package/authoring/doctypes/theme/type.ts +42 -0
  369. package/authoring/doctypes/types.ts +12 -10
  370. package/authoring/gap-report/gap-report.doc.d.mts +12 -0
  371. package/authoring/gap-report/gap-report.doc.mjs +183 -0
  372. package/authoring/gap-report/parse.d.mts +10 -10
  373. package/authoring/gap-report/parse.mjs +6 -6
  374. package/authoring/gap-report/type.ts +1 -1
  375. package/authoring/identity/identity.doc.d.mts +9 -0
  376. package/authoring/identity/identity.doc.mjs +61 -0
  377. package/authoring/identity/type.ts +132 -0
  378. package/authoring/index.d.mts +3 -0
  379. package/authoring/index.d.ts +62 -17
  380. package/authoring/index.mjs +4 -1
  381. package/authoring/integration/integration.doc.mjs +15 -8
  382. package/authoring/integration/parse.d.mts +2 -2
  383. package/authoring/integration/parse.mjs +1 -1
  384. package/authoring/integration/parse.test.mjs +10 -1
  385. package/authoring/integration/schema.d.mts +6 -4
  386. package/authoring/integration/schema.mjs +9 -3
  387. package/authoring/integration/type.ts +19 -8
  388. package/authoring/shadcn/receipt.d.mts +6 -6
  389. package/clients/cli/__tests__/cliManifest.test.ts +27 -29
  390. package/clients/cli/command-load-failure.test.mjs +83 -0
  391. package/clients/cli/command-result-coverage.test.mjs +7 -7
  392. package/clients/cli/commands/blog.doc.mjs +1 -1
  393. package/clients/cli/commands/blog.mjs +23 -8
  394. package/clients/cli/commands/blog.test.mjs +42 -1
  395. package/clients/cli/commands/build-theme.adaptations.test.mjs +100 -1
  396. package/clients/cli/commands/build-theme.ascii-output.test.mjs +161 -0
  397. package/clients/cli/commands/build-theme.flag-docs.test.mjs +110 -0
  398. package/clients/cli/commands/build-theme.mjs +16 -50
  399. package/clients/cli/commands/build-theme.path-safety.test.mjs +86 -1
  400. package/clients/cli/commands/build-theme.variants.test.mjs +76 -0
  401. package/clients/cli/commands/build.doc.mjs +16 -8
  402. package/clients/cli/commands/build.exit-codes-doc.test.mjs +50 -0
  403. package/clients/cli/commands/build.mjs +137 -114
  404. package/clients/cli/commands/build.playbook.test.mjs +75 -0
  405. package/clients/cli/commands/build.text-fields.test.mjs +81 -0
  406. package/clients/cli/commands/component/index.mjs +153 -61
  407. package/clients/cli/commands/component-batch.test.mjs +341 -0
  408. package/clients/cli/commands/component-ownership.test.mjs +92 -3
  409. package/clients/cli/commands/component-package.test.mjs +46 -0
  410. package/clients/cli/commands/component-resolution.test.mjs +21 -0
  411. package/clients/cli/commands/component.doc.mjs +24 -7
  412. package/clients/cli/commands/component.test.mjs +19 -0
  413. package/clients/cli/commands/debug-result-summary.test.mjs +2 -2
  414. package/clients/cli/commands/detail-levels.test.mjs +2 -2
  415. package/clients/cli/commands/discover.broken-integration.test.mjs +52 -5
  416. package/clients/cli/commands/discover.components-flag.test.mjs +97 -0
  417. package/clients/cli/commands/discover.doc.mjs +55 -9
  418. package/clients/cli/commands/discover.mjs +393 -118
  419. package/clients/cli/commands/discover.sources.test.mjs +267 -0
  420. package/clients/cli/commands/discover.text-projection.test.mjs +103 -0
  421. package/clients/cli/commands/docs.doc.mjs +28 -6
  422. package/clients/cli/commands/docs.mjs +240 -26
  423. package/clients/cli/commands/docs.test.mjs +222 -1
  424. package/clients/cli/commands/doctor-integration-components.doc.mjs +1 -1
  425. package/clients/cli/commands/doctor-integration-docs.doc.mjs +3 -3
  426. package/clients/cli/commands/doctor-integration-templates.doc.mjs +15 -8
  427. package/clients/cli/commands/doctor-integration-validate.doc.mjs +1 -1
  428. package/clients/cli/commands/doctor-integration.doc.mjs +1 -1
  429. package/clients/cli/commands/doctor-integration.package-json.test.mjs +53 -0
  430. package/clients/cli/commands/doctor-integration.test.mjs +90 -8
  431. package/clients/cli/commands/doctor.doc.mjs +1 -1
  432. package/clients/cli/commands/doctor.mjs +59 -32
  433. package/clients/cli/commands/doctor.test.mjs +42 -0
  434. package/clients/cli/commands/gap-report.doc.mjs +17 -6
  435. package/clients/cli/commands/gap-report.test.mjs +72 -0
  436. package/clients/cli/commands/hook/index.mjs +7 -17
  437. package/clients/cli/commands/hook.doc.mjs +1 -1
  438. package/clients/cli/commands/hook.text-projection.test.mjs +45 -0
  439. package/clients/cli/commands/init.doc.mjs +20 -9
  440. package/clients/cli/commands/init.flag-help.test.mjs +153 -0
  441. package/clients/cli/commands/integration-add.controls.test.mjs +132 -0
  442. package/clients/cli/commands/integration-add.doc.mjs +32 -6
  443. package/clients/cli/commands/integration-authoring.test.mjs +13 -9
  444. package/clients/cli/commands/integration-pack.doc.mjs +1 -1
  445. package/clients/cli/commands/integration-real-world.test.mjs +3 -9
  446. package/clients/cli/commands/integration.doc.mjs +1 -1
  447. package/clients/cli/commands/integration.mjs +1 -0
  448. package/clients/cli/commands/interactive-guard.test.mjs +101 -24
  449. package/clients/cli/commands/json-contract.test.mjs +33 -0
  450. package/clients/cli/commands/manifest.doc.mjs +1 -1
  451. package/clients/cli/commands/no-prompt-wording.test.mjs +94 -0
  452. package/clients/cli/commands/search.doc.mjs +7 -4
  453. package/clients/cli/commands/search.mjs +28 -9
  454. package/clients/cli/commands/search.test.mjs +75 -0
  455. package/clients/cli/commands/setup-nudge.test.mjs +6 -0
  456. package/clients/cli/commands/swizzle.doc.mjs +3 -2
  457. package/clients/cli/commands/swizzle.path-safety.test.mjs +42 -0
  458. package/clients/cli/commands/template.doc.mjs +52 -13
  459. package/clients/cli/commands/template.flag-help.test.mjs +117 -0
  460. package/clients/cli/commands/template.mjs +4 -91
  461. package/clients/cli/commands/template.path-help.test.mjs +40 -0
  462. package/clients/cli/commands/text-json-parity.test.mjs +702 -0
  463. package/clients/cli/commands/theme-add.doc.mjs +4 -3
  464. package/clients/cli/commands/theme-build.doc.mjs +8 -7
  465. package/clients/cli/commands/theme-list.doc.mjs +2 -2
  466. package/clients/cli/commands/theme-palette-generate.doc.mjs +9 -5
  467. package/clients/cli/commands/theme-palette-generate.test.mjs +19 -0
  468. package/clients/cli/commands/theme-palette.doc.mjs +1 -1
  469. package/clients/cli/commands/theme-targets.behavior.test.mjs +4 -3
  470. package/clients/cli/commands/theme-targets.doc.mjs +1 -1
  471. package/clients/cli/commands/theme-template.behavior.test.mjs +12 -0
  472. package/clients/cli/commands/theme-template.doc.mjs +2 -2
  473. package/clients/cli/commands/theme.doc.mjs +1 -1
  474. package/clients/cli/commands/upgrade.ascii-output.test.mjs +87 -0
  475. package/clients/cli/commands/upgrade.doc.mjs +22 -10
  476. package/clients/cli/commands/upgrade.file-protection.test.mjs +228 -0
  477. package/clients/cli/commands/upgrade.flag-help.test.mjs +188 -0
  478. package/clients/cli/commands/upgrade.hook-output.test.mjs +88 -0
  479. package/clients/cli/commands/upgrade.mjs +29 -7
  480. package/clients/cli/formatters/index.mjs +164 -1
  481. package/clients/cli/formatters/index.test.mjs +97 -0
  482. package/clients/cli/index.mjs +21 -34
  483. package/clients/cli/latest-version-env.test.mjs +50 -0
  484. package/clients/cli/lib/cli-error.test.mjs +7 -0
  485. package/clients/cli/lib/component-format.mjs +9 -9
  486. package/clients/cli/lib/component-format.test.mjs +1 -1
  487. package/clients/cli/lib/define-command.mjs +32 -6
  488. package/clients/cli/lib/doc-text-ascii.test.mjs +82 -0
  489. package/clients/cli/lib/exit-codes.test.mjs +90 -0
  490. package/clients/cli/lib/hook-format.mjs +19 -10
  491. package/clients/cli/lib/json-shim.mjs +62 -16
  492. package/clients/cli/lib/json-shim.test.mjs +69 -0
  493. package/clients/cli/lib/manifest.d.ts +2 -0
  494. package/clients/cli/lib/manifest.mjs +39 -10
  495. package/clients/cli/lib/manifest.test.mjs +22 -2
  496. package/clients/cli/lib/parse-error-format.test.mjs +81 -0
  497. package/foundation/agent-docs/agent-docs.d.mts +7 -2
  498. package/foundation/agent-docs/agent-docs.mjs +82 -12
  499. package/foundation/agent-docs/agent-docs.path-safety.test.mjs +266 -4
  500. package/foundation/agent-docs/agent-docs.test.mjs +19 -1
  501. package/foundation/config/integration-debug.test.mjs +28 -3
  502. package/foundation/config/project-themes.test.mjs +11 -19
  503. package/foundation/config/project.d.mts +20 -11
  504. package/foundation/config/project.mjs +263 -91
  505. package/foundation/config/project.test.mjs +270 -21
  506. package/foundation/discovery/authoring-self-docs.d.mts +87 -0
  507. package/foundation/discovery/authoring-self-docs.mjs +237 -0
  508. package/foundation/discovery/authoring-self-docs.test.mjs +170 -0
  509. package/foundation/discovery/authoring-surface.d.mts +74 -0
  510. package/foundation/discovery/authoring-surface.mjs +525 -0
  511. package/foundation/discovery/authoring-surface.test.mjs +392 -0
  512. package/foundation/discovery/cli-self-docs.d.mts +119 -0
  513. package/foundation/discovery/cli-self-docs.mjs +490 -0
  514. package/foundation/discovery/cli-self-docs.test.mjs +375 -0
  515. package/foundation/discovery/component-discovery.d.mts +39 -1
  516. package/foundation/discovery/component-discovery.mjs +50 -1
  517. package/foundation/discovery/component-loader.d.mts +35 -38
  518. package/foundation/discovery/component-loader.mjs +53 -222
  519. package/foundation/discovery/docs-discovery.d.mts +119 -11
  520. package/foundation/discovery/docs-discovery.mjs +423 -108
  521. package/foundation/discovery/docs-discovery.test.mjs +365 -20
  522. package/foundation/discovery/docs-output-budget.d.mts +28 -0
  523. package/foundation/discovery/docs-output-budget.mjs +50 -0
  524. package/foundation/discovery/docs-section-key.d.mts +116 -0
  525. package/foundation/discovery/docs-section-key.mjs +322 -0
  526. package/foundation/discovery/docs-section-key.test.mjs +246 -0
  527. package/foundation/discovery/template-adapter.d.mts +113 -11
  528. package/foundation/discovery/template-adapter.fixture-refs.test.mjs +248 -0
  529. package/foundation/discovery/template-adapter.integration-isolation.test.mjs +94 -0
  530. package/foundation/discovery/template-adapter.mjs +775 -84
  531. package/foundation/discovery/template-adapter.test.mjs +57 -0
  532. package/foundation/discovery/template-conflict-release.d.mts +13 -0
  533. package/foundation/discovery/template-conflict-release.mjs +40 -0
  534. package/foundation/discovery/template-conflict-release.test.mjs +40 -0
  535. package/foundation/discovery/theme-discovery.d.mts +67 -7
  536. package/foundation/discovery/theme-discovery.mjs +916 -186
  537. package/foundation/discovery/theme-discovery.test.mjs +613 -219
  538. package/foundation/discovery/theming-targets.test.mjs +4 -0
  539. package/foundation/doc-compiler/bundle.d.mts +47 -0
  540. package/foundation/doc-compiler/bundle.mjs +278 -0
  541. package/foundation/doc-compiler/bundle.test.mjs +266 -0
  542. package/foundation/doc-compiler/compile.d.mts +343 -0
  543. package/foundation/doc-compiler/compile.mjs +558 -0
  544. package/foundation/doc-compiler/diagnostics.d.mts +126 -0
  545. package/foundation/doc-compiler/diagnostics.mjs +305 -0
  546. package/foundation/doc-compiler/doc-compiler.test.mjs +714 -0
  547. package/foundation/doc-compiler/doc-loads.test.mjs +1630 -0
  548. package/foundation/doc-compiler/import.d.mts +24 -0
  549. package/foundation/doc-compiler/import.mjs +59 -0
  550. package/foundation/doc-compiler/inputs.d.mts +102 -0
  551. package/foundation/doc-compiler/inputs.mjs +291 -0
  552. package/foundation/doc-compiler/inputs.test.mjs +299 -0
  553. package/foundation/doc-compiler/ir.d.mts +22 -0
  554. package/foundation/doc-compiler/ir.mjs +471 -0
  555. package/foundation/doc-compiler/lenses.d.mts +36 -0
  556. package/foundation/doc-compiler/lenses.mjs +173 -0
  557. package/foundation/doc-compiler/links.d.mts +162 -0
  558. package/foundation/doc-compiler/links.mjs +294 -0
  559. package/foundation/doc-compiler/links.test.mjs +192 -0
  560. package/foundation/doc-compiler/lower-doc.test.mjs +495 -0
  561. package/foundation/doc-compiler/overlays.d.mts +37 -0
  562. package/foundation/doc-compiler/overlays.mjs +206 -0
  563. package/foundation/doc-compiler/parse-readable.d.mts +9 -0
  564. package/foundation/doc-compiler/parse-readable.mjs +29 -0
  565. package/foundation/doc-compiler/read.d.mts +127 -0
  566. package/foundation/doc-compiler/read.mjs +325 -0
  567. package/foundation/doc-compiler/read.test.mjs +313 -0
  568. package/foundation/doc-compiler/source.d.mts +33 -0
  569. package/foundation/doc-compiler/source.mjs +128 -0
  570. package/foundation/doc-compiler/tree.d.mts +288 -0
  571. package/foundation/doc-compiler/tree.mjs +876 -0
  572. package/foundation/doc-compiler/tree.test.mjs +606 -0
  573. package/foundation/fs/file-protection.d.mts +33 -0
  574. package/foundation/fs/file-protection.mjs +825 -0
  575. package/foundation/fs/file-protection.test.mjs +250 -0
  576. package/foundation/fs/module-loader.d.mts +1 -0
  577. package/foundation/fs/module-loader.mjs +50 -1
  578. package/foundation/fs/module-loader.stdout.test.mjs +332 -0
  579. package/foundation/fs/path-safety.d.mts +3 -2
  580. package/foundation/fs/path-safety.mjs +49 -19
  581. package/foundation/fs/path-safety.test.mjs +50 -0
  582. package/foundation/fs/publish-file-hardlink-unavailable.test.mjs +129 -94
  583. package/foundation/identity/provider-identity.d.mts +90 -0
  584. package/foundation/identity/provider-identity.mjs +320 -0
  585. package/foundation/identity/provider-identity.test.mjs +254 -0
  586. package/foundation/identity/providers.d.mts +7 -0
  587. package/foundation/identity/providers.mjs +16 -0
  588. package/foundation/integrations/autolink.d.mts +58 -1
  589. package/foundation/integrations/autolink.mjs +143 -45
  590. package/foundation/integrations/autolink.test.mjs +1 -1
  591. package/foundation/integrations/cli-requirement.d.mts +45 -0
  592. package/foundation/integrations/cli-requirement.mjs +154 -0
  593. package/foundation/integrations/cli-requirement.test.mjs +84 -0
  594. package/foundation/integrations/contribution-fixes.d.mts +145 -0
  595. package/foundation/integrations/contribution-fixes.mjs +1284 -0
  596. package/foundation/integrations/contribution-inventory.d.mts +3 -2
  597. package/foundation/integrations/contribution-inventory.mjs +27 -24
  598. package/foundation/integrations/contribution-inventory.test.mjs +86 -27
  599. package/foundation/integrations/integration-warnings.d.mts +9 -2
  600. package/foundation/integrations/integration-warnings.mjs +52 -21
  601. package/foundation/integrations/integration-warnings.test.mjs +74 -1
  602. package/foundation/integrations/integrations.d.mts +63 -3
  603. package/foundation/integrations/integrations.mjs +122 -9
  604. package/foundation/integrations/integrations.test.mjs +415 -1
  605. package/foundation/integrations/provider-conflicts.test.mjs +125 -0
  606. package/foundation/integrations/provider-ledger.test.mjs +275 -0
  607. package/foundation/integrations/provider-resolution.d.mts +152 -0
  608. package/foundation/integrations/provider-resolution.mjs +576 -0
  609. package/foundation/integrations/provider-resolution.test.mjs +369 -0
  610. package/foundation/integrations/theme-descriptor.d.mts +8 -0
  611. package/foundation/integrations/theme-descriptor.mjs +44 -0
  612. package/foundation/integrations/validate-contributions.d.mts +2 -0
  613. package/foundation/integrations/validate-contributions.mjs +131 -29
  614. package/foundation/response/base.d.ts +8 -4
  615. package/foundation/response/batch.type.d.mts +33 -0
  616. package/foundation/response/batch.type.mjs +34 -0
  617. package/foundation/response/error-codes.d.mts +3 -1
  618. package/foundation/response/error-codes.d.ts +2 -0
  619. package/foundation/response/error-codes.doc.mjs +13 -4
  620. package/foundation/response/error-codes.mjs +8 -2
  621. package/foundation/response/error-codes.test.mjs +137 -10
  622. package/foundation/response/json-contract.test.mjs +57 -17
  623. package/foundation/response/json.d.mts +4 -2
  624. package/foundation/response/json.mjs +8 -10
  625. package/foundation/response/response-types.doc.d.mts +5 -1
  626. package/foundation/response/response-types.doc.mjs +46 -38
  627. package/foundation/response/response-types.doc.test.mjs +181 -0
  628. package/foundation/response/response.doc.mjs +1 -1
  629. package/foundation/text/string-utils.d.mts +8 -0
  630. package/foundation/text/string-utils.mjs +40 -10
  631. package/foundation/xle/browser.d.mts +3 -3
  632. package/foundation/xle/browser.mjs +3 -3
  633. package/foundation/xle/expand.d.mts +2 -0
  634. package/foundation/xle/expand.mjs +6 -5
  635. package/foundation/xle/expand.test.mjs +54 -0
  636. package/foundation/xle/parse.mjs +1 -1
  637. package/foundation/xle/print.mjs +2 -2
  638. package/foundation/xle/splice.mjs +1 -1
  639. package/foundation/xle/xle.test.mjs +13 -0
  640. package/package.json +10 -11
  641. package/api/layout/_adapter.d.mts +0 -34
  642. package/api/layout/_adapter.mjs +0 -133
  643. package/api/layout/check/check.d.mts +0 -16
  644. package/api/layout/check/check.mjs +0 -40
  645. package/api/layout/expand/expand.d.mts +0 -22
  646. package/api/layout/expand/expand.mjs +0 -153
  647. package/api/layout/grammar/grammar.d.mts +0 -13
  648. package/api/layout/grammar/grammar.mjs +0 -86
  649. package/api/layout/layout.d.mts +0 -6
  650. package/api/layout/layout.mjs +0 -17
  651. package/api/layout/layout.test.mjs +0 -297
  652. package/api/layout/layout.type.d.mts +0 -89
  653. package/api/layout/layout.type.mjs +0 -103
  654. package/api/layout/layoutCheck.doc.d.mts +0 -11
  655. package/api/layout/layoutCheck.doc.mjs +0 -84
  656. package/api/layout/layoutExpand.doc.d.mts +0 -11
  657. package/api/layout/layoutExpand.doc.mjs +0 -106
  658. package/api/layout/layoutGrammar.doc.d.mts +0 -11
  659. package/api/layout/layoutGrammar.doc.mjs +0 -56
  660. package/assets/templates/themes/manifest.json +0 -95
  661. package/clients/cli/commands/layout-check.doc.mjs +0 -54
  662. package/clients/cli/commands/layout-expand.doc.mjs +0 -66
  663. package/clients/cli/commands/layout-grammar.doc.mjs +0 -30
  664. package/clients/cli/commands/layout.doc.mjs +0 -34
  665. package/clients/cli/commands/layout.error-codes.test.mjs +0 -66
  666. package/clients/cli/commands/layout.exit-parity.test.mjs +0 -41
  667. package/clients/cli/commands/layout.mjs +0 -263
  668. package/clients/cli/lib/update-check.mjs +0 -83
  669. package/clients/cli/lib/update-check.test.mjs +0 -137
  670. package/clients/cli/update-hint-commands.test.mjs +0 -54
@@ -17,21 +17,31 @@
17
17
  * component fuzzy resolver in lib/string-utils.mjs:
18
18
  *
19
19
  * 100 exact name match
20
+ * 95 name is the term's plural or stem form ("buttons" -> Button)
20
21
  * 90 exact keyword match
21
- * 80 name Levenshtein distance 1
22
- * 70 keyword substring / distance 1
23
- * 60 name substring (>=4 chars, >=50% coverage)
22
+ * 88 keyword is the term's plural or stem form
23
+ * 80 name Levenshtein distance 1 (one-word lookups, words of 5+ letters)
24
+ * 70 keyword word prefix / distance 1 (distance: one-word lookups, 5+ letters)
25
+ * 60 name word prefix (>=4 chars, >=50% coverage)
24
26
  * 60 exact weak-keyword match
25
27
  * 50 description / prose mentions the term
26
28
  * 45 usage guidance mentions the term
27
- * 40 name Levenshtein distance 2
28
- * 40 weak-keyword substring
29
- * 30 keyword Levenshtein distance 2
30
- * 20 name Levenshtein distance 3
29
+ * 40 name Levenshtein distance 2 (one-word lookups, 8+ letters)
30
+ * 40 weak-keyword word prefix
31
+ * 30 keyword Levenshtein distance 2 (one-word lookups, 8+ letters)
32
+ * 20 name Levenshtein distance 3 (one-word lookups, 11+ letters)
31
33
  *
32
34
  * Name + keyword signals always outweigh description/prose, so an exact match
33
35
  * sorts above an incidental mention.
34
36
  *
37
+ * A term matches inside a name or keyword only at the start of one of its
38
+ * words: "dash" finds "dashboard" and "input" finds "TextInput", but "file"
39
+ * does not find "profile". Edit distance is typo tolerance, so it applies only
40
+ * to a one-word lookup, where a typo is the likely explanation, and only to
41
+ * words long enough that one edit rarely makes another real word. In a
42
+ * sentence, a near miss is usually a different word: "site" is not "side",
43
+ * "cable" is not "table".
44
+ *
35
45
  * Description and guidance are separate tiers on purpose. A component's own
36
46
  * one-line description saying "notification" is a claim about what it IS; the
37
47
  * same word inside another component's best-practice advice is a passing
@@ -50,11 +60,11 @@
50
60
  * queries they have nothing to do with.
51
61
  */
52
62
 
53
- import {pathToFileURL} from 'node:url';
63
+ import {readDocView} from '../../foundation/doc-compiler/read.mjs';
54
64
  import {findCoreDir} from '../../foundation/fs/paths.mjs';
55
65
  import {
56
66
  discoverComponents,
57
- discoverIntegrationComponents,
67
+ discoverValidIntegrationComponents,
58
68
  findComponentReadme,
59
69
  resolveImportPath,
60
70
  resolveIntegrationImportPath,
@@ -66,7 +76,20 @@ import {
66
76
  import {loadIntegrationsSafely} from '../component/_adapter.mjs';
67
77
  import {levenshteinDistance} from '../../foundation/text/string-utils.mjs';
68
78
  import {discoverTemplates, extractComponents} from '../template/template.mjs';
69
- import {loadDocsCatalog, loadTopicDoc} from '../docs/_adapter.mjs';
79
+ import {templateLookupIds} from '../../foundation/discovery/template-adapter.mjs';
80
+ import {
81
+ guideEntry,
82
+ loadDocsCatalog,
83
+ lowerTopic,
84
+ projectTree,
85
+ holdsOwnName,
86
+ } from '../docs/_adapter.mjs';
87
+ import {unlinkText} from '../../foundation/doc-compiler/links.mjs';
88
+ import {nodeView} from '../docs/node/node.mjs';
89
+ import {
90
+ sectionKey,
91
+ sectionSummary,
92
+ } from '../../foundation/discovery/docs-section-key.mjs';
70
93
  import {AstryxError} from '../error.mjs';
71
94
  import {ERROR_CODES} from '../../foundation/response/error-codes.mjs';
72
95
  import {setResultCoverage} from './coverage.mjs';
@@ -84,17 +107,28 @@ import {setResultCoverage} from './coverage.mjs';
84
107
  * @property {string[]} [guidance]
85
108
  * @property {string} [_import]
86
109
  * @property {string} [_title]
110
+ * @property {string} [_topic] - A doc result's topic or docs-tree route.
111
+ * @property {string} [_section] - A doc result's section key, when it is one section.
112
+ * @property {string} [_command] - The command that reads exactly this doc part.
113
+ * @property {string} [_parent] - The command that opens the level above a doc
114
+ * part: its topic's section list, or the docs-tree namespace it sits in.
115
+ * @property {string} [_package] - The npm package that authored a docs-tree
116
+ * doc part. Flat topics carry none: an extension's sections can come from
117
+ * another package.
87
118
  * @property {string} [_displayName]
88
119
  * @property {'page'|'block'} [_kind]
120
+ * @property {string} [_resultName]
121
+ * @property {string} [_commandName]
89
122
  */
90
123
 
91
124
  /**
92
125
  * Synonym / intent map: product-language terms an agent is likely to type,
93
126
  * expanded to the catalog's vocabulary so oblique queries still rank. Keys and
94
127
  * values are matched bidirectionally (typing any value also pulls in the key
95
- * and its siblings). Lowercase, single words or short phrases.
128
+ * and its siblings). Lowercase, single words or short phrases. Exported for
129
+ * `build`, whose page ranker expands a query with the same vocabulary.
96
130
  */
97
- const SYNONYMS = {
131
+ export const SYNONYMS = {
98
132
  dashboard: [
99
133
  'overview',
100
134
  'analytics',
@@ -166,14 +200,90 @@ export function stem(w) {
166
200
  return s;
167
201
  }
168
202
 
203
+ /**
204
+ * The forms of a word that count as the same word: itself, its stem, and its
205
+ * singular when it ends in a plural suffix — so "tables" is "table",
206
+ * "statuses" is "status", and "filtering" is "filter".
207
+ * @param {string} w - Lowercase word.
208
+ * @returns {Set<string>}
209
+ */
210
+ function wordForms(w) {
211
+ const forms = new Set([w, stem(w)]);
212
+ if (w.length > 3 && w.endsWith('s')) forms.add(w.slice(0, -1));
213
+ if (w.length > 4 && w.endsWith('es')) forms.add(w.slice(0, -2));
214
+ if (w.length > 4 && w.endsWith('ies')) forms.add(w.slice(0, -3) + 'y');
215
+ return forms;
216
+ }
217
+
218
+ /**
219
+ * Whether two lowercase words are the same word, up to plural and stem form.
220
+ * @param {string} a
221
+ * @param {string} b
222
+ * @returns {boolean}
223
+ */
224
+ export function sameWord(a, b) {
225
+ if (a === b) return true;
226
+ const forms = wordForms(a);
227
+ for (const f of wordForms(b)) if (forms.has(f)) return true;
228
+ return false;
229
+ }
230
+
231
+ /**
232
+ * The lowercase words of a name or keyword: split at non-alphanumerics and at
233
+ * camelCase boundaries, so "TextInput" is ["text", "input"] and
234
+ * "Dashboard - Analytics" is ["dashboard", "analytics"].
235
+ * @param {string} text
236
+ * @returns {string[]}
237
+ */
238
+ function wordsOf(text) {
239
+ return String(text)
240
+ .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
241
+ .replace(/([A-Z])([A-Z][a-z])/g, '$1 $2')
242
+ .toLowerCase()
243
+ .split(/[^a-z0-9]+/)
244
+ .filter(Boolean);
245
+ }
246
+
247
+ /**
248
+ * Whether a term is found inside a name or keyword: it is one of its words, or
249
+ * the start of one (a truncation), and covers at least half of the whole
250
+ * string. Four letters minimum, so a short term never matches by accident.
251
+ * @param {string} term - Lowercase term.
252
+ * @param {string} text - The name or keyword as authored.
253
+ * @returns {boolean}
254
+ */
255
+ function startsAWordOf(term, text) {
256
+ if (term.length < 4) return false;
257
+ if (term.length / String(text).length < 0.5) return false;
258
+ return wordsOf(text).some(w => w.startsWith(term) || sameWord(term, w));
259
+ }
260
+
261
+ /**
262
+ * The fewest letters both words need before an edit distance counts as a
263
+ * typo, by distance. Below them, one edit usually makes a different word.
264
+ */
265
+ const TYPO_MIN_LENGTH = {1: 5, 2: 8, 3: 11};
266
+
267
+ /**
268
+ * @param {string} a
269
+ * @param {string} b
270
+ * @param {number} dist
271
+ */
272
+ const isTypo = (a, b, dist) =>
273
+ dist > 0 &&
274
+ dist <= 3 &&
275
+ Math.min(a.length, b.length) >=
276
+ TYPO_MIN_LENGTH[/** @type {1 | 2 | 3} */ (dist)];
277
+
169
278
  /** Valid domain filters for `--type`. */
170
279
  export const SEARCH_DOMAINS = ['component', 'hook', 'doc', 'template'];
171
280
 
172
281
  /**
173
282
  * Filler words stripped from multi-word queries so natural-language phrasing
174
283
  * ("a page where you can see business stats") ranks on its content words.
284
+ * Exported for `build`, whose page ranker strips the same words.
175
285
  */
176
- const STOPWORDS = new Set([
286
+ export const STOPWORDS = new Set([
177
287
  'a',
178
288
  'an',
179
289
  'the',
@@ -285,14 +395,15 @@ const MIN_TOKEN_SCORE = 50;
285
395
  * (synonym hits are discounted so a direct hit always wins).
286
396
  * @param {string} tok
287
397
  * @param {Candidate} candidate
398
+ * @param {{fuzzy?: boolean}} [opts]
288
399
  * @returns {{score: number, reason: string} | null}
289
400
  */
290
- function bestForToken(tok, candidate) {
291
- let best = scoreCandidate(tok, candidate);
401
+ function bestForToken(tok, candidate, opts = {}) {
402
+ let best = scoreCandidate(tok, candidate, opts);
292
403
  const syns = SYNONYM_INDEX.get(tok);
293
404
  if (syns) {
294
405
  for (const s of syns) {
295
- const h = scoreCandidate(s, candidate);
406
+ const h = scoreCandidate(s, candidate, opts);
296
407
  if (h) {
297
408
  const score = Math.round(h.score * 0.85);
298
409
  if (!best || score > best.score)
@@ -322,14 +433,17 @@ export function scoreQuery(term, tokens, candidate) {
322
433
  matched: total,
323
434
  total,
324
435
  });
325
- const full = scoreCandidate(term, candidate);
436
+ // Typo tolerance is for one-word lookups. In a multi-word query a near miss
437
+ // is usually a different word, not a typo.
438
+ const fuzzy = tokens.length <= 1;
439
+ const full = scoreCandidate(term, candidate, {fuzzy});
326
440
 
327
441
  // 0–1 content tokens: keep whole-phrase fuzzy matching (typo tolerance for
328
442
  // single words), but if stopwords left exactly one DIFFERENT token (e.g.
329
443
  // "pricing page" → "pricing"), score that token too and take the stronger.
330
444
  if (tokens.length <= 1) {
331
445
  const single =
332
- tokens.length === 1 ? bestForToken(tokens[0], candidate) : null;
446
+ tokens.length === 1 ? bestForToken(tokens[0], candidate, {fuzzy}) : null;
333
447
  if (full && (!single || full.score >= single.score)) return asFull(full);
334
448
  return single ? asFull(single) : null;
335
449
  }
@@ -356,7 +470,7 @@ export function scoreQuery(term, tokens, candidate) {
356
470
  /** @type {string[]} */
357
471
  const hitTerms = [];
358
472
  for (const tok of tokens) {
359
- const h = bestForToken(tok, candidate);
473
+ const h = bestForToken(tok, candidate, {fuzzy});
360
474
  if (h && h.score >= MIN_TOKEN_SCORE) {
361
475
  if (h.score > strongest) strongest = h.score;
362
476
  matched++;
@@ -409,6 +523,7 @@ export function scoreQuery(term, tokens, candidate) {
409
523
  * @param {string} [candidate.description]
410
524
  * @param {string[]} [candidate.prose] - Extra free-text blobs (doc section text, best practices).
411
525
  * @param {string[]} [candidate.guidance] - Usage guidance (features, best practices) — scored a tier below description.
526
+ * @param {{fuzzy?: boolean}} [opts] - `fuzzy`: allow edit-distance (typo) matches. Default true; multi-word queries pass false.
412
527
  * @returns {{score: number, reason: string} | null}
413
528
  */
414
529
  export function scoreCandidate(
@@ -421,6 +536,7 @@ export function scoreCandidate(
421
536
  prose = [],
422
537
  guidance = [],
423
538
  },
539
+ {fuzzy = true} = {},
424
540
  ) {
425
541
  let best = 0;
426
542
  let reason = '';
@@ -441,20 +557,20 @@ export function scoreCandidate(
441
557
  if (nameLower === term) {
442
558
  consider(100, 'exact name');
443
559
  } else {
444
- // Substring (both directions), min 4 chars, >=50% coverage.
445
- const shorter = term.length < nameLower.length ? term : nameLower;
446
- const longer = term.length < nameLower.length ? nameLower : term;
447
- if (
448
- shorter.length >= 4 &&
449
- longer.includes(shorter) &&
450
- shorter.length / longer.length >= 0.5
451
- ) {
452
- consider(60, `name contains "${shorter}"`);
560
+ if (sameWord(term, nameLower)) consider(95, `name "${name}"`);
561
+ // The term is a word of the name, or starts one: "input" in TextInput.
562
+ else if (startsAWordOf(term, name)) {
563
+ consider(60, `name contains "${term}"`);
564
+ }
565
+ if (fuzzy) {
566
+ const dist = levenshteinDistance(term, nameLower);
567
+ if (isTypo(term, nameLower, dist)) {
568
+ consider(
569
+ dist === 1 ? 80 : dist === 2 ? 40 : 20,
570
+ `similar name (distance ${dist})`,
571
+ );
572
+ }
453
573
  }
454
- const dist = levenshteinDistance(term, nameLower);
455
- if (dist === 1) consider(80, `similar name (distance ${dist})`);
456
- else if (dist === 2) consider(40, `similar name (distance ${dist})`);
457
- else if (dist === 3) consider(20, `similar name (distance ${dist})`);
458
574
  }
459
575
 
460
576
  // ── Keyword signals ─────────────────────────────────────────────
@@ -464,14 +580,17 @@ export function scoreCandidate(
464
580
  consider(90, `keyword "${kw}"`);
465
581
  continue;
466
582
  }
467
- const s = term.length < kwLower.length ? term : kwLower;
468
- const l = term.length < kwLower.length ? kwLower : term;
469
- if (s.length >= 4 && l.includes(s) && s.length / l.length >= 0.5) {
470
- consider(70, `keyword "${kw}"`);
583
+ if (sameWord(term, kwLower)) {
584
+ consider(88, `keyword "${kw}"`);
585
+ continue;
586
+ }
587
+ if (startsAWordOf(term, kw)) consider(70, `keyword "${kw}"`);
588
+ if (fuzzy) {
589
+ const dist = levenshteinDistance(term, kwLower);
590
+ if (isTypo(term, kwLower, dist) && dist <= 2) {
591
+ consider(dist === 1 ? 70 : 30, `keyword "${kw}" (distance ${dist})`);
592
+ }
471
593
  }
472
- const dist = levenshteinDistance(term, kwLower);
473
- if (dist === 1) consider(70, `keyword "${kw}" (distance ${dist})`);
474
- else if (dist === 2) consider(30, `keyword "${kw}" (distance ${dist})`);
475
594
  }
476
595
 
477
596
  // ── Weak keyword signals (derived, not authored) ─────────────────
@@ -481,15 +600,11 @@ export function scoreCandidate(
481
600
  // No Levenshtein tier — fuzzy matching a derived signal is pure noise.
482
601
  for (const kw of weakKeywords) {
483
602
  const kwLower = String(kw).toLowerCase();
484
- if (kwLower === term) {
603
+ if (kwLower === term || sameWord(term, kwLower)) {
485
604
  consider(60, `renders ${kw}`);
486
605
  continue;
487
606
  }
488
- const s = term.length < kwLower.length ? term : kwLower;
489
- const l = term.length < kwLower.length ? kwLower : term;
490
- if (s.length >= 4 && l.includes(s) && s.length / l.length >= 0.5) {
491
- consider(40, `renders ${kw}`);
492
- }
607
+ if (startsAWordOf(term, kw)) consider(40, `renders ${kw}`);
493
608
  }
494
609
 
495
610
  // ── Prose / description / guidance signals (stem-tolerant whole word) ──
@@ -528,16 +643,26 @@ export function scoreCandidate(
528
643
  }
529
644
 
530
645
  /**
531
- * Load a doc module's `docs`/`doc` export, swallowing errors.
646
+ * A component or hook doc, compiled, or null when it cannot be read.
532
647
  * @param {string} docPath
533
648
  * @param {string} [exportName]
649
+ * @param {'components' | 'hooks'} [root]
534
650
  * @returns {Promise<any>}
535
651
  */
536
- async function loadModuleDoc(docPath, exportName = 'docs') {
652
+ async function loadModuleDoc(
653
+ docPath,
654
+ exportName = 'docs',
655
+ root = 'components',
656
+ ) {
537
657
  try {
538
- const mod = await import(pathToFileURL(docPath).href);
539
658
  // Support both the stamped default export and the legacy named export.
540
- return mod?.default ?? mod[exportName] ?? null;
659
+ return (
660
+ (await readDocView(docPath, {
661
+ root,
662
+ loader: 'native',
663
+ exports: ['default', exportName],
664
+ })) ?? null
665
+ );
541
666
  } catch {
542
667
  return null;
543
668
  }
@@ -637,7 +762,8 @@ async function gatherIntegrationComponents(cwd) {
637
762
  /** @type {Candidate[]} */
638
763
  const candidates = [];
639
764
  for (const integration of loadedIntegrations) {
640
- for (const rec of discoverIntegrationComponents(integration)) {
765
+ const {components} = await discoverValidIntegrationComponents(integration);
766
+ for (const rec of components) {
641
767
  const doc = await loadModuleDoc(rec.docPath);
642
768
  candidates.push({
643
769
  domain: 'component',
@@ -701,7 +827,7 @@ async function gatherHooks(coreDir) {
701
827
  let description = '';
702
828
  let importPath = '@astryxdesign/core/hooks';
703
829
  if (docPath) {
704
- const doc = await loadModuleDoc(docPath);
830
+ const doc = await loadModuleDoc(docPath, 'docs', 'hooks');
705
831
  if (doc) {
706
832
  keywords = Array.isArray(doc.keywords) ? doc.keywords : [];
707
833
  description = doc.usage?.description || doc.description || '';
@@ -720,7 +846,11 @@ async function gatherHooks(coreDir) {
720
846
  }
721
847
 
722
848
  /**
723
- * Build doc-topic candidates: topic name + description + section prose.
849
+ * Build doc candidates at the grain a reader reads them: each section of a
850
+ * topic, whose command reads just that section; each topic as a whole, whose
851
+ * command lists its sections; and each docs-tree node by its route. The tree's
852
+ * guides split into sections like topics, and its typed docs also match by
853
+ * their own name, so `assertResponse` finds `cli/api/functions/assert-response`.
724
854
  *
725
855
  * Reads the project's catalog rather than the CLI's own docs directory, so a
726
856
  * topic an integration contributed (or replaced) is searchable exactly like a
@@ -732,44 +862,274 @@ async function gatherHooks(coreDir) {
732
862
  async function gatherDocs(cwd) {
733
863
  /** @type {Candidate[]} */
734
864
  const candidates = [];
735
- let entries;
865
+ let catalog;
736
866
  try {
737
- entries = (await loadDocsCatalog(cwd)).entries();
867
+ catalog = await loadDocsCatalog(cwd);
738
868
  } catch {
739
869
  return candidates;
740
870
  }
741
- for (const entry of entries) {
742
- let doc = null;
871
+ let tree = null;
872
+ try {
873
+ tree = await projectTree(catalog);
874
+ } catch {
875
+ // `astryx doctor` reports a tree that fails to build; search still
876
+ // indexes the topics.
877
+ }
878
+ for (const entry of catalog.entries()) {
879
+ // A topic whose name opens another doc (spec:AST-046 FR11) is not
880
+ // offered: every hit's command must open the hit.
881
+ if (tree && !holdsOwnName(tree, catalog, entry)) continue;
882
+ let lowered = null;
743
883
  try {
744
- doc = await loadTopicDoc(entry);
884
+ lowered = await lowerTopic(catalog, entry);
745
885
  } catch {
746
886
  // A topic that cannot be loaded is reported by the commands that own
747
887
  // integration issues; search just cannot index it.
748
888
  }
749
- let description = '';
750
- /** @type {string[]} */
751
- const prose = [];
752
- if (doc) {
753
- description = doc.description || '';
754
- for (const section of doc.sections || []) {
755
- if (section.title) prose.push(section.title);
756
- for (const block of section.content || []) {
757
- if (block.type === 'prose' && block.text) prose.push(block.text);
758
- }
889
+ const doc = lowered?.doc ?? null;
890
+ // A flat topic lives in the Unorganized level; its hits say so, and name
891
+ // the package each section came from.
892
+ const home = tree?.get(entry.name);
893
+ const placed = home?.ref?.flatTopic === entry.name ? home : null;
894
+ /** @type {Map<string, string>} */
895
+ const packages = new Map([
896
+ [entry.providerId ?? entry.package, entry.package],
897
+ ...entry.extensions.map(
898
+ ext => /** @type {[string, string]} */ ([ext.providerId ?? ext.package, ext.package]),
899
+ ),
900
+ ]);
901
+ candidates.push(
902
+ ...topicCandidates(
903
+ entry.name,
904
+ doc,
905
+ entry.title,
906
+ '',
907
+ placed && tree
908
+ ? [
909
+ ...tree.ancestors(placed).map(a => a.title),
910
+ doc?.title || entry.title || entry.name,
911
+ ].join(' › ')
912
+ : undefined,
913
+ placed ? `astryx docs ${placed.parent}` : undefined,
914
+ entry.package,
915
+ key =>
916
+ packages.get(lowered?.sectionProviders?.[key] ?? '') ?? entry.package,
917
+ ),
918
+ );
919
+ }
920
+ if (tree == null) return candidates;
921
+ for (const node of tree.nodes.values()) {
922
+ // A flat topic is indexed above, as a topic.
923
+ if (node.ref?.flatTopic) continue;
924
+ // A tree hit names where it lives: its ancestors' titles, then its own.
925
+ const path = [...tree.ancestors(node).map(a => a.title), node.title];
926
+ if (node.kind === 'generic') {
927
+ let doc = null;
928
+ try {
929
+ doc = (await lowerTopic(catalog, guideEntry(node))).doc;
930
+ } catch {
931
+ // As above: the owning commands report it.
759
932
  }
933
+ candidates.push(
934
+ ...topicCandidates(
935
+ node.route,
936
+ doc,
937
+ node.title,
938
+ node.summary,
939
+ path.join(' › '),
940
+ node.parent == null ? undefined : `astryx docs ${node.parent}`,
941
+ node.provider,
942
+ ),
943
+ );
944
+ continue;
945
+ }
946
+ const selfDoc = /** @type {any} */ (node.ref)?.selfDoc;
947
+ // A typed doc's content is what `astryx docs <route>` prints. The first
948
+ // column of its tables names what the doc defines (an error code, an
949
+ // option, a parameter), so each is a keyword the doc answers to.
950
+ /** @type {any[]} */
951
+ const content = (await nodeView(catalog, tree, node)).content ?? [];
952
+ /** @type {string[]} */
953
+ const defined = [];
954
+ for (const block of content) {
955
+ if (block.type !== 'table' || !Array.isArray(block.rows)) continue;
956
+ for (const row of block.rows)
957
+ if (row[0] != null) defined.push(plain(row[0]));
760
958
  }
761
959
  candidates.push({
762
960
  domain: 'doc',
763
- name: entry.name,
764
- keywords: [],
765
- description,
766
- prose,
767
- _title: doc?.title || entry.title || entry.name,
961
+ name: node.name,
962
+ keywords: [
963
+ node.route.slice(node.route.lastIndexOf('/') + 1),
964
+ ...(Array.isArray(selfDoc?.keywords) ? selfDoc.keywords : []),
965
+ ...defined,
966
+ ...codeTerms({content}),
967
+ ],
968
+ description: node.summary || '',
969
+ prose: sectionProse({title: node.title, content}),
970
+ _topic: node.route,
971
+ _title: path.join(' › '),
972
+ _command: `astryx docs ${node.route}`,
973
+ _parent: node.parent == null ? 'astryx docs' : `astryx docs ${node.parent}`,
974
+ _package: node.provider,
768
975
  });
769
976
  }
770
977
  return candidates;
771
978
  }
772
979
 
980
+ /**
981
+ * The words one section says: its prose, headings, and list items.
982
+ * @param {any} section
983
+ * @returns {string[]}
984
+ */
985
+ function sectionProse(section) {
986
+ /** @type {string[]} */
987
+ const prose = [];
988
+ if (section?.title) prose.push(section.title);
989
+ for (const block of section?.content || []) {
990
+ if ((block.type === 'prose' || block.type === 'heading') && block.text) {
991
+ prose.push(block.text);
992
+ } else if (block.type === 'list' && Array.isArray(block.items)) {
993
+ for (const item of block.items) {
994
+ const text = typeof item === 'string' ? item : item?.text;
995
+ if (typeof text === 'string') prose.push(text);
996
+ }
997
+ } else if (block.type === 'table' && Array.isArray(block.rows)) {
998
+ for (const row of block.rows) prose.push(row.map(plain).join(' '));
999
+ } else if (block.type === 'code' && typeof block.code === 'string') {
1000
+ prose.push([block.label, block.code].filter(Boolean).join(' '));
1001
+ }
1002
+ }
1003
+ return prose;
1004
+ }
1005
+
1006
+ /**
1007
+ * The identifiers a doc part names in code ticks (`token-ref`,
1008
+ * `ERR_UNKNOWN_SECTION`). Each is a keyword: a reader who types one exactly
1009
+ * wants the part that defines or explains it.
1010
+ * @param {any} part - a section, or `{content}` of a typed doc
1011
+ * @returns {string[]}
1012
+ */
1013
+ function codeTerms(part) {
1014
+ /** @type {Set<string>} */
1015
+ const terms = new Set();
1016
+ /** @param {unknown} text */
1017
+ const scan = text => {
1018
+ for (const m of String(text ?? '').matchAll(/`([^`\s]{2,40})`/g)) {
1019
+ terms.add(m[1]);
1020
+ }
1021
+ };
1022
+ for (const block of part?.content || []) {
1023
+ if (block.type === 'prose') scan(block.text);
1024
+ else if (block.type === 'list' && Array.isArray(block.items)) {
1025
+ for (const item of block.items) {
1026
+ scan(typeof item === 'string' ? item : item?.text);
1027
+ }
1028
+ } else if (block.type === 'table' && Array.isArray(block.rows)) {
1029
+ for (const row of block.rows) for (const cell of row) scan(cell);
1030
+ }
1031
+ }
1032
+ return [...terms];
1033
+ }
1034
+
1035
+ /**
1036
+ * The headings inside a section. Each names a subsection, so a query that
1037
+ * names one should find the section as surely as one that names its title.
1038
+ * @param {any} section
1039
+ * @returns {string[]}
1040
+ */
1041
+ function headings(section) {
1042
+ return (section?.content || [])
1043
+ .filter(
1044
+ (/** @type {any} */ block) => block.type === 'heading' && block.text,
1045
+ )
1046
+ .map((/** @type {any} */ block) => String(block.text));
1047
+ }
1048
+
1049
+ /**
1050
+ * A table cell as plain words, without its code ticks.
1051
+ * @param {unknown} cell
1052
+ * @returns {string}
1053
+ */
1054
+ function plain(cell) {
1055
+ return unlinkText(String(cell ?? '')).replaceAll('`', '');
1056
+ }
1057
+
1058
+ /**
1059
+ * The candidates one topic yields: the topic itself, and one per section when
1060
+ * it has more than one. A topic's command lists its sections, and a section's
1061
+ * command reads only that section, so a hit never costs a whole-topic read.
1062
+ * @param {string} name - the topic name, or a placed guide's route
1063
+ * @param {any} doc - the lowered topic, or null when it did not load
1064
+ * @param {string} [title]
1065
+ * @param {string} [summary]
1066
+ * @param {string} [path] - where the topic lives in the docs tree, as titles
1067
+ * joined by ` › `; a flat topic is its own title
1068
+ * @param {string} [parent] - the command that opens the level above the
1069
+ * topic: its namespace, or the Unorganized level for a flat topic
1070
+ * @param {string} [pkg] - the npm package that authored the topic
1071
+ * @param {(key: string) => string} [sectionPackage] - the npm package a
1072
+ * section came from: an extension's section names the extension's package
1073
+ * @returns {Candidate[]}
1074
+ */
1075
+ function topicCandidates(
1076
+ name,
1077
+ doc,
1078
+ title,
1079
+ summary = '',
1080
+ path,
1081
+ parent,
1082
+ pkg,
1083
+ sectionPackage,
1084
+ ) {
1085
+ /** @type {any[]} */
1086
+ const sections = doc?.sections ?? [];
1087
+ const docTitle = path || doc?.title || title || name;
1088
+ const split = sections.length > 1;
1089
+ /** @type {Candidate[]} */
1090
+ const out = [
1091
+ {
1092
+ domain: 'doc',
1093
+ name,
1094
+ keywords: [
1095
+ ...(doc?.title || title ? [doc?.title || title] : []),
1096
+ ...(Array.isArray(doc?.keywords) ? doc.keywords : []),
1097
+ ],
1098
+ description: doc?.description || summary,
1099
+ prose: split
1100
+ ? sections.map(section => section.title).filter(Boolean)
1101
+ : sections.flatMap(sectionProse),
1102
+ _topic: name,
1103
+ _title: docTitle,
1104
+ _command: split ? `astryx docs ${name} --index` : `astryx docs ${name}`,
1105
+ ...(parent ? {_parent: parent} : {}),
1106
+ ...(pkg ? {_package: pkg} : {}),
1107
+ },
1108
+ ];
1109
+ if (!split) return out;
1110
+ for (const section of sections) {
1111
+ const key = sectionKey(section);
1112
+ out.push({
1113
+ domain: 'doc',
1114
+ name: key,
1115
+ keywords: [
1116
+ ...(section.title ? [section.title] : []),
1117
+ ...headings(section),
1118
+ ...codeTerms(section),
1119
+ ],
1120
+ description: sectionSummary(section),
1121
+ prose: sectionProse(section),
1122
+ _topic: name,
1123
+ _section: key,
1124
+ _title: `${docTitle} › ${section.title}`,
1125
+ _command: `astryx docs ${name} ${key}`,
1126
+ _parent: `astryx docs ${name} --index`,
1127
+ ...((sectionPackage?.(key) ?? pkg) ? {_package: sectionPackage?.(key) ?? pkg} : {}),
1128
+ });
1129
+ }
1130
+ return out;
1131
+ }
1132
+
773
1133
  /**
774
1134
  * Build template candidates (page + block) from the template discovery API.
775
1135
  * @param {string} cwd
@@ -783,6 +1143,13 @@ async function gatherTemplates(cwd) {
783
1143
  return [];
784
1144
  }
785
1145
  return templates.map(t => {
1146
+ // A replacement's target is its canonical unqualified lookup id. Keep the
1147
+ // integration-owned id as a keyword and response label, but score and print
1148
+ // commands against the id that `template()` resolves back to this entry.
1149
+ // This matters for replacement chains: one replacement's own id can be the
1150
+ // target of another, so using that shadowed id as the command would select
1151
+ // the other template.
1152
+ const commandName = t.replaces ?? t.dirName;
786
1153
  // Blocks ship an authored componentsUsed; page templates don't, so derive
787
1154
  // them from the source. Category words (e.g. "Dashboard - Analytics") are
788
1155
  // strong intent signal for pages, which otherwise only index on name +
@@ -795,6 +1162,7 @@ async function gatherTemplates(cwd) {
795
1162
  const keywords = Array.isArray(t.componentsUsed)
796
1163
  ? [...t.componentsUsed]
797
1164
  : [];
1165
+ keywords.push(...templateLookupIds(t).filter(id => id !== commandName));
798
1166
  /** @type {string[]} */
799
1167
  let weakKeywords = [];
800
1168
  if (t.type === 'page') {
@@ -810,12 +1178,14 @@ async function gatherTemplates(cwd) {
810
1178
  }
811
1179
  return {
812
1180
  domain: 'template',
813
- name: t.dirName,
1181
+ name: commandName,
814
1182
  keywords,
815
1183
  weakKeywords,
816
1184
  description: t.description || '',
817
1185
  _displayName: t.name,
818
1186
  _kind: t.type, // 'page' | 'block'
1187
+ _resultName: t.dirName,
1188
+ _commandName: commandName,
819
1189
  };
820
1190
  });
821
1191
  }
@@ -834,7 +1204,7 @@ async function gatherTemplates(cwd) {
834
1204
  function toResult(c, score, reason, matchedTerms, queryTerms) {
835
1205
  const base = {
836
1206
  domain: c.domain,
837
- name: c.name,
1207
+ name: c._resultName ?? c.name,
838
1208
  score,
839
1209
  reason,
840
1210
  description: c.description || '',
@@ -856,10 +1226,16 @@ function toResult(c, score, reason, matchedTerms, queryTerms) {
856
1226
  };
857
1227
  break;
858
1228
  case 'doc':
1229
+ // A doc result names its topic or route, plus the section when the
1230
+ // hit is one section; its command reads exactly that part.
859
1231
  result = {
860
1232
  ...base,
1233
+ name: c._topic ?? c.name,
1234
+ ...(c._section ? {section: c._section} : {}),
861
1235
  title: c._title,
862
- command: `astryx docs ${c.name}`,
1236
+ command: c._command ?? `astryx docs ${c.name}`,
1237
+ ...(c._parent ? {parent: c._parent} : {}),
1238
+ ...(c._package ? {package: c._package} : {}),
863
1239
  };
864
1240
  break;
865
1241
  case 'template':
@@ -867,7 +1243,7 @@ function toResult(c, score, reason, matchedTerms, queryTerms) {
867
1243
  ...base,
868
1244
  displayName: c._displayName,
869
1245
  kind: c._kind,
870
- command: `astryx template ${c.name}`,
1246
+ command: `astryx template ${c._commandName ?? c.name} --type ${c._kind}`,
871
1247
  };
872
1248
  break;
873
1249
  default:
@@ -884,7 +1260,7 @@ function toResult(c, score, reason, matchedTerms, queryTerms) {
884
1260
  * @param {string} [options.cwd]
885
1261
  * @param {'component'|'hook'|'doc'|'template'} [options.type] - Restrict to one domain.
886
1262
  * @param {number} [options.limit] - Max results (default 20).
887
- * @returns {Promise<{type: 'search', data: {query: string, matchCount: number, results: Array<object>}}>}
1263
+ * @returns {Promise<import('./search.type.mjs').SearchResponse>}
888
1264
  */
889
1265
  export async function search(query, options = {}) {
890
1266
  const {cwd = process.cwd(), type, limit = 20} = options;
@@ -919,17 +1295,26 @@ export async function search(query, options = {}) {
919
1295
  const term = String(query).trim().toLowerCase();
920
1296
  const tokens = tokenizeQuery(term);
921
1297
 
922
- const coreDir = findCoreDir(cwd);
923
- if (!coreDir) {
924
- throw new AstryxError('Could not find @astryxdesign/core package');
1298
+ // `astryx docs` reads docs without @astryxdesign/core, so a docs-only
1299
+ // search must too. Every other domain reads core.
1300
+ const docsOnly = type === 'doc';
1301
+ const coreDir = docsOnly ? null : findCoreDir(cwd);
1302
+ if (!docsOnly && !coreDir) {
1303
+ throw new AstryxError(
1304
+ 'Could not find @astryxdesign/core package',
1305
+ undefined,
1306
+ ERROR_CODES.ERR_CORE_NOT_FOUND,
1307
+ );
925
1308
  }
926
1309
 
927
1310
  // Gather candidates from each requested domain in parallel.
928
1311
  /** @param {string} d */
929
1312
  const wants = d => !type || type === d;
930
1313
  const [components, hooks, docTopics, templates] = await Promise.all([
931
- wants('component') ? gatherComponents(coreDir, cwd) : [],
932
- wants('hook') ? gatherHooks(coreDir) : [],
1314
+ wants('component')
1315
+ ? gatherComponents(/** @type {string} */ (coreDir), cwd)
1316
+ : [],
1317
+ wants('hook') ? gatherHooks(/** @type {string} */ (coreDir)) : [],
933
1318
  wants('doc') ? gatherDocs(cwd) : [],
934
1319
  wants('template') ? gatherTemplates(cwd) : [],
935
1320
  ]);
@@ -971,7 +1356,10 @@ export async function search(query, options = {}) {
971
1356
  data: {
972
1357
  query: String(query).trim(),
973
1358
  matchCount: scored.length,
974
- results: limited,
1359
+ // toResult gives every domain its command and domain fields.
1360
+ results: /** @type {import('./search.type.mjs').SearchResultEntry[]} */ (
1361
+ limited
1362
+ ),
975
1363
  },
976
1364
  };
977
1365
  }