burgee 0.11.1 → 0.12.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/README.md +12 -0
- package/dist/cli.d.ts +1 -90
- package/dist/cli.js +3 -151
- package/dist/commander/command.d.ts +22 -0
- package/dist/commander/command.js +41 -10
- package/dist/compat.d.ts +39 -2
- package/dist/compat.js +90 -2
- package/dist/complete-dynamic.d.ts +18 -0
- package/dist/complete-dynamic.js +19 -0
- package/dist/completions.js +32 -14
- package/dist/config-explain.d.ts +22 -0
- package/dist/config-explain.js +33 -0
- package/dist/define-error.d.ts +29 -0
- package/dist/define-error.js +29 -0
- package/dist/errors.d.ts +29 -0
- package/dist/errors.js +17 -0
- package/dist/execute.d.ts +6 -0
- package/dist/execute.js +71 -13
- package/dist/facade-failure.d.ts +17 -0
- package/dist/facade-failure.js +12 -0
- package/dist/fields.d.ts +32 -0
- package/dist/fields.js +45 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/manifest.d.ts +35 -5
- package/dist/manifest.js +6 -0
- package/dist/mcp.js +68 -4
- package/dist/migrate.d.ts +57 -7
- package/dist/migrate.js +413 -34
- package/dist/parse-hooks.d.ts +7 -0
- package/dist/parse-hooks.js +17 -0
- package/dist/plugin.d.ts +2 -6
- package/dist/plugin.js +1 -1
- package/dist/program-schema.json +1 -0
- package/dist/program.d.ts +90 -0
- package/dist/program.js +151 -0
- package/dist/runtime.d.ts +3 -1
- package/dist/runtime.js +3 -0
- package/dist/schema.d.ts +7 -0
- package/dist/schema.js +6 -11
- package/dist/schema.json +1 -1
- package/dist/stdin-dash.d.ts +15 -0
- package/dist/stdin-dash.js +12 -0
- package/dist/validate.d.ts +1 -27
- package/dist/validate.js +2 -17
- package/dist/yargs/factory.js +39 -13
- package/dist/yargs-parser.d.ts +8 -1
- package/dist/yargs-parser.js +1 -1
- package/dist/yargs.d.ts +1 -0
- package/dist/yargs.js +1 -0
- package/package.json +7 -6
package/README.md
CHANGED
|
@@ -95,6 +95,18 @@ published and ratcheting:
|
|
|
95
95
|
Your code and your tests are unchanged. A façade is never called "compatible" until its
|
|
96
96
|
host's own suite passes 100%; below that the rate is published instead of claimed.
|
|
97
97
|
|
|
98
|
+
Or let the codemod make that change, and the same one for chalk, ora, string-width,
|
|
99
|
+
cross-spawn, signal-exit and every other incumbent the family replaces at full grade:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
npx burgee migrate --dry-run
|
|
103
|
+
npx burgee migrate
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
It rewrites import specifiers and nothing else, leaves a replacement that is not level yet
|
|
107
|
+
alone with its grade, refuses a file it cannot rewrite whole, and prints the install command
|
|
108
|
+
to run next — [Migrate](https://burgee.interlace.tools/docs/migrate).
|
|
109
|
+
|
|
98
110
|
## What is in the box
|
|
99
111
|
|
|
100
112
|
| Import | Gives you |
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,91 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
export
|
|
3
|
-
readonly lead: {
|
|
4
|
-
readonly type: "string";
|
|
5
|
-
readonly required: true;
|
|
6
|
-
readonly description: "leading colour, hex. Your primary; it leads the charge upper-left";
|
|
7
|
-
};
|
|
8
|
-
readonly follow: {
|
|
9
|
-
readonly type: "string";
|
|
10
|
-
readonly required: true;
|
|
11
|
-
readonly description: "following colour, hex. Your secondary; it follows lower-right";
|
|
12
|
-
};
|
|
13
|
-
readonly name: {
|
|
14
|
-
readonly type: "string";
|
|
15
|
-
readonly description: "brand name, used as the accessible label and card title";
|
|
16
|
-
};
|
|
17
|
-
readonly ground: {
|
|
18
|
-
readonly type: "string";
|
|
19
|
-
readonly default: "#0a0a0a";
|
|
20
|
-
readonly description: "the field’s dark midpoint, which is what keeps the charge legible";
|
|
21
|
-
};
|
|
22
|
-
readonly charge: {
|
|
23
|
-
readonly type: "string";
|
|
24
|
-
readonly description: "path to an SVG whose contents replace the bars, drawn in a 0 0 100 100 box";
|
|
25
|
-
};
|
|
26
|
-
readonly bordure: {
|
|
27
|
-
readonly type: "string";
|
|
28
|
-
readonly description: "outline colour, hex. Omit for no outline";
|
|
29
|
-
};
|
|
30
|
-
readonly bordureWidth: {
|
|
31
|
-
readonly type: "string";
|
|
32
|
-
readonly default: "1.5";
|
|
33
|
-
readonly description: "outline width";
|
|
34
|
-
};
|
|
35
|
-
readonly tagline: {
|
|
36
|
-
readonly type: "string";
|
|
37
|
-
readonly description: "one line under the name on the card and cover";
|
|
38
|
-
};
|
|
39
|
-
readonly out: {
|
|
40
|
-
readonly type: "string";
|
|
41
|
-
readonly description: "directory to write into. Omit to print the flag only";
|
|
42
|
-
};
|
|
43
|
-
readonly on: {
|
|
44
|
-
readonly type: "string";
|
|
45
|
-
readonly description: "page colour(s) the flag will fly on, comma separated. Checked for contrast";
|
|
46
|
-
};
|
|
47
|
-
readonly allowLowContrast: {
|
|
48
|
-
readonly type: "boolean";
|
|
49
|
-
readonly description: "emit anyway when a contrast check fails. Says so in the output";
|
|
50
|
-
};
|
|
51
|
-
}>;
|
|
52
|
-
/**
|
|
53
|
-
* `burgee dev <entry>` — watch the entry, reload it on change, serve it as MCP on stdio
|
|
54
|
-
* and print every surface on each save (dev-loop). Loaded only when asked for, so the
|
|
55
|
-
* CLI's own weight and the framework's stay what they were (W4).
|
|
56
|
-
*/
|
|
57
|
-
export declare const devCommand: import("./execute.js").Command<{
|
|
58
|
-
readonly noWatch: {
|
|
59
|
-
readonly type: "boolean";
|
|
60
|
-
readonly description: "load once and serve; do not watch for changes";
|
|
61
|
-
};
|
|
62
|
-
}>;
|
|
63
|
-
/**
|
|
64
|
-
* `burgee migrate [dir]` — rewrite a commander or yargs project's imports to burgee's
|
|
65
|
-
* drop-in front-ends and report what changed, with the numbers that say why it was safe.
|
|
66
|
-
*
|
|
67
|
-
* The engine is loaded on this path only (K6), the same way `dev` is: a dynamic import, so
|
|
68
|
-
* `burgee`'s own start-up and the framework's weight are what they were. `weight.test.ts`
|
|
69
|
-
* denies `migrate.js` to the root entry by name, so that cannot drift back.
|
|
70
|
-
*
|
|
71
|
-
* `idempotent` is the honest answer rather than the conservative one, and it is earned:
|
|
72
|
-
* `burgee/commander` is not a key in the mapping, so a second run over a migrated tree
|
|
73
|
-
* rewrites nothing. N6 then requires the result to report `changed`, which it does.
|
|
74
|
-
*/
|
|
75
|
-
export declare const migrateCommand: import("./execute.js").Command<{
|
|
76
|
-
readonly dryRun: {
|
|
77
|
-
readonly type: "boolean";
|
|
78
|
-
readonly description: "scan and report; write nothing";
|
|
79
|
-
};
|
|
80
|
-
readonly force: {
|
|
81
|
-
readonly type: "boolean";
|
|
82
|
-
readonly description: "migrate even though the git tree has uncommitted changes";
|
|
83
|
-
};
|
|
84
|
-
}>;
|
|
85
|
-
/**
|
|
86
|
-
* `burgee check <plugin-file>` — the feedback loop PRINCIPLES 7 asks every extension surface
|
|
87
|
-
* for, and the one burgee did not have. See `check.ts`: this one returns its report as data, so
|
|
88
|
-
* `--json` is the form an agent that just wrote a plugin reads.
|
|
89
|
-
*/
|
|
90
|
-
export declare const pluginCheckCommand: import("./execute.js").Command<import("./execute.js").OptionSpecs>;
|
|
91
|
-
export declare const program: import("./manifest.js").Manifest;
|
|
2
|
+
export * from './program.js';
|
package/dist/cli.js
CHANGED
|
@@ -1,153 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { DEFAULT_GROUND, defineBurgee, opposedField } from './brand.js';
|
|
5
|
-
import { auditBurgee, report } from './contrast.js';
|
|
6
|
-
import { defineCommand, defineProgram, run } from './execute.js';
|
|
7
|
-
const MASTER = 512;
|
|
8
|
-
const CONTRAST_HINT = 'hint: darken the --ground stop under the charge, or pass --allow-low-contrast';
|
|
9
|
-
const DEFAULT_BORDURE_WIDTH = '1.5';
|
|
10
|
-
function surfaces(brand, tagline) {
|
|
11
|
-
const burgee = defineBurgee(brand);
|
|
12
|
-
const subtitle = tagline === '' ? {} : { subtitle: tagline };
|
|
13
|
-
return [
|
|
14
|
-
{ file: 'flag.svg', svg: burgee.flag(MASTER) },
|
|
15
|
-
{ file: 'icon.svg', svg: burgee.favicon() },
|
|
16
|
-
{ file: 'og.svg', svg: burgee.og(subtitle) },
|
|
17
|
-
{ file: 'cover.svg', svg: burgee.cover(subtitle) },
|
|
18
|
-
{ file: 'lockup.svg', svg: burgee.lockup({ theme: 'dark' }) },
|
|
19
|
-
{ file: 'lockup-light.svg', svg: burgee.lockup({ theme: 'light' }) },
|
|
20
|
-
];
|
|
21
|
-
}
|
|
22
|
-
export const brandCommand = defineCommand({
|
|
23
|
-
name: 'brand',
|
|
24
|
-
description: 'Generate a burgee — flag, favicon, social card, cover and lockup — from two colours',
|
|
25
|
-
effects: 'non_idempotent',
|
|
26
|
-
options: {
|
|
27
|
-
lead: {
|
|
28
|
-
type: 'string',
|
|
29
|
-
required: true,
|
|
30
|
-
description: 'leading colour, hex. Your primary; it leads the charge upper-left',
|
|
31
|
-
},
|
|
32
|
-
follow: {
|
|
33
|
-
type: 'string',
|
|
34
|
-
required: true,
|
|
35
|
-
description: 'following colour, hex. Your secondary; it follows lower-right',
|
|
36
|
-
},
|
|
37
|
-
name: { type: 'string', description: 'brand name, used as the accessible label and card title' },
|
|
38
|
-
ground: {
|
|
39
|
-
type: 'string',
|
|
40
|
-
default: DEFAULT_GROUND,
|
|
41
|
-
description: 'the field’s dark midpoint, which is what keeps the charge legible',
|
|
42
|
-
},
|
|
43
|
-
charge: {
|
|
44
|
-
type: 'string',
|
|
45
|
-
description: 'path to an SVG whose contents replace the bars, drawn in a 0 0 100 100 box',
|
|
46
|
-
},
|
|
47
|
-
bordure: { type: 'string', description: 'outline colour, hex. Omit for no outline' },
|
|
48
|
-
bordureWidth: { type: 'string', default: DEFAULT_BORDURE_WIDTH, description: 'outline width' },
|
|
49
|
-
tagline: { type: 'string', description: 'one line under the name on the card and cover' },
|
|
50
|
-
out: { type: 'string', description: 'directory to write into. Omit to print the flag only' },
|
|
51
|
-
'on': {
|
|
52
|
-
type: 'string',
|
|
53
|
-
description: 'page colour(s) the flag will fly on, comma separated. Checked for contrast',
|
|
54
|
-
},
|
|
55
|
-
allowLowContrast: {
|
|
56
|
-
type: 'boolean',
|
|
57
|
-
description: 'emit anyway when a contrast check fails. Says so in the output',
|
|
58
|
-
},
|
|
59
|
-
},
|
|
60
|
-
run: ({ options }) => {
|
|
61
|
-
const lead = options.lead ?? '';
|
|
62
|
-
const follow = options.follow ?? '';
|
|
63
|
-
const colors = { lead, follow };
|
|
64
|
-
const charge = options.charge === undefined
|
|
65
|
-
? {}
|
|
66
|
-
: { charge: readFileSync(options.charge, 'utf8').replace(/<\/?svg[^>]*>/g, '').trim() };
|
|
67
|
-
const bordure = options.bordure === undefined
|
|
68
|
-
? {}
|
|
69
|
-
: {
|
|
70
|
-
bordure: {
|
|
71
|
-
color: options.bordure,
|
|
72
|
-
width: Number(options.bordureWidth ?? DEFAULT_BORDURE_WIDTH),
|
|
73
|
-
},
|
|
74
|
-
};
|
|
75
|
-
const brand = {
|
|
76
|
-
...(options.name === undefined ? {} : { name: options.name }),
|
|
77
|
-
mark: colors,
|
|
78
|
-
field: opposedField(colors, options.ground ?? DEFAULT_GROUND),
|
|
79
|
-
...charge,
|
|
80
|
-
...bordure,
|
|
81
|
-
};
|
|
82
|
-
const grounds = (options.on ?? '')
|
|
83
|
-
.split(',')
|
|
84
|
-
.map((g) => g.trim())
|
|
85
|
-
.filter((g) => g !== '');
|
|
86
|
-
const findings = auditBurgee(brand, grounds);
|
|
87
|
-
const failed = findings.filter((f) => !f.passes);
|
|
88
|
-
if (failed.length > 0 && options.allowLowContrast !== true) {
|
|
89
|
-
throw new Error(`contrast below WCAG AA:\n${report(failed)}\n${CONTRAST_HINT}`);
|
|
90
|
-
}
|
|
91
|
-
const written = surfaces(brand, options.tagline ?? '');
|
|
92
|
-
const contrast = findings.map((f) => ({ what: f.what, ratio: f.ratio, passes: f.passes }));
|
|
93
|
-
if (options.out === undefined) {
|
|
94
|
-
return { flag: defineBurgee(brand).flag(MASTER), files: [], contrast };
|
|
95
|
-
}
|
|
96
|
-
mkdirSync(options.out, { recursive: true });
|
|
97
|
-
for (const s of written)
|
|
98
|
-
writeFileSync(join(options.out, s.file), `${s.svg}\n`);
|
|
99
|
-
return { out: options.out, files: written.map((s) => s.file), contrast };
|
|
100
|
-
},
|
|
101
|
-
});
|
|
102
|
-
export const devCommand = defineCommand({
|
|
103
|
-
name: 'dev',
|
|
104
|
-
description: 'Watch a CLI entry, reload it on change, and serve it as MCP on stdio while you write it',
|
|
105
|
-
arguments: [{ name: 'entry', description: 'the module that exports the program, as program or as its default export', required: true }],
|
|
106
|
-
options: {
|
|
107
|
-
noWatch: { type: 'boolean', description: 'load once and serve; do not watch for changes' },
|
|
108
|
-
},
|
|
109
|
-
effects: 'withheld',
|
|
110
|
-
run: async ({ positionals, options }) => {
|
|
111
|
-
const [entry] = positionals;
|
|
112
|
-
if (entry === undefined)
|
|
113
|
-
throw new Error('an entry file is required');
|
|
114
|
-
const [{ dev }, { processRuntime }] = await Promise.all([import('./dev.js'), import('./runtime.js')]);
|
|
115
|
-
const handle = dev({ entry, input: processRuntime.stdin, output: processRuntime.stdout, log: processRuntime.stderr, watch: options.noWatch !== true });
|
|
116
|
-
await handle.done;
|
|
117
|
-
},
|
|
118
|
-
});
|
|
119
|
-
export const migrateCommand = defineCommand({
|
|
120
|
-
name: 'migrate',
|
|
121
|
-
description: 'Rewrite commander and yargs imports to burgee’s drop-in front-ends, and report what changed',
|
|
122
|
-
arguments: [{ name: 'dir', description: 'the project to migrate. Defaults to the current directory', required: false }],
|
|
123
|
-
options: {
|
|
124
|
-
dryRun: { type: 'boolean', description: 'scan and report; write nothing' },
|
|
125
|
-
force: { type: 'boolean', description: 'migrate even though the git tree has uncommitted changes' },
|
|
126
|
-
},
|
|
127
|
-
effects: 'idempotent',
|
|
128
|
-
examples: [
|
|
129
|
-
{ command: 'burgee migrate --dry-run', description: 'what it would change, without changing it' },
|
|
130
|
-
{ command: 'burgee migrate --json', description: 'the same report as data; exit 1 when anything was refused' },
|
|
131
|
-
],
|
|
132
|
-
run: async ({ positionals, options }) => {
|
|
133
|
-
const [{ migrate }, { host }] = await Promise.all([import('./migrate.js'), import('./runtime.js')]);
|
|
134
|
-
return await migrate({ dir: positionals[0] ?? host.cwd(), dryRun: options.dryRun === true, force: options.force === true });
|
|
135
|
-
},
|
|
136
|
-
});
|
|
137
|
-
export const pluginCheckCommand = defineCommand({
|
|
138
|
-
name: 'check',
|
|
139
|
-
description: 'Validate a burgee plugin, register it into a throwaway program, and report what it contributes',
|
|
140
|
-
arguments: [{ name: 'file', description: 'the plugin module to check' }],
|
|
141
|
-
effects: 'read_only',
|
|
142
|
-
examples: [
|
|
143
|
-
{ command: 'burgee check ./my-plugin.mjs', description: 'what the plugin contributes, or why it was refused' },
|
|
144
|
-
{ command: 'burgee check ./my-plugin.mjs --json', description: 'the same, as data; exit 1 on a refusal' },
|
|
145
|
-
],
|
|
146
|
-
run: async ({ positionals }) => (await import('./check.js')).checkPlugin(positionals[0] ?? ''),
|
|
147
|
-
});
|
|
148
|
-
export const program = defineProgram({
|
|
149
|
-
name: 'burgee',
|
|
150
|
-
description: 'The agent-native CLI framework, and the tools that come with it',
|
|
151
|
-
commands: [brandCommand, devCommand, migrateCommand, pluginCheckCommand],
|
|
152
|
-
});
|
|
2
|
+
import { run } from './execute.js';
|
|
3
|
+
import { program } from './program.js';
|
|
153
4
|
run(program);
|
|
5
|
+
export * from './program.js';
|
|
@@ -14,6 +14,7 @@ import { EventEmitter } from 'node:events';
|
|
|
14
14
|
* then reports through the E1 taxonomy — the harness's seam (T1).
|
|
15
15
|
*/
|
|
16
16
|
import * as crossSpawn from 'bellpull/cross-spawn';
|
|
17
|
+
import { ExitCode } from '../exit-code.js';
|
|
17
18
|
import { type DeclaredEffects, Manifest, type OptionSpec, type Plugin } from '../manifest.js';
|
|
18
19
|
import { Argument, type ParseArg } from './argument.js';
|
|
19
20
|
import { CommanderError } from './error.js';
|
|
@@ -261,6 +262,14 @@ export declare class Command extends EventEmitter {
|
|
|
261
262
|
error(message: string, errorOptions?: ErrorOptions): never;
|
|
262
263
|
/** One failure as the envelope (E3, G5); `fix` only when one candidate was named. */
|
|
263
264
|
_reportJson(code: string, message: string): void;
|
|
265
|
+
/** The failure envelope on stdout, once per run however many paths see the same failure (G5). */
|
|
266
|
+
_writeFailure(error: Record<string, unknown>): void;
|
|
267
|
+
/**
|
|
268
|
+
* A handler's failure — a throw, a rejection, a thrown non-Error — reported on the surface the
|
|
269
|
+
* run asked for, with the E1 code its error names: `AuthError` is AUTH, `UsageError` USAGE,
|
|
270
|
+
* anything else RUNTIME (E6, D-140). Returns that code.
|
|
271
|
+
*/
|
|
272
|
+
_reportHandlerFailure(err: unknown): ExitCode;
|
|
264
273
|
/** Apply environment variables to options that have no value from the cli or client code. */
|
|
265
274
|
_parseOptionsEnv(): void;
|
|
266
275
|
/** Apply implied option values where the option is undefined or at its default. */
|
|
@@ -362,6 +371,19 @@ export declare class Command extends EventEmitter {
|
|
|
362
371
|
_prepareBurgee(parseOptions?: BurgeeParseOptions): ParseOptions | undefined;
|
|
363
372
|
/** In burgee mode the whole run settles to one E1 exit; otherwise commander's behaviour, untouched. */
|
|
364
373
|
_runBurgee(run: () => unknown): unknown;
|
|
374
|
+
/**
|
|
375
|
+
* Nothing injected — `program.parseAsync(process.argv)`, the way every commander program is
|
|
376
|
+
* run. Commander's own contract, with one exception: a handler that fails under `--json`
|
|
377
|
+
* settles to the envelope on stdout and the E1 code its error names, set as the process's
|
|
378
|
+
* exit code, instead of escaping `parse`/`parseAsync` for Node to print as a stack (D-140).
|
|
379
|
+
*
|
|
380
|
+
* The seam check in `_runBurgee` cannot see this case, and that was the defect: it runs
|
|
381
|
+
* before argv is parsed, and `--json` is only recognised *during* the parse, so the run was
|
|
382
|
+
* already committed to "no burgee" when the handler threw. A `CommanderError` is left alone —
|
|
383
|
+
* `error()` has reported it already, and an `exitOverride` caller is owed the throw — and so
|
|
384
|
+
* is every failure without `--json`, which is commander's to surface as it always has.
|
|
385
|
+
*/
|
|
386
|
+
_runCommanderWay(run: () => unknown): unknown;
|
|
365
387
|
/** Any command from here down declares this flag, so burgee does not serve it: additive only. */
|
|
366
388
|
_declares(flag: string): boolean;
|
|
367
389
|
/** The action, wrapped in the plugin hooks and followed by the envelope or the rendering. */
|
|
@@ -4,6 +4,7 @@ import path from 'node:path';
|
|
|
4
4
|
import { stripVTControlCharacters } from 'node:util';
|
|
5
5
|
import * as crossSpawn from 'bellpull/cross-spawn';
|
|
6
6
|
import { ExitCode } from '../exit-code.js';
|
|
7
|
+
import { handlerFailure } from '../facade-failure.js';
|
|
7
8
|
import { Manifest } from '../manifest.js';
|
|
8
9
|
import { camel } from '../names.js';
|
|
9
10
|
import { host } from '../runtime.js';
|
|
@@ -1035,13 +1036,24 @@ Expecting one of '${HOOK_EVENTS.join("', '")}'`);
|
|
|
1035
1036
|
this._exit(exitCode, code, message);
|
|
1036
1037
|
}
|
|
1037
1038
|
_reportJson(code, message) {
|
|
1039
|
+
const [first = '', ...rest] = message.replace(/^error: /, '').split('\n');
|
|
1040
|
+
const guess = /\(Did you mean (\S+)\?\)/.exec(rest.join(''));
|
|
1041
|
+
this._writeFailure({ code, message: first, ...(guess === null ? {} : { fix: guess[1] }) });
|
|
1042
|
+
}
|
|
1043
|
+
_writeFailure(error) {
|
|
1038
1044
|
const burgee = this._root()._burgee;
|
|
1039
1045
|
if (burgee === undefined || !burgee.json || burgee.reported)
|
|
1040
1046
|
return;
|
|
1041
1047
|
burgee.reported = true;
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1048
|
+
this._outputConfiguration.writeOut(`${JSON.stringify({ ok: false, error })}\n`);
|
|
1049
|
+
}
|
|
1050
|
+
_reportHandlerFailure(err) {
|
|
1051
|
+
const { exit, error } = handlerFailure(err);
|
|
1052
|
+
if (this._burgee?.json === true)
|
|
1053
|
+
this._writeFailure(error);
|
|
1054
|
+
else
|
|
1055
|
+
this._outputConfiguration.writeErr(`error: ${error.message}\n`);
|
|
1056
|
+
return exit;
|
|
1045
1057
|
}
|
|
1046
1058
|
_parseOptionsEnv() {
|
|
1047
1059
|
for (const option of this.options) {
|
|
@@ -1517,7 +1529,7 @@ Expecting one of '${HELP_POSITIONS.join("', '")}'`);
|
|
|
1517
1529
|
const root = this._root();
|
|
1518
1530
|
const burgee = root._burgee;
|
|
1519
1531
|
if (burgee === undefined)
|
|
1520
|
-
return run
|
|
1532
|
+
return root._runCommanderWay(run);
|
|
1521
1533
|
const finish = (code) => {
|
|
1522
1534
|
root._burgee = undefined;
|
|
1523
1535
|
burgee.exit?.(code);
|
|
@@ -1529,12 +1541,7 @@ Expecting one of '${HELP_POSITIONS.join("', '")}'`);
|
|
|
1529
1541
|
finish(e1(err));
|
|
1530
1542
|
return;
|
|
1531
1543
|
}
|
|
1532
|
-
|
|
1533
|
-
if (burgee.json)
|
|
1534
|
-
root._reportJson('runtime', message);
|
|
1535
|
-
else
|
|
1536
|
-
root._outputConfiguration.writeErr(`error: ${message}\n`);
|
|
1537
|
-
finish(ExitCode.RUNTIME);
|
|
1544
|
+
finish(root._reportHandlerFailure(err));
|
|
1538
1545
|
};
|
|
1539
1546
|
try {
|
|
1540
1547
|
const result = run();
|
|
@@ -1548,6 +1555,30 @@ Expecting one of '${HELP_POSITIONS.join("', '")}'`);
|
|
|
1548
1555
|
return undefined;
|
|
1549
1556
|
}
|
|
1550
1557
|
}
|
|
1558
|
+
_runCommanderWay(run) {
|
|
1559
|
+
const done = () => {
|
|
1560
|
+
this._burgee = undefined;
|
|
1561
|
+
};
|
|
1562
|
+
const fail = (err) => {
|
|
1563
|
+
const json = this._burgee?.json === true && !(err instanceof CommanderError);
|
|
1564
|
+
if (json)
|
|
1565
|
+
host.exitCode = this._reportHandlerFailure(err);
|
|
1566
|
+
done();
|
|
1567
|
+
if (!json)
|
|
1568
|
+
throw err;
|
|
1569
|
+
};
|
|
1570
|
+
try {
|
|
1571
|
+
const result = run();
|
|
1572
|
+
if (isThenable(result))
|
|
1573
|
+
return Promise.resolve(result).then(done, fail);
|
|
1574
|
+
done();
|
|
1575
|
+
return undefined;
|
|
1576
|
+
}
|
|
1577
|
+
catch (err) {
|
|
1578
|
+
fail(err);
|
|
1579
|
+
return undefined;
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1551
1582
|
_declares(flag) {
|
|
1552
1583
|
return this._findOption(flag) !== undefined || this.commands.some((sub) => sub._declares(flag));
|
|
1553
1584
|
}
|
package/dist/compat.d.ts
CHANGED
|
@@ -26,5 +26,42 @@ export interface Graded {
|
|
|
26
26
|
/** `passed / reference`, carried rather than recomputed so it is the oracle's own division. */
|
|
27
27
|
rate: number;
|
|
28
28
|
}
|
|
29
|
-
/**
|
|
30
|
-
export
|
|
29
|
+
/** One host's grade, with the control beside it: the incumbent's own suite run against the incumbent in the same harness. */
|
|
30
|
+
export interface Row extends Graded {
|
|
31
|
+
/**
|
|
32
|
+
* Cases the incumbent itself passes here — from the published compatibility page, which
|
|
33
|
+
* `npm run compat:page` generates on ubuntu. A drop-in that passes as many as the control
|
|
34
|
+
* is **level**: every case it misses, the incumbent misses too, in this harness.
|
|
35
|
+
*/
|
|
36
|
+
control: number;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Every host the oracle grades, keyed by its `hosts.ts` name. The lock holds `reference`,
|
|
40
|
+
* `passed` and `rate` equal to `baseline/<host>.json` and `control` equal to the page's
|
|
41
|
+
* control column.
|
|
42
|
+
*/
|
|
43
|
+
export declare const GRADED: Readonly<Record<string, Row>>;
|
|
44
|
+
/** One graded path: an incumbent's specifier, and the family specifier that replaces it. */
|
|
45
|
+
export interface DropIn {
|
|
46
|
+
/** The `hosts.ts` name, which keys `GRADED`. */
|
|
47
|
+
host: string;
|
|
48
|
+
from: string;
|
|
49
|
+
to: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Every drop-in pair the oracle grades, derived from `hosts.ts`: `from` is what the
|
|
53
|
+
* incumbent's own tests import (`<host><subpath>`, or the import's `control` where the
|
|
54
|
+
* incumbent is a separate package, as `yargs-parser` is), `to` is `<target><subpath>`.
|
|
55
|
+
* `scripts/migrate-drop-ins-lock.test.ts` re-derives this list and fails on any difference.
|
|
56
|
+
*/
|
|
57
|
+
export declare const DROP_INS: readonly DropIn[];
|
|
58
|
+
/**
|
|
59
|
+
* The version each incumbent package was graded at: `vendor/<host>/.source.json`, or for
|
|
60
|
+
* `yargs-parser` the copy the yargs control resolves. The grade is for that major and no other
|
|
61
|
+
* — `signal-exit` 3 exports a function where 4 exports `onExit`, and `chalk` 4 is CommonJS where
|
|
62
|
+
* 6 is not — so `migrate` leaves a project on another major alone and says so.
|
|
63
|
+
* `scripts/migrate-drop-ins-lock.test.ts` holds each equal to the oracle's.
|
|
64
|
+
*/
|
|
65
|
+
export declare const GRADED_VERSIONS: Readonly<Record<string, string>>;
|
|
66
|
+
/** Level: the drop-in passes every case the incumbent passes against its own suite (D-137). */
|
|
67
|
+
export declare const isLevel: (host: string) => boolean;
|
package/dist/compat.js
CHANGED
|
@@ -1,4 +1,92 @@
|
|
|
1
1
|
export const GRADED = {
|
|
2
|
-
commander: { reference: 1360, passed: 1360, rate: 1 },
|
|
3
|
-
yargs: { reference: 804, passed: 804, rate: 1 },
|
|
2
|
+
commander: { reference: 1360, passed: 1360, rate: 1, control: 1360 },
|
|
3
|
+
yargs: { reference: 804, passed: 804, rate: 1, control: 802 },
|
|
4
|
+
chalk: { reference: 58, passed: 58, rate: 1, control: 58 },
|
|
5
|
+
ora: { reference: 99, passed: 99, rate: 1, control: 99 },
|
|
6
|
+
'log-update': { reference: 99, passed: 99, rate: 1, control: 99 },
|
|
7
|
+
boxen: { reference: 84, passed: 84, rate: 1, control: 84 },
|
|
8
|
+
'cli-table3': { reference: 29, passed: 29, rate: 1, control: 29 },
|
|
9
|
+
'string-width': { reference: 229, passed: 229, rate: 1, control: 229 },
|
|
10
|
+
'strip-ansi': { reference: 8, passed: 8, rate: 1, control: 8 },
|
|
11
|
+
'cross-spawn': { reference: 68, passed: 68, rate: 1, control: 68 },
|
|
12
|
+
which: { reference: 5, passed: 5, rate: 1, control: 5 },
|
|
13
|
+
rc: { reference: 1, passed: 1, rate: 1, control: 1 },
|
|
14
|
+
'wrap-ansi': { reference: 80, passed: 80, rate: 1, control: 80 },
|
|
15
|
+
'slice-ansi': { reference: 15, passed: 15, rate: 1, control: 15 },
|
|
16
|
+
cosmiconfig: { reference: 243, passed: 186, rate: 0.7654320987654321, control: 240 },
|
|
17
|
+
lilconfig: { reference: 77, passed: 77, rate: 1, control: 77 },
|
|
18
|
+
dotenv: { reference: 141, passed: 106, rate: 0.75177304964539, control: 141 },
|
|
19
|
+
clack: { reference: 17, passed: 14, rate: 0.8235294117647058, control: 17 },
|
|
20
|
+
'inquirer-core': { reference: 41, passed: 41, rate: 1, control: 41 },
|
|
21
|
+
meow: { reference: 148, passed: 132, rate: 0.8918918918918919, control: 146 },
|
|
22
|
+
'ansi-escapes': { reference: 4, passed: 4, rate: 1, control: 4 },
|
|
23
|
+
'terminal-link': { reference: 8, passed: 8, rate: 1, control: 8 },
|
|
24
|
+
'term-img': { reference: 18, passed: 12, rate: 0.6666666666666666, control: 18 },
|
|
25
|
+
'restore-cursor': { reference: 6, passed: 6, rate: 1, control: 6 },
|
|
26
|
+
'exit-hook': { reference: 21, passed: 21, rate: 1, control: 21 },
|
|
27
|
+
'signal-exit': { reference: 135, passed: 134, rate: 0.9925925925925926, control: 134 },
|
|
28
|
+
};
|
|
29
|
+
export const DROP_INS = [
|
|
30
|
+
{ host: 'commander', from: 'commander', to: 'burgee/commander' },
|
|
31
|
+
{ host: 'yargs', from: 'yargs', to: 'burgee/yargs' },
|
|
32
|
+
{ host: 'yargs', from: 'yargs/helpers', to: 'burgee/yargs/helpers' },
|
|
33
|
+
{ host: 'yargs', from: 'yargs-parser', to: 'burgee/yargs/parser' },
|
|
34
|
+
{ host: 'chalk', from: 'chalk', to: 'roundel/chalk' },
|
|
35
|
+
{ host: 'ora', from: 'ora', to: 'flagstaff/ora' },
|
|
36
|
+
{ host: 'log-update', from: 'log-update', to: 'flagstaff/log-update' },
|
|
37
|
+
{ host: 'boxen', from: 'boxen', to: 'flagstaff/boxen' },
|
|
38
|
+
{ host: 'cli-table3', from: 'cli-table3', to: 'flagstaff/cli-table3' },
|
|
39
|
+
{ host: 'string-width', from: 'string-width', to: 'linegauge' },
|
|
40
|
+
{ host: 'strip-ansi', from: 'strip-ansi', to: 'linegauge/strip' },
|
|
41
|
+
{ host: 'cross-spawn', from: 'cross-spawn', to: 'bellpull/cross-spawn' },
|
|
42
|
+
{ host: 'which', from: 'which', to: 'bellpull/node-which' },
|
|
43
|
+
{ host: 'rc', from: 'rc', to: 'seniority/rc' },
|
|
44
|
+
{ host: 'wrap-ansi', from: 'wrap-ansi', to: 'linegauge/wrap' },
|
|
45
|
+
{ host: 'slice-ansi', from: 'slice-ansi', to: 'linegauge/slice' },
|
|
46
|
+
{ host: 'cosmiconfig', from: 'cosmiconfig', to: 'seniority' },
|
|
47
|
+
{ host: 'lilconfig', from: 'lilconfig', to: 'seniority/lilconfig' },
|
|
48
|
+
{ host: 'dotenv', from: 'dotenv', to: 'seniority/dotenv' },
|
|
49
|
+
{ host: 'clack', from: '@clack/prompts', to: 'caique/clack' },
|
|
50
|
+
{ host: 'inquirer-core', from: '@inquirer/core', to: 'caique/inquirer' },
|
|
51
|
+
{ host: 'meow', from: 'meow', to: 'burgee/meow' },
|
|
52
|
+
{ host: 'ansi-escapes', from: 'ansi-escapes', to: 'paratext' },
|
|
53
|
+
{ host: 'terminal-link', from: 'terminal-link', to: 'paratext/terminal-link' },
|
|
54
|
+
{ host: 'term-img', from: 'term-img', to: 'paratext/term-img' },
|
|
55
|
+
{ host: 'restore-cursor', from: 'restore-cursor', to: 'closeout/restore-cursor' },
|
|
56
|
+
{ host: 'exit-hook', from: 'exit-hook', to: 'closeout/exit-hook' },
|
|
57
|
+
{ host: 'signal-exit', from: 'signal-exit', to: 'closeout/signal-exit' },
|
|
58
|
+
{ host: 'signal-exit', from: 'signal-exit/signals', to: 'closeout/signal-exit/signals' },
|
|
59
|
+
];
|
|
60
|
+
export const GRADED_VERSIONS = {
|
|
61
|
+
'@clack/prompts': '1.8.1',
|
|
62
|
+
'@inquirer/core': '12.0.3',
|
|
63
|
+
'ansi-escapes': '7.3.0',
|
|
64
|
+
boxen: '8.0.1',
|
|
65
|
+
chalk: '6.0.0',
|
|
66
|
+
'cli-table3': '0.6.5',
|
|
67
|
+
commander: '15.0.0',
|
|
68
|
+
cosmiconfig: '10.0.1',
|
|
69
|
+
'cross-spawn': '7.0.6',
|
|
70
|
+
dotenv: '17.4.2',
|
|
71
|
+
'exit-hook': '5.1.0',
|
|
72
|
+
lilconfig: '3.1.3',
|
|
73
|
+
'log-update': '8.0.0',
|
|
74
|
+
meow: '14.1.0',
|
|
75
|
+
ora: '9.4.1',
|
|
76
|
+
rc: '1.2.8',
|
|
77
|
+
'restore-cursor': '5.1.0',
|
|
78
|
+
'signal-exit': '4.1.0',
|
|
79
|
+
'slice-ansi': '7.1.2',
|
|
80
|
+
'string-width': '8.2.2',
|
|
81
|
+
'strip-ansi': '7.2.0',
|
|
82
|
+
'term-img': '7.1.0',
|
|
83
|
+
'terminal-link': '5.0.0',
|
|
84
|
+
which: '7.0.0',
|
|
85
|
+
'wrap-ansi': '10.0.1',
|
|
86
|
+
yargs: '18.1.0',
|
|
87
|
+
'yargs-parser': '22.0.0',
|
|
88
|
+
};
|
|
89
|
+
export const isLevel = (host) => {
|
|
90
|
+
const row = GRADED[host];
|
|
91
|
+
return row !== undefined && row.passed >= row.control;
|
|
4
92
|
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 Ofri Peretz
|
|
3
|
+
* Licensed under the MIT License. Use of this source code is governed by the
|
|
4
|
+
* MIT license that can be found in the LICENSE file.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* D3 / D-119 — the program's side of a dynamic completion. A generated script calls
|
|
8
|
+
* `<program> __complete <command…> --<option> <partial>` for an option that declared
|
|
9
|
+
* `complete`, and prints what comes back, one candidate per line.
|
|
10
|
+
*
|
|
11
|
+
* It never fails loudly. A TAB that prints an error into someone's prompt is worse than one
|
|
12
|
+
* that offers nothing, so an unknown command, an option without a completer, or a completer
|
|
13
|
+
* that throws all print nothing and leave 0.
|
|
14
|
+
*
|
|
15
|
+
* Imported by `execute.ts` only when argv starts with `__complete` (M2).
|
|
16
|
+
*/
|
|
17
|
+
import { type Manifest } from './manifest.js';
|
|
18
|
+
export declare function completeDynamic(manifest: Manifest, argv: readonly string[], write: (s: string) => unknown): Promise<void>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { kebab } from './names.js';
|
|
2
|
+
export async function completeDynamic(manifest, argv, write) {
|
|
3
|
+
const flag = argv.findIndex((a) => a.startsWith('--'));
|
|
4
|
+
if (flag === -1)
|
|
5
|
+
return;
|
|
6
|
+
const { node } = manifest.resolve(argv.slice(0, flag), manifest.rootPath);
|
|
7
|
+
const name = argv[flag]?.slice(2);
|
|
8
|
+
const spec = Object.entries(node?.options ?? {}).find(([key]) => kebab(key) === name)?.[1];
|
|
9
|
+
if (spec?.complete === undefined)
|
|
10
|
+
return;
|
|
11
|
+
const partial = argv[flag + 1] ?? '';
|
|
12
|
+
const { complete } = spec;
|
|
13
|
+
const offered = await Promise.resolve()
|
|
14
|
+
.then(async () => [...(await complete(partial))])
|
|
15
|
+
.then((all) => all, () => []);
|
|
16
|
+
const found = offered.filter((c) => c.startsWith(partial));
|
|
17
|
+
if (found.length > 0)
|
|
18
|
+
write(`${found.join('\n')}\n`);
|
|
19
|
+
}
|