Skip to content

Commit 5a0fc8e

Browse files
committed
Merge branch 'jeppe-cursor/auto-spawn-child-host-900b' into jeppe-cursor/attach-preferred-default-900b
2 parents d6a7885 + 9277a06 commit 5a0fc8e

1 file changed

Lines changed: 64 additions & 156 deletions

File tree

code/core/src/cli/tools/help.ts

Lines changed: 64 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import * as v from 'valibot';
22

33
import {
4-
resolveToolsetDescription,
54
type AnyToolsetDefinition,
65
type AnyToolsetMethod,
76
type ToolsetCtx,
87
} from '../../shared/open-service/toolset-definition.ts';
98
import { parseToolsetMethodId, toCliMethodName } from '../../shared/open-service/toolset-names.ts';
10-
import { toToolsetJsonSchema } from './sdk/json-schema.ts';
9+
import { toCatalogEntry } from './sdk/catalog.ts';
1110
import type { ToolsetCatalog, ToolsetCatalogEntry, ToolsetCatalogMethod } from './sdk/types.ts';
1211
import {
1312
JsonSchemaNodeSchema,
@@ -25,135 +24,43 @@ function optionLines(): string[] {
2524
return TOOLS_OPTION_SPECS.map((spec) => ` ${spec.flags.padEnd(column)}${spec.description}`);
2625
}
2726

28-
/**
29-
* One line per tool for the `Commands:` listing, in commander's shape: the subcommand padded to a
30-
* shared column, a one-sentence summary, and the execution badge.
31-
*/
32-
function commandLines(toolsets: AnyToolsetDefinition[], _ctx: ToolsetCtx): string[] {
33-
const commands = toolsets.flatMap((toolset) =>
34-
Object.entries(toolset.methods).map(([methodKey, method]) => ({
35-
path: cliPath(toolset, methodKey),
36-
summary: method.title,
37-
badge: badge(method),
38-
}))
39-
);
40-
const column = Math.max(...commands.map((command) => command.path.length)) + 2;
41-
return commands.map(
42-
(command) => ` ${command.path.padEnd(column)}${command.summary} ${command.badge}`
43-
);
44-
}
45-
4627
function indented(lines: string[], depth: number): string[] {
4728
const pad = ' '.repeat(depth);
4829
// Descriptions and schema lines carry embedded newlines; every physical line gets the base
4930
// indent or the body's continuation lines would fall back to column 0.
5031
return lines.flatMap((line) => line.split('\n')).map((line) => (line ? pad + line : line));
5132
}
5233

53-
function cliPath(toolset: AnyToolsetDefinition, methodKey: string): string {
54-
return `${toolset.id} ${toCliMethodName(methodKey)}`;
55-
}
56-
57-
/**
58-
* The complete agent discovery surface, in commander's conventional shape — Usage, Options, and a
59-
* `Commands:` listing with one-line summaries — followed by a full reference for every tool
60-
* (description, input schema, declared output schema) so agents learn the surface from this single
61-
* invocation instead of paying a project load per lookup. The Options block is the flags' only
62-
* documentation, since commander's own help is disabled in favor of this runtime-derived one.
63-
*/
64-
export function renderToolsHelp(
65-
configDir: string,
66-
toolsets: AnyToolsetDefinition[],
67-
ctx: ToolsetCtx
68-
): string {
69-
const header = [
70-
'Usage: npx storybook tools [options] [toolset] [tool] [args...]',
71-
'',
72-
`Storybook tools from the Storybook configuration at ${configDir}.`,
73-
'',
74-
'Options:',
75-
...optionLines(),
76-
'',
77-
'Commands:',
78-
...commandLines(toolsets, ctx),
79-
].join('\n');
80-
const notes = [
81-
`${LOCAL_BADGE} tools run in this process, without a running Storybook.`,
82-
`${DEV_SERVER_BADGE} tools need a running Storybook dev server; start it first.`,
83-
'Individual `--key value` flags override entries of `--input`.',
84-
].join('\n');
85-
const referenceIntro =
86-
'Tool reference — every command in full (`npx storybook tools <toolset> <tool> --help` shows one alone):';
87-
const sections = [header, notes, referenceIntro];
88-
for (const toolset of toolsets) {
89-
sections.push(renderToolsetSection(toolset, ctx));
90-
}
91-
return sections.join('\n\n');
34+
function cliPath(method: ToolsetCatalogMethod): string {
35+
const { toolsetId, methodName } = parseToolsetMethodId(method.ref);
36+
return `${toolsetId} ${toCliMethodName(methodName)}`;
9237
}
9338

94-
/** The focused view of one toolset (`storybook tools <toolset>`). */
95-
export function renderToolsetHelp(toolset: AnyToolsetDefinition, ctx: ToolsetCtx): string {
96-
return [
97-
`Usage: npx storybook tools ${toolset.id} <tool> [--key value ...]`,
98-
'',
99-
renderToolsetSection(toolset, ctx),
100-
].join('\n');
39+
function badge(method: ToolsetCatalogMethod): string {
40+
return method.requiresDevServer ? DEV_SERVER_BADGE : LOCAL_BADGE;
10141
}
10242

103-
/** One toolset's section of the reference dump. */
104-
function renderToolsetSection(toolset: AnyToolsetDefinition, ctx: ToolsetCtx): string {
105-
const sections = [`${toolset.id}${toolset.description}`];
106-
for (const [methodKey, method] of Object.entries(toolset.methods)) {
107-
const heading = ` ${cliPath(toolset, methodKey)} ${badge(method)}`;
108-
sections.push([heading, '', ...indented(methodBodyLines(method, ctx), 4)].join('\n'));
43+
function argumentLines(schema: Record<string, unknown> | undefined, flagPrefix: boolean) {
44+
if (schema === undefined) {
45+
return undefined;
10946
}
110-
return sections.join('\n\n');
111-
}
112-
113-
/** The focused view of one tool (`storybook tools <toolset> <tool> --help`). */
114-
export function renderMethodHelp(
115-
toolset: AnyToolsetDefinition,
116-
methodKey: string,
117-
method: AnyToolsetMethod,
118-
ctx: ToolsetCtx
119-
): string {
120-
const lines = [
121-
`Usage: npx storybook tools ${cliPath(toolset, methodKey)} [--key value ...]`,
122-
'',
123-
method.requiresDevServer
124-
? 'Execution: requires a running Storybook dev server; start it first.'
125-
: 'Execution: local (no running Storybook required).',
126-
'',
127-
...methodBodyLines(method, ctx),
128-
];
129-
return lines.join('\n');
130-
}
131-
132-
function badge(method: AnyToolsetMethod): string {
133-
return method.requiresDevServer ? DEV_SERVER_BADGE : LOCAL_BADGE;
47+
return propertyLines(schema, { flagPrefix });
13448
}
13549

136-
function methodBodyLines(method: AnyToolsetMethod, ctx: ToolsetCtx): string[] {
137-
const lines = [resolveToolsetDescription(method.description, ctx).trim()];
138-
139-
const inputSchema = toToolsetJsonSchema(method.input);
140-
const argumentLines = inputSchema ? propertyLines(inputSchema, { flagPrefix: true }) : undefined;
141-
if (argumentLines === undefined) {
50+
function methodBodyLines(method: ToolsetCatalogMethod): string[] {
51+
const lines = [method.description.trim()];
52+
const inputLines = argumentLines(method.input, true);
53+
if (inputLines === undefined) {
14254
lines.push('', 'Arguments: (this schema could not be rendered)');
143-
} else if (argumentLines.length === 0) {
55+
} else if (inputLines.length === 0) {
14456
lines.push('', 'Arguments: none.');
14557
} else {
146-
lines.push('', 'Arguments:', ...argumentLines);
58+
lines.push('', 'Arguments:', ...inputLines);
14759
}
148-
149-
if (method.output) {
150-
const outputSchema = toToolsetJsonSchema(method.output);
151-
const outputLines = outputSchema ? propertyLines(outputSchema, { flagPrefix: false }) : [];
152-
if (outputLines.length > 0) {
153-
lines.push('', 'Output:', ...outputLines);
154-
}
60+
const outputLines = argumentLines(method.output, false);
61+
if (outputLines && outputLines.length > 0) {
62+
lines.push('', 'Output:', ...outputLines);
15563
}
156-
15764
return lines;
15865
}
15966

@@ -177,45 +84,21 @@ function propertyLines(
17784
return lines;
17885
}
17986

180-
function catalogCliPath(method: ToolsetCatalogMethod): string {
181-
const { toolsetId, methodName } = parseToolsetMethodId(method.ref);
182-
return `${toolsetId} ${toCliMethodName(methodName)}`;
183-
}
184-
185-
function catalogBadge(method: ToolsetCatalogMethod): string {
186-
return method.requiresDevServer ? DEV_SERVER_BADGE : LOCAL_BADGE;
187-
}
188-
189-
function catalogArgumentLines(schema: Record<string, unknown> | undefined, flagPrefix: boolean) {
190-
if (schema === undefined) {
191-
return undefined;
192-
}
193-
return propertyLines(schema, { flagPrefix });
194-
}
195-
196-
function catalogMethodBodyLines(method: ToolsetCatalogMethod): string[] {
197-
const lines = [method.description.trim()];
198-
const argumentLines = catalogArgumentLines(method.input, true);
199-
if (argumentLines === undefined) {
200-
lines.push('', 'Arguments: (this schema could not be rendered)');
201-
} else if (argumentLines.length === 0) {
202-
lines.push('', 'Arguments: none.');
203-
} else {
204-
lines.push('', 'Arguments:', ...argumentLines);
205-
}
206-
const outputLines = catalogArgumentLines(method.output, false);
207-
if (outputLines && outputLines.length > 0) {
208-
lines.push('', 'Output:', ...outputLines);
87+
function renderToolsetSection(entry: ToolsetCatalogEntry): string {
88+
const sections = [`${entry.id}${entry.description}`];
89+
for (const method of entry.methods) {
90+
const heading = ` ${cliPath(method)} ${badge(method)}`;
91+
sections.push([heading, '', ...indented(methodBodyLines(method), 4)].join('\n'));
20992
}
210-
return lines;
93+
return sections.join('\n\n');
21194
}
21295

21396
export function renderToolsHelpFromCatalog(catalog: ToolsetCatalog): string {
21497
const commands = catalog.toolsets.flatMap((toolset) =>
21598
toolset.methods.map((method) => ({
216-
path: catalogCliPath(method),
99+
path: cliPath(method),
217100
summary: method.title,
218-
badge: catalogBadge(method),
101+
badge: badge(method),
219102
}))
220103
);
221104
const column =
@@ -246,7 +129,7 @@ export function renderToolsHelpFromCatalog(catalog: ToolsetCatalog): string {
246129
'Tool reference — every command in full (`npx storybook tools <toolset> <tool> --help` shows one alone):';
247130
const sections = [header, notes, referenceIntro];
248131
for (const toolset of catalog.toolsets) {
249-
sections.push(renderCatalogToolsetSection(toolset));
132+
sections.push(renderToolsetSection(toolset));
250133
}
251134
return sections.join('\n\n');
252135
}
@@ -255,30 +138,55 @@ export function renderToolsetHelpFromCatalog(entry: ToolsetCatalogEntry): string
255138
return [
256139
`Usage: npx storybook tools ${entry.id} <tool> [--key value ...]`,
257140
'',
258-
renderCatalogToolsetSection(entry),
141+
renderToolsetSection(entry),
259142
].join('\n');
260143
}
261144

262145
export function renderMethodHelpFromCatalog(
263-
entry: ToolsetCatalogEntry,
146+
_entry: ToolsetCatalogEntry,
264147
method: ToolsetCatalogMethod
265148
): string {
266149
return [
267-
`Usage: npx storybook tools ${catalogCliPath(method)} [--key value ...]`,
150+
`Usage: npx storybook tools ${cliPath(method)} [--key value ...]`,
268151
'',
269152
method.requiresDevServer
270153
? 'Execution: requires a running Storybook dev server; start it first.'
271154
: 'Execution: local (no running Storybook required).',
272155
'',
273-
...catalogMethodBodyLines(method),
156+
...methodBodyLines(method),
274157
].join('\n');
275158
}
276159

277-
function renderCatalogToolsetSection(entry: ToolsetCatalogEntry): string {
278-
const sections = [`${entry.id}${entry.description}`];
279-
for (const method of entry.methods) {
280-
const heading = ` ${catalogCliPath(method)} ${catalogBadge(method)}`;
281-
sections.push([heading, '', ...indented(catalogMethodBodyLines(method), 4)].join('\n'));
282-
}
283-
return sections.join('\n\n');
160+
/**
161+
* The complete agent discovery surface, in commander's conventional shape — Usage, Options, and a
162+
* `Commands:` listing with one-line summaries — followed by a full reference for every tool
163+
* (description, input schema, declared output schema) so agents learn the surface from this single
164+
* invocation instead of paying a project load per lookup. The Options block is the flags' only
165+
* documentation, since commander's own help is disabled in favor of this runtime-derived one.
166+
*/
167+
export function renderToolsHelp(
168+
configDir: string,
169+
toolsets: AnyToolsetDefinition[],
170+
ctx: ToolsetCtx
171+
): string {
172+
return renderToolsHelpFromCatalog({
173+
configDir,
174+
toolsets: toolsets.map((toolset) => toCatalogEntry(toolset, ctx)),
175+
});
176+
}
177+
178+
/** The focused view of one toolset (`storybook tools <toolset>`). */
179+
export function renderToolsetHelp(toolset: AnyToolsetDefinition, ctx: ToolsetCtx): string {
180+
return renderToolsetHelpFromCatalog(toCatalogEntry(toolset, ctx));
181+
}
182+
183+
/** The focused view of one tool (`storybook tools <toolset> <tool> --help`). */
184+
export function renderMethodHelp(
185+
toolset: AnyToolsetDefinition,
186+
methodKey: string,
187+
method: AnyToolsetMethod,
188+
ctx: ToolsetCtx
189+
): string {
190+
const entry = toCatalogEntry({ ...toolset, methods: { [methodKey]: method } }, ctx);
191+
return renderMethodHelpFromCatalog(entry, entry.methods[0] as ToolsetCatalogMethod);
284192
}

0 commit comments

Comments
 (0)