@astryxdesign/cli 0.6.3-canary.dea6813 → 0.6.3-canary.e7819e0

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 (176) hide show
  1. package/README.md +18 -32
  2. package/api/build/build.doc.mjs +3 -3
  3. package/api/build/build.test.mjs +3 -14
  4. package/api/build/build.type.d.mts +1 -35
  5. package/api/build/build.type.mjs +3 -24
  6. package/api/build/help/help.d.mts +5 -8
  7. package/api/build/help/help.mjs +6 -56
  8. package/api/component/component.doc.mjs +2 -11
  9. package/api/component/component.mjs +0 -29
  10. package/api/component/component.test.mjs +0 -38
  11. package/api/component/component.type.d.mts +1 -11
  12. package/api/component/component.type.mjs +1 -8
  13. package/api/discover/discover.doc.mjs +1 -1
  14. package/api/gap-report/gap-report.doc.mjs +4 -7
  15. package/api/init/init.doc.mjs +0 -4
  16. package/api/init/init.test.mjs +1 -41
  17. package/api/init/remove/remove.mjs +1 -1
  18. package/api/init/run/run.mjs +10 -20
  19. package/api/integration/add-contribution.mjs +0 -11
  20. package/api/integration/add-contribution.test.mjs +0 -80
  21. package/api/integration/integrationAdd.doc.mjs +4 -7
  22. package/api/integration/integrationAddComponent.doc.mjs +1 -1
  23. package/api/integration/validate-integration.mjs +0 -2
  24. package/api/integration/validateIntegration.doc.mjs +1 -1
  25. package/api/json/parseResponse.doc.mjs +2 -2
  26. package/api/layout/expand/expand.mjs +5 -7
  27. package/api/layout/grammar/grammar.mjs +1 -2
  28. package/api/layout/layoutExpand.doc.mjs +1 -1
  29. package/api/search/search.d.mts +9 -2
  30. package/api/search/search.doc.mjs +1 -5
  31. package/api/search/search.mjs +3 -10
  32. package/api/search/search.test.mjs +0 -34
  33. package/api/swizzle/copy/copy.mjs +11 -28
  34. package/api/swizzle/swizzle.doc.mjs +1 -1
  35. package/api/template/copy/copy.mjs +23 -17
  36. package/api/template/copy/copy.test.mjs +0 -17
  37. package/api/template/template.doc.mjs +1 -2
  38. package/api/theme/add/add.mjs +3 -20
  39. package/api/theme/build/build.mjs +37 -114
  40. package/api/theme/build/build.public-component-vars.test.mjs +1 -1
  41. package/api/theme/build/font-warning.mjs +3 -3
  42. package/api/theme/build/font-warning.test.mjs +2 -5
  43. package/api/theme/palette/generate/generate.mjs +2 -7
  44. package/api/theme/palette/generate/generate.test.mjs +0 -96
  45. package/api/theme/palette/generate/generator.mjs +1 -8
  46. package/api/theme/palette/generate/generator.test.mjs +0 -10
  47. package/api/theme/template/template.mjs +2 -11
  48. package/api/theme/template/template.test.mjs +0 -15
  49. package/api/theme/themeBuild.doc.mjs +4 -7
  50. package/api/theme/themeTemplate.doc.mjs +1 -4
  51. package/api/upgrade/_adapter.mjs +4 -27
  52. package/api/upgrade/list/list.mjs +1 -2
  53. package/api/upgrade/status/status.mjs +2 -2
  54. package/api/upgrade/upgrade.doc.mjs +1 -2
  55. package/api/upgrade/upgrade.type.d.mts +0 -4
  56. package/api/upgrade/upgrade.type.mjs +0 -1
  57. package/assets/codemods/term-log.mjs +8 -32
  58. package/assets/codemods/term-log.test.mjs +1 -19
  59. package/assets/codemods/transforms/v0.6.0/__tests__/next-codemods.test.mjs +0 -47
  60. package/assets/codemods/transforms/v0.6.0/rename-resizable-pixel-bounds.mjs +10 -57
  61. package/authoring/config/config.doc.mjs +1 -1
  62. package/authoring/config/type.ts +3 -15
  63. package/clients/cli/commands/blog.mjs +8 -23
  64. package/clients/cli/commands/blog.test.mjs +1 -42
  65. package/clients/cli/commands/build-theme.mjs +5 -5
  66. package/clients/cli/commands/build-theme.path-safety.test.mjs +1 -86
  67. package/clients/cli/commands/build-theme.variants.test.mjs +0 -76
  68. package/clients/cli/commands/build.doc.mjs +1 -4
  69. package/clients/cli/commands/build.mjs +47 -32
  70. package/clients/cli/commands/component/index.mjs +7 -2
  71. package/clients/cli/commands/component-package.test.mjs +0 -18
  72. package/clients/cli/commands/component-resolution.test.mjs +0 -21
  73. package/clients/cli/commands/component.test.mjs +0 -19
  74. package/clients/cli/commands/detail-levels.test.mjs +2 -2
  75. package/clients/cli/commands/discover.broken-integration.test.mjs +0 -48
  76. package/clients/cli/commands/discover.doc.mjs +2 -4
  77. package/clients/cli/commands/discover.mjs +3 -3
  78. package/clients/cli/commands/doctor.mjs +16 -6
  79. package/clients/cli/commands/doctor.test.mjs +0 -42
  80. package/clients/cli/commands/gap-report.doc.mjs +5 -16
  81. package/clients/cli/commands/gap-report.test.mjs +0 -72
  82. package/clients/cli/commands/hook/index.mjs +17 -7
  83. package/clients/cli/commands/init.doc.mjs +8 -19
  84. package/clients/cli/commands/integration-add.doc.mjs +5 -21
  85. package/clients/cli/commands/interactive-guard.test.mjs +24 -101
  86. package/clients/cli/commands/json-contract.test.mjs +0 -33
  87. package/clients/cli/commands/layout-check.doc.mjs +3 -14
  88. package/clients/cli/commands/layout-expand.doc.mjs +4 -21
  89. package/clients/cli/commands/layout.doc.mjs +2 -2
  90. package/clients/cli/commands/layout.mjs +9 -21
  91. package/clients/cli/commands/search.doc.mjs +3 -6
  92. package/clients/cli/commands/search.mjs +7 -17
  93. package/clients/cli/commands/search.test.mjs +0 -75
  94. package/clients/cli/commands/swizzle.doc.mjs +1 -2
  95. package/clients/cli/commands/swizzle.path-safety.test.mjs +0 -42
  96. package/clients/cli/commands/template.doc.mjs +6 -28
  97. package/clients/cli/commands/theme-add.doc.mjs +1 -2
  98. package/clients/cli/commands/theme-build.doc.mjs +6 -7
  99. package/clients/cli/commands/theme-palette-generate.test.mjs +0 -19
  100. package/clients/cli/commands/theme-template.behavior.test.mjs +0 -12
  101. package/clients/cli/commands/theme-template.doc.mjs +1 -1
  102. package/clients/cli/commands/upgrade.doc.mjs +6 -17
  103. package/clients/cli/index.mjs +30 -13
  104. package/clients/cli/lib/cli-error.test.mjs +0 -7
  105. package/clients/cli/lib/component-format.mjs +9 -9
  106. package/clients/cli/lib/component-format.test.mjs +1 -1
  107. package/clients/cli/lib/define-command.mjs +6 -32
  108. package/clients/cli/lib/hook-format.mjs +5 -5
  109. package/clients/cli/lib/json-shim.mjs +2 -38
  110. package/clients/cli/lib/json-shim.test.mjs +0 -83
  111. package/clients/cli/lib/manifest.d.ts +0 -2
  112. package/clients/cli/lib/manifest.mjs +0 -22
  113. package/clients/cli/lib/manifest.test.mjs +0 -17
  114. package/clients/cli/lib/update-check.mjs +83 -0
  115. package/clients/cli/lib/update-check.test.mjs +137 -0
  116. package/clients/cli/update-hint-commands.test.mjs +54 -0
  117. package/foundation/agent-docs/agent-docs.d.mts +0 -4
  118. package/foundation/agent-docs/agent-docs.mjs +3 -69
  119. package/foundation/agent-docs/agent-docs.path-safety.test.mjs +4 -266
  120. package/foundation/config/integration-debug.test.mjs +3 -28
  121. package/foundation/config/project.mjs +9 -123
  122. package/foundation/config/project.test.mjs +0 -126
  123. package/foundation/discovery/template-adapter.d.mts +5 -14
  124. package/foundation/discovery/template-adapter.mjs +25 -292
  125. package/foundation/fs/module-loader.d.mts +0 -1
  126. package/foundation/fs/module-loader.mjs +1 -50
  127. package/foundation/fs/publish-file-hardlink-unavailable.test.mjs +94 -129
  128. package/foundation/integrations/autolink.mjs +1 -6
  129. package/foundation/response/base.d.ts +4 -8
  130. package/foundation/response/error-codes.doc.mjs +2 -1
  131. package/foundation/response/error-codes.mjs +1 -1
  132. package/foundation/response/error-codes.test.mjs +0 -83
  133. package/foundation/response/json-contract.test.mjs +0 -11
  134. package/foundation/response/json.d.mts +2 -4
  135. package/foundation/response/json.mjs +10 -8
  136. package/foundation/response/response-types.doc.mjs +3 -3
  137. package/foundation/xle/expand.d.mts +0 -2
  138. package/foundation/xle/expand.mjs +2 -3
  139. package/package.json +9 -9
  140. package/api/integration/add-contribution.component-names.test.mjs +0 -120
  141. package/api/json/envelope-types.test.mjs +0 -76
  142. package/api/layout/expand/expand.path-safety.test.mjs +0 -53
  143. package/api/search/search-return-type.test.mjs +0 -54
  144. package/api/theme/add/add.binary.test.mjs +0 -91
  145. package/api/theme/add/add.staging.test.mjs +0 -66
  146. package/api/theme/build/build.receipt-doc.test.mjs +0 -111
  147. package/api/upgrade/list/list.test.mjs +0 -73
  148. package/authoring/config/debug-composition.test.mjs +0 -92
  149. package/clients/cli/command-load-failure.test.mjs +0 -83
  150. package/clients/cli/commands/build-theme.ascii-output.test.mjs +0 -161
  151. package/clients/cli/commands/build-theme.flag-docs.test.mjs +0 -110
  152. package/clients/cli/commands/build.exit-codes-doc.test.mjs +0 -50
  153. package/clients/cli/commands/build.playbook.test.mjs +0 -75
  154. package/clients/cli/commands/build.text-fields.test.mjs +0 -40
  155. package/clients/cli/commands/discover.components-flag.test.mjs +0 -97
  156. package/clients/cli/commands/discover.text-projection.test.mjs +0 -103
  157. package/clients/cli/commands/doctor-integration.package-json.test.mjs +0 -53
  158. package/clients/cli/commands/hook.text-projection.test.mjs +0 -45
  159. package/clients/cli/commands/init.flag-help.test.mjs +0 -153
  160. package/clients/cli/commands/integration-add.controls.test.mjs +0 -132
  161. package/clients/cli/commands/layout.path-help.test.mjs +0 -33
  162. package/clients/cli/commands/layout.stdin-cap.test.mjs +0 -47
  163. package/clients/cli/commands/layout.text-fields.test.mjs +0 -39
  164. package/clients/cli/commands/no-prompt-wording.test.mjs +0 -94
  165. package/clients/cli/commands/template.flag-help.test.mjs +0 -117
  166. package/clients/cli/commands/template.path-help.test.mjs +0 -40
  167. package/clients/cli/commands/upgrade.ascii-output.test.mjs +0 -87
  168. package/clients/cli/commands/upgrade.flag-help.test.mjs +0 -188
  169. package/clients/cli/commands/upgrade.hook-output.test.mjs +0 -88
  170. package/clients/cli/latest-version-env.test.mjs +0 -50
  171. package/clients/cli/lib/doc-text-ascii.test.mjs +0 -82
  172. package/clients/cli/lib/exit-codes.test.mjs +0 -97
  173. package/foundation/discovery/template-adapter.fixture-refs.test.mjs +0 -248
  174. package/foundation/discovery/template-adapter.integration-isolation.test.mjs +0 -94
  175. package/foundation/fs/module-loader.stdout.test.mjs +0 -332
  176. package/foundation/xle/expand.test.mjs +0 -54
package/README.md CHANGED
@@ -51,8 +51,8 @@ Options:
51
51
 
52
52
  - `--type <component|hook|doc|template>`: restrict to a single domain
53
53
  - `--limit <n>`: cap the number of results (default 20)
54
- - `--verbose`: also print each result's match score and reason
55
- - `--json`: typed `{ apiVersion, type: 'search', data: { query, matchCount, results } }` envelope — `matchCount` is how many candidates matched in total, `results` the slice `--limit` allowed
54
+ - `--detail`: include the import path and the match reason/score
55
+ - `--json`: typed `{ type: 'search', data: { query, matchCount, results } }` envelope — `matchCount` is how many candidates matched in total, `results` the slice `--limit` allowed
56
56
 
57
57
  ## Commands
58
58
 
@@ -84,7 +84,7 @@ Options:
84
84
 
85
85
  These flags work with any command:
86
86
 
87
- - `--json`: Output as typed JSON envelope: `{ apiVersion, type, data, meta? }` (errors: `{ apiVersion, error, code, suggestions? }`)
87
+ - `--json`: Output as typed JSON envelope: `{ type, data }` (errors: `{ error, code, suggestions? }`)
88
88
  - `--detail <level>`: Detail level for list views, increasing in size: `brief` (names only, default for `--list`) < `compact` (names + 1-line descriptions) < `full` (full docs per entry). Single-item views default to `full`.
89
89
  - `--zh`: Output docs in Chinese Simplified
90
90
  - `--dense`: Compressed format (token-efficient, useful for AI agents)
@@ -95,14 +95,13 @@ These flags work with any command:
95
95
  Every command supports `--json` for machine-readable output. Responses are typed envelopes:
96
96
 
97
97
  ```json
98
- {"apiVersion": 1, "type": "component.detail", "data": {"name": "Button", ...}}
98
+ {"type": "component.detail", "data": {"name": "Button", ...}}
99
99
  ```
100
100
 
101
101
  Errors:
102
102
 
103
103
  ```json
104
104
  {
105
- "apiVersion": 1,
106
105
  "error": "No component named \"Buttn\"",
107
106
  "code": "ERR_UNKNOWN_COMPONENT",
108
107
  "suggestions": [{"name": "Button", "reason": "similar name"}]
@@ -176,7 +175,7 @@ if (isError(result)) {
176
175
  | `ERR_NO_SOURCE` | No source file could be located for the requested component/template. |
177
176
  | `ERR_INVALID_DOC` | A component's docs failed validation (malformed `.doc.mjs`). |
178
177
  | `ERR_FILE_NOT_FOUND` | A required input file did not exist. |
179
- | `ERR_FILE_EXISTS` | Refused to overwrite an existing file. |
178
+ | `ERR_FILE_EXISTS` | Refused to overwrite an existing file in non-interactive mode. |
180
179
  | `ERR_PATH_TRAVERSAL` | A path escaped its allowed root, or a name contained traversal markers. |
181
180
  | `ERR_WRITE_FAILED` | Writing output files failed (and was rolled back). |
182
181
  | `ERR_THEME_INVALID` | A theme definition or contributed theme catalog is invalid. |
@@ -200,9 +199,8 @@ if (isError(result)) {
200
199
 
201
200
  Agents don't have to scrape `--help` to learn the CLI. A single call returns a
202
201
  **self-describing manifest**: every command, its arguments, flags (with types,
203
- choices, and defaults), whether it supports `--json`, the response `type`
204
- discriminators each command can emit, and its documented exit codes. Think of it
205
- as an OpenAPI spec for the CLI.
202
+ choices, and defaults), whether it supports `--json`, and the response `type`
203
+ discriminators each command can emit. Think of it as an OpenAPI spec for the CLI.
206
204
 
207
205
  ```bash
208
206
  astryx manifest --json # dedicated surface — type: "manifest"
@@ -264,10 +262,6 @@ Shape:
264
262
  "…",
265
263
  ],
266
264
  "examples": ["astryx component Button --props --json"],
267
- "exitCodes": [
268
- {"code": 0, "when": "success"},
269
- {"code": 1, "when": "…"},
270
- ],
271
265
  },
272
266
  // …one entry per command; subcommands (e.g. `theme build`) nest under `subcommands`
273
267
  ],
@@ -349,9 +343,9 @@ import type {
349
343
  // ...import the response types for the commands you consume
350
344
  } from '@astryxdesign/cli/json';
351
345
 
352
- // parseResponse returns the structural { apiVersion, type, data, meta? }
353
- // envelope; `data` is `unknown` until you narrow it. Reconstruct the union you
354
- // care about from the per-command response types, then narrow on `type`:
346
+ // parseResponse returns the structural { type, data, meta? } envelope; `data`
347
+ // is `unknown` until you narrow it. Reconstruct the union you care about from
348
+ // the per-command response types, then narrow on `type`:
355
349
  type MyResponse =
356
350
  ComponentDetailResponse | ComponentListResponse | DocsListResponse;
357
351
 
@@ -418,7 +412,7 @@ Every response has a `type` discriminant. The full set is below (generated from
418
412
  | `discover.detail.doc` | The validated ComponentDoc for one external component: an @scope/name/Component query, or a free-text term resolving to exactly one component. |
419
413
  | `discover.search` | The echoed query plus the matching {package, component} pairs, when a free-text term matches several components. |
420
414
  | `search` | The echoed query, `matchCount` (how many candidates matched in total, before `limit`), plus a ranked SearchResultEntry[] bounded by `limit` (domain, name, score, reason, description, follow-up command, and import path where relevant). |
421
- | `build.help` | The how-to-build-a-page playbook, emitted when no query is given: `playbook: true`, a title, the ordered steps (title, commands, optional returns), the on-system rules, and related lookups. Commands are bare subcommands for the caller to render with its own invocation. |
415
+ | `build.help` | A marker (`playbook: true`) that the renderer expands into the how-to-build-a-page workflow; emitted when no query is given. |
422
416
  | `build.kit` | The grouped composition kit: echoed query, hasResults/matchCount/directMatch fields (matchCount is the total matched, never a cap read back), the closest page templates, drop-in block patterns, idea-specific components/hooks, and the always-on frame + foundation component-name arrays. |
423
417
  | `swizzle.list` | The names of swizzlable components discoverable from cwd's @astryxdesign/core. |
424
418
  | `swizzle.copy` | An eject receipt: component name, owning package, output directory, files-copied count, the written file names, whether any file uses StyleX, and an optional maintainer note. |
@@ -432,7 +426,7 @@ Every response has a `type` discriminant. The full set is below (generated from
432
426
  | `hook.list` | The hook catalog grouped by category: `detail` (the level: names \| compact \| full) and `components`, the grouped map of hook names, brief entries, or a full HookDoc per entry. |
433
427
  | `hook.detail` | One hook's full authored HookDoc. |
434
428
  | `hook.detail.params` | Just one hook's parameters table (HookParamDoc[]). |
435
- | `theme.build` | A theme build receipt: name, tokenCount and componentCount (override counts), sizeKB, the written outputs {css, js, dts, and variantsDts when applicable}, warnings (defects to fix), and notices (advisories about a correct theme, such as a named font it does not load). |
429
+ | `theme.build` | A theme build receipt: name, token- and component-override counts, output size, the written outputs {css, js, dts, and variantsDts when applicable}, and any validation warnings. |
436
430
  | `theme.build.check` | The --check receipt: theme name, an upToDate flag, the stale outputs (each {path, reason: missing \| outdated}), and the full list of checked paths. Writes nothing. |
437
431
  | `theme.build.batch` | Several themes built in one invocation: `count` plus one {file, receipt} per theme in argument order, where receipt is that theme's theme.build (or theme.build.check) envelope, or null when it produced no CSS. |
438
432
  | `theme.list` | Every bundled or installed integration theme as a ThemeListEntry[]: each with slug, displayName, description, maintained flag, and owner package. |
@@ -443,7 +437,7 @@ Every response has a `type` discriminant. The full set is below (generated from
443
437
  | `upgrade.list` | Every available codemod, oldest→newest, as {name, title, version, optional}; returned for --list without running anything. |
444
438
  | `upgrade.status` | A short-circuit outcome with no codemods run (up_to_date, no_codemods, or config_fixable), each carrying the agent-docs summary. |
445
439
  | `upgrade.run` | The run receipt: from/to versions, codemod count, integrations processed, the agent-docs summary, and (apply mode) filesChanged, transformsApplied, and per-codemod errors. |
446
- | `manifest` | The CLI capability manifest: name, version, apiVersion, description, globalOptions, commands (each name, description, arguments, options, json, aliases?, responseTypes?, examples?, exitCodes? as [{code, when}], subcommands?), jsonSupported, and the flat responseTypes index. |
440
+ | `manifest` | The self-describing CLI capability manifest: name, version, apiVersion, global options, the command tree (args, options, json flag, response types, examples), the jsonSupported allowlist, and the flat responseTypes index. |
447
441
  | `doctor` | The health-check report: `checks` (each with id, label, status: pass \| warn \| fail \| info, a message, and a fix when not passing) plus a `summary` of counts per status. |
448
442
  | `integration.add` | A contribution-writer receipt: kind, name, optional root {path, created}, integration-manifest path, every affected project-relative path, written, and dryRun. |
449
443
  | `integration.pack-check` | The packed-package check: package identity, tarball facts, local and packed contribution inventories, and issues. |
@@ -462,33 +456,25 @@ Every response has a `type` discriminant. The full set is below (generated from
462
456
 
463
457
  `astryx doctor` runs read-only health checks against your project and
464
458
  environment. Each record uses `[ok]`, `[warn]`, `[fail]`, or `[info]`, and
465
- includes an actionable `fix` when one is available. Field names match the
466
- `--json` keys. The exact checks and values depend on the project; the output
467
- shape is stable:
459
+ includes an actionable `fix` when one is available. The exact checks and values
460
+ depend on the project; the output shape is stable:
468
461
 
469
462
  ```
470
463
  $ astryx doctor
471
464
  astryx doctor - diagnosing your setup
472
465
 
473
- id: node-version
474
466
  status: [ok]
475
- label: Node.js version
467
+ check: Node.js version
476
468
  message: Node v24.18.1 meets the minimum (>=22.13.0).
477
469
 
478
- id: themes
479
470
  status: [warn]
480
- label: Theme packages
471
+ check: Theme packages
481
472
  message: No @astryxdesign/theme-* packages are installed.
482
473
  fix: Install a theme, e.g. `npm install @astryxdesign/theme-neutral`, then import its CSS or set astryx.theme.
483
474
 
484
475
  ...
485
476
 
486
- summary
487
-
488
- pass: 4
489
- warn: 2
490
- fail: 0
491
- info: 2
477
+ Summary: 4 passed, 2 warnings, 0 failures, 2 info
492
478
 
493
479
  No failures - but review the [warn] warnings above when you can.
494
480
  ```
@@ -16,8 +16,8 @@ export const doc = {
16
16
  'Page-building assistant: the how-to-build playbook, or a composition kit for an idea.',
17
17
  description:
18
18
  'The "assemble a page" entry point. Called with no query it returns the ' +
19
- 'how-to-build-a-page playbook as data: the workflow steps with their ' +
20
- 'commands, the on-system rules, and related lookups. Called with a query it runs the unified search and groups the ' +
19
+ 'playbook signal that the renderer expands into the how-to-build-a-page ' +
20
+ 'workflow. Called with a query it runs the unified search and groups the ' +
21
21
  'hits into a composition KIT: the closest page templates, drop-in blocks, ' +
22
22
  'and idea-specific components/hooks, plus the always-on frame + foundation.',
23
23
  importPath: '@astryxdesign/cli/api',
@@ -54,7 +54,7 @@ export const doc = {
54
54
  {
55
55
  type: 'build.help',
56
56
  description:
57
- 'Emitted when the query is omitted: the page-building playbook — `playbook: true`, a `title`, the ordered `steps` (each a `title`, its `commands`, and optionally what the step `returns`), the on-system `rules`, and `related` lookups. Each command is a bare subcommand ({command, purpose?}) for the caller to render with its own CLI invocation.',
57
+ 'Emitted when the query is omitted: a pure marker (`data.playbook: true`) that the command renderer expands into the page-building workflow prose.',
58
58
  },
59
59
  {
60
60
  type: 'build.kit',
@@ -1,7 +1,7 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
2
 
3
3
  /**
4
- * @file Tests for the build API (playbook + composition kit).
4
+ * @file Tests for the build API (playbook signal + composition kit).
5
5
  */
6
6
 
7
7
  import {describe, it, expect, vi} from 'vitest';
@@ -20,21 +20,10 @@ const REPO = path.resolve(
20
20
  vi.setConfig({testTimeout: 30000});
21
21
 
22
22
  describe('build API', () => {
23
- it('no query → build.help carries the playbook as data', async () => {
23
+ it('no query → build.help playbook signal', async () => {
24
24
  const r = await build();
25
25
  expect(r.type).toBe('build.help');
26
- if (r.type !== 'build.help') return;
27
- expect(r.data.playbook).toBe(true);
28
- expect(r.data.title).toMatch(/build a page/i);
29
- expect(r.data.steps.length).toBeGreaterThan(0);
30
- for (const step of r.data.steps) {
31
- expect(step.title).toBeTruthy();
32
- expect(step.commands.length).toBeGreaterThan(0);
33
- }
34
- expect(r.data.rules.length).toBeGreaterThan(0);
35
- // Bare subcommands: the caller adds its own invocation.
36
- const commands = [...r.data.steps.flatMap(s => s.commands), ...r.data.related];
37
- for (const {command} of commands) expect(command).not.toMatch(/^(astryx|npx|pnpm|yarn|bunx?)\b/);
26
+ expect(r.data).toEqual({playbook: true});
38
27
  });
39
28
 
40
29
  it('query → build.kit with raw entries + static frame/foundation', async () => {
@@ -2,46 +2,12 @@
2
2
  // DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
3
3
 
4
4
  /**
5
- * A command the playbook tells the caller to run.
6
- */
7
- export type BuildPlaybookCommand = {
8
- /**
9
- * Bare subcommand with `<placeholder>` arguments (e.g. `template <name> --skeleton`) and no package-manager prefix — render it with your own CLI invocation.
10
- */
11
- command: string;
12
- /**
13
- * What running it is for.
14
- */
15
- purpose?: string | undefined;
16
- };
17
- /**
18
- * One step of the page-building workflow.
19
- */
20
- export type BuildPlaybookStep = {
21
- /**
22
- * What to do.
23
- */
24
- title: string;
25
- /**
26
- * The commands for this step, in the order to run them.
27
- */
28
- commands: BuildPlaybookCommand[];
29
- /**
30
- * What the step's command gives back, when that decides the next step.
31
- */
32
- returns?: string | undefined;
33
- };
34
- /**
35
- * astryx --json build (no query) — the "how to build a page" playbook.
5
+ * astryx --json build (no query) — the "how to build a page" playbook signal.
36
6
  */
37
7
  export type BuildHelpResponse = {
38
8
  type: "build.help";
39
9
  data: {
40
10
  playbook: true;
41
- title: string;
42
- steps: BuildPlaybookStep[];
43
- rules: string[];
44
- related: BuildPlaybookCommand[];
45
11
  };
46
12
  };
47
13
  /**
@@ -2,38 +2,17 @@
2
2
 
3
3
  /**
4
4
  * @file Colocated types for the `build` command — source of truth for the
5
- * `build.help` (playbook) and `build.kit` (composition kit) JSON responses.
6
- * Re-exported by types/build.d.ts.
5
+ * `build.help` (playbook signal) and `build.kit` (composition kit) JSON
6
+ * responses. Re-exported by types/build.d.ts.
7
7
  */
8
8
 
9
9
  /**
10
- * A command the playbook tells the caller to run.
11
- *
12
- * @typedef {object} BuildPlaybookCommand
13
- * @property {string} command Bare subcommand with `<placeholder>` arguments (e.g. `template <name> --skeleton`) and no package-manager prefix — render it with your own CLI invocation.
14
- * @property {string} [purpose] What running it is for.
15
- */
16
-
17
- /**
18
- * One step of the page-building workflow.
19
- *
20
- * @typedef {object} BuildPlaybookStep
21
- * @property {string} title What to do.
22
- * @property {BuildPlaybookCommand[]} commands The commands for this step, in the order to run them.
23
- * @property {string} [returns] What the step's command gives back, when that decides the next step.
24
- */
25
-
26
- /**
27
- * astryx --json build (no query) — the "how to build a page" playbook.
10
+ * astryx --json build (no query) — the "how to build a page" playbook signal.
28
11
  *
29
12
  * @typedef {object} BuildHelpResponse
30
13
  * @property {'build.help'} type
31
14
  * @property {object} data
32
15
  * @property {true} data.playbook Always true; marks this envelope as the playbook rather than a result set.
33
- * @property {string} data.title The playbook's heading.
34
- * @property {BuildPlaybookStep[]} data.steps The workflow, in order.
35
- * @property {string[]} data.rules The rules that keep a page on-system.
36
- * @property {BuildPlaybookCommand[]} data.related Lookups to reach for alongside the workflow.
37
16
  */
38
17
 
39
18
  /**
@@ -2,17 +2,14 @@
2
2
  // DO NOT EDIT — run `pnpm sync:api-types` to regenerate.
3
3
 
4
4
  /**
5
- * @file build.help leaf — the "how to build a page" playbook.
5
+ * @file build.help leaf — the "how to build a page" playbook signal.
6
6
  *
7
- * `build` with no query returns this envelope. The playbook is data — steps,
8
- * rules, and related lookups — so a `--json` or programmatic caller gets the
9
- * same guidance the terminal shows. Commands are bare subcommands with no
10
- * package-manager prefix, which keeps the JSON environment-agnostic; the CLI
11
- * renders each one with the caller's invocation, as it does build.kit's
12
- * `hint.commands`.
7
+ * `build` with no query returns this envelope: a pure marker that the command
8
+ * renderer expands into the workflow playbook prose. It carries no data beyond
9
+ * `playbook: true`, so the JSON shape stays environment-agnostic and stable.
13
10
  */
14
11
  /**
15
- * The page-building playbook (emitted when `build` runs with no query).
12
+ * The page-building playbook signal (emitted when `build` runs with no query).
16
13
  *
17
14
  * @returns {import('../build.type.mjs').BuildHelpResponse}
18
15
  */
@@ -1,68 +1,18 @@
1
1
  // Copyright (c) Meta Platforms, Inc. and affiliates.
2
2
 
3
3
  /**
4
- * @file build.help leaf — the "how to build a page" playbook.
4
+ * @file build.help leaf — the "how to build a page" playbook signal.
5
5
  *
6
- * `build` with no query returns this envelope. The playbook is data — steps,
7
- * rules, and related lookups — so a `--json` or programmatic caller gets the
8
- * same guidance the terminal shows. Commands are bare subcommands with no
9
- * package-manager prefix, which keeps the JSON environment-agnostic; the CLI
10
- * renders each one with the caller's invocation, as it does build.kit's
11
- * `hint.commands`.
6
+ * `build` with no query returns this envelope: a pure marker that the command
7
+ * renderer expands into the workflow playbook prose. It carries no data beyond
8
+ * `playbook: true`, so the JSON shape stays environment-agnostic and stable.
12
9
  */
13
10
 
14
11
  /**
15
- * The page-building playbook (emitted when `build` runs with no query).
12
+ * The page-building playbook signal (emitted when `build` runs with no query).
16
13
  *
17
14
  * @returns {import('../build.type.mjs').BuildHelpResponse}
18
15
  */
19
16
  export function buildHelp() {
20
- return {
21
- type: 'build.help',
22
- data: {
23
- playbook: true,
24
- title: 'How to build a page with Astryx',
25
- steps: [
26
- {
27
- title: "Find a starting point for what you're building",
28
- commands: [{command: 'build "<what you\'re building>"'}],
29
- returns:
30
- 'the closest [page] template, the [block]s that cover parts, and the [component]s to fill the gaps, with a recommended start',
31
- },
32
- {
33
- title: 'If a [page] template matches, scaffold it and adapt',
34
- commands: [{command: 'template <name> [path]'}],
35
- },
36
- {
37
- title: 'If nothing matches exactly, compose',
38
- commands: [
39
- {
40
- command: 'template <name> --skeleton',
41
- purpose: "study a close page's layout",
42
- },
43
- {
44
- command: 'template <BlockName>',
45
- purpose: 'drop in each block from the kit',
46
- },
47
- {
48
- command: 'component <Name>',
49
- purpose: 'fill remaining gaps (read props)',
50
- },
51
- ],
52
- },
53
- ],
54
- rules: [
55
- 'No <div>/raw HTML for layout — use VStack/HStack/Grid/Stack/Card etc.',
56
- 'No style={{}} — use component props, and design tokens for values.',
57
- 'Wrap the app in <Theme theme={...}> and import core reset.css + astryx.css.',
58
- ],
59
- related: [
60
- {command: 'docs tokens', purpose: 'the design tokens'},
61
- {
62
- command: 'search <query>',
63
- purpose: 'a neutral lookup of any component, doc, or template',
64
- },
65
- ],
66
- },
67
- };
17
+ return {type: 'build.help', data: {playbook: true}};
68
18
  }
@@ -91,8 +91,7 @@ export const doc = {
91
91
  {
92
92
  name: 'options.lang',
93
93
  type: 'string',
94
- description:
95
- "Language code for localized doc content: 'en', 'zh', or 'dense'.",
94
+ description: 'Language code for localized doc content.',
96
95
  },
97
96
  {
98
97
  name: 'options.zh',
@@ -114,7 +113,7 @@ export const doc = {
114
113
  {
115
114
  type: 'component.detail',
116
115
  description:
117
- "One component's authored ComponentDoc plus ownership metadata (owner package, import specifier, whether source is available). When the name is a sub-component documented in a parent's doc, the payload is scoped to it and parentDoc names that parent.",
116
+ "One component's authored ComponentDoc plus ownership metadata (owner package, import specifier, whether source is available).",
118
117
  },
119
118
  {
120
119
  type: 'component.detail.props',
@@ -136,14 +135,6 @@ export const doc = {
136
135
  },
137
136
  ],
138
137
  throws: [
139
- {
140
- code: 'ERR_INVALID_DETAIL',
141
- when: "options.detail is not 'full', 'compact', or 'brief'",
142
- },
143
- {
144
- code: 'ERR_INVALID_LANG',
145
- when: "options.lang is set to anything other than 'en', 'zh', or 'dense'",
146
- },
147
138
  {
148
139
  code: 'ERR_CORE_NOT_FOUND',
149
140
  when: '@astryxdesign/core cannot be resolved from cwd',
@@ -34,11 +34,6 @@ import {componentDetailSource} from './detail/source/source.mjs';
34
34
  import {componentDetailShowcase} from './detail/showcase/showcase.mjs';
35
35
  import {componentDetailBlocks} from './detail/blocks/blocks.mjs';
36
36
 
37
- /** @type {ReadonlyArray<string>} */
38
- const DETAIL_LEVELS = ['full', 'compact', 'brief'];
39
- /** @type {ReadonlyArray<string>} */
40
- const LANGS = ['en', 'zh', 'dense'];
41
-
42
37
  /**
43
38
  * @param {string} [name]
44
39
  * @param {object} [options]
@@ -86,23 +81,6 @@ export async function component(name, options = {}) {
86
81
  const isListView = list || category != null || !name;
87
82
  const detail = detailOption ?? (isListView ? 'brief' : 'full');
88
83
 
89
- // Same accepted values and codes as the CLI's --detail and --lang, checked
90
- // first as the CLI parser does.
91
- if (!DETAIL_LEVELS.includes(detail)) {
92
- throw new AstryxError(
93
- `Invalid detail "${String(detail)}". Valid levels: ${DETAIL_LEVELS.join(', ')}`,
94
- undefined,
95
- ERROR_CODES.ERR_INVALID_DETAIL,
96
- );
97
- }
98
- if (lang != null && !LANGS.includes(lang)) {
99
- throw new AstryxError(
100
- `Invalid lang "${String(lang)}". Valid values: ${LANGS.join(', ')}`,
101
- undefined,
102
- ERROR_CODES.ERR_INVALID_LANG,
103
- );
104
- }
105
-
106
84
  const coreDir = requireCoreDir(cwd);
107
85
 
108
86
  // A public API caller could pass a non-string category; the list leaf does
@@ -178,13 +156,6 @@ export async function component(name, options = {}) {
178
156
  }
179
157
  const extDocPath = resolveLegacyExternalDoc(scoped.ext, dirName);
180
158
  if (extDocPath) {
181
- // Legacy packages ship docs, never source.
182
- if (source) {
183
- return componentDetailSource(dirName, null, {name, notFoundInPackage: packageScope});
184
- }
185
- if (blocks) {
186
- return componentDetailBlocks(dirName);
187
- }
188
159
  const docs = await loadComponentDoc(extDocPath, docOpts);
189
160
  if (props) return componentDetailProps(docs);
190
161
  return componentDetail(docs, {package: scoped.ext.name, sourcePath: null}, dirName, coreDir);
@@ -14,7 +14,6 @@ import * as path from 'node:path';
14
14
  import {fileURLToPath} from 'node:url';
15
15
  import {component} from './component.mjs';
16
16
  import {AstryxError} from '../error.mjs';
17
- import {runCli} from '../../test-utils/run-cli.mjs';
18
17
 
19
18
  // api/component/ -> up 4 = repo root (has packages/core, which findCoreDir walks to).
20
19
  const REPO = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../../..');
@@ -162,40 +161,3 @@ describe('component dispatcher — category guard', () => {
162
161
  }
163
162
  }, SLOW);
164
163
  });
165
-
166
- describe('component dispatcher — detail and lang guards', () => {
167
- // `detail` and `lang` are the same controls as the CLI's --detail and --lang:
168
- // same accepted values, same error codes.
169
- it('rejects a detail level the CLI rejects, with the same code', async () => {
170
- const cliRun = await runCli(['--json', 'component', '--list', '--detail', 'bogus'], cwd);
171
- expect(cliRun.code).toBe(1);
172
- expect(JSON.parse(cliRun.stdout).code).toBe('ERR_INVALID_DETAIL');
173
-
174
- const bogus = /** @type {any} */ ('bogus');
175
- for (const call of [
176
- () => component(undefined, {cwd, list: true, detail: bogus}),
177
- () => component('Button', {cwd, detail: bogus}),
178
- ]) {
179
- const err = await call().catch(e => e);
180
- expect(err).toBeInstanceOf(AstryxError);
181
- expect(err.code).toBe('ERR_INVALID_DETAIL');
182
- }
183
- }, SLOW);
184
-
185
- it('rejects a lang the CLI rejects, with the same code', async () => {
186
- const cliRun = await runCli(['--json', 'component', 'Button', '--lang', 'fr'], cwd);
187
- expect(cliRun.code).toBe(1);
188
- expect(JSON.parse(cliRun.stdout).code).toBe('ERR_INVALID_LANG');
189
-
190
- for (const call of [
191
- () => component('Button', {cwd, lang: 'fr'}),
192
- () => component(undefined, {cwd, list: true, detail: 'compact', lang: 'fr'}),
193
- ]) {
194
- const err = await call().catch(e => e);
195
- expect(err).toBeInstanceOf(AstryxError);
196
- expect(err.code).toBe('ERR_INVALID_LANG');
197
- }
198
- const zh = await component('Button', {cwd, lang: 'zh'});
199
- expect(zh.type).toBe('component.detail');
200
- }, SLOW);
201
- });
@@ -59,17 +59,7 @@ export type ComponentBriefEntry = {
59
59
  */
60
60
  export type ComponentDetailResponse = {
61
61
  type: "component.detail";
62
- data: import("@astryxdesign/cli/authoring").ComponentDoc & ComponentOwnership & ComponentDetailScope;
63
- };
64
- /**
65
- * Present only when the requested name is a sub-component documented inside a
66
- * parent's doc (e.g. `HStack` in the `Stack` doc); the payload is scoped to it.
67
- */
68
- export type ComponentDetailScope = {
69
- /**
70
- * - Name of the parent doc the payload was scoped from, e.g. 'Stack'.
71
- */
72
- parentDoc?: string | undefined;
62
+ data: import("@astryxdesign/cli/authoring").ComponentDoc & ComponentOwnership;
73
63
  };
74
64
  /**
75
65
  * Ownership metadata attached to every `component.detail` payload. Exposes the
@@ -73,14 +73,7 @@
73
73
  * astryx --json component <name>
74
74
  * @typedef {object} ComponentDetailResponse
75
75
  * @property {'component.detail'} type
76
- * @property {import('@astryxdesign/cli/authoring').ComponentDoc & ComponentOwnership & ComponentDetailScope} data
77
- */
78
-
79
- /**
80
- * Present only when the requested name is a sub-component documented inside a
81
- * parent's doc (e.g. `HStack` in the `Stack` doc); the payload is scoped to it.
82
- * @typedef {object} ComponentDetailScope
83
- * @property {string} [parentDoc] - Name of the parent doc the payload was scoped from, e.g. 'Stack'.
76
+ * @property {import('@astryxdesign/cli/authoring').ComponentDoc & ComponentOwnership} data
84
77
  */
85
78
 
86
79
  /**
@@ -41,7 +41,7 @@ export const doc = {
41
41
  name: 'options.components',
42
42
  type: 'boolean',
43
43
  description:
44
- 'In the CLI package list, print every component of each package instead of the first 10. A display flag for the CLI renderer; the programmatic response is unchanged.',
44
+ 'List components only. A CLI display flag consumed by the renderer; the programmatic response shape is unchanged.',
45
45
  },
46
46
  {
47
47
  name: 'options.lang',
@@ -32,19 +32,17 @@ export const doc = {
32
32
  name: 'component',
33
33
  type: 'string',
34
34
  description:
35
- 'Component or general design-system area, up to 120 characters. Required unless listCategories is true.',
35
+ 'Component or general design-system area. Required unless listCategories is true.',
36
36
  },
37
37
  {
38
38
  name: 'options.category',
39
39
  type: 'GapReportCategory',
40
- description:
41
- 'Fixed category from the reported category vocabulary. Required unless listCategories is true.',
40
+ description: 'Fixed category from the reported category vocabulary.',
42
41
  },
43
42
  {
44
43
  name: 'options.reason',
45
44
  type: 'string',
46
- description:
47
- 'What capability was missing or difficult, up to 2000 characters. Required unless listCategories is true.',
45
+ description: 'What capability was missing or difficult.',
48
46
  },
49
47
  {
50
48
  name: 'options.detail',
@@ -67,8 +65,7 @@ export const doc = {
67
65
  {
68
66
  name: 'options.listCategories',
69
67
  type: 'boolean',
70
- description:
71
- 'Return categories without resolving a route or writing; the component and every other option are ignored.',
68
+ description: 'Return categories without resolving a route or writing.',
72
69
  default: 'false',
73
70
  },
74
71
  {
@@ -90,10 +90,6 @@ export const doc = {
90
90
  code: 'ERR_FILE_EXISTS',
91
91
  when: 'scaffolding a template would overwrite an existing page.tsx',
92
92
  },
93
- {
94
- code: 'ERR_PATH_TRAVERSAL',
95
- when: 'the template output path resolves outside cwd, for example through a symlinked src directory',
96
- },
97
93
  ],
98
94
  examples: [
99
95
  {label: 'Default setup', code: 'const r = await init();'},