cli-kiss 0.2.2 → 0.2.4

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.
@@ -1,6 +1,4 @@
1
1
  import { Operation } from "./Operation";
2
- import { OptionUsage } from "./Option";
3
- import { PositionalUsage } from "./Positional";
4
2
  import { ReaderArgs } from "./Reader";
5
3
  import {
6
4
  TypoError,
@@ -9,176 +7,108 @@ import {
9
7
  typoStyleUserInput,
10
8
  TypoText,
11
9
  } from "./Typo";
10
+ import { UsageCommand, UsageSegment } from "./Usage";
12
11
 
13
12
  /**
14
- * Describes a CLI command: how to parse its arguments from raw CLI input and how to
15
- * execute it within a given context.
13
+ * A CLI command. Created with {@link command}, {@link commandWithSubcommands}, or {@link commandChained}.
16
14
  *
17
- * A `Command` is the central building block of a `cli-kiss` CLI.
18
- * You create one with {@link command}, {@link commandWithSubcommands},
19
- * or {@link commandChained}, and pass it to {@link runAndExit} to run your CLI.
20
- *
21
- * @typeParam Context - The value passed into the command when it is executed. It flows
22
- * from {@link runAndExit}'s `context` argument down through the command chain.
23
- * @typeParam Result - The value produced by executing the command. For root commands
24
- * passed to {@link runAndExit} this is always `void`.
15
+ * @typeParam Context - Injected at execution; forwarded to handlers.
16
+ * @typeParam Result - Produced on execution; typically `void`.
25
17
  */
26
18
  export type Command<Context, Result> = {
27
19
  /**
28
- * Returns the static metadata information about this command.
20
+ * Returns static metadata.
29
21
  */
30
22
  getInformation(): CommandInformation;
31
23
  /**
32
- * Parses `readerArgs` and returns a {@link CommandFactory} that can generate usage
33
- * information or create a ready-to-run {@link CommandInstance}.
34
- *
35
- * Parsing errors are captured and deferred: `createFactory` never throws; instead
36
- * the error surfaces when {@link CommandFactory.createInstance} is called on the
37
- * returned factory.
24
+ * Registers options/positionals on `readerArgs`; returns a {@link CommandDecoder}.
38
25
  */
39
- createFactory(readerArgs: ReaderArgs): CommandFactory<Context, Result>;
26
+ consumeAndMakeDecoder(
27
+ readerArgs: ReaderArgs,
28
+ ): CommandDecoder<Context, Result>;
40
29
  };
41
30
 
42
31
  /**
43
- * Produced by {@link Command.createFactory} after the raw CLI arguments have
44
- * been parsed. Provides two capabilities:
32
+ * Produced by {@link Command.consumeAndMakeDecoder}.
45
33
  *
46
- * 1. **Usage generation** always available, even when parsing failed.
47
- * 2. **Instance creation** throws a {@link TypoError} if parsing failed.
48
- *
49
- * @typeParam Context - Input passed to the command {@link Command}.
50
- * @typeParam Result - Result of the command's logic {@link Command}.
34
+ * @typeParam Context - See {@link Command}.
35
+ * @typeParam Result - See {@link Command}.
51
36
  */
52
- export type CommandFactory<Context, Result> = {
37
+ export type CommandDecoder<Context, Result> = {
53
38
  /**
54
- * Builds the complete {@link CommandUsage} for the currently parsed command path.
55
- * This is called to render the `--help` output and on error when `usageOnError`
56
- * is enabled.
39
+ * Returns {@link UsageCommand} for the current command path.
57
40
  */
58
- generateUsage(): CommandUsage;
41
+ generateUsage(): UsageCommand;
59
42
  /**
60
- * Creates a {@link CommandInstance} that is ready to execute.
43
+ * Creates a ready-to-execute {@link CommandInterpreter}.
61
44
  *
62
- * @throws {@link TypoError} if the argument parsing that occurred during
63
- * {@link Command.createFactory} encountered an error (e.g. unknown
64
- * option, missing required positional, invalid type).
45
+ * @throws {@link TypoError} if parsing or decoding failed.
65
46
  */
66
- createInstance(): CommandInstance<Context, Result>;
47
+ decodeAndMakeInterpreter(): CommandInterpreter<Context, Result>;
67
48
  };
68
49
 
69
50
  /**
70
- * A fully parsed, ready-to-execute command.
51
+ * A fully parsed, decoded and ready-to-execute command.
71
52
  *
72
- * @typeParam Context - The value the caller must provide when executing the command.
73
- * @typeParam Result - The value the command produces on successful execution.
53
+ * @typeParam Context - Context passed to the handler.
54
+ * @typeParam Result - Value produced on success.
74
55
  */
75
- export type CommandInstance<Context, Result> = {
56
+ export type CommandInterpreter<Context, Result> = {
76
57
  /**
77
- * Executes the command with the provided context.
78
- *
79
- * @param context - Arbitrary value injected by the caller (see {@link runAndExit}).
80
- * @returns A promise that resolves to the command's result, or rejects if the
81
- * command handler throws.
58
+ * Executes with the provided context.
82
59
  */
83
60
  executeWithContext(context: Context): Promise<Result>;
84
61
  };
85
62
 
86
63
  /**
87
- * Static, human-readable metadata attached to a command.
88
- *
89
- * This information is displayed in the usage/help output produced by {@link usageToStyledLines}.
64
+ * Command metadata shown in `--help` output.
90
65
  */
91
66
  export type CommandInformation = {
92
- /** Short description of what the command does. Shown prominently in the usage header. */
67
+ /**
68
+ * Shown in the usage header.
69
+ */
93
70
  description: string;
94
71
  /**
95
- * Optional supplementary note shown in parentheses next to the description.
96
- * Suitable for short caveats such as `"deprecated"` or `"experimental"`.
72
+ * Shown in parentheses, e.g. `"deprecated"`, `"experimental"`.
97
73
  */
98
74
  hint?: string;
99
75
  /**
100
- * Optional list of additional detail lines printed below the description.
101
- * Useful for multi-line explanations, examples, or caveats that don't fit in
102
- * a single sentence.
76
+ * Extra lines printed below the description.
103
77
  */
104
78
  details?: Array<string>;
105
- // TODO - printable examples ?
106
- };
107
-
108
- /**
109
- * The full usage/help model for a command as it appears after argument parsing.
110
- *
111
- * This is produced by {@link CommandFactory.generateUsage} and consumed by
112
- * {@link usageToStyledLines} to render the `--help` output.
113
- */
114
- export type CommandUsage = {
115
79
  /**
116
- * Ordered list of breadcrumb segments that form the command's usage line, e.g.:
117
- * `Usage: my-cli <POSITIONAL> subcommand <ANOTHER_POSITIONAL>`.
118
- *
119
- * Each element is either a positional placeholder or a literal subcommand name.
80
+ * Shown in the `Examples:` section.
120
81
  */
121
- breadcrumbs: Array<CommandUsageBreadcrumb>;
122
- /** The command's static metadata (description, hint, details). */
123
- information: CommandInformation;
124
- /**
125
- * Positional arguments that belong to the current command path,
126
- * in the order they must appear on the command line.
127
- */
128
- positionals: Array<PositionalUsage>;
129
- /**
130
- * Subcommands available at the current level of the command hierarchy.
131
- * Non-empty only when the command is a {@link commandWithSubcommands} and the
132
- * subcommand selection could not be resolved (i.e. on error or `--help`).
133
- */
134
- subcommands: Array<CommandUsageSubcommand>;
135
- /**
136
- * Options (flags and valued options) accepted by the current command path,
137
- * in the order they were registered.
138
- */
139
- options: Array<OptionUsage>;
140
- };
141
-
142
- /**
143
- * A single element in the usage breadcrumb trail shown at the top of the help output.
144
- *
145
- * - `{ positional: string }` — A positional placeholder such as `<NAME>` or `[FILE]`.
146
- * - `{ command: string }` — A literal subcommand token such as `deploy`.
147
- */
148
- export type CommandUsageBreadcrumb =
149
- | { positional: string }
150
- | { command: string };
151
-
152
- /**
153
- * Summary information about a single subcommand shown in the `Subcommands:` section
154
- * of the usage output.
155
- */
156
- export type CommandUsageSubcommand = {
157
- /** The literal token the user types to select this subcommand (e.g. `"deploy"`). */
158
- name: string;
159
- /** Short description forwarded from the subcommand's {@link CommandInformation}. */
160
- description: string | undefined;
161
- /** Optional hint forwarded from the subcommand's {@link CommandInformation}. */
162
- hint: string | undefined;
82
+ examples?: Array<{
83
+ /**
84
+ * Explanation shown above the example.
85
+ */
86
+ explanation: string;
87
+ /**
88
+ * Example command args.
89
+ */
90
+ commandArgs: Array<
91
+ | string
92
+ | { positional: string }
93
+ | { subcommand: string }
94
+ | {
95
+ option:
96
+ | { long: string; value?: string }
97
+ | { short: string; value?: string };
98
+ }
99
+ >;
100
+ }>;
163
101
  };
164
102
 
165
103
  /**
166
- * Creates a leaf command — a command that has no subcommands and directly executes
167
- * an {@link Operation}.
168
- *
169
- * During parsing, `command` reads all positionals and options consumed by `operation`,
170
- * then asserts that no extra positionals remain. Any unexpected trailing positional
171
- * causes a {@link TypoError} deferred to {@link CommandFactory.createInstance}.
104
+ * Creates a leaf command that directly executes an {@link Operation}.
172
105
  *
173
- * @typeParam Context - The context value forwarded to the operation handler at
174
- * execution time.
175
- * @typeParam Result - The value returned by the operation handler.
106
+ * @typeParam Context - Context forwarded to the handler.
107
+ * @typeParam Result - Value returned by the handler.
176
108
  *
177
- * @param information - Static metadata (description, hint, details) for the command.
178
- * @param operation - The operation that defines options, positionals, and the execution
179
- * handler for this command.
180
- * @returns A {@link Command} suitable for passing to {@link runAndExit}
181
- * or composing with {@link commandWithSubcommands} / {@link commandChained}.
109
+ * @param information - Command metadata.
110
+ * @param operation - Options, positionals, and handler.
111
+ * @returns A {@link Command}.
182
112
  *
183
113
  * @example
184
114
  * ```ts
@@ -199,21 +129,9 @@ export function command<Context, Result>(
199
129
  getInformation() {
200
130
  return information;
201
131
  },
202
- createFactory(readerArgs: ReaderArgs) {
203
- function generateUsage(): CommandUsage {
204
- const operationUsage = operation.generateUsage();
205
- return {
206
- breadcrumbs: operationUsage.positionals.map((positional) =>
207
- breadcrumbPositional(positional.label),
208
- ),
209
- information: information,
210
- positionals: operationUsage.positionals,
211
- subcommands: [],
212
- options: operationUsage.options,
213
- };
214
- }
132
+ consumeAndMakeDecoder(readerArgs: ReaderArgs) {
215
133
  try {
216
- const operationFactory = operation.createFactory(readerArgs);
134
+ const operationDecoder = operation.consumeAndMakeDecoder(readerArgs);
217
135
  const endPositional = readerArgs.consumePositional();
218
136
  if (endPositional !== undefined) {
219
137
  throw new TypoError(
@@ -224,20 +142,21 @@ export function command<Context, Result>(
224
142
  );
225
143
  }
226
144
  return {
227
- generateUsage,
228
- createInstance() {
229
- const operationInstance = operationFactory.createInstance();
145
+ generateUsage: () => generateUsageLeaf(information, operation),
146
+ decodeAndMakeInterpreter() {
147
+ const operationInterpreter =
148
+ operationDecoder.decodeAndMakeInterpreter();
230
149
  return {
231
150
  async executeWithContext(context: Context) {
232
- return await operationInstance.executeWithContext(context);
151
+ return await operationInterpreter.executeWithContext(context);
233
152
  },
234
153
  };
235
154
  },
236
155
  };
237
156
  } catch (error) {
238
157
  return {
239
- generateUsage,
240
- createInstance() {
158
+ generateUsage: () => generateUsageLeaf(information, operation),
159
+ decodeAndMakeInterpreter() {
241
160
  throw error;
242
161
  },
243
162
  };
@@ -247,34 +166,16 @@ export function command<Context, Result>(
247
166
  }
248
167
 
249
168
  /**
250
- * Creates a command that first runs an {@link Operation} to produce an
251
- * intermediate `Payload`, then dispatches execution to one of several named subcommands
252
- * based on the next positional argument.
169
+ * Creates a command that runs `operation` first, then dispatches to a named subcommand.
253
170
  *
254
- * **Parsing behaviour:**
255
- * 1. The `operation`'s positionals and options are parsed from `readerArgs`.
256
- * 2. The next positional token is consumed as the subcommand name.
257
- * - If no token is present, a {@link TypoError} is deferred.
258
- * - If the token does not match any key in `subcommands`, a {@link TypoError} is
259
- * deferred.
260
- * 3. The matched subcommand's factory is created with the remaining `readerArgs`.
171
+ * @typeParam Context - Context accepted by `operation`.
172
+ * @typeParam Payload - Output of `operation`; becomes the subcommand's context.
173
+ * @typeParam Result - Value produced by the selected subcommand.
261
174
  *
262
- * **Usage on error / `--help`:** when the subcommand cannot be determined, the usage
263
- * output lists all available subcommands under a `Subcommands:` section.
264
- *
265
- * @typeParam Context - The context value accepted by the root operation handler.
266
- * @typeParam Payload - The value produced by the root operation and forwarded as the
267
- * context to the selected subcommand.
268
- * @typeParam Result - The value produced by the selected subcommand.
269
- *
270
- * @param information - Static metadata shown in the top-level usage when no valid
271
- * subcommand has been selected.
272
- * @param operation - The operation that is always executed first, before the
273
- * subcommand. Its output becomes the subcommand's context.
274
- * @param subcommands - A map of lowercase subcommand names to their
275
- * {@link Command}s. The keys are the literal tokens the user types.
276
- * @returns A {@link Command} that dispatches to one of the provided
277
- * subcommands.
175
+ * @param information - Command metadata.
176
+ * @param operation - Runs first; output becomes the subcommand's context.
177
+ * @param subcommands - Map of subcommand names to their {@link Command}s.
178
+ * @returns A dispatching {@link Command}.
278
179
  *
279
180
  * @example
280
181
  * ```ts
@@ -297,9 +198,9 @@ export function commandWithSubcommands<Context, Payload, Result>(
297
198
  getInformation() {
298
199
  return information;
299
200
  },
300
- createFactory(readerArgs: ReaderArgs) {
201
+ consumeAndMakeDecoder(readerArgs: ReaderArgs) {
301
202
  try {
302
- const operationFactory = operation.createFactory(readerArgs);
203
+ const operationDecoder = operation.consumeAndMakeDecoder(readerArgs);
303
204
  const subcommandName = readerArgs.consumePositional();
304
205
  if (subcommandName === undefined) {
305
206
  throw new TypoError(
@@ -320,31 +221,29 @@ export function commandWithSubcommands<Context, Payload, Result>(
320
221
  ),
321
222
  );
322
223
  }
323
- const subcommandFactory = subcommandInput.createFactory(readerArgs);
224
+ const subcommandDecoder =
225
+ subcommandInput.consumeAndMakeDecoder(readerArgs);
324
226
  return {
325
227
  generateUsage() {
326
- const operationUsage = operation.generateUsage();
327
- const subcommandUsage = subcommandFactory.generateUsage();
328
- return {
329
- breadcrumbs: operationUsage.positionals
330
- .map((positional) => breadcrumbPositional(positional.label))
331
- .concat([breadcrumbCommand(subcommandName)])
332
- .concat(subcommandUsage.breadcrumbs),
333
- information: subcommandUsage.information,
334
- positionals: operationUsage.positionals.concat(
335
- subcommandUsage.positionals,
336
- ),
337
- subcommands: subcommandUsage.subcommands,
338
- options: operationUsage.options.concat(subcommandUsage.options),
339
- };
228
+ const subcommandUsage = subcommandDecoder.generateUsage();
229
+ const currentUsage = generateUsageLeaf(information, operation);
230
+ currentUsage.segments.push(segmentSubcommand(subcommandName));
231
+ currentUsage.segments.push(...subcommandUsage.segments);
232
+ currentUsage.information = subcommandUsage.information;
233
+ currentUsage.positionals.push(...subcommandUsage.positionals);
234
+ currentUsage.subcommands = subcommandUsage.subcommands;
235
+ currentUsage.options.push(...subcommandUsage.options);
236
+ return currentUsage;
340
237
  },
341
- createInstance() {
342
- const operationInstance = operationFactory.createInstance();
343
- const subcommandInstance = subcommandFactory.createInstance();
238
+ decodeAndMakeInterpreter() {
239
+ const operationInterpreter =
240
+ operationDecoder.decodeAndMakeInterpreter();
241
+ const subcommandInterpreter =
242
+ subcommandDecoder.decodeAndMakeInterpreter();
344
243
  return {
345
244
  async executeWithContext(context: Context) {
346
- return await subcommandInstance.executeWithContext(
347
- await operationInstance.executeWithContext(context),
245
+ return await subcommandInterpreter.executeWithContext(
246
+ await operationInterpreter.executeWithContext(context),
348
247
  );
349
248
  },
350
249
  };
@@ -353,25 +252,15 @@ export function commandWithSubcommands<Context, Payload, Result>(
353
252
  } catch (error) {
354
253
  return {
355
254
  generateUsage() {
356
- const operationUsage = operation.generateUsage();
357
- return {
358
- breadcrumbs: operationUsage.positionals
359
- .map((positional) => breadcrumbPositional(positional.label))
360
- .concat([breadcrumbPositional("<SUBCOMMAND>")]),
361
- information: information,
362
- positionals: operationUsage.positionals,
363
- subcommands: Object.entries(subcommands).map((subcommand) => {
364
- const metadata = subcommand[1].getInformation();
365
- return {
366
- name: subcommand[0],
367
- description: metadata.description,
368
- hint: metadata.hint,
369
- };
370
- }),
371
- options: operationUsage.options,
372
- };
255
+ const currentUsage = generateUsageLeaf(information, operation);
256
+ currentUsage.segments.push(segmentPositional("<SUBCOMMAND>"));
257
+ for (const [name, subcommand] of Object.entries(subcommands)) {
258
+ const { description, hint } = subcommand.getInformation();
259
+ currentUsage.subcommands.push({ name, description, hint });
260
+ }
261
+ return currentUsage;
373
262
  },
374
- createInstance() {
263
+ decodeAndMakeInterpreter() {
375
264
  throw error;
376
265
  },
377
266
  };
@@ -381,36 +270,17 @@ export function commandWithSubcommands<Context, Payload, Result>(
381
270
  }
382
271
 
383
272
  /**
384
- * Creates a command that chains two command stages by piping the output of an
385
- * {@link Operation} directly into a {@link Command} as its context.
273
+ * Chains an {@link Operation} and a {@link Command}: `operation` runs first, its
274
+ * output becomes `subcommand`'s context. No token is consumed for routing.
386
275
  *
387
- * Unlike {@link commandWithSubcommands}, there is no runtime token consumed for routing;
388
- * the `nextCommand` is always the continuation. This is useful for splitting a complex
389
- * command into reusable pieces, such as a shared authentication step followed by
390
- * different sub-actions.
276
+ * @typeParam Context - Context accepted by `operation`.
277
+ * @typeParam Payload - Output of `operation`; becomes `subcommand`'s context.
278
+ * @typeParam Result - Value produced by `subcommand`.
391
279
  *
392
- * **Parsing behaviour:**
393
- * 1. `operation`'s positionals and options are parsed.
394
- * 2. `nextCommand`'s factory is immediately created from the same `readerArgs` (the
395
- * remaining unparsed tokens).
396
- * 3. At execution time, `operation` runs first; its result is passed as the context to
397
- * `nextCommand`.
398
- *
399
- * **Usage:** breadcrumbs, positionals, and options from both stages are merged into a
400
- * single flat usage description. The `information` of `nextCommand` takes precedence in
401
- * the generated usage output.
402
- *
403
- * @typeParam Context - The context value accepted by `operation`.
404
- * @typeParam Payload - The value produced by `operation` and used as the context for
405
- * `nextCommand`.
406
- * @typeParam Result - The value produced by `nextCommand`.
407
- *
408
- * @param information - Fallback metadata used in the usage output when `nextCommand`'s
409
- * factory cannot be created (i.e. on parse error in the next stage).
410
- * @param operation - The first stage operation. Its output becomes `nextCommand`'s
411
- * context.
412
- * @param nextCommand - The second stage command, executed after `operation` succeeds.
413
- * @returns A {@link Command} that transparently composes the two stages.
280
+ * @param information - Command metadata.
281
+ * @param operation - Runs first; output becomes `subcommand`'s context.
282
+ * @param subcommand - Runs after `operation`.
283
+ * @returns A {@link Command} composing both stages.
414
284
  *
415
285
  * @example
416
286
  * ```ts
@@ -427,39 +297,36 @@ export function commandWithSubcommands<Context, Payload, Result>(
427
297
  export function commandChained<Context, Payload, Result>(
428
298
  information: CommandInformation,
429
299
  operation: Operation<Context, Payload>,
430
- nextCommand: Command<Payload, Result>,
300
+ subcommand: Command<Payload, Result>,
431
301
  ): Command<Context, Result> {
432
302
  return {
433
303
  getInformation() {
434
304
  return information;
435
305
  },
436
- createFactory(readerArgs: ReaderArgs) {
306
+ consumeAndMakeDecoder(readerArgs: ReaderArgs) {
437
307
  try {
438
- const operationFactory = operation.createFactory(readerArgs);
439
- const nextCommandFactory = nextCommand.createFactory(readerArgs);
308
+ const operationDecoder = operation.consumeAndMakeDecoder(readerArgs);
309
+ const subcommandDecoder = subcommand.consumeAndMakeDecoder(readerArgs);
440
310
  return {
441
311
  generateUsage() {
442
- const operationUsage = operation.generateUsage();
443
- const nextCommandUsage = nextCommandFactory.generateUsage();
444
- return {
445
- breadcrumbs: operationUsage.positionals
446
- .map((positional) => breadcrumbPositional(positional.label))
447
- .concat(nextCommandUsage.breadcrumbs),
448
- information: nextCommandUsage.information,
449
- positionals: operationUsage.positionals.concat(
450
- nextCommandUsage.positionals,
451
- ),
452
- subcommands: nextCommandUsage.subcommands,
453
- options: operationUsage.options.concat(nextCommandUsage.options),
454
- };
312
+ const subcommandUsage = subcommandDecoder.generateUsage();
313
+ const currentUsage = generateUsageLeaf(information, operation);
314
+ currentUsage.segments.push(...subcommandUsage.segments);
315
+ currentUsage.information = subcommandUsage.information;
316
+ currentUsage.positionals.push(...subcommandUsage.positionals);
317
+ currentUsage.subcommands = subcommandUsage.subcommands;
318
+ currentUsage.options.push(...subcommandUsage.options);
319
+ return currentUsage;
455
320
  },
456
- createInstance() {
457
- const operationInstance = operationFactory.createInstance();
458
- const nextCommandInstance = nextCommandFactory.createInstance();
321
+ decodeAndMakeInterpreter() {
322
+ const operationInterpreter =
323
+ operationDecoder.decodeAndMakeInterpreter();
324
+ const subcommandInterpreter =
325
+ subcommandDecoder.decodeAndMakeInterpreter();
459
326
  return {
460
327
  async executeWithContext(context: Context) {
461
- return await nextCommandInstance.executeWithContext(
462
- await operationInstance.executeWithContext(context),
328
+ return await subcommandInterpreter.executeWithContext(
329
+ await operationInterpreter.executeWithContext(context),
463
330
  );
464
331
  },
465
332
  };
@@ -468,18 +335,11 @@ export function commandChained<Context, Payload, Result>(
468
335
  } catch (error) {
469
336
  return {
470
337
  generateUsage() {
471
- const operationUsage = operation.generateUsage();
472
- return {
473
- breadcrumbs: operationUsage.positionals
474
- .map((positional) => breadcrumbPositional(positional.label))
475
- .concat([breadcrumbPositional("[REST]...")]),
476
- information: information,
477
- positionals: operationUsage.positionals,
478
- subcommands: [],
479
- options: operationUsage.options,
480
- };
338
+ const currentUsage = generateUsageLeaf(information, operation);
339
+ currentUsage.segments.push(segmentPositional("[REST]..."));
340
+ return currentUsage;
481
341
  },
482
- createInstance() {
342
+ decodeAndMakeInterpreter() {
483
343
  throw error;
484
344
  },
485
345
  };
@@ -488,10 +348,26 @@ export function commandChained<Context, Payload, Result>(
488
348
  };
489
349
  }
490
350
 
491
- function breadcrumbPositional(value: string): CommandUsageBreadcrumb {
351
+ function segmentPositional(value: string): UsageSegment {
492
352
  return { positional: value };
493
353
  }
494
354
 
495
- function breadcrumbCommand(value: string): CommandUsageBreadcrumb {
496
- return { command: value };
355
+ function segmentSubcommand(value: string): UsageSegment {
356
+ return { subcommand: value };
357
+ }
358
+
359
+ function generateUsageLeaf(
360
+ information: CommandInformation,
361
+ operation: Operation<any, any>,
362
+ ): UsageCommand {
363
+ const { positionals, options } = operation.generateUsage();
364
+ return {
365
+ segments: positionals.map((positional) =>
366
+ segmentPositional(positional.label),
367
+ ),
368
+ information,
369
+ positionals,
370
+ subcommands: [],
371
+ options,
372
+ };
497
373
  }