@@ -6,6 +6,34 @@ import { formatCommandExample } from './serialization.js';
66
77export type StructuredHelpFormat = 'yaml' | 'json' ;
88
9+ const COMMON_OPTIONS = [
10+ {
11+ flags : '-f, --format <fmt>' ,
12+ name : 'format' ,
13+ help : 'Output format: table, plain, json, yaml, md, csv' ,
14+ default : 'table' ,
15+ choices : [ 'table' , 'plain' , 'json' , 'yaml' , 'md' , 'csv' ] ,
16+ } ,
17+ {
18+ flags : '--trace <mode>' ,
19+ name : 'trace' ,
20+ help : 'Trace capture: off, on, retain-on-failure' ,
21+ default : 'off' ,
22+ choices : [ 'off' , 'on' , 'retain-on-failure' ] ,
23+ } ,
24+ {
25+ flags : '-v, --verbose' ,
26+ name : 'verbose' ,
27+ help : 'Debug output' ,
28+ default : false ,
29+ } ,
30+ {
31+ flags : '-h, --help' ,
32+ name : 'help' ,
33+ help : 'display help for command' ,
34+ } ,
35+ ] as const ;
36+
937function normalizeStructuredHelpFormat ( value : string | undefined ) : StructuredHelpFormat | undefined {
1038 const normalized = value ?. toLowerCase ( ) ;
1139 if ( normalized === 'yaml' || normalized === 'yml' ) return 'yaml' ;
@@ -102,7 +130,7 @@ export function formatRootAdapterHelpText(groups: RootAdapterGroups): string {
102130 lines . push ( ...formatGroupSection ( 'App adapters' , groups . apps ) ) ;
103131 lines . push ( ...formatGroupSection ( 'Site adapters' , groups . sites ) ) ;
104132 lines . push ( "Run 'opencli list' for full command details, or 'opencli <site> --help' to inspect one site." ) ;
105- lines . push ( "Agent tip: use 'opencli <site> --help -f yaml' for structured commands, args, access, and examples ." ) ;
133+ lines . push ( "Agent tip: use 'opencli <site> --help -f yaml' for all command args/options in one structured response ." ) ;
106134 lines . push ( '' ) ;
107135 return lines . join ( '\n' ) ;
108136}
@@ -120,18 +148,62 @@ function compactArg(arg: Arg): Record<string, unknown> {
120148 } ;
121149}
122150
123- function compactCommand ( cmd : CliCommand , opts : { includeColumns ?: boolean } = { } ) : Record < string , unknown > {
151+ function compactCommonOption ( option : typeof COMMON_OPTIONS [ number ] ) : Record < string , unknown > {
152+ return {
153+ name : option . name ,
154+ flags : option . flags ,
155+ help : option . help ,
156+ ...( 'default' in option ? { default : option . default } : { } ) ,
157+ ...( 'choices' in option ? { choices : option . choices } : { } ) ,
158+ } ;
159+ }
160+
161+ function positionals ( cmd : CliCommand ) : Arg [ ] {
162+ return cmd . args . filter ( arg => arg . positional ) ;
163+ }
164+
165+ function commandOptions ( cmd : CliCommand ) : Arg [ ] {
166+ return cmd . args . filter ( arg => ! arg . positional ) ;
167+ }
168+
169+ function formatPositionals ( args : readonly Arg [ ] ) : string {
170+ return args
171+ . map ( arg => arg . required ? `<${ arg . name } >` : `[${ arg . name } ]` )
172+ . join ( ' ' ) ;
173+ }
174+
175+ function formatCommandOptionTerm ( arg : Arg ) : string {
176+ if ( arg . required || arg . valueRequired ) return `--${ arg . name } <value>` ;
177+ return `--${ arg . name } [value]` ;
178+ }
179+
180+ export function formatCommandListTerm ( cmd : CliCommand ) : string {
181+ const positionalText = formatPositionals ( positionals ( cmd ) ) ;
182+ const optionText = commandOptions ( cmd ) . length > 0 ? ' [options]' : '' ;
183+ return `${ cmd . name } ${ positionalText ? ` ${ positionalText } ` : '' } ${ optionText } ` ;
184+ }
185+
186+ function formatUsage ( cmd : CliCommand ) : string {
187+ const positionalText = formatPositionals ( positionals ( cmd ) ) ;
188+ return `opencli ${ cmd . site } ${ cmd . name } ${ positionalText ? ` ${ positionalText } ` : '' } [options]` ;
189+ }
190+
191+ function compactCommand ( cmd : CliCommand ) : Record < string , unknown > {
124192 return {
125193 name : cmd . name ,
126194 command : `opencli ${ cmd . site } ${ cmd . name } ` ,
195+ usage : formatUsage ( cmd ) ,
127196 access : cmd . access ,
128197 description : cmd . description ,
198+ browser : ! ! cmd . browser ,
199+ ...( cmd . domain ? { domain : cmd . domain } : { } ) ,
129200 ...( cmd . aliases ?. length ? { aliases : cmd . aliases } : { } ) ,
130- args : cmd . args . map ( compactArg ) ,
201+ positionals : positionals ( cmd ) . map ( compactArg ) ,
202+ command_options : commandOptions ( cmd ) . map ( compactArg ) ,
131203 example : formatCommandExample ( cmd ) ,
132204 ...( cmd . browserSession ? { browserSession : cmd . browserSession } : { } ) ,
133205 ...( cmd . defaultFormat ? { defaultFormat : cmd . defaultFormat } : { } ) ,
134- ...( opts . includeColumns && cmd . columns ?. length ? { columns : cmd . columns } : { } ) ,
206+ ...( cmd . columns ?. length ? { columns : cmd . columns } : { } ) ,
135207 } ;
136208}
137209
@@ -176,6 +248,7 @@ export function siteHelpData(site: string, commands: readonly CliCommand[]): Rec
176248 site,
177249 command_count : unique . length ,
178250 commands : unique . map ( cmd => compactCommand ( cmd ) ) ,
251+ common_options : COMMON_OPTIONS . map ( compactCommonOption ) ,
179252 next : [
180253 `opencli ${ site } <command> --help -f yaml` ,
181254 `opencli ${ site } <command> -f yaml` ,
@@ -186,11 +259,95 @@ export function siteHelpData(site: string, commands: readonly CliCommand[]): Rec
186259export function commandHelpData ( cmd : CliCommand ) : Record < string , unknown > {
187260 return {
188261 site : cmd . site ,
189- ...compactCommand ( cmd , { includeColumns : true } ) ,
262+ ...compactCommand ( cmd ) ,
263+ common_options : COMMON_OPTIONS . map ( compactCommonOption ) ,
190264 output_formats : [ 'table' , 'plain' , 'yaml' , 'json' , 'md' , 'csv' ] ,
191265 } ;
192266}
193267
268+ function formatRows ( rows : readonly [ string , string ] [ ] ) : string [ ] {
269+ if ( rows . length === 0 ) return [ ] ;
270+ const width = Math . min ( Math . max ( ...rows . map ( ( [ left ] ) => left . length ) ) , 34 ) ;
271+ return rows . map ( ( [ left , right ] ) => ` ${ left . padEnd ( width + 2 ) } ${ right } ` ) ;
272+ }
273+
274+ function formatArgHelp ( arg : Arg ) : string {
275+ const parts : string [ ] = [ ] ;
276+ if ( arg . help ) parts . push ( arg . help ) ;
277+ if ( arg . default !== undefined ) parts . push ( `default: ${ arg . default } ` ) ;
278+ if ( arg . choices ?. length ) parts . push ( `choices: ${ arg . choices . join ( ', ' ) } ` ) ;
279+ return parts . join ( ' ' ) ;
280+ }
281+
282+ export function formatCommonOptionsHelpText ( ) : string {
283+ const rows = COMMON_OPTIONS . map ( option => {
284+ const details : string [ ] = [ option . help ] ;
285+ if ( 'default' in option ) details . push ( `default: ${ option . default } ` ) ;
286+ if ( 'choices' in option ) details . push ( `choices: ${ option . choices . join ( ', ' ) } ` ) ;
287+ return [ option . flags , details . join ( ' ' ) ] as [ string , string ] ;
288+ } ) ;
289+ return [ 'Common options:' , ...formatRows ( rows ) ] . join ( '\n' ) ;
290+ }
291+
292+ export function formatSiteHelpText ( site : string , commands : readonly CliCommand [ ] ) : string {
293+ const unique = [ ...new Map ( commands . map ( cmd => [ fullName ( cmd ) , cmd ] ) ) . values ( ) ]
294+ . sort ( ( a , b ) => a . name . localeCompare ( b . name ) ) ;
295+ const lines : string [ ] = [
296+ `Usage: opencli ${ site } <command> [args] [options]` ,
297+ '' ,
298+ wrapCommaList ( unique . map ( cmd => cmd . name ) , { indent : '' } ) ,
299+ '' ,
300+ 'Commands:' ,
301+ ...formatRows ( unique . map ( cmd => [ formatCommandListTerm ( cmd ) , formatSiteCommandDescription ( cmd ) ] ) ) ,
302+ '' ,
303+ formatCommonOptionsHelpText ( ) ,
304+ '' ,
305+ `Agent tip: use 'opencli ${ site } --help -f yaml' to get all command args/options in one structured response.` ,
306+ '' ,
307+ ] ;
308+ return lines . join ( '\n' ) ;
309+ }
310+
311+ export function formatCommandHelpText ( cmd : CliCommand ) : string {
312+ const lines : string [ ] = [
313+ `Usage: ${ formatUsage ( cmd ) } ` ,
314+ '' ,
315+ cmd . description ,
316+ '' ,
317+ ] ;
318+
319+ const positionalRows = positionals ( cmd ) . map ( arg => [
320+ arg . name ,
321+ formatArgHelp ( arg ) ,
322+ ] as [ string , string ] ) ;
323+ if ( positionalRows . length ) {
324+ lines . push ( 'Arguments:' , ...formatRows ( positionalRows ) , '' ) ;
325+ }
326+
327+ const optionRows = commandOptions ( cmd ) . map ( arg => [
328+ formatCommandOptionTerm ( arg ) ,
329+ formatArgHelp ( arg ) ,
330+ ] as [ string , string ] ) ;
331+ if ( optionRows . length ) {
332+ lines . push ( 'Command options:' , ...formatRows ( optionRows ) , '' ) ;
333+ }
334+
335+ lines . push ( formatCommonOptionsHelpText ( ) , '' ) ;
336+
337+ const meta : string [ ] = [ ] ;
338+ meta . push ( `Access: ${ cmd . access } ` ) ;
339+ meta . push ( `Browser: ${ cmd . browser ? 'yes' : 'no' } ` ) ;
340+ if ( cmd . domain ) meta . push ( `Domain: ${ cmd . domain } ` ) ;
341+ if ( cmd . defaultFormat ) meta . push ( `Default format: ${ cmd . defaultFormat } ` ) ;
342+ if ( cmd . aliases ?. length ) meta . push ( `Aliases: ${ cmd . aliases . join ( ', ' ) } ` ) ;
343+ lines . push ( meta . join ( ' | ' ) ) ;
344+ lines . push ( `Example: ${ formatCommandExample ( cmd ) } ` ) ;
345+ if ( cmd . columns ?. length ) lines . push ( `Output columns: ${ cmd . columns . join ( ', ' ) } ` ) ;
346+ lines . push ( "Agent tip: use '--help -f yaml' for structured args/options." ) ;
347+ lines . push ( '' ) ;
348+ return lines . join ( '\n' ) ;
349+ }
350+
194351export function installStructuredHelp (
195352 command : Command ,
196353 data : ( ) => unknown ,
0 commit comments