@astryxdesign/cli 0.6.4-canary.06c8fa3 → 0.6.4-canary.0e1fbdb

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 (254) hide show
  1. package/README.md +49 -40
  2. package/api/build/build.doc.mjs +6 -1
  3. package/api/build/build.test.mjs +22 -0
  4. package/api/build/kit/kit.mjs +44 -5
  5. package/api/component/component.doc.mjs +14 -7
  6. package/api/docs/_adapter.d.mts +8 -3
  7. package/api/docs/_adapter.mjs +14 -6
  8. package/api/docs/docOverlays.test.mjs +27 -1
  9. package/api/docs/docs.doc.mjs +2 -2
  10. package/api/doctor/doctor.doc.mjs +17 -8
  11. package/api/doctor/doctor.type.d.mts +1 -1
  12. package/api/doctor/doctor.type.mjs +1 -1
  13. package/api/gap-report/gap-report.doc.mjs +19 -10
  14. package/api/hook/hook.doc.mjs +6 -3
  15. package/api/index.d.mts +2 -0
  16. package/api/index.mjs +3 -1
  17. package/api/init/init.doc.mjs +17 -12
  18. package/api/integration/add-theme.mjs +22 -1
  19. package/api/integration/add-theme.test.mjs +34 -0
  20. package/api/integration/authoring-checks.mjs +2 -2
  21. package/api/integration/integrationPackCheck.doc.mjs +3 -3
  22. package/api/integration/pack-check.lifecycle-output.test.mjs +2 -0
  23. package/api/integration/pack-check.mjs +54 -6
  24. package/api/integration/pack-check.test.mjs +90 -0
  25. package/api/integration/pack-check.type.mjs +1 -1
  26. package/api/json/assertResponse.doc.mjs +1 -1
  27. package/api/json/index.ts +1 -0
  28. package/api/json/isError.doc.mjs +1 -1
  29. package/api/layout/_adapter.d.mts +34 -0
  30. package/api/layout/_adapter.mjs +148 -0
  31. package/api/layout/check/check.d.mts +16 -0
  32. package/api/layout/check/check.mjs +40 -0
  33. package/api/layout/expand/expand.d.mts +22 -0
  34. package/api/layout/expand/expand.mjs +155 -0
  35. package/api/layout/expand/expand.path-safety.test.mjs +53 -0
  36. package/api/layout/grammar/grammar.d.mts +13 -0
  37. package/api/layout/grammar/grammar.mjs +87 -0
  38. package/api/layout/layout.d.mts +6 -0
  39. package/api/layout/layout.mjs +17 -0
  40. package/api/layout/layout.test.mjs +297 -0
  41. package/api/layout/layout.type.d.mts +89 -0
  42. package/api/layout/layout.type.mjs +103 -0
  43. package/api/layout/layoutCheck.doc.d.mts +11 -0
  44. package/api/layout/layoutCheck.doc.mjs +85 -0
  45. package/api/layout/layoutExpand.doc.d.mts +11 -0
  46. package/api/layout/layoutExpand.doc.mjs +107 -0
  47. package/api/layout/layoutGrammar.doc.d.mts +11 -0
  48. package/api/layout/layoutGrammar.doc.mjs +57 -0
  49. package/api/search/search.d.mts +27 -1
  50. package/api/search/search.doc.mjs +2 -2
  51. package/api/search/search.mjs +228 -16
  52. package/api/swizzle/swizzle.doc.mjs +7 -5
  53. package/api/template/copy/copy.mjs +1 -1
  54. package/api/template/copy/copy.test.mjs +9 -0
  55. package/api/template/template-integration.test.mjs +65 -1
  56. package/api/template/template.doc.mjs +2 -1
  57. package/api/template/template.mjs +1 -1
  58. package/api/theme/generateTonalPalette.doc.mjs +1 -2
  59. package/api/theme/listThemes.doc.mjs +1 -1
  60. package/api/theme/themeAdd.doc.mjs +9 -10
  61. package/api/theme/themeBuild.doc.mjs +13 -13
  62. package/api/theme/themeList.doc.mjs +1 -1
  63. package/api/theme/themeListAvailable.doc.mjs +2 -1
  64. package/api/theme/themePaletteGenerate.doc.mjs +15 -8
  65. package/api/theme/themeTargets.doc.mjs +3 -2
  66. package/api/theme/themeTemplate.doc.mjs +2 -1
  67. package/api/upgrade/run/run.mjs +1 -1
  68. package/api/upgrade/upgrade.doc.mjs +24 -22
  69. package/assets/docs/README.md +4 -2
  70. package/assets/docs/browser-support.doc.mjs +11 -11
  71. package/assets/docs/color.doc.mjs +8 -2
  72. package/assets/docs/elevation.doc.mjs +6 -4
  73. package/assets/docs/getting-started.doc.mjs +5 -16
  74. package/assets/docs/icons.doc.mjs +2 -21
  75. package/assets/docs/illustrations.doc.mjs +7 -15
  76. package/assets/docs/layout.doc.dense.mjs +130 -82
  77. package/assets/docs/layout.doc.mjs +133 -77
  78. package/assets/docs/migration.doc.mjs +19 -21
  79. package/assets/docs/motion.doc.mjs +16 -3
  80. package/assets/docs/principles.doc.dense.mjs +5 -5
  81. package/assets/docs/principles.doc.mjs +8 -0
  82. package/assets/docs/principles.doc.zh.mjs +6 -6
  83. package/assets/docs/shape.doc.mjs +8 -3
  84. package/assets/docs/spacing.doc.mjs +7 -2
  85. package/assets/docs/styling-libraries.doc.mjs +6 -2
  86. package/assets/docs/styling.doc.mjs +19 -23
  87. package/assets/docs/theme.doc.dense.mjs +58 -18
  88. package/assets/docs/theme.doc.mjs +56 -46
  89. package/assets/docs/theme.doc.zh.mjs +9 -8
  90. package/assets/docs/tokens.doc.dense.mjs +2 -2
  91. package/assets/docs/tokens.doc.mjs +389 -8
  92. package/assets/docs/tokens.doc.zh.mjs +2 -2
  93. package/assets/docs/tree/add-a-component.doc.mjs +75 -0
  94. package/assets/docs/tree/add-a-theme.doc.mjs +85 -0
  95. package/assets/docs/tree/add-a-topic.doc.mjs +144 -0
  96. package/assets/docs/tree/agent-guidance.doc.mjs +138 -0
  97. package/assets/docs/tree/block-template.doc.mjs +130 -0
  98. package/assets/docs/tree/build-the-template.doc.mjs +28 -0
  99. package/assets/docs/tree/building-blocks.doc.mjs +46 -0
  100. package/assets/docs/tree/check-your-docs.doc.mjs +137 -0
  101. package/assets/docs/tree/checks.doc.mjs +119 -0
  102. package/assets/docs/tree/codemods.doc.mjs +147 -0
  103. package/assets/docs/tree/component-family.doc.mjs +113 -0
  104. package/assets/docs/tree/component-imports.doc.mjs +69 -0
  105. package/assets/docs/tree/components.doc.mjs +23 -0
  106. package/assets/docs/tree/configuration.doc.mjs +23 -0
  107. package/assets/docs/tree/debug-and-gap-reports.doc.mjs +182 -0
  108. package/assets/docs/tree/define-the-theme.doc.mjs +118 -0
  109. package/assets/docs/tree/describe-the-component.doc.mjs +57 -0
  110. package/assets/docs/tree/docs.doc.mjs +21 -0
  111. package/assets/docs/tree/document-the-template.doc.mjs +28 -0
  112. package/assets/docs/tree/document-the-theme.doc.mjs +68 -0
  113. package/assets/docs/tree/export-template-assets.doc.mjs +147 -0
  114. package/assets/docs/tree/extend-or-replace.doc.mjs +103 -0
  115. package/assets/docs/tree/fonts-and-assets.doc.mjs +106 -0
  116. package/assets/docs/tree/generate-a-palette.doc.mjs +66 -0
  117. package/assets/docs/tree/grade-template-with-agent.doc.mjs +105 -0
  118. package/assets/docs/tree/help.doc.mjs +16 -0
  119. package/assets/docs/tree/integrations.doc.mjs +25 -470
  120. package/assets/docs/tree/links.doc.mjs +98 -0
  121. package/assets/docs/tree/package-and-test.doc.mjs +32 -0
  122. package/assets/docs/tree/page-template.doc.mjs +71 -0
  123. package/assets/docs/tree/publishing.doc.mjs +111 -0
  124. package/assets/docs/tree/quick-start.doc.mjs +272 -0
  125. package/assets/docs/tree/replace-a-core-component.doc.mjs +104 -0
  126. package/assets/docs/tree/replace-a-core-template.doc.mjs +172 -0
  127. package/assets/docs/tree/sections-and-placement.doc.mjs +108 -0
  128. package/assets/docs/tree/see-it-in-an-app.doc.mjs +59 -0
  129. package/assets/docs/tree/ship.doc.mjs +16 -0
  130. package/assets/docs/tree/short-and-findable.doc.mjs +108 -0
  131. package/assets/docs/tree/single-component.doc.mjs +165 -0
  132. package/assets/docs/tree/start-a-template.doc.mjs +143 -0
  133. package/assets/docs/tree/subcomponent.doc.mjs +115 -0
  134. package/assets/docs/tree/template-assets.doc.mjs +64 -0
  135. package/assets/docs/tree/template-doc-overview.doc.mjs +109 -0
  136. package/assets/docs/tree/template-fonts.doc.mjs +102 -0
  137. package/assets/docs/tree/template-grading-rubric.doc.mjs +452 -0
  138. package/assets/docs/tree/template-icons.doc.mjs +97 -0
  139. package/assets/docs/tree/template-images-media.doc.mjs +127 -0
  140. package/assets/docs/tree/template-styles.doc.mjs +93 -0
  141. package/assets/docs/tree/templates.doc.mjs +34 -0
  142. package/assets/docs/tree/test-in-an-app.doc.mjs +115 -0
  143. package/assets/docs/tree/test-template-in-app.doc.mjs +128 -0
  144. package/assets/docs/tree/themes.doc.mjs +39 -0
  145. package/assets/docs/tree/troubleshooting.doc.mjs +149 -0
  146. package/assets/docs/tree/upgrading.doc.mjs +103 -0
  147. package/assets/docs/tree/use-a-theme-in-an-app.doc.mjs +51 -0
  148. package/assets/docs/tree/verify-packed-template.doc.mjs +77 -0
  149. package/assets/docs/tree/versioning.doc.mjs +161 -0
  150. package/assets/docs/tree/write-good-templates.doc.mjs +64 -0
  151. package/assets/docs/tree/write-the-template-file.doc.mjs +154 -0
  152. package/assets/docs/typography.doc.mjs +24 -4
  153. package/assets/docs/working-with-ai.doc.mjs +30 -22
  154. package/authoring/config/config.doc.mjs +2 -2
  155. package/authoring/config/type.ts +2 -2
  156. package/authoring/doctypes/_schema.d.mts +3 -2
  157. package/authoring/doctypes/_schema.mjs +6 -0
  158. package/authoring/doctypes/base/graph-fields.doc.mjs +3 -3
  159. package/authoring/doctypes/base/type.ts +4 -2
  160. package/authoring/doctypes/command/command.doc.mjs +1 -1
  161. package/authoring/doctypes/command/type.ts +1 -1
  162. package/authoring/doctypes/component/component.doc.mjs +6 -0
  163. package/authoring/doctypes/component/type.ts +8 -0
  164. package/authoring/doctypes/reference/reference.doc.mjs +7 -0
  165. package/authoring/doctypes/reference/type.ts +5 -0
  166. package/authoring/doctypes/schema/schema.doc.mjs +2 -2
  167. package/authoring/doctypes/template/template.doc.mjs +1 -1
  168. package/authoring/doctypes/template/type.ts +2 -2
  169. package/authoring/integration/integration.doc.mjs +12 -10
  170. package/clients/cli/command-result-coverage.test.mjs +7 -7
  171. package/clients/cli/commands/component.doc.mjs +4 -3
  172. package/clients/cli/commands/debug-result-summary.test.mjs +2 -2
  173. package/clients/cli/commands/docs.doc.mjs +1 -1
  174. package/clients/cli/commands/docs.mjs +60 -17
  175. package/clients/cli/commands/doctor-integration-docs.doc.mjs +3 -2
  176. package/clients/cli/commands/doctor-integration.test.mjs +53 -0
  177. package/clients/cli/commands/doctor.doc.mjs +3 -1
  178. package/clients/cli/commands/doctor.mjs +49 -5
  179. package/clients/cli/commands/gap-report.doc.mjs +10 -9
  180. package/clients/cli/commands/init.doc.mjs +9 -6
  181. package/clients/cli/commands/integration-add.doc.mjs +9 -9
  182. package/clients/cli/commands/integration-authoring.test.mjs +61 -10
  183. package/clients/cli/commands/integration-pack.doc.mjs +5 -9
  184. package/clients/cli/commands/integration-real-world.test.mjs +1 -1
  185. package/clients/cli/commands/integration-verify.doc.mjs +22 -0
  186. package/clients/cli/commands/integration.doc.mjs +4 -4
  187. package/clients/cli/commands/integration.mjs +74 -43
  188. package/clients/cli/commands/layout-check.doc.mjs +65 -0
  189. package/clients/cli/commands/layout-expand.doc.mjs +83 -0
  190. package/clients/cli/commands/layout-grammar.doc.mjs +30 -0
  191. package/clients/cli/commands/layout.doc.mjs +34 -0
  192. package/clients/cli/commands/layout.error-codes.test.mjs +66 -0
  193. package/clients/cli/commands/layout.exit-parity.test.mjs +41 -0
  194. package/clients/cli/commands/layout.mjs +275 -0
  195. package/clients/cli/commands/layout.path-help.test.mjs +33 -0
  196. package/clients/cli/commands/layout.stdin-cap.test.mjs +47 -0
  197. package/clients/cli/commands/layout.text-fields.test.mjs +39 -0
  198. package/clients/cli/commands/manifest.doc.mjs +1 -1
  199. package/clients/cli/commands/search.doc.mjs +10 -3
  200. package/clients/cli/commands/search.mjs +21 -2
  201. package/clients/cli/commands/search.test.mjs +21 -4
  202. package/clients/cli/commands/swizzle.doc.mjs +1 -1
  203. package/clients/cli/commands/template.doc.mjs +1 -1
  204. package/clients/cli/commands/text-json-parity.test.mjs +24 -1
  205. package/clients/cli/commands/theme-add.doc.mjs +1 -1
  206. package/clients/cli/commands/theme-palette-generate.doc.mjs +3 -2
  207. package/clients/cli/commands/theme-palette.doc.mjs +1 -2
  208. package/clients/cli/commands/theme-targets.doc.mjs +2 -2
  209. package/clients/cli/commands/theme.doc.mjs +2 -1
  210. package/clients/cli/commands/upgrade.doc.mjs +62 -3
  211. package/clients/cli/index.mjs +32 -6
  212. package/clients/cli/lib/define-command.mjs +28 -4
  213. package/clients/cli/lib/define-command.test.mjs +54 -0
  214. package/clients/cli/lib/exit-codes.test.mjs +25 -2
  215. package/clients/cli/lib/json-shim.test.mjs +20 -6
  216. package/clients/cli/lib/manifest.mjs +23 -5
  217. package/foundation/agent-docs/agent-docs.mjs +1 -1
  218. package/foundation/discovery/authoring-self-docs.test.mjs +6 -2
  219. package/foundation/discovery/cli-self-docs.mjs +16 -2
  220. package/foundation/discovery/cli-self-docs.test.mjs +20 -0
  221. package/foundation/discovery/docs-discovery.mjs +5 -1
  222. package/foundation/discovery/docs-discovery.test.mjs +21 -0
  223. package/foundation/discovery/docs-section-key.d.mts +1 -1
  224. package/foundation/discovery/docs-section-key.mjs +1 -1
  225. package/foundation/discovery/template-adapter.mjs +1 -1
  226. package/foundation/doc-compiler/doc-loads.test.mjs +15 -2
  227. package/foundation/doc-compiler/inputs.test.mjs +0 -1
  228. package/foundation/doc-compiler/tree.d.mts +4 -0
  229. package/foundation/doc-compiler/tree.mjs +6 -1
  230. package/foundation/integrations/cli-requirement.d.mts +26 -6
  231. package/foundation/integrations/cli-requirement.mjs +46 -11
  232. package/foundation/integrations/cli-requirement.test.mjs +7 -2
  233. package/foundation/integrations/contribution-inventory.mjs +1 -1
  234. package/foundation/response/error-codes.doc.mjs +6 -8
  235. package/foundation/response/error-codes.test.mjs +30 -5
  236. package/foundation/response/response-types.doc.d.mts +4 -3
  237. package/foundation/response/response-types.doc.mjs +42 -6
  238. package/foundation/response/response.doc.mjs +11 -10
  239. package/foundation/xle/browser.d.mts +3 -3
  240. package/foundation/xle/browser.mjs +3 -3
  241. package/foundation/xle/expand.mjs +2 -2
  242. package/foundation/xle/parse.mjs +1 -1
  243. package/foundation/xle/print.mjs +2 -2
  244. package/foundation/xle/splice.mjs +1 -1
  245. package/package.json +9 -9
  246. package/api/docs/docs.test.mjs +0 -245
  247. package/api/docs/integration-tree.test.mjs +0 -555
  248. package/api/docs/integrationDocs.test.mjs +0 -314
  249. package/api/search/search.test.mjs +0 -530
  250. package/assets/docs/tree/integrations.test.mjs +0 -62
  251. package/assets/docs/tree/writing-docs.doc.mjs +0 -286
  252. package/clients/cli/commands/docs.test.mjs +0 -323
  253. package/foundation/agent-docs/agent-docs.test.mjs +0 -1159
  254. package/foundation/doc-compiler/tree.test.mjs +0 -606
@@ -13,7 +13,8 @@ export const doc = {
13
13
  name: 'theme',
14
14
  displayName: 'astryx theme',
15
15
  namespace: 'cli/commands',
16
- summary: 'Theme tools: build, export, and manage themes',
16
+ summary:
17
+ 'Create and build themes: add a shipped one, compile to CSS, or list what a theme can override',
17
18
  description:
18
19
  'The theme command group. Running astryx theme with no subcommand prints the ' +
19
20
  'subcommand list; the work happens in the subcommands: compile a theme (build), ' +
@@ -13,7 +13,8 @@ export const doc = {
13
13
  name: 'upgrade',
14
14
  displayName: 'astryx upgrade',
15
15
  namespace: 'cli/commands',
16
- summary: 'Migrate versions and update ShadCN-copied compositions',
16
+ summary:
17
+ 'Update your code after upgrading Astryx, and refresh ShadCN-copied components',
17
18
  description:
18
19
  'Migrates project source from a previous Astryx version to the installed one by ' +
19
20
  'running the registered codemods, and refreshes the fully rendered managed ' +
@@ -33,7 +34,7 @@ export const doc = {
33
34
  {
34
35
  flag: '--apply',
35
36
  param: 'options.apply',
36
- description: 'Write changes to disk (default: dry-run)',
37
+ description: 'Write changes to disk; without it, the run is a dry run',
37
38
  default: false,
38
39
  },
39
40
  {
@@ -80,7 +81,8 @@ export const doc = {
80
81
  flag: '--registry',
81
82
  param: 'options.registry',
82
83
  description:
83
- 'Only reconcile ShadCN-copied compositions; --from is not required. ' +
84
+ 'Only update ShadCN-copied compositions from their install receipts: unchanged files are updated, ' +
85
+ 'edits that do not overlap are merged, and conflicts are left untouched; --from is not required. ' +
84
86
  'Combining it with --list, --from, --force, --codemod, --skip-codemod, --integration or --install-deps exits 1 with ERR_INVALID_ARGUMENT',
85
87
  default: false,
86
88
  },
@@ -113,4 +115,61 @@ export const doc = {
113
115
  },
114
116
  ],
115
117
  related: ['init', 'doctor'],
118
+ notes: [
119
+ {type: 'heading', level: 3, text: 'Protected files'},
120
+ {
121
+ type: 'prose',
122
+ text:
123
+ 'Codemods never write to a file your project marks as generated, vendored, or ignored. ' +
124
+ 'upgrade reads these marks from the files on disk, so the answer is the same with any version control, or none. ' +
125
+ 'A file is protected when:',
126
+ },
127
+ {
128
+ type: 'list',
129
+ style: 'unordered',
130
+ items: [
131
+ 'a `.gitattributes` file marks it `linguist-generated` or `linguist-vendored`',
132
+ 'its leading comment says `@generated`, `@partially-generated`, or `Code generated ... DO NOT EDIT.`',
133
+ 'a `.gitignore`, or the `.hgignore` at the project root, excludes it',
134
+ 'it is an installed dependency (such as anything in `node_modules`), is inside `.git`, `.hg`, or `.sl`, is a symbolic link, or is outside the project',
135
+ ],
136
+ },
137
+ {
138
+ type: 'prose',
139
+ text:
140
+ 'Rules work as they do in Git: a later rule wins, so `linguist-generated=false` or a `!` line in an ignore file ' +
141
+ 'returns a file to normal handling. A folder name such as `dist` or `generated` protects nothing by itself. ' +
142
+ 'If a protection file cannot be read or parsed, upgrade stops with ERR_CODEMOD_PROTECTION_SOURCE before it writes anything.',
143
+ },
144
+ {
145
+ type: 'code',
146
+ lang: 'text',
147
+ code:
148
+ '# .gitattributes\n' +
149
+ 'generated/** linguist-generated=true\n' +
150
+ 'vendor/** linguist-vendored=true\n' +
151
+ '\n' +
152
+ '# A later rule returns one authored file to normal handling\n' +
153
+ 'generated/hand-authored.ts linguist-generated=false',
154
+ },
155
+ {
156
+ type: 'prose',
157
+ text:
158
+ 'When a codemod would change a protected file, upgrade makes the change only in memory and leaves the file as it is. ' +
159
+ 'The rest of the upgrade goes ahead. With --apply, your other files are written, then upgrade runs the ' +
160
+ '`hooks.postCodemod` commands from astryx.config (see `astryx docs authoring config`) and checks the protected ' +
161
+ 'files again. If one still needs the change, the run is incomplete: it exits 1, prints ' +
162
+ 'ERR_CODEMOD_PROTECTED with each file and the rule that protects it, and does not refresh the agent docs. ' +
163
+ 'Regenerate or edit those files, then run the same upgrade again. A dry run reports the same files and writes nothing.',
164
+ },
165
+ {
166
+ type: 'prose',
167
+ text:
168
+ 'With --json, the receipt says `complete: false` and `errorCode: "ERR_CODEMOD_PROTECTED"`. `modifiedFiles` lists the files ' +
169
+ 'upgrade changed (or would change), and `protectedFiles` lists each blocked file with its `reasons`, `declarations` ' +
170
+ '(the rules that protect it), `codemods`, and `commands`. A generated file can name the command that rebuilds it on a ' +
171
+ '`Command:` line in its header, such as `// Command: pnpm run gen:panel`; upgrade prints it as ' +
172
+ '`Regenerate with: <command>` and lists it in `commands`.',
173
+ },
174
+ ],
116
175
  };
@@ -24,7 +24,7 @@ import {emit, section, text, records} from './formatters/index.mjs';
24
24
  import {ERROR_CODES} from '../../foundation/response/error-codes.mjs';
25
25
  import {levenshteinDistance} from '../../foundation/text/string-utils.mjs';
26
26
  import {installJsonShim} from './lib/json-shim.mjs';
27
- import {addExitCodesHelp, markReportsResult} from './lib/define-command.mjs';
27
+ import {addDocHelp, markReportsResult} from './lib/define-command.mjs';
28
28
  import {doc as manifestDoc} from './commands/manifest.doc.mjs';
29
29
  import {isAstryxInitialized} from '../../foundation/agent-docs/agent-docs.mjs';
30
30
  import * as debug from '../../foundation/debug/index.mjs';
@@ -95,6 +95,7 @@ export const JSON_SUPPORTED = new Set([
95
95
  'theme targets',
96
96
  'theme palette generate',
97
97
  'integration add',
98
+ 'integration verify',
98
99
  'integration pack',
99
100
  'upgrade',
100
101
  'manifest',
@@ -103,6 +104,9 @@ export const JSON_SUPPORTED = new Set([
103
104
  'doctor integration templates',
104
105
  'doctor integration components',
105
106
  'doctor integration docs',
107
+ 'layout expand',
108
+ 'layout check',
109
+ 'layout grammar',
106
110
  ]);
107
111
 
108
112
  /**
@@ -261,6 +265,7 @@ const commands = [
261
265
  {name: 'gap-report', path: './commands/gap-report.mjs', register: 'registerGapReport'},
262
266
  // agent-docs folded into init — functions still importable from agent-docs.mjs
263
267
  {name: 'template', path: './commands/template.mjs', register: 'registerTemplate'},
268
+ {name: 'layout', path: './commands/layout.mjs', register: 'registerLayout'},
264
269
  {name: 'upgrade', path: './commands/upgrade.mjs', register: 'registerUpgrade'},
265
270
  {name: 'theme', path: './commands/build-theme.mjs', register: 'registerTheme'},
266
271
  {name: 'integration', path: './commands/integration.mjs', register: 'registerIntegration'},
@@ -338,16 +343,24 @@ export async function createProgram() {
338
343
  .name('astryx')
339
344
  .description('Design system CLI — components, themes, and tooling')
340
345
  .version(pkg.version)
341
- .option('--zh', 'Output docs in Chinese Simplified')
342
- .option('--dense', 'Output docs in compressed dense format (token-efficient)')
346
+ // These four change only the reads named in their text; every other
347
+ // command ignores them.
348
+ .option(
349
+ '--zh',
350
+ 'Simplified Chinese for component reads and for docs topics that have a translation (English otherwise)',
351
+ )
352
+ .option('--dense', 'Token-efficient dense text for component <Name> and docs <topic> reads')
343
353
  .addOption(
344
354
  new Option(
345
355
  '--lang <locale>',
346
- 'Output docs in specified language/format (en, zh, dense)',
356
+ 'Language or format for component and docs reads: en (default), zh (as --zh), or dense (as --dense)',
347
357
  ).choices(['en', 'zh', 'dense']),
348
358
  )
349
359
  .addOption(
350
- new Option('--detail <level>', 'Output detail level (full, compact, brief)')
360
+ new Option(
361
+ '--detail <level>',
362
+ 'Detail level for component, hook, and docs tree reads (e.g. docs cli/commands/build). Lists default to brief',
363
+ )
351
364
  .choices(['full', 'compact', 'brief'])
352
365
  .default('full'),
353
366
  )
@@ -467,6 +480,19 @@ export async function createProgram() {
467
480
  const fullName = fullCommandName(actionCommand, program);
468
481
  if (JSON_SUPPORTED.has(fullName)) return;
469
482
  process.__xdsJsonHandled = true;
483
+ // A group given a word it does not have reports an unknown subcommand and
484
+ // lists the ones it has, in JSON as in text, even when a flag follows it.
485
+ const extras = actionCommand.commands.length > 0 ? actionCommand.args : [];
486
+ const unknown = extras.find(arg => !String(arg).startsWith('-'));
487
+ if (unknown != null) {
488
+ cliError(`unknown subcommand '${fullName} ${unknown}'`, {
489
+ code: ERROR_CODES.ERR_UNKNOWN_SUBCOMMAND,
490
+ suggestions: actionCommand.commands.map(child => ({
491
+ name: child.name(),
492
+ reason: 'available subcommand',
493
+ })),
494
+ });
495
+ }
470
496
  debug.setOutcome('rejected', {
471
497
  exitCode: 1,
472
498
  code: ERROR_CODES.ERR_INVALID_OPTION,
@@ -601,7 +627,7 @@ export async function createProgram() {
601
627
  text(`Run \`${getCliInvocation()} manifest --json\` for the full structured manifest.`),
602
628
  );
603
629
  });
604
- addExitCodesHelp(manifestCommand, manifestDoc.exitCodes);
630
+ addDocHelp(manifestCommand, manifestDoc);
605
631
  markReportsResult(manifestCommand);
606
632
 
607
633
  // Hidden command used by package.json postinstall scripts
@@ -25,6 +25,8 @@
25
25
  */
26
26
 
27
27
  import {recordCommandResult} from '../../../foundation/debug/index.mjs';
28
+ import {routeSegment} from '../../../foundation/discovery/docs-section-key.mjs';
29
+ import {formatCliCommand} from '../../../foundation/env/package-manager.mjs';
28
30
  import {text} from '../formatters/index.mjs';
29
31
 
30
32
  /**
@@ -143,10 +145,11 @@ export function defineCommand(parent, doc, {fn, action} = {}) {
143
145
  cmd.addOption(option);
144
146
  }
145
147
 
146
- // Help ends with the documented exit codes. `choices` stay in the option
147
- // text: Commander `.choices()` would replace the api layer's
148
- // ERR_INVALID_ARGUMENT validation.
149
- addExitCodesHelp(cmd, doc.exitCodes);
148
+ // Help ends with the documented exit codes, the examples, and the docs
149
+ // route that reads the whole command. `choices` stay in the option text:
150
+ // Commander `.choices()` would replace the api layer's ERR_INVALID_ARGUMENT
151
+ // validation.
152
+ addDocHelp(cmd, doc);
150
153
 
151
154
  if (action) {
152
155
  // The recording seam. An action's job ends at "here is what I answered
@@ -165,6 +168,27 @@ export function defineCommand(parent, doc, {fn, action} = {}) {
165
168
  return cmd;
166
169
  }
167
170
 
171
+ /**
172
+ * End `cmd`'s help with what its CommandDoc says: the exit codes, then the
173
+ * examples, then `More:`, the `astryx docs` route that reads the whole command.
174
+ * @param {import('commander').Command} cmd
175
+ * @param {import('@astryxdesign/cli/authoring').CommandDoc} doc
176
+ */
177
+ export function addDocHelp(cmd, doc) {
178
+ addExitCodesHelp(cmd, doc.exitCodes);
179
+ // Rendered when help is shown, so the run prefix (npx astryx, pnpm astryx,
180
+ // ...) is looked up then, not on every start.
181
+ cmd.addHelpText('after', () => {
182
+ const examples = (doc.examples ?? []).flatMap(({label, cli}) => [
183
+ ...(label ? [` # ${label}`] : []),
184
+ ` ${formatCliCommand(cli)}`,
185
+ ]);
186
+ const more = `More: ${formatCliCommand(`docs cli/commands/${routeSegment(doc.name)}`)}`;
187
+ const blocks = examples.length > 0 ? [['Examples:', ...examples].join('\n'), more] : [more];
188
+ return `\n${text(blocks.join('\n\n')).toString()}`;
189
+ });
190
+ }
191
+
168
192
  /**
169
193
  * End `cmd`'s help with a CommandDoc's exit codes.
170
194
  * @param {import('commander').Command} cmd
@@ -7,6 +7,7 @@
7
7
  import {Command} from 'commander';
8
8
  import {describe, it, expect} from 'vitest';
9
9
  import {defineCommand} from './define-command.mjs';
10
+ import {formatCliCommand} from '../../../foundation/env/package-manager.mjs';
10
11
  import {doc as searchCommand} from '../commands/search.doc.mjs';
11
12
  import {doc as searchFn} from '../../../api/search/search.doc.mjs';
12
13
 
@@ -46,4 +47,57 @@ describe('defineCommand', () => {
46
47
  expect(cmd.name()).toBe('build');
47
48
  expect(cmd.registeredArguments.map(a => a.name())).toEqual(['file']);
48
49
  });
50
+
51
+ it('ends help with the exit codes, the examples, and the docs route', () => {
52
+ const program = new Command();
53
+ const group = program.command('grp');
54
+ const cmd = defineCommand(
55
+ group,
56
+ {
57
+ type: 'command',
58
+ name: 'grp sub',
59
+ displayName: 'astryx grp sub',
60
+ summary: 'Sub.',
61
+ examples: [
62
+ {label: 'Run it', cli: 'astryx grp sub x'},
63
+ {cli: 'astryx grp sub y --json'},
64
+ ],
65
+ exitCodes: [{code: 0, when: 'it works'}],
66
+ },
67
+ {action: () => {}},
68
+ );
69
+ let out = '';
70
+ cmd.configureOutput({writeOut: s => (out += s)});
71
+ cmd.outputHelp();
72
+ const stem = formatCliCommand('');
73
+ expect(out.slice(out.indexOf('\nExit codes:\n'))).toBe(
74
+ [
75
+ '',
76
+ 'Exit codes:',
77
+ ' 0 it works',
78
+ '',
79
+ 'Examples:',
80
+ ' # Run it',
81
+ ` ${stem} grp sub x`,
82
+ ` ${stem} grp sub y --json`,
83
+ '',
84
+ `More: ${stem} docs cli/commands/grp-sub`,
85
+ '',
86
+ ].join('\n'),
87
+ );
88
+ });
89
+
90
+ it('still names the docs route when a command has no examples', () => {
91
+ const program = new Command();
92
+ const cmd = defineCommand(
93
+ program,
94
+ {type: 'command', name: 'solo', summary: 'Solo.', exitCodes: [{code: 0, when: 'ok'}]},
95
+ {action: () => {}},
96
+ );
97
+ let out = '';
98
+ cmd.configureOutput({writeOut: s => (out += s)});
99
+ cmd.outputHelp();
100
+ expect(out).not.toContain('Examples:');
101
+ expect(out.endsWith(`\n\nMore: ${formatCliCommand('docs cli/commands/solo')}\n`)).toBe(true);
102
+ });
49
103
  });
@@ -34,14 +34,14 @@ manifest.commands.forEach(walk);
34
34
 
35
35
  describe('command exit codes', () => {
36
36
  it('every CommandDoc documents its exit codes', () => {
37
- expect(commandDocs.length).toBeGreaterThan(25);
37
+ expect(commandDocs.length).toBeGreaterThan(30);
38
38
  for (const doc of commandDocs) {
39
39
  expect(doc.exitCodes?.length, doc.name).toBeGreaterThan(0);
40
40
  }
41
41
  });
42
42
 
43
43
  it.each(commandDocs.map((d) => [d.name, d]))(
44
- '`astryx %s --help` lists the documented exit codes',
44
+ '`astryx %s --help` lists the documented exit codes, then the examples and the docs route',
45
45
  async (name, doc) => {
46
46
  const {status, stdout} = await runCli([...name.split(' '), '--help']);
47
47
  expect(status).toBe(0);
@@ -51,9 +51,32 @@ describe('command exit codes', () => {
51
51
  for (const {code, when} of doc.exitCodes) {
52
52
  expect(section).toContain(`\n ${code} ${when}\n`);
53
53
  }
54
+ // Examples follow the exit codes, each under its label, and a `More:`
55
+ // line names the route that reads the whole command.
56
+ const examples = section.indexOf('\nExamples:\n');
57
+ expect(examples > 0, stdout).toBe((doc.examples ?? []).length > 0);
58
+ for (const {label, cli} of doc.examples ?? []) {
59
+ const line = ` ${cli.replace(/^astryx\s+/, '')}\n`;
60
+ expect(section.slice(examples), stdout).toContain(
61
+ label ? `\n # ${label}\n` : line,
62
+ );
63
+ expect(section.slice(examples)).toContain(line);
64
+ }
65
+ const route = `docs cli/commands/${name.replace(/ /g, '-')}`;
66
+ expect(section, stdout).toMatch(
67
+ new RegExp(`\\n\\nMore: \\S.* ${route}\\n`),
68
+ );
69
+ expect(section.indexOf('\nMore: ')).toBeGreaterThan(examples);
54
70
  },
55
71
  );
56
72
 
73
+ it('bare `astryx layout` exits 1 in both modes, as documented', async () => {
74
+ const doc = commandDocs.find((d) => d.name === 'layout');
75
+ expect(doc.exitCodes.find((e) => e.code === 1)?.when).toMatch(/^no subcommand/);
76
+ expect((await runCli(['layout'])).status).toBe(1);
77
+ expect((await runCli(['layout', '--json'])).status).toBe(1);
78
+ });
79
+
57
80
  it('`astryx discover` with a blank query exits 1 only when packages are discovered', async () => {
58
81
  const doc = commandDocs.find((d) => d.name === 'discover');
59
82
  expect(doc.exitCodes.find((e) => e.code === 1)?.when).toMatch(/blank query when packages are discovered/);
@@ -225,6 +225,23 @@ describe('--json shim: help shown for a failed invocation is an error envelope',
225
225
  expect(parsed.error).toMatch(/bogus/);
226
226
  });
227
227
 
228
+ it('astryx layout --json (group, no subcommand) emits ERR_MISSING_ARGUMENT, exit 1', async () => {
229
+ const {status, stdout, stderr} = await runCli(['layout', '--json']);
230
+ expect(status).toBe(1);
231
+ expect(stderr).toBe('');
232
+ const parsed = parseJson(stdout);
233
+ expect(parsed).not.toHaveProperty('type');
234
+ expect(parsed.code).toBe('ERR_MISSING_ARGUMENT');
235
+ expect(parsed.suggestions.map((s) => s.name)).toContain('layout expand');
236
+ });
237
+
238
+ it('astryx layout (no --json) still prints help to stderr, exit 1', async () => {
239
+ const {status, stdout, stderr} = await runCli(['layout']);
240
+ expect(status).toBe(1);
241
+ expect(stdout).toBe('');
242
+ expect(stderr).toMatch(/Usage: astryx layout/);
243
+ });
244
+
228
245
  it('the real binary emits the same envelope for help bogus --json', () => {
229
246
  // One program per process, so the root's own outputHelp patch is used here.
230
247
  const bin = fileURLToPath(new URL('../bin/astryx.mjs', import.meta.url));
@@ -240,9 +257,6 @@ describe('--json shim: help shown for a failed invocation is an error envelope',
240
257
  });
241
258
 
242
259
  it('a group added after install gets the same error envelope', () => {
243
- // This is the only coverage of the no-subcommand path: every group the CLI
244
- // ships has an action of its own (see command-result-coverage.test.mjs), so
245
- // a group without one has to be built here.
246
260
  const program = new Command('astryx');
247
261
  installJsonShim(program);
248
262
  // Added later, so only the prototype-level patch covers its outputHelp.
@@ -268,11 +282,11 @@ describe('--json shim: help shown for a failed invocation is an error envelope',
268
282
  expect(parsed.suggestions).toEqual([{name: 'late child', reason: 'available subcommand'}]);
269
283
  });
270
284
 
271
- it('astryx help theme --json still emits the help envelope, exit 0', async () => {
272
- const {status, stdout} = await runCli(['help', 'theme', '--json']);
285
+ it('astryx help layout --json still emits the help envelope, exit 0', async () => {
286
+ const {status, stdout} = await runCli(['help', 'layout', '--json']);
273
287
  expect(status).toBe(0);
274
288
  const parsed = parseJson(stdout);
275
289
  expect(parsed.type).toBe('help');
276
- expect(parsed.data.command).toBe('astryx theme');
290
+ expect(parsed.data.command).toBe('astryx layout');
277
291
  });
278
292
  });
@@ -14,7 +14,8 @@
14
14
  * `type` discriminators it can emit — are layered on from:
15
15
  *
16
16
  * - JSON_SUPPORTED (the allowlist in index.mjs), and
17
- * - RESPONSE_TYPES (the declarative map below).
17
+ * - RESPONSE_TYPES (the declarative map below; ROOT_RESPONSE_TYPES names the
18
+ * two, help and version, that no single command owns).
18
19
  *
19
20
  * A drift-guard test (manifest.test.mjs) asserts every registered command
20
21
  * appears in the manifest and every JSON-supported command has a response-type
@@ -92,16 +93,28 @@ export const RESPONSE_TYPES = {
92
93
  'theme targets': ['theme.targets'],
93
94
  'theme palette generate': ['theme.palette.generate'],
94
95
  'integration add': ['integration.add'],
96
+ 'integration verify': ['integration.pack-check'],
95
97
  'integration pack': ['integration.pack-check'],
96
- upgrade: ['upgrade.list', 'upgrade.status', 'upgrade.run'],
98
+ upgrade: ['upgrade.list', 'upgrade.registry', 'upgrade.status', 'upgrade.run'],
97
99
  manifest: ['manifest'],
98
100
  doctor: ['doctor'],
99
101
  'doctor integration validate': ['integration.validate'],
100
102
  'doctor integration templates': ['integration.template-conflicts'],
101
103
  'doctor integration components': ['integration.component-conflicts'],
102
104
  'doctor integration docs': ['integration.doc-conflicts'],
105
+ 'layout expand': ['layout.expand'],
106
+ 'layout check': ['layout.check'],
107
+ 'layout grammar': ['layout.grammar'],
103
108
  };
104
109
 
110
+ /**
111
+ * Response types no single command owns: `help` (a bare `astryx --json`, and
112
+ * `--help --json` on any command) and `version` (`astryx --version --json`).
113
+ * The response-types enum lists them beside {@link RESPONSE_TYPES}.
114
+ * @type {readonly string[]}
115
+ */
116
+ export const ROOT_RESPONSE_TYPES = Object.freeze(['help', 'version']);
117
+
105
118
  /**
106
119
  * Example invocations per fully-qualified command. Optional, agent-facing.
107
120
  * @type {Record<string, string[]>}
@@ -118,7 +131,7 @@ const EXAMPLES = {
118
131
  'astryx docs spacing --json',
119
132
  'astryx docs theme',
120
133
  'astryx docs theme quick-start',
121
- 'astryx docs cli/integrations --full',
134
+ 'astryx docs cli/integrations/quick-start --full',
122
135
  ],
123
136
  discover: ['astryx discover --json'],
124
137
  search: [
@@ -126,7 +139,7 @@ const EXAMPLES = {
126
139
  'astryx search button --type component --json',
127
140
  ],
128
141
  build: ['astryx build', 'astryx build "analytics dashboard" --json'],
129
- swizzle: ['astryx swizzle XDSButton'],
142
+ swizzle: ['astryx swizzle Button'],
130
143
  'gap-report': [
131
144
  'astryx gap-report --list-categories',
132
145
  "astryx gap-report Button --category docs_gap --reason 'Missing keyboard example'",
@@ -159,7 +172,7 @@ const EXAMPLES = {
159
172
  'astryx integration add component AcmeWidget',
160
173
  'astryx integration add doc deploying --dry-run --json',
161
174
  ],
162
- 'integration pack': ['astryx integration pack --check --json'],
175
+ 'integration verify': ['astryx integration verify --json'],
163
176
  upgrade: ['astryx upgrade --json'],
164
177
  manifest: ['astryx manifest --json', 'astryx --json'],
165
178
  doctor: ['astryx doctor', 'astryx doctor --json'],
@@ -180,6 +193,11 @@ const EXAMPLES = {
180
193
  'astryx doctor integration docs @acme/widgets --json',
181
194
  ],
182
195
  init: ['astryx init', 'astryx init --all --json'],
196
+ 'layout expand': [
197
+ `astryx layout expand 'V[g6] > C{card-callout}*4' ./src/Page.tsx`,
198
+ ],
199
+ 'layout check': [`astryx layout check 'A[cp6] > L > LC > S[p6]' --json`],
200
+ 'layout grammar': ['astryx layout grammar'],
183
201
  };
184
202
 
185
203
  /**
@@ -491,7 +491,7 @@ export function generateCompressedIndex(
491
491
  }
492
492
  lines.push(' docs cli commands, API reference, integration authoring (one level at a time)');
493
493
  lines.push(' swizzle <Name> eject component source for deep customization');
494
- lines.push(' upgrade --apply run after any Astryx or integration dependency bump');
494
+ lines.push(' upgrade --from <old version> --apply run after any Astryx or integration dependency bump');
495
495
  const appendCount = agentDocs.reduce(
496
496
  (count, contribution) => count + contribution.append.length,
497
497
  0,
@@ -63,7 +63,9 @@ describe('authoring self-docs', () => {
63
63
  describe('what the authoring docs say about the docs tree', () => {
64
64
  it('marks exactly the fields the docs tree does not read yet', () => {
65
65
  const notReadYet = graphFieldsDoc.fields
66
- .filter(field => /Not read yet/.test(field.description))
66
+ .filter(field =>
67
+ /^Reserved: .*Nothing reads it today/.test(field.description),
68
+ )
67
69
  .map(field => field.name);
68
70
  // The tree reads `placement` for every guide (spec:AST-046); the other
69
71
  // graph fields are still refused by every topic reader.
@@ -79,7 +81,9 @@ describe('what the authoring docs say about the docs tree', () => {
79
81
  expect(placement.description).toMatch(
80
82
  /Read for every guide, the CLI's and each integration's/,
81
83
  );
82
- expect(graphFieldsDoc.description).toMatch(/not built yet/);
84
+ expect(graphFieldsDoc.description).toMatch(
85
+ /Nothing reads `aliases` or `audience` today/,
86
+ );
83
87
  });
84
88
 
85
89
  it('keeps graph blocks behind the separate GraphContentBlock type, and a section takes a reference block', () => {
@@ -315,12 +315,20 @@ function functionSection(fn, index) {
315
315
  }
316
316
  const params = fn.params ?? [];
317
317
  if (params.length > 0) {
318
+ // A Default column only when some parameter declares a default, so a
319
+ // function with none keeps a three-column table.
320
+ const defaults = params.some(
321
+ (/** @type {any} */ p) => typeof p.default === 'string' && p.default !== '',
322
+ );
318
323
  content.push({
319
324
  type: 'table',
320
- headers: ['Parameter', 'Type', 'Description'],
325
+ headers: defaults
326
+ ? ['Parameter', 'Type', 'Default', 'Description']
327
+ : ['Parameter', 'Type', 'Description'],
321
328
  rows: params.map((/** @type {any} */ p) => [
322
- `\`${p.name}\``,
329
+ `\`${p.name}\`${p.required ? ' (required)' : ''}`,
323
330
  `\`${p.type ?? ''}\``,
331
+ ...(defaults ? [p.default ?? ''] : []),
324
332
  p.description ?? '',
325
333
  ]),
326
334
  });
@@ -405,6 +413,12 @@ export function cliDocSection(doc, index) {
405
413
  if (doc.type === 'command') return commandSection(doc, index);
406
414
  if (doc.type === 'function') return functionSection(doc, index);
407
415
  if (doc.type === 'enum') return enumSection(doc);
416
+ if (doc.type === 'namespace')
417
+ return {
418
+ id: routeSegment(doc.name),
419
+ title: doc.title,
420
+ content: doc.blocks ?? [],
421
+ };
408
422
  return {...selfDocSection(doc), id: routeSegment(doc.name)};
409
423
  }
410
424
 
@@ -315,6 +315,26 @@ describe('cliDocSection', () => {
315
315
  ]);
316
316
  });
317
317
 
318
+ it('marks required parameters and adds a Default column when a parameter has a default', () => {
319
+ const beta = fn('beta', {
320
+ params: [
321
+ {name: 'slug', type: 'string', description: 'The slug.', required: true},
322
+ {name: 'options.limit', type: 'number', description: 'How many.', default: '20'},
323
+ ],
324
+ });
325
+ const table = cliDocSection(beta, indexOf([beta])).content.find(
326
+ (/** @type {any} */ block) => block.type === 'table',
327
+ );
328
+ expect(table).toEqual({
329
+ type: 'table',
330
+ headers: ['Parameter', 'Type', 'Default', 'Description'],
331
+ rows: [
332
+ ['`slug` (required)', '`string`', '', 'The slug.'],
333
+ ['`options.limit`', '`number`', '20', 'How many.'],
334
+ ],
335
+ });
336
+ });
337
+
318
338
  it('renders an API function, an enum, and a schema', () => {
319
339
  const alpha = fn('alpha', {
320
340
  description: 'Longer.',
@@ -626,7 +626,11 @@ function findMergeTarget(sections, section) {
626
626
  const sameTitle = sections.findIndex(
627
627
  candidate => sourceTitle(candidate) === title,
628
628
  );
629
- return sameTitle;
629
+ if (sameTitle !== -1) return sameTitle;
630
+ // A base section retitled later keeps its old key as its `id`, so an
631
+ // extension that still names it by the old title finds it by that id. A
632
+ // title variant of a section with no id stays a separate section.
633
+ return sections.findIndex(candidate => candidate.id === key);
630
634
  }
631
635
 
632
636
  /**
@@ -509,6 +509,27 @@ describe('mergeTopic', () => {
509
509
  expect(base.sections[0].content[0].text).toBe('npm i');
510
510
  });
511
511
 
512
+ it('finds a retitled base section by the key its old title derives', () => {
513
+ // A base section retitled later keeps its old key as its `id`; an
514
+ // extension that still names it by the old title replaces it.
515
+ const merged = mergeTopic(
516
+ {
517
+ ...base,
518
+ sections: [
519
+ {id: 'quick-start', title: 'Wrap your app in a theme', content: []},
520
+ {id: 'tokens', title: 'Tokens', content: []},
521
+ ],
522
+ },
523
+ {sections: [{title: 'Quick Start', content: []}]},
524
+ );
525
+ expect(merged.sections.map(section => [section.id, section.title])).toEqual(
526
+ [
527
+ ['quick-start', 'Quick Start'],
528
+ ['tokens', 'Tokens'],
529
+ ],
530
+ );
531
+ });
532
+
512
533
  it('replaces a section by stable ID even when its title changes', () => {
513
534
  const merged = mergeTopic(
514
535
  {
@@ -32,7 +32,7 @@ export function sectionTitleKey(title: unknown): string;
32
32
  export function sectionKey(section: any): string;
33
33
  /**
34
34
  * A name as a docs-tree route segment: lowercase words joined by hyphens, so
35
- * `integrationPackCheck` and `integration pack` both read naturally.
35
+ * `integrationPackCheck` and `integration verify` both read naturally.
36
36
  * @param {string} name
37
37
  * @returns {string}
38
38
  */
@@ -95,7 +95,7 @@ function legacyTitleMatchIndex(sections, query) {
95
95
 
96
96
  /**
97
97
  * A name as a docs-tree route segment: lowercase words joined by hyphens, so
98
- * `integrationPackCheck` and `integration pack` both read naturally.
98
+ * `integrationPackCheck` and `integration verify` both read naturally.
99
99
  * @param {string} name
100
100
  * @returns {string}
101
101
  */
@@ -4,7 +4,7 @@
4
4
  * @file Shared template discovery + IO.
5
5
  *
6
6
  * Owns everything the template leaves (list/show/skeleton/copy) AND other
7
- * commands (component, search, init, discover, Doctor integration)
7
+ * commands (component, layout, search, init, discover, Doctor integration)
8
8
  * share: template discovery across core/external/integration sources, the
9
9
  * template-spec loaders, and the cross-command helpers (stripTemplateAssetRefs,
10
10
  * findShowcase, findRelatedBlocks, extractComponents, listTemplates). The