cli-forge 0.11.0 → 1.0.0

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,3 +1,4 @@
1
+ /* eslint-disable @typescript-eslint/ban-types */
1
2
  import {
2
3
  ArgvParser,
3
4
  EnvOptionConfig,
@@ -10,7 +11,14 @@ import {
10
11
  } from '@cli-forge/parser';
11
12
  import { getCallingFile, getParentPackageJson } from './utils';
12
13
  import { INTERACTIVE_SHELL, InteractiveShell } from './interactive-shell';
13
- import { CLI, CLICommandOptions, Command, ErrorHandler } from './public-api';
14
+ import {
15
+ CLI,
16
+ CLICommandOptions,
17
+ CLIHandlerContext,
18
+ Command,
19
+ ErrorHandler,
20
+ SDKCommand,
21
+ } from './public-api';
14
22
  import { readOptionGroupsForCLI } from './cli-option-groups';
15
23
  import { formatHelp } from './format-help';
16
24
 
@@ -33,19 +41,30 @@ import { formatHelp } from './format-help';
33
41
  * }).forge();
34
42
  * ```
35
43
  */
36
- export class InternalCLI<TArgs extends ParsedArgs = ParsedArgs>
37
- implements CLI<TArgs>
44
+ export class InternalCLI<
45
+ TArgs extends ParsedArgs = ParsedArgs,
46
+ THandlerReturn = void,
47
+ // eslint-disable-next-line @typescript-eslint/ban-types
48
+ TChildren = {},
49
+ TParent = undefined
50
+ > implements CLI<TArgs, THandlerReturn, TChildren, TParent>
38
51
  {
39
52
  /**
40
53
  * For internal use only. Stick to properties available on {@link CLI}.
41
54
  */
42
- registeredCommands: Record<string, InternalCLI<any>> = {};
55
+ registeredCommands: Record<string, InternalCLI<any, any, any, any>> = {};
43
56
 
44
57
  /**
45
58
  * For internal use only. Stick to properties available on {@link CLI}.
46
59
  */
47
60
  commandChain: string[] = [];
48
61
 
62
+ /**
63
+ * Reference to the parent CLI instance, if this command was registered as a subcommand.
64
+ * For internal use only. Use `getParent()` instead.
65
+ */
66
+ private _parent?: InternalCLI<any, any, any, any>;
67
+
49
68
  private requiresCommand: 'IMPLICIT' | 'EXPLICIT' | false = 'IMPLICIT';
50
69
 
51
70
  private _configuration?: CLICommandOptions<any, any>;
@@ -100,14 +119,14 @@ export class InternalCLI<TArgs extends ParsedArgs = ParsedArgs>
100
119
  parser = new ArgvParser<TArgs>({
101
120
  unmatchedParser: (arg) => {
102
121
  // eslint-disable-next-line @typescript-eslint/no-this-alias
103
- let currentCommand: InternalCLI<any> = this;
122
+ let currentCommand: InternalCLI<any, any, any, any> = this;
104
123
  for (const command of this.commandChain) {
105
124
  currentCommand = currentCommand.registeredCommands[command];
106
125
  }
107
126
  const command = currentCommand.registeredCommands[arg];
108
127
  if (command && command.configuration) {
109
128
  command.parser = this.parser;
110
- command.configuration.builder?.(command);
129
+ command.configuration.builder?.(command as any);
111
130
  this.commandChain.push(arg);
112
131
  return true;
113
132
  }
@@ -130,10 +149,15 @@ export class InternalCLI<TArgs extends ParsedArgs = ParsedArgs>
130
149
  */
131
150
  constructor(
132
151
  public name: string,
133
- rootCommandConfiguration?: CLICommandOptions<TArgs>
152
+ rootCommandConfiguration?: CLICommandOptions<
153
+ TArgs,
154
+ any,
155
+ THandlerReturn,
156
+ TChildren
157
+ >
134
158
  ) {
135
159
  if (rootCommandConfiguration) {
136
- this.withRootCommandConfiguration(rootCommandConfiguration);
160
+ this.withRootCommandConfiguration(rootCommandConfiguration as any);
137
161
  } else {
138
162
  this.requiresCommand = 'IMPLICIT';
139
163
  }
@@ -141,16 +165,87 @@ export class InternalCLI<TArgs extends ParsedArgs = ParsedArgs>
141
165
 
142
166
  withRootCommandConfiguration<TRootCommandArgs extends TArgs>(
143
167
  configuration: CLICommandOptions<TArgs, TRootCommandArgs>
144
- ): InternalCLI<TArgs> {
168
+ ): InternalCLI<TArgs, THandlerReturn, TChildren, TParent> {
145
169
  this.configuration = configuration;
146
170
  this.requiresCommand = false;
147
171
  return this;
148
172
  }
149
173
 
150
174
  command<TCommandArgs extends TArgs>(
151
- keyOrCommand: string | Command<TArgs, TCommandArgs>,
152
- options?: CLICommandOptions<TArgs, TCommandArgs>
153
- ): CLI<TArgs> {
175
+ cmd: Command<TArgs, TCommandArgs>
176
+ ): CLI<
177
+ TArgs,
178
+ THandlerReturn,
179
+ TChildren &
180
+ (typeof cmd extends Command<TArgs, infer TCmdArgs, infer TCmdName>
181
+ ? {
182
+ [key in TCmdName]: CLI<
183
+ TCmdArgs,
184
+ void,
185
+ {},
186
+ CLI<TArgs, THandlerReturn, TChildren, TParent>
187
+ >;
188
+ }
189
+ : {}),
190
+ TParent
191
+ >;
192
+ command<
193
+ TCommandArgs extends TArgs,
194
+ TChildHandlerReturn = void,
195
+ TCommandName extends string = string
196
+ >(
197
+ key: TCommandName,
198
+ options: CLICommandOptions<TArgs, TCommandArgs, TChildHandlerReturn>
199
+ ): CLI<
200
+ TArgs,
201
+ THandlerReturn,
202
+ TChildren & {
203
+ [key in TCommandName]: CLI<
204
+ TCommandArgs,
205
+ TChildHandlerReturn,
206
+ {},
207
+ CLI<TArgs, THandlerReturn, TChildren, TParent>
208
+ >;
209
+ },
210
+ TParent
211
+ >;
212
+ command<
213
+ TCommandArgs extends TArgs,
214
+ TChildHandlerReturn = void,
215
+ TCommandName extends string = string
216
+ >(
217
+ keyOrCommand: TCommandName | Command<TArgs, TCommandArgs>,
218
+ options?: CLICommandOptions<TArgs, TCommandArgs, TChildHandlerReturn>
219
+ ): CLI<
220
+ TArgs,
221
+ THandlerReturn,
222
+ TChildren &
223
+ (typeof keyOrCommand extends string
224
+ ? {
225
+ [key in typeof keyOrCommand]: CLI<
226
+ TCommandArgs,
227
+ TChildHandlerReturn,
228
+ {},
229
+ CLI<TArgs, THandlerReturn, TChildren, TParent>
230
+ >;
231
+ }
232
+ : typeof keyOrCommand extends Command<
233
+ TArgs,
234
+ infer TCmdArgs,
235
+ infer TCmdName
236
+ >
237
+ ? {
238
+ [key in TCmdName]: CLI<
239
+ TCmdArgs,
240
+ void,
241
+ {},
242
+ CLI<TArgs, THandlerReturn, TChildren, TParent>
243
+ >;
244
+ }
245
+ : // eslint-disable-next-line @typescript-eslint/ban-types
246
+ {}),
247
+ TParent
248
+ > {
154
249
  if (typeof keyOrCommand === 'string') {
155
250
  const key = keyOrCommand;
156
251
  if (!options) {
@@ -166,9 +261,10 @@ export class InternalCLI<TArgs extends ParsedArgs = ParsedArgs>
166
261
  description: options.description,
167
262
  });
168
263
  }
169
- const cmd = new InternalCLI<TArgs>(key).withRootCommandConfiguration(
170
- options
171
- );
264
+ const cmd = new InternalCLI<TArgs, TChildHandlerReturn>(
265
+ key
266
+ ).withRootCommandConfiguration(options as any);
267
+ cmd._parent = this;
172
268
  this.registeredCommands[key] = cmd;
173
269
  if (options.alias) {
174
270
  for (const alias of options.alias) {
@@ -177,6 +273,7 @@ export class InternalCLI<TArgs extends ParsedArgs = ParsedArgs>
177
273
  }
178
274
  } else if (keyOrCommand instanceof InternalCLI) {
179
275
  const cmd = keyOrCommand;
276
+ cmd._parent = this;
180
277
  this.registeredCommands[cmd.name] = cmd;
181
278
  if (cmd.configuration?.alias) {
182
279
  for (const alias of cmd.configuration.alias) {
@@ -189,13 +286,14 @@ export class InternalCLI<TArgs extends ParsedArgs = ParsedArgs>
189
286
  } & CLICommandOptions<TArgs, TCommandArgs>;
190
287
  this.command<TCommandArgs>(name, configuration);
191
288
  }
192
- return this;
289
+ return this as any;
193
290
  }
194
291
 
195
- commands(...a0: Command[] | Command[][]): CLI<TArgs> {
292
+ commands(...a0: Command[] | Command[][]): any {
196
293
  const commands = a0.flat();
197
294
  for (const val of commands) {
198
295
  if (val instanceof InternalCLI) {
296
+ val._parent = this;
199
297
  this.registeredCommands[val.name] = val;
200
298
  // Include any options that were defined via cli(...).option() instead of via builder
201
299
  this.parser.augment(val.parser);
@@ -211,7 +309,7 @@ export class InternalCLI<TArgs extends ParsedArgs = ParsedArgs>
211
309
 
212
310
  option<
213
311
  TOption extends string,
214
- const TOptionConfig extends OptionConfig<any, any, any, any>
312
+ const TOptionConfig extends OptionConfig<any, any, any>
215
313
  >(name: TOption, config: TOptionConfig) {
216
314
  this.parser.option(name, config);
217
315
  // Interface modifies the return type to reflect new params, cast is necessay.... I think 🤔
@@ -220,58 +318,65 @@ export class InternalCLI<TArgs extends ParsedArgs = ParsedArgs>
220
318
 
221
319
  positional<
222
320
  TOption extends string,
223
- const TOptionConfig extends OptionConfig<any, any, any, any>
321
+ const TOptionConfig extends OptionConfig<any, any, any>
224
322
  >(name: TOption, config: TOptionConfig) {
225
323
  this.parser.positional(name, config);
226
324
  // Interface modifies the return type to reflect new params, cast is necessay.... I think 🤔
227
325
  return this as any;
228
326
  }
229
327
 
230
- conflicts(...args: [string, string, ...string[]]): CLI<TArgs> {
328
+ conflicts(
329
+ ...args: [string, string, ...string[]]
330
+ ): CLI<TArgs, THandlerReturn, TChildren, TParent> {
231
331
  this.parser.conflicts(...args);
232
- return this;
332
+ return this as unknown as CLI<TArgs, THandlerReturn, TChildren, TParent>;
233
333
  }
234
334
 
235
- implies(option: string, ...impliedOptions: string[]): CLI<TArgs> {
335
+ implies(
336
+ option: string,
337
+ ...impliedOptions: string[]
338
+ ): CLI<TArgs, THandlerReturn, TChildren, TParent> {
236
339
  this.parser.implies(option, ...impliedOptions);
237
- return this;
340
+ return this as unknown as CLI<TArgs, THandlerReturn, TChildren, TParent>;
238
341
  }
239
342
 
240
343
  env(
241
344
  a0: string | EnvOptionConfig | undefined = fromCamelOrDashedCaseToConstCase(
242
345
  this.name
243
346
  )
244
- ) {
347
+ ): CLI<TArgs, THandlerReturn, TChildren, TParent> {
245
348
  if (typeof a0 === 'string') {
246
349
  this.parser.env(a0);
247
350
  } else {
248
351
  a0.prefix ??= fromCamelOrDashedCaseToConstCase(this.name);
249
352
  this.parser.env(a0);
250
353
  }
251
- return this;
354
+ return this as unknown as CLI<TArgs, THandlerReturn, TChildren, TParent>;
252
355
  }
253
356
 
254
- demandCommand() {
357
+ demandCommand(): CLI<TArgs, THandlerReturn, TChildren, TParent> {
255
358
  this.requiresCommand = 'EXPLICIT';
256
- return this;
359
+ return this as unknown as CLI<TArgs, THandlerReturn, TChildren, TParent>;
257
360
  }
258
361
 
259
- usage(usageText: string) {
362
+ usage(usageText: string): CLI<TArgs, THandlerReturn, TChildren, TParent> {
260
363
  this.configuration ??= {};
261
364
  this.configuration.usage = usageText;
262
- return this;
365
+ return this as unknown as CLI<TArgs, THandlerReturn, TChildren, TParent>;
263
366
  }
264
367
 
265
- examples(...examples: string[]) {
368
+ examples(
369
+ ...examples: string[]
370
+ ): CLI<TArgs, THandlerReturn, TChildren, TParent> {
266
371
  this.configuration ??= {};
267
372
  this.configuration.examples ??= [];
268
373
  this.configuration.examples.push(...examples);
269
- return this;
374
+ return this as unknown as CLI<TArgs, THandlerReturn, TChildren, TParent>;
270
375
  }
271
376
 
272
- version(version?: string) {
377
+ version(version?: string): CLI<TArgs, THandlerReturn, TChildren, TParent> {
273
378
  this._versionOverride = version;
274
- return this;
379
+ return this as unknown as CLI<TArgs, THandlerReturn, TChildren, TParent>;
275
380
  }
276
381
 
277
382
  /**
@@ -291,7 +396,12 @@ export class InternalCLI<TArgs extends ParsedArgs = ParsedArgs>
291
396
 
292
397
  middleware<TArgs2>(
293
398
  callback: (args: TArgs) => TArgs2 | Promise<TArgs2>
294
- ): CLI<TArgs2 extends void ? TArgs : TArgs & TArgs2> {
399
+ ): CLI<
400
+ TArgs2 extends void ? TArgs : TArgs & TArgs2,
401
+ THandlerReturn,
402
+ TChildren,
403
+ TParent
404
+ > {
295
405
  this.registeredMiddleware.push(callback);
296
406
  // If middleware returns void, TArgs doesn't change...
297
407
  // If it returns something, we need to merge it into TArgs...
@@ -309,7 +419,7 @@ export class InternalCLI<TArgs extends ParsedArgs = ParsedArgs>
309
419
  ...this.registeredMiddleware,
310
420
  ];
311
421
  // eslint-disable-next-line @typescript-eslint/no-this-alias
312
- let cmd: InternalCLI<any> = this;
422
+ let cmd: InternalCLI<any, any, any, any> = this;
313
423
  for (const command of this.commandChain) {
314
424
  cmd = cmd.registeredCommands[command];
315
425
  middlewares.push(...cmd.registeredMiddleware);
@@ -330,8 +440,8 @@ export class InternalCLI<TArgs extends ParsedArgs = ParsedArgs>
330
440
  args = middlewareResult as T;
331
441
  }
332
442
  }
333
- await cmd.configuration.handler(args, {
334
- command: cmd,
443
+ return cmd.configuration.handler(args, {
444
+ command: cmd as any,
335
445
  });
336
446
  } else {
337
447
  // We can treat a command as a subshell if it has subcommands
@@ -340,9 +450,12 @@ export class InternalCLI<TArgs extends ParsedArgs = ParsedArgs>
340
450
  // If we're not in a TTY, we can't run an interactive shell...
341
451
  // Maybe we should warn here?
342
452
  } else if (!INTERACTIVE_SHELL) {
343
- const tui = new InteractiveShell(this, {
344
- prependArgs: originalArgV,
345
- });
453
+ const tui = new InteractiveShell(
454
+ this as unknown as InternalCLI<any>,
455
+ {
456
+ prependArgs: originalArgV,
457
+ }
458
+ );
346
459
  await new Promise<void>((res) => {
347
460
  ['SIGINT', 'SIGTERM', 'SIGQUIT'].forEach((s) =>
348
461
  process.on(s, () => {
@@ -368,7 +481,171 @@ export class InternalCLI<TArgs extends ParsedArgs = ParsedArgs>
368
481
  }
369
482
  }
370
483
 
371
- enableInteractiveShell() {
484
+ getChildren(): TChildren {
485
+ // Return a copy of registered commands, excluding aliases (same command registered under different keys)
486
+ const children: Record<string, InternalCLI<any, any, any, any>> = {};
487
+ const seen = new Set<InternalCLI<any, any, any, any>>();
488
+ for (const [key, cmd] of Object.entries(this.registeredCommands)) {
489
+ if (!seen.has(cmd)) {
490
+ seen.add(cmd);
491
+ children[key] = cmd;
492
+ }
493
+ }
494
+ return children as TChildren;
495
+ }
496
+
497
+ getParent(): TParent {
498
+ return this._parent as TParent;
499
+ }
500
+
501
+ getBuilder():
502
+ | (<TInit extends ParsedArgs, TInitHandlerReturn, TInitChildren, TInitParent>(
503
+ parser: CLI<TInit, TInitHandlerReturn, TInitChildren, TInitParent>
504
+ ) => CLI<TInit & TArgs, TInitHandlerReturn, TInitChildren & TChildren, TInitParent>)
505
+ | undefined {
506
+ const builder = this.configuration?.builder;
507
+ if (!builder) return undefined;
508
+ // Return a composable builder that preserves input types
509
+ return ((parser: CLI<any, any, any, any>) => builder(parser)) as any;
510
+ }
511
+
512
+ getHandler():
513
+ | ((args: Omit<TArgs, keyof ParsedArgs>) => THandlerReturn)
514
+ | undefined {
515
+ const context: CLIHandlerContext<TChildren, TParent> = {
516
+ command: this as unknown as CLI<any, any, TChildren, TParent>,
517
+ };
518
+ const handler = this._configuration?.handler;
519
+ if (!handler) {
520
+ return undefined;
521
+ }
522
+ return (args: Omit<TArgs, keyof ParsedArgs>) =>
523
+ handler(
524
+ args as TArgs,
525
+ context as CLIHandlerContext<any, any>
526
+ ) as THandlerReturn;
527
+ }
528
+
529
+ sdk(): SDKCommand<TArgs, THandlerReturn, TChildren> {
530
+ return this.buildSDKProxy(this) as SDKCommand<
531
+ TArgs,
532
+ THandlerReturn,
533
+ TChildren
534
+ >;
535
+ }
536
+
537
+ private buildSDKProxy(targetCmd: InternalCLI<any, any, any, any>): unknown {
538
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
539
+ const self = this;
540
+
541
+ const invoke = async (
542
+ argsOrArgv?: Record<string, unknown> | string[]
543
+ ): Promise<THandlerReturn & { $args?: TArgs }> => {
544
+ // Clone the target command to avoid mutating the original
545
+ const cmd = targetCmd.clone();
546
+
547
+ const handler = cmd._configuration?.handler;
548
+ if (!handler) {
549
+ throw new Error(`Command '${cmd.name}' has no handler`);
550
+ }
551
+
552
+ let parsedArgs: any;
553
+
554
+ if (Array.isArray(argsOrArgv)) {
555
+ // String array: full pipeline (parse → validate → middleware)
556
+ // Run the builder first if present
557
+ if (cmd._configuration?.builder) {
558
+ cmd._configuration.builder(cmd as any);
559
+ }
560
+ parsedArgs = cmd.parser.parse(argsOrArgv);
561
+ } else {
562
+ // Object args: skip validation, apply defaults, run middleware
563
+ // Run the builder first to register options and get defaults
564
+ if (cmd._configuration?.builder) {
565
+ cmd._configuration.builder(cmd as any);
566
+ }
567
+ // Build defaults from configured options
568
+ const defaults: Record<string, unknown> = {};
569
+ for (const [key, config] of Object.entries(
570
+ cmd.parser.configuredOptions
571
+ )) {
572
+ if (config.default !== undefined) {
573
+ defaults[key] = config.default;
574
+ }
575
+ }
576
+ parsedArgs = {
577
+ ...defaults,
578
+ ...argsOrArgv,
579
+ unmatched: [],
580
+ };
581
+ }
582
+
583
+ // Collect and run middleware from the command chain
584
+ const middlewares = self.collectMiddlewareChain(targetCmd);
585
+ for (const mw of middlewares) {
586
+ const middlewareResult = await mw(parsedArgs);
587
+ if (
588
+ middlewareResult !== void 0 &&
589
+ typeof middlewareResult === 'object'
590
+ ) {
591
+ parsedArgs = middlewareResult;
592
+ }
593
+ }
594
+
595
+ // Execute handler
596
+ const context: CLIHandlerContext<any, any> = {
597
+ command: cmd as unknown as CLI<any, any, any, any>,
598
+ };
599
+ const result = await handler(parsedArgs, context);
600
+
601
+ // Try to attach $args to the result (fails silently for primitives)
602
+ if (result !== null && typeof result === 'object') {
603
+ try {
604
+ (result as any).$args = parsedArgs;
605
+ } catch {
606
+ // Cannot attach to frozen objects or primitives, return as-is
607
+ }
608
+ }
609
+
610
+ return result as any as THandlerReturn & { $args?: TArgs };
611
+ };
612
+
613
+ // Ensure builder has run to register all subcommands
614
+ if (targetCmd._configuration?.builder) {
615
+ targetCmd._configuration.builder(targetCmd as any);
616
+ }
617
+
618
+ // Create proxy that is both callable and has child properties
619
+ return new Proxy(invoke, {
620
+ get(_, prop: string) {
621
+ // Handle special properties
622
+ if (prop === 'then' || prop === 'catch' || prop === 'finally') {
623
+ // Don't intercept Promise methods - this prevents issues with await
624
+ return undefined;
625
+ }
626
+
627
+ const child = targetCmd.registeredCommands[prop];
628
+ if (child) {
629
+ return self.buildSDKProxy(child);
630
+ }
631
+ return undefined;
632
+ },
633
+ });
634
+ }
635
+
636
+ private collectMiddlewareChain(
637
+ cmd: InternalCLI<any, any, any, any>
638
+ ): Array<(args: any) => unknown | Promise<unknown>> {
639
+ const chain: InternalCLI<any, any, any, any>[] = [];
640
+ let current: InternalCLI<any, any, any, any> | undefined = cmd;
641
+ while (current) {
642
+ chain.unshift(current);
643
+ current = current._parent;
644
+ }
645
+ return chain.flatMap((c) => c.registeredMiddleware);
646
+ }
647
+
648
+ enableInteractiveShell(): CLI<TArgs, THandlerReturn, TChildren, TParent> {
372
649
  if (this.requiresCommand === 'EXPLICIT') {
373
650
  throw new Error(
374
651
  'Interactive shell is not supported for commands that require a command.'
@@ -376,7 +653,7 @@ export class InternalCLI<TArgs extends ParsedArgs = ParsedArgs>
376
653
  } else if (process.stdout.isTTY) {
377
654
  this.requiresCommand = false;
378
655
  }
379
- return this;
656
+ return this as unknown as CLI<TArgs, THandlerReturn, TChildren, TParent>;
380
657
  }
381
658
 
382
659
  private versionHandler() {
@@ -415,9 +692,11 @@ export class InternalCLI<TArgs extends ParsedArgs = ParsedArgs>
415
692
  }
416
693
  }
417
694
 
418
- errorHandler(handler: ErrorHandler) {
695
+ errorHandler(
696
+ handler: ErrorHandler
697
+ ): CLI<TArgs, THandlerReturn, TChildren, TParent> {
419
698
  this.registeredErrorHandlers.unshift(handler);
420
- return this;
699
+ return this as unknown as CLI<TArgs, THandlerReturn, TChildren, TParent>;
421
700
  }
422
701
 
423
702
  group(
@@ -425,7 +704,7 @@ export class InternalCLI<TArgs extends ParsedArgs = ParsedArgs>
425
704
  | string
426
705
  | { label: string; keys: (keyof TArgs)[]; sortOrder: number },
427
706
  keys?: (keyof TArgs)[]
428
- ): CLI<TArgs> {
707
+ ): CLI<TArgs, THandlerReturn, TChildren, TParent> {
429
708
  const config =
430
709
  typeof labelOrConfigObject === 'object'
431
710
  ? labelOrConfigObject
@@ -440,16 +719,16 @@ export class InternalCLI<TArgs extends ParsedArgs = ParsedArgs>
440
719
  }
441
720
 
442
721
  this.registeredOptionGroups.push(config);
443
- return this;
722
+ return this as unknown as CLI<TArgs, THandlerReturn, TChildren, TParent>;
444
723
  }
445
724
 
446
725
  config(
447
726
  provider: ConfigurationFiles.ConfigurationProvider<TArgs>
448
- ): CLI<TArgs> {
727
+ ): CLI<TArgs, THandlerReturn, TChildren, TParent> {
449
728
  this.parser.config(
450
729
  provider as ConfigurationFiles.ConfigurationProvider<any>
451
730
  );
452
- return this;
731
+ return this as unknown as CLI<TArgs, THandlerReturn, TChildren, TParent>;
453
732
  }
454
733
 
455
734
  /**
@@ -475,7 +754,7 @@ export class InternalCLI<TArgs extends ParsedArgs = ParsedArgs>
475
754
  }
476
755
  }
477
756
  // eslint-disable-next-line @typescript-eslint/no-this-alias
478
- let currentCommand: InternalCLI<any> = this;
757
+ let currentCommand: InternalCLI<any, any, any, any> = this;
479
758
  for (const command of this.commandChain) {
480
759
  currentCommand = currentCommand.registeredCommands[command];
481
760
  }
@@ -495,7 +774,9 @@ export class InternalCLI<TArgs extends ParsedArgs = ParsedArgs>
495
774
  const finalArgV =
496
775
  this.commandChain.length === 0 && this.configuration?.builder
497
776
  ? (
498
- this.configuration.builder?.(this as any) as InternalCLI<TArgs>
777
+ this.configuration.builder?.(
778
+ this as any
779
+ ) as unknown as InternalCLI<TArgs, any, any, any>
499
780
  ).parser.parse(args)
500
781
  : argv;
501
782
 
@@ -512,15 +793,16 @@ export class InternalCLI<TArgs extends ParsedArgs = ParsedArgs>
512
793
  }
513
794
 
514
795
  clone() {
515
- const clone = new InternalCLI<TArgs>(this.name);
796
+ const clone = new InternalCLI<TArgs, THandlerReturn, TChildren, TParent>(
797
+ this.name
798
+ );
516
799
  clone.parser = this.parser.clone(clone.parser.options) as any;
517
800
  if (this.configuration) {
518
801
  clone.withRootCommandConfiguration(this.configuration);
519
802
  }
520
803
  clone.registeredCommands = {};
521
804
  for (const command in this.registeredCommands ?? {}) {
522
- clone.command(this.registeredCommands[command].clone());
523
- // this.registeredCommands[command].clone();
805
+ clone.command(this.registeredCommands[command].clone() as any);
524
806
  }
525
807
  clone.commandChain = [...this.commandChain];
526
808
  clone.requiresCommand = this.requiresCommand;