Authoring Reference
Every file an integration author writes, field by field: the integration manifest, astryx.config, codemods, identity, and each doc type.Astryx Integration
The astryx.integration.* manifest that sits beside an integration package's package.json. It can preserve a stable provider identity across a package rename, and points the CLI at the package's components, templates, codemods, doc topics, source themes, managed agent guidance, and issue tracker. Every field is optional.
Applies to: astryx.integration.{ts,mjs,js}
| Field | Type | Required | Description |
|---|---|---|---|
| providerId | string | no | Stable logical provider ID. Omit to use package.json#name; set it to the prior package name only when an explicit rename must preserve artifact IDs. If two packages claim the same ID, the package being authored is used, otherwise the first-loaded one, and the CLI warns about the other. Example: '@acme/widgets'. |
| components | string | no | Relative path to the components/docs root (resolved to absolute). Example: './src/components'. |
| templates | string | no | Relative path to the templates root (resolved to absolute). Example: './src/templates'. |
| codemods | string | no | Relative path to the codemods root (resolved to absolute). Example: './codemods'. |
| docs | string | no | Relative path to the reference-docs (topics) root (resolved to absolute). Every {topic}.doc.{ts,mjs,js} under it is served by astryx docs beside the built-in topics; a topic may also declare replaces or extends to take the place of a built-in one or merge onto it. Example: './docs'. |
| themes | string | no | Relative path to a source-theme catalog root containing manifest.json plus one directory per theme slug. Installed themes appear in astryx theme list and can be copied with astryx theme add. Example: './themes'. |
| agentDocs | { append?: readonly string[] } | no | Static package guidance appended to the end of the managed agent block. The CLI owns the section heading, package labels, bullets, target files, and writes. Example: { append: ['Run acme verify.'] }. |
| issuesUrl | string | no | Where to file issues/feedback for this integration. Example: 'https://github.com/acme/widgets/issues'. |
jsexport default {components: './src/components',templates: './src/templates',codemods: './codemods',docs: './docs',themes: './themes',agentDocs: {append: ['Run acme verify before finishing.'],},issuesUrl: 'https://github.com/acme/widgets/issues',};
Provider identity defaults to package.json#name. During an explicit package rename, set providerId to the prior canonical package name so existing artifact IDs remain stable. Package version always comes from package.json.
agentDocs.append may contain at most eight lines. Each line is a trimmed, non-blank string of at most 240 Unicode code points with no line separators, control characters, NUL, or Astryx/XDS managed-marker text. The configured project may contain at most 32 integration lines total.
A themes root is forward-compatible but version-gated: a CLI released before this field ignores it with a warning and continues loading every contribution kind it understands. That older CLI cannot list or add the contributed themes.
Validate the manifest with astryx doctor integration validate. At the load boundary, a known field of the wrong type is an error, issuesUrl must be a valid URL, and unknown fields become warnings so an older CLI can still load the fields it understands. Before publishing, also run templates, components, and docs under the same doctor integration group. Those leaves compare authored identities with Core and explain whether an overlap is intentional or needs a rename.
Astryx Config
The optional astryx.config.* file at your project root. Declares which integrations to load, where to route issue links, post-codemod hooks, local debug-log and gap-report handlers, and experimental layout components. All optional; {} is valid.
Applies to: astryx.config.{ts,mjs,js}
| Field | Type | Required | Description |
|---|---|---|---|
| integrations | string[] | no | Package names of Astryx integrations to load alongside core. Example: ['@acme/astryx-widgets']. |
| issuesUrl | string | no | URL that "report an issue" affordances link to. |
| hooks | { postCodemod?: PostCodemodHook[] } | no | Lifecycle hooks. |
| hooks.postCodemod | PostCodemodHook[] | no | Commands run after an upgrade applies codemods (e.g. re-run your formatter). Each hook returns a command to execute, or null to skip. |
| debug | (event: DebugEvent) => void | no | Record every command run. The handler is synchronous; promises are not awaited and output goes to stderr. Declare debug directly in this file so early commands can discover it. An integration can supply one too, as a debug named export from its manifest — both run; set {"astryx": {"inheritDebug": false}} in package.json to take only your own. Example: event => appendFileSync('runs.ndjson', JSON.stringify(event) + '\n'). |
| gapReport | GapReportHandler | no | Handle explicit gap reports in addition to every loaded integration handler. The project handler runs first. Public handlers require caller consent; internal handlers always run. Example: { audience: 'internal', async handle(report, {signal}) { return sendGap(report, {signal}); } }. |
| experimental | { xle?: { components?: Record<string, XleComponent> } } | no | Unstable features; may change without a breaking bump. |
| experimental.xle.components | Record<string, XleComponent> | no | Custom components the layout expander (XLE) may emit, keyed by tag. |
jsexport default {integrations: ['@acme/astryx-widgets'],};
jsexport default {debug: event => appendFileSync("runs.ndjson", JSON.stringify(event) + "\n"),};
The config is validated at load with a strict schema: unknown keys are errors, so a typo fails fast rather than being silently ignored.
Astryx Codemod
A codemod module the CLI runs during astryx upgrade. Default-export a plain object with a type discriminant: 'code' rewrites source files, 'config' rewrites the astryx.config.* file. There is no factory to call.
Applies to: the codemods/ dir of an integration
| Field | Type | Required | Description |
|---|---|---|---|
| title | string | yes | Short, human-readable title shown in upgrade output. Example: 'Rename Button prop kind to variant'. |
| description | string | no | Optional longer description. |
| isOptional | boolean | no | When true, the codemod runs only when explicitly requested. Default: false. |
| fileExtensions | string[] | no | File extensions this codemod applies to. Code codemods only; a config codemod always targets astryx.config.*. Example: ['.tsx', '.ts']. |
| transform | (file: AstryxCodemodFile, api: AstryxCodemodApi) => string | null | undefined | yes | The transform. Return the new source to rewrite the file, or null/undefined to leave it unchanged. |
| file | AstryxCodemodFile | no | The source file presented to the transform. |
| file.path | string | no | Absolute path to the file being transformed. |
| file.source | string | no | The current source contents of the file. |
| api | AstryxCodemodApi | no | Helpers and context passed as the second argument. |
| api.jscodeshift | unknown | no | A jscodeshift instance configured with a parser for the file. |
| api.stats | (...args: unknown[]) => void | no | Report a statistic (no-op-friendly; provided for jscodeshift parity). |
| api.report | (...args: unknown[]) => void | no | Report progress (no-op-friendly; provided for jscodeshift parity). |
| type | 'code' | yes | Discriminant for the file-transforming variant. Use 'config' for a codemod that rewrites astryx.config.* instead (see notes). Example: 'code'. |
jsexport default {type: 'code',title: 'Rename Button prop kind to variant',fileExtensions: ['.tsx', '.ts'],transform(file, api) {const j = api.jscodeshift;const root = j(file.source);// ...rewrite the AST...return root.toSource();},};
jsexport default {type: 'config',title: 'Move issuesUrl into astryx.config',transform(file, api) {// Rewrite the astryx.config.* source; return null to skip.return null;},};
The config-codemod variant (type: 'config') carries the same fields as a code codemod except fileExtensions: it always targets the astryx.config.* file rather than a set of source files.
Authors write a plain object and default-export it with the type discriminant; there is no factory. The CLI validates it at the load boundary via parseCodemod with a strict schema, and isOptional defaults to false.
Provider and artifact identity
Separates stable provider/artifact identity from package instances and runtime lifecycle state.
Applies to: Compiler inputs, manifests, search, Build, and Doctor
| Field | Type | Required | Description |
|---|---|---|---|
| ProviderId | string | yes | Canonical logical provider ID. It defaults to the lowercase npm package name; Core uses the same model without publishing an integration manifest. |
| ArtifactId | string | yes | Versioned serialization of provider ID, contribution kind, and stable artifact name. Every segment uses RFC 3986 escaping. |
| DocId | ArtifactId | yes | Artifact ID restricted to one authored doc kind. Navigation changes do not change it. |
| ProviderInstance | { id; providerId; packageName; packageVersion; sourceDigest } | yes | One immutable package version and source/content digest. Installed, configured, loaded, selected, and healthy state is a separate runtime overlay. |
| AuthoredDocEntry | { id; provider; kind; stableName; source; authored } | yes | Normalized compiler input. Discovery supplies stableName independently from the authored display name, source paths remain package-relative, and provider provenance plus the authored snapshot are immutable. |
Provider package renames require an explicit mapping: a ProviderInstance may retain its stable providerId while packageName changes. Silent identity changes are not inferred.
Optional placement, compatibility aliases, and audience fields shared by every authored documentation kind.
Applies to: Every supported .doc.mjs object
| Field | Type | Required | Description |
|---|---|---|---|
| placement | DocPlacement | no | Requests one canonical parent. The compiler fails invalid explicit placement instead of silently using Unorganized. |
| placement.parent | string | yes | Stable reference to the requested parent namespace. |
| placement.slot | string | no | Named slot owned by the parent namespace. |
| placement.order | number | no | Integer sibling order within the slot. |
| aliases | string[] | no | Prior names or routes retained for compatibility. Aliases do not create another identity. |
| audience | 'public' | 'internal' | no | Bundle audience. Omit for public documentation. Default: 'public'. |
ComponentDoc
The doc-type for a component directory's {Name}.doc.mjs. A discriminated union of SingleComponentDoc (props on the doc), MultiComponentDoc (a components array), and SubComponentDoc (a subComponentOf pointer). All three share the ComponentBaseDoc fields below; the variant is chosen by which of props / components / subComponentOf you set.
Applies to: {Name}.doc.mjs
| Field | Type | Required | Description |
|---|---|---|---|
| type | 'component' | no | Doc-kind discriminant for the stamped default-export format. Optional: legacy export const docs = {...} docs omit it and the parser falls back to shape-sniffing. |
| name | string | yes | Stable machine identity and directory name without the Astryx prefix, PascalCase. e.g. 'Button', 'TextInput', 'AppShell'. Change displayName, not name, to edit the visible label; registry URLs derive from this identity by default. |
| displayName | string | yes | Human-readable display name with spaces between words ('AppShell' → 'App Shell'). Drives the docsite gallery and sidebar label. |
| registry | RegistryDocIdentity | no | Optional public registry identity. The converter derives a stable kebab-case slug from name; set slug only to override it, and keep prior relative paths in aliases after a published rename. |
| import | string | no | Exact public package specifier consumers use to import an integration-owned component. The packed-package gate resolves this specifier and verifies it exports the component name. |
| keywords | string[] | no | Search keywords for CLI discovery: synonyms and related UI concepts from other design systems (MUI, Chakra, Radix, and others). Lowercase. Used by astryx component <term> fuzzy matching. |
| hiddenComponents | string[] | no | Sub-component names to hide from human-facing UI (CLI listings, docs catalogs). They stay public and importable; agents and tooling can still discover them via source. |
| hidden | boolean | no | Hide this entire component from human-facing UI. It stays public and importable. Use for shared primitives (NavIcon, NavMenu) that only make sense inside their parent. |
| group | string | no | Optional sidebar/docs group. Clusters related components; ungrouped components appear flat in alphabetical order. |
| category | 'Action' | 'Chat' | 'Container' | 'Content' | 'Form Controls' | 'Data Input' | 'Data Visualization' | 'Feedback & Status' | 'Layout' | 'Navigation' | 'Overlay' | 'Table & List' | 'Utility' | no | Overview-gallery category representing the component's functional role. Independent of group (which is for the sidebar). Data Input is a deprecated compatibility alias for Form Controls. |
| isHiddenFromOverview | boolean | no | Exclude from the categorized overview page while keeping the component in the sidebar and CLI. Use for sub-components or internal primitives. |
| theming | { container?: boolean; targets: ComponentThemingTarget[]; vars?: ComponentThemingVar[]; derived?: ComponentThemingDerivedVar[] } | no | Theming configuration: the stable selector surface (xds-* classes + data-attribute reflections) that themes target via @scope selectors in defineTheme. |
| theming.container | boolean | no | When true, container padding props are mapped to container tokens by the theme pipeline instead of emitting raw CSS. |
| theming.targets | ComponentThemingTarget[] | yes | Selector targets rendered by this component. Each entry corresponds to a themeProps() call in the source. |
| theming.vars | ComponentThemingVar[] | no | CSS custom properties exposed for theming. |
| theming.derived | ComponentThemingDerivedVar[] | no | Maps standard CSS properties to internal vars for theme-pipeline expansion. Ordered by priority: earlier entries emit first. |
| usage | UsageDoc | yes | Component usage documentation: concise summary, best practices, component-specific accessibility requirements, and optional visual anatomy. (Optional on SubComponentDoc, where the sub-component description is used instead.) |
| usage.description | string | yes | What the component is and when to use it, in 2-3 short sentences. |
| usage.bestPractices | ComponentBestPractice[] | no | 3-4 do/don't design-guidance items ({guidance: boolean, description: string}). Never start the description with 'Do' or 'Don't'. |
| usage.accessibility | ComponentAccessibilityRequirement[] | no | Component-specific requirements rendered in the shared Accessibility tab. Write at about a grade-7 reading level with short sentences, common words, and active voice. For color contrast, put the ratio in requirement; name the exact foreground, background, state, and any overlay in description; explain exceptions in plain language; and give a human or agent enough detail to reproduce the check. Keep repository audit procedures in the wiki rubric. |
| usage.accessibilityThemeCoverage | ComponentAccessibilityThemeCoverage[] | no | Verified per-theme accessibility measurements rendered in the shared Accessibility tab. Record light and dark mode separately, include rendered color pairs, and mark failed measurements. Put visuals excluded from the audit in notMeasured with a short reason; do not add them as table measurements. Each theme declares applicability for measured values, informed by the component contract and never inferred from the ratio: Conditional is required only in some contexts, Supplemental adds another meaningful cue, and Decorative has no required meaning. These values do not change row status. Provide a complete breakdown when one cell summarizes multiple combinations, and protect derived values with an automated audit. |
| usage.anatomy | ComponentAnatomyElement[] | no | Structural/visual parts in reading order ({name, required, description}). |
| examples | ComponentExampleDoc[] | no | Short code examples ({label?, code}) rendered by the CLI after the props table. |
| playground | ComponentPlaygroundConfig | no | Interactive-preview config: initial prop defaults, overlay for modal-only components, appShellMobile for components gated on AppShell mobile context, and a wrapper for context-dependent sub-components. |
| props | ComponentPropDoc[] | no | SingleComponentDoc variant (required there): all public props for the one primary component. Each prop is {name, type, description, default?, required?, slotElements?}. Skip styling props like xstyle/className/style. Also present on SubComponentDoc. |
| components | (ComponentEntry | ComponentRef)[] | no | MultiComponentDoc variant (required there): one entry per public component/hook exported from the directory. Each entry is a full ComponentEntry (inline: name, displayName, description, props | params+returns) or a name-only ComponentRef pointing at a sibling {Name}.doc.mjs. |
| subComponentOf | string | no | SubComponentDoc variant (required there): the parent component's name (e.g. 'Chat'). Marks this file as a sub-component doc that inherits family fields (group, category, keywords, theming, playground) from the parent. |
| description | string | no | SubComponentDoc variant (required there): one-sentence description of the sub-component's role within the parent composition. Single/Multi docs have no top-level description; they derive their summary from usage. |
js/** @type {import('@astryxdesign/cli/authoring').ComponentDoc} */export const docs = {name: 'Switch',displayName: 'Switch',category: 'Form Controls',keywords: ['toggle', 'switch', 'on off'],usage: {description:'A Switch toggles a single setting on or off. Use it for instant, binary preferences that apply immediately without a submit step.',bestPractices: [{guidance: true, description: 'Apply the change immediately when toggled.'},{guidance: false, description: 'Use a Switch for actions that need confirmation; prefer a Checkbox in a form.'},],},props: [{name: 'isSelected', type: 'boolean', description: 'Whether the switch is on.', required: true},{name: 'onChange', type: '(isSelected: boolean) => void', description: 'Called when the user toggles the switch.'},{name: 'isDisabled', type: 'boolean', description: 'Prevents interaction and dims the control.', default: 'false'},],};
js/** @type {import('@astryxdesign/cli/authoring').ComponentDoc} */export const docs = {name: 'Table',displayName: 'Table',category: 'Table & List',usage: {description: 'Displays rows and columns of data. Compose the sub-components to build headers, rows, and cells.'},components: [{name: 'Table', displayName: 'Table', description: 'The table container.', props: []},{name: 'TableRow', displayName: 'Table Row', description: 'A row within the table body.', props: [{name: 'isSelected', type: 'boolean', description: 'Highlights the row as selected.'},]},{name: 'useTableSelection', displayName: 'useTableSelection', description: 'Manages row selection state.',params: [{name: 'rows', type: 'T[]', description: 'The rows to track.', required: true}],returns: [{name: 'selectedIds', type: 'Set<string>', description: 'Currently selected row ids.'}]},],};
ComponentDoc is a discriminated union of three shapes that all extend ComponentBaseDoc. Pick the variant by which key you set: props (single), components (multi), or subComponentOf (sub).
- SingleComponentDoc: one primary component; put props directly on the doc via
props. Use forSwitch,Badge,Spinner,TextInput. - MultiComponentDoc: a directory exporting several components/hooks; list them in
components(inline ComponentEntry or name-only ComponentRef). Use forTable,Dialog,TabList. - SubComponentDoc: a single sub-component in its own {Name}.doc.mjs inside the parent directory; set
subComponentOfto the parent name. It inherits family fields and may omitusage.
js/** @type {import('@astryxdesign/cli/authoring').ComponentDoc} */export const docs = {name: 'ChatComposer',displayName: 'Chat Composer',subComponentOf: 'Chat',description: 'The message input row within a Chat, with an editor and send affordance.',props: [{name: 'onSend', type: '(text: string) => void', description: 'Called when the user submits a message.', required: true},],};
The stamped format is export default { type: 'component', ... }; legacy docs use export const docs = {...} and omit type (the parser shape-sniffs). A hook that is part of a component API is documented as a ComponentEntry in a MultiComponentDoc components array (with params/returns), not as a standalone HookDoc.
HookDoc
The doc-type for a standalone React hook (e.g. useMediaQuery, useFocusTrap, useOverflow) that gets its own .doc.mjs. Hooks that are part of a component API (e.g. useImperativeDialog) belong in that component's MultiComponentDoc components array instead. HookDoc is the hook-flavored view of the shared type: 'function' kind.
Applies to: {useX}.doc.mjs
| Field | Type | Required | Description |
|---|---|---|---|
| type | 'function' | no | Doc-kind discriminant (shared with FunctionDoc). Legacy export const docs = {...} docs omit it. |
| name | string | yes | Stable hook name exactly as exported, e.g. 'useMediaQuery', 'useFocusTrap'. Change displayName, not name, to edit the visible label; registry URLs derive from this identity by default. |
| displayName | string | yes | Human-readable display name. Hooks read better as the raw identifier ('useMediaQuery') than spaced, so keep the identifier verbatim. |
| registry | RegistryDocIdentity | no | Optional public registry identity. The converter derives a stable kebab-case slug from name; set slug only to override it, and keep prior relative paths in aliases after a published rename. |
| group | string | no | Optional group for sidebar/docs organization; same as ComponentDoc.group. |
| keywords | string[] | no | Search keywords for CLI discovery. |
| params | HookParamDoc[] | yes | Hook parameters or options-object fields. |
| params[].name | string | yes | Parameter or option field name. |
| params[].type | string | yes | TypeScript type signature as a string. |
| params[].description | string | yes | What this parameter does. 1-2 sentences. |
| params[].default | string | no | Default value as a string, if optional with a default. |
| params[].required | boolean | no | True if required. Omit if optional. |
| returns | HookReturnDoc[] | yes | Return value documentation. For object returns, list each field; for primitive returns, use a single entry. |
| returns[].name | string | yes | Field name on the returned object, or 'value' for primitive returns. |
| returns[].type | string | yes | TypeScript type. |
| returns[].description | string | yes | What this return value provides. |
| usage | UsageDoc | yes | Usage documentation: description and best practices. |
| usage.description | string | yes | What the hook is and when to use it, in 2-3 short sentences. |
| usage.bestPractices | ComponentBestPractice[] | no | 3-4 do/don't design-guidance items ({guidance: boolean, description: string}). |
| usage.accessibility | ComponentAccessibilityRequirement[] | no | Accessibility requirements specific to using the hook ({name, description}). |
| usage.anatomy | ComponentAnatomyElement[] | no | Structural/visual anatomy elements (rarely used for hooks). |
| relatedComponents | string[] | no | Component names this hook is commonly used with. Enables cross-referencing (e.g. astryx hook useToast links back to Toast). |
| relatedHooks | string[] | no | Other hook names this hook is commonly used with. |
| importPath | string | no | Import path, e.g. '@astryxdesign/core/hooks' or '@astryxdesign/core/Toast'. |
| category | string | no | Category for grouping in listings. |
js/** @type {import('@astryxdesign/cli/authoring').HookDoc} */export const docs = {type: 'function',name: 'useMediaQuery',displayName: 'useMediaQuery',importPath: '@astryxdesign/core/hooks',params: [{name: 'query', type: 'string', description: 'A CSS media query, e.g. "(min-width: 768px)".', required: true},],returns: [{name: 'matches', type: 'boolean', description: 'Whether the query currently matches.'},],usage: {description:'Subscribes to a CSS media query and re-renders when it changes. Use for responsive behavior that CSS alone cannot express, such as swapping components by breakpoint.',},relatedHooks: ['useIsMobile'],};
A hook's discriminant is type: 'function': HookDoc and FunctionDoc share the generalized function kind. HookDoc is the hook-flavored view: named returns fields and a required usage block.
Only standalone hooks get their own file. A hook that is part of a component API is documented as an entry in that component's MultiComponentDoc components array (with params/returns), so it renders under the component.
FunctionDoc
The generalized type: 'function' doc-type covering both React hooks and CLI/API functions. A hook (HookDoc) is the hook-flavored view of this same kind; FunctionDoc adds the fields an API function needs (a {type, data} return envelope, thrown error codes, the wrapping CLI command). The CLI binding itself lives in a separate CommandDoc.
Applies to: api/<name>/<name>.doc.mjs
| Field | Type | Required | Description |
|---|---|---|---|
| type | 'function' | no | Doc-kind discriminant (shared with hooks). |
| name | string | yes | Export name, e.g. 'search' | 'useMediaQuery'. |
| displayName | string | yes | Human-readable display name, e.g. 'search()'. |
| kind | 'hook' | 'api' | no | Which flavor; drives docsite sectioning; inferred from importPath if omitted. |
| summary | string | no | One-line summary. |
| description | string | no | Longer description. |
| namespace | string | no | Docs namespace path. Defaults (e.g. 'cli/api') applied by the docs index. |
| aliases | string[] | no | Alternate slugs that also resolve to this doc. |
| keywords | string[] | no | Search keywords for discovery. |
| importPath | string | no | Import path, e.g. '@astryxdesign/cli/api' | '@astryxdesign/core/hooks'. |
| signature | string | no | Full signature as a string, e.g. 'search(query, options?): Promise<SearchResponse>'. |
| params | HookParamDoc[] | yes | Parameters / options-object fields. |
| params[].name | string | yes | Parameter or option field name. |
| params[].type | string | yes | TypeScript type signature as a string. |
| params[].description | string | yes | What this parameter does. 1-2 sentences. |
| params[].default | string | no | Default value as a string, if optional with a default. |
| params[].required | boolean | no | True if required. Omit if optional. |
| returns | FunctionReturnDoc[] | yes | Return documentation. Hooks list named return fields (name set); API functions list {type, data} envelope entries where type is the response discriminant and name is omitted. |
| returns[].name | string | no | Field name (hooks); omit for API envelope entries. |
| returns[].type | string | yes | TS type (hooks) or response-type discriminant (API), as a string. |
| returns[].description | string | yes | What this return value / envelope entry provides. |
| throws | FunctionThrowsDoc[] | no | Errors the function throws (API functions), keyed to ERROR_CODES. |
| throws[].code | string | yes | The ERROR_CODES member thrown. |
| throws[].when | string | yes | The condition under which it is thrown. |
| examples | FunctionExampleDoc[] | no | Usage examples. |
| examples[].label | string | no | Optional heading shown above the snippet. |
| examples[].code | string | yes | Language-level usage, e.g. "await search('button')". |
| examples[].result | string | no | Optional sample result. |
| usage | UsageDoc | no | Usage documentation (hooks): description, best practices, accessibility requirements, and anatomy. Same shape as HookDoc.usage. |
| command | string | no | The CLI command that wraps this function, e.g. 'search'. |
| related | string[] | no | Related function/command names. |
| relatedComponents | string[] | no | Component names this is commonly used with (hooks). |
| relatedHooks | string[] | no | Other hook names this is commonly used with (hooks). |
| category | string | no | Category for grouping in listings. |
js/** @type {import('@astryxdesign/cli/authoring').FunctionDoc} */export const doc = {type: 'function',kind: 'api',name: 'search',displayName: 'search()',namespace: 'cli/api',importPath: '@astryxdesign/cli/api',summary: 'Find components, hooks, docs, and templates by term.',signature: 'search(query, options?): Promise<SearchResponse>',params: [{name: 'query', type: 'string', description: 'The search term.', required: true},{name: 'options.type', type: "'component' | 'hook' | 'doc' | 'template'", description: 'Restrict results to one domain.'},],returns: [{type: 'search', description: 'Envelope with the query and ranked results[].'},],throws: [{code: 'ERR_INVALID_ARGUMENT', when: 'query is empty.'},],examples: [{label: 'Basic', code: "await search('button')", result: "{ type: 'search', data: { results: [...] } }"},],command: 'search',};
The type discriminant is 'function' for both flavors. Set kind: 'hook' or kind: 'api' to drive docsite sectioning; it is inferred from importPath when omitted.
The two flavors differ in returns: a hook lists named fields (name set), while an API function lists {type, data} envelope entries whose type is the response discriminant and whose name is omitted. throws (keyed to ERROR_CODES) applies to API functions.
The function does not know it has a CLI. The terminal binding (args, flags, exit codes) lives in a separate CommandDoc that references this function via fn; here you only note the wrapping command name via command.
CommandDoc
The doc-type for a CLI command: the terminal binding of an operation. A command is not its own behavior; it is a FunctionDoc exposed on the CLI, referenced via fn, carrying only CLI-surface facts (args, flags, subcommands, examples, exit codes). A defineCommand converter turns it into Commander config + --help.
Applies to: clients/cli/commands/<name>.doc.mjs
| Field | Type | Required | Description |
|---|---|---|---|
| type | 'command' | no | Doc-kind discriminant. Marks the file as a command doc. |
| name | string | yes | Command path, e.g. 'search' | 'theme build'. |
| displayName | string | yes | Human-readable display name, e.g. 'astryx search'. |
| summary | string | yes | One-line summary → Commander .description() + the docs listing. |
| description | string | no | Longer help body / when-to-use. |
| namespace | string | no | Docs namespace path. Defaults to 'cli' when applied by the docs index. |
| aliases | string[] | no | Alternate slugs that also resolve to this doc. |
| fn | string | no | Name of the FunctionDoc (and @astryxdesign/cli/api export) this command wraps. Example: 'search'. |
| args | CommandArgDoc[] | no | Positional arguments. |
| args[].name | string | yes | Argument name. |
| args[].param | string | no | FunctionDoc param this arg binds to (inherits its description). |
| args[].description | string | no | Override description (else inherited from the referenced param). |
| args[].required | boolean | no | Whether the positional argument must be supplied. |
| args[].variadic | boolean | no | Whether the argument collects a variable number of values. |
| options | CommandOptionDoc[] | no | Flags/options. |
| options[].flag | string | yes | Commander flag spec, e.g. '-l, --limit <n>' | '--json'. |
| options[].param | string | no | FunctionDoc param this flag maps to (inherits its description). |
| options[].description | string | no | Override/explicit description (required when cliOnly). |
| options[].choices | string[] | no | Allowed values for the flag. |
| options[].default | string | no | Default value as a string. |
| options[].cliOnly | boolean | no | True for CLI-only flags with no function param (e.g. --json). |
| subcommands | string[] | no | Subcommand names (for command groups like theme / layout). |
| examples | CommandExampleDoc[] | no | Terminal examples. |
| examples[].label | string | no | Optional heading shown above the invocation. |
| examples[].cli | string | yes | A full terminal invocation, e.g. 'astryx search button --json'. |
| examples[].output | string | no | Optional sample output. |
| exitCodes | { code: number; when: string }[] | no | Documented exit codes. |
| exitCodes[].code | number | yes | The process exit code. |
| exitCodes[].when | string | yes | The condition that produces this exit code. |
| related | string[] | no | Related command names. |
| notes | ReferenceContentBlock[] | no | Freeform prose/notes. |
js/** @type {import('@astryxdesign/cli/authoring').CommandDoc} */export const doc = {type: 'command',name: 'search',displayName: 'astryx search',summary: 'Find components, hooks, docs, and templates.',namespace: 'cli',fn: 'search',args: [{name: 'query', param: 'query', required: true}],options: [{flag: '--type <domain>', param: 'options.type', choices: ['component', 'hook', 'doc', 'template']},{flag: '--json', cliOnly: true, description: 'Emit the typed JSON envelope.'},],examples: [{label: 'Terminal', cli: 'astryx search button --json'}],exitCodes: [{code: 1, when: 'The --type value is not a known domain.'}],related: ['discover'],};
A command carries only CLI-surface facts. Behavior, parameters, returns, and thrown errors live in the FunctionDoc it points at via fn; the function does not know it has a CLI.
Bind an arg/option to a function param via param so it inherits that param's description. Use cliOnly: true for flags with no function param (e.g. --json); those must supply their own description.
EnumDoc
The doc-type for a closed vocabulary: a fixed set of literal values such as error codes or response-type discriminants. Colocated as a .doc.mjs next to the source of truth it documents.
Applies to: <enum>.doc.mjs
| Field | Type | Required | Description |
|---|---|---|---|
| type | 'enum' | no | Doc-kind discriminant. Marks the file as an enum doc. |
| name | string | yes | URL-safe identifier, used as the docs slug within its namespace. Example: 'error-codes'. |
| displayName | string | yes | Human-readable title. Example: 'Error Codes'. |
| description | string | yes | One-line summary shown in listings. |
| namespace | string | no | Docs namespace path. Defaults to 'cli' when applied by the docs index. |
| aliases | string[] | no | Alternate slugs that also resolve to this doc. |
| members | EnumMemberDoc[] | yes | The enumerated members: one entry per literal value. |
| members[].value | string | yes | The literal value, e.g. 'ERR_UNKNOWN_TOPIC' | 'component.list'. |
| members[].description | string | yes | What the value means / when it occurs. |
| members[].deprecated | string | no | Deprecation reason, if deprecated. |
js/** @type {import('@astryxdesign/cli/authoring').EnumDoc} */export const doc = {type: 'enum',name: 'error-codes',displayName: 'Error Codes',namespace: 'cli',description: 'Stable error codes thrown by the CLI/API and surfaced in the JSON envelope.',members: [{value: 'ERR_UNKNOWN_TOPIC', description: 'The requested docs topic does not exist.'},{value: 'ERR_INVALID_ARGUMENT', description: 'A required argument was missing or malformed.'},{value: 'ERR_LEGACY', description: 'Old alias.', deprecated: 'Use ERR_INVALID_ARGUMENT.'},],};
An enum doc is the human-readable mirror of a closed vocabulary defined elsewhere in source (e.g. an ERROR_CODES map or a response-type union). Keep the members in sync with that source of truth.
Mark a value with deprecated (a migration hint) rather than deleting it, so old codes stay documented while readers are pointed at the replacement.
NamespaceDoc
Declares named navigation slots and renderer-neutral layout blocks for already-discovered docs. It never scans folders or copies child documents.
Applies to: <namespace>.doc.mjs
| Field | Type | Required | Description |
|---|---|---|---|
| type | 'namespace' | yes | Doc-kind discriminant. |
| name | string | yes | Stable provider-local identity. Moving the namespace does not change this value. |
| title | string | yes | Human-readable page title. |
| summary | string | yes | One-line summary used in listings and search results. |
| placement | DocPlacement | no | Optional canonical parent request: {parent, slot?, order?}. Invalid explicit placement fails compilation instead of falling back. |
| aliases | string[] | no | Prior names or routes that must keep resolving to this doc. |
| audience | 'public' | 'internal' | no | Bundle audience. Defaults to 'public'. Default: 'public'. |
| keywords | string[] | no | Search terms not already present in the title or summary. |
| slots | Record<string, NamespaceSlot> | yes | Named placement and collection targets. Each slot declares a title and accepted doc kinds; configured providers require an explicit extension slot. |
| adopts | NamespaceAdoptionRule[] | no | Provider-local rules that adopt otherwise-unplaced docs from a logical discovery group. They never scan a folder. |
| blocks | ReferenceContentBlock[] | no | Ordered layout content. V1 adds only workflow, collection, and reference to the existing prose, heading, code, table, list, and token-ref blocks. |
js/** @type {import('@astryxdesign/cli/authoring').NamespaceDoc} */export const docs = {type: 'namespace',name: 'cli',title: 'Astryx CLI',summary: 'Commands, APIs, and integration authoring.',slots: {guides: {title: 'Guides', accepts: {kinds: ['namespace', 'generic']}},reference: {title: 'Reference',accepts: {kinds: ['namespace', 'command']},},},adopts: [{source: {group: 'cli-commands', kinds: ['command']},into: 'reference',}],blocks: [{type: 'collection', source: {slot: 'guides'}, presentation: 'cards'},{type: 'collection', source: {slot: 'reference'}, presentation: 'compact'},],};
Child docs request one canonical home with placement. Collections store and render stable references to those docs; they never create a second identity or parent.
| Guidance | Practices |
|---|---|
| Don't | Use a directory as implicit navigation. |
| Don't | Put JSX, HTML, ANSI, callbacks, or custom renderer code in a doc. |
| Don't | Use choice, callout, or checklist blocks before the full block-extension contract exists. |
ReferenceDoc
The doc-type for a reference/topic doc: tokens, principles, theming, patterns, accessibility, migration guides. Unlike ComponentDoc it is not tied to a component: drop a .doc.mjs in the docs directory and it shows up in astryx docs. Content is built from ordered sections of mixed content blocks.
Applies to: assets/docs/<topic>.doc.mjs
| Field | Type | Required | Description |
|---|---|---|---|
| type | 'generic' | no | Doc-kind discriminant. Stays 'generic' (the reference/topic discriminant). Legacy export const docs = {...} docs omit it. |
| name | string | yes | URL-safe identifier, used as the CLI topic name. e.g. 'tokens', 'principles'. |
| title | string | yes | Human-readable title. e.g. 'All Tokens'. (Reference docs use title, not displayName.) |
| description | string | yes | One-line summary shown in topic listings. |
| category | string | no | Navigation category: 'guide' or 'foundations'. |
| replaces | string | no | Name of an existing topic this doc takes the place of. Authored by an integration that serves its own guide instead of the built-in one: on a doc of the same name it swaps the content, and on a doc of another name it also leaves the old name as an alias so astryx docs <old> still resolves. Exclusive with extends. Example: 'getting-started'. |
| extends | string | no | Name of an existing topic this doc merges onto, section by section: a section whose title matches one in the base replaces it, a section the base does not have is appended. For correcting or adding to a topic rather than owning it. Exclusive with replaces. Example: 'theme'. |
| sections | ReferenceSection[] | yes | Ordered sections that make up the doc. Each becomes an h2 in full output and can be retrieved via astryx docs <topic> <section>. |
| sections[].id | string | no | Stable section anchor. New docs should set this instead of relying on a mutable title. |
| sections[].title | string | yes | Section title, e.g. "Spacing Tokens", "Light/Dark Mode". |
| sections[].category | string | no | Navigation category ('guide' | 'foundations'). Mirrors the parent doc's category so sections can be grouped independently. |
| sections[].content | ReferenceContentBlock[] | yes | Ordered content blocks. Existing prose, heading, code, table, list, and token-ref blocks remain; V1 adds workflow, collection, and reference. |
| sections[].previewType | ReferenceTokenPreviewType | no | Preview type for token tables in this section. When set, the docsite renders a visual preview column from the token's computed value. Omit for non-token sections. |
| tokenCategory | string | no | Token category for foundational docs that map to a token section (e.g. 'color'). Lets the tokens overview link to this doc for detailed guidance. |
js/** @type {import('@astryxdesign/cli/authoring').ReferenceDoc} */export const docs = {type: 'generic',name: 'spacing',title: 'Spacing',description: 'Spacing tokens for gap, margin, and padding.',category: 'foundations',tokenCategory: 'spacing',sections: [{title: 'Spacing Tokens',content: [{type: 'prose', text: 'Use spacing tokens instead of raw pixel values so layouts stay on the 4px grid.'},{type: 'table', headers: ['Token', 'Value'], rows: [['--spacing-4', '16px']]},],previewType: 'spacing-bar',},],};
Each sections[].content is an ordered array of ReferenceContentBlock, a discriminated union. V1 adds only workflow, collection, and reference; choice, callout, and checklist remain invalid. The same union is reused by the notes field on SchemaDoc and CommandDoc.
tstype ReferenceContentBlock =| { type: 'prose'; text: string }| { type: 'heading'; level: 3 | 4 | 5 | 6; text: string }| { type: 'code'; lang: string; code: string; label?: string }| { type: 'table'; headers: string[]; rows: string[][] }| { type: 'list'; style: 'ordered' | 'unordered' | 'do' | 'dont'; items: string[] }| { type: 'token-ref'; topic: string; section: string }| { type: 'workflow'; title?: string; steps: WorkflowStep[] }| { type: 'collection'; source: {slot: string}; presentation?: 'list' | 'cards' | 'compact'; whenEmpty?: 'show' | 'omit' }| { type: 'reference'; target: string; projection?: {fields?: string[]; sections?: string[]} };
A token-ref block (e.g. {type: 'token-ref', topic: 'tokens', section: 'Color Tokens'}) references a token table in another topic; the CLI resolves and inlines it at read time so the docsite can render live theme values.
A section may set previewType to render a visual preview column for token tables: one of 'swatch' | 'shadow-box' | 'radius-box' | 'spacing-bar' | 'size-bar' | 'border-line' | 'duration-bar' | 'easing-curve' | 'font-sample'.
SchemaDoc
The doc-type for documenting an authored/received OBJECT shape (astryx.config, a codemod payload, the doc-types themselves). Colocated as a .doc.mjs next to the schema it describes. Fields nest recursively, so a whole shape is one tree.
Applies to: <schema>.doc.mjs
| Field | Type | Required | Description |
|---|---|---|---|
| type | 'schema' | no | Doc-kind discriminant. Marks the file as a schema doc. |
| name | string | yes | URL-safe identifier, used as the docs slug within its namespace. Example: 'config'. |
| displayName | string | yes | Human-readable title. Example: 'Astryx Config'. |
| description | string | yes | One-line summary shown in listings. |
| namespace | string | no | Docs namespace path (e.g. 'cli' | 'authoring'). Defaults are applied by the docs index; set explicitly to place the schema. |
| aliases | string[] | no | Alternate slugs that also resolve to this doc (back-compat). |
| appliesTo | string | no | What this schema applies to, e.g. 'astryx.config.{ts,mjs,js}' | 'AstryxConfig'. |
| fields | SchemaFieldDoc[] | yes | The fields that make up the shape. Object-typed fields nest their members recursively via each field's own fields, so the whole shape is one tree. |
| fields[].name | string | yes | Field name, or a dotted path for a nested field (e.g. 'hooks.postCodemod'). |
| fields[].type | string | yes | TypeScript type signature as a string, e.g. 'string[]' | "'a' | 'b'". |
| fields[].description | string | yes | What the field is for, in 1-2 sentences. |
| fields[].required | boolean | no | True if the field must be provided. Omit (don't set false) if optional. |
| fields[].default | string | no | Default value as a string, if any. |
| fields[].example | string | no | A short inline example value. |
| fields[].deprecated | string | no | Deprecation reason, if the field is deprecated. |
| fields[].fields | SchemaFieldDoc[] | no | Nested object fields, for object-typed fields. Recursive. |
| examples | { label?: string; code: string }[] | no | Full example objects/snippets. |
| examples[].label | string | no | Optional heading shown above the snippet. |
| examples[].code | string | yes | The example source. |
| notes | ReferenceContentBlock[] | no | Freeform prose/notes rendered after the field table. Same block union as ReferenceDoc (prose, heading, code, table, list, token-ref). |
js/** @type {import('@astryxdesign/cli/authoring').SchemaDoc} */export const doc = {type: 'schema',name: 'integration',displayName: 'Astryx Integration',namespace: 'cli',description: 'The astryx.integration.* manifest that registers the components a package provides.',appliesTo: 'astryx.integration.{ts,mjs,js}',fields: [{name: 'name', type: 'string', description: 'Package name.', required: true},{name: 'components',type: '{ dir: string }',description: 'Where component sources live.',fields: [{name: 'components.dir', type: 'string', description: 'Glob root for XDS*.tsx files.', required: true},],},],};
SchemaFieldDoc is recursive: an object-typed field lists its members in its own fields, so an entire nested shape (including paths like hooks.postCodemod or experimental.xle.components) is documented as one tree.
Name nested fields with a dotted path from the root (e.g. hooks.postCodemod) so readers can see where each field sits in the shape.
| Guidance | Practices |
|---|---|
| Do | Set |
| Do | Keep |
| Don't | Set |
TemplateDoc
The doc-type for template metadata. A discriminated union of PageTemplateDoc (type: 'page') for full page templates and BlockTemplateDoc (type: 'block') for editable compositions. A block can stand alone or use exampleFor to attach to one component; isShowcase requires that component ownership.
Applies to: <Name>.template.mjs
| Field | Type | Required | Description |
|---|---|---|---|
| type | 'page' | 'block' | yes | Discriminant selecting the variant: 'page' for a full page template, 'block' for an editable composition that may be standalone or component-owned. |
| name | string | yes | Stable identifier for block templates; change displayName, not name, to edit their visible label. For page templates it is a human-readable label, while the existing template-directory/CLI slug owns the default registry path. |
| displayName | string | yes | Human-readable label for the gallery/CLI. Spaces out block names that mirror a PascalCase component ('ChatMessageMetadata' → 'Chat Message Metadata'). |
| registry | RegistryDocIdentity | no | Optional public registry identity. The converter derives a stable kebab-case slug from name (or the existing page-template slug); set slug only to override it, and keep prior relative paths in aliases after a published rename. |
| description | string | no | One-sentence description of what the template provides. |
| isReady | boolean | no | Whether the template is ready for use. false shows as '(WIP)' in the gallery and CLI. |
| scaffold | boolean | no | Scaffolding-only template (e.g. blank page): available via the CLI but hidden from browsable galleries. |
| category | TemplateCategory | no | Functional gallery category following a 'Group - Variant' convention (e.g. 'Dashboard - Analytics', 'Table - Basic', 'Form - Wizard'). The overview groups by the text before ' - '. |
| isHiddenFromOverview | boolean | no | Opt out of the Templates overview gallery while staying available via the CLI. Use for duplicate/experimental variants. Scaffold templates are hidden automatically. |
| exampleFor | string | no | Block templates only: optional component ownership. Set this when the block is specifically an example of one component. Omit it for a standalone composition. |
| alsoExampleFor | string[] | no | Block templates only: additional component/hook doc pages whose Examples section should include this block. |
| alsoShowcaseFor | string[] | no | Block templates only: additional doc pages whose hero showcase should reuse this block (secondary placements; does not change the primary showcase). |
| aspectRatio | number | no | Block templates only (required): width-to-height ratio for preview containers (e.g. 16/9, 1, 3/4). |
| scale | number | no | Block templates only: scale factor for the block preview. Default: 1. |
| componentsUsed | string[] | no | Block templates only: component names this block uses, for 'See also'/'Used in' cross-references (not primary attribution). |
| isShowcase | boolean | no | Block templates only: when true this block is the canonical hero showcase for its exampleFor component. Requires exampleFor. |
js/** @type {import('@astryxdesign/cli/authoring').TemplateDoc} */export const doc = {type: 'page',name: 'Dashboard',displayName: 'Dashboard',description: 'An analytics dashboard with KPI cards and charts.',category: 'Dashboard - Analytics',isReady: true,};
js/** @type {import('@astryxdesign/cli/authoring').TemplateDoc} */export const doc = {type: 'block',name: 'ButtonGroupExample',displayName: 'Button Group Example',description: 'A row of related buttons showing primary and secondary actions.',exampleFor: 'Button',aspectRatio: 16 / 9,componentsUsed: ['Button', 'HStack'],isShowcase: true,};
TemplateDoc is a discriminated union keyed by type. Set type: 'page' for a full page template. Set type: 'block' for an editable composition; add exampleFor only when one component owns the example.
- PageTemplateDoc (type: 'page'): a full page template;
namedoubles as its display value. - BlockTemplateDoc (type: 'block'): an editable composition.
exampleForis optional component ownership; omit it for a standalone block.isShowcaserequiresexampleFor.
category uses the shared TemplateCategory taxonomy: 'Group - Variant' strings (e.g. 'Table - Bulk Actions'). Not every value maps to an existing template; unused values are reserved so authors get autocomplete for the full taxonomy.