@xyd-js/opencli 0.0.0-build-b7bd05c-20260701151111

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.
@@ -0,0 +1,68 @@
1
+ import { describe, it, expect } from 'vitest';
2
+
3
+ import { findCommand, generateUsage, generateOptions, generateArguments } from '../index';
4
+ import type { OpencliSpecJson } from '../index';
5
+
6
+ const spec: OpencliSpecJson = {
7
+ opencli: '1.0.0',
8
+ info: { title: 'spice', version: '1.0.0', description: 'Spice CLI' },
9
+ commands: [
10
+ {
11
+ name: 'install',
12
+ aliases: ['i'],
13
+ description: 'Install a package',
14
+ options: [{ name: 'global', aliases: ['g'], description: 'Install globally' }],
15
+ arguments: [{ name: 'package', required: true, description: 'Package name' }],
16
+ commands: [{ name: 'dev', description: 'Install as dev dependency' }],
17
+ },
18
+ ],
19
+ };
20
+
21
+ describe('@xyd-js/opencli core', () => {
22
+ it('findCommand resolves a top-level command', () => {
23
+ const cmd = findCommand(spec, 'install');
24
+ expect(cmd?.name).toBe('install');
25
+ });
26
+
27
+ it('findCommand resolves via alias', () => {
28
+ const cmd = findCommand(spec, 'i');
29
+ expect(cmd?.name).toBe('install');
30
+ });
31
+
32
+ it('findCommand resolves a nested command', () => {
33
+ const cmd = findCommand(spec, 'install dev');
34
+ expect(cmd?.name).toBe('dev');
35
+ });
36
+
37
+ it('findCommand with empty path returns synthetic root', () => {
38
+ const cmd = findCommand(spec, '');
39
+ expect(cmd?.name).toBe('spice');
40
+ expect(cmd?.commands?.[0]?.name).toBe('install');
41
+ });
42
+
43
+ it('generateUsage renders the general form (options + required arg, no subcommand token)', () => {
44
+ const cmd = findCommand(spec, 'install')!;
45
+ expect(generateUsage(spec, cmd, 'spice install')).toBe('spice install [options] <package>');
46
+ });
47
+
48
+ it('generateUsage adds the <command> placeholder for a command group when opted in', () => {
49
+ const cmd = findCommand(spec, 'install')!;
50
+ expect(generateUsage(spec, cmd, 'spice install', { commandPlaceholder: true })).toBe(
51
+ 'spice install <command> [options] <package>',
52
+ );
53
+ });
54
+
55
+ it('generateOptions / generateArguments render tab-indented code style', () => {
56
+ const cmd = findCommand(spec, 'install')!;
57
+ expect(generateOptions(cmd, 'code')).toContain('-g, --global');
58
+ expect(generateArguments(cmd, 'code')).toContain('package');
59
+ });
60
+
61
+ it('x-openapi binding survives on the model (typed extension)', () => {
62
+ const withBinding: OpencliSpecJson = {
63
+ ...spec,
64
+ 'x-openapi': { servers: ['https://api.example.com/v1'] },
65
+ };
66
+ expect(withBinding['x-openapi']?.servers?.[0]).toBe('https://api.example.com/v1');
67
+ });
68
+ });
@@ -0,0 +1,278 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { opencliToReferences } from './converters';
4
+ import type { OpencliSpecJson } from './types';
5
+
6
+ const spec: OpencliSpecJson = {
7
+ opencli: '1.0.0',
8
+ info: { title: 'spice', version: '1.0.0' },
9
+ commands: [
10
+ {
11
+ name: 'install',
12
+ description: 'Install one or more packages.',
13
+ arguments: [{ name: 'packages', required: true, description: 'Packages to install.' }],
14
+ options: [
15
+ { name: 'global', aliases: ['g'], description: 'Install globally.' },
16
+ { name: 'save-dev', description: 'Add to devDependencies.' },
17
+ { name: 'secret', description: 'hidden', hidden: true },
18
+ ],
19
+ commands: [{ name: 'dev', description: 'Install as a dev dependency.' }],
20
+ },
21
+ { name: 'list', description: 'List installed packages.' },
22
+ { name: 'internal', description: 'hidden', hidden: true },
23
+ ],
24
+ };
25
+
26
+ describe('opencliToReferences', () => {
27
+ it('emits one reference per (visible) command, including nested', () => {
28
+ const refs = opencliToReferences(spec);
29
+ const titles = refs.map((r) => r.title).sort();
30
+ // 'install' owns a subcommand → "install <command>"; 'internal' (hidden) excluded
31
+ expect(titles).toEqual(['install <command>', 'install dev', 'list']);
32
+ });
33
+
34
+ it('builds Arguments/Options/Commands definitions with required + alias info', () => {
35
+ const install = opencliToReferences(spec).find((r) => r.title === 'install <command>')!;
36
+ expect(install.canonical).toBe('install');
37
+ expect(install.description).toBe('Install one or more packages.');
38
+
39
+ const byTitle = Object.fromEntries(install.definitions.map((d) => [d.title, d]));
40
+ expect(Object.keys(byTitle).sort()).toEqual(['Arguments', 'Commands', 'Options']);
41
+
42
+ // argument required meta
43
+ const packages = byTitle.Arguments.properties[0];
44
+ expect(packages.name).toBe('packages');
45
+ expect(packages.meta).toEqual([{ name: 'required', value: 'true' }]);
46
+
47
+ // options: kebab name with `--`, alias note, hidden one dropped
48
+ const optNames = byTitle.Options.properties.map((p) => p.name);
49
+ expect(optNames).toEqual(['--global', '--save-dev']); // 'secret' (hidden) excluded
50
+ expect(byTitle.Options.properties[0].description).toContain('alias: -g');
51
+
52
+ // nested subcommand listed
53
+ expect(byTitle.Commands.properties.map((p) => p.name)).toEqual(['dev']);
54
+
55
+ // context.path is the region key the engine round-trips
56
+ expect(install.context.path).toBe('install');
57
+
58
+ // a runnable CLI invocation is emitted as a "CLI Tool" code sample
59
+ const tab = install.examples.groups[0].examples[0].codeblock.tabs[0];
60
+ expect(tab.title).toBe('CLI Tool');
61
+ expect(tab.language).toBe('shell');
62
+ expect(tab.code).toContain('spice install');
63
+ // required positional arg filled with a value, no required options here
64
+ expect(tab.code).toContain("'Example data'");
65
+ });
66
+
67
+ it('fills required options with example/enum values', () => {
68
+ const cliSpec: OpencliSpecJson = {
69
+ opencli: '1.0.0',
70
+ info: { title: 'openai', version: '1.0.0' },
71
+ commands: [
72
+ {
73
+ name: 'transcribe',
74
+ description: 'Create a transcription.',
75
+ options: [
76
+ { name: 'file', required: true, arguments: [{ name: 'path', required: true }] },
77
+ { name: 'model', required: true, arguments: [{ name: 'id', required: true, acceptedValues: ['gpt-4o-transcribe', 'whisper-1'] }] },
78
+ { name: 'verbose', description: 'noise' },
79
+ ],
80
+ },
81
+ ],
82
+ };
83
+ const code = opencliToReferences(cliSpec)[0].examples.groups[0].examples[0].codeblock.tabs[0].code;
84
+ expect(code).toContain('openai transcribe');
85
+ expect(code).toContain("--file 'Example data'");
86
+ expect(code).toContain('--model gpt-4o-transcribe'); // enum value, unquoted (no spaces)
87
+ expect(code).not.toContain('--verbose'); // optional flag omitted
88
+ });
89
+
90
+ it('nests a subcommand under "Commands" > <parent>, keeping leaf commands direct', () => {
91
+ const refs = opencliToReferences(spec);
92
+ const groupOf = (title: string) => refs.find((r) => r.title === title)!.context.group;
93
+ // a leaf top-level command sits directly under "Commands"
94
+ expect(groupOf('list')).toEqual(['Commands']);
95
+ // a command that owns subcommands becomes a nested group named after itself...
96
+ expect(groupOf('install <command>')).toEqual(['Commands', 'install']);
97
+ // ...and its subcommand sits inside that same group
98
+ expect(groupOf('install dev')).toEqual(['Commands', 'install']);
99
+ });
100
+
101
+ it('renders one CLI Tool example per accepted value (an example-level switcher, not codeblock tabs)', () => {
102
+ const cliSpec: OpencliSpecJson = {
103
+ opencli: '1.0.0',
104
+ info: { title: 'xyd', version: '1.0.0' },
105
+ commands: [
106
+ {
107
+ name: 'completion',
108
+ description: 'Generate shell completions.',
109
+ arguments: [{ name: 'shell', required: true, acceptedValues: ['zsh', 'fish'] }],
110
+ },
111
+ ],
112
+ };
113
+ const group = opencliToReferences(cliSpec)[0].examples.groups[0];
114
+ // one example per value (labelled by codeblock.title) so Atlas renders an
115
+ // example switcher — not language tabs inside a single code block
116
+ expect(group.examples.map((e) => e.codeblock.title)).toEqual(['zsh', 'fish']);
117
+ expect(group.examples.map((e) => e.codeblock.tabs[0].code)).toEqual(['xyd completion zsh', 'xyd completion fish']);
118
+ });
119
+
120
+ it('labels the CLI Tool example with a single example value too (e.g. a [diagrams] tab)', () => {
121
+ const cliSpec: OpencliSpecJson = {
122
+ opencli: '1.0.0',
123
+ info: { title: 'xyd', version: '1.0.0' },
124
+ commands: [
125
+ {
126
+ name: 'install',
127
+ description: 'Install a component.',
128
+ arguments: [{ name: 'component', required: true, metadata: [{ name: 'example', value: 'diagrams' }] }],
129
+ },
130
+ ],
131
+ };
132
+ const examples = opencliToReferences(cliSpec)[0].examples.groups[0].examples;
133
+ expect(examples).toHaveLength(1);
134
+ expect(examples[0].codeblock.title).toBe('diagrams'); // rendered as a [diagrams] tab
135
+ expect(examples[0].codeblock.tabs[0].code).toBe('xyd install diagrams');
136
+ });
137
+
138
+ it('marks refs as the CLI category and surfaces an argument example/enum in its meta', () => {
139
+ const cliSpec: OpencliSpecJson = {
140
+ opencli: '1.0.0',
141
+ info: { title: 'xyd', version: '1.0.0' },
142
+ commands: [
143
+ {
144
+ name: 'components',
145
+ description: 'Manage components.',
146
+ commands: [
147
+ {
148
+ name: 'install',
149
+ description: 'Install a component.',
150
+ arguments: [{ name: 'component', required: true, metadata: [{ name: 'example', value: 'diagrams' }] }],
151
+ },
152
+ ],
153
+ },
154
+ {
155
+ name: 'completion',
156
+ description: 'Shell completions.',
157
+ arguments: [{ name: 'shell', required: true, acceptedValues: ['zsh', 'fish'] }],
158
+ },
159
+ ],
160
+ };
161
+ const refs = opencliToReferences(cliSpec);
162
+
163
+ // the parent (command group) reads as "components <command>" and keeps a stable URL
164
+ const parent = refs.find((r) => r.canonical === 'components')!;
165
+ expect(parent.title).toBe('components <command>');
166
+ expect(parent.context.fullPath).toBe('xyd components <command>');
167
+ // its CLI Tool sample shows the subcommand placeholder too (unquoted)
168
+ expect(parent.examples.groups[0].examples[0].codeblock.tabs[0].code).toBe('xyd components <command>');
169
+
170
+ const install = refs.find((r) => r.title === 'components install')!;
171
+ expect(install.category).toBe('cli');
172
+ expect(install.context.fullPath).toBe('xyd components install <component>'); // the usage formula
173
+ // example metadata → an "example" badge in the Arguments section
174
+ expect(install.definitions.find((d) => d.title === 'Arguments')!.properties[0].meta).toEqual([
175
+ { name: 'required', value: 'true' },
176
+ { name: 'example', value: 'diagrams' },
177
+ ]);
178
+ // acceptedValues → an "examples" badge list
179
+ const shell = refs.find((r) => r.title === 'completion')!;
180
+ expect(shell.definitions.find((d) => d.title === 'Arguments')!.properties[0].meta).toEqual([
181
+ { name: 'required', value: 'true' },
182
+ { name: 'examples', value: ['zsh', 'fish'] },
183
+ ]);
184
+ });
185
+
186
+ it('filters by region (command path)', () => {
187
+ const refs = opencliToReferences(spec, { regions: ['install dev'] });
188
+ expect(refs.map((r) => r.title)).toEqual(['install dev']);
189
+ expect(refs[0].context.path).toBe('install dev');
190
+ });
191
+
192
+ it('renders an "Example response" group from the x-openapi response binding', () => {
193
+ const withResponse: OpencliSpecJson = {
194
+ opencli: '1.0.0',
195
+ info: { title: 'openai', version: '1.0.0' },
196
+ commands: [
197
+ {
198
+ name: 'retrieve',
199
+ description: 'Retrieve a thing.',
200
+ 'x-openapi': {
201
+ method: 'get',
202
+ path: '/things/{id}',
203
+ responses: [
204
+ { status: '200', contentType: 'application/json', example: { id: 'thing_123', object: 'thing' } },
205
+ ],
206
+ },
207
+ },
208
+ ],
209
+ };
210
+ const ref = opencliToReferences(withResponse)[0];
211
+
212
+ // the CLI Tool invocation stays at groups[0]
213
+ expect(ref.examples.groups[0].examples[0].codeblock.tabs[0].title).toBe('CLI Tool');
214
+
215
+ // the response sample becomes a second, separate group (status on the
216
+ // codeblock, contentType on the tab — mirrors the OpenAPI response track)
217
+ const responseGroup = ref.examples.groups[1];
218
+ expect(responseGroup.description).toBe('Example response');
219
+ expect(responseGroup.examples[0].codeblock.title).toBe('200');
220
+ const tab = responseGroup.examples[0].codeblock.tabs[0];
221
+ expect(tab.title).toBe('application/json');
222
+ expect(tab.language).toBe('json');
223
+ expect(JSON.parse(tab.code)).toEqual({ id: 'thing_123', object: 'thing' });
224
+ });
225
+
226
+ it('omits the response group when the command has no x-openapi response', () => {
227
+ // the spice fixture carries no x-openapi binding → only the CLI Tool group
228
+ for (const ref of opencliToReferences(spec)) {
229
+ expect(ref.examples.groups).toHaveLength(1);
230
+ expect(ref.examples.groups[0].examples[0].codeblock.tabs[0].title).toBe('CLI Tool');
231
+ }
232
+ });
233
+
234
+ const withGlobals: OpencliSpecJson = {
235
+ opencli: '1.0.0',
236
+ info: { title: 'demo', version: '1.0.0' },
237
+ options: [
238
+ { name: 'help', aliases: ['h'], description: 'Show help', recursive: true },
239
+ { name: 'cwd', description: 'Working dir', arguments: [{ name: 'path' }], recursive: true },
240
+ { name: 'secret', description: 'hidden', recursive: true, hidden: true },
241
+ { name: 'local', description: 'not a global flag' },
242
+ ],
243
+ commands: [{ name: 'build', description: 'Build it' }],
244
+ };
245
+
246
+ it('renders root recursive options on each command when globalOptionsPerCommand is true', () => {
247
+ const ref = opencliToReferences(withGlobals, { globalOptionsPerCommand: true })[0];
248
+ const byTitle = Object.fromEntries(ref.definitions.map((d) => [d.title, d]));
249
+ expect(Object.keys(byTitle)).toContain('Global options');
250
+ // recursive + visible only; the hidden one and the non-recursive one are excluded
251
+ expect(byTitle['Global options'].properties.map((p) => p.name)).toEqual(['--help', '--cwd']);
252
+ // no separate page in this mode
253
+ expect(opencliToReferences(withGlobals, { globalOptionsPerCommand: true }).some((r) => r.title === 'Global options')).toBe(false);
254
+ });
255
+
256
+ it('by default renders global options as a single dedicated page, not per command', () => {
257
+ const refs = opencliToReferences(withGlobals);
258
+ // no per-command "Global options" section
259
+ const build = refs.find((r) => r.title === 'build')!;
260
+ expect(build.definitions.map((d) => d.title)).not.toContain('Global options');
261
+ // a single "Global options" page instead, grouped on its own in the sidebar
262
+ const page = refs.find((r) => r.title === 'Global options')!;
263
+ expect(page).toBeTruthy();
264
+ expect(page.canonical).toBe('global-options');
265
+ expect(page.context.group).toEqual(['Global options']);
266
+ expect(page.definitions[0].properties.map((p) => p.name)).toEqual(['--help', '--cwd']);
267
+ });
268
+
269
+ it('emits no "Global options" section when there are no recursive root options', () => {
270
+ for (const ref of opencliToReferences(spec)) {
271
+ expect(ref.definitions.map((d) => d.title)).not.toContain('Global options');
272
+ }
273
+ });
274
+
275
+ it('returns [] for null spec', () => {
276
+ expect(opencliToReferences(null)).toEqual([]);
277
+ });
278
+ });
@@ -0,0 +1,342 @@
1
+ import { generateUsage } from './generate';
2
+ import type { Argument, Command, OpencliSpecJson, Option } from './types';
3
+
4
+ // Structural subset of @xyd-js/uniform's `Reference` — kept local so the OpenCLI
5
+ // core stays free of the (React-typed) uniform package. The docs engine consumes
6
+ // these as uniform References (api.cli → Atlas).
7
+ interface OpencliRefProperty {
8
+ name: string;
9
+ type: string;
10
+ description: string;
11
+ meta?: { name: string; value: unknown }[];
12
+ }
13
+ interface OpencliRefDefinition {
14
+ title: string;
15
+ properties: OpencliRefProperty[];
16
+ }
17
+ interface OpencliRefCodeTab {
18
+ title: string;
19
+ code: string;
20
+ language: string;
21
+ }
22
+ interface OpencliRefExampleGroup {
23
+ description?: string;
24
+ examples: { description?: string; codeblock: { title?: string; tabs: OpencliRefCodeTab[] } }[];
25
+ }
26
+ export interface OpencliReference {
27
+ title: string;
28
+ canonical: string;
29
+ description: string;
30
+ // Marks these as CLI references so the docs engine / Atlas can render the
31
+ // usage-formula header (mirrors an OpenAPI page's method + path).
32
+ category: 'cli';
33
+ // `group` drives sidebar placement (the docs engine groups references by it);
34
+ // `path` is the region key round-tripped through the page frontmatter.
35
+ context: { path: string; fullPath: string; group: string[] };
36
+ // a runnable CLI invocation, rendered by Atlas as a code sample (like the
37
+ // request/response samples on an OpenAPI page).
38
+ examples: { groups: OpencliRefExampleGroup[] };
39
+ definitions: OpencliRefDefinition[];
40
+ }
41
+
42
+ export interface OpencliToReferencesOptions {
43
+ /**
44
+ * Restrict output to specific commands. Each region is a command path,
45
+ * space-joined from the CLI root (e.g. `"install"`, `"remote add"`).
46
+ */
47
+ regions?: string[];
48
+
49
+ /**
50
+ * Render the CLI's global (root recursive) options on every command page.
51
+ * When false (the default), a single "Global options" reference is emitted
52
+ * instead — so the options appear once in the sidebar rather than repeated
53
+ * on every command.
54
+ */
55
+ globalOptionsPerCommand?: boolean;
56
+ }
57
+
58
+ const GLOBAL_OPTIONS_REGION = 'global-options';
59
+
60
+ /**
61
+ * Convert an OpenCLI document to uniform `Reference[]` — one Reference per
62
+ * command — so the docs engine can render CLI reference pages the same way it
63
+ * renders OpenAPI/GraphQL (`api.cli`). Mirrors `oapSchemaToReferences`.
64
+ */
65
+ export function opencliToReferences(
66
+ spec: OpencliSpecJson | null | undefined,
67
+ options: OpencliToReferencesOptions = {},
68
+ ): OpencliReference[] {
69
+ if (!spec) return [];
70
+
71
+ const cliTitle = spec.info?.title || 'cli';
72
+ const regionSet = options.regions?.length ? new Set(options.regions) : null;
73
+ const perCommand = options.globalOptionsPerCommand ?? false;
74
+ const refs: OpencliReference[] = [];
75
+
76
+ // Root-level recursive options (e.g. a CLI's global flags) apply to every
77
+ // command. By default they get their own page; with `globalOptionsPerCommand`
78
+ // they're rendered as a "Global options" section on each command instead.
79
+ const globalOptions = (spec.options || []).filter((o) => o.recursive && !o.hidden);
80
+
81
+ const walk = (commands: Command[] | undefined, parentPath: string[]) => {
82
+ for (const cmd of commands || []) {
83
+ if (cmd.hidden) continue;
84
+ const cmdPath = [...parentPath, cmd.name];
85
+ const region = cmdPath.join(' ');
86
+ if (!regionSet || regionSet.has(region)) {
87
+ refs.push(commandToReference(spec, cmd, cmdPath, cliTitle, perCommand ? globalOptions : []));
88
+ }
89
+ if (cmd.commands?.length) walk(cmd.commands, cmdPath);
90
+ }
91
+ };
92
+ walk(spec.commands, []);
93
+
94
+ // Default: the global options live on a single dedicated page in the sidebar.
95
+ if (!perCommand && globalOptions.length && (!regionSet || regionSet.has(GLOBAL_OPTIONS_REGION))) {
96
+ refs.push(globalOptionsReference(globalOptions));
97
+ }
98
+
99
+ return refs;
100
+ }
101
+
102
+ function globalOptionsReference(globalOptions: Option[]): OpencliReference {
103
+ return {
104
+ title: 'Global options',
105
+ canonical: GLOBAL_OPTIONS_REGION,
106
+ description: 'Options available on every command.',
107
+ category: 'cli',
108
+ context: { path: GLOBAL_OPTIONS_REGION, fullPath: '', group: ['Global options'] },
109
+ examples: { groups: [] },
110
+ definitions: [{ title: 'Global options', properties: globalOptions.map(optionToProperty) }],
111
+ };
112
+ }
113
+
114
+ function commandToReference(
115
+ spec: OpencliSpecJson,
116
+ cmd: Command,
117
+ cmdPath: string[],
118
+ cliTitle: string,
119
+ globalOptions: Option[] = [],
120
+ ): OpencliReference {
121
+ const region = cmdPath.join(' ');
122
+ const displayPath = `${cliTitle} ${region}`.trim();
123
+
124
+ const definitions: OpencliRefDefinition[] = [];
125
+
126
+ const args = (cmd.arguments || []).filter((a) => !a.hidden);
127
+ if (args.length) {
128
+ definitions.push({ title: 'Arguments', properties: args.map(argumentToProperty) });
129
+ }
130
+
131
+ const opts = (cmd.options || []).filter((o) => !o.hidden);
132
+ if (opts.length) {
133
+ definitions.push({ title: 'Options', properties: opts.map(optionToProperty) });
134
+ }
135
+
136
+ const subs = (cmd.commands || []).filter((c) => !c.hidden);
137
+ if (subs.length) {
138
+ definitions.push({
139
+ title: 'Commands',
140
+ properties: subs.map((s) => ({
141
+ name: s.name,
142
+ type: 'command',
143
+ description: s.description || '',
144
+ })),
145
+ });
146
+ }
147
+
148
+ // Global flags (root-level recursive options) available on every command.
149
+ if (globalOptions.length) {
150
+ definitions.push({ title: 'Global options', properties: globalOptions.map(optionToProperty) });
151
+ }
152
+
153
+ // Sidebar grouping: a leaf top-level command sits directly under "Commands".
154
+ // A command that owns subcommands becomes a nested group (named after itself)
155
+ // under "Commands", and each of its subcommands sits inside that same group —
156
+ // e.g. `components` + `components install` → "Commands" › "components".
157
+ const hasSubcommands = (cmd.commands || []).some((c) => !c.hidden);
158
+ const group =
159
+ cmdPath.length === 1 && !hasSubcommands ? ['Commands'] : ['Commands', ...(hasSubcommands ? cmdPath : cmdPath.slice(0, -1))];
160
+
161
+ // The runnable invocation ("CLI Tool") at groups[0], plus — when the OpenAPI
162
+ // binding carried one — an "Example response" group rendered as a JSON sample.
163
+ // This mirrors an OpenAPI page's request + response code samples; Atlas stacks
164
+ // the groups vertically. Keep the CLI Tool group first.
165
+ const groups: OpencliRefExampleGroup[] = [{ examples: cliToolExamples(cliTitle, cmd, cmdPath) }];
166
+ const responseGroup = responseExampleGroup(cmd);
167
+ if (responseGroup) groups.push(responseGroup);
168
+
169
+ return {
170
+ // A command that owns subcommands reads as "<path> <command>" so it's
171
+ // distinct from its group and shows it takes a subcommand.
172
+ title: (hasSubcommands ? `${region} <command>` : region) || cliTitle,
173
+ canonical: cmdPath.join('/') || cliTitle,
174
+ description: cmd.description || '',
175
+ category: 'cli',
176
+ // context.path is the region key the docs engine writes into the page
177
+ // frontmatter (`cli: <spec>#<path>`) and reads back to re-resolve the command.
178
+ // api.cli shows the more specific form — a command group carries `<command>`.
179
+ context: { path: region, fullPath: generateUsage(spec, cmd, displayPath, { commandPlaceholder: true }), group },
180
+ examples: { groups },
181
+ definitions,
182
+ };
183
+ }
184
+
185
+ /** Map a response content type to a code-sample language. */
186
+ function responseLanguage(contentType: string | undefined): string {
187
+ if (!contentType) return 'json';
188
+ if (/json/i.test(contentType)) return 'json';
189
+ if (/xml/i.test(contentType)) return 'xml';
190
+ return 'text';
191
+ }
192
+
193
+ /**
194
+ * The "Example response" group built from the command's `x-openapi.responses`
195
+ * binding (emitted by openapi2opencli) — one code sample per captured response,
196
+ * rendered alongside the "CLI Tool" invocation like an OpenAPI page's
197
+ * request/response samples. Returns null when no response example is available.
198
+ */
199
+ function responseExampleGroup(cmd: Command): OpencliRefExampleGroup | null {
200
+ const responses = cmd['x-openapi']?.responses;
201
+ if (!responses?.length) return null;
202
+
203
+ const examples: OpencliRefExampleGroup['examples'] = [];
204
+ for (const res of responses) {
205
+ if (res.example == null) continue; // skip missing / null bodies (no useful sample)
206
+ const code = typeof res.example === 'string' ? res.example : JSON.stringify(res.example, null, 2);
207
+ examples.push({
208
+ codeblock: {
209
+ title: res.status || '200',
210
+ tabs: [{ title: res.contentType || 'application/json', language: responseLanguage(res.contentType), code }],
211
+ },
212
+ });
213
+ }
214
+ if (!examples.length) return null;
215
+ return { description: 'Example response', examples };
216
+ }
217
+
218
+ /** Shell-quote a value only when it needs it (spaces / special chars). */
219
+ function shellQuote(value: string): string {
220
+ return /[\s'"$`\\<>|&;()]/.test(value) ? `'${value.replace(/'/g, "'\\''")}'` : value;
221
+ }
222
+
223
+ /** A representative value for an argument: an explicit example, an accepted/enum value, else a placeholder. */
224
+ function exampleValue(arg: Argument | undefined): string {
225
+ const example = arg?.metadata?.find((m) => m.name === 'example')?.value;
226
+ if (typeof example === 'string' && example) return example;
227
+ if (arg?.acceptedValues?.length) return String(arg.acceptedValues[0]);
228
+ return 'Example data';
229
+ }
230
+
231
+ /**
232
+ * The "CLI Tool" example(s) for a command. When a required argument enumerates
233
+ * its accepted values (e.g. `completion <zsh|fish>`), each value becomes its own
234
+ * example (`codeblock.title` = the value) — so Atlas renders an example-level
235
+ * switcher (`[zsh] [fish]`) rather than language tabs inside a single code
236
+ * block. Otherwise a single "CLI Tool" example.
237
+ */
238
+ function cliToolExamples(cliTitle: string, cmd: Command, cmdPath: string[]): OpencliRefExampleGroup['examples'] {
239
+ const reqArgs = (cmd.arguments || []).filter((a) => a.required && !a.hidden);
240
+ // The argument whose representative value(s) label the example(s): an enum
241
+ // (acceptedValues) gives one tab per value; a single example value gives one.
242
+ const variantArg = [...reqArgs].reverse().find((a) => a.acceptedValues?.length || argExampleValue(a) != null);
243
+ const values = variantArg?.acceptedValues?.length
244
+ ? variantArg.acceptedValues.map(String)
245
+ : variantArg
246
+ ? [String(argExampleValue(variantArg))]
247
+ : null;
248
+
249
+ if (variantArg && values) {
250
+ // Each value is its own example (`codeblock.title` = the value) so Atlas
251
+ // renders an example switcher — `[zsh] [fish]`, `[diagrams]`, etc.
252
+ return values.map((value) => ({
253
+ codeblock: {
254
+ title: value,
255
+ tabs: [
256
+ {
257
+ title: 'CLI Tool',
258
+ language: 'shell',
259
+ code: generateCliExample(cliTitle, cmd, cmdPath, { [variantArg.name]: value }),
260
+ },
261
+ ],
262
+ },
263
+ }));
264
+ }
265
+
266
+ return [{ codeblock: { tabs: [{ title: 'CLI Tool', language: 'shell', code: generateCliExample(cliTitle, cmd, cmdPath) }] } }];
267
+ }
268
+
269
+ /** An argument's explicit example value (from metadata), or null. */
270
+ function argExampleValue(arg: Argument): string | null {
271
+ const example = arg.metadata?.find((m) => m.name === 'example')?.value;
272
+ return typeof example === 'string' && example ? example : null;
273
+ }
274
+
275
+ /**
276
+ * A runnable, concrete CLI invocation for a command — the command path plus its
277
+ * required arguments and options filled with example values, one option per line
278
+ * (the shape shown on an OpenAPI page's request sample). `overrides` pins a
279
+ * specific value for a named argument (used to render one tab per accepted value).
280
+ */
281
+ function generateCliExample(
282
+ cliTitle: string,
283
+ cmd: Command,
284
+ cmdPath: string[],
285
+ overrides: Record<string, string> = {},
286
+ ): string {
287
+ let head = [cliTitle, ...cmdPath].join(' ');
288
+
289
+ // A command group takes a subcommand — shown as a placeholder (unquoted, like
290
+ // the usage formula), e.g. `xyd components <command>`.
291
+ if ((cmd.commands || []).some((c) => !c.hidden)) {
292
+ head += ' <command>';
293
+ }
294
+
295
+ for (const arg of (cmd.arguments || []).filter((a) => a.required && !a.hidden)) {
296
+ head += ` ${shellQuote(overrides[arg.name] ?? exampleValue(arg))}`;
297
+ }
298
+
299
+ const lines = [head];
300
+ for (const opt of (cmd.options || []).filter((o) => o.required && !o.hidden)) {
301
+ if (!opt.arguments?.length) {
302
+ lines.push(`--${opt.name}`); // boolean flag
303
+ } else {
304
+ lines.push(`--${opt.name} ${shellQuote(exampleValue(opt.arguments[0]))}`);
305
+ }
306
+ }
307
+
308
+ return lines.join(' \\\n ');
309
+ }
310
+
311
+ function argumentToProperty(arg: Argument): OpencliRefProperty {
312
+ const meta: { name: string; value: unknown }[] = [];
313
+ if (arg.required) meta.push({ name: 'required', value: 'true' });
314
+ // Surface the argument's accepted values / example in the Arguments section
315
+ // (e.g. `component` → diagrams, `shell` → zsh, fish).
316
+ if (arg.acceptedValues?.length) {
317
+ meta.push({ name: 'examples', value: arg.acceptedValues });
318
+ } else {
319
+ const example = arg.metadata?.find((m) => m.name === 'example')?.value;
320
+ if (example != null && example !== '') meta.push({ name: 'example', value: example });
321
+ }
322
+ return {
323
+ name: arg.name,
324
+ type: arg.arity ? 'array' : 'string',
325
+ description: arg.description || '',
326
+ ...(meta.length ? { meta } : {}),
327
+ };
328
+ }
329
+
330
+ function optionToProperty(opt: Option): OpencliRefProperty {
331
+ const aliasNote = opt.aliases?.length
332
+ ? ` (alias: ${opt.aliases.map((a) => (a.length === 1 ? `-${a}` : `--${a}`)).join(', ')})`
333
+ : '';
334
+ const meta = opt.required ? [{ name: 'required', value: 'true' }] : undefined;
335
+ return {
336
+ name: `--${opt.name}`,
337
+ // an option that takes a value vs. a boolean flag
338
+ type: opt.arguments?.length ? 'string' : 'boolean',
339
+ description: `${opt.description || ''}${aliasNote}`,
340
+ ...(meta ? { meta } : {}),
341
+ };
342
+ }