@astryxdesign/cli 0.6.3 → 0.6.4-canary.078fd25

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 (728) hide show
  1. package/CHANGELOG.md +194 -0
  2. package/README.md +152 -107
  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 +22 -10
  7. package/api/build/build.test.mjs +219 -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 +208 -53
  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 +49 -19
  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 +277 -41
  54. package/api/docs/_adapter.mjs +993 -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/docOverlays.test.mjs +27 -1
  61. package/api/docs/docs.d.mts +10 -3
  62. package/api/docs/docs.doc.mjs +55 -16
  63. package/api/docs/docs.mjs +53 -10
  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/list/list.mjs +28 -12
  70. package/api/docs/node/node.d.mts +43 -0
  71. package/api/docs/node/node.mjs +192 -0
  72. package/api/docs/reference-blocks.test.mjs +406 -0
  73. package/api/doctor/doctor.d.mts +104 -1
  74. package/api/doctor/doctor.doc.mjs +18 -8
  75. package/api/doctor/doctor.mjs +635 -7
  76. package/api/doctor/doctor.test.mjs +732 -11
  77. package/api/doctor/doctor.type.d.mts +1 -1
  78. package/api/doctor/doctor.type.mjs +1 -1
  79. package/api/gap-report/gap-report.doc.mjs +27 -14
  80. package/api/hook/_adapter.mjs +19 -5
  81. package/api/hook/hook.doc.mjs +7 -3
  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 -1
  87. package/api/index.mjs +6 -3
  88. package/api/init/init.doc.mjs +22 -12
  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 +56 -65
  99. package/api/integration/add-theme.test.mjs +139 -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 +5 -4
  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 +107 -0
  119. package/api/integration/pack-check.mjs +160 -11
  120. package/api/integration/pack-check.test.mjs +477 -47
  121. package/api/integration/pack-check.type.d.mts +26 -2
  122. package/api/integration/pack-check.type.mjs +15 -2
  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 +2 -1
  131. package/api/json/envelope-types.test.mjs +76 -0
  132. package/api/json/index.ts +2 -0
  133. package/api/json/isError.doc.mjs +2 -1
  134. package/api/json/parseResponse.doc.mjs +3 -2
  135. package/api/layout/_adapter.mjs +20 -5
  136. package/api/layout/expand/expand.mjs +7 -5
  137. package/api/layout/expand/expand.path-safety.test.mjs +53 -0
  138. package/api/layout/grammar/grammar.mjs +2 -1
  139. package/api/layout/layoutCheck.doc.mjs +1 -0
  140. package/api/layout/layoutExpand.doc.mjs +2 -1
  141. package/api/layout/layoutGrammar.doc.mjs +1 -0
  142. package/api/search/search-return-type.test.mjs +54 -0
  143. package/api/search/search.d.mts +89 -12
  144. package/api/search/search.doc.mjs +8 -2
  145. package/api/search/search.mjs +697 -97
  146. package/api/search/search.type.d.mts +15 -3
  147. package/api/search/search.type.mjs +5 -2
  148. package/api/swizzle/copy/copy.mjs +28 -11
  149. package/api/swizzle/swizzle.doc.mjs +8 -5
  150. package/api/swizzle/swizzle.type.d.mts +2 -2
  151. package/api/swizzle/swizzle.type.mjs +2 -2
  152. package/api/template/copy/copy.mjs +18 -24
  153. package/api/template/copy/copy.test.mjs +26 -0
  154. package/api/template/list/list.mjs +1 -0
  155. package/api/template/table-floating-bulk-actions.test.mjs +66 -0
  156. package/api/template/template-integration.test.mjs +1072 -3
  157. package/api/template/template-suffix.test.mjs +41 -21
  158. package/api/template/template.d.mts +1 -1
  159. package/api/template/template.doc.mjs +32 -9
  160. package/api/template/template.mjs +45 -8
  161. package/api/template/template.type.d.mts +12 -14
  162. package/api/template/template.type.mjs +15 -14
  163. package/api/theme/_adapter.d.mts +2 -3
  164. package/api/theme/_adapter.mjs +4 -5
  165. package/api/theme/add/add.binary.test.mjs +84 -0
  166. package/api/theme/add/add.mjs +31 -22
  167. package/api/theme/add/add.rollback.test.mjs +158 -0
  168. package/api/theme/add/add.staging.test.mjs +83 -0
  169. package/api/theme/add/add.test.mjs +14 -1
  170. package/api/theme/build/build.family.test.mjs +7 -12
  171. package/api/theme/build/build.mjs +140 -59
  172. package/api/theme/build/build.public-component-vars.test.mjs +1 -1
  173. package/api/theme/build/build.receipt-doc.test.mjs +111 -0
  174. package/api/theme/build/build.rollback.test.mjs +148 -0
  175. package/api/theme/build/build.test.mjs +127 -0
  176. package/api/theme/build/font-warning.mjs +3 -3
  177. package/api/theme/build/font-warning.test.mjs +5 -2
  178. package/api/theme/generateTonalPalette.doc.mjs +2 -2
  179. package/api/theme/integration-themes.test.mjs +39 -28
  180. package/api/theme/list/list.test.mjs +19 -20
  181. package/api/theme/listThemes.doc.mjs +6 -5
  182. package/api/theme/palette/generate/generate.mjs +8 -3
  183. package/api/theme/palette/generate/generate.test.mjs +96 -0
  184. package/api/theme/palette/generate/generator.d.mts +10 -13
  185. package/api/theme/palette/generate/generator.mjs +15 -4
  186. package/api/theme/palette/generate/generator.test.mjs +10 -0
  187. package/api/theme/template/template.mjs +11 -2
  188. package/api/theme/template/template.test.mjs +20 -0
  189. package/api/theme/theme.type.d.mts +170 -11
  190. package/api/theme/theme.type.mjs +94 -27
  191. package/api/theme/themeAdd.doc.mjs +12 -12
  192. package/api/theme/themeBuild.doc.mjs +21 -17
  193. package/api/theme/themeList.doc.mjs +6 -3
  194. package/api/theme/themeListAvailable.doc.mjs +6 -3
  195. package/api/theme/themePaletteGenerate.doc.mjs +16 -8
  196. package/api/theme/themeTargets.doc.mjs +4 -2
  197. package/api/theme/themeTemplate.doc.mjs +8 -3
  198. package/api/upgrade/_adapter.d.mts +32 -5
  199. package/api/upgrade/_adapter.mjs +139 -22
  200. package/api/upgrade/list/list.mjs +2 -1
  201. package/api/upgrade/list/list.test.mjs +73 -0
  202. package/api/upgrade/project-context.test.mjs +272 -0
  203. package/api/upgrade/provider-agreement.test.mjs +152 -0
  204. package/api/upgrade/run/files-changed.test.mjs +111 -0
  205. package/api/upgrade/run/run.mjs +358 -59
  206. package/api/upgrade/status/status.mjs +2 -2
  207. package/api/upgrade/upgrade.doc.mjs +32 -23
  208. package/api/upgrade/upgrade.type.d.mts +43 -5
  209. package/api/upgrade/upgrade.type.mjs +29 -13
  210. package/assets/codemods/__tests__/registry.test.mjs +1 -0
  211. package/assets/codemods/__tests__/runner.test.mjs +332 -8
  212. package/assets/codemods/file-count.test.mjs +163 -0
  213. package/assets/codemods/integration-discovery.mjs +48 -4
  214. package/assets/codemods/integration-discovery.test.mjs +73 -0
  215. package/assets/codemods/integration-runner.mjs +59 -7
  216. package/assets/codemods/integration-runner.protection.test.mjs +153 -0
  217. package/assets/codemods/registry.mjs +1 -0
  218. package/assets/codemods/run-codemod.mjs +177 -34
  219. package/assets/codemods/runner.mjs +353 -104
  220. package/assets/codemods/term-log.mjs +32 -8
  221. package/assets/codemods/term-log.test.mjs +19 -1
  222. package/assets/codemods/transform-prop.mjs +109 -0
  223. package/assets/codemods/transform-prop.test.mjs +95 -0
  224. package/assets/codemods/transforms/v0.0.14/__tests__/rename-status-variants.test.mjs +86 -165
  225. package/assets/codemods/transforms/v0.0.14/rename-status-variants.mjs +72 -210
  226. package/assets/codemods/transforms/v0.1.8/__tests__/rename-avatar-size-scale.test.mjs +83 -115
  227. package/assets/codemods/transforms/v0.1.8/rename-avatar-size-scale.mjs +57 -186
  228. package/assets/codemods/transforms/v0.3.0/__tests__/unwrap-authoring-factories.test.mjs +27 -5
  229. package/assets/codemods/transforms/v0.3.0/unwrap-authoring-factories.mjs +20 -5
  230. package/assets/codemods/transforms/v0.6.0/__tests__/next-codemods.test.mjs +47 -0
  231. package/assets/codemods/transforms/v0.6.0/rename-resizable-pixel-bounds.mjs +57 -10
  232. package/assets/codemods/transforms/v0.6.4/__tests__/migrate-native-picker-to-presentation.test.mjs +63 -0
  233. package/assets/codemods/transforms/v0.6.4/__tests__/migrate-theme-catalog-to-descriptors.test.mjs +220 -0
  234. package/assets/codemods/transforms/v0.6.4/index.mjs +31 -0
  235. package/assets/codemods/transforms/v0.6.4/migrate-native-picker-to-presentation.mjs +148 -0
  236. package/assets/codemods/transforms/v0.6.4/migrate-theme-catalog-to-descriptors.mjs +141 -0
  237. package/assets/docs/README.md +12 -1
  238. package/assets/docs/authoring.doc.mjs +14 -0
  239. package/assets/docs/browser-support.doc.mjs +11 -11
  240. package/assets/docs/color.doc.mjs +8 -2
  241. package/assets/docs/elevation.doc.mjs +6 -4
  242. package/assets/docs/getting-started.doc.mjs +6 -17
  243. package/assets/docs/icons.doc.mjs +2 -21
  244. package/assets/docs/illustrations.doc.mjs +7 -15
  245. package/assets/docs/internationalization.doc.mjs +7 -5
  246. package/assets/docs/layout.doc.dense.mjs +132 -84
  247. package/assets/docs/layout.doc.mjs +134 -78
  248. package/assets/docs/migration.doc.mjs +19 -21
  249. package/assets/docs/motion.doc.mjs +16 -3
  250. package/assets/docs/principles.doc.dense.mjs +5 -5
  251. package/assets/docs/principles.doc.mjs +14 -6
  252. package/assets/docs/principles.doc.zh.mjs +6 -6
  253. package/assets/docs/shape.doc.mjs +8 -3
  254. package/assets/docs/spacing.doc.mjs +7 -2
  255. package/assets/docs/styling-libraries.doc.mjs +10 -6
  256. package/assets/docs/styling.doc.mjs +22 -26
  257. package/assets/docs/theme.doc.dense.mjs +58 -18
  258. package/assets/docs/theme.doc.mjs +60 -50
  259. package/assets/docs/theme.doc.zh.mjs +9 -8
  260. package/assets/docs/tokens.doc.dense.mjs +2 -2
  261. package/assets/docs/tokens.doc.mjs +390 -9
  262. package/assets/docs/tokens.doc.zh.mjs +2 -2
  263. package/assets/docs/tree/add-a-component.doc.mjs +75 -0
  264. package/assets/docs/tree/add-a-theme.doc.mjs +85 -0
  265. package/assets/docs/tree/add-a-topic.doc.mjs +144 -0
  266. package/assets/docs/tree/agent-guidance.doc.mjs +138 -0
  267. package/assets/docs/tree/api.doc.mjs +30 -0
  268. package/assets/docs/tree/block-template.doc.mjs +130 -0
  269. package/assets/docs/tree/build-the-template.doc.mjs +28 -0
  270. package/assets/docs/tree/building-blocks.doc.mjs +46 -0
  271. package/assets/docs/tree/check-your-docs.doc.mjs +137 -0
  272. package/assets/docs/tree/checks.doc.mjs +119 -0
  273. package/assets/docs/tree/cli.doc.mjs +23 -0
  274. package/assets/docs/tree/codemods.doc.mjs +147 -0
  275. package/assets/docs/tree/commands.doc.mjs +25 -0
  276. package/assets/docs/tree/component-family.doc.mjs +113 -0
  277. package/assets/docs/tree/component-imports.doc.mjs +69 -0
  278. package/assets/docs/tree/component-lookups.doc.mjs +149 -0
  279. package/assets/docs/tree/components.doc.mjs +23 -0
  280. package/assets/docs/tree/configuration.doc.mjs +23 -0
  281. package/assets/docs/tree/debug-and-gap-reports.doc.mjs +182 -0
  282. package/assets/docs/tree/define-the-theme.doc.mjs +118 -0
  283. package/assets/docs/tree/describe-the-component.doc.mjs +57 -0
  284. package/assets/docs/tree/docs.doc.mjs +21 -0
  285. package/assets/docs/tree/document-the-template.doc.mjs +28 -0
  286. package/assets/docs/tree/document-the-theme.doc.mjs +68 -0
  287. package/assets/docs/tree/export-template-assets.doc.mjs +147 -0
  288. package/assets/docs/tree/extend-or-replace.doc.mjs +103 -0
  289. package/assets/docs/tree/fonts-and-assets.doc.mjs +106 -0
  290. package/assets/docs/tree/generate-a-palette.doc.mjs +66 -0
  291. package/assets/docs/tree/grade-template-with-agent.doc.mjs +105 -0
  292. package/assets/docs/tree/help.doc.mjs +16 -0
  293. package/assets/docs/tree/integrations.doc.mjs +40 -0
  294. package/assets/docs/tree/links.doc.mjs +98 -0
  295. package/assets/docs/tree/package-and-test.doc.mjs +32 -0
  296. package/assets/docs/tree/page-template.doc.mjs +71 -0
  297. package/assets/docs/tree/publishing.doc.mjs +111 -0
  298. package/assets/docs/tree/quick-start.doc.mjs +272 -0
  299. package/assets/docs/tree/replace-a-core-component.doc.mjs +104 -0
  300. package/assets/docs/tree/replace-a-core-template.doc.mjs +172 -0
  301. package/assets/docs/tree/sections-and-placement.doc.mjs +108 -0
  302. package/assets/docs/tree/see-it-in-an-app.doc.mjs +59 -0
  303. package/assets/docs/tree/ship.doc.mjs +16 -0
  304. package/assets/docs/tree/short-and-findable.doc.mjs +108 -0
  305. package/assets/docs/tree/single-component.doc.mjs +165 -0
  306. package/assets/docs/tree/start-a-template.doc.mjs +143 -0
  307. package/assets/docs/tree/subcomponent.doc.mjs +115 -0
  308. package/assets/docs/tree/template-assets.doc.mjs +64 -0
  309. package/assets/docs/tree/template-doc-overview.doc.mjs +109 -0
  310. package/assets/docs/tree/template-fonts.doc.mjs +102 -0
  311. package/assets/docs/tree/template-grading-rubric.doc.mjs +452 -0
  312. package/assets/docs/tree/template-icons.doc.mjs +97 -0
  313. package/assets/docs/tree/template-images-media.doc.mjs +127 -0
  314. package/assets/docs/tree/template-styles.doc.mjs +93 -0
  315. package/assets/docs/tree/templates.doc.mjs +34 -0
  316. package/assets/docs/tree/test-in-an-app.doc.mjs +115 -0
  317. package/assets/docs/tree/test-template-in-app.doc.mjs +128 -0
  318. package/assets/docs/tree/themes.doc.mjs +39 -0
  319. package/assets/docs/tree/troubleshooting.doc.mjs +149 -0
  320. package/assets/docs/tree/upgrading.doc.mjs +103 -0
  321. package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +51 -0
  322. package/assets/docs/tree/verify-packed-template.doc.mjs +77 -0
  323. package/assets/docs/tree/versioning.doc.mjs +161 -0
  324. package/assets/docs/tree/write-good-templates.doc.mjs +64 -0
  325. package/assets/docs/tree/write-the-template-file.doc.mjs +154 -0
  326. package/assets/docs/typography.doc.mjs +24 -4
  327. package/assets/docs/working-with-ai.doc.mjs +34 -26
  328. package/assets/templates/blocks/components/CheckboxList/CheckboxListSelectAllPattern.doc.mjs +1 -1
  329. package/assets/templates/blocks/components/CheckboxList/CheckboxListSelectAllPattern.tsx +1 -3
  330. package/assets/templates/blocks/components/DateInput/DateInputDateRange.tsx +1 -1
  331. package/assets/templates/blocks/components/InternationalizationProvider/InternationalizationProvider01ShippedLocale.tsx +1 -1
  332. package/assets/templates/blocks/components/Item/ItemDocumentTabs.doc.mjs +14 -0
  333. package/assets/templates/blocks/components/Item/ItemDocumentTabs.tsx +100 -0
  334. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaInlineRail.doc.mjs +14 -0
  335. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaInlineRail.tsx +61 -0
  336. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaOverscrollChaining.doc.mjs +14 -0
  337. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaOverscrollChaining.tsx +126 -0
  338. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaShowcase.doc.mjs +15 -0
  339. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaShowcase.tsx +86 -0
  340. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyGroupHeaders.doc.mjs +14 -0
  341. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyGroupHeaders.tsx +99 -0
  342. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyPassthrough.doc.mjs +14 -0
  343. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaStickyPassthrough.tsx +122 -0
  344. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaTwoAxisBoard.doc.mjs +14 -0
  345. package/assets/templates/blocks/components/ScrollableArea/ScrollableAreaTwoAxisBoard.tsx +95 -0
  346. package/assets/templates/blocks/components/Table/TableBulkActionsTable.doc.mjs +14 -0
  347. package/assets/templates/blocks/components/Table/TableBulkActionsTable.tsx +71 -0
  348. package/assets/templates/blocks/components/Table/TableFloatingBulkActionsTable.doc.mjs +19 -0
  349. package/assets/templates/blocks/components/Table/TableFloatingBulkActionsTable.tsx +139 -0
  350. package/assets/templates/blocks/components/TimeInput/TimeInputConstrained.tsx +1 -0
  351. package/assets/templates/blocks/components/Timer/TimerFormats.doc.mjs +14 -0
  352. package/assets/templates/blocks/components/Timer/TimerFormats.tsx +34 -0
  353. package/assets/templates/blocks/components/Timer/TimerInline.doc.mjs +14 -0
  354. package/assets/templates/blocks/components/Timer/TimerInline.tsx +14 -0
  355. package/assets/templates/blocks/components/Timer/TimerShowcase.doc.mjs +13 -0
  356. package/assets/templates/blocks/components/Timer/TimerShowcase.tsx +47 -0
  357. package/assets/templates/blocks/components/Timer/TimerTypography.doc.mjs +14 -0
  358. package/assets/templates/blocks/components/Timer/TimerTypography.tsx +31 -0
  359. package/assets/templates/blocks/components/Toolbar/ToolbarTableFilter.doc.mjs +19 -3
  360. package/assets/templates/blocks/components/Toolbar/ToolbarTableFilter.tsx +383 -65
  361. package/assets/templates/pages/table-tree/page.tsx +1704 -0
  362. package/assets/templates/pages/table-tree/template.doc.mjs +12 -0
  363. package/assets/templates/themes/butter/butterTheme.doc.mjs +11 -0
  364. package/assets/templates/themes/chocolate/chocolateTheme.doc.mjs +11 -0
  365. package/assets/templates/themes/gothic/gothicTheme.doc.mjs +11 -0
  366. package/assets/templates/themes/matcha/matchaTheme.doc.mjs +11 -0
  367. package/assets/templates/themes/neutral/neutralTheme.doc.mjs +11 -0
  368. package/assets/templates/themes/stone/stoneTheme.doc.mjs +11 -0
  369. package/assets/templates/themes/y2k/y2kTheme.doc.mjs +11 -0
  370. package/authoring/_shared/contract.ts +22 -0
  371. package/authoring/codemod/codemod.doc.mjs +7 -2
  372. package/authoring/codemod/parse.d.mts +8 -8
  373. package/authoring/codemod/parse.mjs +8 -6
  374. package/authoring/codemod/type.ts +12 -0
  375. package/authoring/config/config.doc.mjs +11 -3
  376. package/authoring/config/debug-composition.test.mjs +92 -0
  377. package/authoring/config/parse.d.mts +15 -13
  378. package/authoring/config/parse.mjs +27 -8
  379. package/authoring/config/parse.test.mjs +8 -0
  380. package/authoring/config/type.ts +29 -6
  381. package/authoring/debug/debug.doc.d.mts +11 -0
  382. package/authoring/debug/debug.doc.mjs +182 -0
  383. package/authoring/debug/parse.d.mts +8 -8
  384. package/authoring/debug/parse.mjs +3 -3
  385. package/authoring/discover/discover.doc.d.mts +13 -0
  386. package/authoring/discover/discover.doc.mjs +138 -0
  387. package/authoring/discover/parse.d.mts +24 -0
  388. package/authoring/discover/parse.mjs +128 -0
  389. package/authoring/discover/parse.test.mjs +124 -0
  390. package/authoring/discover/type.ts +87 -0
  391. package/authoring/doctypes/_schema.d.mts +792 -24
  392. package/authoring/doctypes/_schema.mjs +549 -39
  393. package/authoring/doctypes/base/graph-fields.doc.d.mts +9 -0
  394. package/authoring/doctypes/base/graph-fields.doc.mjs +64 -0
  395. package/authoring/doctypes/base/type.ts +43 -0
  396. package/authoring/doctypes/command/command.doc.mjs +4 -3
  397. package/authoring/doctypes/command/parse.d.mts +2 -2
  398. package/authoring/doctypes/command/parse.mjs +1 -1
  399. package/authoring/doctypes/command/type.ts +5 -4
  400. package/authoring/doctypes/component/component.doc.mjs +12 -3
  401. package/authoring/doctypes/component/parse.d.mts +2 -2
  402. package/authoring/doctypes/component/parse.mjs +1 -1
  403. package/authoring/doctypes/component/type.ts +14 -5
  404. package/authoring/doctypes/doctypes-new.test.mjs +48 -6
  405. package/authoring/doctypes/enum/enum.doc.mjs +1 -1
  406. package/authoring/doctypes/enum/parse.d.mts +2 -2
  407. package/authoring/doctypes/enum/parse.mjs +1 -1
  408. package/authoring/doctypes/enum/type.ts +4 -2
  409. package/authoring/doctypes/function/function.doc.mjs +7 -2
  410. package/authoring/doctypes/function/parse.d.mts +2 -2
  411. package/authoring/doctypes/function/parse.mjs +1 -1
  412. package/authoring/doctypes/function/type.ts +9 -4
  413. package/authoring/doctypes/hook/hook.doc.mjs +4 -0
  414. package/authoring/doctypes/hook/parse.d.mts +2 -2
  415. package/authoring/doctypes/hook/parse.mjs +1 -1
  416. package/authoring/doctypes/hook/type.ts +5 -4
  417. package/authoring/doctypes/legacy.d.mts +8 -6
  418. package/authoring/doctypes/legacy.mjs +5 -4
  419. package/authoring/doctypes/load-contract.test.mjs +233 -0
  420. package/authoring/doctypes/namespace/namespace.doc.d.mts +9 -0
  421. package/authoring/doctypes/namespace/namespace.doc.mjs +128 -0
  422. package/authoring/doctypes/namespace/parse.d.mts +12 -0
  423. package/authoring/doctypes/namespace/parse.mjs +25 -0
  424. package/authoring/doctypes/namespace/parse.test.mjs +163 -0
  425. package/authoring/doctypes/namespace/type.ts +74 -0
  426. package/authoring/doctypes/parse.d.mts +22 -18
  427. package/authoring/doctypes/parse.mjs +22 -11
  428. package/authoring/doctypes/parse.test.mjs +77 -3
  429. package/authoring/doctypes/reference/parse.d.mts +2 -2
  430. package/authoring/doctypes/reference/parse.mjs +8 -5
  431. package/authoring/doctypes/reference/reference.doc.mjs +55 -6
  432. package/authoring/doctypes/reference/type.ts +75 -7
  433. package/authoring/doctypes/schema/parse.d.mts +2 -2
  434. package/authoring/doctypes/schema/parse.mjs +1 -1
  435. package/authoring/doctypes/schema/schema.doc.mjs +2 -2
  436. package/authoring/doctypes/schema/type.ts +4 -4
  437. package/authoring/doctypes/template/parse.d.mts +94 -1
  438. package/authoring/doctypes/template/parse.mjs +40 -2
  439. package/authoring/doctypes/template/parse.test.mjs +26 -2
  440. package/authoring/doctypes/template/template.doc.mjs +13 -3
  441. package/authoring/doctypes/template/type.ts +13 -2
  442. package/authoring/doctypes/theme/parse.d.mts +35 -0
  443. package/authoring/doctypes/theme/parse.mjs +76 -0
  444. package/authoring/doctypes/theme/theme.doc.d.mts +9 -0
  445. package/authoring/doctypes/theme/theme.doc.mjs +79 -0
  446. package/authoring/doctypes/theme/type.ts +42 -0
  447. package/authoring/doctypes/types.ts +12 -10
  448. package/authoring/gap-report/gap-report.doc.d.mts +12 -0
  449. package/authoring/gap-report/gap-report.doc.mjs +183 -0
  450. package/authoring/gap-report/parse.d.mts +10 -10
  451. package/authoring/gap-report/parse.mjs +6 -6
  452. package/authoring/gap-report/type.ts +1 -1
  453. package/authoring/identity/identity.doc.d.mts +9 -0
  454. package/authoring/identity/identity.doc.mjs +61 -0
  455. package/authoring/identity/type.ts +132 -0
  456. package/authoring/index.d.mts +3 -0
  457. package/authoring/index.d.ts +62 -17
  458. package/authoring/index.mjs +4 -1
  459. package/authoring/integration/integration.doc.mjs +22 -13
  460. package/authoring/integration/parse.d.mts +2 -2
  461. package/authoring/integration/parse.mjs +1 -1
  462. package/authoring/integration/parse.test.mjs +10 -1
  463. package/authoring/integration/schema.d.mts +6 -4
  464. package/authoring/integration/schema.mjs +9 -3
  465. package/authoring/integration/type.ts +19 -8
  466. package/authoring/shadcn/receipt.d.mts +6 -6
  467. package/clients/cli/__tests__/cliManifest.test.ts +27 -29
  468. package/clients/cli/command-load-failure.test.mjs +83 -0
  469. package/clients/cli/commands/blog.doc.mjs +1 -1
  470. package/clients/cli/commands/blog.mjs +23 -8
  471. package/clients/cli/commands/blog.test.mjs +42 -1
  472. package/clients/cli/commands/build-theme.adaptations.test.mjs +100 -1
  473. package/clients/cli/commands/build-theme.ascii-output.test.mjs +161 -0
  474. package/clients/cli/commands/build-theme.flag-docs.test.mjs +110 -0
  475. package/clients/cli/commands/build-theme.mjs +16 -50
  476. package/clients/cli/commands/build-theme.path-safety.test.mjs +86 -1
  477. package/clients/cli/commands/build-theme.variants.test.mjs +76 -0
  478. package/clients/cli/commands/build.doc.mjs +16 -8
  479. package/clients/cli/commands/build.exit-codes-doc.test.mjs +50 -0
  480. package/clients/cli/commands/build.mjs +137 -114
  481. package/clients/cli/commands/build.playbook.test.mjs +75 -0
  482. package/clients/cli/commands/build.text-fields.test.mjs +81 -0
  483. package/clients/cli/commands/component/index.mjs +153 -61
  484. package/clients/cli/commands/component-batch.test.mjs +341 -0
  485. package/clients/cli/commands/component-ownership.test.mjs +92 -3
  486. package/clients/cli/commands/component-package.test.mjs +46 -0
  487. package/clients/cli/commands/component-resolution.test.mjs +21 -0
  488. package/clients/cli/commands/component.doc.mjs +28 -10
  489. package/clients/cli/commands/component.test.mjs +19 -0
  490. package/clients/cli/commands/detail-levels.test.mjs +2 -2
  491. package/clients/cli/commands/discover.broken-integration.test.mjs +52 -5
  492. package/clients/cli/commands/discover.components-flag.test.mjs +97 -0
  493. package/clients/cli/commands/discover.doc.mjs +55 -9
  494. package/clients/cli/commands/discover.mjs +393 -118
  495. package/clients/cli/commands/discover.sources.test.mjs +267 -0
  496. package/clients/cli/commands/discover.text-projection.test.mjs +103 -0
  497. package/clients/cli/commands/docs.doc.mjs +28 -6
  498. package/clients/cli/commands/docs.mjs +295 -38
  499. package/clients/cli/commands/doctor-integration-components.doc.mjs +1 -1
  500. package/clients/cli/commands/doctor-integration-docs.doc.mjs +6 -5
  501. package/clients/cli/commands/doctor-integration-templates.doc.mjs +15 -8
  502. package/clients/cli/commands/doctor-integration-validate.doc.mjs +1 -1
  503. package/clients/cli/commands/doctor-integration.doc.mjs +1 -1
  504. package/clients/cli/commands/doctor-integration.package-json.test.mjs +53 -0
  505. package/clients/cli/commands/doctor-integration.test.mjs +143 -8
  506. package/clients/cli/commands/doctor.doc.mjs +4 -2
  507. package/clients/cli/commands/doctor.mjs +108 -37
  508. package/clients/cli/commands/doctor.test.mjs +42 -0
  509. package/clients/cli/commands/gap-report.doc.mjs +27 -15
  510. package/clients/cli/commands/gap-report.test.mjs +72 -0
  511. package/clients/cli/commands/hook/index.mjs +7 -17
  512. package/clients/cli/commands/hook.doc.mjs +1 -1
  513. package/clients/cli/commands/hook.text-projection.test.mjs +45 -0
  514. package/clients/cli/commands/init.doc.mjs +24 -10
  515. package/clients/cli/commands/init.flag-help.test.mjs +153 -0
  516. package/clients/cli/commands/integration-add.controls.test.mjs +132 -0
  517. package/clients/cli/commands/integration-add.doc.mjs +39 -13
  518. package/clients/cli/commands/integration-authoring.test.mjs +74 -19
  519. package/clients/cli/commands/integration-pack.doc.mjs +6 -10
  520. package/clients/cli/commands/integration-real-world.test.mjs +4 -10
  521. package/clients/cli/commands/integration-verify.doc.mjs +22 -0
  522. package/clients/cli/commands/integration.doc.mjs +5 -5
  523. package/clients/cli/commands/integration.mjs +75 -43
  524. package/clients/cli/commands/interactive-guard.test.mjs +101 -24
  525. package/clients/cli/commands/json-contract.test.mjs +33 -0
  526. package/clients/cli/commands/layout-check.doc.mjs +15 -4
  527. package/clients/cli/commands/layout-expand.doc.mjs +22 -5
  528. package/clients/cli/commands/layout-grammar.doc.mjs +1 -1
  529. package/clients/cli/commands/layout.doc.mjs +3 -3
  530. package/clients/cli/commands/layout.mjs +21 -9
  531. package/clients/cli/commands/layout.path-help.test.mjs +33 -0
  532. package/clients/cli/commands/layout.stdin-cap.test.mjs +47 -0
  533. package/clients/cli/commands/layout.text-fields.test.mjs +39 -0
  534. package/clients/cli/commands/manifest.doc.mjs +2 -2
  535. package/clients/cli/commands/no-prompt-wording.test.mjs +94 -0
  536. package/clients/cli/commands/search.doc.mjs +16 -6
  537. package/clients/cli/commands/search.mjs +49 -11
  538. package/clients/cli/commands/search.test.mjs +92 -0
  539. package/clients/cli/commands/setup-nudge.test.mjs +6 -0
  540. package/clients/cli/commands/swizzle.doc.mjs +4 -3
  541. package/clients/cli/commands/swizzle.path-safety.test.mjs +42 -0
  542. package/clients/cli/commands/template.doc.mjs +53 -14
  543. package/clients/cli/commands/template.flag-help.test.mjs +117 -0
  544. package/clients/cli/commands/template.mjs +4 -91
  545. package/clients/cli/commands/template.path-help.test.mjs +40 -0
  546. package/clients/cli/commands/text-json-parity.test.mjs +725 -0
  547. package/clients/cli/commands/theme-add.doc.mjs +5 -4
  548. package/clients/cli/commands/theme-build.doc.mjs +8 -7
  549. package/clients/cli/commands/theme-list.doc.mjs +2 -2
  550. package/clients/cli/commands/theme-palette-generate.doc.mjs +12 -7
  551. package/clients/cli/commands/theme-palette-generate.test.mjs +19 -0
  552. package/clients/cli/commands/theme-palette.doc.mjs +2 -3
  553. package/clients/cli/commands/theme-targets.behavior.test.mjs +4 -3
  554. package/clients/cli/commands/theme-targets.doc.mjs +3 -3
  555. package/clients/cli/commands/theme-template.behavior.test.mjs +12 -0
  556. package/clients/cli/commands/theme-template.doc.mjs +2 -2
  557. package/clients/cli/commands/theme.doc.mjs +3 -2
  558. package/clients/cli/commands/upgrade.ascii-output.test.mjs +87 -0
  559. package/clients/cli/commands/upgrade.doc.mjs +83 -12
  560. package/clients/cli/commands/upgrade.file-protection.test.mjs +228 -0
  561. package/clients/cli/commands/upgrade.flag-help.test.mjs +188 -0
  562. package/clients/cli/commands/upgrade.hook-output.test.mjs +88 -0
  563. package/clients/cli/commands/upgrade.mjs +29 -7
  564. package/clients/cli/formatters/index.mjs +164 -1
  565. package/clients/cli/formatters/index.test.mjs +97 -0
  566. package/clients/cli/index.mjs +47 -34
  567. package/clients/cli/latest-version-env.test.mjs +50 -0
  568. package/clients/cli/lib/cli-error.test.mjs +7 -0
  569. package/clients/cli/lib/component-format.mjs +9 -9
  570. package/clients/cli/lib/component-format.test.mjs +1 -1
  571. package/clients/cli/lib/define-command.mjs +56 -6
  572. package/clients/cli/lib/define-command.test.mjs +54 -0
  573. package/clients/cli/lib/doc-text-ascii.test.mjs +82 -0
  574. package/clients/cli/lib/exit-codes.test.mjs +113 -0
  575. package/clients/cli/lib/hook-format.mjs +19 -10
  576. package/clients/cli/lib/json-shim.mjs +62 -16
  577. package/clients/cli/lib/json-shim.test.mjs +83 -0
  578. package/clients/cli/lib/manifest.d.ts +2 -0
  579. package/clients/cli/lib/manifest.mjs +53 -6
  580. package/clients/cli/lib/manifest.test.mjs +22 -2
  581. package/clients/cli/lib/parse-error-format.test.mjs +81 -0
  582. package/foundation/agent-docs/agent-docs.d.mts +7 -2
  583. package/foundation/agent-docs/agent-docs.mjs +83 -13
  584. package/foundation/agent-docs/agent-docs.path-safety.test.mjs +266 -4
  585. package/foundation/config/integration-debug.test.mjs +28 -3
  586. package/foundation/config/project-themes.test.mjs +11 -19
  587. package/foundation/config/project.d.mts +20 -11
  588. package/foundation/config/project.mjs +263 -91
  589. package/foundation/config/project.test.mjs +270 -21
  590. package/foundation/discovery/authoring-self-docs.d.mts +87 -0
  591. package/foundation/discovery/authoring-self-docs.mjs +237 -0
  592. package/foundation/discovery/authoring-self-docs.test.mjs +174 -0
  593. package/foundation/discovery/authoring-surface.d.mts +74 -0
  594. package/foundation/discovery/authoring-surface.mjs +525 -0
  595. package/foundation/discovery/authoring-surface.test.mjs +392 -0
  596. package/foundation/discovery/cli-self-docs.d.mts +119 -0
  597. package/foundation/discovery/cli-self-docs.mjs +504 -0
  598. package/foundation/discovery/cli-self-docs.test.mjs +395 -0
  599. package/foundation/discovery/component-discovery.d.mts +39 -1
  600. package/foundation/discovery/component-discovery.mjs +50 -1
  601. package/foundation/discovery/component-loader.d.mts +35 -38
  602. package/foundation/discovery/component-loader.mjs +53 -222
  603. package/foundation/discovery/docs-discovery.d.mts +119 -11
  604. package/foundation/discovery/docs-discovery.mjs +427 -108
  605. package/foundation/discovery/docs-discovery.test.mjs +386 -20
  606. package/foundation/discovery/docs-output-budget.d.mts +28 -0
  607. package/foundation/discovery/docs-output-budget.mjs +50 -0
  608. package/foundation/discovery/docs-section-key.d.mts +116 -0
  609. package/foundation/discovery/docs-section-key.mjs +322 -0
  610. package/foundation/discovery/docs-section-key.test.mjs +246 -0
  611. package/foundation/discovery/template-adapter.d.mts +113 -11
  612. package/foundation/discovery/template-adapter.fixture-refs.test.mjs +248 -0
  613. package/foundation/discovery/template-adapter.integration-isolation.test.mjs +94 -0
  614. package/foundation/discovery/template-adapter.mjs +774 -83
  615. package/foundation/discovery/template-adapter.test.mjs +57 -0
  616. package/foundation/discovery/template-conflict-release.d.mts +13 -0
  617. package/foundation/discovery/template-conflict-release.mjs +40 -0
  618. package/foundation/discovery/template-conflict-release.test.mjs +40 -0
  619. package/foundation/discovery/theme-discovery.d.mts +67 -7
  620. package/foundation/discovery/theme-discovery.mjs +916 -186
  621. package/foundation/discovery/theme-discovery.test.mjs +613 -219
  622. package/foundation/discovery/theming-targets.test.mjs +4 -0
  623. package/foundation/doc-compiler/bundle.d.mts +47 -0
  624. package/foundation/doc-compiler/bundle.mjs +278 -0
  625. package/foundation/doc-compiler/bundle.test.mjs +266 -0
  626. package/foundation/doc-compiler/compile.d.mts +343 -0
  627. package/foundation/doc-compiler/compile.mjs +558 -0
  628. package/foundation/doc-compiler/diagnostics.d.mts +126 -0
  629. package/foundation/doc-compiler/diagnostics.mjs +305 -0
  630. package/foundation/doc-compiler/doc-compiler.test.mjs +714 -0
  631. package/foundation/doc-compiler/doc-loads.test.mjs +1643 -0
  632. package/foundation/doc-compiler/import.d.mts +24 -0
  633. package/foundation/doc-compiler/import.mjs +59 -0
  634. package/foundation/doc-compiler/inputs.d.mts +102 -0
  635. package/foundation/doc-compiler/inputs.mjs +291 -0
  636. package/foundation/doc-compiler/inputs.test.mjs +298 -0
  637. package/foundation/doc-compiler/ir.d.mts +22 -0
  638. package/foundation/doc-compiler/ir.mjs +471 -0
  639. package/foundation/doc-compiler/lenses.d.mts +36 -0
  640. package/foundation/doc-compiler/lenses.mjs +173 -0
  641. package/foundation/doc-compiler/links.d.mts +162 -0
  642. package/foundation/doc-compiler/links.mjs +294 -0
  643. package/foundation/doc-compiler/links.test.mjs +192 -0
  644. package/foundation/doc-compiler/lower-doc.test.mjs +495 -0
  645. package/foundation/doc-compiler/overlays.d.mts +37 -0
  646. package/foundation/doc-compiler/overlays.mjs +206 -0
  647. package/foundation/doc-compiler/parse-readable.d.mts +9 -0
  648. package/foundation/doc-compiler/parse-readable.mjs +29 -0
  649. package/foundation/doc-compiler/read.d.mts +127 -0
  650. package/foundation/doc-compiler/read.mjs +325 -0
  651. package/foundation/doc-compiler/read.test.mjs +313 -0
  652. package/foundation/doc-compiler/source.d.mts +33 -0
  653. package/foundation/doc-compiler/source.mjs +128 -0
  654. package/foundation/doc-compiler/tree.d.mts +292 -0
  655. package/foundation/doc-compiler/tree.mjs +881 -0
  656. package/foundation/fs/file-protection.d.mts +33 -0
  657. package/foundation/fs/file-protection.mjs +825 -0
  658. package/foundation/fs/file-protection.test.mjs +250 -0
  659. package/foundation/fs/module-loader.d.mts +1 -0
  660. package/foundation/fs/module-loader.mjs +50 -1
  661. package/foundation/fs/module-loader.stdout.test.mjs +332 -0
  662. package/foundation/fs/path-safety.d.mts +3 -2
  663. package/foundation/fs/path-safety.mjs +49 -19
  664. package/foundation/fs/path-safety.test.mjs +50 -0
  665. package/foundation/fs/publish-file-hardlink-unavailable.test.mjs +129 -94
  666. package/foundation/identity/provider-identity.d.mts +90 -0
  667. package/foundation/identity/provider-identity.mjs +320 -0
  668. package/foundation/identity/provider-identity.test.mjs +254 -0
  669. package/foundation/identity/providers.d.mts +7 -0
  670. package/foundation/identity/providers.mjs +16 -0
  671. package/foundation/integrations/autolink.d.mts +58 -1
  672. package/foundation/integrations/autolink.mjs +143 -45
  673. package/foundation/integrations/autolink.test.mjs +1 -1
  674. package/foundation/integrations/cli-requirement.d.mts +65 -0
  675. package/foundation/integrations/cli-requirement.mjs +189 -0
  676. package/foundation/integrations/cli-requirement.test.mjs +89 -0
  677. package/foundation/integrations/contribution-fixes.d.mts +145 -0
  678. package/foundation/integrations/contribution-fixes.mjs +1284 -0
  679. package/foundation/integrations/contribution-inventory.d.mts +3 -2
  680. package/foundation/integrations/contribution-inventory.mjs +28 -25
  681. package/foundation/integrations/contribution-inventory.test.mjs +86 -27
  682. package/foundation/integrations/integration-warnings.d.mts +9 -2
  683. package/foundation/integrations/integration-warnings.mjs +52 -21
  684. package/foundation/integrations/integration-warnings.test.mjs +74 -1
  685. package/foundation/integrations/integrations.d.mts +63 -3
  686. package/foundation/integrations/integrations.mjs +122 -9
  687. package/foundation/integrations/integrations.test.mjs +415 -1
  688. package/foundation/integrations/provider-conflicts.test.mjs +125 -0
  689. package/foundation/integrations/provider-ledger.test.mjs +275 -0
  690. package/foundation/integrations/provider-resolution.d.mts +152 -0
  691. package/foundation/integrations/provider-resolution.mjs +576 -0
  692. package/foundation/integrations/provider-resolution.test.mjs +369 -0
  693. package/foundation/integrations/theme-descriptor.d.mts +8 -0
  694. package/foundation/integrations/theme-descriptor.mjs +44 -0
  695. package/foundation/integrations/validate-contributions.d.mts +2 -0
  696. package/foundation/integrations/validate-contributions.mjs +131 -29
  697. package/foundation/response/base.d.ts +8 -4
  698. package/foundation/response/batch.type.d.mts +33 -0
  699. package/foundation/response/batch.type.mjs +34 -0
  700. package/foundation/response/error-codes.d.mts +3 -1
  701. package/foundation/response/error-codes.d.ts +2 -0
  702. package/foundation/response/error-codes.doc.mjs +19 -12
  703. package/foundation/response/error-codes.mjs +8 -2
  704. package/foundation/response/error-codes.test.mjs +166 -14
  705. package/foundation/response/json-contract.test.mjs +57 -17
  706. package/foundation/response/json.d.mts +4 -2
  707. package/foundation/response/json.mjs +8 -10
  708. package/foundation/response/response-types.doc.d.mts +7 -2
  709. package/foundation/response/response-types.doc.mjs +69 -25
  710. package/foundation/response/response-types.doc.test.mjs +181 -0
  711. package/foundation/response/response.doc.mjs +12 -11
  712. package/foundation/text/string-utils.d.mts +8 -0
  713. package/foundation/text/string-utils.mjs +40 -10
  714. package/foundation/xle/expand.d.mts +2 -0
  715. package/foundation/xle/expand.mjs +4 -3
  716. package/foundation/xle/expand.test.mjs +54 -0
  717. package/foundation/xle/xle.test.mjs +13 -0
  718. package/package.json +10 -11
  719. package/api/docs/docs.test.mjs +0 -83
  720. package/api/docs/integrationDocs.test.mjs +0 -208
  721. package/api/search/search.test.mjs +0 -389
  722. package/assets/docs/cli-integrations.doc.mjs +0 -367
  723. package/assets/templates/themes/manifest.json +0 -95
  724. package/clients/cli/commands/docs.test.mjs +0 -102
  725. package/clients/cli/lib/update-check.mjs +0 -83
  726. package/clients/cli/lib/update-check.test.mjs +0 -137
  727. package/clients/cli/update-hint-commands.test.mjs +0 -54
  728. package/foundation/agent-docs/agent-docs.test.mjs +0 -1141
@@ -17,21 +17,43 @@
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
+ *
45
+ * A multi-word query has a reserved top tier (see {@link scoreQuery}): the
46
+ * whole query as a candidate's name or keyword (190-200), then the whole query
47
+ * as a phrase inside a doc's title or one of its headings (170), then a whole
48
+ * title of two words or more inside the query (160-169), then a candidate that
49
+ * matches every word of the query, at least one of them by name or keyword
50
+ * (151-159). Below those sits everything else: a partial match, or every word
51
+ * matched only in prose or through the components a page renders. A section titled "Light/Dark Mode" answers `dark
52
+ * mode` better than any doc that merely names `mode` in code, however exactly;
53
+ * "Dark mode" answers `how do I add dark mode`; and a guide whose title and
54
+ * description hold both words of `troubleshoot integration` answers it better
55
+ * than a doc named `integration`.
56
+ *
35
57
  * Description and guidance are separate tiers on purpose. A component's own
36
58
  * one-line description saying "notification" is a claim about what it IS; the
37
59
  * same word inside another component's best-practice advice is a passing
@@ -50,11 +72,11 @@
50
72
  * queries they have nothing to do with.
51
73
  */
52
74
 
53
- import {pathToFileURL} from 'node:url';
75
+ import {readDocView} from '../../foundation/doc-compiler/read.mjs';
54
76
  import {findCoreDir} from '../../foundation/fs/paths.mjs';
55
77
  import {
56
78
  discoverComponents,
57
- discoverIntegrationComponents,
79
+ discoverValidIntegrationComponents,
58
80
  findComponentReadme,
59
81
  resolveImportPath,
60
82
  resolveIntegrationImportPath,
@@ -66,7 +88,20 @@ import {
66
88
  import {loadIntegrationsSafely} from '../component/_adapter.mjs';
67
89
  import {levenshteinDistance} from '../../foundation/text/string-utils.mjs';
68
90
  import {discoverTemplates, extractComponents} from '../template/template.mjs';
69
- import {loadDocsCatalog, loadTopicDoc} from '../docs/_adapter.mjs';
91
+ import {templateLookupIds} from '../../foundation/discovery/template-adapter.mjs';
92
+ import {
93
+ guideEntry,
94
+ loadDocsCatalog,
95
+ lowerTopic,
96
+ projectTree,
97
+ holdsOwnName,
98
+ } from '../docs/_adapter.mjs';
99
+ import {unlinkText} from '../../foundation/doc-compiler/links.mjs';
100
+ import {nodeView} from '../docs/node/node.mjs';
101
+ import {
102
+ sectionKey,
103
+ sectionSummary,
104
+ } from '../../foundation/discovery/docs-section-key.mjs';
70
105
  import {AstryxError} from '../error.mjs';
71
106
  import {ERROR_CODES} from '../../foundation/response/error-codes.mjs';
72
107
  import {setResultCoverage} from './coverage.mjs';
@@ -82,19 +117,33 @@ import {setResultCoverage} from './coverage.mjs';
82
117
  * @property {string} [description]
83
118
  * @property {string[]} [prose]
84
119
  * @property {string[]} [guidance]
120
+ * @property {string[]} [titles] - A doc's title and the headings inside it:
121
+ * the lines a reader scans to pick it. The whole query standing in one of
122
+ * them, or one of them standing whole in the query, is a top-tier match.
85
123
  * @property {string} [_import]
86
124
  * @property {string} [_title]
125
+ * @property {string} [_topic] - A doc result's topic or docs-tree route.
126
+ * @property {string} [_section] - A doc result's section key, when it is one section.
127
+ * @property {string} [_command] - The command that reads exactly this doc part.
128
+ * @property {string} [_parent] - The command that opens the level above a doc
129
+ * part: its topic's section list, or the docs-tree namespace it sits in.
130
+ * @property {string} [_package] - The npm package that authored a docs-tree
131
+ * doc part. Flat topics carry none: an extension's sections can come from
132
+ * another package.
87
133
  * @property {string} [_displayName]
88
134
  * @property {'page'|'block'} [_kind]
135
+ * @property {string} [_resultName]
136
+ * @property {string} [_commandName]
89
137
  */
90
138
 
91
139
  /**
92
140
  * Synonym / intent map: product-language terms an agent is likely to type,
93
141
  * expanded to the catalog's vocabulary so oblique queries still rank. Keys and
94
142
  * values are matched bidirectionally (typing any value also pulls in the key
95
- * and its siblings). Lowercase, single words or short phrases.
143
+ * and its siblings). Lowercase, single words or short phrases. Exported for
144
+ * `build`, whose page ranker expands a query with the same vocabulary.
96
145
  */
97
- const SYNONYMS = {
146
+ export const SYNONYMS = {
98
147
  dashboard: [
99
148
  'overview',
100
149
  'analytics',
@@ -166,14 +215,90 @@ export function stem(w) {
166
215
  return s;
167
216
  }
168
217
 
218
+ /**
219
+ * The forms of a word that count as the same word: itself, its stem, and its
220
+ * singular when it ends in a plural suffix — so "tables" is "table",
221
+ * "statuses" is "status", and "filtering" is "filter".
222
+ * @param {string} w - Lowercase word.
223
+ * @returns {Set<string>}
224
+ */
225
+ function wordForms(w) {
226
+ const forms = new Set([w, stem(w)]);
227
+ if (w.length > 3 && w.endsWith('s')) forms.add(w.slice(0, -1));
228
+ if (w.length > 4 && w.endsWith('es')) forms.add(w.slice(0, -2));
229
+ if (w.length > 4 && w.endsWith('ies')) forms.add(w.slice(0, -3) + 'y');
230
+ return forms;
231
+ }
232
+
233
+ /**
234
+ * Whether two lowercase words are the same word, up to plural and stem form.
235
+ * @param {string} a
236
+ * @param {string} b
237
+ * @returns {boolean}
238
+ */
239
+ export function sameWord(a, b) {
240
+ if (a === b) return true;
241
+ const forms = wordForms(a);
242
+ for (const f of wordForms(b)) if (forms.has(f)) return true;
243
+ return false;
244
+ }
245
+
246
+ /**
247
+ * The lowercase words of a name or keyword: split at non-alphanumerics and at
248
+ * camelCase boundaries, so "TextInput" is ["text", "input"] and
249
+ * "Dashboard - Analytics" is ["dashboard", "analytics"].
250
+ * @param {string} text
251
+ * @returns {string[]}
252
+ */
253
+ function wordsOf(text) {
254
+ return String(text)
255
+ .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
256
+ .replace(/([A-Z])([A-Z][a-z])/g, '$1 $2')
257
+ .toLowerCase()
258
+ .split(/[^a-z0-9]+/)
259
+ .filter(Boolean);
260
+ }
261
+
262
+ /**
263
+ * Whether a term is found inside a name or keyword: it is one of its words, or
264
+ * the start of one (a truncation), and covers at least half of the whole
265
+ * string. Four letters minimum, so a short term never matches by accident.
266
+ * @param {string} term - Lowercase term.
267
+ * @param {string} text - The name or keyword as authored.
268
+ * @returns {boolean}
269
+ */
270
+ function startsAWordOf(term, text) {
271
+ if (term.length < 4) return false;
272
+ if (term.length / String(text).length < 0.5) return false;
273
+ return wordsOf(text).some(w => w.startsWith(term) || sameWord(term, w));
274
+ }
275
+
276
+ /**
277
+ * The fewest letters both words need before an edit distance counts as a
278
+ * typo, by distance. Below them, one edit usually makes a different word.
279
+ */
280
+ const TYPO_MIN_LENGTH = {1: 5, 2: 8, 3: 11};
281
+
282
+ /**
283
+ * @param {string} a
284
+ * @param {string} b
285
+ * @param {number} dist
286
+ */
287
+ const isTypo = (a, b, dist) =>
288
+ dist > 0 &&
289
+ dist <= 3 &&
290
+ Math.min(a.length, b.length) >=
291
+ TYPO_MIN_LENGTH[/** @type {1 | 2 | 3} */ (dist)];
292
+
169
293
  /** Valid domain filters for `--type`. */
170
294
  export const SEARCH_DOMAINS = ['component', 'hook', 'doc', 'template'];
171
295
 
172
296
  /**
173
297
  * Filler words stripped from multi-word queries so natural-language phrasing
174
298
  * ("a page where you can see business stats") ranks on its content words.
299
+ * Exported for `build`, whose page ranker strips the same words.
175
300
  */
176
- const STOPWORDS = new Set([
301
+ export const STOPWORDS = new Set([
177
302
  'a',
178
303
  'an',
179
304
  'the',
@@ -285,14 +410,15 @@ const MIN_TOKEN_SCORE = 50;
285
410
  * (synonym hits are discounted so a direct hit always wins).
286
411
  * @param {string} tok
287
412
  * @param {Candidate} candidate
413
+ * @param {{fuzzy?: boolean}} [opts]
288
414
  * @returns {{score: number, reason: string} | null}
289
415
  */
290
- function bestForToken(tok, candidate) {
291
- let best = scoreCandidate(tok, candidate);
416
+ function bestForToken(tok, candidate, opts = {}) {
417
+ let best = scoreCandidate(tok, candidate, opts);
292
418
  const syns = SYNONYM_INDEX.get(tok);
293
419
  if (syns) {
294
420
  for (const s of syns) {
295
- const h = scoreCandidate(s, candidate);
421
+ const h = scoreCandidate(s, candidate, opts);
296
422
  if (h) {
297
423
  const score = Math.round(h.score * 0.85);
298
424
  if (!best || score > best.score)
@@ -303,6 +429,120 @@ function bestForToken(tok, candidate) {
303
429
  return best;
304
430
  }
305
431
 
432
+ /**
433
+ * The score of a whole-query phrase inside a doc's title or heading: a keyword
434
+ * substring hit (70) promoted by the same 100 as the exact tier. Below an
435
+ * exact name or keyword (190-200), above the token-sum path (~151 at most).
436
+ */
437
+ const TITLE_PHRASE_SCORE = 170;
438
+
439
+ /**
440
+ * The score of a whole title inside a longer query, before its coverage bonus:
441
+ * one step below {@link TITLE_PHRASE_SCORE}. The bonus (one per query term the
442
+ * candidate matches, at most 9) orders the sections that share a common title,
443
+ * so "best practices for spacing" puts Spacing's Best Practices first.
444
+ */
445
+ const TITLE_IN_QUERY_SCORE = 160;
446
+
447
+ /**
448
+ * The score of a candidate that matches every content word of a multi-word
449
+ * query, before a bonus of up to 8 for how strong its strongest match is: just
450
+ * above anything that matches only some of the words. The token-sum path tops
451
+ * out near 150 for a partial match (a 100 on one word, the per-word bonus, and
452
+ * the coverage term), so an AND-match with one keyword-strength hit (see
453
+ * {@link STRONG_TOKEN_SCORE}) always outranks an OR-match, and stays below the
454
+ * title tiers.
455
+ */
456
+ const FULL_COVERAGE_SCORE = 151;
457
+
458
+ /**
459
+ * The strongest single-word hit an every-word match needs to take that tier: a
460
+ * keyword substring. Two passing mentions in prose, or the components a page
461
+ * happens to render, are breadth, not relevance; they stay on the token sum,
462
+ * below an exact name or keyword hit on one of the words.
463
+ */
464
+ const STRONG_TOKEN_SCORE = 70;
465
+
466
+ /**
467
+ * The words of a title or query, lowercased, without punctuation or code ticks.
468
+ * @param {string} text
469
+ * @returns {string[]}
470
+ */
471
+ function phraseWords(text) {
472
+ return unlinkText(text).toLowerCase().match(/[a-z0-9]+/g) ?? [];
473
+ }
474
+
475
+ /**
476
+ * Whether two words are the same word, allowing a plural on either side, so
477
+ * `data attributes selector` still reads "Data attribute selectors".
478
+ * @param {string} a
479
+ * @param {string} b
480
+ */
481
+ function samePhraseWord(a, b) {
482
+ return (
483
+ a === b ||
484
+ `${a}s` === b ||
485
+ `${b}s` === a ||
486
+ `${a}es` === b ||
487
+ `${b}es` === a
488
+ );
489
+ }
490
+
491
+ /**
492
+ * Whether `plural` is the plural of `word`: `integrations` of `integration`,
493
+ * `boxes` of `box`. `es` only follows s, x, z, ch, or sh, so `notes` is not a
494
+ * plural of `not`.
495
+ * @param {string} plural
496
+ * @param {string} word
497
+ */
498
+ function pluralOf(plural, word) {
499
+ if (word.length < 3) return false;
500
+ if (plural === `${word}s`) return true;
501
+ return /(?:s|x|z|ch|sh)$/.test(word) && plural === `${word}es`;
502
+ }
503
+
504
+ /**
505
+ * The first title or heading that holds every word of the query, in order and
506
+ * side by side, or null.
507
+ * @param {string} term - Lowercased full query.
508
+ * @param {string[] | undefined} titles
509
+ * @returns {string | null}
510
+ */
511
+ export function headingWithPhrase(term, titles) {
512
+ const query = phraseWords(term);
513
+ if (query.length < 2 || !titles) return null;
514
+ for (const title of titles) {
515
+ const words = phraseWords(String(title ?? ''));
516
+ for (let i = 0; i + query.length <= words.length; i++) {
517
+ if (query.every((word, j) => samePhraseWord(words[i + j], word)))
518
+ return title;
519
+ }
520
+ }
521
+ return null;
522
+ }
523
+
524
+ /**
525
+ * The first title or heading of two words or more that the query holds whole,
526
+ * in order and side by side, or null. A question such as "how do I add dark
527
+ * mode" names the "Dark mode" section outright, around words no title has.
528
+ * @param {string} term - Lowercased full query.
529
+ * @param {string[] | undefined} titles
530
+ * @returns {string | null}
531
+ */
532
+ export function titleInQuery(term, titles) {
533
+ const query = phraseWords(term);
534
+ if (!titles) return null;
535
+ for (const title of titles) {
536
+ const words = phraseWords(String(title ?? ''));
537
+ if (words.length < 2 || words.length > query.length) continue;
538
+ for (let i = 0; i + words.length <= query.length; i++) {
539
+ if (words.every((word, j) => samePhraseWord(query[i + j], word)))
540
+ return title;
541
+ }
542
+ }
543
+ return null;
544
+ }
545
+
306
546
  /**
307
547
  * @param {string} term - Lowercased full query.
308
548
  * @param {string[]} tokens - Content tokens from tokenizeQuery(term).
@@ -322,17 +562,26 @@ export function scoreQuery(term, tokens, candidate) {
322
562
  matched: total,
323
563
  total,
324
564
  });
325
- const full = scoreCandidate(term, candidate);
565
+ // Typo tolerance is for one-word lookups. In a multi-word query a near miss
566
+ // is usually a different word, not a typo.
567
+ const fuzzy = tokens.length <= 1;
568
+ const full = scoreCandidate(term, candidate, {fuzzy});
569
+ // A query of several words keeps its phrase tiers below even when stopwords
570
+ // leave one content word: "make an integration" is still the phrase an
571
+ // author declares as a keyword, and "build an integration" still names a
572
+ // title outright, though each tokenizes to `integration` alone.
573
+ const phrase = phraseWords(term).length >= 2;
326
574
 
327
- // 0–1 content tokens: keep whole-phrase fuzzy matching (typo tolerance for
328
- // single words), but if stopwords left exactly one DIFFERENT token (e.g.
329
- // "pricing page" → "pricing"), score that token too and take the stronger.
330
- if (tokens.length <= 1) {
575
+ /** 0–1 content tokens: whole-phrase fuzzy matching (typo tolerance for
576
+ * single words), but if stopwords left exactly one DIFFERENT token (e.g.
577
+ * "pricing page" → "pricing"), score that token too and take the stronger. */
578
+ const fewTokens = () => {
331
579
  const single =
332
- tokens.length === 1 ? bestForToken(tokens[0], candidate) : null;
580
+ tokens.length === 1 ? bestForToken(tokens[0], candidate, {fuzzy}) : null;
333
581
  if (full && (!single || full.score >= single.score)) return asFull(full);
334
582
  return single ? asFull(single) : null;
335
- }
583
+ };
584
+ if (tokens.length <= 1 && !phrase) return fewTokens();
336
585
 
337
586
  // The full (untokenized) query matching a candidate's name or a declared
338
587
  // keyword VERBATIM — full.score 90 or 100, the only two scoreCandidate
@@ -349,6 +598,22 @@ export function scoreQuery(term, tokens, candidate) {
349
598
  return asFull({score: full.score + 100, reason: full.reason});
350
599
  }
351
600
 
601
+ // The whole query standing as a phrase in a doc's title or one of its
602
+ // headings is the next tier down, and still above the token-sum path. The
603
+ // reader named what the section is about, in order: `dark mode` is the
604
+ // "Light/Dark Mode" section. Without this, the title scores a keyword
605
+ // substring (70) and loses to a doc that happens to name `mode` exactly in
606
+ // a code tick (90 on one token, 98 with coverage), so API enum docs outrank
607
+ // the guide section.
608
+ const heading = headingWithPhrase(term, candidate.titles);
609
+ if (heading != null) {
610
+ return asFull({
611
+ score: TITLE_PHRASE_SCORE,
612
+ reason: `title "${heading}" holds the whole query`,
613
+ });
614
+ }
615
+ if (tokens.length <= 1) return fewTokens();
616
+
352
617
  // Multi-word natural language: score each content token, counting only
353
618
  // strong hits, then reward coverage so candidates matching more terms win.
354
619
  let strongest = 0;
@@ -356,15 +621,49 @@ export function scoreQuery(term, tokens, candidate) {
356
621
  /** @type {string[]} */
357
622
  const hitTerms = [];
358
623
  for (const tok of tokens) {
359
- const h = bestForToken(tok, candidate);
624
+ const h = bestForToken(tok, candidate, {fuzzy});
360
625
  if (h && h.score >= MIN_TOKEN_SCORE) {
361
626
  if (h.score > strongest) strongest = h.score;
362
627
  matched++;
363
628
  hitTerms.push(tok);
364
629
  }
365
630
  }
631
+ // The reverse of the title tier, a step lower: the query holds a whole title
632
+ // of two words or more, so the reader asked a question around the section's
633
+ // name ("how do I add dark mode"). Coverage breaks ties between sections
634
+ // that share a title such as "Best Practices".
635
+ const named = titleInQuery(term, candidate.titles);
636
+ if (named != null) {
637
+ return {
638
+ score: TITLE_IN_QUERY_SCORE + Math.min(matched, 9),
639
+ reason: `the query names the title "${named}"`,
640
+ matched,
641
+ total,
642
+ };
643
+ }
366
644
  if (matched === 0) return full ? asFull(full) : null;
367
645
 
646
+ const reason = `matches ${matched}/${tokens.length} terms: ${hitTerms.join(', ')}`;
647
+
648
+ // Every word matched is its own tier. Summed per word, a doc that matches
649
+ // both words of `troubleshoot integration` in its title and description
650
+ // (50 + bonus + coverage = 77) lost to thirty docs that each match
651
+ // `integration` alone, by name or in a code tick (98-108). The reader asked for
652
+ // both; a candidate that has both comes first, ordered among its peers by
653
+ // how strong its strongest match is. It needs one keyword-strength hit:
654
+ // every word mentioned in prose, or rendered by a page, is breadth, and
655
+ // stays on the token sum below an exact hit on one word.
656
+ if (matched === tokens.length && strongest >= STRONG_TOKEN_SCORE) {
657
+ return {
658
+ score:
659
+ FULL_COVERAGE_SCORE +
660
+ Math.floor((strongest - MIN_TOKEN_SCORE) / 6.25),
661
+ reason,
662
+ matched,
663
+ total,
664
+ };
665
+ }
666
+
368
667
  // Base the score on the STRONGEST concept that matched, plus a bonus per
369
668
  // additional matched concept and a coverage term.
370
669
  //
@@ -386,14 +685,8 @@ export function scoreQuery(term, tokens, candidate) {
386
685
  const tokenScore = Math.round(
387
686
  strongest + Math.min(matched - 1, 3) * 12 + coverage * 15,
388
687
  );
389
-
390
688
  if (full && full.score >= tokenScore) return asFull(full);
391
- return {
392
- score: tokenScore,
393
- reason: `matches ${matched}/${tokens.length} terms: ${hitTerms.join(', ')}`,
394
- matched,
395
- total,
396
- };
689
+ return {score: tokenScore, reason, matched, total};
397
690
  }
398
691
 
399
692
  /**
@@ -404,23 +697,28 @@ export function scoreQuery(term, tokens, candidate) {
404
697
  * @param {string} term - Lowercased search term.
405
698
  * @param {object} candidate
406
699
  * @param {string} candidate.name - Primary identifier (component/hook name, topic, template name).
700
+ * @param {string} [candidate.domain] - A component, hook, or template name
701
+ * also matches typed as words: `command palette` is CommandPalette.
407
702
  * @param {string[]} [candidate.keywords] - Authored intent (componentsUsed, category words).
408
703
  * @param {string[]} [candidate.weakKeywords] - Derived signal (components a page renders).
409
704
  * @param {string} [candidate.description]
410
705
  * @param {string[]} [candidate.prose] - Extra free-text blobs (doc section text, best practices).
411
706
  * @param {string[]} [candidate.guidance] - Usage guidance (features, best practices) — scored a tier below description.
707
+ * @param {{fuzzy?: boolean}} [opts] - `fuzzy`: allow edit-distance (typo) matches. Default true; multi-word queries pass false.
412
708
  * @returns {{score: number, reason: string} | null}
413
709
  */
414
710
  export function scoreCandidate(
415
711
  term,
416
712
  {
417
713
  name,
714
+ domain,
418
715
  keywords = [],
419
716
  weakKeywords = [],
420
717
  description = '',
421
718
  prose = [],
422
719
  guidance = [],
423
720
  },
721
+ {fuzzy = true} = {},
424
722
  ) {
425
723
  let best = 0;
426
724
  let reason = '';
@@ -436,25 +734,41 @@ export function scoreCandidate(
436
734
  };
437
735
 
438
736
  const nameLower = name.toLowerCase();
737
+ // A placed guide's name is its route, and the route's last segment is its
738
+ // name too, as a flat topic's is: `codemods` is cli/integrations/codemods.
739
+ const leafLower = nameLower.slice(nameLower.lastIndexOf('/') + 1);
439
740
 
440
741
  // ── Name signals ────────────────────────────────────────────────
441
- if (nameLower === term) {
742
+ // A plural of the name is the name: `integration` is the `integrations`
743
+ // guides, `tab` the `tabs` doc.
744
+ // A component, hook, or template name typed as words is its name:
745
+ // `command palette` is CommandPalette. A doc's name is a route or key,
746
+ // matched as written.
747
+ const spelled =
748
+ domain !== 'doc' &&
749
+ !/[\s_-]/.test(nameLower) &&
750
+ nameLower === term.replace(/\s+/g, '');
751
+ if (nameLower === term || leafLower === term || spelled) {
442
752
  consider(100, 'exact name');
753
+ } else if (pluralOf(nameLower, term) || pluralOf(term, nameLower)) {
754
+ // One point under the exact spelling, so the doc named `tokens` still
755
+ // outranks the Token component for `tokens`.
756
+ consider(99, 'plural of the name');
443
757
  } 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}"`);
758
+ if (sameWord(term, nameLower)) consider(95, `name "${name}"`);
759
+ // The term is a word of the name, or starts one: "input" in TextInput.
760
+ else if (startsAWordOf(term, name)) {
761
+ consider(60, `name contains "${term}"`);
762
+ }
763
+ if (fuzzy) {
764
+ const dist = levenshteinDistance(term, nameLower);
765
+ if (isTypo(term, nameLower, dist)) {
766
+ consider(
767
+ dist === 1 ? 80 : dist === 2 ? 40 : 20,
768
+ `similar name (distance ${dist})`,
769
+ );
770
+ }
453
771
  }
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
772
  }
459
773
 
460
774
  // ── Keyword signals ─────────────────────────────────────────────
@@ -464,14 +778,17 @@ export function scoreCandidate(
464
778
  consider(90, `keyword "${kw}"`);
465
779
  continue;
466
780
  }
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}"`);
781
+ if (sameWord(term, kwLower)) {
782
+ consider(88, `keyword "${kw}"`);
783
+ continue;
784
+ }
785
+ if (startsAWordOf(term, kw)) consider(70, `keyword "${kw}"`);
786
+ if (fuzzy) {
787
+ const dist = levenshteinDistance(term, kwLower);
788
+ if (isTypo(term, kwLower, dist) && dist <= 2) {
789
+ consider(dist === 1 ? 70 : 30, `keyword "${kw}" (distance ${dist})`);
790
+ }
471
791
  }
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
792
  }
476
793
 
477
794
  // ── Weak keyword signals (derived, not authored) ─────────────────
@@ -481,15 +798,11 @@ export function scoreCandidate(
481
798
  // No Levenshtein tier — fuzzy matching a derived signal is pure noise.
482
799
  for (const kw of weakKeywords) {
483
800
  const kwLower = String(kw).toLowerCase();
484
- if (kwLower === term) {
801
+ if (kwLower === term || sameWord(term, kwLower)) {
485
802
  consider(60, `renders ${kw}`);
486
803
  continue;
487
804
  }
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
- }
805
+ if (startsAWordOf(term, kw)) consider(40, `renders ${kw}`);
493
806
  }
494
807
 
495
808
  // ── Prose / description / guidance signals (stem-tolerant whole word) ──
@@ -528,16 +841,26 @@ export function scoreCandidate(
528
841
  }
529
842
 
530
843
  /**
531
- * Load a doc module's `docs`/`doc` export, swallowing errors.
844
+ * A component or hook doc, compiled, or null when it cannot be read.
532
845
  * @param {string} docPath
533
846
  * @param {string} [exportName]
847
+ * @param {'components' | 'hooks'} [root]
534
848
  * @returns {Promise<any>}
535
849
  */
536
- async function loadModuleDoc(docPath, exportName = 'docs') {
850
+ async function loadModuleDoc(
851
+ docPath,
852
+ exportName = 'docs',
853
+ root = 'components',
854
+ ) {
537
855
  try {
538
- const mod = await import(pathToFileURL(docPath).href);
539
856
  // Support both the stamped default export and the legacy named export.
540
- return mod?.default ?? mod[exportName] ?? null;
857
+ return (
858
+ (await readDocView(docPath, {
859
+ root,
860
+ loader: 'native',
861
+ exports: ['default', exportName],
862
+ })) ?? null
863
+ );
541
864
  } catch {
542
865
  return null;
543
866
  }
@@ -637,7 +960,8 @@ async function gatherIntegrationComponents(cwd) {
637
960
  /** @type {Candidate[]} */
638
961
  const candidates = [];
639
962
  for (const integration of loadedIntegrations) {
640
- for (const rec of discoverIntegrationComponents(integration)) {
963
+ const {components} = await discoverValidIntegrationComponents(integration);
964
+ for (const rec of components) {
641
965
  const doc = await loadModuleDoc(rec.docPath);
642
966
  candidates.push({
643
967
  domain: 'component',
@@ -701,7 +1025,7 @@ async function gatherHooks(coreDir) {
701
1025
  let description = '';
702
1026
  let importPath = '@astryxdesign/core/hooks';
703
1027
  if (docPath) {
704
- const doc = await loadModuleDoc(docPath);
1028
+ const doc = await loadModuleDoc(docPath, 'docs', 'hooks');
705
1029
  if (doc) {
706
1030
  keywords = Array.isArray(doc.keywords) ? doc.keywords : [];
707
1031
  description = doc.usage?.description || doc.description || '';
@@ -720,7 +1044,11 @@ async function gatherHooks(coreDir) {
720
1044
  }
721
1045
 
722
1046
  /**
723
- * Build doc-topic candidates: topic name + description + section prose.
1047
+ * Build doc candidates at the grain a reader reads them: each section of a
1048
+ * topic, whose command reads just that section; each topic as a whole, whose
1049
+ * command lists its sections; and each docs-tree node by its route. The tree's
1050
+ * guides split into sections like topics, and its typed docs also match by
1051
+ * their own name, so `assertResponse` finds `cli/api/functions/assert-response`.
724
1052
  *
725
1053
  * Reads the project's catalog rather than the CLI's own docs directory, so a
726
1054
  * topic an integration contributed (or replaced) is searchable exactly like a
@@ -732,44 +1060,287 @@ async function gatherHooks(coreDir) {
732
1060
  async function gatherDocs(cwd) {
733
1061
  /** @type {Candidate[]} */
734
1062
  const candidates = [];
735
- let entries;
1063
+ let catalog;
736
1064
  try {
737
- entries = (await loadDocsCatalog(cwd)).entries();
1065
+ catalog = await loadDocsCatalog(cwd);
738
1066
  } catch {
739
1067
  return candidates;
740
1068
  }
741
- for (const entry of entries) {
742
- let doc = null;
1069
+ let tree = null;
1070
+ try {
1071
+ tree = await projectTree(catalog);
1072
+ } catch {
1073
+ // `astryx doctor` reports a tree that fails to build; search still
1074
+ // indexes the topics.
1075
+ }
1076
+ for (const entry of catalog.entries()) {
1077
+ // A topic whose name opens another doc (spec:AST-046 FR11) is not
1078
+ // offered: every hit's command must open the hit.
1079
+ if (tree && !holdsOwnName(tree, catalog, entry)) continue;
1080
+ let lowered = null;
743
1081
  try {
744
- doc = await loadTopicDoc(entry);
1082
+ lowered = await lowerTopic(catalog, entry);
745
1083
  } catch {
746
1084
  // A topic that cannot be loaded is reported by the commands that own
747
1085
  // integration issues; search just cannot index it.
748
1086
  }
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
- }
1087
+ const doc = lowered?.doc ?? null;
1088
+ // A flat topic lives in the Unorganized level; its hits say so, and name
1089
+ // the package each section came from.
1090
+ const home = tree?.get(entry.name);
1091
+ const placed = home?.ref?.flatTopic === entry.name ? home : null;
1092
+ /** @type {Map<string, string>} */
1093
+ const packages = new Map([
1094
+ [entry.providerId ?? entry.package, entry.package],
1095
+ ...entry.extensions.map(
1096
+ ext => /** @type {[string, string]} */ ([ext.providerId ?? ext.package, ext.package]),
1097
+ ),
1098
+ ]);
1099
+ candidates.push(
1100
+ ...topicCandidates(
1101
+ entry.name,
1102
+ doc,
1103
+ entry.title,
1104
+ '',
1105
+ placed && tree
1106
+ ? [
1107
+ ...tree.ancestors(placed).map(a => a.title),
1108
+ doc?.title || entry.title || entry.name,
1109
+ ].join(' › ')
1110
+ : undefined,
1111
+ placed ? `astryx docs ${placed.parent}` : undefined,
1112
+ entry.package,
1113
+ key =>
1114
+ packages.get(lowered?.sectionProviders?.[key] ?? '') ?? entry.package,
1115
+ ),
1116
+ );
1117
+ }
1118
+ if (tree == null) return candidates;
1119
+ for (const node of tree.nodes.values()) {
1120
+ // A flat topic is indexed above, as a topic.
1121
+ if (node.ref?.flatTopic) continue;
1122
+ // A tree hit names where it lives: its ancestors' titles, then its own.
1123
+ const path = [...tree.ancestors(node).map(a => a.title), node.title];
1124
+ if (node.kind === 'generic') {
1125
+ let doc = null;
1126
+ try {
1127
+ doc = (await lowerTopic(catalog, guideEntry(node))).doc;
1128
+ } catch {
1129
+ // As above: the owning commands report it.
759
1130
  }
1131
+ candidates.push(
1132
+ ...topicCandidates(
1133
+ node.route,
1134
+ doc,
1135
+ node.title,
1136
+ node.summary,
1137
+ path.join(' › '),
1138
+ node.parent == null ? undefined : `astryx docs ${node.parent}`,
1139
+ node.provider,
1140
+ ),
1141
+ );
1142
+ continue;
1143
+ }
1144
+ const selfDoc = /** @type {any} */ (node.ref)?.selfDoc;
1145
+ // A typed doc's content is what `astryx docs <route>` prints. The first
1146
+ // column of its tables names what the doc defines (an error code, an
1147
+ // option, a parameter), so each is a keyword the doc answers to.
1148
+ /** @type {any[]} */
1149
+ const content = (await nodeView(catalog, tree, node)).content ?? [];
1150
+ /** @type {string[]} */
1151
+ const defined = [];
1152
+ for (const block of content) {
1153
+ if (block.type !== 'table' || !Array.isArray(block.rows)) continue;
1154
+ for (const row of block.rows)
1155
+ if (row[0] != null) defined.push(plain(row[0]));
760
1156
  }
761
1157
  candidates.push({
762
1158
  domain: 'doc',
763
- name: entry.name,
764
- keywords: [],
765
- description,
766
- prose,
767
- _title: doc?.title || entry.title || entry.name,
1159
+ name: node.name,
1160
+ keywords: [
1161
+ node.route.slice(node.route.lastIndexOf('/') + 1),
1162
+ ...(Array.isArray(selfDoc?.keywords) ? selfDoc.keywords : []),
1163
+ // A namespace doc's own keywords, which it declares for search.
1164
+ ...(Array.isArray(node.keywords) ? node.keywords : []),
1165
+ ...defined,
1166
+ ...codeTerms({content}),
1167
+ ],
1168
+ description: node.summary || '',
1169
+ prose: sectionProse({title: node.title, content}),
1170
+ titles: [node.title],
1171
+ _topic: node.route,
1172
+ _title: path.join(' › '),
1173
+ _command: `astryx docs ${node.route}`,
1174
+ _parent: node.parent == null ? 'astryx docs' : `astryx docs ${node.parent}`,
1175
+ _package: node.provider,
768
1176
  });
769
1177
  }
770
1178
  return candidates;
771
1179
  }
772
1180
 
1181
+ /**
1182
+ * The words one section says: its prose, headings, and list items.
1183
+ * @param {any} section
1184
+ * @returns {string[]}
1185
+ */
1186
+ function sectionProse(section) {
1187
+ /** @type {string[]} */
1188
+ const prose = [];
1189
+ if (section?.title) prose.push(section.title);
1190
+ for (const block of section?.content || []) {
1191
+ if ((block.type === 'prose' || block.type === 'heading') && block.text) {
1192
+ prose.push(block.text);
1193
+ } else if (block.type === 'list' && Array.isArray(block.items)) {
1194
+ for (const item of block.items) {
1195
+ const text = typeof item === 'string' ? item : item?.text;
1196
+ if (typeof text === 'string') prose.push(text);
1197
+ }
1198
+ } else if (block.type === 'table' && Array.isArray(block.rows)) {
1199
+ for (const row of block.rows) prose.push(row.map(plain).join(' '));
1200
+ } else if (block.type === 'code' && typeof block.code === 'string') {
1201
+ prose.push([block.label, block.code].filter(Boolean).join(' '));
1202
+ }
1203
+ }
1204
+ return prose;
1205
+ }
1206
+
1207
+ /**
1208
+ * The identifiers a doc part names in code ticks (`token-ref`,
1209
+ * `ERR_UNKNOWN_SECTION`). Each is a keyword: a reader who types one exactly
1210
+ * wants the part that defines or explains it.
1211
+ * @param {any} part - a section, or `{content}` of a typed doc
1212
+ * @returns {string[]}
1213
+ */
1214
+ function codeTerms(part) {
1215
+ /** @type {Set<string>} */
1216
+ const terms = new Set();
1217
+ /** @param {unknown} text */
1218
+ const scan = text => {
1219
+ for (const m of String(text ?? '').matchAll(/`([^`\s]{2,40})`/g)) {
1220
+ terms.add(m[1]);
1221
+ }
1222
+ };
1223
+ for (const block of part?.content || []) {
1224
+ if (block.type === 'prose') scan(block.text);
1225
+ else if (block.type === 'list' && Array.isArray(block.items)) {
1226
+ for (const item of block.items) {
1227
+ scan(typeof item === 'string' ? item : item?.text);
1228
+ }
1229
+ } else if (block.type === 'table' && Array.isArray(block.rows)) {
1230
+ for (const row of block.rows) for (const cell of row) scan(cell);
1231
+ }
1232
+ }
1233
+ return [...terms];
1234
+ }
1235
+
1236
+ /**
1237
+ * The headings inside a section. Each names a subsection, so a query that
1238
+ * names one should find the section as surely as one that names its title.
1239
+ * @param {any} section
1240
+ * @returns {string[]}
1241
+ */
1242
+ function headings(section) {
1243
+ return (section?.content || [])
1244
+ .filter(
1245
+ (/** @type {any} */ block) => block.type === 'heading' && block.text,
1246
+ )
1247
+ .map((/** @type {any} */ block) => String(block.text));
1248
+ }
1249
+
1250
+ /**
1251
+ * A table cell as plain words, without its code ticks.
1252
+ * @param {unknown} cell
1253
+ * @returns {string}
1254
+ */
1255
+ function plain(cell) {
1256
+ return unlinkText(String(cell ?? '')).replaceAll('`', '');
1257
+ }
1258
+
1259
+ /**
1260
+ * The candidates one topic yields: the topic itself, and one per section when
1261
+ * it has more than one. A topic's command lists its sections, and a section's
1262
+ * command reads only that section, so a hit never costs a whole-topic read.
1263
+ * @param {string} name - the topic name, or a placed guide's route
1264
+ * @param {any} doc - the lowered topic, or null when it did not load
1265
+ * @param {string} [title]
1266
+ * @param {string} [summary]
1267
+ * @param {string} [path] - where the topic lives in the docs tree, as titles
1268
+ * joined by ` › `; a flat topic is its own title
1269
+ * @param {string} [parent] - the command that opens the level above the
1270
+ * topic: its namespace, or the Unorganized level for a flat topic
1271
+ * @param {string} [pkg] - the npm package that authored the topic
1272
+ * @param {(key: string) => string} [sectionPackage] - the npm package a
1273
+ * section came from: an extension's section names the extension's package
1274
+ * @returns {Candidate[]}
1275
+ */
1276
+ function topicCandidates(
1277
+ name,
1278
+ doc,
1279
+ title,
1280
+ summary = '',
1281
+ path,
1282
+ parent,
1283
+ pkg,
1284
+ sectionPackage,
1285
+ ) {
1286
+ /** @type {any[]} */
1287
+ const sections = doc?.sections ?? [];
1288
+ const docTitle = path || doc?.title || title || name;
1289
+ const split = sections.length > 1;
1290
+ // A placed guide also answers to its last route segment's words:
1291
+ // `quick start` is cli/integrations/quick-start.
1292
+ const leaf = name.slice(name.lastIndexOf('/') + 1);
1293
+ /** @type {Candidate[]} */
1294
+ const out = [
1295
+ {
1296
+ domain: 'doc',
1297
+ name,
1298
+ keywords: [
1299
+ ...(leaf !== name ? [leaf.replaceAll('-', ' ')] : []),
1300
+ ...(doc?.title || title ? [doc?.title || title] : []),
1301
+ ...(Array.isArray(doc?.keywords) ? doc.keywords : []),
1302
+ ],
1303
+ description: doc?.description || summary,
1304
+ prose: split
1305
+ ? sections.map(section => section.title).filter(Boolean)
1306
+ : sections.flatMap(sectionProse),
1307
+ titles: [
1308
+ doc?.title || title || name,
1309
+ // A topic read whole answers for the headings inside it.
1310
+ ...(split ? [] : sections.flatMap(s => [s.title, ...headings(s)])),
1311
+ ].filter(Boolean),
1312
+ _topic: name,
1313
+ _title: docTitle,
1314
+ _command: split ? `astryx docs ${name} --index` : `astryx docs ${name}`,
1315
+ ...(parent ? {_parent: parent} : {}),
1316
+ ...(pkg ? {_package: pkg} : {}),
1317
+ },
1318
+ ];
1319
+ if (!split) return out;
1320
+ for (const section of sections) {
1321
+ const key = sectionKey(section);
1322
+ out.push({
1323
+ domain: 'doc',
1324
+ name: key,
1325
+ keywords: [
1326
+ ...(section.title ? [section.title] : []),
1327
+ ...headings(section),
1328
+ ...codeTerms(section),
1329
+ ],
1330
+ description: sectionSummary(section),
1331
+ prose: sectionProse(section),
1332
+ titles: [section.title, ...headings(section)].filter(Boolean),
1333
+ _topic: name,
1334
+ _section: key,
1335
+ _title: `${docTitle} › ${section.title}`,
1336
+ _command: `astryx docs ${name} ${key}`,
1337
+ _parent: `astryx docs ${name} --index`,
1338
+ ...((sectionPackage?.(key) ?? pkg) ? {_package: sectionPackage?.(key) ?? pkg} : {}),
1339
+ });
1340
+ }
1341
+ return out;
1342
+ }
1343
+
773
1344
  /**
774
1345
  * Build template candidates (page + block) from the template discovery API.
775
1346
  * @param {string} cwd
@@ -783,6 +1354,13 @@ async function gatherTemplates(cwd) {
783
1354
  return [];
784
1355
  }
785
1356
  return templates.map(t => {
1357
+ // A replacement's target is its canonical unqualified lookup id. Keep the
1358
+ // integration-owned id as a keyword and response label, but score and print
1359
+ // commands against the id that `template()` resolves back to this entry.
1360
+ // This matters for replacement chains: one replacement's own id can be the
1361
+ // target of another, so using that shadowed id as the command would select
1362
+ // the other template.
1363
+ const commandName = t.replaces ?? t.dirName;
786
1364
  // Blocks ship an authored componentsUsed; page templates don't, so derive
787
1365
  // them from the source. Category words (e.g. "Dashboard - Analytics") are
788
1366
  // strong intent signal for pages, which otherwise only index on name +
@@ -795,6 +1373,7 @@ async function gatherTemplates(cwd) {
795
1373
  const keywords = Array.isArray(t.componentsUsed)
796
1374
  ? [...t.componentsUsed]
797
1375
  : [];
1376
+ keywords.push(...templateLookupIds(t).filter(id => id !== commandName));
798
1377
  /** @type {string[]} */
799
1378
  let weakKeywords = [];
800
1379
  if (t.type === 'page') {
@@ -810,12 +1389,14 @@ async function gatherTemplates(cwd) {
810
1389
  }
811
1390
  return {
812
1391
  domain: 'template',
813
- name: t.dirName,
1392
+ name: commandName,
814
1393
  keywords,
815
1394
  weakKeywords,
816
1395
  description: t.description || '',
817
1396
  _displayName: t.name,
818
1397
  _kind: t.type, // 'page' | 'block'
1398
+ _resultName: t.dirName,
1399
+ _commandName: commandName,
819
1400
  };
820
1401
  });
821
1402
  }
@@ -834,7 +1415,7 @@ async function gatherTemplates(cwd) {
834
1415
  function toResult(c, score, reason, matchedTerms, queryTerms) {
835
1416
  const base = {
836
1417
  domain: c.domain,
837
- name: c.name,
1418
+ name: c._resultName ?? c.name,
838
1419
  score,
839
1420
  reason,
840
1421
  description: c.description || '',
@@ -856,10 +1437,16 @@ function toResult(c, score, reason, matchedTerms, queryTerms) {
856
1437
  };
857
1438
  break;
858
1439
  case 'doc':
1440
+ // A doc result names its topic or route, plus the section when the
1441
+ // hit is one section; its command reads exactly that part.
859
1442
  result = {
860
1443
  ...base,
1444
+ name: c._topic ?? c.name,
1445
+ ...(c._section ? {section: c._section} : {}),
861
1446
  title: c._title,
862
- command: `astryx docs ${c.name}`,
1447
+ command: c._command ?? `astryx docs ${c.name}`,
1448
+ ...(c._parent ? {parent: c._parent} : {}),
1449
+ ...(c._package ? {package: c._package} : {}),
863
1450
  };
864
1451
  break;
865
1452
  case 'template':
@@ -867,7 +1454,7 @@ function toResult(c, score, reason, matchedTerms, queryTerms) {
867
1454
  ...base,
868
1455
  displayName: c._displayName,
869
1456
  kind: c._kind,
870
- command: `astryx template ${c.name}`,
1457
+ command: `astryx template ${c._commandName ?? c.name} --type ${c._kind}`,
871
1458
  };
872
1459
  break;
873
1460
  default:
@@ -884,7 +1471,7 @@ function toResult(c, score, reason, matchedTerms, queryTerms) {
884
1471
  * @param {string} [options.cwd]
885
1472
  * @param {'component'|'hook'|'doc'|'template'} [options.type] - Restrict to one domain.
886
1473
  * @param {number} [options.limit] - Max results (default 20).
887
- * @returns {Promise<{type: 'search', data: {query: string, matchCount: number, results: Array<object>}}>}
1474
+ * @returns {Promise<import('./search.type.mjs').SearchResponse>}
888
1475
  */
889
1476
  export async function search(query, options = {}) {
890
1477
  const {cwd = process.cwd(), type, limit = 20} = options;
@@ -919,17 +1506,27 @@ export async function search(query, options = {}) {
919
1506
  const term = String(query).trim().toLowerCase();
920
1507
  const tokens = tokenizeQuery(term);
921
1508
 
922
- const coreDir = findCoreDir(cwd);
923
- if (!coreDir) {
924
- throw new AstryxError('Could not find @astryxdesign/core package');
1509
+ // `astryx docs` reads docs without @astryxdesign/core, so a docs-only
1510
+ // search must too. Every other domain reads core: asked for by name, it is
1511
+ // an error without core; an open search then covers the docs alone.
1512
+ const docsOnly = type === 'doc';
1513
+ const coreDir = docsOnly ? null : findCoreDir(cwd);
1514
+ if (type && !docsOnly && !coreDir) {
1515
+ throw new AstryxError(
1516
+ 'Could not find @astryxdesign/core package',
1517
+ undefined,
1518
+ ERROR_CODES.ERR_CORE_NOT_FOUND,
1519
+ );
925
1520
  }
926
1521
 
927
1522
  // Gather candidates from each requested domain in parallel.
928
1523
  /** @param {string} d */
929
- const wants = d => !type || type === d;
1524
+ const wants = d => (!type && (coreDir != null || d === 'doc')) || type === d;
930
1525
  const [components, hooks, docTopics, templates] = await Promise.all([
931
- wants('component') ? gatherComponents(coreDir, cwd) : [],
932
- wants('hook') ? gatherHooks(coreDir) : [],
1526
+ wants('component')
1527
+ ? gatherComponents(/** @type {string} */ (coreDir), cwd)
1528
+ : [],
1529
+ wants('hook') ? gatherHooks(/** @type {string} */ (coreDir)) : [],
933
1530
  wants('doc') ? gatherDocs(cwd) : [],
934
1531
  wants('template') ? gatherTemplates(cwd) : [],
935
1532
  ]);
@@ -971,7 +1568,10 @@ export async function search(query, options = {}) {
971
1568
  data: {
972
1569
  query: String(query).trim(),
973
1570
  matchCount: scored.length,
974
- results: limited,
1571
+ // toResult gives every domain its command and domain fields.
1572
+ results: /** @type {import('./search.type.mjs').SearchResultEntry[]} */ (
1573
+ limited
1574
+ ),
975
1575
  },
976
1576
  };
977
1577
  }