11import * as v from 'valibot' ;
22
33import {
4- resolveToolsetDescription ,
54 type AnyToolsetDefinition ,
65 type AnyToolsetMethod ,
76 type ToolsetCtx ,
87} from '../../shared/open-service/toolset-definition.ts' ;
98import { parseToolsetMethodId , toCliMethodName } from '../../shared/open-service/toolset-names.ts' ;
10- import { toToolsetJsonSchema } from './sdk/json-schema .ts' ;
9+ import { toCatalogEntry } from './sdk/catalog .ts' ;
1110import type { ToolsetCatalog , ToolsetCatalogEntry , ToolsetCatalogMethod } from './sdk/types.ts' ;
1211import {
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-
4627function 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
21396export 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
262145export 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