@astryxdesign/cli 0.1.7 → 0.1.8-canary.0c4d8d7

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 (134) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +115 -19
  3. package/docs/cli-integrations.doc.mjs +150 -0
  4. package/docs/elevation.doc.mjs +79 -1
  5. package/docs/getting-started.doc.mjs +9 -9
  6. package/docs/migration.doc.mjs +18 -18
  7. package/docs/principles.doc.dense.mjs +1 -1
  8. package/docs/principles.doc.mjs +6 -6
  9. package/docs/principles.doc.zh.mjs +1 -1
  10. package/docs/styling-libraries.doc.mjs +3 -3
  11. package/docs/styling.doc.mjs +4 -4
  12. package/docs/theme.doc.dense.mjs +2 -2
  13. package/docs/theme.doc.mjs +7 -7
  14. package/docs/theme.doc.zh.mjs +1 -1
  15. package/docs/tokens.doc.mjs +1 -1
  16. package/docs/working-with-ai.doc.mjs +19 -19
  17. package/package.json +13 -11
  18. package/scripts/postinstall.mjs +74 -0
  19. package/src/api/doctor.mjs +3 -3
  20. package/src/api/error.mjs +2 -2
  21. package/src/codemods/__tests__/registry.test.mjs +1 -0
  22. package/src/codemods/ensure-jscodeshift.mjs +11 -27
  23. package/src/codemods/registry.mjs +1 -0
  24. package/src/codemods/run-codemod.mjs +1 -1
  25. package/src/codemods/runner.mjs +2 -2
  26. package/src/codemods/transforms/v0.1.0/__tests__/drop-xds-prefix-imports.test.mjs +42 -7
  27. package/src/codemods/transforms/v0.1.0/__tests__/migrate-xds-module-specifiers.test.mjs +43 -0
  28. package/src/codemods/transforms/v0.1.0/drop-xds-prefix-imports.mjs +102 -3
  29. package/src/codemods/transforms/v0.1.0/migrate-xds-module-specifiers.mjs +72 -6
  30. package/src/codemods/transforms/v0.1.8/__tests__/rename-avatar-size-scale.test.mjs +161 -0
  31. package/src/codemods/transforms/v0.1.8/index.mjs +19 -0
  32. package/src/codemods/transforms/v0.1.8/rename-avatar-size-scale.mjs +234 -0
  33. package/src/commands/agent-docs.mjs +136 -20
  34. package/src/commands/agent-docs.test.mjs +123 -10
  35. package/src/commands/build-theme.mjs +10 -71
  36. package/src/commands/build.mjs +15 -15
  37. package/src/commands/cli-postinstall.test.mjs +42 -0
  38. package/src/commands/component/index.mjs +4 -4
  39. package/src/commands/discover.mjs +7 -5
  40. package/src/commands/docs.mjs +4 -4
  41. package/src/commands/hook/index.mjs +4 -4
  42. package/src/commands/init.mjs +48 -152
  43. package/src/commands/init.next-steps.test.mjs +1 -1
  44. package/src/commands/interactive-guard.test.mjs +19 -22
  45. package/src/commands/json-contract.test.mjs +2 -2
  46. package/src/commands/layout.mjs +1 -1
  47. package/src/commands/search.mjs +4 -4
  48. package/src/commands/setup-nudge.test.mjs +108 -0
  49. package/src/commands/swizzle.mjs +12 -35
  50. package/src/commands/template.mjs +11 -31
  51. package/src/commands/upgrade.mjs +138 -32
  52. package/src/commands/upgrade.test.mjs +155 -1
  53. package/src/index.mjs +40 -6
  54. package/src/lib/cli-error.mjs +2 -2
  55. package/src/lib/component-format.mjs +2 -1
  56. package/src/lib/json-shim.mjs +1 -1
  57. package/src/lib/json.mjs +3 -3
  58. package/src/lib/term-log.mjs +48 -0
  59. package/src/types/api.d.ts +4 -9
  60. package/src/types/base.d.ts +24 -3
  61. package/src/types/build.d.ts +23 -0
  62. package/src/types/index.d.ts +1 -0
  63. package/src/types/swizzle.d.ts +4 -0
  64. package/src/types/template-api.d.ts +4 -1
  65. package/src/types/upgrade.d.ts +39 -2
  66. package/src/utils/package-manager.mjs +108 -4
  67. package/src/utils/package-manager.test.mjs +108 -1
  68. package/src/utils/path-safety.mjs +0 -18
  69. package/src/utils/update-check.mjs +5 -4
  70. package/src/utils/update-check.test.mjs +3 -3
  71. package/templates/blocks/components/Avatar/AvatarFallbackChain.tsx +4 -4
  72. package/templates/blocks/components/Avatar/AvatarGroup.tsx +2 -2
  73. package/templates/blocks/components/Avatar/AvatarInitialsFallback.tsx +1 -1
  74. package/templates/blocks/components/Avatar/AvatarShowcase.tsx +4 -4
  75. package/templates/blocks/components/Avatar/AvatarUserCard.tsx +1 -1
  76. package/templates/blocks/components/Avatar/AvatarWithImage.tsx +4 -4
  77. package/templates/blocks/components/Avatar/AvatarWithStatus.tsx +3 -3
  78. package/templates/blocks/components/AvatarGroup/AvatarGroupShowcase.tsx +2 -2
  79. package/templates/blocks/components/AvatarGroupOverflow/AvatarGroupOverflowCustomText.tsx +1 -1
  80. package/templates/blocks/components/AvatarGroupOverflow/AvatarGroupOverflowDefault.tsx +1 -1
  81. package/templates/blocks/components/AvatarGroupOverflow/AvatarGroupOverflowShowcase.tsx +2 -2
  82. package/templates/blocks/components/AvatarStatusDot/AvatarStatusDotShowcase.tsx +3 -3
  83. package/templates/blocks/components/AvatarStatusDot/AvatarStatusDotVariants.tsx +3 -3
  84. package/templates/blocks/components/Banner/BannerFloating.doc.mjs +14 -0
  85. package/templates/blocks/components/Banner/BannerFloating.tsx +16 -0
  86. package/templates/blocks/components/Button/ButtonFloating.doc.mjs +14 -0
  87. package/templates/blocks/components/Button/ButtonFloating.tsx +37 -0
  88. package/templates/blocks/components/ButtonGroup/ButtonGroupFloating.doc.mjs +14 -0
  89. package/templates/blocks/components/ButtonGroup/ButtonGroupFloating.tsx +23 -0
  90. package/templates/blocks/components/Card/CardElevations.doc.mjs +14 -0
  91. package/templates/blocks/components/Card/CardElevations.tsx +32 -0
  92. package/templates/blocks/components/Card/ClickableCardElevated.doc.mjs +14 -0
  93. package/templates/blocks/components/Card/ClickableCardElevated.tsx +21 -0
  94. package/templates/blocks/components/Card/SelectableCardElevated.doc.mjs +14 -0
  95. package/templates/blocks/components/Card/SelectableCardElevated.tsx +37 -0
  96. package/templates/blocks/components/Carousel/CarouselSnap.tsx +1 -1
  97. package/templates/blocks/components/ChatComposer/ChatComposerFlat.doc.mjs +14 -0
  98. package/templates/blocks/components/ChatComposer/ChatComposerFlat.tsx +75 -0
  99. package/templates/blocks/components/ChatMessage/ChatMessageAvatarName.tsx +2 -2
  100. package/templates/blocks/components/ChatMessage/ChatMessageMultiBubble.tsx +1 -1
  101. package/templates/blocks/components/ChatMessageBubble/ChatMessageBubbleGrouping.tsx +1 -1
  102. package/templates/blocks/components/ChatMessageBubble/ChatMessageBubbleMetadata.tsx +1 -1
  103. package/templates/blocks/components/ChatMessageList/ChatMessageListDensity.tsx +5 -9
  104. package/templates/blocks/components/ChatMessageList/ChatMessageListFullFeatured.tsx +1 -1
  105. package/templates/blocks/components/CodeBlock/CodeBlockTerminal.tsx +1 -1
  106. package/templates/blocks/components/HoverCard/HoverCardShowcase.tsx +1 -1
  107. package/templates/blocks/components/IconButton/IconButtonFloating.doc.mjs +14 -0
  108. package/templates/blocks/components/IconButton/IconButtonFloating.tsx +37 -0
  109. package/templates/blocks/components/Item/ItemWithMedia.tsx +2 -2
  110. package/templates/blocks/components/ListItem/ListItemWithMedia.tsx +2 -2
  111. package/templates/blocks/components/OverflowList/OverflowListCappedToolbar.doc.mjs +14 -0
  112. package/templates/blocks/components/OverflowList/OverflowListCappedToolbar.tsx +39 -0
  113. package/templates/blocks/components/OverflowList/OverflowListMultiRowTags.doc.mjs +14 -0
  114. package/templates/blocks/components/OverflowList/OverflowListMultiRowTags.tsx +44 -0
  115. package/templates/blocks/components/Pagination/PaginationDotsCarousel.tsx +2 -6
  116. package/templates/blocks/components/Stack/StackFillItem.tsx +2 -6
  117. package/templates/blocks/components/TabList/TabListTabsWithActions.doc.mjs +1 -1
  118. package/templates/blocks/components/TabList/TabListTabsWithActions.tsx +2 -7
  119. package/templates/blocks/components/Table/TableRowStatusTable.doc.mjs +14 -0
  120. package/templates/blocks/components/Table/TableRowStatusTable.tsx +59 -0
  121. package/templates/blocks/components/Thumbnail/ThumbnailElevated.doc.mjs +14 -0
  122. package/templates/blocks/components/Thumbnail/ThumbnailElevated.tsx +31 -0
  123. package/templates/blocks/components/TypeaheadItem/TypeaheadItemShowcase.tsx +1 -1
  124. package/templates/pages/ai-chat/page.tsx +4 -4
  125. package/templates/pages/dashboard-portfolio/page.tsx +3 -11
  126. package/templates/pages/detail-page/page.tsx +5 -12
  127. package/templates/pages/documentation-design/page.tsx +1 -1
  128. package/templates/pages/messaging-shell/page.tsx +6 -8
  129. package/templates/pages/table-grouped/page.tsx +9 -14
  130. package/templates/pages/table-page/page.tsx +7 -9
  131. package/templates/pages/table-page-heatmap-status/page.tsx +5 -13
  132. package/docs/integration-authoring.md +0 -105
  133. package/src/utils/interactive.mjs +0 -76
  134. package/src/utils/interactive.test.mjs +0 -70
package/CHANGELOG.md CHANGED
@@ -1,5 +1,45 @@
1
1
  # @xds/cli
2
2
 
3
+ # 0.1.8
4
+
5
+ #### Breaking Changes
6
+
7
+ - Avatar and AvatarGroup adopt Icon's abbreviated size scale — `size` now takes `xsm`/`sm`/`md`/`lg`/`xl` instead of `tiny`/`xsmall`/`small`/`medium`/`large`. Pixel values are unchanged (20/24/36/48/128px) and the default is now `md` (still 36px, formerly `small`). Avatar's tiers stay larger than Icon's because avatars align with media rather than glyphs. Run `astryx upgrade` to migrate call sites. (#2672)
8
+
9
+ #### New Features
10
+
11
+ - `astryx init --features agents` now defaults to creating root `AGENTS.md` — the tool-agnostic standard that Codex/Copilot, Cursor, and most agents read — instead of the Claude-specific `.claude/CLAUDE.md`. Claude output is now opt-in via `--agent claude` (→ `.claude/CLAUDE.md`), and `--agent all` still writes both. Projects with existing agent-doc files are unaffected: init still discovers and updates every file already present, so this only changes the from-scratch default. (#4216)
12
+ - "Foolproof init": both `@astryxdesign/core` and `@astryxdesign/cli` now print a postinstall nudge pointing you to `npx @astryxdesign/cli init`, `astryx` commands nudge you to finish setup until init has run, and `astryx init` runs non-interactively (no TTY required) so it works in CI and agent environments. (#4147, #4153, #4154, #4155)
13
+
14
+ #### Fixes
15
+
16
+ - Stop suggesting bare `npx astryx` before the CLI is installed — it resolves to an unrelated package on the npm registry.
17
+ The CLI now emits an install-aware invocation everywhere it prints a command:
18
+ - Extend the v0.1.0 upgrade codemods to cover test files that mock `@xds/core` modules, which were previously left half-migrated and broke after upgrade:
19
+ - `astryx upgrade` now keeps the managed agent-docs block (`<!-- ASTRYX:START --> … <!-- ASTRYX:END -->`) in sync with the installed version on **every** path — including the up-to-date and no-codemods short-circuits that previously returned before any refresh, leaving AI agents reading a stale component index and superseded rules. The block documents the installed library, so it's now refreshed up front (independent of codemods) and reported in the `--json` receipt as `agentDocs`. One detection pass covers three cases: a stale block is rewritten (`--apply`) or reported as a pending change (dry-run, which no longer writes); a project with core installed but no managed block is nudged to run `astryx init --features agents`; an already-current block stays silent. (#4168, #4169)
20
+
21
+ #### Documentation
22
+
23
+ - Add a `cli-integrations` CLI docs topic (`astryx docs cli-integrations`) so the integration-authoring guide (originally written by @ejhammond) is discoverable through the CLI and docsite instead of an unreferenced markdown file. Rewrite the CLI README's Configuration section to match the current strict config schema (`integrations`, `issuesUrl`, `hooks.postCodemod`, `experimental.xle`) and reframe the Integrations section around the two-file API.
24
+
25
+ #### Other Changes
26
+
27
+ - Installed / global / dev runs suggest `<pm> astryx <cmd>` (e.g. `pnpm exec astryx …`), unchanged.
28
+ - One-off runs (launched via `npx`/`pnpm dlx`/`yarn dlx`/`bunx`) suggest the scoped package `<dlx> @astryxdesign/cli <cmd>`, which always resolves to us.
29
+ - **migrate-xds-module-specifiers**: rewrite the mocked-module path in `vi.mock`/`vi.doMock`/`jest.mock`/`jest.doMock` (and bare `mock`) calls, plus `import(...)` specifiers used in TS type positions (`typeof import('@xds/core/Text')`), so the mock still intercepts the renamed `@astryxdesign/*` import.
30
+ - **drop-xds-prefix-imports**: un-prefix partial-mock override keys inside an `@xds/core` mock factory (e.g. `useXDSTruncation` → `useTruncation`) so the override matches the renamed export instead of silently overriding nothing. Scoped to recognized `@xds/core` mock factories only; unrelated object keys are untouched.
31
+
32
+ #### Contributors
33
+
34
+ Thanks to everyone who contributed to this release:
35
+
36
+ - @cixzhang
37
+ - @ejhammond
38
+ - @joeyfarina
39
+ - @josephfarina
40
+
41
+ ---
42
+
3
43
  # 0.1.7
4
44
 
5
45
  #### New Features
package/README.md CHANGED
@@ -2,15 +2,19 @@
2
2
 
3
3
  The CLI is the primary interface for working with the design system, for humans and machines alike. It provides component documentation, design tokens, page templates, theming tools, and upgrade codemods, all accessible via terminal commands, a typed JSON API, or programmatic imports. AI agents and build tools use the same API that powers the CLI, enabling end-to-end frontend development loops.
4
4
 
5
+ Run it one-off with the scoped package (works whether or not it's installed):
6
+
5
7
  ```bash
6
- npx astryx --help
7
- npx astryx search button
8
- npx astryx component Button
9
- npx astryx docs tokens
10
- npx astryx docs migration
11
- npx astryx template --list
8
+ npx @astryxdesign/cli --help
9
+ npx @astryxdesign/cli search button
10
+ npx @astryxdesign/cli component Button
11
+ npx @astryxdesign/cli docs tokens
12
+ npx @astryxdesign/cli docs migration
13
+ npx @astryxdesign/cli template --list
12
14
  ```
13
15
 
16
+ Once it's a project dependency (`npm install -D @astryxdesign/cli`), drop the scope and use the shorter `astryx` — e.g. `npx astryx component Button` or `pnpm exec astryx component Button`. Bare `astryx` resolves to an unrelated npm package until the CLI is installed, so prefer the scoped form above for first-run/one-off use.
17
+
14
18
  ## Finding things: `astryx search`
15
19
 
16
20
  When you don't know whether what you need is a component, a hook, a docs topic,
@@ -20,27 +24,29 @@ fuzzy matching for typos) and tagged with their domain plus the follow-up
20
24
  command to run:
21
25
 
22
26
  ```bash
23
- $ npx astryx search button
27
+ $ astryx search button
24
28
 
25
29
  Results for "button" (20):
26
30
 
27
31
  [component] Button
28
32
  Button triggers an action when clicked. Use it for form submissions…
29
- npx astryx component Button
33
+ → astryx component Button
30
34
 
31
35
  [component] IconButton
32
36
  A button that shows only an icon with no visible text…
33
- npx astryx component IconButton
37
+ → astryx component IconButton
34
38
 
35
39
  [hook] useClickableContainer
36
40
  Makes a container element clickable while preserving nested…
37
- npx astryx hook useClickableContainer
41
+ → astryx hook useClickableContainer
38
42
 
39
43
  [template] Banner — Collapsible
40
44
  Combine an action button, dismiss control, and expandable detail area…
41
- npx astryx template BannerCollapsibleContent
45
+ → astryx template BannerCollapsibleContent
42
46
  ```
43
47
 
48
+ (The CLI prints the follow-up commands with your actual runner — `npx astryx …` when installed, or `npx @astryxdesign/cli …` when run one-off.)
49
+
44
50
  Options:
45
51
 
46
52
  - `--type <component|hook|doc|template>`: restrict to a single domain
@@ -415,7 +421,7 @@ failures (warnings are fine) and `1` when any check fails. That makes it
415
421
  usable directly as a CI step:
416
422
 
417
423
  ```yaml
418
- - run: npx astryx doctor
424
+ - run: npx @astryxdesign/cli doctor
419
425
  ```
420
426
 
421
427
  Use `--json` for a structured envelope (`{ apiVersion, type: "doctor",
@@ -423,13 +429,103 @@ data: { checks, summary } }`) that AI agents and scripts can parse.
423
429
 
424
430
  ## Configuration
425
431
 
426
- The CLI reads from an optional `astryx.config.mjs` in your project root:
432
+ The CLI reads an optional `astryx.config.{ts,mjs,js}` from your project root
433
+ (a sibling of `package.json`). Every field is optional; with no config file the
434
+ CLI runs on defaults.
427
435
 
428
- ```javascript
429
- export default {
430
- templates: {
431
- get: async id => fetchTemplateFromAPI(id),
432
- },
436
+ ```typescript
437
+ import {createConfig} from '@astryxdesign/core/config';
438
+
439
+ export default createConfig({
440
+ integrations: ['@acme/astryx-widgets'],
433
441
  issuesUrl: 'https://github.com/your-org/your-repo/issues',
434
- };
442
+ });
443
+ ```
444
+
445
+ `createConfig` is a type-preserving helper: it returns its argument unchanged
446
+ and exists only to give the config file editor autocomplete and type-checking. A
447
+ plain `export default {}` object works identically. It's exported from
448
+ `@astryxdesign/core` (not the CLI) so your config file gets type feedback
449
+ without depending on the CLI; the same helper is re-exported from
450
+ `@astryxdesign/cli/config` for back-compat.
451
+
452
+ | Field | Type | Purpose |
453
+ | ----------------------------- | ------------------------------ | ----------------------------------------------------------------------------------------------- |
454
+ | `integrations` | `string[]` | Integration package names to load (see [Integrations](#integrations)). |
455
+ | `issuesUrl` | `string` | Where "report an issue" links point for your project. Defaults to the core issue tracker. |
456
+ | `hooks.postCodemod` | `PostCodemodHook[]` | Commands to run after `astryx upgrade` applies codemods (e.g. reinstall, rebuild, reformat). |
457
+ | `experimental.xle.components` | `Record<string, XleComponent>` | Register app-local components so layout (XLE) expressions can reference them by name. Unstable. |
458
+
459
+ The config is validated against a strict schema when the CLI loads it, so an
460
+ unknown field is a hard error rather than a silent no-op. `astryx doctor`
461
+ reports whether the config loads cleanly.
462
+
463
+ ## Integrations
464
+
465
+ An **integration** is any npm package that contributes its own components,
466
+ templates, and upgrade codemods to Astryx. The CLI surfaces them next to core's,
467
+ through the same commands, so a consumer can `astryx component`,
468
+ `astryx template`, and `astryx upgrade` across core and every integration
469
+ uniformly. Use it to ship a first-party add-on, publish a third-party component
470
+ library, or share an internal design-system package across apps.
471
+
472
+ The system runs on two files, each with a small typed API:
473
+
474
+ | File | Written by | Role |
475
+ | -------------------------------- | ---------- | ----------------------------------------- |
476
+ | `astryx.config.{ts,mjs,js}` | Consumer | Lists which integration packages to load. |
477
+ | `astryx.integration.{ts,mjs,js}` | Author | Declares what a package contributes. |
478
+
479
+ The consumer side is the `integrations` field of [`astryx.config`](#configuration).
480
+ The author side is the integration manifest below.
481
+
482
+ ### The integration manifest
483
+
484
+ A package becomes an integration by exporting a manifest from
485
+ `astryx.integration.{ts,mjs,js}` at its root (a sibling of `package.json`). The
486
+ manifest points at where each kind of contribution lives; identity (name,
487
+ version) comes from `package.json`, not the manifest.
488
+
489
+ ```typescript
490
+ import {createIntegration} from '@astryxdesign/core/authoring';
491
+
492
+ export default createIntegration({
493
+ components: './components',
494
+ templates: './templates',
495
+ codemods: './codemods',
496
+ issuesUrl: 'https://github.com/acme/widgets/issues',
497
+ });
498
+ ```
499
+
500
+ | Field | Type | Purpose |
501
+ | ------------ | -------- | --------------------------------------------------------------------- |
502
+ | `components` | `string` | Directory holding the package's components and their `.doc.*` files. |
503
+ | `templates` | `string` | Directory holding the package's page/block templates. |
504
+ | `codemods` | `string` | Directory holding upgrade codemods run by `astryx upgrade`. |
505
+ | `issuesUrl` | `string` | Where "report an issue" links for this package's contributions point. |
506
+
507
+ Every field is optional; declare only the roots the package ships.
508
+ `createIntegration` is a type-preserving helper (editor autocomplete and
509
+ type-checking); it lives in `@astryxdesign/core/authoring` and is re-exported
510
+ from `@astryxdesign/cli/integration` for back-compat.
511
+
512
+ ### How it works
513
+
514
+ Every command loads the consumer's `astryx.config`, resolves each listed
515
+ integration's manifest from `node_modules`, and discovers its contributions.
516
+ Everything is validated against one strict schema at the load boundary, so the
517
+ CLI presents core and integration contributions through a single, uniform
518
+ surface.
519
+
520
+ Discovery is resilient: a broken or misconfigured integration is skipped with a
521
+ one-line warning on stderr instead of crashing the CLI, and it never corrupts a
522
+ `--json` envelope. To inspect problems, run
523
+ `astryx validate-integration <package>` for a detailed report on one package, or
524
+ `astryx doctor` for an overall health check.
525
+
526
+ For the full authoring walkthrough (component doc format, template packaging
527
+ and `exports` requirements, and codemod authoring), see the guide:
528
+
529
+ ```bash
530
+ astryx docs cli-integrations
435
531
  ```
@@ -0,0 +1,150 @@
1
+ // Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ /** @type {import('../../core/src/docs-types').ReferenceDoc} */
4
+
5
+ export const docs = {
6
+ name: 'cli-integrations',
7
+ title: 'CLI Integrations',
8
+ category: 'guide',
9
+ description:
10
+ 'Author an npm package that contributes components, templates, and upgrade codemods to Astryx.',
11
+
12
+ sections: [
13
+ {
14
+ title: 'Overview',
15
+ category: 'guide',
16
+ content: [
17
+ {
18
+ type: 'prose',
19
+ text: 'An integration is an npm package that contributes components, templates, and/or upgrade codemods to a consumer\u2019s design-system workflow. Consumers install the package and add it to their `astryx.config`; from then on the integration\u2019s contributions show up alongside core\u2019s in the same CLI commands.',
20
+ },
21
+ {
22
+ type: 'prose',
23
+ text: 'The system runs on two files. The consumer writes `astryx.config.{ts,mjs,js}` at their project root to list which packages to load. The author writes `astryx.integration.{ts,mjs,js}` at the package root to declare what the package contributes. This page is the author\u2019s guide. For the consumer side, run `npx astryx docs getting-started`.',
24
+ },
25
+ {
26
+ type: 'prose',
27
+ text: 'On the consumer side, adding your package is one line:',
28
+ },
29
+ {
30
+ type: 'code',
31
+ lang: 'typescript',
32
+ code: "import {createConfig} from '@astryxdesign/core/config';\n\nexport default createConfig({\n integrations: ['@acme/astryx-widgets'],\n});",
33
+ },
34
+ {
35
+ type: 'prose',
36
+ text: 'Your components and templates then appear next to core\u2019s:',
37
+ },
38
+ {
39
+ type: 'code',
40
+ lang: 'bash',
41
+ code: 'astryx component --list --package @acme/astryx-widgets\nastryx component AcmeCarousel --props',
42
+ },
43
+ ],
44
+ },
45
+ {
46
+ title: 'The Integration File',
47
+ category: 'guide',
48
+ content: [
49
+ {
50
+ type: 'prose',
51
+ text: 'To register your package as an integration, add an `astryx.integration.{ts,mjs,js}` file as a sibling of your `package.json`. It tells the CLI where to find your components, templates, and codemods. Identity (name, version) comes from your `package.json`, not this file.',
52
+ },
53
+ {
54
+ type: 'code',
55
+ lang: 'typescript',
56
+ code: "// astryx.integration.ts\nimport {createIntegration} from '@astryxdesign/core/authoring';\n\nexport default createIntegration({\n components: './components',\n templates: './templates',\n codemods: './codemods',\n issuesUrl: 'https://github.com/acme/widgets/issues',\n});",
57
+ },
58
+ {
59
+ type: 'prose',
60
+ text: 'Every field is optional. Declare only the contribution roots your package ships. `createIntegration` is a type-preserving helper for editor autocomplete and type-checking. It lives in `@astryxdesign/core/authoring` and is also re-exported from `@astryxdesign/cli/integration` for back-compat.',
61
+ },
62
+ ],
63
+ },
64
+ {
65
+ title: 'Components',
66
+ category: 'guide',
67
+ content: [
68
+ {
69
+ type: 'prose',
70
+ text: 'Export your components from your library however you like, and consumers still import them from your package. For each component the CLI should document, ship a `.doc.{ts,mjs,js}` file with the same stem, for example `AcmeCarousel.tsx` alongside `AcmeCarousel.doc.ts`.',
71
+ },
72
+ {
73
+ type: 'code',
74
+ lang: 'typescript',
75
+ code: "// AcmeCarousel.doc.ts\nimport {createComponentDoc} from '@astryxdesign/core/authoring';\n\nexport default createComponentDoc({\n name: 'AcmeCarousel',\n description: 'A carousel that cycles through slides.',\n // props, usage, examples, ...\n});",
76
+ },
77
+ ],
78
+ },
79
+ {
80
+ title: 'Templates',
81
+ category: 'guide',
82
+ content: [
83
+ {
84
+ type: 'prose',
85
+ text: 'Templates are usually not exported from the package directly. Instead, consumers browse them through the CLI and materialize them into their app. Define a template with `createPageTemplate` (full pages) or `createBlockTemplate` (smaller chunks) in a `.template.{ts,mjs,js}` file next to the source, for example `AcmeLandingPage.tsx` and `AcmeLandingPage.template.ts`.',
86
+ },
87
+ {
88
+ type: 'code',
89
+ lang: 'typescript',
90
+ code: "// AcmeLandingPage.template.ts\nimport {createPageTemplate} from '@astryxdesign/core/authoring';\n\nexport default createPageTemplate({\n // name, description, preview, ...\n});",
91
+ },
92
+ {
93
+ type: 'prose',
94
+ text: 'The CLI needs the template source at consume time, so make sure it is included in your published package. This is typically done via the `exports` key in `package.json`. It also lets the docsite render template previews in the future.',
95
+ },
96
+ {
97
+ type: 'code',
98
+ lang: 'jsonc',
99
+ code: '{\n "exports": {\n // ...\n "./templates/*.tsx": "./templates/*.tsx"\n }\n}',
100
+ },
101
+ {
102
+ type: 'prose',
103
+ text: 'To verify it resolves, try importing the template component with its `.tsx` extension. An extensionless specifier will not resolve under `moduleResolution: bundler`, and the extensionful export above is what lets this type-check without consumers enabling `allowImportingTsExtensions`.',
104
+ },
105
+ {
106
+ type: 'code',
107
+ lang: 'typescript',
108
+ code: "import('@acme/astryx-widgets/templates/AcmeLandingPage.tsx');",
109
+ },
110
+ ],
111
+ },
112
+ {
113
+ title: 'Codemods',
114
+ category: 'guide',
115
+ content: [
116
+ {
117
+ type: 'prose',
118
+ text: 'Ship codemods so `astryx upgrade` can migrate consumers across breaking changes in your package. Point the integration file\u2019s `codemods` field at your codemods root, and author each one with `createCodemod` (transforms source files) or `createConfigCodemod` (rewrites the consumer\u2019s `astryx.config`).',
119
+ },
120
+ {
121
+ type: 'code',
122
+ lang: 'typescript',
123
+ code: "// codemods/v2-rename-prop.ts\nimport {createCodemod} from '@astryxdesign/cli/codemod';\n\nexport default createCodemod({\n // version, description, transform, ...\n});",
124
+ },
125
+ {
126
+ type: 'prose',
127
+ text: 'The codemod helpers live in `@astryxdesign/cli/codemod`, not `@astryxdesign/core/authoring` like the doc, integration, and template helpers. Consumers can also run their own post-codemod hooks, such as a reinstall or rebuild, via `hooks.postCodemod` in their `astryx.config`.',
128
+ },
129
+ ],
130
+ },
131
+ {
132
+ title: 'How It Works',
133
+ category: 'guide',
134
+ content: [
135
+ {
136
+ type: 'prose',
137
+ text: 'Every CLI command loads the consumer\u2019s `astryx.config`, resolves each listed integration\u2019s manifest from `node_modules`, and discovers its contributions. Everything is validated against one strict schema at the load boundary. The `create*` helpers do not validate. They are identity functions whose value is their TypeScript surface, so validation happens when the CLI loads the file, not when you author it.',
138
+ },
139
+ {
140
+ type: 'prose',
141
+ text: 'Discovery is resilient. A broken or misconfigured integration is skipped with a single non-blocking warning on stderr instead of crashing the CLI, and it never corrupts a `--json` stdout envelope. Everyday commands keep working with the remaining valid contributions.',
142
+ },
143
+ {
144
+ type: 'prose',
145
+ text: 'To inspect problems, run `astryx validate-integration <package>` for a detailed report on one package, or `astryx doctor` for an overall health check of the setup.',
146
+ },
147
+ ],
148
+ },
149
+ ],
150
+ };
@@ -36,10 +36,85 @@ export const docs = {
36
36
  },
37
37
  ],
38
38
  },
39
+ {
40
+ title: 'Choosing a level',
41
+ category: 'foundations',
42
+ content: [
43
+ {
44
+ type: 'prose',
45
+ text: 'Pick the level by how far the surface sits from the page, not by how much shadow you want. Elevation encodes stacking order: a higher level means the surface is layered over more of the UI. Use exactly one level per surface, and only raise a surface above `none` when it actually sits above other content.',
46
+ },
47
+ {
48
+ type: 'table',
49
+ headers: ['Level', 'When to use', 'Examples'],
50
+ rows: [
51
+ [
52
+ 'none',
53
+ 'The component is flat and embedded in the surface — it is part of the page, not layered above it. This is the default for every surface except ChatComposer.',
54
+ 'A Card in a grid, an inline Banner, a standard Button',
55
+ ],
56
+ [
57
+ 'low',
58
+ 'The component is in the normal page flow but should read as distinct from the background. Use for emphasis or to separate the component from the surface behind it — the component still sits on the page, it is not floating over other content.',
59
+ 'A raised Card that needs emphasis, a ChatComposer, a resting Thumbnail',
60
+ ],
61
+ [
62
+ 'med',
63
+ 'The component sits over other content on the same page — it floats above nearby elements but not the whole screen.',
64
+ 'A Popover, a floating Banner, a floating action Button',
65
+ ],
66
+ [
67
+ 'high',
68
+ 'The component is placed over the entire UI — it is the topmost layer and typically has a backdrop or takes focus from everything else.',
69
+ 'A modal Dialog, a full-screen overlay surface',
70
+ ],
71
+ ],
72
+ },
73
+ {
74
+ type: 'prose',
75
+ text: 'If two surfaces overlap, the one on top takes the higher level. If a surface does not overlap anything, it is `none` or `low` — never `med` or `high`.',
76
+ },
77
+ ],
78
+ },
79
+ {
80
+ title: 'The elevation prop',
81
+ category: 'foundations',
82
+ content: [
83
+ {
84
+ type: 'prose',
85
+ text: 'Configurable surfaces expose a single `elevation` prop instead of asking consumers to hand-write a box-shadow. It takes the graded enum `none | low | med | high`, narrowed per component to the steps that surface needs: Card, ClickableCard, SelectableCard, Button, IconButton, ButtonGroup, Thumbnail, and Banner expose the full scale, while ChatComposer exposes only `none | low`. `none` is a flat literal (`box-shadow: none`); the other levels map to the `--shadow-*` tokens above, so a surface stays theme-agnostic.',
86
+ },
87
+ {
88
+ type: 'prose',
89
+ text: 'Prop defaults preserve current appearance: every surface defaults to `none` except ChatComposer, which defaults to `low` to keep its raised look. Set `elevation="none"` to flatten it — the flat composer draws a border with the same rest / hover / focus treatment as a text input. Thumbnail keeps its existing hover-only shadow at the `none` default.',
90
+ },
91
+ {
92
+ type: 'code',
93
+ lang: 'tsx',
94
+ label: 'Raising a surface with the elevation prop',
95
+ code: `// Flat by default — raise only when the surface needs to float.
96
+ <Card elevation="low">Raised card</Card>
97
+
98
+ // A floating action button.
99
+ <IconButton icon={<Icon icon="add" />} label="New" variant="primary" elevation="med" />
100
+
101
+ // Flatten the composer (defaults to 'low').
102
+ <ChatComposer elevation="none" onSubmit={handleSubmit} />`,
103
+ },
104
+ {
105
+ type: 'prose',
106
+ text: 'Always float — no prop: intrinsic overlays (Dialog, Popover, Tooltip, Toast, HoverCard, DropdownMenu, and the components that compose them) bake their elevation in and never expose the prop. Flow content (inputs, Text, layout) is never elevated; input fields use inset rings, which are a separate concept from elevation.',
107
+ },
108
+ ],
109
+ },
39
110
  {
40
111
  title: 'Usage',
41
112
  category: 'foundations',
42
113
  content: [
114
+ {
115
+ type: 'prose',
116
+ text: 'When building a custom surface, read elevation from the token scale rather than a hand-rolled shadow — the same tokens the elevation prop resolves to.',
117
+ },
43
118
  {
44
119
  type: 'code',
45
120
  lang: 'tsx',
@@ -68,7 +143,8 @@ const styles = stylex.create({
68
143
  type: 'list',
69
144
  style: 'do',
70
145
  items: [
71
- 'Match elevation to interaction context: low for tooltips, med for dropdowns, high for dialogs.',
146
+ 'Reach for the `elevation` prop on a configurable surface before writing any custom shadow.',
147
+ 'Choose the level by how far the surface sits from the page: `none` when flat/embedded, `low` when in-flow but distinct, `med` when over page content, `high` when over the whole UI. See "Choosing a level".',
72
148
  'Use inset shadows for input focus/selection states; they compose better than outlines.',
73
149
  ],
74
150
  },
@@ -76,6 +152,8 @@ const styles = stylex.create({
76
152
  type: 'list',
77
153
  style: 'dont',
78
154
  items: [
155
+ 'Hand-write a `box-shadow` in app code — set the `elevation` prop or read `shadowVars` instead.',
156
+ 'Raise a surface that does not sit above other content — a non-overlapping surface is `none` or `low`, never `med` or `high`.',
79
157
  'Stack multiple elevation levels on the same element.',
80
158
  'Use elevation shadows for decorative borders. Use --color-border tokens instead.',
81
159
  ],
@@ -21,7 +21,7 @@ export const docs = {
21
21
  type: 'code',
22
22
  lang: 'text',
23
23
  label: 'Paste this into your AI',
24
- code: 'Install @astryxdesign/core, @astryxdesign/theme-neutral, and @astryxdesign/cli in this project. Run `npx astryx init` to set up agent docs. Read the generated files to learn the conventions.',
24
+ code: 'Install @astryxdesign/core, @astryxdesign/theme-neutral, and @astryxdesign/cli in this project, then run `npx @astryxdesign/cli init` to set up agent docs. Read the generated files to learn the conventions.',
25
25
  },
26
26
  ],
27
27
  },
@@ -40,7 +40,7 @@ export const docs = {
40
40
  },
41
41
  {
42
42
  type: 'prose',
43
- text: 'Then run the init wizard to set up AI agent docs, pick a starter template, and learn about theming.',
43
+ text: "Then run `astryx init` to install the AI agent cheat sheet (AGENTS.md/CLAUDE.md). It's non-interactive — no prompts — so it's safe for AI agents, CI, and scripts. Add `--all` for pointers to the theme and page-building workflows.",
44
44
  },
45
45
  {
46
46
  type: 'code',
@@ -67,11 +67,11 @@ export const docs = {
67
67
  },
68
68
  {
69
69
  type: 'prose',
70
- text: 'Available themes: @astryxdesign/theme-neutral (muted minimal, a good starting point), @astryxdesign/theme-butter, @astryxdesign/theme-chocolate, @astryxdesign/theme-gothic (dark-only), @astryxdesign/theme-matcha, @astryxdesign/theme-stone, and @astryxdesign/theme-y2k. See `npx astryx docs theme` for the full theming guide.',
70
+ text: 'Available themes: @astryxdesign/theme-neutral (muted minimal, a good starting point), @astryxdesign/theme-butter, @astryxdesign/theme-chocolate, @astryxdesign/theme-gothic (dark-only), @astryxdesign/theme-matcha, @astryxdesign/theme-stone, and @astryxdesign/theme-y2k. See `astryx docs theme` for the full theming guide.',
71
71
  },
72
72
  {
73
73
  type: 'prose',
74
- text: 'These stylesheets are cascade-layered: the reset loads in @layer reset and component styles in @layer astryx-base. If your project has existing global CSS, a legacy reset, or Tailwind, declare the layer order explicitly and assign every stylesheet to a layer deliberately: unlayered styles and later layers both override astryx-base regardless of specificity. See the Cascade Layer Safety section in `npx astryx docs migration` before building screens.',
74
+ text: 'These stylesheets are cascade-layered: the reset loads in @layer reset and component styles in @layer astryx-base. If your project has existing global CSS, a legacy reset, or Tailwind, declare the layer order explicitly and assign every stylesheet to a layer deliberately: unlayered styles and later layers both override astryx-base regardless of specificity. See the Cascade Layer Safety section in `astryx docs migration` before building screens.',
75
75
  },
76
76
  ],
77
77
  },
@@ -172,11 +172,11 @@ pnpm dev`,
172
172
  type: 'code',
173
173
  lang: 'bash',
174
174
  label: 'Terminal',
175
- code: `npx astryx component # list all components
176
- npx astryx component Button # props, usage, theming for Button
177
- npx astryx docs # list all doc topics
178
- npx astryx template --list # available page templates
179
- npx astryx docs tokens # spacing, color, radius reference`,
175
+ code: `astryx component # list all components
176
+ astryx component Button # props, usage, theming for Button
177
+ astryx docs # list all doc topics
178
+ astryx template --list # available page templates
179
+ astryx docs tokens # spacing, color, radius reference`,
180
180
  },
181
181
  ],
182
182
  },
@@ -54,18 +54,18 @@ export const docs = {
54
54
  type: 'code',
55
55
  lang: 'bash',
56
56
  label: 'Migration-oriented CLI pass',
57
- code: `npx astryx docs migration
58
- npx astryx docs theme
59
- npx astryx docs styling
60
- npx astryx template --list --type block
61
- npx astryx template AppShellTopNavWithSideNav --skeleton
62
- npx astryx template PopoverSettingsPanel --skeleton
63
- npx astryx component AppShell
64
- npx astryx component SideNav
65
- npx astryx component TopNav
66
- npx astryx component CommandPalette
67
- npx astryx component Button
68
- npx astryx component TextInput`,
57
+ code: `astryx docs migration
58
+ astryx docs theme
59
+ astryx docs styling
60
+ astryx template --list --type block
61
+ astryx template AppShellTopNavWithSideNav --skeleton
62
+ astryx template PopoverSettingsPanel --skeleton
63
+ astryx component AppShell
64
+ astryx component SideNav
65
+ astryx component TopNav
66
+ astryx component CommandPalette
67
+ astryx component Button
68
+ astryx component TextInput`,
69
69
  },
70
70
  {
71
71
  type: 'prose',
@@ -75,8 +75,8 @@ npx astryx component TextInput`,
75
75
  type: 'code',
76
76
  lang: 'bash',
77
77
  label: 'Dense and JSON modes',
78
- code: `npx astryx docs migration --dense
79
- npx astryx component Button --json`,
78
+ code: `astryx docs migration --dense
79
+ astryx component Button --json`,
80
80
  },
81
81
  ],
82
82
  },
@@ -371,10 +371,10 @@ if (getComputedStyle(button).paddingInline === '0px') {
371
371
  code: `We are migrating this existing Tailwind/shadcn app to Astryx incrementally.
372
372
 
373
373
  First run:
374
- - npx astryx docs migration --dense
375
- - npx astryx docs theme --dense
376
- - npx astryx docs styling --dense
377
- - npx astryx template AppShellTopNavWithSideNav --skeleton
374
+ - astryx docs migration --dense
375
+ - astryx docs theme --dense
376
+ - astryx docs styling --dense
377
+ - astryx template AppShellTopNavWithSideNav --skeleton
378
378
 
379
379
  Then migrate one route or shell surface at a time. Keep business logic and routing intact. Replace shadcn/Radix/Tailwind primitives with Astryx components, remove hardcoded colors, verify light and dark mode, and take screenshots before moving to the next surface.`,
380
380
  },
@@ -9,6 +9,6 @@ export const docsDense = {
9
9
  { section: 'Rules', title: 'Rules', content: [{ type: 'list', items: ['use components', 'frame-first layout: shell + region budgets before content (astryx docs layout)', 'dense data = rows (Table, List/Item) not Cards; Card = widgets/galleries/settings groups', 'StyleX or Tailwind for styling', 'semantic tokens only', 'CSS vars for colors', 'controlled form inputs', 'useLinkComponent() for navigation'] }] },
10
10
  { section: 'Styling Approach', title: 'Styling', content: [{ type: 'prose', text: 'xstyle prop for component overrides. StyleX or Tailwind for layout. See astryx docs styling.' }] },
11
11
  { section: 'Anti-Patterns', title: 'Anti-Patterns', content: [{ type: 'list', items: ['no inline styles on raw elements', 'no hardcoded colors — use tokens or Tailwind semantic classes', 'no hardcoded spacing', 'no hardcoded <a> — use useLinkComponent()', 'no Card-wrapped list items — frame first, rows for dense data (astryx docs layout)', 'no decorative Badge — StatusDot/Token for status', 'read docs before inventing props'] }] },
12
- { section: 'Design Tokens', title: 'Tokens', content: [{ type: 'prose', text: 'run npx astryx docs tokens for full reference' }] },
12
+ { section: 'Design Tokens', title: 'Tokens', content: [{ type: 'prose', text: 'run astryx docs tokens for full reference' }] },
13
13
  ],
14
14
  };
@@ -39,10 +39,10 @@ export const docs = {
39
39
  style: 'ordered',
40
40
  items: [
41
41
  'Use components for everything they cover',
42
- 'Layout is frame-first: pick the shell and budget regions before writing content (see \`npx astryx docs layout\`)',
42
+ 'Layout is frame-first: pick the shell and budget regions before writing content (see \`astryx docs layout\`)',
43
43
  'Dense data renders as rows (Table, List/Item), edge-to-edge with dividers; Card is for widgets, galleries, and settings groups',
44
- 'StyleX or Tailwind for custom styling; both are first-class (see \`npx astryx docs styling\`)',
45
- 'Semantic tokens, not hardcoded values (see \`npx astryx docs tokens\`)',
44
+ 'StyleX or Tailwind for custom styling; both are first-class (see \`astryx docs styling\`)',
45
+ 'Semantic tokens, not hardcoded values (see \`astryx docs tokens\`)',
46
46
  'CSS custom properties for colors, not hex values',
47
47
  'Form inputs are controlled (value + onChange)',
48
48
  'Use useLinkComponent() for navigation so consumers can plug in their framework router via LinkProvider',
@@ -60,7 +60,7 @@ export const docs = {
60
60
  },
61
61
  {
62
62
  type: 'prose',
63
- text: 'See \`npx astryx docs styling\` for the complete guide with examples.',
63
+ text: 'See \`astryx docs styling\` for the complete guide with examples.',
64
64
  },
65
65
  ],
66
66
  },
@@ -76,7 +76,7 @@ export const docs = {
76
76
  'Hardcoded colors (#fff). Use var(--color-*) or Tailwind semantic classes (text-primary, bg-surface)',
77
77
  'Hardcoded spacing (16px). Use spacing tokens or Tailwind spacing utilities',
78
78
  'Hardcoded <a> elements. Use useLinkComponent() so consumers can swap in their framework router via LinkProvider',
79
- 'Wrapping every list item or page section in a Card. Decide the frame first; dense data renders as rows (see \`npx astryx docs layout\`)',
79
+ 'Wrapping every list item or page section in a Card. Decide the frame first; dense data renders as rows (see \`astryx docs layout\`)',
80
80
  'Badge as decoration. Reserve Badge for counts and enumerated states; use StatusDot or Token for status',
81
81
  'Inventing props. Read component docs first',
82
82
  ],
@@ -89,7 +89,7 @@ export const docs = {
89
89
  content: [
90
90
  {
91
91
  type: 'prose',
92
- text: 'The design system provides semantic design tokens for spacing, color, radius, shadow, typography, and size. Tokens adapt to the active theme and color mode. Run \`npx astryx docs tokens\` for the full reference with all values.',
92
+ text: 'The design system provides semantic design tokens for spacing, color, radius, shadow, typography, and size. Tokens adapt to the active theme and color mode. Run \`astryx docs tokens\` for the full reference with all values.',
93
93
  },
94
94
  ],
95
95
  },