@astryxdesign/cli 0.6.4 → 0.6.5-canary.00f1ed9

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 (405) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/README.md +103 -95
  3. package/api/build/_adapter.d.mts +36 -2
  4. package/api/build/_adapter.mjs +41 -10
  5. package/api/build/build.doc.mjs +8 -3
  6. package/api/build/build.test.mjs +60 -2
  7. package/api/build/kit/kit.mjs +109 -26
  8. package/api/build/kit/rank.d.mts +24 -8
  9. package/api/build/kit/rank.mjs +277 -97
  10. package/api/build/kit/rank.test.mjs +231 -48
  11. package/api/component/_adapter.d.mts +25 -0
  12. package/api/component/_adapter.mjs +59 -5
  13. package/api/component/component.d.mts +6 -3
  14. package/api/component/component.doc.mjs +37 -17
  15. package/api/component/component.mjs +249 -9
  16. package/api/component/component.type.d.mts +25 -0
  17. package/api/component/component.type.mjs +44 -0
  18. package/api/discover/_adapter.d.mts +114 -6
  19. package/api/discover/_adapter.mjs +372 -17
  20. package/api/discover/_adapter.test.mjs +215 -0
  21. package/api/discover/_catalog-view.d.mts +115 -0
  22. package/api/discover/_catalog-view.mjs +203 -0
  23. package/api/discover/_catalog-view.test.mjs +128 -0
  24. package/api/discover/detail/detail.d.mts +18 -6
  25. package/api/discover/detail/detail.mjs +67 -13
  26. package/api/discover/detail/detail.test.mjs +85 -0
  27. package/api/discover/detail/item/item.d.mts +26 -0
  28. package/api/discover/detail/item/item.mjs +78 -0
  29. package/api/discover/detail/item/item.test.mjs +73 -0
  30. package/api/discover/discover.d.mts +3 -9
  31. package/api/discover/discover.doc.mjs +61 -18
  32. package/api/discover/discover.mjs +220 -36
  33. package/api/discover/discover.test.mjs +11 -2
  34. package/api/discover/discover.type.d.mts +147 -8
  35. package/api/discover/discover.type.mjs +102 -12
  36. package/api/discover/list/list.d.mts +20 -6
  37. package/api/discover/list/list.mjs +45 -12
  38. package/api/discover/list/list.test.mjs +46 -0
  39. package/api/discover/search/search.d.mts +18 -16
  40. package/api/discover/search/search.mjs +102 -56
  41. package/api/discover/search/search.test.mjs +144 -10
  42. package/api/docs/_adapter.d.mts +8 -3
  43. package/api/docs/_adapter.mjs +14 -6
  44. package/api/docs/docOverlays.test.mjs +27 -1
  45. package/api/docs/docs.doc.mjs +2 -2
  46. package/api/docs/docs.test.mjs +54 -18
  47. package/api/docs/integration-tree.test.mjs +17 -0
  48. package/api/docs/integrationDocs.test.mjs +27 -5
  49. package/api/doctor/doctor.d.mts +8 -3
  50. package/api/doctor/doctor.doc.mjs +17 -8
  51. package/api/doctor/doctor.mjs +90 -9
  52. package/api/doctor/doctor.test.mjs +122 -10
  53. package/api/doctor/doctor.type.d.mts +1 -1
  54. package/api/doctor/doctor.type.mjs +1 -1
  55. package/api/error.d.mts +22 -0
  56. package/api/error.mjs +42 -0
  57. package/api/gap-report/gap-report.doc.mjs +19 -10
  58. package/api/hook/hook.doc.mjs +6 -3
  59. package/api/index.d.mts +1 -0
  60. package/api/index.mjs +5 -3
  61. package/api/init/init.doc.mjs +17 -12
  62. package/api/integration/add-contribution.d.mts +2 -1
  63. package/api/integration/add-contribution.mjs +7 -3
  64. package/api/integration/add-contribution.test.mjs +3 -3
  65. package/api/integration/add-helpers.d.mts +5 -2
  66. package/api/integration/add-helpers.mjs +36 -9
  67. package/api/integration/add-theme.mjs +266 -23
  68. package/api/integration/add-theme.test.mjs +247 -0
  69. package/api/integration/authoring-checks.mjs +12 -9
  70. package/api/integration/authoring-checks.type.d.mts +8 -0
  71. package/api/integration/authoring-checks.type.mjs +7 -3
  72. package/api/integration/integration-authoring.type.d.mts +4 -1
  73. package/api/integration/integration-authoring.type.mjs +6 -1
  74. package/api/integration/integrationAdd.doc.mjs +6 -0
  75. package/api/integration/integrationAddTheme.doc.mjs +10 -0
  76. package/api/integration/integrationComponentConflicts.doc.mjs +1 -1
  77. package/api/integration/integrationDocConflicts.doc.mjs +1 -1
  78. package/api/integration/integrationPackCheck.doc.mjs +3 -3
  79. package/api/integration/integrationTemplateConflicts.doc.mjs +1 -1
  80. package/api/integration/pack-check.lifecycle-output.test.mjs +107 -0
  81. package/api/integration/pack-check.mjs +92 -10
  82. package/api/integration/pack-check.test.mjs +140 -1
  83. package/api/integration/pack-check.type.mjs +1 -1
  84. package/api/integration/validate-integration.d.mts +4 -2
  85. package/api/integration/validate-integration.mjs +7 -2
  86. package/api/integration/validate-integration.test.mjs +55 -0
  87. package/api/integration/validate-integration.type.d.mts +5 -0
  88. package/api/integration/validate-integration.type.mjs +5 -1
  89. package/api/integration/validateIntegration.doc.mjs +1 -1
  90. package/api/json/assertResponse.doc.mjs +1 -1
  91. package/api/json/isError.doc.mjs +1 -1
  92. package/api/layout/expand/expand.mjs +12 -7
  93. package/api/layout/expand/expand.receipt.test.mjs +74 -0
  94. package/api/layout/layout.type.d.mts +1 -0
  95. package/api/layout/layout.type.mjs +1 -0
  96. package/api/layout/layoutExpand.doc.mjs +1 -1
  97. package/api/search/search.d.mts +51 -1
  98. package/api/search/search.doc.mjs +2 -2
  99. package/api/search/search.mjs +299 -17
  100. package/api/search/search.test.mjs +216 -18
  101. package/api/swizzle/copy/copy.mjs +66 -3
  102. package/api/swizzle/swizzle.doc.mjs +11 -5
  103. package/api/template/copy/copy.mjs +15 -9
  104. package/api/template/copy/copy.receipt.test.mjs +77 -0
  105. package/api/template/copy/copy.test.mjs +9 -0
  106. package/api/template/show/show.mjs +15 -4
  107. package/api/template/show/show.test.mjs +76 -0
  108. package/api/template/template-integration.test.mjs +14 -0
  109. package/api/template/template.d.mts +1 -1
  110. package/api/template/template.doc.mjs +8 -3
  111. package/api/template/template.mjs +1 -0
  112. package/api/template/template.type.d.mts +2 -0
  113. package/api/template/template.type.mjs +2 -0
  114. package/api/theme/add/add.mjs +17 -25
  115. package/api/theme/add/add.rollback.test.mjs +158 -0
  116. package/api/theme/add/add.staging.test.mjs +40 -23
  117. package/api/theme/build/build.d.mts +24 -0
  118. package/api/theme/build/build.family.test.mjs +7 -12
  119. package/api/theme/build/build.mjs +243 -26
  120. package/api/theme/build/build.project-core.test.mjs +165 -0
  121. package/api/theme/build/build.rollback.test.mjs +148 -0
  122. package/api/theme/generateTonalPalette.doc.mjs +1 -2
  123. package/api/theme/listThemes.doc.mjs +1 -1
  124. package/api/theme/themeAdd.doc.mjs +9 -10
  125. package/api/theme/themeBuild.doc.mjs +13 -13
  126. package/api/theme/themeList.doc.mjs +1 -1
  127. package/api/theme/themeListAvailable.doc.mjs +2 -1
  128. package/api/theme/themePaletteGenerate.doc.mjs +15 -8
  129. package/api/theme/themeTargets.doc.mjs +3 -2
  130. package/api/theme/themeTemplate.doc.mjs +2 -1
  131. package/api/upgrade/run/files-changed.test.mjs +111 -0
  132. package/api/upgrade/run/run.mjs +25 -6
  133. package/api/upgrade/run/run.test.mjs +45 -1
  134. package/api/upgrade/upgrade.doc.mjs +24 -22
  135. package/api/upgrade/upgrade.type.d.mts +1 -0
  136. package/api/upgrade/upgrade.type.mjs +3 -2
  137. package/assets/codemods/__tests__/runner.test.mjs +3 -1
  138. package/assets/codemods/file-count.test.mjs +163 -0
  139. package/assets/codemods/integration-runner.mjs +3 -3
  140. package/assets/codemods/runner.mjs +5 -4
  141. package/assets/docs/README.md +4 -2
  142. package/assets/docs/browser-support.doc.mjs +11 -11
  143. package/assets/docs/color.doc.mjs +8 -2
  144. package/assets/docs/elevation.doc.mjs +6 -4
  145. package/assets/docs/getting-started.doc.mjs +5 -16
  146. package/assets/docs/icons.doc.mjs +3 -21
  147. package/assets/docs/illustrations.doc.mjs +7 -15
  148. package/assets/docs/internationalization.doc.mjs +7 -5
  149. package/assets/docs/layout.doc.dense.mjs +130 -82
  150. package/assets/docs/layout.doc.mjs +133 -77
  151. package/assets/docs/migration.doc.mjs +19 -21
  152. package/assets/docs/motion.doc.mjs +16 -3
  153. package/assets/docs/principles.doc.dense.mjs +5 -5
  154. package/assets/docs/principles.doc.mjs +8 -0
  155. package/assets/docs/principles.doc.zh.mjs +6 -6
  156. package/assets/docs/shape.doc.mjs +8 -3
  157. package/assets/docs/spacing.doc.mjs +7 -2
  158. package/assets/docs/styling-libraries.doc.mjs +6 -2
  159. package/assets/docs/styling.doc.mjs +19 -23
  160. package/assets/docs/theme.doc.dense.mjs +58 -18
  161. package/assets/docs/theme.doc.mjs +57 -47
  162. package/assets/docs/theme.doc.zh.mjs +9 -8
  163. package/assets/docs/tokens.doc.dense.mjs +2 -2
  164. package/assets/docs/tokens.doc.mjs +389 -8
  165. package/assets/docs/tokens.doc.zh.mjs +2 -2
  166. package/assets/docs/tree/add-a-component.doc.mjs +75 -0
  167. package/assets/docs/tree/add-a-theme.doc.mjs +85 -0
  168. package/assets/docs/tree/add-a-topic.doc.mjs +144 -0
  169. package/assets/docs/tree/agent-guidance.doc.mjs +138 -0
  170. package/assets/docs/tree/block-template.doc.mjs +130 -0
  171. package/assets/docs/tree/build-the-template.doc.mjs +28 -0
  172. package/assets/docs/tree/building-blocks.doc.mjs +46 -0
  173. package/assets/docs/tree/check-your-docs.doc.mjs +137 -0
  174. package/assets/docs/tree/checks.doc.mjs +119 -0
  175. package/assets/docs/tree/codemods.doc.mjs +147 -0
  176. package/assets/docs/tree/component-family.doc.mjs +113 -0
  177. package/assets/docs/tree/component-imports.doc.mjs +69 -0
  178. package/assets/docs/tree/component-lookups.doc.mjs +149 -0
  179. package/assets/docs/tree/components.doc.mjs +23 -0
  180. package/assets/docs/tree/configuration.doc.mjs +23 -0
  181. package/assets/docs/tree/debug-and-gap-reports.doc.mjs +182 -0
  182. package/assets/docs/tree/define-the-theme.doc.mjs +118 -0
  183. package/assets/docs/tree/describe-the-component.doc.mjs +57 -0
  184. package/assets/docs/tree/docs.doc.mjs +21 -0
  185. package/assets/docs/tree/document-the-template.doc.mjs +28 -0
  186. package/assets/docs/tree/document-the-theme.doc.mjs +68 -0
  187. package/assets/docs/tree/export-template-assets.doc.mjs +147 -0
  188. package/assets/docs/tree/extend-or-replace.doc.mjs +103 -0
  189. package/assets/docs/tree/fonts-and-assets.doc.mjs +106 -0
  190. package/assets/docs/tree/generate-a-palette.doc.mjs +66 -0
  191. package/assets/docs/tree/grade-template-with-agent.doc.mjs +105 -0
  192. package/assets/docs/tree/help.doc.mjs +16 -0
  193. package/assets/docs/tree/integrations.doc.mjs +25 -451
  194. package/assets/docs/tree/links.doc.mjs +98 -0
  195. package/assets/docs/tree/package-and-test.doc.mjs +32 -0
  196. package/assets/docs/tree/page-template.doc.mjs +71 -0
  197. package/assets/docs/tree/publishing.doc.mjs +111 -0
  198. package/assets/docs/tree/quick-start.doc.mjs +272 -0
  199. package/assets/docs/tree/replace-a-core-component.doc.mjs +104 -0
  200. package/assets/docs/tree/replace-a-core-template.doc.mjs +172 -0
  201. package/assets/docs/tree/sections-and-placement.doc.mjs +108 -0
  202. package/assets/docs/tree/see-it-in-an-app.doc.mjs +59 -0
  203. package/assets/docs/tree/ship.doc.mjs +16 -0
  204. package/assets/docs/tree/short-and-findable.doc.mjs +108 -0
  205. package/assets/docs/tree/single-component.doc.mjs +165 -0
  206. package/assets/docs/tree/start-a-template.doc.mjs +143 -0
  207. package/assets/docs/tree/subcomponent.doc.mjs +115 -0
  208. package/assets/docs/tree/template-assets.doc.mjs +64 -0
  209. package/assets/docs/tree/template-doc-overview.doc.mjs +121 -0
  210. package/assets/docs/tree/template-fonts.doc.mjs +102 -0
  211. package/assets/docs/tree/template-grading-rubric.doc.mjs +452 -0
  212. package/assets/docs/tree/template-icons.doc.mjs +97 -0
  213. package/assets/docs/tree/template-images-media.doc.mjs +127 -0
  214. package/assets/docs/tree/template-styles.doc.mjs +93 -0
  215. package/assets/docs/tree/templates.doc.mjs +34 -0
  216. package/assets/docs/tree/test-in-an-app.doc.mjs +115 -0
  217. package/assets/docs/tree/test-template-in-app.doc.mjs +128 -0
  218. package/assets/docs/tree/themes.doc.mjs +39 -0
  219. package/assets/docs/tree/troubleshooting.doc.mjs +153 -0
  220. package/assets/docs/tree/upgrading.doc.mjs +103 -0
  221. package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +51 -0
  222. package/assets/docs/tree/verify-packed-template.doc.mjs +77 -0
  223. package/assets/docs/tree/versioning.doc.mjs +162 -0
  224. package/assets/docs/tree/write-good-templates.doc.mjs +64 -0
  225. package/assets/docs/tree/write-the-template-file.doc.mjs +154 -0
  226. package/assets/docs/typography.doc.mjs +24 -4
  227. package/assets/docs/working-with-ai.doc.mjs +30 -22
  228. package/assets/templates/blocks/components/InternationalizationProvider/InternationalizationProvider01ShippedLocale.tsx +1 -1
  229. package/assets/templates/pages/ai-chat/template.doc.mjs +16 -1
  230. package/assets/templates/pages/ai-chat-landing/template.doc.mjs +9 -1
  231. package/assets/templates/pages/blank/template.doc.mjs +3 -1
  232. package/assets/templates/pages/canvas-editor/template.doc.mjs +9 -1
  233. package/assets/templates/pages/centered-hero/template.doc.mjs +3 -1
  234. package/assets/templates/pages/checkout-wizard/template.doc.mjs +1 -0
  235. package/assets/templates/pages/classic-gallery/template.doc.mjs +3 -1
  236. package/assets/templates/pages/contact-form/template.doc.mjs +16 -1
  237. package/assets/templates/pages/dashboard/template.doc.mjs +15 -1
  238. package/assets/templates/pages/dashboard-alert-rail/template.doc.mjs +23 -1
  239. package/assets/templates/pages/dashboard-cohort-funnel/template.doc.mjs +10 -1
  240. package/assets/templates/pages/dashboard-comparison/template.doc.mjs +9 -1
  241. package/assets/templates/pages/dashboard-composition/template.doc.mjs +10 -1
  242. package/assets/templates/pages/dashboard-progress/template.doc.mjs +17 -1
  243. package/assets/templates/pages/dashboard-scorecard/template.doc.mjs +2 -1
  244. package/assets/templates/pages/detail-page/template.doc.mjs +8 -0
  245. package/assets/templates/pages/documentation/template.doc.mjs +10 -1
  246. package/assets/templates/pages/documentation-design/template.doc.mjs +10 -1
  247. package/assets/templates/pages/documentation-technical/template.doc.mjs +10 -1
  248. package/assets/templates/pages/editor/template.doc.mjs +9 -1
  249. package/assets/templates/pages/file-explorer/template.doc.mjs +3 -1
  250. package/assets/templates/pages/form-two-column/template.doc.mjs +17 -1
  251. package/assets/templates/pages/form-wizard/template.doc.mjs +14 -1
  252. package/assets/templates/pages/form-wizard-dialog/template.doc.mjs +1 -0
  253. package/assets/templates/pages/gallery-hero/template.doc.mjs +10 -1
  254. package/assets/templates/pages/ide/template.doc.mjs +9 -1
  255. package/assets/templates/pages/incident-console/template.doc.mjs +10 -1
  256. package/assets/templates/pages/kanban-board/template.doc.mjs +11 -1
  257. package/assets/templates/pages/library/template.doc.mjs +15 -1
  258. package/assets/templates/pages/login/template.doc.mjs +10 -1
  259. package/assets/templates/pages/login-card/template.doc.mjs +11 -1
  260. package/assets/templates/pages/login-split/template.doc.mjs +10 -1
  261. package/assets/templates/pages/login-sso/template.doc.mjs +10 -1
  262. package/assets/templates/pages/messaging-shell/template.doc.mjs +11 -1
  263. package/assets/templates/pages/mixed-gallery/template.doc.mjs +10 -1
  264. package/assets/templates/pages/payment-form/template.doc.mjs +3 -1
  265. package/assets/templates/pages/product-detail/template.doc.mjs +9 -1
  266. package/assets/templates/pages/product-gallery/template.doc.mjs +10 -1
  267. package/assets/templates/pages/settings/template.doc.mjs +3 -1
  268. package/assets/templates/pages/settings-dialog/template.doc.mjs +9 -1
  269. package/assets/templates/pages/settings-sidebar/template.doc.mjs +9 -1
  270. package/assets/templates/pages/shell-nav/template.doc.mjs +10 -1
  271. package/assets/templates/pages/shell-side-nav/template.doc.mjs +14 -1
  272. package/assets/templates/pages/shell-top-nav/template.doc.mjs +11 -1
  273. package/assets/templates/pages/side-gallery/template.doc.mjs +3 -1
  274. package/assets/templates/pages/table/template.doc.mjs +11 -1
  275. package/assets/templates/pages/table-filter/template.doc.mjs +21 -1
  276. package/assets/templates/pages/table-grouped/template.doc.mjs +15 -1
  277. package/assets/templates/pages/table-inbox/template.doc.mjs +18 -6
  278. package/assets/templates/pages/table-page/template.doc.mjs +20 -1
  279. package/assets/templates/pages/table-tree/template.doc.mjs +14 -1
  280. package/assets/templates/pages/theme-showcase/template.doc.mjs +10 -1
  281. package/assets/templates/pages/work-item-detail/template.doc.mjs +10 -0
  282. package/assets/templates/themes/butter/icons.tsx +2 -0
  283. package/assets/templates/themes/chocolate/icons.tsx +2 -0
  284. package/assets/templates/themes/gothic/icons.tsx +2 -0
  285. package/assets/templates/themes/matcha/icons.tsx +2 -0
  286. package/assets/templates/themes/neutral/icons.tsx +2 -0
  287. package/assets/templates/themes/stone/icons.tsx +2 -0
  288. package/assets/templates/themes/y2k/icons.tsx +2 -0
  289. package/authoring/config/config.doc.mjs +9 -1
  290. package/authoring/config/parse.d.mts +2 -0
  291. package/authoring/config/parse.mjs +19 -0
  292. package/authoring/config/parse.test.mjs +8 -0
  293. package/authoring/config/type.ts +11 -0
  294. package/authoring/discover/discover.doc.d.mts +13 -0
  295. package/authoring/discover/discover.doc.mjs +138 -0
  296. package/authoring/discover/parse.d.mts +24 -0
  297. package/authoring/discover/parse.mjs +128 -0
  298. package/authoring/discover/parse.test.mjs +124 -0
  299. package/authoring/discover/type.ts +87 -0
  300. package/authoring/doctypes/_schema.d.mts +3 -2
  301. package/authoring/doctypes/_schema.mjs +6 -0
  302. package/authoring/doctypes/base/graph-fields.doc.mjs +3 -3
  303. package/authoring/doctypes/base/type.ts +4 -2
  304. package/authoring/doctypes/component/component.doc.mjs +6 -0
  305. package/authoring/doctypes/component/type.ts +8 -0
  306. package/authoring/doctypes/reference/reference.doc.mjs +7 -0
  307. package/authoring/doctypes/reference/type.ts +5 -0
  308. package/authoring/doctypes/schema/schema.doc.mjs +2 -2
  309. package/authoring/doctypes/template/parse.d.mts +2 -0
  310. package/authoring/doctypes/template/parse.mjs +1 -0
  311. package/authoring/doctypes/template/parse.test.mjs +21 -0
  312. package/authoring/doctypes/template/template.doc.mjs +7 -1
  313. package/authoring/doctypes/template/type.ts +12 -2
  314. package/authoring/index.d.mts +1 -0
  315. package/authoring/index.d.ts +10 -0
  316. package/authoring/index.mjs +1 -0
  317. package/authoring/integration/integration.doc.mjs +12 -10
  318. package/clients/cli/commands/component/index.mjs +152 -55
  319. package/clients/cli/commands/component-batch.test.mjs +341 -0
  320. package/clients/cli/commands/component-ownership.test.mjs +89 -0
  321. package/clients/cli/commands/component.doc.mjs +27 -9
  322. package/clients/cli/commands/discover.doc.mjs +53 -9
  323. package/clients/cli/commands/discover.mjs +393 -118
  324. package/clients/cli/commands/discover.sources.test.mjs +267 -0
  325. package/clients/cli/commands/docs.doc.mjs +1 -1
  326. package/clients/cli/commands/docs.mjs +60 -17
  327. package/clients/cli/commands/docs.test.mjs +113 -24
  328. package/clients/cli/commands/doctor-integration-docs.doc.mjs +3 -2
  329. package/clients/cli/commands/doctor-integration.test.mjs +53 -0
  330. package/clients/cli/commands/doctor.doc.mjs +3 -1
  331. package/clients/cli/commands/doctor.mjs +53 -9
  332. package/clients/cli/commands/gap-report.doc.mjs +10 -9
  333. package/clients/cli/commands/init.doc.mjs +9 -6
  334. package/clients/cli/commands/integration-add.doc.mjs +17 -7
  335. package/clients/cli/commands/integration-authoring.test.mjs +70 -9
  336. package/clients/cli/commands/integration-pack.doc.mjs +5 -9
  337. package/clients/cli/commands/integration-real-world.test.mjs +1 -1
  338. package/clients/cli/commands/integration-verify.doc.mjs +22 -0
  339. package/clients/cli/commands/integration.doc.mjs +4 -4
  340. package/clients/cli/commands/integration.mjs +76 -43
  341. package/clients/cli/commands/layout-expand.doc.mjs +3 -1
  342. package/clients/cli/commands/layout.expand-receipt.test.mjs +94 -0
  343. package/clients/cli/commands/layout.mjs +16 -0
  344. package/clients/cli/commands/manifest.doc.mjs +1 -1
  345. package/clients/cli/commands/search.doc.mjs +10 -3
  346. package/clients/cli/commands/search.mjs +21 -2
  347. package/clients/cli/commands/search.test.mjs +21 -4
  348. package/clients/cli/commands/swizzle.doc.mjs +1 -1
  349. package/clients/cli/commands/template.copy-receipt.test.mjs +60 -0
  350. package/clients/cli/commands/template.doc.mjs +1 -1
  351. package/clients/cli/commands/template.mjs +19 -5
  352. package/clients/cli/commands/template.show-media.test.mjs +62 -0
  353. package/clients/cli/commands/text-json-parity.test.mjs +7 -1
  354. package/clients/cli/commands/theme-add.doc.mjs +1 -1
  355. package/clients/cli/commands/theme-palette-generate.doc.mjs +3 -2
  356. package/clients/cli/commands/theme-palette.doc.mjs +1 -2
  357. package/clients/cli/commands/theme-targets.doc.mjs +2 -2
  358. package/clients/cli/commands/theme.doc.mjs +2 -1
  359. package/clients/cli/commands/upgrade.ascii-output.test.mjs +14 -0
  360. package/clients/cli/commands/upgrade.doc.mjs +62 -3
  361. package/clients/cli/commands/write-failure.test.mjs +175 -0
  362. package/clients/cli/index.mjs +28 -6
  363. package/clients/cli/lib/define-command.mjs +28 -4
  364. package/clients/cli/lib/define-command.test.mjs +54 -0
  365. package/clients/cli/lib/exit-codes.test.mjs +17 -1
  366. package/clients/cli/lib/json-shim.mjs +24 -14
  367. package/clients/cli/lib/manifest.mjs +48 -156
  368. package/clients/cli/lib/manifest.test.mjs +103 -9
  369. package/clients/cli/lib/parse-error-format.test.mjs +81 -0
  370. package/foundation/agent-docs/agent-docs.mjs +1 -1
  371. package/foundation/agent-docs/agent-docs.test.mjs +3 -2
  372. package/foundation/discovery/authoring-self-docs.mjs +1 -0
  373. package/foundation/discovery/authoring-self-docs.test.mjs +6 -2
  374. package/foundation/discovery/cli-self-docs.mjs +16 -2
  375. package/foundation/discovery/cli-self-docs.test.mjs +20 -0
  376. package/foundation/discovery/docs-discovery.mjs +5 -1
  377. package/foundation/discovery/docs-discovery.test.mjs +21 -0
  378. package/foundation/discovery/docs-section-key.d.mts +1 -1
  379. package/foundation/discovery/docs-section-key.mjs +1 -1
  380. package/foundation/discovery/template-adapter.d.mts +14 -0
  381. package/foundation/discovery/template-adapter.fixture-refs.test.mjs +37 -1
  382. package/foundation/discovery/template-adapter.mjs +21 -1
  383. package/foundation/doc-compiler/doc-loads.test.mjs +5 -4
  384. package/foundation/doc-compiler/inputs.test.mjs +0 -1
  385. package/foundation/doc-compiler/tree.d.mts +4 -0
  386. package/foundation/doc-compiler/tree.mjs +6 -1
  387. package/foundation/doc-compiler/tree.test.mjs +65 -14
  388. package/foundation/integrations/cli-requirement.d.mts +75 -11
  389. package/foundation/integrations/cli-requirement.mjs +120 -23
  390. package/foundation/integrations/cli-requirement.test.mjs +141 -9
  391. package/foundation/integrations/contribution-inventory.mjs +1 -1
  392. package/foundation/integrations/integrations.d.mts +14 -1
  393. package/foundation/integrations/integrations.mjs +41 -1
  394. package/foundation/integrations/integrations.test.mjs +31 -0
  395. package/foundation/response/batch.type.d.mts +33 -0
  396. package/foundation/response/batch.type.mjs +34 -0
  397. package/foundation/response/error-codes.doc.mjs +6 -8
  398. package/foundation/response/error-codes.test.mjs +30 -5
  399. package/foundation/response/response-types.doc.d.mts +5 -4
  400. package/foundation/response/response-types.doc.mjs +49 -19
  401. package/foundation/response/response-types.doc.test.mjs +23 -0
  402. package/foundation/response/response.doc.mjs +11 -10
  403. package/package.json +9 -9
  404. package/assets/docs/tree/integrations.test.mjs +0 -62
  405. package/assets/docs/tree/writing-docs.doc.mjs +0 -286
@@ -2,13 +2,15 @@
2
2
 
3
3
  /**
4
4
  * @file The page ranker behind `build`'s START: which page template an idea
5
- * should start from.
5
+ * should start from, and whether the idea asks for a page at all.
6
6
  *
7
- * @input A free-text idea and the project's ready page templates (id, display
8
- * name, description, `Family - Variant` category), all read from each
9
- * template's own descriptor.
7
+ * @input A free-text idea, the project's ready page templates (id, display
8
+ * name, description, keywords, `Family - Variant` category) and its
9
+ * components (name, keywords), all read from each item's own descriptor.
10
10
  * @output Every template, ranked, with the query terms it matched and whether
11
- * the idea names its family; `pickStart` turns that into a template or null.
11
+ * the idea names its family; `ideaKind`, whether the idea asks for a page, a
12
+ * part of one, or a change to a page the builder has; `pickStart` turns both
13
+ * into a template or null.
12
14
  * @position Beside kit.mjs (api/build/kit/). Search ranks components, docs,
13
15
  * blocks and pages against short lookups; this ranks only page templates,
14
16
  * against the long descriptions builders actually write ("ops dashboard with
@@ -36,8 +38,27 @@
36
38
  * next one counts half, unless the template names the same pair
37
39
  * ("executive summary").
38
40
  * - The family base. A template whose id is its family's name (`dashboard`,
39
- * `settings`) is that family's default; it wins when no variant's own words
40
- * outweigh it.
41
+ * `settings`) is that family's default. A variant displaces it only with two
42
+ * matched terms of its own ("a small comparison note" does not make a
43
+ * dashboard the comparison dashboard), and a base too weak to start on its
44
+ * own displaces no variant ("a funnel for a dashboard" is the funnel).
45
+ *
46
+ * Parts (spec:AST-048/FR3). "A date range picker" asks for a part of a page,
47
+ * not a page, and the system's own components say what a part is: an idea
48
+ * whose head noun is a word of a component's name or keywords, Core's or an
49
+ * integration's, asks for that part, unless the noun is a family word ("a data
50
+ * table") or the idea lists a page's worth of pieces. Words search drops as
51
+ * stopwords ("page", "screen", "app", "view") name no component, so "a calendar
52
+ * page" asks for a page and "a calendar" for a part. A part starts from the
53
+ * base template of the family the idea names ("an empty state for a settings
54
+ * page"), else from the app shell. An idea that changes a page or part the
55
+ * builder already has starts from the app shell too: that page is the
56
+ * builder's to keep (FR9), and no template scaffolds it. "Existing" marks one
57
+ * when its phrase names a page family or a component ("add a column to the
58
+ * existing table"), unless the idea asks for a new page ("a new dashboard like
59
+ * the existing dashboard", "clone the existing dashboard as a new page"). A
60
+ * bare "existing" phrase that names a page ("existing reports dashboard with a
61
+ * date filter") reads as the builder's own page.
41
62
  *
42
63
  * Family words come from the templates' own ids — a word in the ids of two or
43
64
  * more templates of one family, plus the family's name when a template carries
@@ -50,79 +71,68 @@ import {stem, STOPWORDS, SYNONYMS} from '../../search/search.mjs';
50
71
 
51
72
  /**
52
73
  * How much a term counts by where the template names it. The id and category
53
- * are the author's own label for what the page is; the description's last
54
- * sentence lists the ideas it serves; its body describes the layout.
74
+ * are the author's own label for what the page is; its keywords name the ideas
75
+ * it serves; its description describes the layout.
55
76
  */
56
77
  const FIELD_WEIGHT = {
57
78
  id: 3,
58
79
  variant: 3,
59
80
  family: 2,
60
81
  display: 2,
61
- intent: 2,
62
- body: 1,
82
+ keywords: 2,
83
+ description: 1,
63
84
  };
64
- /** A synonym hit counts at this share of a direct hit. */
85
+ /**
86
+ * A synonym hit counts half a direct hit: a synonym says the idea is near the
87
+ * page, not that it names it.
88
+ */
65
89
  const SYNONYM_SHARE = 0.5;
66
- /** A container word ("in a modal") counts this many times over. */
90
+ /**
91
+ * A container word ("in a modal") counts double: it names the page's frame,
92
+ * which is what a template is.
93
+ */
67
94
  const CONTAINER = 2;
68
- /** A word that only modifies another ("product" in "product response"). */
95
+ /**
96
+ * A word that only modifies another ("product" in "product response") counts
97
+ * half: it describes its head, not the page.
98
+ */
69
99
  const MODIFIER = 0.5;
70
- /** Added when the idea's head names the template's family. */
100
+ /**
101
+ * Added when the idea's head names the template's family: more than any one
102
+ * word scores (an id word only one template carries is worth about ten), so the
103
+ * head's family outranks a rarer word among the parts.
104
+ */
71
105
  const HEAD_FAMILY = 12;
72
- /** Added when the family is named anywhere else in the idea. */
106
+ /**
107
+ * Added when the family is named anywhere else in the idea: about a description
108
+ * word, enough to settle a near tie.
109
+ */
73
110
  const BODY_FAMILY = 2;
74
- /** Added to a family's base template when the idea names that family. */
111
+ /**
112
+ * Added to a family's base template when the idea names that family: settles a
113
+ * near tie toward the family's default page.
114
+ */
75
115
  const FAMILY_BASE = 2;
76
116
  /**
77
117
  * The least a start must score, and the evidence it needs: two matched terms,
78
118
  * or the idea naming its family. One rare word alone is how a tic-tac-toe game
79
- * board became a kanban board.
119
+ * board became a kanban board, and it is also too little for a variant to
120
+ * displace its family's base template.
80
121
  */
81
122
  const START_SCORE = 10;
82
123
  const START_TERMS = 2;
124
+ /**
125
+ * How many listed pieces make an idea a page: a part is one thing with a detail
126
+ * or two ("a status pill with a tooltip"), while three pieces, two commas
127
+ * apart, describe a page.
128
+ */
129
+ const PAGE_PIECES = 3;
83
130
 
84
131
  /**
85
132
  * Words search drops that name layout: "side navigation", "side by side".
86
133
  */
87
134
  const LAYOUT_WORDS = new Set(['side']);
88
135
 
89
- /** Filler that search keeps but that says nothing about a page's layout. */
90
- const FILLER = new Set([
91
- 'per',
92
- 'each',
93
- 'every',
94
- 'using',
95
- 'via',
96
- 'into',
97
- 'onto',
98
- 'about',
99
- 'above',
100
- 'below',
101
- 'across',
102
- 'within',
103
- 'without',
104
- 'between',
105
- 'plus',
106
- 'also',
107
- 'then',
108
- 'them',
109
- 'they',
110
- 'which',
111
- 'while',
112
- 'when',
113
- 'all',
114
- 'any',
115
- 'new',
116
- 'use',
117
- 'used',
118
- 'show',
119
- 'shows',
120
- 'showing',
121
- 'display',
122
- 'displays',
123
- 'displaying',
124
- ]);
125
-
126
136
  /**
127
137
  * Where an idea's head ends: its first clause, before the parts it lists.
128
138
  */
@@ -142,6 +152,24 @@ const OVERLAY_FRAMES = new Set([
142
152
  'overlay',
143
153
  ]);
144
154
 
155
+ /**
156
+ * The word that marks a page or part the builder already has, when its phrase
157
+ * names one (see `changesExistingPage`).
158
+ */
159
+ const EXISTING = /\bexisting\b/i;
160
+
161
+ /**
162
+ * The word that marks an idea asking for a new page ("a new dashboard", "as a
163
+ * new page"), which is never a change to one the builder has.
164
+ */
165
+ const NEW = /\bnew\b/;
166
+
167
+ /**
168
+ * The family whose templates are app chrome rather than page content: the
169
+ * explicit `Shell -` category (architecture:template-authoring/INV7).
170
+ */
171
+ const SHELL_FAMILY = 'Shell';
172
+
145
173
  /** A container phrase: "in a modal", "inside the side panel". */
146
174
  const CONTAINER_PHRASE =
147
175
  /\b(?:in|inside|within)\s+(?:a|an|the)\s+([a-z-]+)(?:\s+([a-z-]+))?/gi;
@@ -155,12 +183,11 @@ const PHRASE_END =
155
183
 
156
184
  /** @param {string} t */
157
185
  const isContentWord = t =>
158
- t.length >= 2 &&
159
- (LAYOUT_WORDS.has(t) || (!STOPWORDS.has(t) && !FILLER.has(t)));
186
+ t.length >= 2 && (LAYOUT_WORDS.has(t) || !STOPWORDS.has(t));
160
187
 
161
188
  /**
162
- * Content terms of a text: lowercase alphanumeric words, stopwords and filler
163
- * removed, stemmed.
189
+ * Content terms of a text: lowercase alphanumeric words, stopwords removed,
190
+ * stemmed.
164
191
  * @param {string} text
165
192
  * @returns {string[]}
166
193
  */
@@ -262,8 +289,44 @@ const SYNONYMS_OF = (() => {
262
289
 
263
290
  /**
264
291
  * @typedef {import('../_adapter.mjs').PageTemplate} PageTemplate
265
- * @typedef {{name: string, score: number, hits: number, familyNamed: boolean, containerMatched: boolean}} RankedPage
292
+ * @typedef {import('../_adapter.mjs').ComponentWords} ComponentWords
293
+ * @typedef {{name: string, score: number, hits: number, familyNamed: boolean, containerMatched: boolean, family: string, base: boolean, matched: Set<string>}} RankedPage
294
+ * @typedef {'page' | 'part' | 'edit'} IdeaKind
295
+ */
296
+
297
+ /** @param {PageTemplate} page */
298
+ const familyOf = page => (page.category.split(' - ')[0] ?? '').trim();
299
+
300
+ /**
301
+ * Family words, from the templates' own ids: a word in the ids of two or more
302
+ * templates of one family, plus the family's name when a template carries it.
303
+ * Each word maps to its family.
304
+ * @param {PageTemplate[]} pages
305
+ * @returns {Map<string, string>}
266
306
  */
307
+ function familyWordsOf(pages) {
308
+ /** @type {Map<string, Map<string, number>>} */
309
+ const idWords = new Map();
310
+ for (const page of pages) {
311
+ const family = familyOf(page);
312
+ const counts = idWords.get(family) ?? new Map();
313
+ for (const w of new Set(page.name.split('-'))) {
314
+ if (w.length >= 3) counts.set(stem(w), (counts.get(stem(w)) ?? 0) + 1);
315
+ }
316
+ idWords.set(family, counts);
317
+ }
318
+ /** @type {Map<string, string>} */
319
+ const familyOfWord = new Map();
320
+ for (const [family, counts] of idWords) {
321
+ const heads = [...counts].filter(([, c]) => c >= 2).map(([w]) => w);
322
+ const name = normalized(family).split(' ').pop() ?? '';
323
+ if (name.length >= 3 && counts.has(stem(name))) heads.push(stem(name));
324
+ for (const head of heads) {
325
+ if (!familyOfWord.has(head)) familyOfWord.set(head, family);
326
+ }
327
+ }
328
+ return familyOfWord;
329
+ }
267
330
 
268
331
  /**
269
332
  * Rank page templates against an idea, best first. Ties go to the template
@@ -276,20 +339,15 @@ const SYNONYMS_OF = (() => {
276
339
  export function rankPages(query, pages) {
277
340
  const docs = pages.map(page => {
278
341
  const [family = '', ...variantParts] = page.category.split(' - ');
279
- const sentences = page.description.trim().split(/(?<=[.!?])\s+/);
280
- const intent = sentences.length > 1 ? sentences[sentences.length - 1] : '';
281
- const body =
282
- sentences.length > 1
283
- ? sentences.slice(0, -1).join(' ')
284
- : page.description;
285
342
  /** @type {Record<keyof typeof FIELD_WEIGHT, string>} */
286
343
  const fields = {
287
344
  id: page.name.replace(/-/g, ' '),
288
345
  variant: variantParts.join(' - '),
289
346
  family,
290
347
  display: page.displayName,
291
- intent,
292
- body,
348
+ // Each keyword is its own phrase: a pair never spans two of them.
349
+ keywords: (page.keywords ?? []).join(', '),
350
+ description: page.description,
293
351
  };
294
352
  /** @type {Map<string, number>} */
295
353
  const bag = new Map();
@@ -320,26 +378,7 @@ export function rankPages(query, pages) {
320
378
  return d ? Math.log(1 + (n - d + 0.5) / (d + 0.5)) : 0;
321
379
  };
322
380
 
323
- // Family words, from the templates' own ids.
324
- /** @type {Map<string, Map<string, number>>} */
325
- const idWords = new Map();
326
- for (const {page, family} of docs) {
327
- const counts = idWords.get(family) ?? new Map();
328
- for (const w of new Set(page.name.split('-'))) {
329
- if (w.length >= 3) counts.set(stem(w), (counts.get(stem(w)) ?? 0) + 1);
330
- }
331
- idWords.set(family, counts);
332
- }
333
- /** @type {Map<string, string>} */
334
- const familyOfWord = new Map();
335
- for (const [family, counts] of idWords) {
336
- const heads = [...counts].filter(([, c]) => c >= 2).map(([w]) => w);
337
- const name = normalized(family).split(' ').pop() ?? '';
338
- if (name.length >= 3 && counts.has(stem(name))) heads.push(stem(name));
339
- for (const head of heads) {
340
- if (!familyOfWord.has(head)) familyOfWord.set(head, family);
341
- }
342
- }
381
+ const familyOfWord = familyWordsOf(pages);
343
382
  /** @param {string[]} ts */
344
383
  const familiesIn = ts =>
345
384
  new Set(ts.filter(t => familyOfWord.has(t)).map(t => familyOfWord.get(t)));
@@ -352,11 +391,13 @@ export function rankPages(query, pages) {
352
391
  const modifiers = modifiersOf(query);
353
392
  const containers = containersOf(query);
354
393
 
355
- return docs
394
+ const ranked = docs
356
395
  .map(({page, family, bag, pairs}) => {
357
396
  let score = 0;
358
397
  let hits = 0;
359
398
  let containerMatched = false;
399
+ /** @type {Set<string>} */
400
+ const matched = new Set();
360
401
  for (const t of queryTerms) {
361
402
  // A modifier is discounted where the template names it outright; a
362
403
  // synonym hit is already discounted by SYNONYM_SHARE.
@@ -378,16 +419,25 @@ export function rankPages(query, pages) {
378
419
  if (value > 0) {
379
420
  score += value;
380
421
  hits++;
422
+ matched.add(t);
381
423
  if (containers.has(t)) containerMatched = true;
382
424
  }
383
425
  }
384
426
  const familyNamed = namedFamilies.has(family);
385
427
  if (headFamilies.has(family)) score += HEAD_FAMILY;
386
428
  else if (familyNamed) score += BODY_FAMILY;
387
- if (familyNamed && normalized(page.name) === normalized(family)) {
388
- score += FAMILY_BASE;
389
- }
390
- return {name: page.name, score, hits, familyNamed, containerMatched};
429
+ const base = normalized(page.name) === normalized(family);
430
+ if (familyNamed && base) score += FAMILY_BASE;
431
+ return {
432
+ name: page.name,
433
+ score,
434
+ hits,
435
+ familyNamed,
436
+ containerMatched,
437
+ family,
438
+ base,
439
+ matched,
440
+ };
391
441
  })
392
442
  .sort(
393
443
  (a, b) =>
@@ -395,6 +445,123 @@ export function rankPages(query, pages) {
395
445
  a.name.split('-').length - b.name.split('-').length ||
396
446
  a.name.localeCompare(b.name),
397
447
  );
448
+ return baseFirst(ranked);
449
+ }
450
+
451
+ /**
452
+ * Put a named family's base template ahead of the variant that outranks it,
453
+ * unless the variant matched START_TERMS terms the base did not, or the base
454
+ * scores too little to start on its own.
455
+ * @param {RankedPage[]} ranked
456
+ * @returns {RankedPage[]}
457
+ */
458
+ function baseFirst(ranked) {
459
+ const top = ranked[0];
460
+ if (!top || top.base || !top.familyNamed) return ranked;
461
+ const base = ranked.find(r => r.base && r.family === top.family);
462
+ if (!base || base.score < START_SCORE) return ranked;
463
+ const own = [...top.matched].filter(t => !base.matched.has(t)).length;
464
+ return own >= START_TERMS
465
+ ? ranked
466
+ : [base, ...ranked.filter(r => r !== base)];
467
+ }
468
+
469
+ /**
470
+ * What an idea asks for (spec:AST-048/FR3): a whole `page`; a `part` of one,
471
+ * when its head noun names one of the system's components and it lists fewer
472
+ * than PAGE_PIECES pieces; or an `edit` of a page the builder already has.
473
+ *
474
+ * @param {string} query
475
+ * @param {PageTemplate[]} pages
476
+ * @param {ComponentWords[]} components
477
+ * @returns {IdeaKind}
478
+ */
479
+ export function ideaKind(query, pages, components) {
480
+ const text = String(query);
481
+ const familyWords = familyWordsOf(pages);
482
+ if (changesExistingPage(text, familyWords, components)) return 'edit';
483
+ // The head's last word, page words included: "calendar" is the noun of "a
484
+ // calendar", "page" the noun of "a calendar page". An overlay frame names
485
+ // where the thing lives, not the thing: "drafts" is the noun of "drafts in
486
+ // a modal".
487
+ const head = (text.split(HEAD_END)[0] ?? '').replace(CONTAINER_PHRASE, m =>
488
+ containersOf(m).size > 0 ? ' ' : m,
489
+ );
490
+ const words = (head.toLowerCase().match(/[a-z0-9]+/g) ?? []).filter(
491
+ w => w.length >= 2,
492
+ );
493
+ const noun = stem(words[words.length - 1] ?? '');
494
+ if (familyWords.has(noun)) return 'page';
495
+ const pieces = (text.match(/,/g) ?? []).length + 1;
496
+ if (pieces >= PAGE_PIECES) return 'page';
497
+ return components.some(c => componentTerms(c).includes(noun))
498
+ ? 'part'
499
+ : 'page';
500
+ }
501
+
502
+ /**
503
+ * Whether an idea changes a page or part the builder already has: "existing"
504
+ * in the same phrase as a word that names a family of page templates or one of
505
+ * the system's components ("the existing incidents table", "the existing
506
+ * banner"). A new page "inspired by the existing one", or "existing users" on a
507
+ * new page, names neither, and an idea that asks for a new page is no change
508
+ * whatever it names.
509
+ * @param {string} text
510
+ * @param {Map<string, string>} familyWords
511
+ * @param {ComponentWords[]} components
512
+ * @returns {boolean}
513
+ */
514
+ function changesExistingPage(text, familyWords, components) {
515
+ const componentWords = new Set(components.flatMap(nameTerms));
516
+ const phrases = text.toLowerCase().split(PHRASE_END);
517
+ if (asksNewPage(phrases, familyWords)) return false;
518
+ return phrases.some(phrase => {
519
+ const at = phrase.search(EXISTING);
520
+ return (
521
+ at >= 0 &&
522
+ terms(phrase.slice(at)).some(
523
+ t => familyWords.has(t) || componentWords.has(t),
524
+ )
525
+ );
526
+ });
527
+ }
528
+
529
+ /**
530
+ * Whether an idea asks for a new page: "new" before a word that names a family
531
+ * of page templates ("a new dashboard"), or before only words search drops,
532
+ * such as "page" ("as a new page"). "A new column" asks for a part.
533
+ * @param {string[]} phrases
534
+ * @param {Map<string, string>} familyWords
535
+ * @returns {boolean}
536
+ */
537
+ function asksNewPage(phrases, familyWords) {
538
+ return phrases.some(phrase => {
539
+ const at = phrase.search(NEW);
540
+ if (at < 0) return false;
541
+ const after = terms(phrase.slice(at + 'new'.length));
542
+ return after.length === 0 || familyWords.has(after[0]);
543
+ });
544
+ }
545
+
546
+ /**
547
+ * The words of a component's name: "DateRangeInput" is date, range, input.
548
+ * @param {ComponentWords} component
549
+ * @returns {string[]}
550
+ */
551
+ function nameTerms(component) {
552
+ return terms(component.name.replace(/([a-z0-9])([A-Z])/g, '$1 $2'));
553
+ }
554
+
555
+ /**
556
+ * The terms a component answers to: the words of its name and of its keywords.
557
+ * @param {ComponentWords} component
558
+ * @returns {string[]}
559
+ */
560
+ function componentTerms(component) {
561
+ return [
562
+ ...nameTerms(component),
563
+ ...component.keywords.flatMap(keyword => terms(keyword)),
564
+ ];
398
565
  }
399
566
 
400
567
  /**
@@ -416,14 +583,27 @@ export function pickAlternatives(ranked, startName, count = 2) {
416
583
  }
417
584
 
418
585
  /**
419
- * The template to start from, or null when the best one has too little
420
- * evidence to lead and the page should start from the app shell. Evidence is
421
- * two matched terms, the idea naming the template's family, or its container.
586
+ * The template to start from, or null for the kit's neutral app shell. A page
587
+ * needs evidence to lead: two matched terms, the idea naming the template's
588
+ * family, or its container. A part starts from the base template of the family
589
+ * the idea places it in, else from the app shell (spec:AST-048/FR3); an edit of
590
+ * a page the builder already has starts from the app shell (FR9). Either way,
591
+ * the app shell is the shell template the idea describes when one leads.
422
592
  *
423
593
  * @param {RankedPage[]} ranked
594
+ * @param {IdeaKind} [kind]
424
595
  * @returns {RankedPage | null}
425
596
  */
426
- export function pickStart(ranked) {
597
+ export function pickStart(ranked, kind = 'page') {
598
+ if (kind !== 'page') {
599
+ const host = kind === 'part' && ranked.find(r => r.base && r.familyNamed);
600
+ if (host) return host;
601
+ // Otherwise the app shell (FR2): the shell template the idea describes
602
+ // when one leads ("a frame with sidebar navigation"), else none, for the
603
+ // kit's neutral fallback.
604
+ const lead = pickStart(ranked);
605
+ return lead?.family === SHELL_FAMILY ? lead : null;
606
+ }
427
607
  const top = ranked[0];
428
608
  if (!top || top.score < START_SCORE) return null;
429
609
  return top.hits >= START_TERMS || top.familyNamed || top.containerMatched