@kaynooo/cli-doctor 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/README.md +8 -0
- package/dist/cli-bin.js +532 -113
- package/dist/cli.js +468 -104
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -2,16 +2,19 @@
|
|
|
2
2
|
// @bun
|
|
3
3
|
|
|
4
4
|
// src/cli.ts
|
|
5
|
-
import
|
|
5
|
+
import process11 from "process";
|
|
6
6
|
|
|
7
7
|
// ../kit/src/args.ts
|
|
8
|
-
import
|
|
8
|
+
import process4 from "process";
|
|
9
9
|
import { parseArgs } from "util";
|
|
10
10
|
|
|
11
|
+
// ../kit/src/format.ts
|
|
12
|
+
import process2 from "process";
|
|
13
|
+
|
|
11
14
|
// ../kit/src/style.ts
|
|
12
15
|
import process from "process";
|
|
13
16
|
|
|
14
|
-
// ../../node_modules/.bun/@kaynooo+utils@2.31.
|
|
17
|
+
// ../../node_modules/.bun/@kaynooo+utils@2.31.2+d0769e28dc4dad16/node_modules/@kaynooo/utils/dist/chunk-mtetydvc.js
|
|
15
18
|
function x(s) {
|
|
16
19
|
let r = s.startsWith("#") ? s.slice(1).toLowerCase() : s.toLowerCase();
|
|
17
20
|
if (!/^[a-f0-9]+$/.test(r))
|
|
@@ -231,7 +234,7 @@ class e {
|
|
|
231
234
|
}
|
|
232
235
|
}
|
|
233
236
|
|
|
234
|
-
// ../../node_modules/.bun/@kaynooo+utils@2.31.
|
|
237
|
+
// ../../node_modules/.bun/@kaynooo+utils@2.31.2+d0769e28dc4dad16/node_modules/@kaynooo/utils/dist/chunk-y1msbssb.js
|
|
235
238
|
var r = 39;
|
|
236
239
|
var o = 49;
|
|
237
240
|
var f2 = "\x1B[";
|
|
@@ -293,11 +296,102 @@ function upTo(text, separator) {
|
|
|
293
296
|
const end = text.indexOf(separator);
|
|
294
297
|
return end === -1 ? text : text.slice(0, end);
|
|
295
298
|
}
|
|
299
|
+
function truncate(text, width) {
|
|
300
|
+
return text.length <= width ? text : `${text.slice(0, Math.max(0, width - 1))}\u2026`;
|
|
301
|
+
}
|
|
302
|
+
var LINE_BREAKS = /\s*\n\s*/g;
|
|
296
303
|
var ESCAPE = String.fromCharCode(27);
|
|
304
|
+
function textWidth(text) {
|
|
305
|
+
return Bun.stringWidth(text);
|
|
306
|
+
}
|
|
307
|
+
function table(rows, { cellWidth, header = false, align = [], fit } = {}) {
|
|
308
|
+
const cells = cellWidth === undefined ? rows : rows.map((row) => row.map((cell) => truncate(cell.replaceAll(LINE_BREAKS, " "), cellWidth)));
|
|
309
|
+
const widths = [];
|
|
310
|
+
for (const row of cells) {
|
|
311
|
+
for (const [index, cell] of row.entries())
|
|
312
|
+
widths[index] = Math.max(widths[index] ?? 0, textWidth(cell));
|
|
313
|
+
}
|
|
314
|
+
const shown = fit === undefined ? cells : narrow(cells, widths, fit);
|
|
315
|
+
const colors = paint(process2.stdout);
|
|
316
|
+
const lines = shown.map((row) => row.map((cell, index) => {
|
|
317
|
+
const width = widths[index] ?? 0;
|
|
318
|
+
const gap = " ".repeat(Math.max(0, width - textWidth(cell)));
|
|
319
|
+
if (align[index] === "right")
|
|
320
|
+
return `${gap}${cell}`;
|
|
321
|
+
return index === row.length - 1 ? cell : `${cell}${gap}`;
|
|
322
|
+
}).join(" ").trimEnd());
|
|
323
|
+
return lines.map((line, index) => header && index === 0 ? colors.bold(line) : line).join(`
|
|
324
|
+
`);
|
|
325
|
+
}
|
|
326
|
+
function narrow(rows, widths, limit) {
|
|
327
|
+
const used = () => widths.reduce((sum, width) => sum + width, 0) + 2 * Math.max(0, widths.length - 1);
|
|
328
|
+
const cuttable = (index) => rows.every((row) => !(row[index] ?? "").includes(ESCAPE));
|
|
329
|
+
const floor = 8;
|
|
330
|
+
while (used() > limit) {
|
|
331
|
+
let widest = -1;
|
|
332
|
+
for (const [index, width] of widths.entries()) {
|
|
333
|
+
if (cuttable(index) && width > floor && width > (widths[widest] ?? 0))
|
|
334
|
+
widest = index;
|
|
335
|
+
}
|
|
336
|
+
if (widest === -1)
|
|
337
|
+
break;
|
|
338
|
+
widths[widest] = Math.max(floor, (widths[widest] ?? 0) - (used() - limit));
|
|
339
|
+
}
|
|
340
|
+
return rows.map((row) => row.map((cell, index) => textWidth(cell) > (widths[index] ?? 0) ? truncate(cell, widths[index] ?? 0) : cell));
|
|
341
|
+
}
|
|
297
342
|
function printJson(value) {
|
|
298
343
|
console.log(JSON.stringify(value, undefined, 2));
|
|
299
344
|
}
|
|
300
345
|
|
|
346
|
+
// ../kit/src/help.ts
|
|
347
|
+
import process3 from "process";
|
|
348
|
+
var OPTIONAL = /\[[^\]]+\]/g;
|
|
349
|
+
var USAGE_PREFIX = /^usage:\s*/i;
|
|
350
|
+
var FULL_STOP = /\.$/;
|
|
351
|
+
function usageLine(usage, colors = paint(process3.stdout)) {
|
|
352
|
+
return `${colors.bold("Usage:")} ${usage.replace(USAGE_PREFIX, "").replaceAll(OPTIONAL, (part) => colors.dim(part))}`;
|
|
353
|
+
}
|
|
354
|
+
function helpText(usage, options, colors = paint(process3.stdout), { examples = [], replaces } = {}) {
|
|
355
|
+
return [usageLine(usage, colors), ...replacesRows(replaces, colors), "", colors.bold("Options:"), ...optionRows(options, colors), ...exampleRows(examples, colors)].join(`
|
|
356
|
+
`);
|
|
357
|
+
}
|
|
358
|
+
function replacesRows(replaces, colors = paint(process3.stdout)) {
|
|
359
|
+
return replaces === undefined ? [] : ["", `${colors.bold("Instead of:")} ${replaces}`];
|
|
360
|
+
}
|
|
361
|
+
function wantsHelpJson(env = process3.env) {
|
|
362
|
+
return env.KIT_HELP === "json";
|
|
363
|
+
}
|
|
364
|
+
function description(usage, { examples = [], replaces } = {}) {
|
|
365
|
+
const lines = usage.replace(USAGE_PREFIX, "").split(`
|
|
366
|
+
`);
|
|
367
|
+
const first = lines[0];
|
|
368
|
+
const colon = first.indexOf(": ");
|
|
369
|
+
const prose = lines[lines.indexOf("") + 1] ?? "";
|
|
370
|
+
const fallback = lines.includes("") ? `${prose.charAt(0).toLowerCase()}${prose.slice(1)}`.replace(FULL_STOP, "") : "";
|
|
371
|
+
return {
|
|
372
|
+
synopsis: colon === -1 ? first : first.slice(0, colon),
|
|
373
|
+
summary: colon === -1 ? fallback : first.slice(colon + 2),
|
|
374
|
+
...replaces === undefined ? {} : { replaces },
|
|
375
|
+
examples: examples.map((example) => typeof example === "string" ? { line: example } : { line: example[0], note: example[1] })
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
function exampleRows(examples, colors = paint(process3.stdout)) {
|
|
379
|
+
if (examples.length === 0)
|
|
380
|
+
return [];
|
|
381
|
+
return ["", colors.bold("Examples:"), ...examples.map((example) => typeof example === "string" ? ` ${example}` : ` ${example[0]} ${colors.dim(`# ${example[1]}`)}`)];
|
|
382
|
+
}
|
|
383
|
+
function optionRows(options, colors = paint(process3.stdout)) {
|
|
384
|
+
const rows = Object.entries(options).map(([name, option]) => {
|
|
385
|
+
const short = option.short === undefined ? " " : `${colors.cyan(`-${option.short}`)}, `;
|
|
386
|
+
const named = option.choices === undefined ? "<value>" : `<${option.choices.join("|")}>`;
|
|
387
|
+
const placeholder = option.optional === undefined ? named : `[${named}]`;
|
|
388
|
+
const value = option.type === "string" ? ` ${colors.dim(placeholder)}` : "";
|
|
389
|
+
return { flag: `${short}${colors.cyan(`--${name}`)}${value}`, width: `-x, --${name}`.length + (option.type === "string" ? placeholder.length + 1 : 0), description: option.description ?? "" };
|
|
390
|
+
});
|
|
391
|
+
const width = Math.max(0, ...rows.map((row) => row.width));
|
|
392
|
+
return rows.map((row) => row.description === "" ? ` ${row.flag}` : ` ${row.flag}${" ".repeat(width - row.width)} ${colors.dim(row.description)}`);
|
|
393
|
+
}
|
|
394
|
+
|
|
301
395
|
// ../kit/src/text.ts
|
|
302
396
|
var ANSI = new RegExp(`${String.fromCharCode(27)}\\[[\\d;?]*[a-z]`, "gi");
|
|
303
397
|
function distance(a, b) {
|
|
@@ -317,8 +411,21 @@ function closest(name, names) {
|
|
|
317
411
|
const [best] = names.map((known) => ({ known, far: distance(name, known) })).sort((a, b) => a.far - b.far);
|
|
318
412
|
return best !== undefined && best.far <= (name.length < 4 ? 1 : 2) ? best.known : undefined;
|
|
319
413
|
}
|
|
414
|
+
// ../kit/src/input.ts
|
|
415
|
+
var DURATION = /^(\d+(?:\.\d+)?)(ms|[smhd])?$/;
|
|
416
|
+
var UNIT_MS = { ms: 1, s: 1000, m: 60000, h: 3600000, d: 86400000 };
|
|
417
|
+
function duration(value, name, fallback, unit = "s") {
|
|
418
|
+
if (value === undefined)
|
|
419
|
+
return fallback;
|
|
420
|
+
const match = DURATION.exec(value.trim());
|
|
421
|
+
const ms = match === null ? 0 : Number(match[1]) * UNIT_MS[match[2] ?? unit];
|
|
422
|
+
if (ms < 1)
|
|
423
|
+
badUsage(`--${name} takes a duration such as 30s, 5m, 1h or 7d, not ${value}`);
|
|
424
|
+
return ms;
|
|
425
|
+
}
|
|
320
426
|
|
|
321
427
|
// ../kit/src/args.ts
|
|
428
|
+
var DASHES = /^-+/;
|
|
322
429
|
function jsonOption(holds) {
|
|
323
430
|
return { type: "boolean", description: holds === undefined ? "print JSON" : `print JSON: ${holds}` };
|
|
324
431
|
}
|
|
@@ -349,61 +456,20 @@ class HelpShown extends Exit {
|
|
|
349
456
|
|
|
350
457
|
class Completing {
|
|
351
458
|
options;
|
|
352
|
-
|
|
459
|
+
positionals;
|
|
460
|
+
candidates;
|
|
461
|
+
constructor(options, positionals = [], candidates = {}) {
|
|
353
462
|
this.options = options;
|
|
463
|
+
this.positionals = positionals;
|
|
464
|
+
this.candidates = candidates;
|
|
354
465
|
}
|
|
355
466
|
}
|
|
356
467
|
var completion = { active: false };
|
|
357
|
-
var OPTIONAL = /\[[^\]]+\]/g;
|
|
358
|
-
var USAGE_PREFIX = /^usage:\s*/i;
|
|
359
|
-
var FULL_STOP = /\.$/;
|
|
360
|
-
var DASHES = /^-+/;
|
|
361
|
-
function usageLine(usage, colors = paint(process2.stdout)) {
|
|
362
|
-
return `${colors.bold("Usage:")} ${usage.replace(USAGE_PREFIX, "").replaceAll(OPTIONAL, (part) => colors.dim(part))}`;
|
|
363
|
-
}
|
|
364
|
-
function helpText(usage, options, colors = paint(process2.stdout), { examples = [], replaces } = {}) {
|
|
365
|
-
return [usageLine(usage, colors), ...replacesRows(replaces, colors), "", colors.bold("Options:"), ...optionRows(options, colors), ...exampleRows(examples, colors)].join(`
|
|
366
|
-
`);
|
|
367
|
-
}
|
|
368
|
-
function replacesRows(replaces, colors = paint(process2.stdout)) {
|
|
369
|
-
return replaces === undefined ? [] : ["", `${colors.bold("Instead of:")} ${replaces}`];
|
|
370
|
-
}
|
|
371
|
-
function wantsHelpJson(env = process2.env) {
|
|
372
|
-
return env.KIT_HELP === "json";
|
|
373
|
-
}
|
|
374
|
-
function description(usage, { examples = [], replaces } = {}) {
|
|
375
|
-
const lines = usage.replace(USAGE_PREFIX, "").split(`
|
|
376
|
-
`);
|
|
377
|
-
const first = lines[0];
|
|
378
|
-
const colon = first.indexOf(": ");
|
|
379
|
-
const prose = lines[lines.indexOf("") + 1] ?? "";
|
|
380
|
-
const fallback = lines.includes("") ? `${prose.charAt(0).toLowerCase()}${prose.slice(1)}`.replace(FULL_STOP, "") : "";
|
|
381
|
-
return {
|
|
382
|
-
synopsis: colon === -1 ? first : first.slice(0, colon),
|
|
383
|
-
summary: colon === -1 ? fallback : first.slice(colon + 2),
|
|
384
|
-
...replaces === undefined ? {} : { replaces },
|
|
385
|
-
examples: examples.map((example) => typeof example === "string" ? { line: example } : { line: example[0], note: example[1] })
|
|
386
|
-
};
|
|
387
|
-
}
|
|
388
|
-
function exampleRows(examples, colors = paint(process2.stdout)) {
|
|
389
|
-
if (examples.length === 0)
|
|
390
|
-
return [];
|
|
391
|
-
return ["", colors.bold("Examples:"), ...examples.map((example) => typeof example === "string" ? ` ${example}` : ` ${example[0]} ${colors.dim(`# ${example[1]}`)}`)];
|
|
392
|
-
}
|
|
393
|
-
function optionRows(options, colors = paint(process2.stdout)) {
|
|
394
|
-
const rows = Object.entries(options).map(([name, option]) => {
|
|
395
|
-
const short = option.short === undefined ? " " : `${colors.cyan(`-${option.short}`)}, `;
|
|
396
|
-
const value = option.type === "string" ? ` ${colors.dim("<value>")}` : "";
|
|
397
|
-
return { flag: `${short}${colors.cyan(`--${name}`)}${value}`, width: `-x, --${name}`.length + (option.type === "string" ? " <value>".length : 0), description: option.description ?? "" };
|
|
398
|
-
});
|
|
399
|
-
const width = Math.max(0, ...rows.map((row) => row.width));
|
|
400
|
-
return rows.map((row) => row.description === "" ? ` ${row.flag}` : ` ${row.flag}${" ".repeat(width - row.width)} ${colors.dim(row.description)}`);
|
|
401
|
-
}
|
|
402
468
|
var OUTPUT_OPTIONS = {
|
|
403
469
|
color: { type: "string", description: "auto (on a terminal alone), always or never" },
|
|
404
470
|
plain: { type: "boolean", description: "plain text, no colour or spinner: --color never" }
|
|
405
471
|
};
|
|
406
|
-
function applyOutput(color, plain, env =
|
|
472
|
+
function applyOutput(color, plain, env = process4.env) {
|
|
407
473
|
const when = plain === true ? "never" : color ?? "auto";
|
|
408
474
|
if (when === "auto")
|
|
409
475
|
return;
|
|
@@ -420,14 +486,13 @@ function applyOutput(color, plain, env = process2.env) {
|
|
|
420
486
|
function parse(args, usage, options, settings = {}) {
|
|
421
487
|
const output = Object.fromEntries(Object.entries(OUTPUT_OPTIONS).filter(([name]) => !(name in options)));
|
|
422
488
|
const all = { ...options, ...output, help: { type: "boolean", short: "h", description: "show this help" } };
|
|
489
|
+
const listing = listed(all, settings.allowNegative === true);
|
|
423
490
|
if (completion.active)
|
|
424
|
-
throw new Completing(
|
|
425
|
-
const
|
|
426
|
-
const own = separator === -1 ? args : args.slice(0, separator);
|
|
427
|
-
const rest = separator === -1 ? undefined : args.slice(separator + 1);
|
|
491
|
+
throw new Completing(listing, settings.positionals, settings.candidates);
|
|
492
|
+
const { own, rest } = settings.trailing === true ? splitTrailing(args, all, settings.positionals?.length ?? 0) : splitRest(args, settings.rest === true);
|
|
428
493
|
let parsed;
|
|
429
494
|
try {
|
|
430
|
-
parsed = parseArgs({ args: joinDashValues(own, all), options: all, allowPositionals: true, allowNegative: settings.allowNegative === true, strict: true });
|
|
495
|
+
parsed = parseArgs({ args: joinDashValues(withBare(own, all), all), options: all, allowPositionals: true, allowNegative: settings.allowNegative === true, strict: true });
|
|
431
496
|
} catch (error) {
|
|
432
497
|
const message = parseError(error);
|
|
433
498
|
throw new UsageError(message, nearestOption(message, all, settings.allowNegative === true));
|
|
@@ -435,12 +500,84 @@ function parse(args, usage, options, settings = {}) {
|
|
|
435
500
|
const values = parsed.values;
|
|
436
501
|
applyOutput("color" in output ? values.color : undefined, "plain" in output ? values.plain : undefined);
|
|
437
502
|
if (values.help === true) {
|
|
438
|
-
console.log(wantsHelpJson() ? JSON.stringify(description(usage, settings)) : helpText(usage,
|
|
503
|
+
console.log(wantsHelpJson() ? JSON.stringify(description(usage, settings)) : helpText(usage, listing, undefined, settings));
|
|
439
504
|
throw new HelpShown;
|
|
440
505
|
}
|
|
506
|
+
checkChoices(parsed.values, all);
|
|
507
|
+
checkConflicts(parsed.values, all);
|
|
441
508
|
if (settings.positionals !== undefined)
|
|
442
509
|
checkPositionals(parsed.positionals, settings.positionals);
|
|
443
|
-
return { ...parsed, positionals: parsed.positionals, rest };
|
|
510
|
+
return { ...parsed, values: parsed.values, positionals: parsed.positionals, rest };
|
|
511
|
+
}
|
|
512
|
+
function splitRest(args, passes) {
|
|
513
|
+
const separator = passes ? args.indexOf("--") : -1;
|
|
514
|
+
return { own: separator === -1 ? args : args.slice(0, separator), rest: separator === -1 ? undefined : args.slice(separator + 1) };
|
|
515
|
+
}
|
|
516
|
+
function splitTrailing(args, options, count) {
|
|
517
|
+
const takesValue = (word) => !word.includes("=") && Object.entries(options).some(([name, { short, type }]) => type === "string" && (word === `--${name}` || word === `-${short}`));
|
|
518
|
+
let seen = 0;
|
|
519
|
+
if (count === 0)
|
|
520
|
+
return { own: args, rest: undefined };
|
|
521
|
+
for (let at = 0;at < args.length; at++) {
|
|
522
|
+
const word = args[at];
|
|
523
|
+
if (word === "--")
|
|
524
|
+
break;
|
|
525
|
+
if (word.startsWith("-")) {
|
|
526
|
+
if (takesValue(word))
|
|
527
|
+
at++;
|
|
528
|
+
continue;
|
|
529
|
+
}
|
|
530
|
+
seen++;
|
|
531
|
+
if (seen === count)
|
|
532
|
+
return { own: args.slice(0, at + 1), rest: args.slice(at + 1) };
|
|
533
|
+
}
|
|
534
|
+
return { own: args, rest: undefined };
|
|
535
|
+
}
|
|
536
|
+
function listed(options, allowNegative) {
|
|
537
|
+
if (!allowNegative)
|
|
538
|
+
return options;
|
|
539
|
+
return Object.fromEntries(Object.entries(options).flatMap(([name, option]) => {
|
|
540
|
+
if (option.type !== "boolean" || option.negative === undefined)
|
|
541
|
+
return [[name, option]];
|
|
542
|
+
return [...option.description === undefined ? [] : [[name, option]], [`no-${name}`, { type: "boolean", description: option.negative }]];
|
|
543
|
+
}));
|
|
544
|
+
}
|
|
545
|
+
function withBare(args, options) {
|
|
546
|
+
const bare = new Map(Object.entries(options).flatMap(([name, { short, optional }]) => optional === undefined ? [] : [[`--${name}`, optional], ...short === undefined ? [] : [[`-${short}`, optional]]]));
|
|
547
|
+
if (bare.size === 0)
|
|
548
|
+
return args;
|
|
549
|
+
const separator = args.indexOf("--");
|
|
550
|
+
const own = separator === -1 ? args : args.slice(0, separator);
|
|
551
|
+
return [...own.flatMap((arg, index) => {
|
|
552
|
+
const optional = bare.get(arg);
|
|
553
|
+
const next = own[index + 1];
|
|
554
|
+
const value = next !== undefined && (optional?.accepts === undefined ? !next.startsWith("-") : optional.accepts.test(next));
|
|
555
|
+
return optional === undefined || value ? [arg] : [arg, optional.bare];
|
|
556
|
+
}), ...separator === -1 ? [] : args.slice(separator)];
|
|
557
|
+
}
|
|
558
|
+
function checkConflicts(values, options) {
|
|
559
|
+
const given = (name) => values[name] !== undefined && values[name] !== false;
|
|
560
|
+
for (const [name, { conflicts = [] }] of Object.entries(options)) {
|
|
561
|
+
const other = given(name) ? conflicts.find(given) : undefined;
|
|
562
|
+
if (other !== undefined)
|
|
563
|
+
badUsage(`--${name} cannot be used with --${other}`);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
function checkChoices(values, options) {
|
|
567
|
+
for (const [name, option] of Object.entries(options)) {
|
|
568
|
+
if (option.choices === undefined)
|
|
569
|
+
continue;
|
|
570
|
+
const given = values[name];
|
|
571
|
+
for (const value of Array.isArray(given) ? given : [given]) {
|
|
572
|
+
if (typeof value === "string")
|
|
573
|
+
oneOf(value, name, option.choices);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
function oneOf(value, name, choices) {
|
|
578
|
+
if (!choices.includes(value))
|
|
579
|
+
badUsage(`${name.startsWith("<") ? name : `--${name}`} takes one of ${choices.join(", ")}, not ${value}`);
|
|
580
|
+
return value;
|
|
444
581
|
}
|
|
445
582
|
function checkPositionals(given, names) {
|
|
446
583
|
const variadic = names.at(-1)?.replace("]", "").endsWith("...") === true;
|
|
@@ -491,7 +628,7 @@ function nearestOption(message, options, allowNegative) {
|
|
|
491
628
|
}
|
|
492
629
|
// ../kit/src/process.ts
|
|
493
630
|
import { constants } from "os";
|
|
494
|
-
import
|
|
631
|
+
import process5 from "process";
|
|
495
632
|
var KILL_GRACE_MS = 2000;
|
|
496
633
|
function exitCodeOf(code, signal) {
|
|
497
634
|
if (code !== null)
|
|
@@ -511,7 +648,7 @@ async function runCommand(argv, { cwd, env, stdin = "ignore", timeoutMs, signal,
|
|
|
511
648
|
return;
|
|
512
649
|
try {
|
|
513
650
|
if (grouped)
|
|
514
|
-
|
|
651
|
+
process5.kill(-proc.pid, killing);
|
|
515
652
|
else
|
|
516
653
|
proc.kill(killing);
|
|
517
654
|
} catch {}
|
|
@@ -585,14 +722,103 @@ var ESCAPE_CODE = new RegExp(`${ESC}(?:\\[[\\x30-\\x3F]*[\\x20-\\x2F]*[\\x40-\\x
|
|
|
585
722
|
var RESET_STYLE = `${ESC}[0m`;
|
|
586
723
|
var CLOSE_LINK = `${ESC}]8;;${ESC}\\`;
|
|
587
724
|
// ../kit/src/cli.ts
|
|
588
|
-
import
|
|
725
|
+
import process7 from "process";
|
|
726
|
+
|
|
727
|
+
// ../kit/src/usage.ts
|
|
728
|
+
import { appendFileSync, mkdirSync } from "fs";
|
|
729
|
+
import { homedir } from "os";
|
|
730
|
+
import { dirname, isAbsolute, join } from "path";
|
|
731
|
+
import process6 from "process";
|
|
732
|
+
|
|
733
|
+
// ../kit/src/logs.ts
|
|
734
|
+
var DAY_MS = 24 * 60 * 60 * 1000;
|
|
735
|
+
var CLI_FOLDER = "kaynooo-cli";
|
|
736
|
+
|
|
737
|
+
// ../kit/src/usage.ts
|
|
738
|
+
var UNKNOWN_OPTION2 = /unknown option '(--[a-z][a-z0-9-]*)[=']/;
|
|
739
|
+
var FLAG_NAME = /^(?:--[a-z][a-z0-9-]*|-[a-zA-Z])$/;
|
|
740
|
+
var LONGEST_NAME = 40;
|
|
741
|
+
var usageError;
|
|
742
|
+
function noteUsageError(message) {
|
|
743
|
+
usageError = message;
|
|
744
|
+
}
|
|
745
|
+
function stateHome(env = process6.env) {
|
|
746
|
+
const dir = env.XDG_STATE_HOME;
|
|
747
|
+
return dir !== undefined && isAbsolute(dir) ? dir : join(homedir(), ".local", "state");
|
|
748
|
+
}
|
|
749
|
+
function usagePath(env = process6.env) {
|
|
750
|
+
return join(stateHome(env), CLI_FOLDER, "usage.jsonl");
|
|
751
|
+
}
|
|
752
|
+
function usageRecorded(env = process6.env) {
|
|
753
|
+
const off = env.KAYNOOO_CLI_NO_USAGE;
|
|
754
|
+
return env.NODE_ENV !== "test" && (off === undefined || off === "" || off === "0");
|
|
755
|
+
}
|
|
756
|
+
function flagNames(argv) {
|
|
757
|
+
const end = argv.indexOf("--");
|
|
758
|
+
const names = (end === -1 ? argv : argv.slice(0, end)).filter((word) => word.startsWith("-") && word !== "-").map((word) => word.includes("=") ? word.slice(0, word.indexOf("=")) : word);
|
|
759
|
+
return [...new Set(names)].filter((name) => name.length <= LONGEST_NAME && FLAG_NAME.test(name));
|
|
760
|
+
}
|
|
761
|
+
function usageEntry(bin, command, argv, code, now = new Date) {
|
|
762
|
+
const unknown = code === 2 && usageError !== undefined ? UNKNOWN_OPTION2.exec(usageError)?.[1] : undefined;
|
|
763
|
+
usageError = undefined;
|
|
764
|
+
return { at: now.toISOString(), bin, ...command === undefined ? {} : { command }, flags: flagNames(argv), code, ...unknown === undefined ? {} : { unknown } };
|
|
765
|
+
}
|
|
766
|
+
function recordUsage(entry, env = process6.env) {
|
|
767
|
+
if (!usageRecorded(env))
|
|
768
|
+
return;
|
|
769
|
+
try {
|
|
770
|
+
const path = usagePath(env);
|
|
771
|
+
mkdirSync(dirname(path), { recursive: true, mode: 448 });
|
|
772
|
+
appendFileSync(path, `${JSON.stringify(entry)}
|
|
773
|
+
`, { mode: 384 });
|
|
774
|
+
} catch {}
|
|
775
|
+
}
|
|
776
|
+
async function readUsage(path = usagePath(), since = 0) {
|
|
777
|
+
const file = Bun.file(path);
|
|
778
|
+
if (!await file.exists())
|
|
779
|
+
return [];
|
|
780
|
+
return (await file.text()).split(`
|
|
781
|
+
`).flatMap((line) => {
|
|
782
|
+
try {
|
|
783
|
+
const entry = JSON.parse(line);
|
|
784
|
+
return Date.parse(entry.at) >= since ? [entry] : [];
|
|
785
|
+
} catch {
|
|
786
|
+
return [];
|
|
787
|
+
}
|
|
788
|
+
});
|
|
789
|
+
}
|
|
790
|
+
function summarizeUsage(entries) {
|
|
791
|
+
const bins = new Map;
|
|
792
|
+
const errors = new Map;
|
|
793
|
+
for (const entry of entries) {
|
|
794
|
+
const bin = bins.get(entry.bin) ?? { runs: 0, failed: 0, usageErrors: 0 };
|
|
795
|
+
bin.runs++;
|
|
796
|
+
bin.failed += entry.code === 0 ? 0 : 1;
|
|
797
|
+
bin.usageErrors += entry.code === 2 ? 1 : 0;
|
|
798
|
+
bins.set(entry.bin, bin);
|
|
799
|
+
if (entry.code === 2 && entry.unknown !== undefined) {
|
|
800
|
+
const key = `${entry.bin} ${entry.command ?? ""} ${entry.unknown}`;
|
|
801
|
+
const error = errors.get(key) ?? { bin: entry.bin, ...entry.command === undefined ? {} : { command: entry.command }, unknown: entry.unknown, count: 0 };
|
|
802
|
+
error.count++;
|
|
803
|
+
errors.set(key, error);
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
return {
|
|
807
|
+
runs: entries.length,
|
|
808
|
+
bins: [...bins].map(([bin, counts]) => ({ bin, ...counts })).toSorted((a, b) => b.runs - a.runs),
|
|
809
|
+
usageErrors: [...errors.values()].toSorted((a, b) => b.count - a.count)
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
// ../kit/src/cli.ts
|
|
589
814
|
var LEVEL_OPTIONS = {
|
|
590
815
|
...OUTPUT_OPTIONS,
|
|
591
816
|
help: { type: "boolean", short: "h", description: "show this help" }
|
|
592
817
|
};
|
|
593
818
|
var TOP_OPTIONS = { ...LEVEL_OPTIONS, version: { type: "boolean", short: "v", description: "print the version" } };
|
|
594
|
-
async function commandRows(
|
|
595
|
-
const
|
|
819
|
+
async function commandRows(all, colors) {
|
|
820
|
+
const commands = shown(all);
|
|
821
|
+
const width = Math.max(0, ...Object.keys(commands).map((name) => name.length));
|
|
596
822
|
const groups = new Map([["Commands:", []]]);
|
|
597
823
|
const summaries = await Promise.all(Object.values(commands).map(async ({ summary }) => resolveSummary(summary)));
|
|
598
824
|
for (const [index, [name, { group }]] of Object.entries(commands).entries()) {
|
|
@@ -603,6 +829,9 @@ async function commandRows(commands, colors) {
|
|
|
603
829
|
}
|
|
604
830
|
return [...groups].filter(([, rows]) => rows.length > 0).flatMap(([heading, rows], index) => [...index === 0 ? [] : [""], colors.bold(heading), ...rows]);
|
|
605
831
|
}
|
|
832
|
+
function shown(commands) {
|
|
833
|
+
return Object.fromEntries(Object.entries(commands).filter(([, { hidden }]) => hidden !== true));
|
|
834
|
+
}
|
|
606
835
|
function takeOutputFlags(argv) {
|
|
607
836
|
let index = 0;
|
|
608
837
|
for (;index < argv.length; index++) {
|
|
@@ -634,7 +863,7 @@ async function expand(scope, words) {
|
|
|
634
863
|
}
|
|
635
864
|
return { ...scope, commands, lazy: [] };
|
|
636
865
|
}
|
|
637
|
-
async function
|
|
866
|
+
async function help2({ path, header, summary: lazySummary, commands, defaultCommand, examples, replaces }, stream) {
|
|
638
867
|
const colors = paint(stream);
|
|
639
868
|
const prefix = path.join(" ");
|
|
640
869
|
const summary = await resolveSummary(lazySummary);
|
|
@@ -661,8 +890,8 @@ async function help({ path, header, summary: lazySummary, commands, defaultComma
|
|
|
661
890
|
`);
|
|
662
891
|
}
|
|
663
892
|
function printError(prefix, message, hint, cause) {
|
|
664
|
-
const colors = paint(
|
|
665
|
-
const styled = takesControlCodes(
|
|
893
|
+
const colors = paint(process7.stderr);
|
|
894
|
+
const styled = takesControlCodes(process7.stderr);
|
|
666
895
|
const head = styled ? `${colors.red.bold("error")} ${colors.dim(`${prefix}:`)}` : `error: ${prefix}:`;
|
|
667
896
|
const under = (label, text, tint) => text === undefined ? "" : `
|
|
668
897
|
${styled ? tint(label) : `${label}:`} ${text}`;
|
|
@@ -681,6 +910,8 @@ function reportThrown(prefix, error, redact = (text) => text, json = false) {
|
|
|
681
910
|
const help = error instanceof UsageError ? `run ${prefix} --help for its options` : undefined;
|
|
682
911
|
const named = error instanceof Error && !(error instanceof Exit) && error.name !== "Error" ? `${error.name}: ${error.message}` : undefined;
|
|
683
912
|
const cause = error instanceof Error && error.cause instanceof Error ? redact(error.cause.message) : undefined;
|
|
913
|
+
if (error instanceof UsageError)
|
|
914
|
+
noteUsageError(error.message);
|
|
684
915
|
const report = json ? printJsonError : (message, hint, causeText) => printError(prefix, message, hint, causeText);
|
|
685
916
|
report(redact(named ?? (error instanceof Error ? error.message : String(error))), error instanceof Exit ? error.hint ?? help : undefined, cause);
|
|
686
917
|
return error instanceof Exit ? error.code : 1;
|
|
@@ -694,12 +925,20 @@ async function dispatch(level, typed, { version, redact }) {
|
|
|
694
925
|
return reportThrown(prefix, error);
|
|
695
926
|
}
|
|
696
927
|
const [name, ...args] = argv;
|
|
928
|
+
const fallback = level.defaultCommand === undefined ? undefined : level.commands[level.defaultCommand];
|
|
929
|
+
if (name === undefined && level.runBare && fallback !== undefined && "run" in fallback) {
|
|
930
|
+
try {
|
|
931
|
+
return await fallback.run([]);
|
|
932
|
+
} catch (error) {
|
|
933
|
+
return reportThrown(`${prefix} ${level.defaultCommand}`, error, redact, false);
|
|
934
|
+
}
|
|
935
|
+
}
|
|
697
936
|
if (name === undefined && level.path.length > 1) {
|
|
698
|
-
console.error(await
|
|
937
|
+
console.error(await help2(await expand(level, [...level.path.slice(1), ...argv]), process7.stderr));
|
|
699
938
|
return 2;
|
|
700
939
|
}
|
|
701
940
|
if (name === undefined || name === "help" || name === "--help" || name === "-h") {
|
|
702
|
-
console.log(await
|
|
941
|
+
console.log(await help2(await expand(level, [...level.path.slice(1), ...argv]), process7.stdout));
|
|
703
942
|
return 0;
|
|
704
943
|
}
|
|
705
944
|
if (level.path.length === 1 && (name === "--version" || name === "-v")) {
|
|
@@ -708,6 +947,7 @@ async function dispatch(level, typed, { version, redact }) {
|
|
|
708
947
|
}
|
|
709
948
|
let command = level.commands[level.aliases?.[name] ?? name];
|
|
710
949
|
let rest = args;
|
|
950
|
+
let ran = name;
|
|
711
951
|
if (command === undefined && level.lazy.length > 0) {
|
|
712
952
|
level = await expand(level, [...level.path.slice(1), ...argv]);
|
|
713
953
|
command = level.commands[level.aliases?.[name] ?? name];
|
|
@@ -715,40 +955,77 @@ async function dispatch(level, typed, { version, redact }) {
|
|
|
715
955
|
if (command === undefined && level.defaultCommand !== undefined) {
|
|
716
956
|
command = level.commands[level.defaultCommand];
|
|
717
957
|
rest = argv;
|
|
958
|
+
ran = level.defaultCommand;
|
|
718
959
|
}
|
|
719
960
|
if (command === undefined) {
|
|
720
|
-
const meant = closest(name, Object.keys(level.commands));
|
|
961
|
+
const meant = closest(name, Object.keys(shown(level.commands)));
|
|
721
962
|
printError(prefix, `no command ${name}`, meant === undefined ? `run ${prefix} --help for its commands` : `did you mean ${meant}?`);
|
|
722
963
|
return 2;
|
|
723
964
|
}
|
|
724
965
|
if ("commands" in command) {
|
|
725
966
|
const { summary } = command;
|
|
726
|
-
return dispatch({ path: [...level.path, name], header: (colors, text) => `${colors.bold(`${prefix} ${name}`)} ${text}`, summary, ...scopeOf(command.commands, command.extraCommands, command.aliases), defaultCommand: undefined, examples: command.examples, replaces: undefined }, rest, { version, redact });
|
|
967
|
+
return dispatch({ path: [...level.path, name], header: (colors, text) => `${colors.bold(`${prefix} ${name}`)} ${text}`, summary, ...scopeOf(command.commands, command.extraCommands, command.aliases), defaultCommand: undefined, runBare: false, examples: command.examples, replaces: undefined }, rest, { version, redact });
|
|
727
968
|
}
|
|
728
969
|
try {
|
|
729
970
|
return await command.run(rest);
|
|
730
971
|
} catch (error) {
|
|
731
|
-
return reportThrown(`${prefix} ${
|
|
972
|
+
return reportThrown(`${prefix} ${ran}`, error, redact, wantsJson(rest));
|
|
732
973
|
}
|
|
733
974
|
}
|
|
734
975
|
var COMPLETE = "__complete";
|
|
735
976
|
var FILES = "__files__";
|
|
736
|
-
async function
|
|
977
|
+
async function completingOf(run) {
|
|
737
978
|
completion.active = true;
|
|
738
979
|
try {
|
|
739
980
|
await run([]);
|
|
740
981
|
} catch (error) {
|
|
741
982
|
if (error instanceof Completing)
|
|
742
|
-
return error
|
|
983
|
+
return error;
|
|
743
984
|
} finally {
|
|
744
985
|
completion.active = false;
|
|
745
986
|
}
|
|
746
|
-
return {};
|
|
987
|
+
return new Completing({});
|
|
988
|
+
}
|
|
989
|
+
async function optionsOf(run) {
|
|
990
|
+
return (await completingOf(run)).options;
|
|
991
|
+
}
|
|
992
|
+
function candidateLine(candidate) {
|
|
993
|
+
return typeof candidate === "string" ? candidate : candidate.join("\t");
|
|
994
|
+
}
|
|
995
|
+
function positionalBeingTyped(before, { options, positionals }) {
|
|
996
|
+
const takesValue = (word) => !word.includes("=") && Object.entries(options).some(([name, { short, type }]) => type === "string" && (word === `--${name}` || word === `-${short}`));
|
|
997
|
+
let index = 0;
|
|
998
|
+
let at = 0;
|
|
999
|
+
while (at < before.length) {
|
|
1000
|
+
const word = before[at];
|
|
1001
|
+
if (word === "--") {
|
|
1002
|
+
index += before.length - at - 1;
|
|
1003
|
+
break;
|
|
1004
|
+
}
|
|
1005
|
+
if (!word.startsWith("-"))
|
|
1006
|
+
index++;
|
|
1007
|
+
else if (takesValue(word))
|
|
1008
|
+
at++;
|
|
1009
|
+
at++;
|
|
1010
|
+
}
|
|
1011
|
+
const last = positionals.at(-1);
|
|
1012
|
+
if (index < positionals.length)
|
|
1013
|
+
return positionals[index];
|
|
1014
|
+
return last?.replace("]", "").endsWith("...") === true ? last : undefined;
|
|
747
1015
|
}
|
|
748
1016
|
async function leafCandidates(run, words) {
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
1017
|
+
const completing = await completingOf(run);
|
|
1018
|
+
if ((words.at(-1) ?? "").startsWith("-"))
|
|
1019
|
+
return Object.entries(completing.options).map(([name, option]) => option.description === undefined ? `--${name}` : `--${name} ${option.description}`);
|
|
1020
|
+
const flag = words.at(-2);
|
|
1021
|
+
const option = flag?.startsWith("-") === true ? Object.entries(completing.options).find(([name, { short }]) => flag === `--${name}` || flag === `-${short}`)?.[1] : undefined;
|
|
1022
|
+
if (option?.type === "string") {
|
|
1023
|
+
const values = option.choices === undefined ? await option.candidates?.() : option.choices;
|
|
1024
|
+
return values === undefined ? [FILES] : values.map(candidateLine);
|
|
1025
|
+
}
|
|
1026
|
+
const name = positionalBeingTyped(words.slice(0, -1), completing);
|
|
1027
|
+
const values = name === undefined ? undefined : await completing.candidates[name]?.();
|
|
1028
|
+
return values === undefined ? [FILES] : values.map(candidateLine);
|
|
752
1029
|
}
|
|
753
1030
|
async function candidates(scope, defaultCommand, words, before = []) {
|
|
754
1031
|
const [word = "", ...rest] = words;
|
|
@@ -759,8 +1036,13 @@ async function candidates(scope, defaultCommand, words, before = []) {
|
|
|
759
1036
|
({ commands, aliases } = await expand(scope, [...before, ...words]));
|
|
760
1037
|
command = commands[aliases?.[word] ?? word];
|
|
761
1038
|
}
|
|
762
|
-
if (listing)
|
|
763
|
-
|
|
1039
|
+
if (listing) {
|
|
1040
|
+
const own = await Promise.all(Object.entries(shown(commands)).map(async ([name, { summary }]) => `${name} ${await resolveSummary(summary)}`));
|
|
1041
|
+
const fallback = defaultCommand === undefined ? undefined : commands[defaultCommand];
|
|
1042
|
+
const values = fallback !== undefined && "run" in fallback ? await leafCandidates(fallback.run, [word]) : [];
|
|
1043
|
+
const valueOf = (line) => line.slice(0, line.includes("\t") ? line.indexOf("\t") : undefined);
|
|
1044
|
+
return [...own, ...values.filter((line) => line !== FILES && !(valueOf(line) in commands))];
|
|
1045
|
+
}
|
|
764
1046
|
if (command === undefined) {
|
|
765
1047
|
const fallback = defaultCommand === undefined ? undefined : commands[defaultCommand];
|
|
766
1048
|
return fallback !== undefined && "run" in fallback ? leafCandidates(fallback.run, words) : [];
|
|
@@ -772,26 +1054,49 @@ async function printCandidates(found) {
|
|
|
772
1054
|
console.log(line);
|
|
773
1055
|
return 0;
|
|
774
1056
|
}
|
|
775
|
-
async function runCli(cli, argv =
|
|
1057
|
+
async function runCli(cli, argv = process7.argv.slice(2)) {
|
|
776
1058
|
if (argv[0] === COMPLETE)
|
|
777
1059
|
return printCandidates(candidates(scopeOf(cli.commands, cli.extraCommands, cli.aliases), cli.defaultCommand, argv.slice(1)));
|
|
778
|
-
|
|
1060
|
+
const code = await dispatch({ path: [cli.name], header: (colors, about) => `${colors.bold(cli.name)} ${colors.dim(cli.version)} ${about}`, summary: cli.about, ...scopeOf(cli.commands, cli.extraCommands, cli.aliases), defaultCommand: cli.defaultCommand, runBare: cli.runBare === true, examples: cli.examples, replaces: cli.replaces }, argv, { version: cli.version, redact: cli.redact });
|
|
1061
|
+
if (cli.usage !== false) {
|
|
1062
|
+
try {
|
|
1063
|
+
recordUsage(usageEntry(cli.name, commandPath(scopeOf(cli.commands, cli.extraCommands, cli.aliases), argv), argv, code));
|
|
1064
|
+
} catch {}
|
|
1065
|
+
}
|
|
1066
|
+
return code;
|
|
1067
|
+
}
|
|
1068
|
+
function commandPath(scope, argv) {
|
|
1069
|
+
const path = [];
|
|
1070
|
+
let level = scope;
|
|
1071
|
+
for (const word of argv.filter((each) => !each.startsWith("-"))) {
|
|
1072
|
+
const name = level.aliases !== undefined && Object.hasOwn(level.aliases, word) ? level.aliases[word] ?? word : word;
|
|
1073
|
+
const command = Object.hasOwn(level.commands, name) ? level.commands[name] : undefined;
|
|
1074
|
+
if (command === undefined)
|
|
1075
|
+
break;
|
|
1076
|
+
path.push(word);
|
|
1077
|
+
if (!("commands" in command))
|
|
1078
|
+
break;
|
|
1079
|
+
level = scopeOf(command.commands, command.extraCommands, command.aliases);
|
|
1080
|
+
}
|
|
1081
|
+
return path.length === 0 ? undefined : path.join(" ");
|
|
779
1082
|
}
|
|
780
|
-
async function runMain({ name, version, run, redact }, argv =
|
|
1083
|
+
async function runMain({ name, version, run, redact, usage }, argv = process7.argv.slice(2)) {
|
|
781
1084
|
if (argv[0] === COMPLETE)
|
|
782
1085
|
return printCandidates(leafCandidates(run, argv.slice(1)));
|
|
783
1086
|
if (argv.length === 1 && (argv[0] === "--version" || argv[0] === "-v")) {
|
|
784
1087
|
console.log(version);
|
|
785
1088
|
return 0;
|
|
786
1089
|
}
|
|
1090
|
+
let code;
|
|
787
1091
|
try {
|
|
788
|
-
|
|
1092
|
+
code = await run(argv);
|
|
789
1093
|
} catch (error) {
|
|
790
|
-
|
|
1094
|
+
code = reportThrown(name, error, redact, wantsJson(argv));
|
|
791
1095
|
}
|
|
1096
|
+
if (usage !== false)
|
|
1097
|
+
recordUsage(usageEntry(name, undefined, argv, code));
|
|
1098
|
+
return code;
|
|
792
1099
|
}
|
|
793
|
-
// ../kit/src/logs.ts
|
|
794
|
-
var DAY_MS = 24 * 60 * 60 * 1000;
|
|
795
1100
|
// ../kit/src/secrets.ts
|
|
796
1101
|
var SECRET_WORDS = new Set(["key", "pass", "pwd", "auth", "salt", "cookie", "session", "jwt", "ssn", "iban", "card", "cvv", "otp"]);
|
|
797
1102
|
// ../kit/src/sessions.ts
|
|
@@ -799,11 +1104,55 @@ var {Glob } = globalThis.Bun;
|
|
|
799
1104
|
var SESSION_FILES = new Glob("*.json");
|
|
800
1105
|
var ACCOUNT_DIRS = new Glob("*/sessions");
|
|
801
1106
|
// package.json
|
|
802
|
-
var version = "0.
|
|
1107
|
+
var version = "0.4.0";
|
|
1108
|
+
|
|
1109
|
+
// src/checks.ts
|
|
1110
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
1111
|
+
import { basename, join as join3 } from "path";
|
|
1112
|
+
|
|
1113
|
+
// src/completions.ts
|
|
1114
|
+
import { existsSync, mkdirSync as mkdirSync2, readdirSync, readFileSync, rmSync } from "fs";
|
|
1115
|
+
import { join as join2 } from "path";
|
|
1116
|
+
import process9 from "process";
|
|
1117
|
+
var MARKER = "# Generated by `cli completions fish`";
|
|
1118
|
+
var FISH_FILE = /\.fish$/;
|
|
1119
|
+
var FUNCTION = `functions -q __kaynooo_cli_complete; or function __kaynooo_cli_complete --argument-names bin --description 'what a @kaynooo/cli-* bin offers for the word being typed'
|
|
1120
|
+
set -l tokens (commandline -xpc 2>/dev/null; or commandline -opc)
|
|
1121
|
+
set -l current (commandline -ct)
|
|
1122
|
+
for line in ($bin __complete $tokens[2..-1] $current 2>/dev/null)
|
|
1123
|
+
if test "$line" = __files__
|
|
1124
|
+
__fish_complete_path $current
|
|
1125
|
+
else
|
|
1126
|
+
echo $line
|
|
1127
|
+
end
|
|
1128
|
+
end
|
|
1129
|
+
end`;
|
|
1130
|
+
function completionDir(home, env = process9.env) {
|
|
1131
|
+
return join2(env.XDG_CACHE_HOME ?? join2(home, ".cache"), "fish", "kaynooo-cli");
|
|
1132
|
+
}
|
|
1133
|
+
function installedBins(host) {
|
|
1134
|
+
return Object.values(TOOLS).flat().filter((bin) => host.which(bin) !== null);
|
|
1135
|
+
}
|
|
1136
|
+
function completionFile(bin) {
|
|
1137
|
+
return `${MARKER}
|
|
1138
|
+
${FUNCTION}
|
|
1139
|
+
complete -c ${bin} -f -a '(__kaynooo_cli_complete ${bin})'
|
|
1140
|
+
`;
|
|
1141
|
+
}
|
|
1142
|
+
async function installCompletions(host, dir) {
|
|
1143
|
+
const bins = installedBins(host);
|
|
1144
|
+
mkdirSync2(dir, { recursive: true });
|
|
1145
|
+
const removed = readdirSync(dir).filter((name) => FISH_FILE.test(name) && !bins.includes(name.replace(FISH_FILE, "")) && readFileSync(join2(dir, name), "utf8").startsWith(MARKER));
|
|
1146
|
+
for (const name of removed)
|
|
1147
|
+
rmSync(join2(dir, name));
|
|
1148
|
+
await Promise.all(bins.map(async (bin) => Bun.write(join2(dir, `${bin}.fish`), completionFile(bin))));
|
|
1149
|
+
return { written: bins, removed: removed.map((name) => name.replace(FISH_FILE, "")) };
|
|
1150
|
+
}
|
|
1151
|
+
function missingCompletions(host, dir) {
|
|
1152
|
+
return installedBins(host).filter((bin) => !existsSync(join2(dir, `${bin}.fish`)));
|
|
1153
|
+
}
|
|
803
1154
|
|
|
804
1155
|
// src/checks.ts
|
|
805
|
-
import { existsSync, readFileSync } from "fs";
|
|
806
|
-
import { basename, join } from "path";
|
|
807
1156
|
var TOOLS = {
|
|
808
1157
|
"@kaynooo/cli-base": ["base", "proof"],
|
|
809
1158
|
"@kaynooo/cli-changelog": ["changelog"],
|
|
@@ -818,8 +1167,14 @@ var TOOLS = {
|
|
|
818
1167
|
"@kaynooo/cli-gitlab": ["gl"],
|
|
819
1168
|
"@kaynooo/cli-guard": ["guard"],
|
|
820
1169
|
"@kaynooo/cli-out": ["out"],
|
|
1170
|
+
"@kaynooo/cli-dev": ["dev"],
|
|
1171
|
+
"@kaynooo/cli-pick": ["pick"],
|
|
821
1172
|
"@kaynooo/cli-pkg": ["pkg"],
|
|
1173
|
+
"@kaynooo/cli-probe": ["probe"],
|
|
1174
|
+
"@kaynooo/cli-repos": ["repos"],
|
|
822
1175
|
"@kaynooo/cli-stack": ["stack"],
|
|
1176
|
+
"@kaynooo/cli-sweep": ["sweep"],
|
|
1177
|
+
"@kaynooo/cli-view": ["view"],
|
|
823
1178
|
"@kaynooo/cli-wait": ["waitfor", "slot"]
|
|
824
1179
|
};
|
|
825
1180
|
var FORGE = "git.kaynooo.fr";
|
|
@@ -837,7 +1192,7 @@ function readJsonText(text) {
|
|
|
837
1192
|
}
|
|
838
1193
|
}
|
|
839
1194
|
function readJson(path) {
|
|
840
|
-
return
|
|
1195
|
+
return existsSync2(path) ? readJsonText(readFileSync2(path, "utf8")) : undefined;
|
|
841
1196
|
}
|
|
842
1197
|
function firstLine(text) {
|
|
843
1198
|
const trimmed = text.trim();
|
|
@@ -846,12 +1201,12 @@ function firstLine(text) {
|
|
|
846
1201
|
return end === -1 ? trimmed : trimmed.slice(0, end);
|
|
847
1202
|
}
|
|
848
1203
|
function installed(host, name) {
|
|
849
|
-
return readJson(
|
|
1204
|
+
return readJson(join3(host.globalModules, name, "package.json"));
|
|
850
1205
|
}
|
|
851
1206
|
function ownBin(host, name, bin) {
|
|
852
1207
|
const declared = installed(host, name)?.bin;
|
|
853
1208
|
const file = typeof declared === "string" ? declared : declared?.[bin];
|
|
854
|
-
return file === undefined ? undefined :
|
|
1209
|
+
return file === undefined ? undefined : join3(host.globalModules, name, file);
|
|
855
1210
|
}
|
|
856
1211
|
async function checkTool(host, name, bins) {
|
|
857
1212
|
const check = name.slice(name.indexOf("/") + 1);
|
|
@@ -860,7 +1215,7 @@ async function checkTool(host, name, bins) {
|
|
|
860
1215
|
return { check, ok: false, detail: "not installed", fix: `bun install -g ${name}` };
|
|
861
1216
|
const off = bins.filter((bin) => host.which(bin) === null);
|
|
862
1217
|
if (off.length > 0)
|
|
863
|
-
return { check, ok: false, detail: `${version}, ${off.join(", ")} not on the PATH`, fix: `add ${
|
|
1218
|
+
return { check, ok: false, detail: `${version}, ${off.join(", ")} not on the PATH`, fix: `add ${join3(host.globalModules, "..", "..", "..", "bin")} to the PATH` };
|
|
864
1219
|
const info = await host.run(["bun", "info", name, "version", "--no-cache"]);
|
|
865
1220
|
const answered = info.code === 0 ? firstLine(info.stdout.trim().split(`
|
|
866
1221
|
`).at(-1) ?? "") : "";
|
|
@@ -887,9 +1242,9 @@ async function checkTea(host) {
|
|
|
887
1242
|
return { check: "tea", ok: true, detail: `logged in to ${forge} as ${login.name}, Forgejo ${served}` };
|
|
888
1243
|
}
|
|
889
1244
|
function checkHooks(host) {
|
|
890
|
-
const settings = readJson(
|
|
1245
|
+
const settings = readJson(join3(host.home, ".claude", "settings.json")) ?? {};
|
|
891
1246
|
const plugin = settings.enabledPlugins?.[PLUGIN] === true;
|
|
892
|
-
const mod = (settings.env?.CLAUDE_CODE_PLUGIN_DIRS ?? "").split(":").map((dir) => dir.replace(LEADING_TILDE, host.home)).find((dir) => basename(dir) === "cli" &&
|
|
1247
|
+
const mod = (settings.env?.CLAUDE_CODE_PLUGIN_DIRS ?? "").split(":").map((dir) => dir.replace(LEADING_TILDE, host.home)).find((dir) => basename(dir) === "cli" && existsSync2(join3(dir, "hooks", "hooks.json")));
|
|
893
1248
|
if (plugin && mod !== undefined)
|
|
894
1249
|
return { check: "hooks", ok: false, detail: `both the ${PLUGIN} plugin and the mod in ${mod} run guard, so every call is checked twice`, fix: `claude plugin disable ${PLUGIN}` };
|
|
895
1250
|
if (plugin)
|
|
@@ -905,28 +1260,37 @@ function checkBun(host) {
|
|
|
905
1260
|
return { check: "bun", ok: false, detail: `${host.bunVersion}, the tools ask for ${[...new Set(unmet)].join(" and ")}`, fix: "bun upgrade" };
|
|
906
1261
|
return { check: "bun", ok: true, detail: floors.length === 0 ? host.bunVersion : `${host.bunVersion}, the tools ask for ${[...new Set(floors)].join(" and ")}` };
|
|
907
1262
|
}
|
|
1263
|
+
function checkCompletions(host) {
|
|
1264
|
+
if (host.which("fish") === null)
|
|
1265
|
+
return { check: "completions", ok: true, detail: "fish is not installed" };
|
|
1266
|
+
const dir = completionDir(host.home);
|
|
1267
|
+
const missing = missingCompletions(host, dir);
|
|
1268
|
+
if (missing.length > 0)
|
|
1269
|
+
return { check: "completions", ok: false, detail: `no fish completion in ${dir} for ${missing.join(", ")}`, fix: "cli completions fish --install" };
|
|
1270
|
+
return { check: "completions", ok: true, detail: dir };
|
|
1271
|
+
}
|
|
908
1272
|
async function runChecks(host) {
|
|
909
1273
|
const entries = Object.entries(TOOLS);
|
|
910
1274
|
const tools = [];
|
|
911
1275
|
for (let at = 0;at < entries.length; at += INFO_AT_ONCE)
|
|
912
1276
|
tools.push(...await Promise.all(entries.slice(at, at + INFO_AT_ONCE).map(async ([name, bins]) => checkTool(host, name, bins))));
|
|
913
|
-
return [...tools, await checkTea(host), checkHooks(host), checkBun(host)];
|
|
1277
|
+
return [...tools, await checkTea(host), checkHooks(host), checkBun(host), checkCompletions(host)];
|
|
914
1278
|
}
|
|
915
1279
|
|
|
916
1280
|
// src/host.ts
|
|
917
|
-
import { homedir } from "os";
|
|
918
|
-
import { join as
|
|
919
|
-
import
|
|
1281
|
+
import { homedir as homedir2 } from "os";
|
|
1282
|
+
import { join as join4 } from "path";
|
|
1283
|
+
import process10 from "process";
|
|
920
1284
|
var RUN_TIMEOUT_MS = 20000;
|
|
921
1285
|
function hostHere() {
|
|
922
|
-
const home =
|
|
1286
|
+
const home = homedir2();
|
|
923
1287
|
return {
|
|
924
1288
|
home,
|
|
925
|
-
globalModules:
|
|
1289
|
+
globalModules: join4(process10.env.BUN_INSTALL ?? join4(home, ".bun"), "install", "global", "node_modules"),
|
|
926
1290
|
bunVersion: Bun.version,
|
|
927
1291
|
which: (bin) => Bun.which(bin),
|
|
928
1292
|
run: async (argv, env) => {
|
|
929
|
-
const ran = await runCommand(argv, { timeoutMs: RUN_TIMEOUT_MS, ...env === undefined ? {} : { env: { ...
|
|
1293
|
+
const ran = await runCommand(argv, { timeoutMs: RUN_TIMEOUT_MS, ...env === undefined ? {} : { env: { ...process10.env, ...env } } });
|
|
930
1294
|
if (ran.timedOut)
|
|
931
1295
|
return { code: 1, stdout: "", stderr: `timed out after ${RUN_TIMEOUT_MS / 1000}s` };
|
|
932
1296
|
const of = (stream) => Buffer.concat(ran.chunks.filter((chunk) => chunk.stream === stream).map((chunk) => chunk.data)).toString();
|
|
@@ -945,7 +1309,7 @@ var OPTIONS = {
|
|
|
945
1309
|
json: jsonOption("[{check, ok, detail, fix?}]")
|
|
946
1310
|
};
|
|
947
1311
|
function print(results) {
|
|
948
|
-
const { green, red, dim } = paint(
|
|
1312
|
+
const { green, red, dim } = paint(process11.stdout);
|
|
949
1313
|
const width = Math.max(...results.map((result) => result.check.length));
|
|
950
1314
|
for (const { check, ok, detail, fix } of results)
|
|
951
1315
|
console.log(`${ok ? green("ok ") : red("FAIL")} ${check.padEnd(width)} ${detail}${fix === undefined ? "" : ` ${dim("fix:")} ${fix}`}`);
|
|
@@ -964,7 +1328,7 @@ async function main(args, host = hostHere()) {
|
|
|
964
1328
|
return results.every((result) => result.ok) ? 0 : 1;
|
|
965
1329
|
}
|
|
966
1330
|
if (import.meta.main)
|
|
967
|
-
|
|
1331
|
+
process11.exitCode = await runMain({ name: "doctor", version, run: async (args) => main(args) });
|
|
968
1332
|
export {
|
|
969
1333
|
main
|
|
970
1334
|
};
|