burgee 0.11.1 → 0.12.1
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 +88 -5
- 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/dist/migrate.js
CHANGED
|
@@ -1,17 +1,40 @@
|
|
|
1
|
-
import { readdirSync } from 'node:fs';
|
|
1
|
+
import { existsSync, readdirSync } from 'node:fs';
|
|
2
2
|
import { readFile, writeFile } from 'node:fs/promises';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { ambientRuntime, run } from 'bellpull';
|
|
5
|
-
import { GRADED } from './compat.js';
|
|
5
|
+
import { DROP_INS, GRADED, GRADED_VERSIONS, isLevel } from './compat.js';
|
|
6
6
|
import { ExitCode } from './exit-code.js';
|
|
7
|
-
export
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
export function packageOf(specifier) {
|
|
8
|
+
const parts = specifier.split('/');
|
|
9
|
+
return parts.slice(0, specifier.startsWith('@') ? 2 : 1).join('/');
|
|
10
|
+
}
|
|
11
|
+
export const MAPPING = Object.fromEntries(DROP_INS.filter((d) => isLevel(d.host)).flatMap((d) => (d.from === 'yargs' ? [[d.from, d.to], ['yargs/yargs', d.to]] : [[d.from, d.to]])));
|
|
12
|
+
export const HOSTS = [...new Set(Object.keys(MAPPING).map(packageOf))];
|
|
13
|
+
const PARTIAL = DROP_INS.filter((d) => !isLevel(d.host));
|
|
14
|
+
export function majorOf(version) {
|
|
15
|
+
const digits = /\d+/.exec(version);
|
|
16
|
+
return digits === null ? undefined : Number(digits[0]);
|
|
17
|
+
}
|
|
18
|
+
const HOST_OF = new Map(DROP_INS.map((d) => [packageOf(d.from), d.host]));
|
|
14
19
|
export const FACADE_EXPORTS = {
|
|
20
|
+
'bellpull/cross-spawn': [
|
|
21
|
+
'ChildProcess',
|
|
22
|
+
'Parsed',
|
|
23
|
+
'SpawnOptions',
|
|
24
|
+
'SpawnSyncReturns',
|
|
25
|
+
'_enoent',
|
|
26
|
+
'crossSpawn',
|
|
27
|
+
'default',
|
|
28
|
+
'module.exports',
|
|
29
|
+
'parse',
|
|
30
|
+
'spawn',
|
|
31
|
+
'sync',
|
|
32
|
+
],
|
|
33
|
+
'bellpull/node-which': [
|
|
34
|
+
'NodeWhichOptions',
|
|
35
|
+
'default',
|
|
36
|
+
'module.exports',
|
|
37
|
+
],
|
|
15
38
|
'burgee/commander': [
|
|
16
39
|
'AddHelpTextContext',
|
|
17
40
|
'AddHelpTextPosition',
|
|
@@ -88,12 +111,312 @@ export const FACADE_EXPORTS = {
|
|
|
88
111
|
'isPromise',
|
|
89
112
|
'isYargsInstance',
|
|
90
113
|
'looksLikeNumber',
|
|
114
|
+
'module.exports',
|
|
91
115
|
'objFilter',
|
|
92
116
|
'parseCommand',
|
|
93
117
|
'platformShim',
|
|
94
118
|
],
|
|
95
|
-
'burgee/yargs/helpers': [
|
|
119
|
+
'burgee/yargs/helpers': [
|
|
120
|
+
'Parser',
|
|
121
|
+
'applyExtends',
|
|
122
|
+
'hideBin',
|
|
123
|
+
],
|
|
124
|
+
'burgee/yargs/parser': [
|
|
125
|
+
'Arguments',
|
|
126
|
+
'Configuration',
|
|
127
|
+
'DetailedArguments',
|
|
128
|
+
'Options',
|
|
129
|
+
'Parser',
|
|
130
|
+
'ParserMixin',
|
|
131
|
+
'YargsParser',
|
|
132
|
+
'camelCase',
|
|
133
|
+
'decamelize',
|
|
134
|
+
'default',
|
|
135
|
+
'looksLikeNumber',
|
|
136
|
+
'module.exports',
|
|
137
|
+
'tokenizeArgString',
|
|
138
|
+
],
|
|
139
|
+
'caique/inquirer': [
|
|
140
|
+
'AbortPromptError',
|
|
141
|
+
'CancelPromptError',
|
|
142
|
+
'CancelablePromise',
|
|
143
|
+
'Context',
|
|
144
|
+
'ExitPromptError',
|
|
145
|
+
'HookError',
|
|
146
|
+
'Keybinding',
|
|
147
|
+
'KeypressEvent',
|
|
148
|
+
'PartialTheme',
|
|
149
|
+
'Prompt',
|
|
150
|
+
'Separator',
|
|
151
|
+
'SetState',
|
|
152
|
+
'Status',
|
|
153
|
+
'Theme',
|
|
154
|
+
'ValidationError',
|
|
155
|
+
'ViewFunction',
|
|
156
|
+
'createPrompt',
|
|
157
|
+
'defaultTheme',
|
|
158
|
+
'getDefaultKeybindings',
|
|
159
|
+
'getDefaultTheme',
|
|
160
|
+
'isBackspaceKey',
|
|
161
|
+
'isDownKey',
|
|
162
|
+
'isEnterKey',
|
|
163
|
+
'isNumberKey',
|
|
164
|
+
'isShiftKey',
|
|
165
|
+
'isSpaceKey',
|
|
166
|
+
'isTabKey',
|
|
167
|
+
'isUpKey',
|
|
168
|
+
'makeTheme',
|
|
169
|
+
'useEffect',
|
|
170
|
+
'useKeypress',
|
|
171
|
+
'useMemo',
|
|
172
|
+
'usePrefix',
|
|
173
|
+
'useRef',
|
|
174
|
+
'useState',
|
|
175
|
+
],
|
|
176
|
+
'closeout/exit-hook': [
|
|
177
|
+
'AsyncExitHookOptions',
|
|
178
|
+
'ExitHookCallback',
|
|
179
|
+
'Options',
|
|
180
|
+
'asyncExitHook',
|
|
181
|
+
'default',
|
|
182
|
+
'gracefulExit',
|
|
183
|
+
],
|
|
184
|
+
'closeout/restore-cursor': [
|
|
185
|
+
'default',
|
|
186
|
+
],
|
|
187
|
+
'closeout/signal-exit': [
|
|
188
|
+
'load',
|
|
189
|
+
'onExit',
|
|
190
|
+
'signals',
|
|
191
|
+
'unload',
|
|
192
|
+
],
|
|
193
|
+
'closeout/signal-exit/signals': [
|
|
194
|
+
'signals',
|
|
195
|
+
],
|
|
196
|
+
'flagstaff/boxen': [
|
|
197
|
+
'BoxenBorderStyle',
|
|
198
|
+
'BoxenOptions',
|
|
199
|
+
'Boxes',
|
|
200
|
+
'CustomBorderStyle',
|
|
201
|
+
'Options',
|
|
202
|
+
'Spacing',
|
|
203
|
+
'_borderStyles',
|
|
204
|
+
'default',
|
|
205
|
+
],
|
|
206
|
+
'flagstaff/cli-table3': [
|
|
207
|
+
'Cell',
|
|
208
|
+
'ColSpanCell',
|
|
209
|
+
'RowSpanCell',
|
|
210
|
+
'Table',
|
|
211
|
+
'TableChars',
|
|
212
|
+
'TableOptions',
|
|
213
|
+
'TableStyle',
|
|
214
|
+
'computeHeights',
|
|
215
|
+
'computeWidths',
|
|
216
|
+
'default',
|
|
217
|
+
'hyperlink',
|
|
218
|
+
'makeTableLayout',
|
|
219
|
+
'mergeOptions',
|
|
220
|
+
'module.exports',
|
|
221
|
+
'pad',
|
|
222
|
+
'strlen',
|
|
223
|
+
'truncate',
|
|
224
|
+
'wordWrap',
|
|
225
|
+
],
|
|
226
|
+
'flagstaff/log-update': [
|
|
227
|
+
'LogUpdate',
|
|
228
|
+
'LogUpdateOptions',
|
|
229
|
+
'LogUpdateStream',
|
|
230
|
+
'Options',
|
|
231
|
+
'createLogUpdate',
|
|
232
|
+
'default',
|
|
233
|
+
'logUpdateStderr',
|
|
234
|
+
],
|
|
235
|
+
'flagstaff/ora': [
|
|
236
|
+
'Affix',
|
|
237
|
+
'Color',
|
|
238
|
+
'Options',
|
|
239
|
+
'Ora',
|
|
240
|
+
'OraStream',
|
|
241
|
+
'PersistOptions',
|
|
242
|
+
'PrefixTextGenerator',
|
|
243
|
+
'PromiseOptions',
|
|
244
|
+
'Spinner',
|
|
245
|
+
'SpinnerDefinition',
|
|
246
|
+
'SuffixTextGenerator',
|
|
247
|
+
'default',
|
|
248
|
+
'oraPromise',
|
|
249
|
+
'spinners',
|
|
250
|
+
],
|
|
251
|
+
'linegauge': [
|
|
252
|
+
'Options',
|
|
253
|
+
'TruncateOptions',
|
|
254
|
+
'WidthOptions',
|
|
255
|
+
'WrapOptions',
|
|
256
|
+
'default',
|
|
257
|
+
'lineCount',
|
|
258
|
+
'measure',
|
|
259
|
+
'slice',
|
|
260
|
+
'strip',
|
|
261
|
+
'truncate',
|
|
262
|
+
'widest',
|
|
263
|
+
'width',
|
|
264
|
+
'wrap',
|
|
265
|
+
],
|
|
266
|
+
'linegauge/slice': [
|
|
267
|
+
'default',
|
|
268
|
+
'slice',
|
|
269
|
+
],
|
|
270
|
+
'linegauge/strip': [
|
|
271
|
+
'default',
|
|
272
|
+
'strip',
|
|
273
|
+
],
|
|
274
|
+
'linegauge/wrap': [
|
|
275
|
+
'Options',
|
|
276
|
+
'WrapOptions',
|
|
277
|
+
'default',
|
|
278
|
+
'visibleWidth',
|
|
279
|
+
'wrap',
|
|
280
|
+
],
|
|
281
|
+
'paratext': [
|
|
282
|
+
'AnsiEscapes',
|
|
283
|
+
'Capability',
|
|
284
|
+
'CapabilityError',
|
|
285
|
+
'ConEmu',
|
|
286
|
+
'DEPRECATED',
|
|
287
|
+
'Fields',
|
|
288
|
+
'ImageOptions',
|
|
289
|
+
'NotImplemented',
|
|
290
|
+
'Runtime',
|
|
291
|
+
'Support',
|
|
292
|
+
'ansiEscapesFor',
|
|
293
|
+
'beep',
|
|
294
|
+
'beginSynchronizedOutput',
|
|
295
|
+
'bell',
|
|
296
|
+
'builtins',
|
|
297
|
+
'capabilities',
|
|
298
|
+
'capability',
|
|
299
|
+
'check',
|
|
300
|
+
'clearScreen',
|
|
301
|
+
'clearTerminal',
|
|
302
|
+
'clearViewport',
|
|
303
|
+
'clipboard',
|
|
304
|
+
'cursorBackward',
|
|
305
|
+
'cursorDown',
|
|
306
|
+
'cursorForward',
|
|
307
|
+
'cursorGetPosition',
|
|
308
|
+
'cursorHide',
|
|
309
|
+
'cursorLeft',
|
|
310
|
+
'cursorMove',
|
|
311
|
+
'cursorNextLine',
|
|
312
|
+
'cursorPrevLine',
|
|
313
|
+
'cursorRestorePosition',
|
|
314
|
+
'cursorSavePosition',
|
|
315
|
+
'cursorShow',
|
|
316
|
+
'cursorTo',
|
|
317
|
+
'cursorUp',
|
|
318
|
+
'cwd',
|
|
319
|
+
'default',
|
|
320
|
+
'emit',
|
|
321
|
+
'endSynchronizedOutput',
|
|
322
|
+
'enterAlternativeScreen',
|
|
323
|
+
'eraseDown',
|
|
324
|
+
'eraseEndLine',
|
|
325
|
+
'eraseLine',
|
|
326
|
+
'eraseLines',
|
|
327
|
+
'eraseScreen',
|
|
328
|
+
'eraseStartLine',
|
|
329
|
+
'eraseUp',
|
|
330
|
+
'exitAlternativeScreen',
|
|
331
|
+
'fieldsUsed',
|
|
332
|
+
'iTerm',
|
|
333
|
+
'image',
|
|
334
|
+
'isDeprecation',
|
|
335
|
+
'link',
|
|
336
|
+
'notify',
|
|
337
|
+
'processRuntime',
|
|
338
|
+
'refusals',
|
|
339
|
+
'register',
|
|
340
|
+
'registerBuiltins',
|
|
341
|
+
'render',
|
|
342
|
+
'reset',
|
|
343
|
+
'scrollDown',
|
|
344
|
+
'scrollUp',
|
|
345
|
+
'setCwd',
|
|
346
|
+
'supports',
|
|
347
|
+
'synchronizedOutput',
|
|
348
|
+
'title',
|
|
349
|
+
],
|
|
350
|
+
'paratext/terminal-link': [
|
|
351
|
+
'LinkOptions',
|
|
352
|
+
'Options',
|
|
353
|
+
'Target',
|
|
354
|
+
'TerminalLink',
|
|
355
|
+
'default',
|
|
356
|
+
'terminalLinkFor',
|
|
357
|
+
],
|
|
358
|
+
'roundel/chalk': [
|
|
359
|
+
'BackgroundColor',
|
|
360
|
+
'BackgroundColorName',
|
|
361
|
+
'Chalk',
|
|
362
|
+
'ChalkInstance',
|
|
363
|
+
'ChalkOptions',
|
|
364
|
+
'Color',
|
|
365
|
+
'ColorInfo',
|
|
366
|
+
'ColorName',
|
|
367
|
+
'ColorSupport',
|
|
368
|
+
'ColorSupportLevel',
|
|
369
|
+
'ForegroundColor',
|
|
370
|
+
'ForegroundColorName',
|
|
371
|
+
'ModifierName',
|
|
372
|
+
'Modifiers',
|
|
373
|
+
'Options',
|
|
374
|
+
'UnderlineColorName',
|
|
375
|
+
'backgroundColorNames',
|
|
376
|
+
'chalkStderr',
|
|
377
|
+
'colorNames',
|
|
378
|
+
'default',
|
|
379
|
+
'foregroundColorNames',
|
|
380
|
+
'modifierNames',
|
|
381
|
+
'supportsColor',
|
|
382
|
+
'supportsColorStderr',
|
|
383
|
+
'underlineColorNames',
|
|
384
|
+
],
|
|
385
|
+
'seniority/lilconfig': [
|
|
386
|
+
'AsyncSearcher',
|
|
387
|
+
'LilconfigResult',
|
|
388
|
+
'Loader',
|
|
389
|
+
'LoaderSync',
|
|
390
|
+
'Loaders',
|
|
391
|
+
'LoadersSync',
|
|
392
|
+
'Options',
|
|
393
|
+
'OptionsSync',
|
|
394
|
+
'SyncSearcher',
|
|
395
|
+
'Transform',
|
|
396
|
+
'TransformSync',
|
|
397
|
+
'defaultLoaders',
|
|
398
|
+
'defaultLoadersSync',
|
|
399
|
+
'lilconfig',
|
|
400
|
+
'lilconfigSync',
|
|
401
|
+
],
|
|
402
|
+
'seniority/rc': [
|
|
403
|
+
'RcConfig',
|
|
404
|
+
'RcOptions',
|
|
405
|
+
'RcParse',
|
|
406
|
+
'default',
|
|
407
|
+
'module.exports',
|
|
408
|
+
'parse',
|
|
409
|
+
'rc',
|
|
410
|
+
],
|
|
96
411
|
};
|
|
412
|
+
export const REQUIRE_NAMESPACE = ['ansi-escapes', 'chalk', 'ora', 'log-update', 'boxen', 'string-width', 'strip-ansi', 'wrap-ansi', 'slice-ansi', 'restore-cursor', 'exit-hook', 'terminal-link'];
|
|
413
|
+
function requireShapesDiffer(from, to) {
|
|
414
|
+
const exported = FACADE_EXPORTS[to] ?? [];
|
|
415
|
+
const toGivesDefault = exported.includes('module.exports');
|
|
416
|
+
if (REQUIRE_NAMESPACE.includes(from))
|
|
417
|
+
return toGivesDefault;
|
|
418
|
+
return exported.includes('default') && !toGivesDefault;
|
|
419
|
+
}
|
|
97
420
|
const WORD = /[A-Za-z0-9_$]/;
|
|
98
421
|
const DIVIDES = new Set([')', ']', '}']);
|
|
99
422
|
function startsRegex(previous) {
|
|
@@ -220,6 +543,8 @@ function siteOf(source, at, tokens) {
|
|
|
220
543
|
const site = { specifier: source.slice(open + 1, close - 1), start: open + 1, end: close - 1, line };
|
|
221
544
|
if (tokens.previous === 'from' && tokens.clause !== undefined)
|
|
222
545
|
site.clause = tokens.clause.slice(0, -1);
|
|
546
|
+
if (tokens.previous === '(' && tokens.before === 'require')
|
|
547
|
+
site.require = true;
|
|
223
548
|
return site;
|
|
224
549
|
}
|
|
225
550
|
export function scan(source) {
|
|
@@ -293,27 +618,40 @@ function missingFrom(site, to) {
|
|
|
293
618
|
const { typeOnly, names } = bindingsOf(site.clause);
|
|
294
619
|
return { typeOnly, missing: names.filter((name) => !exported.includes(name)) };
|
|
295
620
|
}
|
|
296
|
-
|
|
621
|
+
function classify(site) {
|
|
622
|
+
const to = MAPPING[site.specifier];
|
|
623
|
+
if (to === undefined)
|
|
624
|
+
return 'unmapped';
|
|
625
|
+
if (site.require === true && requireShapesDiffer(site.specifier, to))
|
|
626
|
+
return { line: site.line, specifier: site.specifier, reason: 'require-of-default' };
|
|
627
|
+
const { typeOnly, missing } = missingFrom(site, to);
|
|
628
|
+
if (missing.length === 0)
|
|
629
|
+
return 'moves';
|
|
630
|
+
if (typeOnly)
|
|
631
|
+
return { line: site.line, specifier: site.specifier, names: missing, note: `${to} does not export ${missing.join(', ')}; this type-only import stays on '${site.specifier}', so keep its types installed` };
|
|
632
|
+
return { line: site.line, specifier: site.specifier, reason: 'unknown-export', names: missing };
|
|
633
|
+
}
|
|
634
|
+
const NONE = new Set();
|
|
635
|
+
export function rewriteSource(source, skip = NONE) {
|
|
297
636
|
if (!mentionsAHost(source))
|
|
298
637
|
return { source, mapped: [], refused: [], kept: [], relevant: false };
|
|
299
638
|
const { sites, nonLiteral } = scan(source);
|
|
300
|
-
const hits = sites.filter((s) => MAPPING[s.specifier] !== undefined || isDeep(s.specifier));
|
|
639
|
+
const hits = sites.filter((s) => !skip.has(packageOf(s.specifier)) && (MAPPING[s.specifier] !== undefined || isDeep(s.specifier)));
|
|
301
640
|
if (hits.length === 0)
|
|
302
641
|
return { source, mapped: [], refused: [], kept: [], relevant: false };
|
|
303
642
|
const kept = [];
|
|
304
643
|
const unknown = [];
|
|
305
644
|
const moving = [];
|
|
306
645
|
for (const site of hits) {
|
|
307
|
-
const
|
|
308
|
-
if (
|
|
646
|
+
const verdict = classify(site);
|
|
647
|
+
if (verdict === 'unmapped')
|
|
309
648
|
continue;
|
|
310
|
-
|
|
311
|
-
if (missing.length === 0)
|
|
649
|
+
if (verdict === 'moves')
|
|
312
650
|
moving.push(site);
|
|
313
|
-
else if (
|
|
314
|
-
kept.push(
|
|
651
|
+
else if ('note' in verdict)
|
|
652
|
+
kept.push(verdict);
|
|
315
653
|
else
|
|
316
|
-
unknown.push(
|
|
654
|
+
unknown.push(verdict);
|
|
317
655
|
}
|
|
318
656
|
const refused = [
|
|
319
657
|
...hits.filter((s) => isDeep(s.specifier)).map((s) => ({ line: s.line, specifier: s.specifier, reason: 'deep-import' })),
|
|
@@ -345,16 +683,48 @@ export function sourceFiles(dir, at = '', found = []) {
|
|
|
345
683
|
}
|
|
346
684
|
return found;
|
|
347
685
|
}
|
|
348
|
-
async function
|
|
686
|
+
async function declaredDependencies(dir) {
|
|
349
687
|
try {
|
|
350
688
|
const raw = JSON.parse(await readFile(join(dir, 'package.json'), 'utf8'));
|
|
351
|
-
|
|
352
|
-
return HOSTS.filter((host) => declared[host] !== undefined);
|
|
689
|
+
return new Map(Object.entries({ ...raw.dependencies, ...raw.devDependencies }));
|
|
353
690
|
}
|
|
354
691
|
catch {
|
|
355
|
-
return
|
|
692
|
+
return new Map();
|
|
356
693
|
}
|
|
357
694
|
}
|
|
695
|
+
async function installedVersion(dir, name) {
|
|
696
|
+
try {
|
|
697
|
+
return JSON.parse(await readFile(join(dir, 'node_modules', name, 'package.json'), 'utf8')).version;
|
|
698
|
+
}
|
|
699
|
+
catch {
|
|
700
|
+
return undefined;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
async function offMajorOf(dir, dependencies) {
|
|
704
|
+
const found = await Promise.all(HOSTS.map(async (from) => ({ from, found: (await installedVersion(dir, from)) ?? dependencies.get(from) })));
|
|
705
|
+
return found.flatMap(({ from, found: version }) => {
|
|
706
|
+
const graded = GRADED_VERSIONS[from];
|
|
707
|
+
if (version === undefined || graded === undefined)
|
|
708
|
+
return [];
|
|
709
|
+
const major = majorOf(version);
|
|
710
|
+
return major === undefined || major === majorOf(graded) ? [] : [{ from, found: version, graded }];
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
function installer(dir) {
|
|
714
|
+
const has = (file) => existsSync(join(dir, file));
|
|
715
|
+
if (has('pnpm-lock.yaml'))
|
|
716
|
+
return { add: 'pnpm add', remove: 'pnpm remove' };
|
|
717
|
+
if (has('yarn.lock'))
|
|
718
|
+
return { add: 'yarn add', remove: 'yarn remove' };
|
|
719
|
+
if (has('bun.lockb') || has('bun.lock'))
|
|
720
|
+
return { add: 'bun add', remove: 'bun remove' };
|
|
721
|
+
return { add: 'npm install', remove: 'npm uninstall' };
|
|
722
|
+
}
|
|
723
|
+
function nextStep(dir, add, remove) {
|
|
724
|
+
const pm = installer(dir);
|
|
725
|
+
const steps = [add.length > 0 ? `${pm.add} ${add.join(' ')}` : '', remove.length > 0 ? `${pm.remove} ${remove.join(' ')}` : ''];
|
|
726
|
+
return steps.filter((c) => c !== '').join(' && ');
|
|
727
|
+
}
|
|
358
728
|
export async function workingTree(dir) {
|
|
359
729
|
const result = await run('git', ['-C', dir, 'status', '--porcelain'], { runtime: ambientRuntime(), stdio: 'pipe' });
|
|
360
730
|
if (!result.ok)
|
|
@@ -371,9 +741,9 @@ export class DirtyTreeError extends Error {
|
|
|
371
741
|
}
|
|
372
742
|
}
|
|
373
743
|
const EMPTY = { source: '', mapped: [], refused: [], kept: [], relevant: false };
|
|
374
|
-
async function migrateBatch(dir, batch, write) {
|
|
744
|
+
async function migrateBatch(dir, batch, write, skip) {
|
|
375
745
|
const sources = await Promise.all(batch.map(async (file) => await readFile(join(dir, file))));
|
|
376
|
-
const results = sources.map((bytes) => (mentionsAHost(bytes) ? rewriteSource(bytes.toString('utf8')) : EMPTY));
|
|
746
|
+
const results = sources.map((bytes) => (mentionsAHost(bytes) ? rewriteSource(bytes.toString('utf8'), skip) : EMPTY));
|
|
377
747
|
if (write)
|
|
378
748
|
await Promise.all(results.map(async (r, i) => (r.mapped.length === 0 ? undefined : await writeFile(join(dir, batch[i]), r.source))));
|
|
379
749
|
return results;
|
|
@@ -386,7 +756,8 @@ function rollup(all) {
|
|
|
386
756
|
})
|
|
387
757
|
.filter((row) => row.imports > 0);
|
|
388
758
|
}
|
|
389
|
-
function gradedFor(
|
|
759
|
+
function gradedFor(packages) {
|
|
760
|
+
const hosts = [...new Set(packages.map((p) => HOST_OF.get(p)).filter((h) => h !== undefined))];
|
|
390
761
|
return hosts.filter((host) => GRADED[host] !== undefined).map((host) => ({ host, ...GRADED[host] }));
|
|
391
762
|
}
|
|
392
763
|
const BATCH = 256;
|
|
@@ -395,30 +766,38 @@ export async function migrate(options) {
|
|
|
395
766
|
const entries = await (options.status ?? workingTree)(dir);
|
|
396
767
|
if (!dryRun && !force && entries !== undefined && entries.length > 0)
|
|
397
768
|
throw new DirtyTreeError(entries);
|
|
769
|
+
const dependencies = await declaredDependencies(dir);
|
|
770
|
+
const offMajor = await offMajorOf(dir, dependencies);
|
|
771
|
+
const skip = new Set(offMajor.map((o) => o.from));
|
|
398
772
|
const files = sourceFiles(dir);
|
|
399
773
|
const results = [];
|
|
400
774
|
for (let i = 0; i < files.length; i += BATCH) {
|
|
401
775
|
const batch = files.slice(i, i + BATCH);
|
|
402
|
-
const done = await migrateBatch(dir, batch, !dryRun);
|
|
776
|
+
const done = await migrateBatch(dir, batch, !dryRun, skip);
|
|
403
777
|
results.push(...done.map((result, k) => ({ file: batch[k], result })));
|
|
404
778
|
}
|
|
405
779
|
const all = results.flatMap(({ file, result }) => result.mapped.map((m) => ({ ...m, file })));
|
|
406
780
|
const refused = results.flatMap(({ file, result }) => result.refused.map((r) => ({ file, ...r })));
|
|
407
781
|
const kept = results.flatMap(({ file, result }) => result.kept.map((k) => ({ file, ...k })));
|
|
408
|
-
const imported = [...new Set(results.flatMap(({ result }) => (result.relevant ? [...result.mapped.map((m) => m.from), ...result.kept.map((k) => k.specifier)] : [])))];
|
|
409
|
-
const declared =
|
|
410
|
-
const stillUsed = new Set([...refused, ...kept].map((r) => r.specifier
|
|
411
|
-
const removable = declared.filter((host) => !stillUsed.has(host));
|
|
782
|
+
const imported = [...new Set(results.flatMap(({ result }) => (result.relevant ? [...result.mapped.map((m) => packageOf(m.from)), ...result.kept.map((k) => packageOf(k.specifier))] : [])))].sort();
|
|
783
|
+
const declared = HOSTS.filter((host) => dependencies.has(host));
|
|
784
|
+
const stillUsed = new Set([...refused, ...kept].map((r) => packageOf(r.specifier)));
|
|
785
|
+
const removable = declared.filter((host) => !stillUsed.has(host) && !skip.has(host));
|
|
412
786
|
const touched = [...new Set(all.map((m) => m.file))];
|
|
787
|
+
const add = [...new Set(all.map((m) => packageOf(m.to)))].filter((p) => !dependencies.has(p)).sort();
|
|
788
|
+
const partial = PARTIAL.filter((d) => dependencies.has(d.from)).map((d) => ({ from: d.from, to: d.to, ...GRADED[d.host] }));
|
|
413
789
|
return {
|
|
414
790
|
files: touched.length,
|
|
415
791
|
imports: all.length,
|
|
416
792
|
mapped: rollup(all),
|
|
417
793
|
refused,
|
|
418
794
|
kept,
|
|
419
|
-
detected: { declared, imported
|
|
420
|
-
dependencies: { before: declared, removable, after: declared.length - removable.length },
|
|
421
|
-
graded: gradedFor([...new Set([...declared, ...imported
|
|
795
|
+
detected: { declared, imported },
|
|
796
|
+
dependencies: { before: declared, removable, after: declared.length - removable.length, add },
|
|
797
|
+
graded: gradedFor([...new Set([...declared, ...imported])].sort()),
|
|
798
|
+
partial,
|
|
799
|
+
offMajor,
|
|
800
|
+
next: nextStep(dir, add, removable),
|
|
422
801
|
dryRun,
|
|
423
802
|
changed: !dryRun && touched.length > 0,
|
|
424
803
|
exitCode: refused.length > 0 ? ExitCode.RUNTIME : ExitCode.OK,
|
|
@@ -0,0 +1,7 @@
|
|
|
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
|
+
import { type Plugin } from './plugin.js';
|
|
7
|
+
export declare function runParseHooks(plugins: readonly Plugin[], argv: string[]): Promise<string[]>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { hookApplies } from './manifest.js';
|
|
2
|
+
export async function runParseHooks(plugins, argv) {
|
|
3
|
+
let current = argv;
|
|
4
|
+
for (const plugin of plugins) {
|
|
5
|
+
const hook = plugin.hooks?.parse;
|
|
6
|
+
const typed = current.join(' ');
|
|
7
|
+
if (!hookApplies(hook, typed))
|
|
8
|
+
continue;
|
|
9
|
+
const next = await hook.handler({ command: typed, options: {}, argv: [...current] });
|
|
10
|
+
if (next === undefined)
|
|
11
|
+
continue;
|
|
12
|
+
if (!Array.isArray(next) || !next.every((a) => typeof a === 'string'))
|
|
13
|
+
throw new Error(`burgee: plugin "${plugin.name}"'s parse hook returned ${typeof next}; return string[] or nothing`);
|
|
14
|
+
current = next;
|
|
15
|
+
}
|
|
16
|
+
return current;
|
|
17
|
+
}
|
package/dist/plugin.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type CommandNode, type Hook } from './manifest.js';
|
|
1
|
+
import { type CommandNode, type Hook, type HookStage } from './manifest.js';
|
|
2
2
|
/**
|
|
3
3
|
* The plugin contract version. One number for the family — the same `1` flagstaff, caique and
|
|
4
4
|
* closeout declare, written out rather than imported because a layer never imports a layer.
|
|
@@ -12,11 +12,7 @@ export interface Plugin {
|
|
|
12
12
|
name: string;
|
|
13
13
|
contract?: number;
|
|
14
14
|
commands?: CommandNode[];
|
|
15
|
-
hooks?:
|
|
16
|
-
preRun?: Hook;
|
|
17
|
-
postRun?: Hook;
|
|
18
|
-
onError?: Hook;
|
|
19
|
-
};
|
|
15
|
+
hooks?: Partial<Record<HookStage, Hook>>;
|
|
20
16
|
enforce?: 'pre' | 'post';
|
|
21
17
|
}
|
|
22
18
|
export type PluginErrorCode = 'E_PLUGIN_SCHEMA' | 'E_PLUGIN_CONTRACT' | 'E_NO_CONTRIBUTION';
|
package/dist/plugin.js
CHANGED
|
@@ -13,7 +13,7 @@ export class PluginError extends Error {
|
|
|
13
13
|
}
|
|
14
14
|
const isRecord = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
15
15
|
const ENFORCE = ['pre', 'post'];
|
|
16
|
-
const STAGES = ['preRun', 'postRun', 'onError'];
|
|
16
|
+
const STAGES = ['parse', 'preRun', 'postRun', 'onError', 'shutdown'];
|
|
17
17
|
const schema = (message, fix) => new PluginError('E_PLUGIN_SCHEMA', message, fix);
|
|
18
18
|
export function validate(plugin, taken = []) {
|
|
19
19
|
if (!isRecord(plugin))
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"$schema":"https://json-schema.org/draft/2020-12/schema","$id":"https://burgee.dev/program-schema.json","title":"burgee --schema","description":"The document `<program> --schema` prints: the program as data, for an agent or an MCP host to read without running anything. Published so a reader can validate what a program says about itself (burgee F1, D-123); burgee's own test validates `--schema` output against this file on every run.","type":"object","required":["schemaVersion","name","exitCodes","commands"],"additionalProperties":false,"properties":{"schemaVersion":{"const":1,"description":"Bumped only when a field changes meaning."},"name":{"type":"string","description":"The program as typed."},"version":{"type":"string"},"description":{"type":"string"},"exitCodes":{"type":"object","additionalProperties":{"type":"integer"},"description":"What each exit code means (F1): the contract's names and numbers, so a caller branches on the code without reading prose."},"commands":{"type":"array","description":"Every runnable, visible command, in declaration order.","items":{"$ref":"#/$defs/command"}}},"$defs":{"command":{"type":"object","required":["name","arguments","options","examples","inputSchema"],"additionalProperties":false,"properties":{"name":{"type":"string","description":"The command as typed, without the program name: `config get`."},"description":{"type":"string"},"summary":{"type":"string"},"effects":{"type":"string","pattern":"^(read_only|idempotent|non_idempotent|withheld)$","description":"What running it does to the world, or `withheld` (N6)."},"deprecated":{"description":"`true`, or the replacement's name (M5)."},"group":{"type":"string"},"lazy":{"const":true,"description":"Its handler loads on dispatch (M2)."},"plugin":{"type":"string","description":"The plugin that contributed it (M3)."},"fields":{"type":"array","items":{"type":"string"},"description":"The result's top-level fields, what `--json=` selects from (N14)."},"arguments":{"type":"array","items":{"$ref":"#/$defs/argument"}},"options":{"type":"object","additionalProperties":{"$ref":"#/$defs/option"}},"relations":{"type":"array","items":{"type":"object"},"description":"Constraints between options (S2, S6): exactlyOneOf, atLeastOneOf, atMostOneOf, conflicts, implies."},"examples":{"type":"array","items":{"$ref":"#/$defs/example"}},"inputSchema":{"$ref":"#/$defs/inputSchema"}}},"argument":{"type":"object","required":["name"],"additionalProperties":false,"properties":{"name":{"type":"string"},"description":{"type":"string"},"required":{"type":"boolean"},"variadic":{"type":"boolean"},"default":{"type":"string"},"type":{"type":"string","pattern":"^file$","description":"`file`: `-` means standard input (S4)."}}},"option":{"type":"object","required":["type"],"additionalProperties":false,"properties":{"type":{"type":"string","pattern":"^(string|boolean|number)$"},"description":{"type":"string"},"required":{"type":"boolean"},"short":{"type":"string"},"default":{"description":"A string, boolean, number, or a list of strings or numbers."},"env":{"type":"string"},"choices":{"type":"array","items":{"type":"string"}},"multiple":{"type":"boolean"},"separator":{"type":"string"},"dependsOn":{"type":"array","items":{"type":"string"}},"exclusive":{"type":"array","items":{"type":"string"}},"minimum":{"type":"number"},"maximum":{"type":"number"},"integer":{"type":"boolean"},"schema":{"type":"object","description":"A Standard Schema validator, as its own library serialises it."},"placeholder":{"type":"string"},"deprecated":{"description":"`true`, or the replacement's name."},"hidden":{"type":"boolean"},"sharedFrom":{"type":"string"}}},"example":{"type":"object","required":["command"],"additionalProperties":false,"properties":{"command":{"type":"string"},"description":{"type":"string"}}},"inputSchema":{"type":"object","required":["type","properties","required","additionalProperties"],"additionalProperties":false,"description":"The arguments and options as one JSON Schema object — what an MCP tool call takes.","properties":{"type":{"const":"object"},"properties":{"type":"object"},"required":{"type":"array","items":{"type":"string"}},"additionalProperties":{"const":false}}}}}
|