@astryxdesign/cli 0.6.3-canary.db4e378 → 0.6.3-canary.db52d98

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 (397) hide show
  1. package/README.md +38 -15
  2. package/api/blog/blog.doc.mjs +1 -0
  3. package/api/build/_adapter.d.mts +50 -0
  4. package/api/build/_adapter.mjs +60 -0
  5. package/api/build/build.doc.mjs +14 -7
  6. package/api/build/build.test.mjs +184 -6
  7. package/api/build/build.type.d.mts +57 -2
  8. package/api/build/build.type.mjs +30 -7
  9. package/api/build/help/help.d.mts +4 -0
  10. package/api/build/help/help.mjs +24 -11
  11. package/api/build/kit/kit.d.mts +4 -1
  12. package/api/build/kit/kit.mjs +165 -49
  13. package/api/build/kit/rank.d.mts +44 -0
  14. package/api/build/kit/rank.mjs +432 -0
  15. package/api/build/kit/rank.test.mjs +196 -0
  16. package/api/component/_adapter.d.mts +6 -12
  17. package/api/component/_adapter.mjs +20 -10
  18. package/api/component/component.doc.mjs +1 -0
  19. package/api/component/component.mjs +60 -12
  20. package/api/component/list/list.mjs +3 -2
  21. package/api/discover/discover.doc.mjs +1 -0
  22. package/api/docs/_adapter.d.mts +252 -34
  23. package/api/docs/_adapter.mjs +923 -132
  24. package/api/docs/detail/detail.mjs +11 -3
  25. package/api/docs/detail/section/section.mjs +24 -13
  26. package/api/docs/detail/section/section.test.mjs +15 -6
  27. package/api/docs/docs.d.mts +5 -3
  28. package/api/docs/docs.doc.mjs +39 -17
  29. package/api/docs/docs.mjs +44 -8
  30. package/api/docs/docs.test.mjs +158 -4
  31. package/api/docs/docs.type.d.mts +181 -2
  32. package/api/docs/docs.type.mjs +117 -3
  33. package/api/docs/index/index.mjs +11 -3
  34. package/api/docs/index/index.test.mjs +1 -1
  35. package/api/docs/integration-tree.test.mjs +555 -0
  36. package/api/docs/integrationDocs.test.mjs +14 -14
  37. package/api/docs/list/list.mjs +28 -12
  38. package/api/docs/node/node.d.mts +43 -0
  39. package/api/docs/node/node.mjs +192 -0
  40. package/api/docs/reference-blocks.test.mjs +406 -0
  41. package/api/doctor/doctor.d.mts +54 -4
  42. package/api/doctor/doctor.doc.mjs +1 -0
  43. package/api/doctor/doctor.mjs +331 -16
  44. package/api/doctor/doctor.test.mjs +420 -7
  45. package/api/gap-report/gap-report.doc.mjs +1 -0
  46. package/api/hook/_adapter.mjs +19 -5
  47. package/api/hook/hook.doc.mjs +1 -0
  48. package/api/hook/list/list.d.mts +1 -1
  49. package/api/hook/list/list.mjs +69 -17
  50. package/api/index.d.mts +1 -1
  51. package/api/index.mjs +1 -0
  52. package/api/init/init.doc.mjs +2 -1
  53. package/api/integration/add-contribution.d.mts +2 -1
  54. package/api/integration/add-contribution.mjs +114 -12
  55. package/api/integration/add-contribution.test.mjs +174 -3
  56. package/api/integration/add-theme.mjs +34 -64
  57. package/api/integration/add-theme.test.mjs +105 -21
  58. package/api/integration/authoring-checks.mjs +138 -28
  59. package/api/integration/authoring-checks.test.mjs +179 -7
  60. package/api/integration/authoring-checks.type.mjs +6 -1
  61. package/api/integration/integration-authoring.type.d.mts +2 -0
  62. package/api/integration/integration-authoring.type.mjs +2 -0
  63. package/api/integration/integration-block-exports.test.mjs +10 -6
  64. package/api/integration/integrationAdd.doc.mjs +7 -0
  65. package/api/integration/integrationAddAgentDoc.doc.mjs +1 -0
  66. package/api/integration/integrationAddCodemod.doc.mjs +1 -0
  67. package/api/integration/integrationAddComponent.doc.mjs +1 -0
  68. package/api/integration/integrationAddDoc.doc.mjs +8 -1
  69. package/api/integration/integrationAddTemplate.doc.mjs +1 -0
  70. package/api/integration/integrationAddTheme.doc.mjs +6 -5
  71. package/api/integration/integrationComponentConflicts.doc.mjs +1 -0
  72. package/api/integration/integrationDocConflicts.doc.mjs +2 -1
  73. package/api/integration/integrationPackCheck.doc.mjs +2 -1
  74. package/api/integration/integrationTemplateConflicts.doc.d.mts +3 -0
  75. package/api/integration/integrationTemplateConflicts.doc.mjs +4 -0
  76. package/api/integration/pack-check.mjs +34 -0
  77. package/api/integration/pack-check.test.mjs +138 -47
  78. package/api/integration/pack-check.type.d.mts +26 -2
  79. package/api/integration/pack-check.type.mjs +14 -1
  80. package/api/integration/summarizeIssues.doc.mjs +1 -0
  81. package/api/integration/template-conflict-compatibility.test.mjs +73 -0
  82. package/api/integration/validate-integration-fixes.test.mjs +1389 -0
  83. package/api/integration/validate-integration.mjs +50 -102
  84. package/api/integration/validate-integration.test.mjs +88 -26
  85. package/api/integration/validate-unread-theme-folders.test.mjs +110 -0
  86. package/api/integration/validateIntegration.doc.mjs +2 -1
  87. package/api/json/assertResponse.doc.mjs +1 -0
  88. package/api/json/index.ts +2 -0
  89. package/api/json/isError.doc.mjs +1 -0
  90. package/api/json/parseResponse.doc.mjs +1 -0
  91. package/api/layout/_adapter.mjs +20 -5
  92. package/api/layout/layoutCheck.doc.mjs +1 -0
  93. package/api/layout/layoutExpand.doc.mjs +1 -0
  94. package/api/layout/layoutGrammar.doc.mjs +1 -0
  95. package/api/search/search.d.mts +59 -1
  96. package/api/search/search.doc.mjs +5 -3
  97. package/api/search/search.mjs +458 -77
  98. package/api/search/search.test.mjs +91 -2
  99. package/api/search/search.type.d.mts +13 -1
  100. package/api/search/search.type.mjs +4 -1
  101. package/api/swizzle/swizzle.doc.mjs +1 -0
  102. package/api/template/list/list.mjs +1 -0
  103. package/api/template/table-floating-bulk-actions.test.mjs +66 -0
  104. package/api/template/template-integration.test.mjs +1072 -3
  105. package/api/template/template-suffix.test.mjs +41 -21
  106. package/api/template/template.doc.mjs +28 -7
  107. package/api/template/template.mjs +45 -8
  108. package/api/template/template.type.d.mts +6 -8
  109. package/api/template/template.type.mjs +3 -2
  110. package/api/theme/_adapter.d.mts +2 -3
  111. package/api/theme/_adapter.mjs +4 -5
  112. package/api/theme/add/add.binary.test.mjs +10 -17
  113. package/api/theme/add/add.test.mjs +14 -1
  114. package/api/theme/generateTonalPalette.doc.mjs +1 -0
  115. package/api/theme/integration-themes.test.mjs +39 -28
  116. package/api/theme/list/list.test.mjs +19 -20
  117. package/api/theme/listThemes.doc.mjs +6 -5
  118. package/api/theme/template/template.test.mjs +5 -0
  119. package/api/theme/themeAdd.doc.mjs +4 -3
  120. package/api/theme/themeBuild.doc.mjs +1 -0
  121. package/api/theme/themeList.doc.mjs +6 -3
  122. package/api/theme/themeListAvailable.doc.mjs +5 -3
  123. package/api/theme/themePaletteGenerate.doc.mjs +1 -0
  124. package/api/theme/themeTargets.doc.mjs +1 -0
  125. package/api/theme/themeTemplate.doc.mjs +2 -1
  126. package/api/upgrade/_adapter.d.mts +32 -5
  127. package/api/upgrade/_adapter.mjs +97 -69
  128. package/api/upgrade/provider-agreement.test.mjs +152 -0
  129. package/api/upgrade/run/run.mjs +356 -59
  130. package/api/upgrade/upgrade.doc.mjs +6 -1
  131. package/api/upgrade/upgrade.type.d.mts +34 -0
  132. package/api/upgrade/upgrade.type.mjs +15 -0
  133. package/assets/codemods/__tests__/runner.test.mjs +330 -8
  134. package/assets/codemods/integration-discovery.mjs +8 -2
  135. package/assets/codemods/integration-discovery.test.mjs +15 -0
  136. package/assets/codemods/integration-runner.mjs +56 -4
  137. package/assets/codemods/integration-runner.protection.test.mjs +153 -0
  138. package/assets/codemods/run-codemod.mjs +177 -34
  139. package/assets/codemods/runner.mjs +350 -102
  140. package/assets/codemods/transforms/next/__tests__/migrate-native-picker-to-presentation.test.mjs +63 -0
  141. package/assets/codemods/transforms/next/__tests__/migrate-theme-catalog-to-descriptors.test.mjs +220 -0
  142. package/assets/codemods/transforms/next/index.mjs +19 -1
  143. package/assets/codemods/transforms/next/migrate-native-picker-to-presentation.mjs +148 -0
  144. package/assets/codemods/transforms/next/migrate-theme-catalog-to-descriptors.mjs +141 -0
  145. package/assets/docs/getting-started.doc.mjs +2 -2
  146. package/assets/docs/layout.doc.dense.mjs +2 -2
  147. package/assets/docs/layout.doc.mjs +1 -1
  148. package/assets/docs/principles.doc.mjs +6 -6
  149. package/assets/docs/styling-libraries.doc.mjs +3 -3
  150. package/assets/docs/styling.doc.mjs +4 -4
  151. package/assets/docs/theme.doc.mjs +5 -5
  152. package/assets/docs/tokens.doc.mjs +1 -1
  153. package/assets/docs/tree/api.doc.mjs +30 -0
  154. package/assets/docs/tree/cli.doc.mjs +23 -0
  155. package/assets/docs/tree/commands.doc.mjs +25 -0
  156. package/assets/docs/{cli-integrations.doc.mjs → tree/integrations.doc.mjs} +66 -38
  157. package/assets/docs/tree/integrations.test.mjs +62 -0
  158. package/assets/docs/tree/writing-docs.doc.mjs +286 -0
  159. package/assets/docs/working-with-ai.doc.mjs +3 -3
  160. package/assets/templates/blocks/components/CheckboxList/CheckboxListSelectAllPattern.doc.mjs +1 -1
  161. package/assets/templates/blocks/components/CheckboxList/CheckboxListSelectAllPattern.tsx +1 -3
  162. package/assets/templates/blocks/components/DateInput/DateInputDateRange.tsx +1 -1
  163. package/assets/templates/blocks/components/Item/ItemDocumentTabs.doc.mjs +14 -0
  164. package/assets/templates/blocks/components/Item/ItemDocumentTabs.tsx +100 -0
  165. package/assets/templates/blocks/components/Table/TableBulkActionsTable.doc.mjs +14 -0
  166. package/assets/templates/blocks/components/Table/TableBulkActionsTable.tsx +71 -0
  167. package/assets/templates/blocks/components/Table/TableFloatingBulkActionsTable.doc.mjs +19 -0
  168. package/assets/templates/blocks/components/Table/TableFloatingBulkActionsTable.tsx +139 -0
  169. package/assets/templates/blocks/components/TimeInput/TimeInputConstrained.tsx +1 -0
  170. package/assets/templates/themes/butter/butterTheme.doc.mjs +11 -0
  171. package/assets/templates/themes/chocolate/chocolateTheme.doc.mjs +11 -0
  172. package/assets/templates/themes/gothic/gothicTheme.doc.mjs +11 -0
  173. package/assets/templates/themes/matcha/matchaTheme.doc.mjs +11 -0
  174. package/assets/templates/themes/neutral/neutralTheme.doc.mjs +11 -0
  175. package/assets/templates/themes/stone/stoneTheme.doc.mjs +11 -0
  176. package/assets/templates/themes/y2k/y2kTheme.doc.mjs +11 -0
  177. package/authoring/codemod/codemod.doc.mjs +1 -1
  178. package/authoring/codemod/type.ts +12 -0
  179. package/authoring/config/config.doc.mjs +1 -1
  180. package/authoring/debug/debug.doc.d.mts +11 -0
  181. package/authoring/debug/debug.doc.mjs +182 -0
  182. package/authoring/debug/parse.d.mts +3 -3
  183. package/authoring/doctypes/_schema.d.mts +119 -117
  184. package/authoring/doctypes/_schema.mjs +54 -3
  185. package/authoring/doctypes/base/graph-fields.doc.mjs +8 -6
  186. package/authoring/doctypes/base/type.ts +6 -5
  187. package/authoring/doctypes/command/command.doc.mjs +1 -1
  188. package/authoring/doctypes/command/type.ts +2 -2
  189. package/authoring/doctypes/component/type.ts +2 -2
  190. package/authoring/doctypes/doctypes-new.test.mjs +48 -6
  191. package/authoring/doctypes/enum/enum.doc.mjs +1 -1
  192. package/authoring/doctypes/enum/type.ts +1 -1
  193. package/authoring/doctypes/function/function.doc.mjs +3 -2
  194. package/authoring/doctypes/function/type.ts +3 -2
  195. package/authoring/doctypes/hook/type.ts +2 -2
  196. package/authoring/doctypes/load-contract.test.mjs +28 -2
  197. package/authoring/doctypes/namespace/namespace.doc.mjs +6 -10
  198. package/authoring/doctypes/namespace/parse.test.mjs +23 -25
  199. package/authoring/doctypes/namespace/type.ts +5 -2
  200. package/authoring/doctypes/parse.d.mts +4 -2
  201. package/authoring/doctypes/parse.mjs +10 -5
  202. package/authoring/doctypes/reference/reference.doc.mjs +35 -6
  203. package/authoring/doctypes/reference/type.ts +30 -13
  204. package/authoring/doctypes/schema/schema.doc.mjs +1 -1
  205. package/authoring/doctypes/schema/type.ts +1 -2
  206. package/authoring/doctypes/template/parse.d.mts +2 -0
  207. package/authoring/doctypes/template/parse.mjs +4 -0
  208. package/authoring/doctypes/template/parse.test.mjs +18 -0
  209. package/authoring/doctypes/template/template.doc.mjs +9 -3
  210. package/authoring/doctypes/template/type.ts +8 -0
  211. package/authoring/doctypes/theme/parse.d.mts +35 -0
  212. package/authoring/doctypes/theme/parse.mjs +76 -0
  213. package/authoring/doctypes/theme/theme.doc.d.mts +9 -0
  214. package/authoring/doctypes/theme/theme.doc.mjs +79 -0
  215. package/authoring/doctypes/theme/type.ts +42 -0
  216. package/authoring/doctypes/types.ts +2 -1
  217. package/authoring/gap-report/gap-report.doc.d.mts +12 -0
  218. package/authoring/gap-report/gap-report.doc.mjs +183 -0
  219. package/authoring/identity/identity.doc.mjs +2 -2
  220. package/authoring/index.d.mts +1 -0
  221. package/authoring/index.d.ts +7 -4
  222. package/authoring/index.mjs +2 -1
  223. package/authoring/integration/integration.doc.mjs +2 -2
  224. package/authoring/integration/type.ts +4 -10
  225. package/clients/cli/__tests__/cliManifest.test.ts +27 -29
  226. package/clients/cli/commands/blog.doc.mjs +1 -1
  227. package/clients/cli/commands/build-theme.adaptations.test.mjs +100 -1
  228. package/clients/cli/commands/build-theme.mjs +11 -45
  229. package/clients/cli/commands/build.doc.mjs +12 -7
  230. package/clients/cli/commands/build.mjs +105 -67
  231. package/clients/cli/commands/build.text-fields.test.mjs +41 -0
  232. package/clients/cli/commands/component/index.mjs +1 -1
  233. package/clients/cli/commands/component-ownership.test.mjs +3 -3
  234. package/clients/cli/commands/component.doc.mjs +1 -1
  235. package/clients/cli/commands/discover.broken-integration.test.mjs +7 -8
  236. package/clients/cli/commands/discover.doc.mjs +1 -1
  237. package/clients/cli/commands/discover.mjs +1 -1
  238. package/clients/cli/commands/docs.doc.mjs +21 -9
  239. package/clients/cli/commands/docs.mjs +184 -70
  240. package/clients/cli/commands/docs.test.mjs +120 -16
  241. package/clients/cli/commands/doctor-integration-components.doc.mjs +1 -1
  242. package/clients/cli/commands/doctor-integration-docs.doc.mjs +3 -3
  243. package/clients/cli/commands/doctor-integration-templates.doc.mjs +15 -8
  244. package/clients/cli/commands/doctor-integration-validate.doc.mjs +1 -1
  245. package/clients/cli/commands/doctor-integration.doc.mjs +1 -1
  246. package/clients/cli/commands/doctor-integration.test.mjs +90 -8
  247. package/clients/cli/commands/doctor.doc.mjs +1 -1
  248. package/clients/cli/commands/doctor.mjs +53 -16
  249. package/clients/cli/commands/gap-report.doc.mjs +1 -1
  250. package/clients/cli/commands/hook.doc.mjs +1 -1
  251. package/clients/cli/commands/init.doc.mjs +1 -1
  252. package/clients/cli/commands/integration-add.controls.test.mjs +1 -1
  253. package/clients/cli/commands/integration-add.doc.mjs +11 -1
  254. package/clients/cli/commands/integration-pack.doc.mjs +1 -1
  255. package/clients/cli/commands/integration-real-world.test.mjs +3 -9
  256. package/clients/cli/commands/integration.doc.mjs +1 -1
  257. package/clients/cli/commands/integration.mjs +1 -0
  258. package/clients/cli/commands/layout-check.doc.mjs +1 -1
  259. package/clients/cli/commands/layout-expand.doc.mjs +1 -1
  260. package/clients/cli/commands/layout-grammar.doc.mjs +1 -1
  261. package/clients/cli/commands/layout.doc.mjs +1 -1
  262. package/clients/cli/commands/manifest.doc.mjs +1 -1
  263. package/clients/cli/commands/search.doc.mjs +1 -1
  264. package/clients/cli/commands/search.mjs +11 -2
  265. package/clients/cli/commands/setup-nudge.test.mjs +6 -0
  266. package/clients/cli/commands/swizzle.doc.mjs +1 -1
  267. package/clients/cli/commands/template.doc.mjs +24 -7
  268. package/clients/cli/commands/template.mjs +4 -91
  269. package/clients/cli/commands/text-json-parity.test.mjs +719 -0
  270. package/clients/cli/commands/theme-add.doc.mjs +2 -2
  271. package/clients/cli/commands/theme-build.doc.mjs +1 -1
  272. package/clients/cli/commands/theme-list.doc.mjs +2 -2
  273. package/clients/cli/commands/theme-palette-generate.doc.mjs +3 -3
  274. package/clients/cli/commands/theme-palette.doc.mjs +1 -1
  275. package/clients/cli/commands/theme-targets.behavior.test.mjs +4 -3
  276. package/clients/cli/commands/theme-targets.doc.mjs +1 -1
  277. package/clients/cli/commands/theme-template.doc.mjs +1 -1
  278. package/clients/cli/commands/theme.doc.mjs +1 -1
  279. package/clients/cli/commands/upgrade.doc.mjs +3 -2
  280. package/clients/cli/commands/upgrade.file-protection.test.mjs +228 -0
  281. package/clients/cli/commands/upgrade.mjs +29 -7
  282. package/clients/cli/formatters/index.mjs +2 -0
  283. package/clients/cli/formatters/index.test.mjs +6 -0
  284. package/clients/cli/index.mjs +8 -0
  285. package/clients/cli/lib/hook-format.mjs +14 -5
  286. package/clients/cli/lib/manifest.mjs +9 -2
  287. package/foundation/agent-docs/agent-docs.d.mts +3 -2
  288. package/foundation/agent-docs/agent-docs.mjs +13 -9
  289. package/foundation/agent-docs/agent-docs.test.mjs +19 -1
  290. package/foundation/config/project-themes.test.mjs +11 -19
  291. package/foundation/config/project.d.mts +20 -11
  292. package/foundation/config/project.mjs +123 -80
  293. package/foundation/config/project.test.mjs +144 -21
  294. package/foundation/discovery/authoring-self-docs.d.mts +18 -0
  295. package/foundation/discovery/authoring-self-docs.mjs +37 -15
  296. package/foundation/discovery/authoring-self-docs.test.mjs +25 -9
  297. package/foundation/discovery/authoring-surface.d.mts +74 -0
  298. package/foundation/discovery/authoring-surface.mjs +525 -0
  299. package/foundation/discovery/authoring-surface.test.mjs +392 -0
  300. package/foundation/discovery/cli-self-docs.d.mts +119 -0
  301. package/foundation/discovery/cli-self-docs.mjs +490 -0
  302. package/foundation/discovery/cli-self-docs.test.mjs +375 -0
  303. package/foundation/discovery/component-discovery.d.mts +38 -0
  304. package/foundation/discovery/component-discovery.mjs +48 -0
  305. package/foundation/discovery/component-loader.d.mts +35 -38
  306. package/foundation/discovery/component-loader.mjs +53 -222
  307. package/foundation/discovery/docs-discovery.d.mts +112 -11
  308. package/foundation/discovery/docs-discovery.mjs +231 -36
  309. package/foundation/discovery/docs-discovery.test.mjs +111 -32
  310. package/foundation/discovery/docs-output-budget.d.mts +2 -2
  311. package/foundation/discovery/docs-output-budget.mjs +1 -1
  312. package/foundation/discovery/docs-section-key.d.mts +28 -10
  313. package/foundation/discovery/docs-section-key.mjs +134 -33
  314. package/foundation/discovery/docs-section-key.test.mjs +41 -19
  315. package/foundation/discovery/template-adapter.d.mts +99 -6
  316. package/foundation/discovery/template-adapter.mjs +480 -57
  317. package/foundation/discovery/template-adapter.test.mjs +57 -0
  318. package/foundation/discovery/template-conflict-release.d.mts +13 -0
  319. package/foundation/discovery/template-conflict-release.mjs +40 -0
  320. package/foundation/discovery/template-conflict-release.test.mjs +40 -0
  321. package/foundation/discovery/theme-discovery.d.mts +67 -7
  322. package/foundation/discovery/theme-discovery.mjs +916 -186
  323. package/foundation/discovery/theme-discovery.test.mjs +613 -219
  324. package/foundation/doc-compiler/bundle.d.mts +47 -0
  325. package/foundation/doc-compiler/bundle.mjs +278 -0
  326. package/foundation/doc-compiler/bundle.test.mjs +266 -0
  327. package/foundation/doc-compiler/compile.d.mts +220 -39
  328. package/foundation/doc-compiler/compile.mjs +311 -15
  329. package/foundation/doc-compiler/diagnostics.d.mts +126 -0
  330. package/foundation/doc-compiler/diagnostics.mjs +305 -0
  331. package/foundation/doc-compiler/doc-compiler.test.mjs +28 -1
  332. package/foundation/doc-compiler/doc-loads.test.mjs +1642 -0
  333. package/foundation/doc-compiler/import.d.mts +24 -0
  334. package/foundation/doc-compiler/import.mjs +59 -0
  335. package/foundation/doc-compiler/inputs.d.mts +102 -0
  336. package/foundation/doc-compiler/inputs.mjs +291 -0
  337. package/foundation/doc-compiler/inputs.test.mjs +299 -0
  338. package/foundation/doc-compiler/ir.d.mts +13 -0
  339. package/foundation/doc-compiler/ir.mjs +196 -12
  340. package/foundation/doc-compiler/lenses.d.mts +8 -5
  341. package/foundation/doc-compiler/lenses.mjs +50 -4
  342. package/foundation/doc-compiler/links.d.mts +162 -0
  343. package/foundation/doc-compiler/links.mjs +294 -0
  344. package/foundation/doc-compiler/links.test.mjs +192 -0
  345. package/foundation/doc-compiler/lower-doc.test.mjs +495 -0
  346. package/foundation/doc-compiler/overlays.d.mts +37 -0
  347. package/foundation/doc-compiler/overlays.mjs +206 -0
  348. package/foundation/doc-compiler/parse-readable.d.mts +9 -0
  349. package/foundation/doc-compiler/parse-readable.mjs +29 -0
  350. package/foundation/doc-compiler/read.d.mts +127 -0
  351. package/foundation/doc-compiler/read.mjs +325 -0
  352. package/foundation/doc-compiler/read.test.mjs +313 -0
  353. package/foundation/doc-compiler/source.d.mts +33 -0
  354. package/foundation/doc-compiler/source.mjs +128 -0
  355. package/foundation/doc-compiler/tree.d.mts +288 -0
  356. package/foundation/doc-compiler/tree.mjs +876 -0
  357. package/foundation/doc-compiler/tree.test.mjs +598 -0
  358. package/foundation/fs/file-protection.d.mts +33 -0
  359. package/foundation/fs/file-protection.mjs +825 -0
  360. package/foundation/fs/file-protection.test.mjs +250 -0
  361. package/foundation/integrations/autolink.d.mts +58 -1
  362. package/foundation/integrations/autolink.mjs +143 -57
  363. package/foundation/integrations/autolink.test.mjs +1 -1
  364. package/foundation/integrations/cli-requirement.d.mts +45 -0
  365. package/foundation/integrations/cli-requirement.mjs +154 -0
  366. package/foundation/integrations/cli-requirement.test.mjs +84 -0
  367. package/foundation/integrations/contribution-fixes.d.mts +145 -0
  368. package/foundation/integrations/contribution-fixes.mjs +1284 -0
  369. package/foundation/integrations/contribution-inventory.d.mts +3 -2
  370. package/foundation/integrations/contribution-inventory.mjs +27 -24
  371. package/foundation/integrations/contribution-inventory.test.mjs +86 -27
  372. package/foundation/integrations/integration-warnings.d.mts +9 -2
  373. package/foundation/integrations/integration-warnings.mjs +51 -26
  374. package/foundation/integrations/integration-warnings.test.mjs +74 -1
  375. package/foundation/integrations/integrations.d.mts +3 -0
  376. package/foundation/integrations/integrations.mjs +11 -97
  377. package/foundation/integrations/provider-ledger.test.mjs +275 -0
  378. package/foundation/integrations/provider-resolution.d.mts +152 -0
  379. package/foundation/integrations/provider-resolution.mjs +576 -0
  380. package/foundation/integrations/provider-resolution.test.mjs +369 -0
  381. package/foundation/integrations/theme-descriptor.d.mts +8 -0
  382. package/foundation/integrations/theme-descriptor.mjs +44 -0
  383. package/foundation/integrations/validate-contributions.mjs +121 -29
  384. package/foundation/response/error-codes.d.mts +3 -1
  385. package/foundation/response/error-codes.d.ts +2 -0
  386. package/foundation/response/error-codes.doc.mjs +12 -2
  387. package/foundation/response/error-codes.mjs +7 -1
  388. package/foundation/response/error-codes.test.mjs +55 -11
  389. package/foundation/response/response-types.doc.d.mts +5 -1
  390. package/foundation/response/response-types.doc.mjs +20 -11
  391. package/foundation/response/response.doc.mjs +1 -1
  392. package/foundation/text/string-utils.d.mts +8 -0
  393. package/foundation/text/string-utils.mjs +40 -10
  394. package/foundation/xle/expand.mjs +1 -1
  395. package/foundation/xle/xle.test.mjs +13 -0
  396. package/package.json +10 -9
  397. package/assets/templates/themes/manifest.json +0 -95
@@ -12,13 +12,17 @@ export const doc = {
12
12
  type: 'command',
13
13
  name: 'docs',
14
14
  displayName: 'astryx docs',
15
- namespace: 'cli',
15
+ namespace: 'cli/commands',
16
16
  summary: 'Print reference docs',
17
17
  description:
18
- 'Reads the reference docs: with no topic it lists every topic; a topic prints that ' +
19
- 'full doc; `--index` lists its sections instead, each with the key to read it by; a ' +
20
- 'topic plus a section prints that section (by key, exact title, or a unique part of ' +
21
- 'a title).',
18
+ 'Reads the reference docs one level at a time: with no topic it lists every topic; a ' +
19
+ 'topic lists its sections, each with the key to read it by (a topic with one section ' +
20
+ 'prints whole); a topic plus a section prints that section; `--full` prints the whole ' +
21
+ 'topic, as does `--dense`. With `--json`, a topic returns its whole doc, as docs() ' +
22
+ 'does, and `--index` its section index. A route ' +
23
+ 'opens a node of the docs tree: `cli` lists its guides and reference, ' +
24
+ '`cli/api/functions` lists every API function, and `cli/api/functions/search` prints ' +
25
+ 'one.',
22
26
  fn: 'docs',
23
27
  args: [
24
28
  {name: 'topic', param: 'topic', required: false},
@@ -28,20 +32,28 @@ export const doc = {
28
32
  {
29
33
  flag: '--index',
30
34
  param: 'options.index',
31
- description: "List the topic's sections and their keys instead of printing the whole topic",
35
+ description: "List the topic's sections and their keys, even for a topic with one section",
36
+ },
37
+ {
38
+ flag: '--full',
39
+ description:
40
+ 'Print the whole topic instead of its sections (text; `--json` returns the whole topic unless --index). Cannot be set with --index',
32
41
  },
33
42
  ],
34
43
  examples: [
35
44
  {label: 'List topics', cli: 'astryx docs'},
36
- {label: 'One topic as JSON', cli: 'astryx docs spacing --json'},
37
- {label: "A topic's sections", cli: 'astryx docs theme --index'},
45
+ {label: "A topic's sections", cli: 'astryx docs theme'},
46
+ {label: 'A whole topic as JSON', cli: 'astryx docs spacing --json'},
38
47
  {label: 'One section', cli: 'astryx docs theme quick-start'},
48
+ {label: 'The CLI docs tree', cli: 'astryx docs cli'},
49
+ {label: 'One API function', cli: 'astryx docs cli/api/functions/search'},
50
+ {label: 'A whole guide', cli: 'astryx docs cli/integrations --full'},
39
51
  ],
40
52
  exitCodes: [
41
53
  {code: 0, when: 'success'},
42
54
  {
43
55
  code: 1,
44
- when: 'unknown topic, or a section that matches no section or more than one',
56
+ when: 'unknown topic or route, or a section that matches no section or more than one',
45
57
  },
46
58
  ],
47
59
  related: ['search', 'component', 'hook', 'template'],
@@ -3,18 +3,26 @@
3
3
  /**
4
4
  * @file docs command — Print Astryx reference docs
5
5
  *
6
- * A topic prints its whole doc; `--index` lists its sections instead, so a
7
- * reader can open one section by its key.
6
+ * In text, every read is one level: a topic lists its sections, so a reader can
7
+ * open one section by its key, and `--full` prints the whole topic. `--json`
8
+ * keeps the docs() contract: a topic returns its whole doc, and `--index` its
9
+ * sections.
8
10
  * Supports --detail (full|compact|brief) and --lang (en|zh|dense).
9
11
  *
10
12
  * Usage:
11
13
  * astryx docs List available topics
12
- * astryx docs <topic> Print the whole topic
13
- * astryx docs <topic> --index List the topic's sections
14
+ * astryx docs <topic> List the topic's sections (a topic
15
+ * with one section prints whole)
14
16
  * astryx docs <topic> <section> Print one section
17
+ * astryx docs <topic> --full Print the whole topic
18
+ * astryx docs <route> Open a node of the docs tree, such as
19
+ * cli, cli/api, or cli/api/functions/search
15
20
  */
16
21
 
17
- import {getCliInvocation} from '../../../foundation/env/package-manager.mjs';
22
+ import {
23
+ formatCliCommand,
24
+ getCliInvocation,
25
+ } from '../../../foundation/env/package-manager.mjs';
18
26
  import {jsonOut} from '../../../foundation/response/json.mjs';
19
27
  import {
20
28
  emit,
@@ -23,10 +31,9 @@ import {
23
31
  text,
24
32
  code,
25
33
  wrapText,
26
- displayWidth,
27
- WRAP_WIDTH,
28
34
  } from '../formatters/index.mjs';
29
35
  import {cliError} from '../lib/cli-error.mjs';
36
+ import {ERROR_CODES} from '../../../foundation/response/error-codes.mjs';
30
37
  import {defineCommand} from '../lib/define-command.mjs';
31
38
  import {resultSet} from '../../../foundation/debug/index.mjs';
32
39
  import {docs as docsApi} from '../../../api/docs/docs.mjs';
@@ -52,28 +59,6 @@ function formatTable(headers, rows) {
52
59
  return `${head}\n${sep}\n${body}`;
53
60
  }
54
61
 
55
- /**
56
- * A table too wide for {@link WRAP_WIDTH}: one `header: cell` line per cell and
57
- * a blank line between rows, so nothing runs off the side of a terminal.
58
- * @param {string[]} headers
59
- * @param {string[][]} rows
60
- * @returns {string}
61
- */
62
- function formatTableVertical(headers, rows) {
63
- const width = Math.max(...headers.map(h => h.length)) + 2;
64
- return rows
65
- .map(row =>
66
- headers
67
- .map((h, i) =>
68
- wrapText(`${`${h}:`.padEnd(width)}${row[i] ?? ''}`, {
69
- indent: ' '.repeat(width),
70
- }),
71
- )
72
- .join('\n'),
73
- )
74
- .join('\n\n');
75
- }
76
-
77
62
  /**
78
63
  * @param {string[]} headers
79
64
  * @param {string[][]} rows
@@ -91,7 +76,7 @@ function formatTableCompact(headers, rows) {
91
76
  function formatBlock(block, detail) {
92
77
  switch (block.type) {
93
78
  case 'prose':
94
- return wrapText(block.text);
79
+ return block.text;
95
80
 
96
81
  case 'heading':
97
82
  return `${'#'.repeat(block.level || 3)} ${block.text}`;
@@ -110,12 +95,7 @@ function formatBlock(block, detail) {
110
95
  if (detail === 'compact') {
111
96
  return formatTableCompact(block.headers, block.rows);
112
97
  }
113
- {
114
- const table = formatTable(block.headers, block.rows);
115
- return table.split('\n').some(line => displayWidth(line) > WRAP_WIDTH)
116
- ? formatTableVertical(block.headers, block.rows)
117
- : table;
118
- }
98
+ return formatTable(block.headers, block.rows);
119
99
 
120
100
  case 'list': {
121
101
  const prefix =
@@ -126,28 +106,16 @@ function formatBlock(block, detail) {
126
106
  : block.style === 'do'
127
107
  ? () => '+ '
128
108
  : () => '- ';
129
- return block.items
130
- .map((item, i) => {
131
- const head = prefix(i);
132
- return wrapText(`${head}${item}`, {indent: ' '.repeat(head.length)});
133
- })
134
- .join('\n');
109
+ return block.items.map((item, i) => `${prefix(i)}${item}`).join('\n');
135
110
  }
136
111
 
137
- case 'workflow':
138
- case 'collection':
139
- case 'reference':
140
- throw new Error(
141
- `Documentation block "${block.type}" requires the compiled graph renderer.`,
142
- );
143
-
144
112
  default:
145
113
  return null;
146
114
  }
147
115
  }
148
116
 
149
117
  /**
150
- * @param {import('@astryxdesign/cli/authoring').ReferenceSection} section
118
+ * @param {import('../../../api/docs/docs.type.mjs').DocsReadSection} section
151
119
  * @param {'full' | 'compact' | 'brief'} detail
152
120
  * @returns {string}
153
121
  */
@@ -167,27 +135,44 @@ function formatSection(section, detail) {
167
135
  }
168
136
 
169
137
  /**
170
- * @param {import('@astryxdesign/cli/authoring').ReferenceDoc} docs
138
+ * @param {import('../../../api/docs/docs.type.mjs').DocsReadDoc} docs
171
139
  * @param {'full' | 'compact' | 'brief'} detail
172
140
  * @returns {string}
173
141
  */
174
142
  function formatReferenceFull(docs, detail) {
175
143
  if (detail === 'brief') {
176
- const header = wrapText(`${docs.title}: ${docs.description}`);
144
+ const header = `${docs.title}: ${docs.description}`;
177
145
  const sections = docs.sections.map(s => formatSection(s, detail));
178
146
  return `${header}\n${sections.join('\n')}`;
179
147
  }
180
148
 
181
- const description = wrapText(docs.description);
182
149
  const header =
183
150
  detail === 'compact'
184
- ? `# ${docs.title}\n${description}`
185
- : `# ${docs.title}\n\n${description}`;
151
+ ? `# ${docs.title}\n${docs.description}`
152
+ : `# ${docs.title}\n\n${docs.description}`;
186
153
  const sections = docs.sections.map(s => formatSection(s, detail));
187
154
  const sep = detail === 'compact' ? '\n\n' : '\n\n';
188
155
  return `${header}\n\n${sections.join(sep)}`;
189
156
  }
190
157
 
158
+ /**
159
+ * The moves a read offers (spec:AST-047), one runnable command per line: up to
160
+ * the level it sits in, the item before and after it, and the docs it names.
161
+ * @param {import('../../../api/docs/docs.type.mjs').DocsLinks | undefined} links
162
+ * @returns {string[]}
163
+ */
164
+ function linkLines(links) {
165
+ if (!links) return [];
166
+ return [
167
+ `Up: ${formatCliCommand(links.up)}`,
168
+ ...(links.previous ? [`Previous: ${formatCliCommand(links.previous)}`] : []),
169
+ ...(links.next ? [`Next: ${formatCliCommand(links.next)}`] : []),
170
+ ...(links.related ?? []).map(
171
+ (command, i) => `${i === 0 ? 'Related: ' : ' '}${formatCliCommand(command)}`,
172
+ ),
173
+ ];
174
+ }
175
+
191
176
  /**
192
177
  * A topic's section index: what the topic is, one line per section with the
193
178
  * key to read it by, and how to read further.
@@ -196,21 +181,84 @@ function formatReferenceFull(docs, detail) {
196
181
  */
197
182
  function emitIndex(index, run) {
198
183
  emit(
199
- section(index.title, index.description ? wrapText(index.description) : undefined),
184
+ section(
185
+ index.title,
186
+ index.description ? wrapText(index.description) : undefined,
187
+ ),
188
+ // Summaries wrap rather than being cut: the summary is how a reader picks
189
+ // the one section to open.
200
190
  records(index.sections, {
201
191
  fields: ['id', 'title', 'summary'],
202
192
  layout: 'inline',
203
- overflow: 'truncate',
204
193
  }),
205
194
  text(
206
195
  [
207
196
  `Read one section: ${run} docs ${index.name} <section>`,
208
- `Read everything: ${run} docs ${index.name}`,
197
+ `Read everything: ${run} docs ${index.name} --full`,
198
+ ...linkLines(index.links),
209
199
  ].join('\n'),
210
200
  ),
211
201
  );
212
202
  }
213
203
 
204
+ /**
205
+ * One child row of a namespace: its route name, then the doc's own title when
206
+ * it is not the route name (`assertResponse()`, `search()`), then its summary.
207
+ * @param {import('../../../api/docs/docs.type.mjs').DocsNodeChild} child
208
+ * @returns {{name: string, summary: string}}
209
+ */
210
+ function childRow(child) {
211
+ return {
212
+ name: child.name,
213
+ summary:
214
+ child.title === child.name ? child.summary : `${child.title}: ${child.summary}`,
215
+ };
216
+ }
217
+
218
+ /**
219
+ * One node of the docs tree. A namespace lists each slot's children, one level
220
+ * down, with the command to open one; a typed doc prints its content. Both end
221
+ * with the way back up.
222
+ * @param {import('../../../api/docs/docs.type.mjs').DocsNode} node
223
+ * @param {'full' | 'compact' | 'brief'} detail
224
+ * @param {string} run
225
+ */
226
+ function emitNode(node, detail, run) {
227
+ if (node.kind === 'namespace') {
228
+ emit(
229
+ section(node.title, wrapText(node.summary)),
230
+ ...node.slots.flatMap(slot => [
231
+ // A namespace with one slot titled like itself needs no second heading.
232
+ ...(node.slots.length === 1 && slot.title === node.title
233
+ ? []
234
+ : [section(slot.title)]),
235
+ records(slot.children.map(childRow), {
236
+ fields: ['name', 'summary'],
237
+ layout: 'inline',
238
+ overflow: 'truncate',
239
+ }),
240
+ ]),
241
+ text(
242
+ [
243
+ // A child's route is its parent's route and its name, except a flat
244
+ // topic in the Unorganized level, which keeps its own name.
245
+ node.slots.every(slot =>
246
+ slot.children.every(child => child.route.startsWith(`${node.route}/`)),
247
+ )
248
+ ? `Open one: ${run} docs ${node.route}/<name>`
249
+ : `Open one: ${run} docs <name>`,
250
+ ...linkLines(node.links),
251
+ ].join('\n'),
252
+ ),
253
+ );
254
+ return;
255
+ }
256
+ emit(
257
+ code(formatSection({title: node.title, content: node.content}, detail)),
258
+ text(linkLines(node.links).join('\n')),
259
+ );
260
+ }
261
+
214
262
  /**
215
263
  * What the run answered with. A named topic (or one of its sections) resolves
216
264
  * or throws, so it is always a direct match of one doc; the bare form lists
@@ -219,7 +267,8 @@ function emitIndex(index, run) {
219
267
  * @param {import('../../../api/docs/docs.type.mjs').DocsListResponse
220
268
  * | import('../../../api/docs/docs.type.mjs').DocsIndexResponse
221
269
  * | import('../../../api/docs/docs.type.mjs').DocsDetailResponse
222
- * | import('../../../api/docs/docs.type.mjs').DocsDetailSectionResponse} result
270
+ * | import('../../../api/docs/docs.type.mjs').DocsDetailSectionResponse
271
+ * | import('../../../api/docs/docs.type.mjs').DocsNodeResponse} result
223
272
  * @returns {import('../../../foundation/debug/command-result.mjs').CommandResult}
224
273
  */
225
274
  function summarize(result) {
@@ -239,7 +288,7 @@ export function registerDocs(program) {
239
288
  action: async (
240
289
  /** @type {string | undefined} */ topic,
241
290
  /** @type {string | undefined} */ sectionName,
242
- /** @type {{index?: boolean}} */ options = {},
291
+ /** @type {{index?: boolean, full?: boolean}} */ options = {},
243
292
  ) => {
244
293
  const run = getCliInvocation();
245
294
  const lang = program.opts().lang || null;
@@ -248,14 +297,37 @@ export function registerDocs(program) {
248
297
  const detail = program.opts().detail || 'full';
249
298
  const json = program.opts().json || false;
250
299
 
300
+ if (options.index && options.full) {
301
+ return cliError(
302
+ 'Ask for the section list or the whole topic, not both: --index and --full cannot both be set.',
303
+ {code: ERROR_CODES.ERR_INVALID_ARGUMENT},
304
+ );
305
+ }
306
+ // Text reads one level: a topic lists its sections (one with a single
307
+ // section prints whole). JSON keeps docs(): the whole topic unless
308
+ // --index. The dense variant is written to be read whole.
309
+ const listSections =
310
+ Boolean(options.index) ||
311
+ (!json &&
312
+ !options.full &&
313
+ !dense &&
314
+ lang !== 'dense' &&
315
+ sectionName == null);
251
316
  let result;
252
317
  try {
253
318
  result = await docsApi(topic, sectionName, {
254
319
  lang,
255
320
  zh,
256
321
  dense,
257
- index: Boolean(options.index),
322
+ index: listSections,
258
323
  });
324
+ if (
325
+ !options.index &&
326
+ result.type === 'docs.index' &&
327
+ result.data.sections.length <= 1
328
+ ) {
329
+ result = await docsApi(topic, sectionName, {lang, zh, dense});
330
+ }
259
331
  } catch (e) {
260
332
  // docs API throws structured errors with {name, reason} suggestions —
261
333
  // pass them through untouched so the CLI envelope matches the API.
@@ -275,21 +347,50 @@ export function registerDocs(program) {
275
347
 
276
348
  switch (result.type) {
277
349
  case 'docs.list': {
278
- // The text view mirrors the JSON list: one record per topic (topic +
279
- // description), then the usage footer as plain prose.
350
+ // The text view mirrors the JSON list, with the docs tree's
351
+ // namespaces (`meta.namespaces`) first under their own heading: that
352
+ // is where the CLI's own docs start, and a namespace reads
353
+ // differently from a topic.
354
+ const namespaces = result.meta?.namespaces ?? [];
355
+ const topics = result.data;
280
356
  emit(
281
- section('Available docs'),
282
- records(result.data, {
283
- fields: ['topic', 'description'],
357
+ ...(namespaces.length > 0
358
+ ? [
359
+ section('Docs tree'),
360
+ records(namespaces, {
361
+ fields: ['topic', 'description', 'package'],
362
+ layout: 'inline',
363
+ }),
364
+ ]
365
+ : []),
366
+ section('Topics'),
367
+ records(topics, {
368
+ fields: ['topic', 'description', 'package'],
284
369
  layout: 'inline',
285
370
  }),
286
371
  text(
287
372
  [
288
- `Usage: ${run} docs <topic> read the whole topic`,
289
- ` ${run} docs <topic> --index list its sections`,
373
+ `Usage: ${run} docs <topic> list a topic's sections`,
290
374
  ` ${run} docs <topic> <section> read one section`,
375
+ ` ${run} docs <topic> --full read the whole topic`,
376
+ ` ${run} docs cli/api go down the docs tree one level at a time`,
377
+ `With --json, a topic returns its whole doc; add --index for its section list.`,
291
378
  ].join('\n'),
292
379
  ),
380
+ // A package whose doc files did not load has no topics here; say
381
+ // so, and where to look, instead of leaving them silently missing.
382
+ ...(result.meta?.notLoaded?.length
383
+ ? [
384
+ section('Not loaded'),
385
+ records(result.meta.notLoaded, {
386
+ fields: ['package', 'message'],
387
+ layout: 'inline',
388
+ }),
389
+ text(
390
+ `Run \`${run} doctor integration docs\` in that package to see every problem.`,
391
+ ),
392
+ ]
393
+ : []),
293
394
  );
294
395
  break;
295
396
  }
@@ -300,12 +401,25 @@ export function registerDocs(program) {
300
401
  }
301
402
 
302
403
  case 'docs.detail': {
303
- emit(code(formatReferenceFull(result.data, detail)));
404
+ emit(
405
+ code(formatReferenceFull(result.data, detail)),
406
+ text(linkLines(result.data.links).join('\n')),
407
+ );
304
408
  break;
305
409
  }
306
410
 
307
411
  case 'docs.detail.section': {
308
- emit(code(formatSection(result.data, detail)));
412
+ // One section ends with its moves: up to its topic's index, and to
413
+ // the sections before and after it.
414
+ emit(
415
+ code(formatSection(result.data, detail)),
416
+ text(linkLines(result.data.links).join('\n')),
417
+ );
418
+ break;
419
+ }
420
+
421
+ case 'docs.node': {
422
+ emitNode(result.data, detail, run);
309
423
  break;
310
424
  }
311
425
  }
@@ -75,7 +75,7 @@ describe('hyphenated doc filenames', () => {
75
75
 
76
76
  it('returns docs.detail via API for hyphenated topic', async () => {
77
77
  const {docs: docsApi} = await import('../../../api/docs/docs.mjs');
78
- const result = await docsApi('getting-started');
78
+ const result = await docsApi('getting-started', undefined, {full: true});
79
79
  expect(result.type).toBe('docs.detail');
80
80
  expect(result.data).toBeDefined();
81
81
  expect(result.data.description).toBeDefined();
@@ -113,6 +113,7 @@ describe('progressive reads', () => {
113
113
  expect(status).toBe(0);
114
114
  expect(stdout).toMatch(/^principles +\S/m);
115
115
  expect(widest(stdout)).toBeLessThanOrEqual(120);
116
+ expect(stdout).toContain('Usage: pnpm exec astryx docs <topic>');
116
117
  }, SLOW);
117
118
 
118
119
  it("prints a topic's section index with the keys to read by", async () => {
@@ -120,7 +121,7 @@ describe('progressive reads', () => {
120
121
  expect(status).toBe(0);
121
122
  expect(stdout).toMatch(/^quick-start +Quick Start/m);
122
123
  expect(stdout).toContain('docs theme <section>');
123
- expect(stdout).toMatch(/Read everything: +\S.* docs theme$/m);
124
+ expect(stdout).toMatch(/Read everything: +\S.* docs theme --full$/m);
124
125
  expect(widest(stdout)).toBeLessThanOrEqual(120);
125
126
  }, SLOW);
126
127
 
@@ -130,23 +131,23 @@ describe('progressive reads', () => {
130
131
  expect(stdout).toMatch(/^## Quick Start/m);
131
132
  }, SLOW);
132
133
 
133
- it('prints the whole topic by default, as before', async () => {
134
+ it("lists a topic's sections by default; --full prints the whole topic", async () => {
134
135
  const index = await runCli(['docs', 'theme', '--index']);
135
- const full = await runCli(['docs', 'theme']);
136
+ expect((await runCli(['docs', 'theme'])).stdout).toBe(index.stdout);
137
+ const full = await runCli(['docs', 'theme', '--full']);
136
138
  expect(full.status).toBe(0);
137
139
  expect(full.stdout).toMatch(/^## Quick Start/m);
138
140
  expect(full.stdout.length).toBeGreaterThan(index.stdout.length * 3);
139
- expect((await runCli(['--detail', 'full', 'docs', 'theme'])).stdout).toBe(
141
+ expect((await runCli(['--detail', 'full', 'docs', 'theme', '--full'])).stdout).toBe(
140
142
  full.stdout,
141
143
  );
142
- expect(widest(full.stdout.replace(/```[\s\S]*?```/g, ''))).toBeLessThanOrEqual(
143
- 120,
144
- );
145
144
  }, SLOW);
146
145
 
147
146
  it('returns the matching envelopes as JSON', async () => {
148
147
  const envelope = async args => JSON.parse((await runCli([...args, '--json'])).stdout);
148
+ // JSON keeps docs(): a bare read is the whole topic.
149
149
  expect((await envelope(['docs', 'theme'])).type).toBe('docs.detail');
150
+ expect((await envelope(['docs', 'theme', '--full'])).type).toBe('docs.detail');
150
151
  expect((await envelope(['docs', 'theme', '--index'])).type).toBe(
151
152
  'docs.index',
152
153
  );
@@ -156,6 +157,109 @@ describe('progressive reads', () => {
156
157
  }, SLOW);
157
158
  });
158
159
 
160
+ describe('the docs tree, one level at a time', () => {
161
+ const SLOW = 60_000;
162
+ /** @param {string} out */
163
+ const widest = out => Math.max(...out.split('\n').map(line => line.length));
164
+
165
+ it('lists the docs tree first, under its own heading, then the topics', async () => {
166
+ const {status, stdout} = await runCli(['docs']);
167
+ expect(status).toBe(0);
168
+ expect(stdout).toMatch(/^Docs tree$/m);
169
+ expect(stdout).toMatch(/^Topics$/m);
170
+ expect(stdout).toMatch(/^cli +Commands, programmatic APIs/m);
171
+ expect(stdout.indexOf('\ncli ')).toBeLessThan(stdout.indexOf('\nprinciples '));
172
+ expect(stdout).not.toMatch(/^cli-integrations /m);
173
+ }, SLOW);
174
+
175
+ it("shows a child's own name when its route name does not spell it", async () => {
176
+ const {status, stdout} = await runCli(['docs', 'cli/api/functions']);
177
+ expect(status).toBe(0);
178
+ expect(stdout).toMatch(/^assert-response +assertResponse\(\): \S/m);
179
+ expect(stdout).toMatch(/^search +search\(\): Unified ranked search/m);
180
+ }, SLOW);
181
+
182
+ it('prints a namespace: each slot, its children, and how to go down and up', async () => {
183
+ const {status, stdout} = await runCli(['docs', 'cli/api']);
184
+ expect(status).toBe(0);
185
+ expect(stdout).toMatch(/^API$/m);
186
+ expect(stdout).toMatch(/^Reference$/m);
187
+ expect(stdout).toMatch(/^functions +Functions: Every function/m);
188
+ expect(stdout).toMatch(/^schemas +/m);
189
+ expect(stdout).toMatch(/^enums +/m);
190
+ // One level only: no function is listed on the api page.
191
+ expect(stdout).not.toMatch(/^search +/m);
192
+ expect(stdout).toMatch(/Open one: .*docs cli\/api\/<name>$/m);
193
+ expect(stdout).toMatch(/Up: .*docs cli$/m);
194
+ expect(widest(stdout)).toBeLessThanOrEqual(120);
195
+ }, SLOW);
196
+
197
+ it('prints a typed doc and the way back up', async () => {
198
+ const {status, stdout} = await runCli(['docs', 'cli/api/functions/search']);
199
+ expect(status).toBe(0);
200
+ expect(stdout).toMatch(/^## search\(\)/m);
201
+ expect(stdout).toContain('Read it with `astryx docs cli/commands/search`.');
202
+ expect(stdout).toMatch(/Up: .*docs cli\/api\/functions$/m);
203
+ }, SLOW);
204
+
205
+ it('reads the dense variant whole, asked with --dense or with --lang dense', async () => {
206
+ for (const args of [['--dense', 'docs', 'theme'], ['--lang', 'dense', 'docs', 'theme']]) {
207
+ const {status, stdout} = await runCli(args);
208
+ expect(status).toBe(0);
209
+ expect(stdout).not.toContain('Read one section:');
210
+ }
211
+ }, SLOW);
212
+
213
+ it('refuses --index with --full', async () => {
214
+ const both = await runCli(['docs', 'theme', '--index', '--full', '--json']);
215
+ expect(both.status).toBe(1);
216
+ expect(JSON.parse(both.stdout).code).toBe('ERR_INVALID_ARGUMENT');
217
+ }, SLOW);
218
+
219
+ it('reads the integration guide by its route, and not by its old name', async () => {
220
+ const guide = await runCli(['docs', 'cli/integrations', '--index']);
221
+ expect(guide.status).toBe(0);
222
+ expect(guide.stdout).toMatch(/Read one section: .*docs cli\/integrations <section>/);
223
+ // A bare read is one level too: the sections, and how to read it all.
224
+ const bare = await runCli(['docs', 'cli/integrations']);
225
+ expect(bare.status).toBe(0);
226
+ expect(bare.stdout).toBe(guide.stdout);
227
+ expect(bare.stdout).toMatch(/Read everything: +.*docs cli\/integrations --full$/m);
228
+ const one = await runCli(['docs', 'cli/integrations', 'codemods']);
229
+ expect(one.status).toBe(0);
230
+ expect(one.stdout).toMatch(/^## Codemods$/m);
231
+ expect(one.stdout).toMatch(/^Up: .*docs cli\/integrations --index$/m);
232
+ expect(one.stdout).toMatch(/^Previous: .*docs cli\/integrations agent-docs$/m);
233
+ expect(one.stdout).toMatch(/^Next: .*docs cli\/integrations recording-runs$/m);
234
+ const full = await runCli(['docs', 'cli/integrations', '--full']);
235
+ expect(full.status).toBe(0);
236
+ expect(full.stdout).toMatch(/^## Overview$/m);
237
+ expect(full.stdout.length).toBeGreaterThan(bare.stdout.length * 4);
238
+ const old = await runCli(['docs', 'cli-integrations']);
239
+ expect(old.status).toBe(1);
240
+ expect(old.stderr).toContain('Unknown topic "cli-integrations"');
241
+ }, SLOW);
242
+
243
+ it('returns docs.node as JSON, and fails a section of a namespace', async () => {
244
+ const node = JSON.parse((await runCli(['docs', 'cli', '--json'])).stdout);
245
+ expect(node).toMatchObject({
246
+ type: 'docs.node',
247
+ data: {route: 'cli', kind: 'namespace', breadcrumb: []},
248
+ });
249
+ expect(node.data.slots.map(slot => slot.name)).toEqual(['guides', 'reference']);
250
+ const section = await runCli(['docs', 'cli', 'commands', '--json']);
251
+ expect(section.status).toBe(1);
252
+ const error = JSON.parse(section.stdout);
253
+ expect(error).toMatchObject({code: 'ERR_UNKNOWN_SECTION'});
254
+ expect(error.suggestions.map(s => s.name)).toEqual([
255
+ 'cli/integrations',
256
+ 'cli/writing-docs',
257
+ 'cli/commands',
258
+ 'cli/api',
259
+ ]);
260
+ }, SLOW);
261
+ });
262
+
159
263
  describe('text width in every language', () => {
160
264
  const SLOW = 60_000;
161
265
  /** Widest line outside code blocks, in terminal columns. */
@@ -176,14 +280,14 @@ describe('text width in every language', () => {
176
280
  return max;
177
281
  };
178
282
 
179
- it.each([
180
- [['docs', 'theme', '--index', '--lang', 'zh']],
181
- [['docs', 'theme', '--lang', 'zh']],
182
- [['--detail', 'full', 'docs', 'theme', '--lang', 'zh']],
183
- [['--detail', 'full', 'docs', 'internationalization']],
184
- [['--detail', 'full', 'docs', 'styling']],
185
- ])('%j fits in 120 columns', async args => {
186
- const {status, stdout} = await runCli(args);
283
+ it('keeps the additive section index within 120 columns', async () => {
284
+ const {status, stdout} = await runCli([
285
+ 'docs',
286
+ 'theme',
287
+ '--index',
288
+ '--lang',
289
+ 'zh',
290
+ ]);
187
291
  expect(status).toBe(0);
188
292
  expect(widest(stdout)).toBeLessThanOrEqual(120);
189
293
  }, SLOW);
@@ -7,7 +7,7 @@ export const doc = {
7
7
  type: 'command',
8
8
  name: 'doctor integration components',
9
9
  displayName: 'astryx doctor integration components',
10
- namespace: 'cli',
10
+ namespace: 'cli/commands',
11
11
  summary: 'Warn when integration component names conflict with Core',
12
12
  description:
13
13
  'Compares one local or installed integration with Core component names. A ' +