burgee 0.2.0 → 0.4.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.
- package/LICENSE +21 -0
- package/README.md +61 -9
- package/dist/brand.d.ts +62 -1
- package/dist/brand.js +73 -7
- package/dist/cli.d.ts +11 -0
- package/dist/cli.js +17 -1
- package/dist/{commander-argument.js → commander/argument.js} +4 -1
- package/dist/{commander-command.d.ts → commander/command.d.ts} +15 -5
- package/dist/{commander-command.js → commander/command.js} +175 -22
- package/dist/{commander-error.js → commander/error.js} +2 -0
- package/dist/{commander-help.d.ts → commander/help.d.ts} +3 -3
- package/dist/{commander-help.js → commander/help.js} +18 -1
- package/dist/{commander-option.d.ts → commander/option.d.ts} +1 -1
- package/dist/{commander-option.js → commander/option.js} +15 -1
- package/dist/commander.d.ts +8 -8
- package/dist/commander.js +8 -8
- package/dist/completions.js +15 -4
- package/dist/dev.d.ts +47 -0
- package/dist/dev.js +132 -0
- package/dist/execute.d.ts +22 -1
- package/dist/execute.js +75 -24
- package/dist/help.d.ts +19 -7
- package/dist/help.js +50 -21
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -1
- package/dist/manifest.d.ts +15 -0
- package/dist/manifest.js +12 -1
- package/dist/mcp.d.ts +14 -2
- package/dist/mcp.js +15 -2
- package/dist/runtime.d.ts +12 -0
- package/dist/runtime.js +7 -0
- package/dist/schema.d.ts +10 -0
- package/dist/schema.js +11 -0
- package/dist/testing-helpers.d.ts +18 -1
- package/dist/testing-helpers.js +35 -0
- package/dist/testing.d.ts +2 -2
- package/dist/testing.js +1 -1
- package/dist/unknown-option.d.ts +9 -0
- package/dist/unknown-option.js +27 -0
- package/dist/{yargs-burgee.d.ts → yargs/burgee.d.ts} +2 -2
- package/dist/{yargs-burgee.js → yargs/burgee.js} +15 -1
- package/dist/{yargs-cliui.js → yargs/cliui.js} +8 -0
- package/dist/{yargs-command.d.ts → yargs/command.d.ts} +5 -5
- package/dist/{yargs-command.js → yargs/command.js} +9 -2
- package/dist/{yargs-completion.d.ts → yargs/completion.d.ts} +3 -3
- package/dist/{yargs-completion.js → yargs/completion.js} +7 -2
- package/dist/{yargs-factory.d.ts → yargs/factory.d.ts} +8 -8
- package/dist/{yargs-factory.js → yargs/factory.js} +63 -13
- package/dist/{yargs-middleware.js → yargs/middleware.js} +6 -1
- package/dist/{yargs-shim.d.ts → yargs/shim.d.ts} +3 -3
- package/dist/{yargs-shim.js → yargs/shim.js} +24 -5
- package/dist/{yargs-usage.d.ts → yargs/usage.d.ts} +1 -1
- package/dist/{yargs-usage.js → yargs/usage.js} +9 -1
- package/dist/{yargs-utils.js → yargs/utils.js} +11 -0
- package/dist/{yargs-validation.d.ts → yargs/validation.d.ts} +1 -1
- package/dist/{yargs-validation.js → yargs/validation.js} +7 -1
- package/dist/{yargs-y18n.js → yargs/y18n.js} +6 -0
- package/dist/yargs-helpers.d.ts +1 -1
- package/dist/yargs-helpers.js +1 -1
- package/dist/yargs.d.ts +4 -4
- package/dist/yargs.js +5 -5
- package/package.json +3 -2
- /package/dist/{commander-argument.d.ts → commander/argument.d.ts} +0 -0
- /package/dist/{commander-error.d.ts → commander/error.d.ts} +0 -0
- /package/dist/{commander-suggest.d.ts → suggest.d.ts} +0 -0
- /package/dist/{commander-suggest.js → suggest.js} +0 -0
- /package/dist/{yargs-cliui.d.ts → yargs/cliui.d.ts} +0 -0
- /package/dist/{yargs-middleware.d.ts → yargs/middleware.d.ts} +0 -0
- /package/dist/{yargs-utils.d.ts → yargs/utils.d.ts} +0 -0
- /package/dist/{yargs-y18n.d.ts → yargs/y18n.d.ts} +0 -0
|
@@ -1,18 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* commander's `Command`, ported method for method from commander 15 and graded by
|
|
3
|
+
* commander's own suite through `compat-oracle`. The parse pipeline, the option
|
|
4
|
+
* grammar, every error string and exit code are commander's — that is what makes a
|
|
5
|
+
* user's existing program run unchanged (J2).
|
|
6
|
+
*
|
|
7
|
+
* burgee's additions sit beside it and never alter the default behaviour:
|
|
8
|
+
* - `manifest` projects the command tree, so plugins (`use`) and the generated
|
|
9
|
+
* surfaces read commander-syntax programs exactly like native ones (J7, J8);
|
|
10
|
+
* - `--json`, when the program has not declared that option itself, wraps the
|
|
11
|
+
* action's return value in the envelope (N-family);
|
|
12
|
+
* - `parse(argv, { stdout, stderr, exit })` injects the streams and the exit, and
|
|
13
|
+
* then reports through the E1 taxonomy — the harness's seam (T1).
|
|
14
|
+
*/
|
|
1
15
|
import childProcess from 'node:child_process';
|
|
2
16
|
import { EventEmitter } from 'node:events';
|
|
3
17
|
import fs from 'node:fs';
|
|
4
18
|
import path from 'node:path';
|
|
5
19
|
import process from 'node:process';
|
|
6
20
|
import { stripVTControlCharacters } from 'node:util';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import { suggestSimilar } from '
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
21
|
+
import { ExitCode } from '../exit-code.js';
|
|
22
|
+
import { Manifest } from '../manifest.js';
|
|
23
|
+
import { serveMcp } from '../mcp.js';
|
|
24
|
+
import { machineJson, schemaOf } from '../schema.js';
|
|
25
|
+
import { suggestSimilar } from '../suggest.js';
|
|
26
|
+
import { Argument, humanReadableArgName } from './argument.js';
|
|
27
|
+
import { CommanderError } from './error.js';
|
|
28
|
+
import { Help } from './help.js';
|
|
29
|
+
import { DualOptions, Option } from './option.js';
|
|
30
|
+
/** Names that would reach Object.prototype if used as an option key. */
|
|
16
31
|
const POLLUTING = new Set(['__proto__', 'constructor', 'prototype']);
|
|
17
32
|
const ENV_SOURCES = ['default', 'config', 'env'];
|
|
18
33
|
const IMPLIED_SOURCES = ['default', 'implied'];
|
|
@@ -20,10 +35,12 @@ const HOOK_EVENTS = ['preSubcommand', 'preAction', 'postAction'];
|
|
|
20
35
|
const HELP_POSITIONS = ['beforeAll', 'before', 'after', 'afterAll'];
|
|
21
36
|
const SOURCE_EXT = ['.js', '.ts', '.tsx', '.mjs', '.cjs'];
|
|
22
37
|
const FORWARDED_SIGNALS = ['SIGUSR1', 'SIGUSR2', 'SIGTERM', 'SIGINT', 'SIGHUP'];
|
|
38
|
+
/** Wraps in single quotes. Used where a message says "from" right before a quoted method name, which a static import scanner would otherwise read as a specifier. */
|
|
23
39
|
const quoted = (s) => `'${s}'`;
|
|
24
40
|
function isThenable(value) {
|
|
25
41
|
return typeof value?.then === 'function';
|
|
26
42
|
}
|
|
43
|
+
/** commander's exit codes, read through burgee's taxonomy when the exit is injected (E1). */
|
|
27
44
|
function e1(err) {
|
|
28
45
|
switch (err.code) {
|
|
29
46
|
case 'commander.helpDisplayed':
|
|
@@ -38,6 +55,7 @@ function e1(err) {
|
|
|
38
55
|
return err.exitCode === 1 ? ExitCode.USAGE : err.exitCode;
|
|
39
56
|
}
|
|
40
57
|
}
|
|
58
|
+
/** What a run prints for an action's return value when the streams are injected. */
|
|
41
59
|
function render(value) {
|
|
42
60
|
if (value === undefined || value === null)
|
|
43
61
|
return '';
|
|
@@ -55,9 +73,12 @@ export class Command extends EventEmitter {
|
|
|
55
73
|
options = [];
|
|
56
74
|
parent = null;
|
|
57
75
|
registeredArguments = [];
|
|
76
|
+
/** @deprecated old name for registeredArguments */
|
|
58
77
|
_args;
|
|
78
|
+
/** cli args with options removed */
|
|
59
79
|
args = [];
|
|
60
80
|
rawArgs = [];
|
|
81
|
+
/** like .args but after custom processing and collecting variadic */
|
|
61
82
|
processedArgs = [];
|
|
62
83
|
runningCommand = undefined;
|
|
63
84
|
_allowUnknownOption = false;
|
|
@@ -86,6 +107,7 @@ export class Command extends EventEmitter {
|
|
|
86
107
|
_savedState = null;
|
|
87
108
|
_outputConfiguration;
|
|
88
109
|
_hidden = false;
|
|
110
|
+
/** Lazy created on demand; null once disabled. */
|
|
89
111
|
_helpOption = undefined;
|
|
90
112
|
_addImplicitHelpCommand = undefined;
|
|
91
113
|
_helpCommand = undefined;
|
|
@@ -96,8 +118,14 @@ export class Command extends EventEmitter {
|
|
|
96
118
|
_version = undefined;
|
|
97
119
|
_versionOptionName = undefined;
|
|
98
120
|
_usage = undefined;
|
|
121
|
+
/** burgee: the root's projection, created on first use. */
|
|
99
122
|
_manifest = undefined;
|
|
123
|
+
/** burgee: what this command does to the world (N6); declaring it exposes the command as an MCP tool. */
|
|
100
124
|
_effects = undefined;
|
|
125
|
+
/** burgee: `true`, or the replacement's name (M5). Shown in help, schema and a one-line warning on use. */
|
|
126
|
+
_deprecated = undefined;
|
|
127
|
+
_deprecationWarned = false;
|
|
128
|
+
/** burgee: set for the duration of a parse that injected the streams or the exit. */
|
|
101
129
|
_burgee = undefined;
|
|
102
130
|
constructor(name) {
|
|
103
131
|
super();
|
|
@@ -114,6 +142,7 @@ export class Command extends EventEmitter {
|
|
|
114
142
|
stripColor: (str) => stripVTControlCharacters(str),
|
|
115
143
|
};
|
|
116
144
|
}
|
|
145
|
+
/** Copy settings useful to share between the root and its subcommands. */
|
|
117
146
|
copyInheritedSettings(sourceCommand) {
|
|
118
147
|
this._outputConfiguration = sourceCommand._outputConfiguration;
|
|
119
148
|
this._helpOption = sourceCommand._helpOption;
|
|
@@ -161,6 +190,7 @@ export class Command extends EventEmitter {
|
|
|
161
190
|
return this;
|
|
162
191
|
return cmd;
|
|
163
192
|
}
|
|
193
|
+
/** Factory for an unattached command; override to customise subcommands. */
|
|
164
194
|
createCommand(name) {
|
|
165
195
|
return new Command(name);
|
|
166
196
|
}
|
|
@@ -232,6 +262,7 @@ export class Command extends EventEmitter {
|
|
|
232
262
|
this.registeredArguments.push(argument);
|
|
233
263
|
return this;
|
|
234
264
|
}
|
|
265
|
+
/** Customise or disable the default help command (added by default when there are subcommands). */
|
|
235
266
|
helpCommand(enableOrNameAndArgs, description) {
|
|
236
267
|
if (typeof enableOrNameAndArgs === 'boolean') {
|
|
237
268
|
this._addImplicitHelpCommand = enableOrNameAndArgs;
|
|
@@ -288,21 +319,25 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
288
319
|
this._lifeCycleHooks[event] = [listener];
|
|
289
320
|
return this;
|
|
290
321
|
}
|
|
322
|
+
/** Replace the call to process.exit; defaults to throwing the CommanderError. */
|
|
291
323
|
exitOverride(fn) {
|
|
292
324
|
this._exitCallback =
|
|
293
325
|
fn ??
|
|
294
326
|
((err) => {
|
|
295
327
|
if (err.code !== 'commander.executeSubCommandAsync')
|
|
296
328
|
throw err;
|
|
329
|
+
// Async callback from spawn events, not useful to throw.
|
|
297
330
|
});
|
|
298
331
|
return this;
|
|
299
332
|
}
|
|
300
333
|
_exit(exitCode, code, message) {
|
|
301
334
|
if (this._exitCallback) {
|
|
302
335
|
this._exitCallback(new CommanderError(exitCode, code, message));
|
|
336
|
+
// Expecting this line is not reached.
|
|
303
337
|
}
|
|
304
338
|
process.exit(exitCode);
|
|
305
339
|
}
|
|
340
|
+
// commander's contract: the positional args, then the options, then the command itself.
|
|
306
341
|
action(fn) {
|
|
307
342
|
const listener = (args) => {
|
|
308
343
|
const expectedArgsCount = this.registeredArguments.length;
|
|
@@ -317,6 +352,7 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
317
352
|
createOption(flags, description) {
|
|
318
353
|
return new Option(flags, description);
|
|
319
354
|
}
|
|
355
|
+
/** Wrap parseArg to turn `commander.invalidArgument` into an error with context. */
|
|
320
356
|
_callParseArg(target, value, previous, invalidArgumentMessage) {
|
|
321
357
|
try {
|
|
322
358
|
return target.parseArg?.(value, previous);
|
|
@@ -364,6 +400,7 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
364
400
|
if (option.defaultValue !== undefined) {
|
|
365
401
|
this.setOptionValueWithSource(name, option.defaultValue, 'default');
|
|
366
402
|
}
|
|
403
|
+
// val is null for an optional option used without its argument, undefined for boolean and negated.
|
|
367
404
|
const handleOptionValue = (val, invalidValueMessage, valueSource) => {
|
|
368
405
|
let value = val;
|
|
369
406
|
if (value == null && option.presetArg !== undefined)
|
|
@@ -381,7 +418,7 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
381
418
|
else if (option.isBoolean() || option.optional)
|
|
382
419
|
value = true;
|
|
383
420
|
else
|
|
384
|
-
value = '';
|
|
421
|
+
value = ''; // not normal, parseArg might have failed or be a mock function for testing
|
|
385
422
|
}
|
|
386
423
|
this.setOptionValueWithSource(name, value, valueSource);
|
|
387
424
|
};
|
|
@@ -405,6 +442,7 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
405
442
|
option.default(defaultValue).argParser(fn);
|
|
406
443
|
}
|
|
407
444
|
else if (fn instanceof RegExp) {
|
|
445
|
+
// deprecated
|
|
408
446
|
const regex = fn;
|
|
409
447
|
option.default(defaultValue).argParser((val, def) => {
|
|
410
448
|
const m = regex.exec(val);
|
|
@@ -422,6 +460,7 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
422
460
|
requiredOption(flags, description, parseArg, defaultValue) {
|
|
423
461
|
return this._optionEx({ mandatory: true }, flags, description, parseArg, defaultValue);
|
|
424
462
|
}
|
|
463
|
+
/** `-f80` as `--flag=80` (default) versus `-fb` as `-f -b`. */
|
|
425
464
|
combineFlagAndOptionalValue(combine = true) {
|
|
426
465
|
this._combineFlagAndOptionalValue = !!combine;
|
|
427
466
|
return this;
|
|
@@ -434,10 +473,12 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
434
473
|
this._allowExcessArguments = !!allowExcess;
|
|
435
474
|
return this;
|
|
436
475
|
}
|
|
476
|
+
/** Global options before subcommands only, so subcommands may reuse option names. */
|
|
437
477
|
enablePositionalOptions(positional = true) {
|
|
438
478
|
this._enablePositionalOptions = !!positional;
|
|
439
479
|
return this;
|
|
440
480
|
}
|
|
481
|
+
/** Options after the first command-argument are passed through, not parsed. */
|
|
441
482
|
passThroughOptions(passThrough = true) {
|
|
442
483
|
this._passThroughOptions = !!passThrough;
|
|
443
484
|
this._checkForBrokenPassThrough();
|
|
@@ -464,6 +505,7 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
464
505
|
setOptionValue(key, value) {
|
|
465
506
|
return this.setOptionValueWithSource(key, value, undefined);
|
|
466
507
|
}
|
|
508
|
+
/** `source` is default | config | env | cli | implied. */
|
|
467
509
|
setOptionValueWithSource(key, value, source) {
|
|
468
510
|
if (this._storeOptionsAsProperties)
|
|
469
511
|
this[key] = value;
|
|
@@ -475,6 +517,7 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
475
517
|
getOptionValueSource(key) {
|
|
476
518
|
return this._optionValueSources[key];
|
|
477
519
|
}
|
|
520
|
+
/** Globals overwrite locals, like optsWithGlobals. */
|
|
478
521
|
getOptionValueSourceWithGlobals(key) {
|
|
479
522
|
let source;
|
|
480
523
|
for (const cmd of this._getCommandAndAncestors()) {
|
|
@@ -483,6 +526,7 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
483
526
|
}
|
|
484
527
|
return source;
|
|
485
528
|
}
|
|
529
|
+
/** User args from argv per `from`; sets `_scriptPath` and the default program name. */
|
|
486
530
|
_prepareUserArgs(argv, parseOptions) {
|
|
487
531
|
if (argv !== undefined && !Array.isArray(argv))
|
|
488
532
|
throw new Error('first parameter to parse must be array or undefined');
|
|
@@ -492,7 +536,7 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
492
536
|
parseOptions.from = 'electron';
|
|
493
537
|
const execArgv = process.execArgv ?? [];
|
|
494
538
|
if (execArgv.includes('-e') || execArgv.includes('--eval') || execArgv.includes('-p') || execArgv.includes('--print')) {
|
|
495
|
-
parseOptions.from = 'eval';
|
|
539
|
+
parseOptions.from = 'eval'; // internal usage, not documented
|
|
496
540
|
}
|
|
497
541
|
}
|
|
498
542
|
if (argv === undefined)
|
|
@@ -528,10 +572,17 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
528
572
|
this._name = this._name || 'program';
|
|
529
573
|
return userArgs;
|
|
530
574
|
}
|
|
575
|
+
/**
|
|
576
|
+
* Parse argv, set options and run commands. Use `parseAsync` when an action is async.
|
|
577
|
+
* With no arguments, parses process.argv and auto-detects Electron and `node --eval`.
|
|
578
|
+
*/
|
|
531
579
|
parse(argv, parseOptions) {
|
|
532
580
|
const from = this._prepareBurgee(parseOptions);
|
|
533
581
|
this._prepareForParse();
|
|
534
582
|
const userArgs = this._prepareUserArgs(argv, from);
|
|
583
|
+
// The surface check is synchronous unless a surface is actually served (completions,
|
|
584
|
+
// --mcp), so a synchronous action has run by the time parse() returns — commander's
|
|
585
|
+
// contract, which its suite asserts on after every parse(). commander-sync.test.ts.
|
|
535
586
|
this._runBurgee(() => {
|
|
536
587
|
const served = this._burgeeSurface(userArgs);
|
|
537
588
|
if (isThenable(served))
|
|
@@ -544,6 +595,8 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
544
595
|
const from = this._prepareBurgee(parseOptions);
|
|
545
596
|
this._prepareForParse();
|
|
546
597
|
const userArgs = this._prepareUserArgs(argv, from);
|
|
598
|
+
// Same synchronous start as parse(): a preAction hook has run before the promise is
|
|
599
|
+
// handed back, which commander's hook tests assert on.
|
|
547
600
|
await this._runBurgee(() => {
|
|
548
601
|
const served = this._burgeeSurface(userArgs);
|
|
549
602
|
if (isThenable(served))
|
|
@@ -554,6 +607,7 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
554
607
|
}
|
|
555
608
|
_prepareForParse() {
|
|
556
609
|
if (this._savedState === null) {
|
|
610
|
+
// Lone negated option (--no-foo without --foo) defaults to true, now that all options are known.
|
|
557
611
|
for (const option of this.options) {
|
|
558
612
|
if (option.negate && option.defaultValue === undefined && this.getOptionValue(option.attributeName()) === undefined) {
|
|
559
613
|
const positiveLongFlag = (option.long ?? '').replace(/^--no-/, '--');
|
|
@@ -567,6 +621,7 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
567
621
|
this.restoreStateBeforeParse();
|
|
568
622
|
}
|
|
569
623
|
}
|
|
624
|
+
/** Called lazily on first parse; available for subclasses to save custom state. */
|
|
570
625
|
saveStateBeforeParse() {
|
|
571
626
|
this._savedState = {
|
|
572
627
|
_name: this._name,
|
|
@@ -612,6 +667,7 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
612
667
|
const foundExt = SOURCE_EXT.find((ext) => fs.existsSync(`${localBin}${ext}`));
|
|
613
668
|
return foundExt ? `${localBin}${foundExt}` : undefined;
|
|
614
669
|
};
|
|
670
|
+
// Not checking for help first: can't robustly test for help flags in an external command.
|
|
615
671
|
this._checkForMissingMandatoryOptions();
|
|
616
672
|
this._checkForConflictingOptions();
|
|
617
673
|
let executableFile = subcommand._executableFile || `${this._name}-${subcommand._name}`;
|
|
@@ -628,6 +684,7 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
628
684
|
}
|
|
629
685
|
if (executableDir) {
|
|
630
686
|
let localFile = findFile(executableDir, executableFile);
|
|
687
|
+
// Legacy search using the script name as prefix instead of the command name.
|
|
631
688
|
if (!localFile && !subcommand._executableFile && this._scriptPath) {
|
|
632
689
|
const legacyName = path.basename(this._scriptPath, path.extname(this._scriptPath));
|
|
633
690
|
if (legacyName !== this._name)
|
|
@@ -654,6 +711,7 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
654
711
|
proc = childProcess.spawn(process.execPath, args, { stdio: 'inherit' });
|
|
655
712
|
}
|
|
656
713
|
if (!proc.killed) {
|
|
714
|
+
// Testing mainly to avoid leak warnings during unit tests with mocked spawn.
|
|
657
715
|
for (const signal of FORWARDED_SIGNALS) {
|
|
658
716
|
process.on(signal, () => {
|
|
659
717
|
if (proc.killed === false && proc.exitCode === null)
|
|
@@ -663,7 +721,7 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
663
721
|
}
|
|
664
722
|
const exitCallback = this._exitCallback;
|
|
665
723
|
proc.on('close', (code) => {
|
|
666
|
-
code = code ?? 1;
|
|
724
|
+
code = code ?? 1; // null when the spawned process terminated due to a signal
|
|
667
725
|
if (!exitCallback)
|
|
668
726
|
process.exit(code);
|
|
669
727
|
else
|
|
@@ -703,12 +761,14 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
703
761
|
});
|
|
704
762
|
return promiseChain;
|
|
705
763
|
}
|
|
764
|
+
/** `help foo`: invoke help directly if possible, or dispatch if necessary. */
|
|
706
765
|
_dispatchHelpCommand(subcommandName) {
|
|
707
766
|
if (!subcommandName)
|
|
708
767
|
this.help();
|
|
709
768
|
const subCommand = this._findCommand(subcommandName);
|
|
710
769
|
if (subCommand && !subCommand._executableHandler)
|
|
711
770
|
subCommand.help();
|
|
771
|
+
// Fallback to parsing the help flag to invoke the help.
|
|
712
772
|
return this._dispatchSubcommand(subcommandName ?? '', [], [
|
|
713
773
|
this._getHelpOption()?.long ?? this._getHelpOption()?.short ?? '--help',
|
|
714
774
|
]);
|
|
@@ -724,6 +784,7 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
724
784
|
if (this.args.length > this.registeredArguments.length)
|
|
725
785
|
this._excessArguments(this.args);
|
|
726
786
|
}
|
|
787
|
+
/** Process this.args against registeredArguments into this.processedArgs. */
|
|
727
788
|
_processArguments() {
|
|
728
789
|
const myParseArg = (argument, value, previous) => {
|
|
729
790
|
let parsedValue = value;
|
|
@@ -756,6 +817,7 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
756
817
|
});
|
|
757
818
|
this.processedArgs = processedArgs;
|
|
758
819
|
}
|
|
820
|
+
/** Chain once we have a promise; call synchronously until then. */
|
|
759
821
|
_chainOrCall(promise, fn) {
|
|
760
822
|
if (isThenable(promise))
|
|
761
823
|
return promise.then(() => fn());
|
|
@@ -782,9 +844,10 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
782
844
|
}
|
|
783
845
|
return result;
|
|
784
846
|
}
|
|
847
|
+
/** Process arguments in the context of this command; returns the action result in case it is a promise. */
|
|
785
848
|
_parseCommand(operands, unknown) {
|
|
786
849
|
const parsed = this.parseOptions(unknown);
|
|
787
|
-
this._parseOptionsEnv();
|
|
850
|
+
this._parseOptionsEnv(); // after cli, so parseArg not called on both cli and env
|
|
788
851
|
this._parseOptionsImplied();
|
|
789
852
|
operands = operands.concat(parsed.operands);
|
|
790
853
|
unknown = parsed.unknown;
|
|
@@ -799,15 +862,17 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
799
862
|
return this._dispatchHelpCommand(operands[1]);
|
|
800
863
|
}
|
|
801
864
|
if (this._defaultCommandName) {
|
|
802
|
-
this._outputHelpIfRequested(unknown);
|
|
865
|
+
this._outputHelpIfRequested(unknown); // help for the default command comes from the parent
|
|
803
866
|
return this._dispatchSubcommand(this._defaultCommandName, operands, unknown);
|
|
804
867
|
}
|
|
805
868
|
if (this.commands.length && this.args.length === 0 && !this._actionHandler && !this._defaultCommandName) {
|
|
869
|
+
// probably missing subcommand and no handler, user needs help (and exit)
|
|
806
870
|
this.help({ error: true });
|
|
807
871
|
}
|
|
808
872
|
this._outputHelpIfRequested(parsed.unknown);
|
|
809
873
|
this._checkForMissingMandatoryOptions();
|
|
810
874
|
this._checkForConflictingOptions();
|
|
875
|
+
// Not always called, to avoid masking a "better" error like unknown command.
|
|
811
876
|
const checkForUnknownOptions = () => {
|
|
812
877
|
if (parsed.unknown.length > 0)
|
|
813
878
|
this.unknownOption(parsed.unknown[0] ?? '');
|
|
@@ -821,7 +886,7 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
821
886
|
promiseChain = this._chainOrCall(promiseChain, () => this._runAction());
|
|
822
887
|
if (this.parent) {
|
|
823
888
|
promiseChain = this._chainOrCall(promiseChain, () => {
|
|
824
|
-
this.parent?.emit(commandEvent, operands, unknown);
|
|
889
|
+
this.parent?.emit(commandEvent, operands, unknown); // legacy
|
|
825
890
|
});
|
|
826
891
|
}
|
|
827
892
|
promiseChain = this._chainOrCallHooks(promiseChain, 'postAction');
|
|
@@ -830,13 +895,15 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
830
895
|
if (this.parent?.listenerCount(commandEvent)) {
|
|
831
896
|
checkForUnknownOptions();
|
|
832
897
|
this._processArguments();
|
|
833
|
-
this.parent.emit(commandEvent, operands, unknown);
|
|
898
|
+
this.parent.emit(commandEvent, operands, unknown); // legacy
|
|
834
899
|
}
|
|
835
900
|
else if (operands.length) {
|
|
836
901
|
if (this._findCommand('*')) {
|
|
902
|
+
// legacy default command
|
|
837
903
|
return this._dispatchSubcommand('*', operands, unknown);
|
|
838
904
|
}
|
|
839
905
|
if (this.listenerCount('command:*')) {
|
|
906
|
+
// skip option check, emit event for possible misspelling suggestion
|
|
840
907
|
this.emit('command:*', operands, unknown);
|
|
841
908
|
}
|
|
842
909
|
else if (this.commands.length) {
|
|
@@ -849,11 +916,13 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
849
916
|
}
|
|
850
917
|
else if (this.commands.length) {
|
|
851
918
|
checkForUnknownOptions();
|
|
919
|
+
// This command has subcommands and nothing hooked up at this level, so display help (and exit).
|
|
852
920
|
this.help({ error: true });
|
|
853
921
|
}
|
|
854
922
|
else {
|
|
855
923
|
checkForUnknownOptions();
|
|
856
924
|
this._processArguments();
|
|
925
|
+
// fall through for caller to handle after calling .parse()
|
|
857
926
|
}
|
|
858
927
|
return undefined;
|
|
859
928
|
}
|
|
@@ -865,6 +934,7 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
865
934
|
_findOption(arg) {
|
|
866
935
|
return this.options.find((option) => option.is(arg));
|
|
867
936
|
}
|
|
937
|
+
/** Walks up the hierarchy so a subcommand can check after displaying help. */
|
|
868
938
|
_checkForMissingMandatoryOptions() {
|
|
869
939
|
for (const cmd of this._getCommandAndAncestors()) {
|
|
870
940
|
for (const anOption of cmd.options) {
|
|
@@ -892,6 +962,15 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
892
962
|
for (const cmd of this._getCommandAndAncestors())
|
|
893
963
|
cmd._checkForConflictingLocalOptions();
|
|
894
964
|
}
|
|
965
|
+
/**
|
|
966
|
+
* Parse options from `args`, removing known options, and return argv split into
|
|
967
|
+
* operands and unknown arguments. Side effect: stores option values on the command.
|
|
968
|
+
*
|
|
969
|
+
* --known kkk op => [op], []
|
|
970
|
+
* op --known kkk => [op], []
|
|
971
|
+
* sub --unknown uuu op => [sub], [--unknown uuu op]
|
|
972
|
+
* sub -- --unknown uuu op => [sub --unknown uuu op], []
|
|
973
|
+
*/
|
|
895
974
|
parseOptions(args) {
|
|
896
975
|
const operands = [];
|
|
897
976
|
const unknown = [];
|
|
@@ -900,10 +979,11 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
900
979
|
const negativeNumberArg = (arg) => {
|
|
901
980
|
if (!/^-(\d+|\d*\.\d+)(e[+-]?\d+)?$/.test(arg))
|
|
902
981
|
return false;
|
|
982
|
+
// a negative number is ok unless a digit is used as an option in the command hierarchy
|
|
903
983
|
return !this._getCommandAndAncestors().some((cmd) => cmd.options.some((opt) => /^-\d$/.test(opt.short ?? '')));
|
|
904
984
|
};
|
|
905
985
|
let activeVariadicOption = null;
|
|
906
|
-
let activeGroup = null;
|
|
986
|
+
let activeGroup = null; // working through a group of short options, like -abc
|
|
907
987
|
let i = 0;
|
|
908
988
|
while (i < args.length || activeGroup) {
|
|
909
989
|
const arg = activeGroup ?? args[i++] ?? '';
|
|
@@ -930,6 +1010,7 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
930
1010
|
}
|
|
931
1011
|
else if (option.optional) {
|
|
932
1012
|
let value = null;
|
|
1013
|
+
// historical behaviour: the optional value is the following arg unless it is an option
|
|
933
1014
|
const next = args[i];
|
|
934
1015
|
if (i < args.length && next !== undefined && (!maybeOption(next) || negativeNumberArg(next))) {
|
|
935
1016
|
value = next;
|
|
@@ -944,6 +1025,7 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
944
1025
|
continue;
|
|
945
1026
|
}
|
|
946
1027
|
}
|
|
1028
|
+
// Combined short options: eat the first one if known.
|
|
947
1029
|
if (arg.length > 2 && arg[0] === '-' && arg[1] !== '-') {
|
|
948
1030
|
const option = this._findOption(`-${arg[1]}`);
|
|
949
1031
|
if (option) {
|
|
@@ -957,6 +1039,7 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
957
1039
|
continue;
|
|
958
1040
|
}
|
|
959
1041
|
}
|
|
1042
|
+
// Known long flag with value, like --foo=bar
|
|
960
1043
|
if (/^--[^=]+=/.test(arg)) {
|
|
961
1044
|
const index = arg.indexOf('=');
|
|
962
1045
|
const option = this._findOption(arg.slice(0, index));
|
|
@@ -965,9 +1048,13 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
965
1048
|
continue;
|
|
966
1049
|
}
|
|
967
1050
|
}
|
|
1051
|
+
// Not recognised by this command: command-argument, subcommand option, unknown option, or help.
|
|
1052
|
+
// An unknown option makes everything after it unknown too, for a subcommand to reprocess.
|
|
1053
|
+
// A negative number in a leaf command is not an unknown option.
|
|
968
1054
|
if (dest === operands && maybeOption(arg) && !(this.commands.length === 0 && negativeNumberArg(arg))) {
|
|
969
1055
|
dest = unknown;
|
|
970
1056
|
}
|
|
1057
|
+
// Positional options: stop processing our options at a subcommand.
|
|
971
1058
|
if ((this._enablePositionalOptions || this._passThroughOptions) && operands.length === 0 && unknown.length === 0) {
|
|
972
1059
|
if (this._findCommand(arg)) {
|
|
973
1060
|
operands.push(arg);
|
|
@@ -983,6 +1070,7 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
983
1070
|
break;
|
|
984
1071
|
}
|
|
985
1072
|
}
|
|
1073
|
+
// Pass-through options: stop processing options at the first command-argument.
|
|
986
1074
|
if (this._passThroughOptions) {
|
|
987
1075
|
dest.push(arg, ...args.slice(i));
|
|
988
1076
|
break;
|
|
@@ -991,6 +1079,7 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
991
1079
|
}
|
|
992
1080
|
return { operands, unknown };
|
|
993
1081
|
}
|
|
1082
|
+
/** Local option values as key-value pairs. */
|
|
994
1083
|
opts() {
|
|
995
1084
|
if (this._storeOptionsAsProperties) {
|
|
996
1085
|
const result = {};
|
|
@@ -1002,9 +1091,11 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
1002
1091
|
}
|
|
1003
1092
|
return this._optionValues;
|
|
1004
1093
|
}
|
|
1094
|
+
/** Merged local and global option values; globals overwrite locals. */
|
|
1005
1095
|
optsWithGlobals() {
|
|
1006
1096
|
return this._getCommandAndAncestors().reduce((combined, cmd) => Object.assign(combined, cmd.opts()), {});
|
|
1007
1097
|
}
|
|
1098
|
+
/** Display an error message and exit (or call exitOverride). */
|
|
1008
1099
|
error(message, errorOptions) {
|
|
1009
1100
|
this._outputConfiguration.outputError(`${message}\n`, this._outputConfiguration.writeErr);
|
|
1010
1101
|
if (typeof this._showHelpAfterError === 'string') {
|
|
@@ -1019,10 +1110,12 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
1019
1110
|
const code = config.code || 'commander.error';
|
|
1020
1111
|
this._exit(exitCode, code, message);
|
|
1021
1112
|
}
|
|
1113
|
+
/** Apply environment variables to options that have no value from the cli or client code. */
|
|
1022
1114
|
_parseOptionsEnv() {
|
|
1023
1115
|
for (const option of this.options) {
|
|
1024
1116
|
if (option.envVar && option.envVar in process.env) {
|
|
1025
1117
|
const optionKey = option.attributeName();
|
|
1118
|
+
// Do not overwrite cli values or values from an unknown (client-code) source.
|
|
1026
1119
|
if (this.getOptionValue(optionKey) === undefined || ENV_SOURCES.includes(this.getOptionValueSource(optionKey) ?? '')) {
|
|
1027
1120
|
if (option.required || option.optional)
|
|
1028
1121
|
this.emit(`optionEnv:${option.name()}`, process.env[option.envVar]);
|
|
@@ -1032,6 +1125,7 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
1032
1125
|
}
|
|
1033
1126
|
}
|
|
1034
1127
|
}
|
|
1128
|
+
/** Apply implied option values where the option is undefined or at its default. */
|
|
1035
1129
|
_parseOptionsImplied() {
|
|
1036
1130
|
const dualHelper = new DualOptions(this.options);
|
|
1037
1131
|
const hasCustomOptionValue = (optionKey) => this.getOptionValue(optionKey) !== undefined && !IMPLIED_SOURCES.includes(this.getOptionValueSource(optionKey) ?? '');
|
|
@@ -1055,6 +1149,7 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
1055
1149
|
this.error(`error: required option '${option.flags}' not specified`, { code: 'commander.missingMandatoryOptionValue' });
|
|
1056
1150
|
}
|
|
1057
1151
|
_conflictingOption(option, conflictingOption) {
|
|
1152
|
+
// The caller does not know whether a negated option is the source of the value; take an educated guess.
|
|
1058
1153
|
const findBestOptionFromValue = (candidate) => {
|
|
1059
1154
|
const optionKey = candidate.attributeName();
|
|
1060
1155
|
const optionValue = this.getOptionValue(optionKey);
|
|
@@ -1082,6 +1177,7 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
1082
1177
|
return;
|
|
1083
1178
|
let suggestion = '';
|
|
1084
1179
|
if (flag.startsWith('--') && this._showSuggestionAfterError) {
|
|
1180
|
+
// Looping to pick up the global options too.
|
|
1085
1181
|
let candidateFlags = [];
|
|
1086
1182
|
let command = this;
|
|
1087
1183
|
do {
|
|
@@ -1158,6 +1254,7 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
1158
1254
|
let command = this;
|
|
1159
1255
|
const last = this.commands[this.commands.length - 1];
|
|
1160
1256
|
if (this.commands.length !== 0 && last?._executableHandler) {
|
|
1257
|
+
// assume adding an alias for the last added executable subcommand, rather than this
|
|
1161
1258
|
command = last;
|
|
1162
1259
|
}
|
|
1163
1260
|
if (alias === command._name)
|
|
@@ -1221,6 +1318,7 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
1221
1318
|
if (this._defaultCommandGroup && !cmd.helpGroup())
|
|
1222
1319
|
cmd.helpGroup(this._defaultCommandGroup);
|
|
1223
1320
|
}
|
|
1321
|
+
/** Name the command from a script filename, such as process.argv[1] or import.meta.filename. */
|
|
1224
1322
|
nameFromFilename(filename) {
|
|
1225
1323
|
this._name = path.basename(filename, path.extname(filename));
|
|
1226
1324
|
return this;
|
|
@@ -1267,6 +1365,7 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
1267
1365
|
};
|
|
1268
1366
|
return { error, write, hasColors, helpWidth };
|
|
1269
1367
|
}
|
|
1368
|
+
/** Output built-in help plus any text added with `addHelpText`. */
|
|
1270
1369
|
outputHelp(contextOptions) {
|
|
1271
1370
|
let deprecatedCallback;
|
|
1272
1371
|
if (typeof contextOptions === 'function') {
|
|
@@ -1288,16 +1387,17 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
1288
1387
|
outputContext.write(String(helpInformation));
|
|
1289
1388
|
const helpLong = this._getHelpOption()?.long;
|
|
1290
1389
|
if (helpLong)
|
|
1291
|
-
this.emit(helpLong);
|
|
1390
|
+
this.emit(helpLong); // deprecated
|
|
1292
1391
|
this.emit('afterHelp', eventContext);
|
|
1293
1392
|
for (const command of this._getCommandAndAncestors())
|
|
1294
1393
|
command.emit('afterAllHelp', eventContext);
|
|
1295
1394
|
}
|
|
1395
|
+
/** Customise the built-in help option, or pass false to disable it. */
|
|
1296
1396
|
helpOption(flags, description) {
|
|
1297
1397
|
if (typeof flags === 'boolean') {
|
|
1298
1398
|
if (flags) {
|
|
1299
1399
|
if (this._helpOption === null)
|
|
1300
|
-
this._helpOption = undefined;
|
|
1400
|
+
this._helpOption = undefined; // reenable
|
|
1301
1401
|
if (this._defaultOptionGroup) {
|
|
1302
1402
|
const helpOption = this._getHelpOption();
|
|
1303
1403
|
if (helpOption)
|
|
@@ -1305,7 +1405,7 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
1305
1405
|
}
|
|
1306
1406
|
}
|
|
1307
1407
|
else {
|
|
1308
|
-
this._helpOption = null;
|
|
1408
|
+
this._helpOption = null; // disable
|
|
1309
1409
|
}
|
|
1310
1410
|
return this;
|
|
1311
1411
|
}
|
|
@@ -1314,6 +1414,7 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
1314
1414
|
this._initOptionGroup(this._helpOption);
|
|
1315
1415
|
return this;
|
|
1316
1416
|
}
|
|
1417
|
+
/** Lazily created; null once disabled with `helpOption(false)`. */
|
|
1317
1418
|
_getHelpOption() {
|
|
1318
1419
|
if (this._helpOption === undefined)
|
|
1319
1420
|
this.helpOption(undefined, undefined);
|
|
@@ -1324,13 +1425,16 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
1324
1425
|
this._initOptionGroup(option);
|
|
1325
1426
|
return this;
|
|
1326
1427
|
}
|
|
1428
|
+
/** Output help and exit. */
|
|
1327
1429
|
help(contextOptions) {
|
|
1328
1430
|
this.outputHelp(contextOptions);
|
|
1329
1431
|
let exitCode = Number(process.exitCode ?? 0);
|
|
1330
1432
|
if (exitCode === 0 && contextOptions && typeof contextOptions !== 'function' && contextOptions.error)
|
|
1331
1433
|
exitCode = 1;
|
|
1434
|
+
// message: not all displayed text is available, so only a placeholder is passed.
|
|
1332
1435
|
this._exit(exitCode, 'commander.help', '(outputHelp)');
|
|
1333
1436
|
}
|
|
1437
|
+
/** Extra help text: 'before'/'after' for this command, 'beforeAll'/'afterAll' for its subcommands too. */
|
|
1334
1438
|
addHelpText(position, text) {
|
|
1335
1439
|
if (!HELP_POSITIONS.includes(position)) {
|
|
1336
1440
|
throw new Error(`Unexpected value for position to addHelpText.
|
|
@@ -1350,12 +1454,17 @@ Expecting one of '${HELP_POSITIONS.join("', '")}'`);
|
|
|
1350
1454
|
this._exit(0, 'commander.helpDisplayed', '(outputHelp)');
|
|
1351
1455
|
}
|
|
1352
1456
|
}
|
|
1457
|
+
// ───── burgee: the manifest projection, plugins, `--json` and the injected seam ─────
|
|
1353
1458
|
_root() {
|
|
1354
1459
|
let command = this;
|
|
1355
1460
|
while (command.parent)
|
|
1356
1461
|
command = command.parent;
|
|
1357
1462
|
return command;
|
|
1358
1463
|
}
|
|
1464
|
+
/**
|
|
1465
|
+
* The manifest every surface reads. Projected from the command tree on each access,
|
|
1466
|
+
* so it is never stale; plugin-contributed nodes are kept across projections.
|
|
1467
|
+
*/
|
|
1359
1468
|
get manifest() {
|
|
1360
1469
|
const root = this._root();
|
|
1361
1470
|
root._manifest ??= new Manifest();
|
|
@@ -1375,6 +1484,8 @@ Expecting one of '${HELP_POSITIONS.join("', '")}'`);
|
|
|
1375
1484
|
...(cmd._description ? { description: cmd._description } : {}),
|
|
1376
1485
|
...(cmd._summary ? { summary: cmd._summary } : {}),
|
|
1377
1486
|
...(cmd._effects === undefined ? {} : { effects: cmd._effects }),
|
|
1487
|
+
...(cmd._deprecated === undefined ? {} : { deprecated: cmd._deprecated }),
|
|
1488
|
+
...(cmd._helpGroupHeading === undefined || cmd._helpGroupHeading === '' ? {} : { group: cmd._helpGroupHeading }),
|
|
1378
1489
|
...(cmd._hidden ? { hidden: true } : {}),
|
|
1379
1490
|
options: cmd._optionSpecs(),
|
|
1380
1491
|
arguments: cmd.registeredArguments.map((a) => ({ name: a.name(), required: a.required, variadic: a.variadic, ...(a.description ? { description: a.description } : {}) })),
|
|
@@ -1385,6 +1496,7 @@ Expecting one of '${HELP_POSITIONS.join("', '")}'`);
|
|
|
1385
1496
|
};
|
|
1386
1497
|
visit(this, [rootName]);
|
|
1387
1498
|
}
|
|
1499
|
+
/** Options as the manifest describes them, on a null-prototype record. */
|
|
1388
1500
|
_optionSpecs() {
|
|
1389
1501
|
const specs = Object.create(null);
|
|
1390
1502
|
for (const option of this.options) {
|
|
@@ -1403,17 +1515,33 @@ Expecting one of '${HELP_POSITIONS.join("', '")}'`);
|
|
|
1403
1515
|
}
|
|
1404
1516
|
return specs;
|
|
1405
1517
|
}
|
|
1518
|
+
/** burgee: declare what the command does to the world (N6). This is what exposes it as an MCP tool (N2). */
|
|
1406
1519
|
effects(value) {
|
|
1407
1520
|
this._effects = value;
|
|
1408
1521
|
return this;
|
|
1409
1522
|
}
|
|
1523
|
+
/**
|
|
1524
|
+
* burgee: mark the command deprecated (M5). Help and `--schema` show it; running it prints
|
|
1525
|
+
* `warning: 'old' is deprecated, use 'new'` on stderr once and goes on, exit unchanged.
|
|
1526
|
+
*/
|
|
1527
|
+
deprecate(use) {
|
|
1528
|
+
this._deprecated = use ?? true;
|
|
1529
|
+
return this;
|
|
1530
|
+
}
|
|
1531
|
+
/**
|
|
1532
|
+
* burgee: `--schema` and `--mcp` on a commander-syntax program, from its manifest (J2).
|
|
1533
|
+
* Only when the program declares neither option itself; `--mcp` runs commands through
|
|
1534
|
+
* this very program with the streams captured, so tool results are the `--json` envelope.
|
|
1535
|
+
*/
|
|
1410
1536
|
_burgeeSurface(userArgs) {
|
|
1411
1537
|
const root = this._root();
|
|
1538
|
+
// Any command in the tree that declares the flag keeps it: the surface is additive only.
|
|
1412
1539
|
const declared = (flag, at = root) => at._findOption(flag) !== undefined || at.commands.some((sub) => declared(flag, sub));
|
|
1413
1540
|
const terminator = userArgs.indexOf('--');
|
|
1414
1541
|
const head = terminator === -1 ? userArgs : userArgs.slice(0, terminator);
|
|
1415
1542
|
if (head[0] === 'completion' && root._findCommand('completion') === undefined) {
|
|
1416
|
-
|
|
1543
|
+
// Loaded on this command only (K6), exactly as the engine does.
|
|
1544
|
+
return import('../completions.js').then(({ renderCompletion, renderFigSpec, SHELLS }) => {
|
|
1417
1545
|
const shell = head[1] ?? '';
|
|
1418
1546
|
if (shell === 'fig') {
|
|
1419
1547
|
root._outputConfiguration.writeOut(`${JSON.stringify(renderFigSpec(this.manifest), null, 2)}\n`);
|
|
@@ -1429,10 +1557,13 @@ Expecting one of '${HELP_POSITIONS.join("', '")}'`);
|
|
|
1429
1557
|
}
|
|
1430
1558
|
return this._burgeeSurfaceRest(head, declared);
|
|
1431
1559
|
}
|
|
1560
|
+
/** The surfaces after `completion`: `--schema` is synchronous, `--mcp` serves until stdin closes. */
|
|
1432
1561
|
_burgeeSurfaceRest(head, declared) {
|
|
1433
1562
|
const root = this._root();
|
|
1434
1563
|
if (head.includes('--schema') && !declared('--schema')) {
|
|
1435
|
-
|
|
1564
|
+
// R1, and the same escape hatch the engine has: `--schema` is burgee's surface, not
|
|
1565
|
+
// commander's, so it answers to E-floor byte discipline rather than to the host.
|
|
1566
|
+
root._outputConfiguration.writeOut(`${machineJson(schemaOf(this.manifest), head)}\n`);
|
|
1436
1567
|
return true;
|
|
1437
1568
|
}
|
|
1438
1569
|
if (head[0] === '--mcp' && !declared('--mcp')) {
|
|
@@ -1447,10 +1578,12 @@ Expecting one of '${HELP_POSITIONS.join("', '")}'`);
|
|
|
1447
1578
|
}
|
|
1448
1579
|
return false;
|
|
1449
1580
|
}
|
|
1581
|
+
/** Additive, and the point of the whole exercise: plugins commander has never had (#2505, unlanded). */
|
|
1450
1582
|
use(plugin) {
|
|
1451
1583
|
this.manifest.use(plugin);
|
|
1452
1584
|
return this;
|
|
1453
1585
|
}
|
|
1586
|
+
/** Inject the streams and the exit for one parse; returns commander's own parse options. */
|
|
1454
1587
|
_prepareBurgee(parseOptions) {
|
|
1455
1588
|
if (parseOptions === undefined)
|
|
1456
1589
|
return undefined;
|
|
@@ -1477,6 +1610,7 @@ Expecting one of '${HELP_POSITIONS.join("', '")}'`);
|
|
|
1477
1610
|
visit(root);
|
|
1478
1611
|
return from;
|
|
1479
1612
|
}
|
|
1613
|
+
/** In burgee mode the whole run settles to one E1 exit; otherwise commander's behaviour, untouched. */
|
|
1480
1614
|
_runBurgee(run) {
|
|
1481
1615
|
const root = this._root();
|
|
1482
1616
|
const burgee = root._burgee;
|
|
@@ -1513,6 +1647,7 @@ Expecting one of '${HELP_POSITIONS.join("', '")}'`);
|
|
|
1513
1647
|
return undefined;
|
|
1514
1648
|
}
|
|
1515
1649
|
}
|
|
1650
|
+
/** `--json` that no command in the chain declared is burgee's envelope, not an unknown option. */
|
|
1516
1651
|
_takeJson(unknown) {
|
|
1517
1652
|
const terminator = unknown.indexOf('--');
|
|
1518
1653
|
const index = unknown.indexOf('--json');
|
|
@@ -1525,10 +1660,12 @@ Expecting one of '${HELP_POSITIONS.join("', '")}'`);
|
|
|
1525
1660
|
root._burgee ??= { exit: undefined, json: false };
|
|
1526
1661
|
root._burgee.json = true;
|
|
1527
1662
|
}
|
|
1663
|
+
/** The action, wrapped in the plugin hooks and followed by the envelope or the rendering. */
|
|
1528
1664
|
_runAction() {
|
|
1529
1665
|
const handler = this._actionHandler;
|
|
1530
1666
|
if (handler === null)
|
|
1531
1667
|
return undefined;
|
|
1668
|
+
this._warnDeprecated();
|
|
1532
1669
|
const root = this._root();
|
|
1533
1670
|
const manifest = root._manifest;
|
|
1534
1671
|
const settle = (value) => this._emitResult(value);
|
|
@@ -1546,6 +1683,15 @@ Expecting one of '${HELP_POSITIONS.join("', '")}'`);
|
|
|
1546
1683
|
settle(value);
|
|
1547
1684
|
});
|
|
1548
1685
|
}
|
|
1686
|
+
/** burgee (M5): once per process, on stderr; only for a command that asked, so commander's own output is untouched. */
|
|
1687
|
+
_warnDeprecated() {
|
|
1688
|
+
if (this._deprecated === undefined || this._deprecated === false || this._deprecationWarned)
|
|
1689
|
+
return;
|
|
1690
|
+
this._deprecationWarned = true;
|
|
1691
|
+
const use = typeof this._deprecated === 'string' ? `, use '${this._deprecated}'` : '';
|
|
1692
|
+
this._outputConfiguration.writeErr(`warning: '${this.name()}' is deprecated${use}\n`);
|
|
1693
|
+
}
|
|
1694
|
+
/** burgee: where every option value came from, from commander's own value sources (V3). */
|
|
1549
1695
|
_provenance() {
|
|
1550
1696
|
const out = {};
|
|
1551
1697
|
const names = { cli: 'flag', env: 'env', config: 'config', default: 'default', implied: 'implied' };
|
|
@@ -1568,7 +1714,9 @@ Expecting one of '${HELP_POSITIONS.join("', '")}'`);
|
|
|
1568
1714
|
this._outputConfiguration.writeOut(text);
|
|
1569
1715
|
}
|
|
1570
1716
|
}
|
|
1717
|
+
/** Bump inspector ports so a spawned subcommand does not collide with the parent. */
|
|
1571
1718
|
function incrementNodeInspectorPort(args) {
|
|
1719
|
+
// --inspect[=[host:]port], --inspect-brk[=[host:]port], --inspect-port=[host:]port
|
|
1572
1720
|
return args.map((arg) => {
|
|
1573
1721
|
if (!arg.startsWith('--inspect'))
|
|
1574
1722
|
return arg;
|
|
@@ -1596,6 +1744,10 @@ function incrementNodeInspectorPort(args) {
|
|
|
1596
1744
|
return arg;
|
|
1597
1745
|
});
|
|
1598
1746
|
}
|
|
1747
|
+
/**
|
|
1748
|
+
* The common colour conventions: NO_COLOR and FORCE_COLOR=0/false disable, FORCE_COLOR
|
|
1749
|
+
* and CLICOLOR_FORCE enable, otherwise undecided (the stream's TTY-ness decides).
|
|
1750
|
+
*/
|
|
1599
1751
|
export function useColor() {
|
|
1600
1752
|
if (process.env['NO_COLOR'] || process.env['FORCE_COLOR'] === '0' || process.env['FORCE_COLOR'] === 'false')
|
|
1601
1753
|
return false;
|
|
@@ -1603,3 +1755,4 @@ export function useColor() {
|
|
|
1603
1755
|
return true;
|
|
1604
1756
|
return undefined;
|
|
1605
1757
|
}
|
|
1758
|
+
//# sourceMappingURL=command.js.map
|