@guanghechen/commander 4.7.2 → 4.7.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.
package/lib/esm/node.mjs CHANGED
@@ -305,6 +305,30 @@ function parsePrimitiveNumber(rawValue) {
305
305
  }
306
306
  return value;
307
307
  }
308
+ function normalizeSubcommandNameForDistance(name) {
309
+ return camelToKebabCase$1(name).toLowerCase();
310
+ }
311
+ function levenshteinDistance(left, right) {
312
+ if (left === right) {
313
+ return 0;
314
+ }
315
+ if (left.length === 0) {
316
+ return right.length;
317
+ }
318
+ if (right.length === 0) {
319
+ return left.length;
320
+ }
321
+ let prev = Array.from({ length: right.length + 1 }, (_, i) => i);
322
+ for (let i = 0; i < left.length; i += 1) {
323
+ const current = [i + 1];
324
+ for (let j = 0; j < right.length; j += 1) {
325
+ const substitutionCost = left[i] === right[j] ? 0 : 1;
326
+ current[j + 1] = Math.min(current[j] + 1, prev[j + 1] + 1, prev[j] + substitutionCost);
327
+ }
328
+ prev = current;
329
+ }
330
+ return prev[right.length];
331
+ }
308
332
  function tokenizeLongOption(arg, commandPath) {
309
333
  const eqIdx = arg.indexOf('=');
310
334
  const namePart = eqIdx !== -1 ? arg.slice(0, eqIdx) : arg;
@@ -707,7 +731,10 @@ class Command {
707
731
  const kebabLong = camelToKebabCase$1(opt.long);
708
732
  let sig = opt.short ? `-${opt.short}, ` : ' ';
709
733
  sig += `--${kebabLong}`;
710
- if (opt.args !== 'none') {
734
+ if (opt.args === 'optional') {
735
+ sig += ' [value]';
736
+ }
737
+ else if (opt.args !== 'none') {
711
738
  sig += ' <value>';
712
739
  }
713
740
  let desc = opt.desc;
@@ -848,7 +875,15 @@ class Command {
848
875
  return ` ${outputLabel} ${desc}`;
849
876
  }
850
877
  getCompletionMeta() {
851
- const allOptions = this.#resolveOptionPolicy().mergedOptions;
878
+ const optionMap = new Map();
879
+ for (const option of this.#resolveOptionPolicy().mergedOptions) {
880
+ optionMap.set(option.long, option);
881
+ }
882
+ optionMap.set('help', BUILTIN_HELP_OPTION);
883
+ if (this.#supportsBuiltinVersion()) {
884
+ optionMap.set('version', BUILTIN_VERSION_OPTION);
885
+ }
886
+ const allOptions = Array.from(optionMap.values());
852
887
  const options = [];
853
888
  const argumentsMeta = [];
854
889
  for (const opt of allOptions) {
@@ -856,7 +891,8 @@ class Command {
856
891
  long: opt.long,
857
892
  short: opt.short,
858
893
  desc: opt.desc,
859
- takesValue: opt.args !== 'none',
894
+ type: opt.type,
895
+ args: opt.args,
860
896
  choices: opt.choices?.map(choice => String(choice)),
861
897
  });
862
898
  }
@@ -1326,6 +1362,14 @@ class Command {
1326
1362
  consumed.push(tokens[i]);
1327
1363
  }
1328
1364
  }
1365
+ else if (opt.args === 'optional') {
1366
+ if (!token.resolved.includes('=') &&
1367
+ i + 1 < tokens.length &&
1368
+ tokens[i + 1].type === 'none') {
1369
+ i += 1;
1370
+ consumed.push(tokens[i]);
1371
+ }
1372
+ }
1329
1373
  else if (opt.args === 'variadic') {
1330
1374
  if (!token.resolved.includes('=')) {
1331
1375
  while (i + 1 < tokens.length && tokens[i + 1].type === 'none') {
@@ -1351,6 +1395,12 @@ class Command {
1351
1395
  consumed.push(tokens[i]);
1352
1396
  }
1353
1397
  }
1398
+ else if (opt.args === 'optional') {
1399
+ if (i + 1 < tokens.length && tokens[i + 1].type === 'none') {
1400
+ i += 1;
1401
+ consumed.push(tokens[i]);
1402
+ }
1403
+ }
1354
1404
  else if (opt.args === 'variadic') {
1355
1405
  while (i + 1 < tokens.length && tokens[i + 1].type === 'none') {
1356
1406
  i += 1;
@@ -1392,6 +1442,7 @@ class Command {
1392
1442
  leafLocalOpts[opt.long] = leafParsedOpts[opt.long];
1393
1443
  }
1394
1444
  }
1445
+ leafCommand.#assertUnknownSubcommand(ctx.sources.user.argv);
1395
1446
  const rawArgStrings = [...argTokens.map(t => t.original), ...restArgs];
1396
1447
  const { args, rawArgs } = leafCommand.#parseArguments(rawArgStrings);
1397
1448
  const parseCtx = {
@@ -1474,6 +1525,23 @@ class Command {
1474
1525
  i += 1;
1475
1526
  continue;
1476
1527
  }
1528
+ if (opt.args === 'optional') {
1529
+ const eqIdx = token.resolved.indexOf('=');
1530
+ if (eqIdx !== -1) {
1531
+ opts[opt.long] = this.#convertValue(opt, token.resolved.slice(eqIdx + 1));
1532
+ i += 1;
1533
+ continue;
1534
+ }
1535
+ if (i + 1 < tokens.length && tokens[i + 1].type === 'none') {
1536
+ opts[opt.long] = this.#convertValue(opt, tokens[i + 1].original);
1537
+ i += 1;
1538
+ }
1539
+ else {
1540
+ opts[opt.long] = undefined;
1541
+ }
1542
+ i += 1;
1543
+ continue;
1544
+ }
1477
1545
  if (opt.args === 'variadic') {
1478
1546
  const values = Array.isArray(opts[opt.long]) ? opts[opt.long] : [];
1479
1547
  const eqIdx = token.resolved.indexOf('=');
@@ -1493,7 +1561,7 @@ class Command {
1493
1561
  i += 1;
1494
1562
  }
1495
1563
  for (const opt of allOptions) {
1496
- if (opt.required && opts[opt.long] === undefined) {
1564
+ if (opt.required && !Object.prototype.hasOwnProperty.call(opts, opt.long)) {
1497
1565
  throw new CommanderError('MissingRequired', `missing required option "--${camelToKebabCase$1(opt.long)}"`, this.#getCommandPath());
1498
1566
  }
1499
1567
  }
@@ -1530,6 +1598,9 @@ class Command {
1530
1598
  #parseArguments(rawArgs) {
1531
1599
  const argumentDefs = this.#arguments;
1532
1600
  const args = {};
1601
+ if (argumentDefs.length === 0 && rawArgs.length > 0) {
1602
+ throw new CommanderError('UnexpectedArgument', `unexpected argument "${rawArgs[0]}"`, this.#getCommandPath());
1603
+ }
1533
1604
  const missing = [];
1534
1605
  let remaining = rawArgs.length;
1535
1606
  for (const def of argumentDefs) {
@@ -1570,25 +1641,23 @@ class Command {
1570
1641
  }
1571
1642
  if (def.kind === 'some') {
1572
1643
  const rest = rawArgs.slice(index);
1573
- if (rest.length === 0) {
1574
- throw new CommanderError('MissingRequiredArgument', `missing required argument(s): ${def.name}`, this.#getCommandPath());
1575
- }
1576
1644
  args[def.name] = rest.map(raw => this.#convertArgument(def, raw));
1577
1645
  index = rawArgs.length;
1578
1646
  break;
1579
1647
  }
1580
- const raw = rawArgs[index];
1581
- if (raw === undefined) {
1582
- if (def.kind === 'optional') {
1648
+ if (def.kind === 'optional') {
1649
+ const raw = rawArgs[index];
1650
+ if (raw === undefined) {
1583
1651
  args[def.name] = def.default ?? undefined;
1584
1652
  continue;
1585
1653
  }
1586
- throw new CommanderError('MissingRequiredArgument', `missing required argument(s): ${def.name}`, this.#getCommandPath());
1587
- }
1588
- else {
1589
1654
  args[def.name] = this.#convertArgument(def, raw);
1590
1655
  index += 1;
1656
+ continue;
1591
1657
  }
1658
+ const raw = rawArgs[index];
1659
+ args[def.name] = this.#convertArgument(def, raw);
1660
+ index += 1;
1592
1661
  }
1593
1662
  const hasRestArgument = argumentDefs.some(a => a.kind === 'variadic' || a.kind === 'some');
1594
1663
  if (!hasRestArgument && index < rawArgs.length) {
@@ -1622,6 +1691,50 @@ class Command {
1622
1691
  }
1623
1692
  return value;
1624
1693
  }
1694
+ #assertUnknownSubcommand(userTailArgv) {
1695
+ if (this.#subcommandsList.length === 0) {
1696
+ return;
1697
+ }
1698
+ const token = userTailArgv[0];
1699
+ if (token === undefined || token.startsWith('-') || token === 'help') {
1700
+ return;
1701
+ }
1702
+ if (this.#findSubcommandEntry(token) !== undefined) {
1703
+ return;
1704
+ }
1705
+ const hints = [];
1706
+ if (this.#arguments.length === 0) {
1707
+ hints.push(`Hint: command "${this.#getCommandPath()}" does not accept positional arguments.`);
1708
+ }
1709
+ const candidate = this.#resolveDidYouMeanSubcommandName(token);
1710
+ if (candidate !== undefined) {
1711
+ hints.push(`Hint: did you mean "${candidate}"?`);
1712
+ }
1713
+ const details = hints.length > 0 ? `\n${hints.join('\n')}` : '';
1714
+ throw new CommanderError('UnknownSubcommand', `unknown subcommand "${token}" for command "${this.#getCommandPath()}"${details}`, this.#getCommandPath());
1715
+ }
1716
+ #resolveDidYouMeanSubcommandName(token) {
1717
+ const source = normalizeSubcommandNameForDistance(token);
1718
+ let minDistance = Number.POSITIVE_INFINITY;
1719
+ let bestName;
1720
+ let isUniqueBest = false;
1721
+ for (const entry of this.#subcommandsList) {
1722
+ const target = normalizeSubcommandNameForDistance(entry.name);
1723
+ const distance = levenshteinDistance(source, target);
1724
+ if (distance < minDistance) {
1725
+ minDistance = distance;
1726
+ bestName = entry.name;
1727
+ isUniqueBest = true;
1728
+ }
1729
+ else if (distance === minDistance) {
1730
+ isUniqueBest = false;
1731
+ }
1732
+ }
1733
+ if (minDistance <= 2 && isUniqueBest) {
1734
+ return bestName;
1735
+ }
1736
+ return undefined;
1737
+ }
1625
1738
  #hasUserOption(long) {
1626
1739
  return this.#options.some(option => option.long === long);
1627
1740
  }
@@ -1665,11 +1778,9 @@ class Command {
1665
1778
  return optionPolicyMap;
1666
1779
  }
1667
1780
  #mustGetOptionPolicy(optionPolicyMap, cmd) {
1668
- const policy = optionPolicyMap.get(cmd);
1669
- if (policy !== undefined) {
1670
- return policy;
1671
- }
1672
- throw new CommanderError('ConfigurationError', `missing option policy for command "${cmd.#getCommandPath()}"`, this.#getCommandPath());
1781
+ const policy = optionPolicyMap.get(cmd) ?? cmd.#resolveOptionPolicy();
1782
+ optionPolicyMap.set(cmd, policy);
1783
+ return policy;
1673
1784
  }
1674
1785
  #validateMergedShortOptions(chain, optionPolicyMap) {
1675
1786
  const mergedByLong = new Map();
@@ -1698,7 +1809,10 @@ class Command {
1698
1809
  throw new CommanderError('ConfigurationError', `boolean option "--${opt.long}" must have args: 'none'`, this.#getCommandPath());
1699
1810
  }
1700
1811
  if ((opt.type === 'string' || opt.type === 'number') && opt.args === 'none') {
1701
- throw new CommanderError('ConfigurationError', `${opt.type} option "--${opt.long}" must have args: 'required' or 'variadic'`, this.#getCommandPath());
1812
+ throw new CommanderError('ConfigurationError', `${opt.type} option "--${opt.long}" must have args: 'required', 'optional', or 'variadic'`, this.#getCommandPath());
1813
+ }
1814
+ if (opt.type === 'number' && opt.args === 'optional') {
1815
+ throw new CommanderError('ConfigurationError', `number option "--${opt.long}" does not support args: 'optional'`, this.#getCommandPath());
1702
1816
  }
1703
1817
  if (opt.long.startsWith('no')) {
1704
1818
  throw new CommanderError('ConfigurationError', `option long name cannot start with "no": "${opt.long}"`, this.#getCommandPath());
@@ -1715,6 +1829,9 @@ class Command {
1715
1829
  if (opt.type === 'boolean' && opt.required) {
1716
1830
  throw new CommanderError('ConfigurationError', `boolean option "--${opt.long}" cannot be required`, this.#getCommandPath());
1717
1831
  }
1832
+ if (opt.required && opt.args !== 'required') {
1833
+ throw new CommanderError('ConfigurationError', `required option "--${opt.long}" must use args: 'required'`, this.#getCommandPath());
1834
+ }
1718
1835
  }
1719
1836
  #checkOptionUniqueness(opt) {
1720
1837
  if (this.#options.some(o => o.long === opt.long)) {
@@ -2023,6 +2140,41 @@ class Coerce {
2023
2140
  function camelToKebabCase(str) {
2024
2141
  return str.replace(/[A-Z]/g, m => '-' + m.toLowerCase());
2025
2142
  }
2143
+ function canGenerateNegativeCompletion(opt) {
2144
+ return (opt.type === 'boolean' && opt.args === 'none' && opt.long !== 'help' && opt.long !== 'version');
2145
+ }
2146
+ function optionTakesValue(opt) {
2147
+ return opt.args !== 'none';
2148
+ }
2149
+ const COMPLETION_SHELL_STATE = Symbol('completion-shell-state');
2150
+ function getCommandPath(ctx) {
2151
+ const names = ctx.chain
2152
+ .map(command => command.name)
2153
+ .filter((name) => Boolean(name));
2154
+ if (names.length > 0) {
2155
+ return names.join(' ');
2156
+ }
2157
+ return ctx.cmd.name ?? 'command';
2158
+ }
2159
+ function getCompletionShellState(ctx) {
2160
+ const host = ctx;
2161
+ host[COMPLETION_SHELL_STATE] ??= {};
2162
+ return host[COMPLETION_SHELL_STATE];
2163
+ }
2164
+ function registerCompletionShell(ctx, shell) {
2165
+ const state = getCompletionShellState(ctx);
2166
+ if (state.shell !== undefined && state.shell !== shell) {
2167
+ throw new CommanderError('OptionConflict', 'options "--bash", "--fish", and "--pwsh" are mutually exclusive', getCommandPath(ctx));
2168
+ }
2169
+ state.shell = shell;
2170
+ }
2171
+ function mustGetCompletionShell(ctx) {
2172
+ const state = getCompletionShellState(ctx);
2173
+ if (state.shell === undefined) {
2174
+ throw new CommanderError('MissingRequired', 'missing required option: one of "--bash", "--fish", or "--pwsh"', getCommandPath(ctx));
2175
+ }
2176
+ return state.shell;
2177
+ }
2026
2178
  class CompletionCommand extends Command {
2027
2179
  constructor(root, config = {}) {
2028
2180
  const programName = config.programName ?? root.name ?? 'program';
@@ -2036,45 +2188,45 @@ class CompletionCommand extends Command {
2036
2188
  type: 'boolean',
2037
2189
  args: 'none',
2038
2190
  desc: 'Generate Bash completion script',
2191
+ apply: (value, ctx) => {
2192
+ if (value === true) {
2193
+ registerCompletionShell(ctx, 'bash');
2194
+ }
2195
+ },
2039
2196
  })
2040
2197
  .option({
2041
2198
  long: 'fish',
2042
2199
  type: 'boolean',
2043
2200
  args: 'none',
2044
2201
  desc: 'Generate Fish completion script',
2202
+ apply: (value, ctx) => {
2203
+ if (value === true) {
2204
+ registerCompletionShell(ctx, 'fish');
2205
+ }
2206
+ },
2045
2207
  })
2046
2208
  .option({
2047
2209
  long: 'pwsh',
2048
2210
  type: 'boolean',
2049
2211
  args: 'none',
2050
2212
  desc: 'Generate PowerShell completion script',
2213
+ apply: (value, ctx) => {
2214
+ if (value === true) {
2215
+ registerCompletionShell(ctx, 'pwsh');
2216
+ }
2217
+ mustGetCompletionShell(ctx);
2218
+ },
2051
2219
  })
2052
2220
  .option({
2053
2221
  long: 'write',
2054
2222
  short: 'w',
2055
2223
  type: 'string',
2056
- args: 'required',
2057
- desc: 'Write to file (use shell default path if empty)',
2058
- default: undefined,
2224
+ args: 'optional',
2225
+ desc: 'Write to file (use shell default path when value is omitted or empty)',
2059
2226
  })
2060
- .action(({ opts }) => {
2227
+ .action(({ opts, ctx }) => {
2061
2228
  const meta = root.getCompletionMeta();
2062
- const selectedShells = [
2063
- opts['bash'] && 'bash',
2064
- opts['fish'] && 'fish',
2065
- opts['pwsh'] && 'pwsh',
2066
- ].filter(Boolean);
2067
- if (selectedShells.length === 0) {
2068
- console.error('Please specify a shell: --bash, --fish, or --pwsh');
2069
- process.exit(1);
2070
- return;
2071
- }
2072
- if (selectedShells.length > 1) {
2073
- console.error('Please specify only one shell option');
2074
- process.exit(1);
2075
- return;
2076
- }
2077
- const shell = selectedShells[0];
2229
+ const shell = mustGetCompletionShell(ctx);
2078
2230
  let script;
2079
2231
  switch (shell) {
2080
2232
  case 'bash':
@@ -2087,8 +2239,9 @@ class CompletionCommand extends Command {
2087
2239
  script = new PwshCompletion(meta, programName).generate();
2088
2240
  break;
2089
2241
  }
2090
- const writeOpt = opts['write'];
2091
- if (writeOpt !== undefined) {
2242
+ const hasWrite = Object.prototype.hasOwnProperty.call(opts, 'write');
2243
+ if (hasWrite) {
2244
+ const writeOpt = opts['write'];
2092
2245
  const filePath = typeof writeOpt === 'string' && writeOpt !== '' ? writeOpt : paths[shell];
2093
2246
  const expandedPath = expandHome(filePath);
2094
2247
  const dir = path.dirname(expandedPath);
@@ -2155,7 +2308,7 @@ class BashCompletion {
2155
2308
  if (opt.short)
2156
2309
  optParts.push(this.#escapeWord(`-${opt.short}`));
2157
2310
  optParts.push(this.#escapeWord(`--${kebabLong}`));
2158
- if (!opt.takesValue) {
2311
+ if (canGenerateNegativeCompletion(opt)) {
2159
2312
  optParts.push(this.#escapeWord(`--no-${kebabLong}`));
2160
2313
  }
2161
2314
  }
@@ -2187,7 +2340,7 @@ class BashCompletion {
2187
2340
  return words.map(choice => this.#escapeWord(choice)).join(' ');
2188
2341
  }
2189
2342
  #appendChoiceLogicForCommand(lines, indent, cmd, depth) {
2190
- const valueOptions = cmd.options.filter(opt => opt.takesValue);
2343
+ const valueOptions = cmd.options.filter(optionTakesValue);
2191
2344
  const valueOptionsWithChoices = valueOptions.filter(opt => opt.choices && opt.choices.length > 0);
2192
2345
  const valueLongPatterns = valueOptions.map(opt => `--${camelToKebabCase(opt.long)}`);
2193
2346
  const valueShortPatterns = valueOptions
@@ -2314,7 +2467,7 @@ class FishCompletion {
2314
2467
  line += ` -xa '${opt.choices.map(choice => this.#escapeChoice(choice)).join(' ')}'`;
2315
2468
  }
2316
2469
  lines.push(line);
2317
- if (!opt.takesValue) {
2470
+ if (canGenerateNegativeCompletion(opt)) {
2318
2471
  let noLine = `complete -c ${this.#programName}`;
2319
2472
  if (condition)
2320
2473
  noLine += ` -n '${condition}'`;
@@ -2324,11 +2477,11 @@ class FishCompletion {
2324
2477
  }
2325
2478
  }
2326
2479
  const valueOptionLongs = cmd.options
2327
- .filter(opt => opt.takesValue)
2480
+ .filter(optionTakesValue)
2328
2481
  .map(opt => camelToKebabCase(opt.long))
2329
2482
  .join(',');
2330
2483
  const valueOptionShorts = cmd.options
2331
- .filter(opt => opt.takesValue && opt.short)
2484
+ .filter(opt => optionTakesValue(opt) && opt.short)
2332
2485
  .map(opt => opt.short)
2333
2486
  .join(',');
2334
2487
  const argCount = cmd.arguments.length;
@@ -2527,7 +2680,7 @@ class PwshCompletion {
2527
2680
  ' if ($token.StartsWith("--")) {',
2528
2681
  ' if ($token.Contains("=")) { continue }',
2529
2682
  ' foreach ($opt in $cmd.options) {',
2530
- ' if ($token -eq "--$($opt.long)" -and $opt.takesValue) {',
2683
+ ' if ($token -eq "--$($opt.long)" -and $opt.args -ne "none") {',
2531
2684
  ' $expectValue = $true',
2532
2685
  ' break',
2533
2686
  ' }',
@@ -2537,7 +2690,7 @@ class PwshCompletion {
2537
2690
  ' if ($token.StartsWith("-") -and $token -ne "-") {',
2538
2691
  ' if ($token.Length -eq 2) {',
2539
2692
  ' foreach ($opt in $cmd.options) {',
2540
- ' if ($opt.short -and $token -eq "-$($opt.short)" -and $opt.takesValue) {',
2693
+ ' if ($opt.short -and $token -eq "-$($opt.short)" -and $opt.args -ne "none") {',
2541
2694
  ' $expectValue = $true',
2542
2695
  ' break',
2543
2696
  ' }',
@@ -2589,7 +2742,7 @@ class PwshCompletion {
2589
2742
  ' $opt.description',
2590
2743
  ' )',
2591
2744
  ' }',
2592
- ' if ($opt.isBoolean -and "--no-$($opt.long)" -like "$current*") {',
2745
+ ' if ($opt.canNegate -and "--no-$($opt.long)" -like "$current*") {',
2593
2746
  ' $completions += [System.Management.Automation.CompletionResult]::new(',
2594
2747
  ' "--no-$($opt.long)",',
2595
2748
  ' "no-$($opt.long)",',
@@ -2639,8 +2792,9 @@ class PwshCompletion {
2639
2792
  lines.push(`${indent} short = '${opt.short}'`);
2640
2793
  lines.push(`${indent} long = '${kebabLong}'`);
2641
2794
  lines.push(`${indent} description = '${this.#escape(opt.desc)}'`);
2642
- lines.push(`${indent} isBoolean = $${!opt.takesValue}`);
2643
- lines.push(`${indent} takesValue = $${opt.takesValue}`);
2795
+ lines.push(`${indent} type = '${opt.type}'`);
2796
+ lines.push(`${indent} args = '${opt.args}'`);
2797
+ lines.push(`${indent} canNegate = $${canGenerateNegativeCompletion(opt)}`);
2644
2798
  if (opt.choices) {
2645
2799
  lines.push(`${indent} choices = @('${opt.choices
2646
2800
  .map(choice => this.#escape(choice))
@@ -29,13 +29,14 @@ interface ICommandToken {
29
29
  /** Option value type */
30
30
  type ICommandOptionType = 'boolean' | 'number' | 'string';
31
31
  /** Option argument mode */
32
- type ICommandOptionArgs = 'none' | 'required' | 'variadic';
32
+ type ICommandOptionArgs = 'none' | 'required' | 'optional' | 'variadic';
33
33
  /**
34
34
  * Option configuration.
35
35
  *
36
36
  * `type` and `args` must be specified together. Valid combinations:
37
37
  * - boolean + none → boolean
38
38
  * - string + required → string
39
+ * - string + optional → string | undefined
39
40
  * - number + required → number
40
41
  * - string + variadic → string[]
41
42
  * - number + variadic → number[]
@@ -364,8 +365,10 @@ interface ICompletionOptionMeta {
364
365
  short?: string;
365
366
  /** Description */
366
367
  desc: string;
367
- /** Whether option takes value (args !== 'none') */
368
- takesValue: boolean;
368
+ /** Option type */
369
+ type: ICommandOptionType;
370
+ /** Option args mode */
371
+ args: ICommandOptionArgs;
369
372
  /** Allowed values */
370
373
  choices?: string[];
371
374
  }