@astryxdesign/cli 0.1.0-canary.e2d38fb → 0.1.0-canary.e457dac

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 (112) hide show
  1. package/README.md +75 -117
  2. package/docs/theme.doc.dense.mjs +2 -2
  3. package/docs/theme.doc.mjs +0 -14
  4. package/docs/theme.doc.zh.mjs +2 -2
  5. package/docs/working-with-ai.doc.mjs +1 -1
  6. package/package.json +8 -8
  7. package/src/api/search.mjs +13 -207
  8. package/src/api/template.mjs +1 -2
  9. package/src/codemods/__tests__/registry.test.mjs +0 -1
  10. package/src/codemods/registry.mjs +0 -1
  11. package/src/codemods/runner.mjs +51 -105
  12. package/src/commands/agent-docs.mjs +56 -92
  13. package/src/commands/agent-docs.test.mjs +9 -65
  14. package/src/commands/init.mjs +1 -9
  15. package/src/commands/json-contract.test.mjs +2 -9
  16. package/src/commands/upgrade.mjs +169 -353
  17. package/src/commands/upgrade.test.mjs +26 -40
  18. package/src/index.mjs +0 -1
  19. package/src/lib/config.mjs +0 -12
  20. package/src/lib/error-codes.mjs +0 -3
  21. package/src/types/error-codes.d.ts +0 -1
  22. package/src/utils/update-check.mjs +26 -4
  23. package/src/utils/update-check.test.mjs +63 -1
  24. package/templates/blocks/components/AppShell/AppShellContentOnly.tsx +9 -1
  25. package/templates/blocks/components/AppShell/AppShellShowcase.tsx +10 -1
  26. package/templates/blocks/components/AppShell/AppShellSideNavOnly.tsx +9 -1
  27. package/templates/blocks/components/AppShell/AppShellTopNavOnly.tsx +9 -1
  28. package/templates/blocks/components/AppShell/AppShellTopNavWithSideNav.tsx +9 -1
  29. package/templates/blocks/components/AppShell/AppShellWithBanner.tsx +9 -1
  30. package/templates/blocks/components/AspectRatio/AspectRatioShowcase.tsx +19 -12
  31. package/templates/blocks/components/Banner/BannerShowcase.tsx +8 -1
  32. package/templates/blocks/components/Blockquote/BlockquoteShowcase.tsx +8 -1
  33. package/templates/blocks/components/Carousel/CarouselShowcase.tsx +12 -2
  34. package/templates/blocks/components/ChatComposerDrawer/ChatComposerDrawerShowcase.tsx +9 -6
  35. package/templates/blocks/components/ChatLayout/ChatLayoutPanelChat.tsx +12 -10
  36. package/templates/blocks/components/ChatMessageList/ChatMessageListDensity.tsx +9 -1
  37. package/templates/blocks/components/ChatMessageList/ChatMessageListFullFeatured.tsx +9 -1
  38. package/templates/blocks/components/ChatMessageList/ChatMessageListShowcase.tsx +9 -1
  39. package/templates/blocks/components/ChatMessageMetadata/ChatMessageMetadataShowcase.tsx +8 -1
  40. package/templates/blocks/components/ChatSendButton/ChatSendButtonInComposer.tsx +8 -1
  41. package/templates/blocks/components/Citation/CitationInlineText.tsx +4 -4
  42. package/templates/blocks/components/Code/CodeInlineInParagraph.tsx +8 -1
  43. package/templates/blocks/components/CodeBlock/CodeBlockBashCommand.tsx +1 -1
  44. package/templates/blocks/components/CodeBlock/CodeBlockJSONConfig.tsx +1 -1
  45. package/templates/blocks/components/CommandPaletteItem/CommandPaletteItemShowcase.tsx +12 -9
  46. package/templates/blocks/components/ContextMenu/ContextMenuShowcase.tsx +15 -13
  47. package/templates/blocks/components/Divider/DividerShowcase.tsx +8 -1
  48. package/templates/blocks/components/Divider/DividerVertical.tsx +9 -7
  49. package/templates/blocks/components/Field/FieldShowcase.tsx +8 -1
  50. package/templates/blocks/components/FormLayout/FormLayoutHorizontal.tsx +6 -1
  51. package/templates/blocks/components/Grid/GridResponsiveAutoFit.tsx +9 -1
  52. package/templates/blocks/components/HoverCard/HoverCardInlineTextHoverCard.tsx +6 -4
  53. package/templates/blocks/components/HoverCard/HoverCardInteractiveContent.tsx +6 -1
  54. package/templates/blocks/components/HoverCard/HoverCardProfileHoverCard.tsx +8 -2
  55. package/templates/blocks/components/HoverCard/HoverCardShowcase.tsx +8 -1
  56. package/templates/blocks/components/MoreMenu/MoreMenuInToolbar.tsx +12 -2
  57. package/templates/blocks/components/OverflowList/OverflowListOverflowBadges.tsx +11 -8
  58. package/templates/blocks/components/OverflowList/OverflowListOverflowDropdownActions.tsx +12 -9
  59. package/templates/blocks/components/Overlay/OverlayBottomStrip.tsx +17 -4
  60. package/templates/blocks/components/Overlay/OverlayHoverReveal.tsx +16 -15
  61. package/templates/blocks/components/Overlay/OverlayShowcase.tsx +21 -5
  62. package/templates/blocks/components/Pagination/PaginationDotsCarousel.tsx +14 -2
  63. package/templates/blocks/components/Pagination/PaginationPageSize.tsx +14 -12
  64. package/templates/blocks/components/Pagination/PaginationVariants.tsx +8 -1
  65. package/templates/blocks/components/Pagination/PaginationWithTable.tsx +14 -2
  66. package/templates/blocks/components/Tokenizer/TokenizerClear.tsx +6 -1
  67. package/templates/blocks/components/Tokenizer/TokenizerCreatable.tsx +7 -2
  68. package/templates/blocks/components/Tokenizer/TokenizerEndContent.tsx +6 -1
  69. package/templates/blocks/components/Tokenizer/TokenizerIcon.tsx +6 -1
  70. package/templates/blocks/components/Tokenizer/TokenizerMaxEntries.tsx +6 -1
  71. package/templates/blocks/components/Tokenizer/TokenizerOverflow.tsx +7 -2
  72. package/templates/blocks/components/Tokenizer/TokenizerShowcase.tsx +6 -1
  73. package/templates/blocks/components/Tokenizer/TokenizerStates.tsx +9 -4
  74. package/templates/blocks/components/Toolbar/ToolbarCardHeader.tsx +10 -1
  75. package/templates/blocks/components/Toolbar/ToolbarSizes.tsx +8 -1
  76. package/templates/blocks/components/Toolbar/ToolbarTableFilter.tsx +8 -1
  77. package/templates/blocks/components/Toolbar/ToolbarThreeSlot.tsx +10 -1
  78. package/templates/blocks/components/Toolbar/ToolbarWithTabs.tsx +11 -8
  79. package/templates/pages/ai-chat/page.tsx +64 -71
  80. package/templates/pages/ai-chat-landing/page.tsx +12 -8
  81. package/templates/pages/centered-hero/page.tsx +15 -13
  82. package/templates/pages/classic-gallery/page.tsx +34 -27
  83. package/templates/pages/detail-page/page.tsx +18 -18
  84. package/templates/pages/documentation/page.tsx +14 -11
  85. package/templates/pages/documentation-design/page.tsx +13 -10
  86. package/templates/pages/documentation-technical/page.tsx +16 -15
  87. package/templates/pages/editor/page.tsx +54 -42
  88. package/templates/pages/file-explorer/page.tsx +16 -13
  89. package/templates/pages/form-two-column/page.tsx +17 -13
  90. package/templates/pages/gallery-hero/page.tsx +15 -13
  91. package/templates/pages/ide/page.tsx +39 -32
  92. package/templates/pages/library/page.tsx +23 -16
  93. package/templates/pages/login/page.tsx +18 -14
  94. package/templates/pages/login-card/page.tsx +18 -14
  95. package/templates/pages/login-split/page.tsx +48 -50
  96. package/templates/pages/login-sso/page.tsx +13 -9
  97. package/templates/pages/mixed-gallery/page.tsx +45 -51
  98. package/templates/pages/payment-form/page.tsx +70 -56
  99. package/templates/pages/product-detail/page.tsx +33 -27
  100. package/templates/pages/product-gallery/page.tsx +13 -7
  101. package/templates/pages/settings-dialog/page.tsx +43 -35
  102. package/templates/pages/settings-sidebar/page.tsx +47 -39
  103. package/templates/pages/side-gallery/page.tsx +9 -6
  104. package/templates/pages/table-grouped/page.tsx +15 -11
  105. package/templates/pages/theme-showcase/page.tsx +37 -33
  106. package/src/codemods/transforms/v0.1.0/__tests__/migrate-xds-config-surfaces.test.mjs +0 -116
  107. package/src/codemods/transforms/v0.1.0/__tests__/migrate-xds-module-specifiers.test.mjs +0 -51
  108. package/src/codemods/transforms/v0.1.0/index.mjs +0 -28
  109. package/src/codemods/transforms/v0.1.0/migrate-xds-config-surfaces.mjs +0 -230
  110. package/src/codemods/transforms/v0.1.0/migrate-xds-module-specifiers.mjs +0 -84
  111. package/src/commands/build.mjs +0 -196
  112. package/src/lib/config.test.mjs +0 -42
package/README.md CHANGED
@@ -11,7 +11,7 @@ npx astryx docs migration
11
11
  npx astryx template --list
12
12
  ```
13
13
 
14
- ## Finding things: `astryx search`
14
+ ### Finding things: `astryx search`
15
15
 
16
16
  When you don't know whether what you need is a component, a hook, a docs topic,
17
17
  or a template, search across all of them at once. Results are ranked by
@@ -50,20 +50,20 @@ Options:
50
50
 
51
51
  ## Commands
52
52
 
53
- | Command | Description |
54
- | ------------- | ---------------------------------------------------------------------------------------------------- |
53
+ | Command | Description |
54
+ | ------------- | --------------------------------------------------------------------------------------- |
55
55
  | `init` | Initialize the design system in your project: installs packages, sets up theming, adds AI agent docs |
56
- | `component` | List components or print detailed docs, props, usage examples, and source |
57
- | `search` | Find components, hooks, docs, and templates in one ranked, cross-domain result set |
58
- | `docs` | Print reference documentation (tokens, theme, color, typography, spacing, etc.) |
59
- | `template` | Inject page or block templates into your project |
60
- | `hook` | List hooks and print hook documentation |
61
- | `swizzle` | Copy component source into your project for deep customization |
62
- | `upgrade` | Run codemods to migrate between versions |
63
- | `theme build` | Compile a defineTheme file to production CSS and JS |
64
- | `discover` | Discover external packages and components |
65
- | `gap-report` | Report a gap when a component doesn't meet your needs |
66
- | `doctor` | Diagnose your XDS setup and report problems with fixes (CI-friendly via exit code) |
56
+ | `component` | List components or print detailed docs, props, usage examples, and source |
57
+ | `search` | Find components, hooks, docs, and templates in one ranked, cross-domain result set |
58
+ | `docs` | Print reference documentation (tokens, theme, color, typography, spacing, etc.) |
59
+ | `template` | Inject page or block templates into your project |
60
+ | `hook` | List hooks and print hook documentation |
61
+ | `swizzle` | Copy component source into your project for deep customization |
62
+ | `upgrade` | Run codemods to migrate between versions |
63
+ | `theme build` | Compile a defineTheme file to production CSS and JS |
64
+ | `discover` | Discover external packages and components |
65
+ | `gap-report` | Report a gap when a component doesn't meet your needs |
66
+ | `doctor` | Diagnose your XDS setup and report problems with fixes (CI-friendly via exit code) |
67
67
 
68
68
  ### Global options
69
69
 
@@ -122,46 +122,46 @@ if (isError(result)) {
122
122
 
123
123
  ### Error codes
124
124
 
125
- | Code | Meaning |
126
- | ------------------------ | -------------------------------------------------------------------------------------- |
127
- | `ERR_UNKNOWN` | Generic fallback for any error without a more specific code. |
128
- | `ERR_UNKNOWN_COMMAND` | A top-level command name was not recognized (e.g. `astryx bogus`). |
129
- | `ERR_UNKNOWN_SUBCOMMAND` | A subcommand under a group was not recognized (e.g. `astryx theme bogus`). |
130
- | `ERR_INVALID_OPTION` | An unknown flag was passed, or `--json` was used on a command that doesn't support it. |
131
- | `ERR_INVALID_ARGUMENT` | An option/argument value was rejected, or required flags were missing. |
132
- | `ERR_MISSING_ARGUMENT` | A required positional argument was omitted (e.g. `astryx theme build` with no file). |
133
- | `ERR_INVALID_LANG` | `--lang` was given a value outside its choices (`en`, `zh`, `dense`). |
134
- | `ERR_INVALID_DETAIL` | `--detail` was given a value outside its choices (`full`, `compact`, `brief`). |
135
- | `ERR_NODE_VERSION` | The running Node.js version is below the supported minimum. |
136
- | `ERR_CORE_NOT_FOUND` | `@astryxdesign/core` could not be located (not installed / not in a monorepo). |
137
- | `ERR_UNKNOWN_COMPONENT` | No component matched the requested name. |
138
- | `ERR_UNKNOWN_HOOK` | No hook matched the requested name. |
139
- | `ERR_UNKNOWN_TOPIC` | No docs topic matched the requested name. |
140
- | `ERR_UNKNOWN_SECTION` | A docs topic exists but the requested section within it does not. |
141
- | `ERR_UNKNOWN_CATEGORY` | A `--category` filter value did not match any known category. |
142
- | `ERR_UNKNOWN_TEMPLATE` | No template matched the requested name. |
143
- | `ERR_UNKNOWN_PACKAGE` | No package matched the requested name (discover). |
144
- | `ERR_UNKNOWN_AGENT` | An unrecognized `--agent` value was passed (agent docs / init). |
145
- | `ERR_UNKNOWN_FEATURE` | An unrecognized `--features` value was passed to `init`. |
146
- | `ERR_UNKNOWN_CODEMOD` | A `--codemod` value did not match any registered codemod (upgrade). |
147
- | `ERR_NOT_FOUND` | A discover/lookup query matched nothing in any package. |
148
- | `ERR_NO_DOC` | A component exists but has no typed `.doc.mjs` file. |
149
- | `ERR_NO_SHOWCASE` | No showcase exists for the requested component. |
150
- | `ERR_NO_SOURCE` | No source file could be located for the component/template. |
151
- | `ERR_INVALID_DOC` | A component's docs failed validation (malformed `.doc.mjs`). |
152
- | `ERR_FILE_NOT_FOUND` | A required input file did not exist. |
153
- | `ERR_FILE_EXISTS` | Refused to overwrite an existing file in non-interactive mode. |
154
- | `ERR_PATH_TRAVERSAL` | A path escaped its allowed root, or a name contained traversal markers. |
155
- | `ERR_WRITE_FAILED` | Writing output files failed (and was rolled back). |
156
- | `ERR_THEME_INVALID` | A theme definition was missing a required property (e.g. `name`). |
157
- | `ERR_THEME_LOAD` | A theme file could not be loaded / parsed into a `defineTheme` result. |
158
- | `ERR_TEMPLATE_CONFIG` | `template.get` is not configured in `astryx.config.mjs` (fetch-by-id). |
159
- | `ERR_TEMPLATE_GET` | A configured `template.get` threw or returned an invalid value. |
160
- | `ERR_VERSION_DETECT` | The current `@astryxdesign/core` version could not be detected. |
161
- | `ERR_INVALID_VERSION` | A `--from`/`--to` value was not a valid semver string. |
162
- | `ERR_DEP_MISSING` | A required external dependency (e.g. jscodeshift) is missing. |
163
- | `ERR_GH_CLI` | GitHub CLI (`gh`) is not installed or not authenticated. |
164
- | `ERR_GAP_REPORT_FAILED` | Filing a gap report failed (disabled, or the integration errored). |
125
+ | Code | Meaning |
126
+ | --- | --- |
127
+ | `ERR_UNKNOWN` | Generic fallback for any error without a more specific code. |
128
+ | `ERR_UNKNOWN_COMMAND` | A top-level command name was not recognized (e.g. `astryx bogus`). |
129
+ | `ERR_UNKNOWN_SUBCOMMAND` | A subcommand under a group was not recognized (e.g. `astryx theme bogus`). |
130
+ | `ERR_INVALID_OPTION` | An unknown flag was passed, or `--json` was used on a command that doesn't support it. |
131
+ | `ERR_INVALID_ARGUMENT` | An option/argument value was rejected, or required flags were missing. |
132
+ | `ERR_MISSING_ARGUMENT` | A required positional argument was omitted (e.g. `astryx theme build` with no file). |
133
+ | `ERR_INVALID_LANG` | `--lang` was given a value outside its choices (`en`, `zh`, `dense`). |
134
+ | `ERR_INVALID_DETAIL` | `--detail` was given a value outside its choices (`full`, `compact`, `brief`). |
135
+ | `ERR_NODE_VERSION` | The running Node.js version is below the supported minimum. |
136
+ | `ERR_CORE_NOT_FOUND` | `@astryxdesign/core` could not be located (not installed / not in a monorepo). |
137
+ | `ERR_UNKNOWN_COMPONENT` | No component matched the requested name. |
138
+ | `ERR_UNKNOWN_HOOK` | No hook matched the requested name. |
139
+ | `ERR_UNKNOWN_TOPIC` | No docs topic matched the requested name. |
140
+ | `ERR_UNKNOWN_SECTION` | A docs topic exists but the requested section within it does not. |
141
+ | `ERR_UNKNOWN_CATEGORY` | A `--category` filter value did not match any known category. |
142
+ | `ERR_UNKNOWN_TEMPLATE` | No template matched the requested name. |
143
+ | `ERR_UNKNOWN_PACKAGE` | No package matched the requested name (discover). |
144
+ | `ERR_UNKNOWN_AGENT` | An unrecognized `--agent` value was passed (agent docs / init). |
145
+ | `ERR_UNKNOWN_FEATURE` | An unrecognized `--features` value was passed to `init`. |
146
+ | `ERR_UNKNOWN_CODEMOD` | A `--codemod` value did not match any registered codemod (upgrade). |
147
+ | `ERR_NOT_FOUND` | A discover/lookup query matched nothing in any package. |
148
+ | `ERR_NO_DOC` | A component exists but has no typed `.doc.mjs` file. |
149
+ | `ERR_NO_SHOWCASE` | No showcase exists for the requested component. |
150
+ | `ERR_NO_SOURCE` | No source file could be located for the component/template. |
151
+ | `ERR_INVALID_DOC` | A component's docs failed validation (malformed `.doc.mjs`). |
152
+ | `ERR_FILE_NOT_FOUND` | A required input file did not exist. |
153
+ | `ERR_FILE_EXISTS` | Refused to overwrite an existing file in non-interactive mode. |
154
+ | `ERR_PATH_TRAVERSAL` | A path escaped its allowed root, or a name contained traversal markers. |
155
+ | `ERR_WRITE_FAILED` | Writing output files failed (and was rolled back). |
156
+ | `ERR_THEME_INVALID` | A theme definition was missing a required property (e.g. `name`). |
157
+ | `ERR_THEME_LOAD` | A theme file could not be loaded / parsed into a `defineTheme` result. |
158
+ | `ERR_TEMPLATE_CONFIG` | `template.get` is not configured in `astryx.config.mjs` (fetch-by-id). |
159
+ | `ERR_TEMPLATE_GET` | A configured `template.get` threw or returned an invalid value. |
160
+ | `ERR_VERSION_DETECT` | The current `@astryxdesign/core` version could not be detected. |
161
+ | `ERR_INVALID_VERSION` | A `--from`/`--to` value was not a valid semver string. |
162
+ | `ERR_DEP_MISSING` | A required external dependency (e.g. jscodeshift) is missing. |
163
+ | `ERR_GH_CLI` | GitHub CLI (`gh`) is not installed or not authenticated. |
164
+ | `ERR_GAP_REPORT_FAILED` | Filing a gap report failed (disabled, or the integration errored). |
165
165
 
166
166
  ## Capability manifest (agent discovery)
167
167
 
@@ -186,59 +186,25 @@ Shape:
186
186
  "version": "0.0.14",
187
187
  "description": "Design system CLI — components, themes, and tooling",
188
188
  "globalOptions": [
189
- {
190
- "flag": "--json",
191
- "type": "boolean",
192
- "description": "Output as typed JSON…",
193
- },
194
- {
195
- "flag": "--lang <locale>",
196
- "type": "enum",
197
- "choices": ["en", "zh", "dense"],
198
- },
199
- {
200
- "flag": "--detail <level>",
201
- "type": "enum",
202
- "choices": ["full", "compact", "brief"],
203
- "default": "full",
204
- },
189
+ {"flag": "--json", "type": "boolean", "description": "Output as typed JSON…"},
190
+ {"flag": "--lang <locale>", "type": "enum", "choices": ["en", "zh", "dense"]},
191
+ {"flag": "--detail <level>", "type": "enum", "choices": ["full", "compact", "brief"], "default": "full"}
205
192
  ],
206
193
  "commands": [
207
194
  {
208
195
  "name": "component",
209
196
  "description": "List components or print component docs",
210
- "arguments": [
211
- {
212
- "name": "name",
213
- "required": false,
214
- "variadic": false,
215
- "description": "",
216
- },
217
- ],
218
- "options": [
219
- {
220
- "flag": "--props",
221
- "type": "boolean",
222
- "description": "Print only the props table",
223
- },
224
- ],
197
+ "arguments": [{"name": "name", "required": false, "variadic": false, "description": ""}],
198
+ "options": [{"flag": "--props", "type": "boolean", "description": "Print only the props table"}],
225
199
  "json": true,
226
- "responseTypes": [
227
- "component.list",
228
- "component.detail",
229
- "component.detail.props",
230
- "…",
231
- ],
232
- "examples": ["astryx component Button --props --json"],
233
- },
200
+ "responseTypes": ["component.list", "component.detail", "component.detail.props", "…"],
201
+ "examples": ["astryx component Button --props --json"]
202
+ }
234
203
  // …one entry per command; subcommands (e.g. `theme build`) nest under `subcommands`
235
204
  ],
236
205
  "jsonSupported": ["component", "docs", "…"],
237
- "responseTypes": {
238
- "component": ["component.list", "…"],
239
- "theme build": ["theme.build"],
240
- },
241
- },
206
+ "responseTypes": {"component": ["component.list", "…"], "theme build": ["theme.build"]}
207
+ }
242
208
  }
243
209
  ```
244
210
 
@@ -259,15 +225,7 @@ For the standalone manifest envelope (`type: "manifest"`), use `astryx manifest
259
225
  The same logic that powers `xds --json` is available as importable, type-safe functions:
260
226
 
261
227
  ```typescript
262
- import {
263
- component,
264
- docs,
265
- discover,
266
- template,
267
- hook,
268
- search,
269
- AstryxError,
270
- } from '@astryxdesign/cli/api';
228
+ import {component, docs, discover, template, hook, search, AstryxError} from '@astryxdesign/cli/api';
271
229
 
272
230
  // Same result as: xds --json component Button
273
231
  const btn = await component('Button');
@@ -401,16 +359,16 @@ No failures — but review the ⚠ warnings above when you can.
401
359
 
402
360
  ### Checks
403
361
 
404
- | Check | Status it can return | What it verifies |
405
- | ---------------------------- | -------------------- | -------------------------------------------------------------------- |
406
- | Node.js version | pass / fail | Running Node meets the CLI's minimum |
407
- | @astryxdesign/core installed | pass / fail | `@astryxdesign/core` is resolvable from the project |
408
- | Version alignment | pass / warn / info | Installed `@astryxdesign/core` is in step with `@astryxdesign/cli` |
409
- | Theme packages | pass / warn | An `@astryxdesign/theme-*` package is installed and a theme is wired |
410
- | astryx.config.mjs | pass / fail / info | Config (if present) loads cleanly with a valid shape |
411
- | AI agent docs | pass / warn / info | Agent docs exist and contain the XDS section markers |
412
- | Peer dependencies | pass / warn / info | `@astryxdesign/core`'s peer deps (react, …) are installed |
413
- | Package manager | info | Reports the detected package manager |
362
+ | Check | Status it can return | What it verifies |
363
+ | -------------------- | -------------------- | ----------------------------------------------------------- |
364
+ | Node.js version | pass / fail | Running Node meets the CLI's minimum |
365
+ | @astryxdesign/core installed | pass / fail | `@astryxdesign/core` is resolvable from the project |
366
+ | Version alignment | pass / warn / info | Installed `@astryxdesign/core` is in step with `@astryxdesign/cli` |
367
+ | Theme packages | pass / warn | An `@astryxdesign/theme-*` package is installed and a theme is wired |
368
+ | astryx.config.mjs | pass / fail / info | Config (if present) loads cleanly with a valid shape |
369
+ | AI agent docs | pass / warn / info | Agent docs exist and contain the XDS section markers |
370
+ | Peer dependencies | pass / warn / info | `@astryxdesign/core`'s peer deps (react, …) are installed |
371
+ | Package manager | info | Reports the detected package manager |
414
372
 
415
373
  ### CI gate
416
374
 
@@ -5,8 +5,8 @@
5
5
  export const docsDense = {
6
6
  description: 'Theme provider, custom themes, light/dark, component overrides',
7
7
  sections: [
8
- { title: 'Quick Start', content: [null, null, null, null, { type: 'prose', text: 'default import = runtime injection. /built import = pre-compiled CSS (pair with theme.css).' }] },
9
- { title: 'Themes', content: [null, null, { type: 'prose', text: 'published: neutral (start here), butter, chocolate, gothic (dark-only), matcha, stone, y2k. @astryxdesign/theme-{name} = source (runtime). @astryxdesign/theme-{name}/built = optimized (+ theme.css).' }] },
8
+ { title: 'Quick Start', content: [null, null, { type: 'prose', text: 'default import = runtime injection. /built import = pre-compiled CSS (pair with theme.css).' }] },
9
+ { title: 'Themes', content: [null, { type: 'prose', text: 'published: neutral (start here), butter, chocolate, gothic (dark-only), matcha, stone, y2k. @astryxdesign/theme-{name} = source (runtime). @astryxdesign/theme-{name}/built = optimized (+ theme.css).' }] },
10
10
  { title: 'Props', content: [null] },
11
11
  { title: 'Custom Theme', content: [{ type: 'prose', text: 'CLI wizard or manual defineTheme. only override tokens that differ.' }, null] },
12
12
  { title: 'defineTheme', content: [{ type: 'prose', text: 'scale configs (color, typography, radius, motion) + explicit token overrides + component overrides. color derives full palette from accent hex via HCT.' }, null, null] },
@@ -14,12 +14,6 @@ export const docs = {
14
14
  title: 'Quick Start',
15
15
  category: 'guide',
16
16
  content: [
17
- {
18
- type: 'code',
19
- lang: 'bash',
20
- label: 'Install a theme package',
21
- code: 'npm install @astryxdesign/theme-neutral',
22
- },
23
17
  {
24
18
  type: 'code',
25
19
  lang: 'tsx',
@@ -51,10 +45,6 @@ function App() {
51
45
  );
52
46
  }`,
53
47
  },
54
- {
55
- type: 'prose',
56
- text: 'Each theme ships as its own npm package. Install the one you want, then wrap your app in `<Theme>` — the same pattern works for every theme; just swap the package and import name.',
57
- },
58
48
  {
59
49
  type: 'prose',
60
50
  text: 'The default import uses runtime style injection, which works everywhere with no build step. The `/built` import skips injection and relies on the pre-compiled CSS file for better performance and SSR support.',
@@ -65,10 +55,6 @@ function App() {
65
55
  title: 'Available Themes',
66
56
  category: 'guide',
67
57
  content: [
68
- {
69
- type: 'prose',
70
- text: 'Install the theme package you want with `npm install @astryxdesign/theme-{name}`, then import its theme object as shown below.',
71
- },
72
58
  {
73
59
  type: 'table',
74
60
  headers: ['Theme', 'Import', 'Description'],
@@ -5,8 +5,8 @@
5
5
  export const docsZh = {
6
6
  description: 'Theme 提供者、自定义主题、亮/暗模式和组件样式覆盖。',
7
7
  sections: [
8
- { title: '快速开始', content: [null, null, null, null, { type: 'prose', text: '默认导入使用运行时样式注入。/built 导入使用预编译 CSS(需配合 theme.css)。' }] },
9
- { title: '可用主题', content: [null, null, { type: 'prose', text: '已发布主题:neutral(推荐起点)、butter、chocolate、gothic(仅暗色)、matcha、stone、y2k。@astryxdesign/theme-{name} = 源码版(运行时注入)。@astryxdesign/theme-{name}/built = 优化版(配合 theme.css)。' }] },
8
+ { title: '快速开始', content: [null, null, { type: 'prose', text: '默认导入使用运行时样式注入。/built 导入使用预编译 CSS(需配合 theme.css)。' }] },
9
+ { title: '可用主题', content: [null, { type: 'prose', text: '已发布主题:neutral(推荐起点)、butter、chocolate、gothic(仅暗色)、matcha、stone、y2k。@astryxdesign/theme-{name} = 源码版(运行时注入)。@astryxdesign/theme-{name}/built = 优化版(配合 theme.css)。' }] },
10
10
  { title: 'Theme 属性', content: [null] },
11
11
  { title: '创建自定义主题', content: [{ type: 'prose', text: '使用 CLI 向导(推荐)或手动 defineTheme。只覆盖与默认值不同的令牌。' }, null] },
12
12
  { title: 'defineTheme', content: [{ type: 'prose', text: '支持比例配置(typography、radius、motion)+ 显式令牌覆盖 + 组件覆盖。' }, null, null] },
@@ -183,7 +183,7 @@ npx astryx docs tokens --dense`,
183
183
  "mcpServers": {
184
184
  "xds": {
185
185
  "type": "url",
186
- "url": "https://astryx.atmeta.com/mcp"
186
+ "url": "https://astryx.meta.com/mcp"
187
187
  }
188
188
  }
189
189
  }`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astryxdesign/cli",
3
- "version": "0.1.0-canary.e2d38fb",
3
+ "version": "0.1.0-canary.e457dac",
4
4
  "displayName": "CLI",
5
5
  "description": "Scaffold projects, browse templates, generate themes, and get agent-ready docs from the command line.",
6
6
  "author": "Meta Open Source",
@@ -15,7 +15,7 @@
15
15
  "url": "https://github.com/facebook/astryx/issues"
16
16
  },
17
17
  "keywords": [
18
- "astryx",
18
+ "xds",
19
19
  "cli",
20
20
  "design-system",
21
21
  "init",
@@ -54,9 +54,9 @@
54
54
  "jscodeshift": "^17.3.0"
55
55
  },
56
56
  "peerDependencies": {
57
- "@astryxdesign/core": "0.1.0-canary.e2d38fb",
58
- "@astryxdesign/lab": "0.1.0-canary.e2d38fb",
59
- "@astryxdesign/theme-neutral": "0.1.0-canary.e2d38fb"
57
+ "@astryxdesign/core": "0.1.0-canary.e457dac",
58
+ "@astryxdesign/lab": "0.1.0-canary.e457dac",
59
+ "@astryxdesign/theme-neutral": "0.1.0-canary.e457dac"
60
60
  },
61
61
  "peerDependenciesMeta": {
62
62
  "@astryxdesign/core": {
@@ -70,9 +70,9 @@
70
70
  }
71
71
  },
72
72
  "devDependencies": {
73
- "@astryxdesign/core": "0.1.0-canary.e2d38fb",
74
- "@astryxdesign/lab": "0.1.0-canary.e2d38fb",
75
- "@astryxdesign/theme-neutral": "0.1.0-canary.e2d38fb"
73
+ "@astryxdesign/core": "0.1.0-canary.e457dac",
74
+ "@astryxdesign/lab": "0.1.0-canary.e457dac",
75
+ "@astryxdesign/theme-neutral": "0.1.0-canary.e457dac"
76
76
  },
77
77
  "scripts": {
78
78
  "astryx": "node bin/astryx.mjs",
@@ -41,184 +41,14 @@ import {
41
41
  } from '../lib/component-discovery.mjs';
42
42
  import {discoverHooks, findHookDoc} from '../lib/hook-discovery.mjs';
43
43
  import {levenshteinDistance} from '../lib/string-utils.mjs';
44
- import {discoverTemplates, extractComponents} from './template.mjs';
44
+ import {discoverTemplates} from './template.mjs';
45
45
  import {AstryxError} from './error.mjs';
46
46
 
47
47
  const DOCS_DIR = path.join(CLI_ROOT, 'docs');
48
48
 
49
- /**
50
- * Synonym / intent map: product-language terms an agent is likely to type,
51
- * expanded to the catalog's vocabulary so oblique queries still rank. Keys and
52
- * values are matched bidirectionally (typing any value also pulls in the key
53
- * and its siblings). Lowercase, single words or short phrases.
54
- */
55
- const SYNONYMS = {
56
- dashboard: ['overview', 'analytics', 'kpi', 'kpis', 'metrics', 'stats', 'reporting', 'insights', 'control'],
57
- login: ['signin', 'auth', 'authentication', 'sso', 'credentials', 'account'],
58
- signup: ['register', 'registration', 'onboarding'],
59
- payment: ['checkout', 'billing', 'card', 'pay', 'purchase', 'order'],
60
- pricing: ['plans', 'plan', 'tiers', 'tier', 'subscription', 'subscriptions'],
61
- chat: ['messaging', 'message', 'messages', 'conversation', 'inbox', 'dm'],
62
- settings: ['preferences', 'config', 'configuration', 'account'],
63
- calendar: ['schedule', 'scheduling', 'events', 'event', 'month', 'agenda'],
64
- table: ['list', 'rows', 'records', 'grid', 'spreadsheet', 'datatable'],
65
- gallery: ['photos', 'photo', 'images', 'image', 'pictures'],
66
- hero: ['banner', 'splash', 'headline', 'landing'],
67
- form: ['fields', 'input', 'inputs', 'survey'],
68
- profile: ['bio', 'avatar', 'user'],
69
- documentation: ['docs', 'reference', 'guide', 'api'],
70
- navigation: ['nav', 'menu', 'sidebar'],
71
- };
72
-
73
- // Flatten into a token -> Set(expansions) lookup (bidirectional).
74
- const SYNONYM_INDEX = (() => {
75
- const idx = new Map();
76
- const add = (a, b) => {
77
- if (!idx.has(a)) idx.set(a, new Set());
78
- idx.get(a).add(b);
79
- };
80
- for (const [key, vals] of Object.entries(SYNONYMS)) {
81
- for (const v of vals) {
82
- add(key, v);
83
- add(v, key);
84
- for (const v2 of vals) if (v2 !== v) add(v, v2);
85
- }
86
- }
87
- return idx;
88
- })();
89
-
90
- /**
91
- * Light stemmer: strips common English suffixes so "charts"/"charting" and
92
- * "chart" share a root. Deliberately crude (no Porter) — good enough to bridge
93
- * plural/gerund gaps without a dependency.
94
- * @param {string} w
95
- * @returns {string}
96
- */
97
- export function stem(w) {
98
- let s = w;
99
- for (const suf of ['ing', 'ed', 'ies', 'es', 's']) {
100
- if (s.length > suf.length + 2 && s.endsWith(suf)) {
101
- s = suf === 'ies' ? s.slice(0, -3) + 'y' : s.slice(0, -suf.length);
102
- break;
103
- }
104
- }
105
- return s;
106
- }
107
-
108
-
109
49
  /** Valid domain filters for `--type`. */
110
50
  export const SEARCH_DOMAINS = ['component', 'hook', 'doc', 'template'];
111
51
 
112
- /**
113
- * Filler words stripped from multi-word queries so natural-language phrasing
114
- * ("a page where you can see business stats") ranks on its content words.
115
- */
116
- const STOPWORDS = new Set([
117
- 'a', 'an', 'the', 'of', 'for', 'to', 'with', 'and', 'or', 'in', 'on', 'at',
118
- 'by', 'that', 'this', 'my', 'your', 'our', 'their', 'is', 'are', 'be', 'it',
119
- 'its', 'as', 'from', 'page', 'screen', 'app', 'application', 'view', 'where',
120
- 'you', 'can', 'some', 'like', 'just', 'basically', 'kinda', 'want', 'wants',
121
- 'need', 'needs', 'something', 'thing', 'things', 'build', 'make', 'create',
122
- 'i', 'me', 'we', 'us', 'so', 'up', 'out', 'over', 'side', 'one', 'big',
123
- ]);
124
-
125
- /**
126
- * Split a query into meaningful content tokens (lowercased, stopwords + very
127
- * short words removed). Empty for single-word queries (callers fall back to
128
- * whole-phrase scoring).
129
- * @param {string} term - Already-lowercased query.
130
- * @returns {string[]}
131
- */
132
- export function tokenizeQuery(term) {
133
- return term
134
- .split(/\s+/)
135
- // Strip only leading/trailing punctuation; keep joined identifiers intact
136
- // (e.g. "foo_bar" stays one token) so gibberish stays gibberish.
137
- .map(t => t.replace(/^[^a-z0-9]+|[^a-z0-9]+$/g, ''))
138
- .filter(t => t.length >= 2 && !STOPWORDS.has(t));
139
- }
140
-
141
- /**
142
- * Score a candidate against a query, handling multi-word natural language.
143
- * Tries the whole phrase (so exact/near matches still win) AND a per-token
144
- * pass (so "data table with filters" matches `table-page` via table+filter),
145
- * and returns whichever is stronger.
146
- *
147
- * @param {string} term - Lowercased full query.
148
- * @param {string[]} tokens - Content tokens from tokenizeQuery(term).
149
- * @param {object} candidate
150
- * @returns {{score: number, reason: string} | null}
151
- */
152
- /**
153
- * Minimum per-token score (in the multi-word pass) to count as a real match.
154
- * 50 = a genuine name/keyword/description hit; below that is loose Levenshtein
155
- * fuzz that would otherwise turn gibberish queries into noise.
156
- */
157
- const MIN_TOKEN_SCORE = 50;
158
-
159
- /**
160
- * Best score for a token against a candidate, fanning out through synonyms
161
- * (synonym hits are discounted so a direct hit always wins).
162
- * @returns {{score: number, reason: string} | null}
163
- */
164
- function bestForToken(tok, candidate) {
165
- let best = scoreCandidate(tok, candidate);
166
- const syns = SYNONYM_INDEX.get(tok);
167
- if (syns) {
168
- for (const s of syns) {
169
- const h = scoreCandidate(s, candidate);
170
- if (h) {
171
- const score = Math.round(h.score * 0.85);
172
- if (!best || score > best.score) best = {score, reason: `${h.reason} (~${tok})`};
173
- }
174
- }
175
- }
176
- return best;
177
- }
178
-
179
- export function scoreQuery(term, tokens, candidate) {
180
- const full = scoreCandidate(term, candidate);
181
-
182
- // 0–1 content tokens: keep whole-phrase fuzzy matching (typo tolerance for
183
- // single words), but if stopwords left exactly one DIFFERENT token (e.g.
184
- // "pricing page" → "pricing"), score that token too and take the stronger.
185
- if (tokens.length <= 1) {
186
- const single = tokens.length === 1 ? bestForToken(tokens[0], candidate) : null;
187
- if (full && (!single || full.score >= single.score)) return full;
188
- return single;
189
- }
190
-
191
- // Multi-word natural language: score each content token, counting only
192
- // strong hits, then reward coverage so candidates matching more terms win.
193
- let sum = 0;
194
- let matched = 0;
195
- const hitTerms = [];
196
- for (const tok of tokens) {
197
- const h = bestForToken(tok, candidate);
198
- if (h && h.score >= MIN_TOKEN_SCORE) {
199
- sum += h.score;
200
- matched++;
201
- hitTerms.push(tok);
202
- }
203
- }
204
- if (matched === 0) return full;
205
-
206
- // Reward the AVERAGE strength of the concepts that matched (not divided by
207
- // total query length — that penalizes verbose / low-fidelity prompts), plus
208
- // a bonus per additional matched concept and a coverage term. A candidate
209
- // that matches several of the query's concepts beats one matching a single
210
- // incidental word.
211
- const avgMatched = sum / matched;
212
- const coverage = matched / tokens.length;
213
- const tokenScore = Math.round(avgMatched + Math.min(matched - 1, 3) * 12 + coverage * 15);
214
-
215
- if (full && full.score >= tokenScore) return full;
216
- return {
217
- score: tokenScore,
218
- reason: `matches ${matched}/${tokens.length} terms: ${hitTerms.join(', ')}`,
219
- };
220
- }
221
-
222
52
  /**
223
53
  * Score a single candidate against the search term across name, keywords,
224
54
  * and prose signals. Returns the best (highest) score plus a human reason,
@@ -277,13 +107,10 @@ export function scoreCandidate(term, {name, keywords = [], description = '', pro
277
107
  else if (dist === 2) consider(30, `keyword "${kw}" (distance ${dist})`);
278
108
  }
279
109
 
280
- // ── Prose / description signals (stem-tolerant whole word) ──────
281
- // Match the term's stem as a whole word, tolerating plural/gerund suffixes
282
- // so "chart" matches "charts" and "filter" matches "filtering".
110
+ // ── Prose / description signals (whole-word boundary) ───────────
283
111
  if (term.length >= 3) {
284
- const root = stem(term);
285
- const escaped = root.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
286
- const re = new RegExp(`\\b${escaped}(s|es|ing|ed|ies)?\\b`);
112
+ const escaped = term.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
113
+ const re = new RegExp(`\\b${escaped}\\b`);
287
114
  if (description && re.test(description.toLowerCase())) {
288
115
  consider(50, `description mentions "${term}"`);
289
116
  } else {
@@ -413,30 +240,14 @@ async function gatherTemplates(cwd) {
413
240
  } catch {
414
241
  return [];
415
242
  }
416
- return templates.map(t => {
417
- // Blocks ship componentsUsed; page templates don't, so derive them from the
418
- // source. Category words (e.g. "Dashboard - Analytics") are strong intent
419
- // signal for pages, which otherwise only index on name + description.
420
- let keywords = Array.isArray(t.componentsUsed) ? [...t.componentsUsed] : [];
421
- if (t.type === 'page') {
422
- if (t.filePath) {
423
- try {
424
- keywords = keywords.concat(extractComponents(t.filePath));
425
- } catch {
426
- // Best-effort: skip keyword enrichment if the source can't be read.
427
- }
428
- }
429
- if (t.category) keywords = keywords.concat(t.category.split(/[^A-Za-z0-9]+/).filter(Boolean));
430
- }
431
- return {
432
- domain: 'template',
433
- name: t.dirName,
434
- keywords,
435
- description: t.description || '',
436
- _displayName: t.name,
437
- _kind: t.type, // 'page' | 'block'
438
- };
439
- });
243
+ return templates.map(t => ({
244
+ domain: 'template',
245
+ name: t.dirName,
246
+ keywords: Array.isArray(t.componentsUsed) ? t.componentsUsed : [],
247
+ description: t.description || '',
248
+ _displayName: t.name,
249
+ _kind: t.type, // 'page' | 'block'
250
+ }));
440
251
  }
441
252
 
442
253
  /**
@@ -514,7 +325,6 @@ export async function search(query, options = {}) {
514
325
  }
515
326
 
516
327
  const term = String(query).trim().toLowerCase();
517
- const tokens = tokenizeQuery(term);
518
328
 
519
329
  const coreDir = findCoreDir(cwd);
520
330
  if (!coreDir) {
@@ -532,13 +342,9 @@ export async function search(query, options = {}) {
532
342
 
533
343
  const all = [...components, ...hooks, ...docTopics, ...templates];
534
344
 
535
- // Score every candidate on its own merits. The consumer groups results by
536
- // role (page / block / component) and takes the top of each, so there's no
537
- // cross-role competition to engineer — a target page only needs to be the
538
- // strongest PAGE, not outrank every component.
539
345
  const scored = [];
540
346
  for (const candidate of all) {
541
- const hit = scoreQuery(term, tokens, candidate);
347
+ const hit = scoreCandidate(term, candidate);
542
348
  if (hit) scored.push(toResult(candidate, hit.score, hit.reason));
543
349
  }
544
350
 
@@ -95,7 +95,6 @@ async function discoverPages() {
95
95
  dirName: dir.name,
96
96
  name: doc?.name || dir.name,
97
97
  description: doc?.description || '',
98
- category: doc?.category || '',
99
98
  isReady: doc?.isReady ?? true,
100
99
  scaffold: doc?.scaffold ?? false,
101
100
  filePath: path.join(dirPath, 'page.tsx'),
@@ -246,7 +245,7 @@ const UBIQUITOUS = new Set([
246
245
  'StackItem', 'Icon',
247
246
  ]);
248
247
 
249
- export function extractComponents(pagePath) {
248
+ function extractComponents(pagePath) {
250
249
  const src = fs.readFileSync(pagePath, 'utf-8');
251
250
  // Match JSX opening tags, e.g. `<Section` or the legacy `<XDSSection`.
252
251
  // Templates author bare component names post un-prefix migration