@kaynooo/cli-doctor 0.2.0 → 0.3.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/dist/cli-bin.js +268 -58
- package/dist/cli.js +229 -43
- package/package.json +2 -2
package/dist/cli-bin.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// @bun
|
|
3
3
|
|
|
4
4
|
// src/cli-bin.ts
|
|
5
|
-
import
|
|
5
|
+
import process8 from "process";
|
|
6
6
|
|
|
7
7
|
// ../kit/src/args.ts
|
|
8
8
|
import process2 from "process";
|
|
@@ -11,7 +11,7 @@ import { parseArgs } from "util";
|
|
|
11
11
|
// ../kit/src/style.ts
|
|
12
12
|
import process from "process";
|
|
13
13
|
|
|
14
|
-
// ../../node_modules/.bun/@kaynooo+utils@2.31.
|
|
14
|
+
// ../../node_modules/.bun/@kaynooo+utils@2.31.2+d0769e28dc4dad16/node_modules/@kaynooo/utils/dist/chunk-mtetydvc.js
|
|
15
15
|
function x(s) {
|
|
16
16
|
let r = s.startsWith("#") ? s.slice(1).toLowerCase() : s.toLowerCase();
|
|
17
17
|
if (!/^[a-f0-9]+$/.test(r))
|
|
@@ -231,7 +231,7 @@ class e {
|
|
|
231
231
|
}
|
|
232
232
|
}
|
|
233
233
|
|
|
234
|
-
// ../../node_modules/.bun/@kaynooo+utils@2.31.
|
|
234
|
+
// ../../node_modules/.bun/@kaynooo+utils@2.31.2+d0769e28dc4dad16/node_modules/@kaynooo/utils/dist/chunk-y1msbssb.js
|
|
235
235
|
var r = 39;
|
|
236
236
|
var o = 49;
|
|
237
237
|
var f2 = "\x1B[";
|
|
@@ -349,8 +349,12 @@ class HelpShown extends Exit {
|
|
|
349
349
|
|
|
350
350
|
class Completing {
|
|
351
351
|
options;
|
|
352
|
-
|
|
352
|
+
positionals;
|
|
353
|
+
candidates;
|
|
354
|
+
constructor(options, positionals = [], candidates = {}) {
|
|
353
355
|
this.options = options;
|
|
356
|
+
this.positionals = positionals;
|
|
357
|
+
this.candidates = candidates;
|
|
354
358
|
}
|
|
355
359
|
}
|
|
356
360
|
var completion = { active: false };
|
|
@@ -393,8 +397,10 @@ function exampleRows(examples, colors = paint(process2.stdout)) {
|
|
|
393
397
|
function optionRows(options, colors = paint(process2.stdout)) {
|
|
394
398
|
const rows = Object.entries(options).map(([name, option]) => {
|
|
395
399
|
const short = option.short === undefined ? " " : `${colors.cyan(`-${option.short}`)}, `;
|
|
396
|
-
const
|
|
397
|
-
|
|
400
|
+
const named = option.choices === undefined ? "<value>" : `<${option.choices.join("|")}>`;
|
|
401
|
+
const placeholder = option.optional === undefined ? named : `[${named}]`;
|
|
402
|
+
const value = option.type === "string" ? ` ${colors.dim(placeholder)}` : "";
|
|
403
|
+
return { flag: `${short}${colors.cyan(`--${name}`)}${value}`, width: `-x, --${name}`.length + (option.type === "string" ? placeholder.length + 1 : 0), description: option.description ?? "" };
|
|
398
404
|
});
|
|
399
405
|
const width = Math.max(0, ...rows.map((row) => row.width));
|
|
400
406
|
return rows.map((row) => row.description === "" ? ` ${row.flag}` : ` ${row.flag}${" ".repeat(width - row.width)} ${colors.dim(row.description)}`);
|
|
@@ -420,14 +426,13 @@ function applyOutput(color, plain, env = process2.env) {
|
|
|
420
426
|
function parse(args, usage, options, settings = {}) {
|
|
421
427
|
const output = Object.fromEntries(Object.entries(OUTPUT_OPTIONS).filter(([name]) => !(name in options)));
|
|
422
428
|
const all = { ...options, ...output, help: { type: "boolean", short: "h", description: "show this help" } };
|
|
429
|
+
const listing = listed(all, settings.allowNegative === true);
|
|
423
430
|
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);
|
|
431
|
+
throw new Completing(listing, settings.positionals, settings.candidates);
|
|
432
|
+
const { own, rest } = settings.trailing === true ? splitTrailing(args, all, settings.positionals?.length ?? 0) : splitRest(args, settings.rest === true);
|
|
428
433
|
let parsed;
|
|
429
434
|
try {
|
|
430
|
-
parsed = parseArgs({ args: joinDashValues(own, all), options: all, allowPositionals: true, allowNegative: settings.allowNegative === true, strict: true });
|
|
435
|
+
parsed = parseArgs({ args: joinDashValues(withBare(own, all), all), options: all, allowPositionals: true, allowNegative: settings.allowNegative === true, strict: true });
|
|
431
436
|
} catch (error) {
|
|
432
437
|
const message = parseError(error);
|
|
433
438
|
throw new UsageError(message, nearestOption(message, all, settings.allowNegative === true));
|
|
@@ -435,12 +440,84 @@ function parse(args, usage, options, settings = {}) {
|
|
|
435
440
|
const values = parsed.values;
|
|
436
441
|
applyOutput("color" in output ? values.color : undefined, "plain" in output ? values.plain : undefined);
|
|
437
442
|
if (values.help === true) {
|
|
438
|
-
console.log(wantsHelpJson() ? JSON.stringify(description(usage, settings)) : helpText(usage,
|
|
443
|
+
console.log(wantsHelpJson() ? JSON.stringify(description(usage, settings)) : helpText(usage, listing, undefined, settings));
|
|
439
444
|
throw new HelpShown;
|
|
440
445
|
}
|
|
446
|
+
checkChoices(parsed.values, all);
|
|
447
|
+
checkConflicts(parsed.values, all);
|
|
441
448
|
if (settings.positionals !== undefined)
|
|
442
449
|
checkPositionals(parsed.positionals, settings.positionals);
|
|
443
|
-
return { ...parsed, positionals: parsed.positionals, rest };
|
|
450
|
+
return { ...parsed, values: parsed.values, positionals: parsed.positionals, rest };
|
|
451
|
+
}
|
|
452
|
+
function splitRest(args, passes) {
|
|
453
|
+
const separator = passes ? args.indexOf("--") : -1;
|
|
454
|
+
return { own: separator === -1 ? args : args.slice(0, separator), rest: separator === -1 ? undefined : args.slice(separator + 1) };
|
|
455
|
+
}
|
|
456
|
+
function splitTrailing(args, options, count) {
|
|
457
|
+
const takesValue = (word) => !word.includes("=") && Object.entries(options).some(([name, { short, type }]) => type === "string" && (word === `--${name}` || word === `-${short}`));
|
|
458
|
+
let seen = 0;
|
|
459
|
+
if (count === 0)
|
|
460
|
+
return { own: args, rest: undefined };
|
|
461
|
+
for (let at = 0;at < args.length; at++) {
|
|
462
|
+
const word = args[at];
|
|
463
|
+
if (word === "--")
|
|
464
|
+
break;
|
|
465
|
+
if (word.startsWith("-")) {
|
|
466
|
+
if (takesValue(word))
|
|
467
|
+
at++;
|
|
468
|
+
continue;
|
|
469
|
+
}
|
|
470
|
+
seen++;
|
|
471
|
+
if (seen === count)
|
|
472
|
+
return { own: args.slice(0, at + 1), rest: args.slice(at + 1) };
|
|
473
|
+
}
|
|
474
|
+
return { own: args, rest: undefined };
|
|
475
|
+
}
|
|
476
|
+
function listed(options, allowNegative) {
|
|
477
|
+
if (!allowNegative)
|
|
478
|
+
return options;
|
|
479
|
+
return Object.fromEntries(Object.entries(options).flatMap(([name, option]) => {
|
|
480
|
+
if (option.type !== "boolean" || option.negative === undefined)
|
|
481
|
+
return [[name, option]];
|
|
482
|
+
return [...option.description === undefined ? [] : [[name, option]], [`no-${name}`, { type: "boolean", description: option.negative }]];
|
|
483
|
+
}));
|
|
484
|
+
}
|
|
485
|
+
function withBare(args, options) {
|
|
486
|
+
const bare = new Map(Object.entries(options).flatMap(([name, { short, optional }]) => optional === undefined ? [] : [[`--${name}`, optional], ...short === undefined ? [] : [[`-${short}`, optional]]]));
|
|
487
|
+
if (bare.size === 0)
|
|
488
|
+
return args;
|
|
489
|
+
const separator = args.indexOf("--");
|
|
490
|
+
const own = separator === -1 ? args : args.slice(0, separator);
|
|
491
|
+
return [...own.flatMap((arg, index) => {
|
|
492
|
+
const optional = bare.get(arg);
|
|
493
|
+
const next = own[index + 1];
|
|
494
|
+
const value = next !== undefined && (optional?.accepts === undefined ? !next.startsWith("-") : optional.accepts.test(next));
|
|
495
|
+
return optional === undefined || value ? [arg] : [arg, optional.bare];
|
|
496
|
+
}), ...separator === -1 ? [] : args.slice(separator)];
|
|
497
|
+
}
|
|
498
|
+
function checkConflicts(values, options) {
|
|
499
|
+
const given = (name) => values[name] !== undefined && values[name] !== false;
|
|
500
|
+
for (const [name, { conflicts = [] }] of Object.entries(options)) {
|
|
501
|
+
const other = given(name) ? conflicts.find(given) : undefined;
|
|
502
|
+
if (other !== undefined)
|
|
503
|
+
badUsage(`--${name} cannot be used with --${other}`);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
function checkChoices(values, options) {
|
|
507
|
+
for (const [name, option] of Object.entries(options)) {
|
|
508
|
+
if (option.choices === undefined)
|
|
509
|
+
continue;
|
|
510
|
+
const given = values[name];
|
|
511
|
+
for (const value of Array.isArray(given) ? given : [given]) {
|
|
512
|
+
if (typeof value === "string")
|
|
513
|
+
oneOf(value, name, option.choices);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
function oneOf(value, name, choices) {
|
|
518
|
+
if (!choices.includes(value))
|
|
519
|
+
badUsage(`${name.startsWith("<") ? name : `--${name}`} takes one of ${choices.join(", ")}, not ${value}`);
|
|
520
|
+
return value;
|
|
444
521
|
}
|
|
445
522
|
function checkPositionals(given, names) {
|
|
446
523
|
const variadic = names.at(-1)?.replace("]", "").endsWith("...") === true;
|
|
@@ -591,8 +668,9 @@ var LEVEL_OPTIONS = {
|
|
|
591
668
|
help: { type: "boolean", short: "h", description: "show this help" }
|
|
592
669
|
};
|
|
593
670
|
var TOP_OPTIONS = { ...LEVEL_OPTIONS, version: { type: "boolean", short: "v", description: "print the version" } };
|
|
594
|
-
async function commandRows(
|
|
595
|
-
const
|
|
671
|
+
async function commandRows(all, colors) {
|
|
672
|
+
const commands = shown(all);
|
|
673
|
+
const width = Math.max(0, ...Object.keys(commands).map((name) => name.length));
|
|
596
674
|
const groups = new Map([["Commands:", []]]);
|
|
597
675
|
const summaries = await Promise.all(Object.values(commands).map(async ({ summary }) => resolveSummary(summary)));
|
|
598
676
|
for (const [index, [name, { group }]] of Object.entries(commands).entries()) {
|
|
@@ -603,6 +681,9 @@ async function commandRows(commands, colors) {
|
|
|
603
681
|
}
|
|
604
682
|
return [...groups].filter(([, rows]) => rows.length > 0).flatMap(([heading, rows], index) => [...index === 0 ? [] : [""], colors.bold(heading), ...rows]);
|
|
605
683
|
}
|
|
684
|
+
function shown(commands) {
|
|
685
|
+
return Object.fromEntries(Object.entries(commands).filter(([, { hidden }]) => hidden !== true));
|
|
686
|
+
}
|
|
606
687
|
function takeOutputFlags(argv) {
|
|
607
688
|
let index = 0;
|
|
608
689
|
for (;index < argv.length; index++) {
|
|
@@ -694,6 +775,14 @@ async function dispatch(level, typed, { version, redact }) {
|
|
|
694
775
|
return reportThrown(prefix, error);
|
|
695
776
|
}
|
|
696
777
|
const [name, ...args] = argv;
|
|
778
|
+
const fallback = level.defaultCommand === undefined ? undefined : level.commands[level.defaultCommand];
|
|
779
|
+
if (name === undefined && level.runBare && fallback !== undefined && "run" in fallback) {
|
|
780
|
+
try {
|
|
781
|
+
return await fallback.run([]);
|
|
782
|
+
} catch (error) {
|
|
783
|
+
return reportThrown(`${prefix} ${level.defaultCommand}`, error, redact, false);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
697
786
|
if (name === undefined && level.path.length > 1) {
|
|
698
787
|
console.error(await help(await expand(level, [...level.path.slice(1), ...argv]), process4.stderr));
|
|
699
788
|
return 2;
|
|
@@ -708,6 +797,7 @@ async function dispatch(level, typed, { version, redact }) {
|
|
|
708
797
|
}
|
|
709
798
|
let command = level.commands[level.aliases?.[name] ?? name];
|
|
710
799
|
let rest = args;
|
|
800
|
+
let ran = name;
|
|
711
801
|
if (command === undefined && level.lazy.length > 0) {
|
|
712
802
|
level = await expand(level, [...level.path.slice(1), ...argv]);
|
|
713
803
|
command = level.commands[level.aliases?.[name] ?? name];
|
|
@@ -715,40 +805,77 @@ async function dispatch(level, typed, { version, redact }) {
|
|
|
715
805
|
if (command === undefined && level.defaultCommand !== undefined) {
|
|
716
806
|
command = level.commands[level.defaultCommand];
|
|
717
807
|
rest = argv;
|
|
808
|
+
ran = level.defaultCommand;
|
|
718
809
|
}
|
|
719
810
|
if (command === undefined) {
|
|
720
|
-
const meant = closest(name, Object.keys(level.commands));
|
|
811
|
+
const meant = closest(name, Object.keys(shown(level.commands)));
|
|
721
812
|
printError(prefix, `no command ${name}`, meant === undefined ? `run ${prefix} --help for its commands` : `did you mean ${meant}?`);
|
|
722
813
|
return 2;
|
|
723
814
|
}
|
|
724
815
|
if ("commands" in command) {
|
|
725
816
|
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 });
|
|
817
|
+
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
818
|
}
|
|
728
819
|
try {
|
|
729
820
|
return await command.run(rest);
|
|
730
821
|
} catch (error) {
|
|
731
|
-
return reportThrown(`${prefix} ${
|
|
822
|
+
return reportThrown(`${prefix} ${ran}`, error, redact, wantsJson(rest));
|
|
732
823
|
}
|
|
733
824
|
}
|
|
734
825
|
var COMPLETE = "__complete";
|
|
735
826
|
var FILES = "__files__";
|
|
736
|
-
async function
|
|
827
|
+
async function completingOf(run) {
|
|
737
828
|
completion.active = true;
|
|
738
829
|
try {
|
|
739
830
|
await run([]);
|
|
740
831
|
} catch (error) {
|
|
741
832
|
if (error instanceof Completing)
|
|
742
|
-
return error
|
|
833
|
+
return error;
|
|
743
834
|
} finally {
|
|
744
835
|
completion.active = false;
|
|
745
836
|
}
|
|
746
|
-
return {};
|
|
837
|
+
return new Completing({});
|
|
838
|
+
}
|
|
839
|
+
async function optionsOf(run) {
|
|
840
|
+
return (await completingOf(run)).options;
|
|
841
|
+
}
|
|
842
|
+
function candidateLine(candidate) {
|
|
843
|
+
return typeof candidate === "string" ? candidate : candidate.join("\t");
|
|
844
|
+
}
|
|
845
|
+
function positionalBeingTyped(before, { options, positionals }) {
|
|
846
|
+
const takesValue = (word) => !word.includes("=") && Object.entries(options).some(([name, { short, type }]) => type === "string" && (word === `--${name}` || word === `-${short}`));
|
|
847
|
+
let index = 0;
|
|
848
|
+
let at = 0;
|
|
849
|
+
while (at < before.length) {
|
|
850
|
+
const word = before[at];
|
|
851
|
+
if (word === "--") {
|
|
852
|
+
index += before.length - at - 1;
|
|
853
|
+
break;
|
|
854
|
+
}
|
|
855
|
+
if (!word.startsWith("-"))
|
|
856
|
+
index++;
|
|
857
|
+
else if (takesValue(word))
|
|
858
|
+
at++;
|
|
859
|
+
at++;
|
|
860
|
+
}
|
|
861
|
+
const last = positionals.at(-1);
|
|
862
|
+
if (index < positionals.length)
|
|
863
|
+
return positionals[index];
|
|
864
|
+
return last?.replace("]", "").endsWith("...") === true ? last : undefined;
|
|
747
865
|
}
|
|
748
866
|
async function leafCandidates(run, words) {
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
867
|
+
const completing = await completingOf(run);
|
|
868
|
+
if ((words.at(-1) ?? "").startsWith("-"))
|
|
869
|
+
return Object.entries(completing.options).map(([name, option]) => option.description === undefined ? `--${name}` : `--${name} ${option.description}`);
|
|
870
|
+
const flag = words.at(-2);
|
|
871
|
+
const option = flag?.startsWith("-") === true ? Object.entries(completing.options).find(([name, { short }]) => flag === `--${name}` || flag === `-${short}`)?.[1] : undefined;
|
|
872
|
+
if (option?.type === "string") {
|
|
873
|
+
const values = option.choices === undefined ? await option.candidates?.() : option.choices;
|
|
874
|
+
return values === undefined ? [FILES] : values.map(candidateLine);
|
|
875
|
+
}
|
|
876
|
+
const name = positionalBeingTyped(words.slice(0, -1), completing);
|
|
877
|
+
const values = name === undefined ? undefined : await completing.candidates[name]?.();
|
|
878
|
+
return values === undefined ? [FILES] : values.map(candidateLine);
|
|
752
879
|
}
|
|
753
880
|
async function candidates(scope, defaultCommand, words, before = []) {
|
|
754
881
|
const [word = "", ...rest] = words;
|
|
@@ -759,8 +886,13 @@ async function candidates(scope, defaultCommand, words, before = []) {
|
|
|
759
886
|
({ commands, aliases } = await expand(scope, [...before, ...words]));
|
|
760
887
|
command = commands[aliases?.[word] ?? word];
|
|
761
888
|
}
|
|
762
|
-
if (listing)
|
|
763
|
-
|
|
889
|
+
if (listing) {
|
|
890
|
+
const own = await Promise.all(Object.entries(shown(commands)).map(async ([name, { summary }]) => `${name} ${await resolveSummary(summary)}`));
|
|
891
|
+
const fallback = defaultCommand === undefined ? undefined : commands[defaultCommand];
|
|
892
|
+
const values = fallback !== undefined && "run" in fallback ? await leafCandidates(fallback.run, [word]) : [];
|
|
893
|
+
const valueOf = (line) => line.slice(0, line.includes("\t") ? line.indexOf("\t") : undefined);
|
|
894
|
+
return [...own, ...values.filter((line) => line !== FILES && !(valueOf(line) in commands))];
|
|
895
|
+
}
|
|
764
896
|
if (command === undefined) {
|
|
765
897
|
const fallback = defaultCommand === undefined ? undefined : commands[defaultCommand];
|
|
766
898
|
return fallback !== undefined && "run" in fallback ? leafCandidates(fallback.run, words) : [];
|
|
@@ -775,7 +907,7 @@ async function printCandidates(found) {
|
|
|
775
907
|
async function runCli(cli, argv = process4.argv.slice(2)) {
|
|
776
908
|
if (argv[0] === COMPLETE)
|
|
777
909
|
return printCandidates(candidates(scopeOf(cli.commands, cli.extraCommands, cli.aliases), cli.defaultCommand, argv.slice(1)));
|
|
778
|
-
return 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, examples: cli.examples, replaces: cli.replaces }, argv, { version: cli.version, redact: cli.redact });
|
|
910
|
+
return 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 });
|
|
779
911
|
}
|
|
780
912
|
async function runMain({ name, version, run, redact }, argv = process4.argv.slice(2)) {
|
|
781
913
|
if (argv[0] === COMPLETE)
|
|
@@ -799,33 +931,16 @@ var {Glob } = globalThis.Bun;
|
|
|
799
931
|
var SESSION_FILES = new Glob("*.json");
|
|
800
932
|
var ACCOUNT_DIRS = new Glob("*/sessions");
|
|
801
933
|
// package.json
|
|
802
|
-
var version = "0.
|
|
934
|
+
var version = "0.3.0";
|
|
803
935
|
|
|
804
|
-
// src/
|
|
805
|
-
import {
|
|
806
|
-
import { join } from "path";
|
|
936
|
+
// src/completions.ts
|
|
937
|
+
import { existsSync as existsSync2, mkdirSync, readdirSync, readFileSync as readFileSync2, rmSync } from "fs";
|
|
938
|
+
import { join as join2 } from "path";
|
|
807
939
|
import process6 from "process";
|
|
808
|
-
var RUN_TIMEOUT_MS = 20000;
|
|
809
|
-
function hostHere() {
|
|
810
|
-
const home = homedir();
|
|
811
|
-
return {
|
|
812
|
-
home,
|
|
813
|
-
globalModules: join(process6.env.BUN_INSTALL ?? join(home, ".bun"), "install", "global", "node_modules"),
|
|
814
|
-
bunVersion: Bun.version,
|
|
815
|
-
which: (bin) => Bun.which(bin),
|
|
816
|
-
run: async (argv, env) => {
|
|
817
|
-
const ran = await runCommand(argv, { timeoutMs: RUN_TIMEOUT_MS, ...env === undefined ? {} : { env: { ...process6.env, ...env } } });
|
|
818
|
-
if (ran.timedOut)
|
|
819
|
-
return { code: 1, stdout: "", stderr: `timed out after ${RUN_TIMEOUT_MS / 1000}s` };
|
|
820
|
-
const of = (stream) => Buffer.concat(ran.chunks.filter((chunk) => chunk.stream === stream).map((chunk) => chunk.data)).toString();
|
|
821
|
-
return { code: ran.code, stdout: of("stdout"), stderr: of("stderr") };
|
|
822
|
-
}
|
|
823
|
-
};
|
|
824
|
-
}
|
|
825
940
|
|
|
826
941
|
// src/checks.ts
|
|
827
942
|
import { existsSync, readFileSync } from "fs";
|
|
828
|
-
import { basename, join
|
|
943
|
+
import { basename, join } from "path";
|
|
829
944
|
var TOOLS = {
|
|
830
945
|
"@kaynooo/cli-base": ["base", "proof"],
|
|
831
946
|
"@kaynooo/cli-changelog": ["changelog"],
|
|
@@ -842,6 +957,7 @@ var TOOLS = {
|
|
|
842
957
|
"@kaynooo/cli-out": ["out"],
|
|
843
958
|
"@kaynooo/cli-pkg": ["pkg"],
|
|
844
959
|
"@kaynooo/cli-stack": ["stack"],
|
|
960
|
+
"@kaynooo/cli-view": ["view"],
|
|
845
961
|
"@kaynooo/cli-wait": ["waitfor", "slot"]
|
|
846
962
|
};
|
|
847
963
|
var FORGE = "git.kaynooo.fr";
|
|
@@ -868,12 +984,12 @@ function firstLine(text) {
|
|
|
868
984
|
return end === -1 ? trimmed : trimmed.slice(0, end);
|
|
869
985
|
}
|
|
870
986
|
function installed(host, name) {
|
|
871
|
-
return readJson(
|
|
987
|
+
return readJson(join(host.globalModules, name, "package.json"));
|
|
872
988
|
}
|
|
873
989
|
function ownBin(host, name, bin) {
|
|
874
990
|
const declared = installed(host, name)?.bin;
|
|
875
991
|
const file = typeof declared === "string" ? declared : declared?.[bin];
|
|
876
|
-
return file === undefined ? undefined :
|
|
992
|
+
return file === undefined ? undefined : join(host.globalModules, name, file);
|
|
877
993
|
}
|
|
878
994
|
async function checkTool(host, name, bins) {
|
|
879
995
|
const check = name.slice(name.indexOf("/") + 1);
|
|
@@ -882,7 +998,7 @@ async function checkTool(host, name, bins) {
|
|
|
882
998
|
return { check, ok: false, detail: "not installed", fix: `bun install -g ${name}` };
|
|
883
999
|
const off = bins.filter((bin) => host.which(bin) === null);
|
|
884
1000
|
if (off.length > 0)
|
|
885
|
-
return { check, ok: false, detail: `${version}, ${off.join(", ")} not on the PATH`, fix: `add ${
|
|
1001
|
+
return { check, ok: false, detail: `${version}, ${off.join(", ")} not on the PATH`, fix: `add ${join(host.globalModules, "..", "..", "..", "bin")} to the PATH` };
|
|
886
1002
|
const info = await host.run(["bun", "info", name, "version", "--no-cache"]);
|
|
887
1003
|
const answered = info.code === 0 ? firstLine(info.stdout.trim().split(`
|
|
888
1004
|
`).at(-1) ?? "") : "";
|
|
@@ -909,9 +1025,9 @@ async function checkTea(host) {
|
|
|
909
1025
|
return { check: "tea", ok: true, detail: `logged in to ${forge} as ${login.name}, Forgejo ${served}` };
|
|
910
1026
|
}
|
|
911
1027
|
function checkHooks(host) {
|
|
912
|
-
const settings = readJson(
|
|
1028
|
+
const settings = readJson(join(host.home, ".claude", "settings.json")) ?? {};
|
|
913
1029
|
const plugin = settings.enabledPlugins?.[PLUGIN] === true;
|
|
914
|
-
const mod = (settings.env?.CLAUDE_CODE_PLUGIN_DIRS ?? "").split(":").map((dir) => dir.replace(LEADING_TILDE, host.home)).find((dir) => basename(dir) === "cli" && existsSync(
|
|
1030
|
+
const mod = (settings.env?.CLAUDE_CODE_PLUGIN_DIRS ?? "").split(":").map((dir) => dir.replace(LEADING_TILDE, host.home)).find((dir) => basename(dir) === "cli" && existsSync(join(dir, "hooks", "hooks.json")));
|
|
915
1031
|
if (plugin && mod !== undefined)
|
|
916
1032
|
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}` };
|
|
917
1033
|
if (plugin)
|
|
@@ -927,12 +1043,82 @@ function checkBun(host) {
|
|
|
927
1043
|
return { check: "bun", ok: false, detail: `${host.bunVersion}, the tools ask for ${[...new Set(unmet)].join(" and ")}`, fix: "bun upgrade" };
|
|
928
1044
|
return { check: "bun", ok: true, detail: floors.length === 0 ? host.bunVersion : `${host.bunVersion}, the tools ask for ${[...new Set(floors)].join(" and ")}` };
|
|
929
1045
|
}
|
|
1046
|
+
function checkCompletions(host) {
|
|
1047
|
+
if (host.which("fish") === null)
|
|
1048
|
+
return { check: "completions", ok: true, detail: "fish is not installed" };
|
|
1049
|
+
const dir = completionDir(host.home);
|
|
1050
|
+
const missing = missingCompletions(host, dir);
|
|
1051
|
+
if (missing.length > 0)
|
|
1052
|
+
return { check: "completions", ok: false, detail: `no fish completion in ${dir} for ${missing.join(", ")}`, fix: "cli completions fish --install" };
|
|
1053
|
+
return { check: "completions", ok: true, detail: dir };
|
|
1054
|
+
}
|
|
930
1055
|
async function runChecks(host) {
|
|
931
1056
|
const entries = Object.entries(TOOLS);
|
|
932
1057
|
const tools = [];
|
|
933
1058
|
for (let at = 0;at < entries.length; at += INFO_AT_ONCE)
|
|
934
1059
|
tools.push(...await Promise.all(entries.slice(at, at + INFO_AT_ONCE).map(async ([name, bins]) => checkTool(host, name, bins))));
|
|
935
|
-
return [...tools, await checkTea(host), checkHooks(host), checkBun(host)];
|
|
1060
|
+
return [...tools, await checkTea(host), checkHooks(host), checkBun(host), checkCompletions(host)];
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
// src/completions.ts
|
|
1064
|
+
var MARKER = "# Generated by `cli completions fish`";
|
|
1065
|
+
var FISH_FILE = /\.fish$/;
|
|
1066
|
+
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'
|
|
1067
|
+
set -l tokens (commandline -xpc 2>/dev/null; or commandline -opc)
|
|
1068
|
+
set -l current (commandline -ct)
|
|
1069
|
+
for line in ($bin __complete $tokens[2..-1] $current 2>/dev/null)
|
|
1070
|
+
if test "$line" = __files__
|
|
1071
|
+
__fish_complete_path $current
|
|
1072
|
+
else
|
|
1073
|
+
echo $line
|
|
1074
|
+
end
|
|
1075
|
+
end
|
|
1076
|
+
end`;
|
|
1077
|
+
function completionDir(home, env = process6.env) {
|
|
1078
|
+
return join2(env.XDG_CACHE_HOME ?? join2(home, ".cache"), "fish", "kaynooo-cli");
|
|
1079
|
+
}
|
|
1080
|
+
function installedBins(host) {
|
|
1081
|
+
return Object.values(TOOLS).flat().filter((bin) => host.which(bin) !== null);
|
|
1082
|
+
}
|
|
1083
|
+
function completionFile(bin) {
|
|
1084
|
+
return `${MARKER}
|
|
1085
|
+
${FUNCTION}
|
|
1086
|
+
complete -c ${bin} -f -a '(__kaynooo_cli_complete ${bin})'
|
|
1087
|
+
`;
|
|
1088
|
+
}
|
|
1089
|
+
async function installCompletions(host, dir) {
|
|
1090
|
+
const bins = installedBins(host);
|
|
1091
|
+
mkdirSync(dir, { recursive: true });
|
|
1092
|
+
const removed = readdirSync(dir).filter((name) => FISH_FILE.test(name) && !bins.includes(name.replace(FISH_FILE, "")) && readFileSync2(join2(dir, name), "utf8").startsWith(MARKER));
|
|
1093
|
+
for (const name of removed)
|
|
1094
|
+
rmSync(join2(dir, name));
|
|
1095
|
+
await Promise.all(bins.map(async (bin) => Bun.write(join2(dir, `${bin}.fish`), completionFile(bin))));
|
|
1096
|
+
return { written: bins, removed: removed.map((name) => name.replace(FISH_FILE, "")) };
|
|
1097
|
+
}
|
|
1098
|
+
function missingCompletions(host, dir) {
|
|
1099
|
+
return installedBins(host).filter((bin) => !existsSync2(join2(dir, `${bin}.fish`)));
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
// src/host.ts
|
|
1103
|
+
import { homedir } from "os";
|
|
1104
|
+
import { join as join3 } from "path";
|
|
1105
|
+
import process7 from "process";
|
|
1106
|
+
var RUN_TIMEOUT_MS = 20000;
|
|
1107
|
+
function hostHere() {
|
|
1108
|
+
const home = homedir();
|
|
1109
|
+
return {
|
|
1110
|
+
home,
|
|
1111
|
+
globalModules: join3(process7.env.BUN_INSTALL ?? join3(home, ".bun"), "install", "global", "node_modules"),
|
|
1112
|
+
bunVersion: Bun.version,
|
|
1113
|
+
which: (bin) => Bun.which(bin),
|
|
1114
|
+
run: async (argv, env) => {
|
|
1115
|
+
const ran = await runCommand(argv, { timeoutMs: RUN_TIMEOUT_MS, ...env === undefined ? {} : { env: { ...process7.env, ...env } } });
|
|
1116
|
+
if (ran.timedOut)
|
|
1117
|
+
return { code: 1, stdout: "", stderr: `timed out after ${RUN_TIMEOUT_MS / 1000}s` };
|
|
1118
|
+
const of = (stream) => Buffer.concat(ran.chunks.filter((chunk) => chunk.stream === stream).map((chunk) => chunk.data)).toString();
|
|
1119
|
+
return { code: ran.code, stdout: of("stdout"), stderr: of("stderr") };
|
|
1120
|
+
}
|
|
1121
|
+
};
|
|
936
1122
|
}
|
|
937
1123
|
|
|
938
1124
|
// src/list.ts
|
|
@@ -960,7 +1146,7 @@ var LIST_OPTIONS = {
|
|
|
960
1146
|
json: jsonOption("[{bin, package, installed, synopsis?, summary?, replaces?, examples?}]")
|
|
961
1147
|
};
|
|
962
1148
|
function print(listed) {
|
|
963
|
-
const { cyan, dim } = paint(
|
|
1149
|
+
const { cyan, dim } = paint(process8.stdout);
|
|
964
1150
|
const width = Math.max(...listed.map((each) => each.bin.length));
|
|
965
1151
|
for (const each of listed) {
|
|
966
1152
|
const what = !each.installed ? dim(`not installed: bun install -g ${each.package}`) : `${each.summary ?? ""}${each.replaces === undefined ? "" : dim(`; instead of ${each.replaces}`)}`;
|
|
@@ -976,18 +1162,42 @@ async function list(args, host = hostHere()) {
|
|
|
976
1162
|
print(listed);
|
|
977
1163
|
return 0;
|
|
978
1164
|
}
|
|
1165
|
+
var COMPLETIONS_USAGE = `cli completions fish [--install] [--dir <folder>]: fish completions for every installed bin of the @kaynooo/cli-* packages
|
|
1166
|
+
|
|
1167
|
+
Each bin completes itself through \`<bin> __complete\`; this writes the one line fish needs per bin. Without --install the files print on stdout, with it they go to ~/.cache/fish/kaynooo-cli (or $XDG_CACHE_HOME), one <bin>.fish each, the stale ones this command wrote taken out. That folder has to be in fish_complete_path, first so a completion fish ships for the same name does not win: \`set -p fish_complete_path ~/.cache/fish/kaynooo-cli\`.
|
|
1168
|
+
Exits 0 when it printed or wrote them, 2 on a usage error.`;
|
|
1169
|
+
var COMPLETIONS_OPTIONS = {
|
|
1170
|
+
install: { type: "boolean", description: "write the files to the folder instead of printing them" },
|
|
1171
|
+
dir: { type: "string", short: "d", description: "the folder to write to, instead of ~/.cache/fish/kaynooo-cli" }
|
|
1172
|
+
};
|
|
1173
|
+
async function completions(args, host = hostHere()) {
|
|
1174
|
+
const { values, positionals } = parse(args, COMPLETIONS_USAGE, COMPLETIONS_OPTIONS, { positionals: ["<shell>"], candidates: { "<shell>": () => ["fish"] }, examples: ["cli completions fish", ["cli completions fish --install", "write one file per bin"]] });
|
|
1175
|
+
if (positionals[0] !== "fish")
|
|
1176
|
+
badUsage("only fish is supported: cli completions fish");
|
|
1177
|
+
if (values.install !== true) {
|
|
1178
|
+
console.log(installedBins(host).map(completionFile).join(`
|
|
1179
|
+
`));
|
|
1180
|
+
return 0;
|
|
1181
|
+
}
|
|
1182
|
+
const dir = values.dir ?? completionDir(host.home);
|
|
1183
|
+
const { written, removed } = await installCompletions(host, dir);
|
|
1184
|
+
console.log(`wrote ${written.length} completions to ${dir}${removed.length === 0 ? "" : `, took out ${removed.join(", ")}`}`);
|
|
1185
|
+
return 0;
|
|
1186
|
+
}
|
|
979
1187
|
if (import.meta.main) {
|
|
980
|
-
|
|
1188
|
+
process8.exitCode = await runCli({
|
|
981
1189
|
name: "cli",
|
|
982
1190
|
version,
|
|
983
1191
|
about: "the bins of the @kaynooo/cli-* packages, described by their own help",
|
|
984
1192
|
replaces: "running each bin's --help to find the one that fits",
|
|
985
1193
|
examples: ["cli list", ["cli list --json", "with each bin's synopsis and examples"]],
|
|
986
1194
|
commands: {
|
|
987
|
-
list: { summary: "every bin in one line: what it does and what it is reached for instead of", run: async (args) => list(args) }
|
|
1195
|
+
list: { summary: "every bin in one line: what it does and what it is reached for instead of", run: async (args) => list(args) },
|
|
1196
|
+
completions: { summary: "fish completions for every installed bin, printed or written to the folder fish reads", run: async (args) => completions(args) }
|
|
988
1197
|
}
|
|
989
1198
|
});
|
|
990
1199
|
}
|
|
991
1200
|
export {
|
|
1201
|
+
completions,
|
|
992
1202
|
list
|
|
993
1203
|
};
|
package/dist/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// @bun
|
|
3
3
|
|
|
4
4
|
// src/cli.ts
|
|
5
|
-
import
|
|
5
|
+
import process8 from "process";
|
|
6
6
|
|
|
7
7
|
// ../kit/src/args.ts
|
|
8
8
|
import process2 from "process";
|
|
@@ -11,7 +11,7 @@ import { parseArgs } from "util";
|
|
|
11
11
|
// ../kit/src/style.ts
|
|
12
12
|
import process from "process";
|
|
13
13
|
|
|
14
|
-
// ../../node_modules/.bun/@kaynooo+utils@2.31.
|
|
14
|
+
// ../../node_modules/.bun/@kaynooo+utils@2.31.2+d0769e28dc4dad16/node_modules/@kaynooo/utils/dist/chunk-mtetydvc.js
|
|
15
15
|
function x(s) {
|
|
16
16
|
let r = s.startsWith("#") ? s.slice(1).toLowerCase() : s.toLowerCase();
|
|
17
17
|
if (!/^[a-f0-9]+$/.test(r))
|
|
@@ -231,7 +231,7 @@ class e {
|
|
|
231
231
|
}
|
|
232
232
|
}
|
|
233
233
|
|
|
234
|
-
// ../../node_modules/.bun/@kaynooo+utils@2.31.
|
|
234
|
+
// ../../node_modules/.bun/@kaynooo+utils@2.31.2+d0769e28dc4dad16/node_modules/@kaynooo/utils/dist/chunk-y1msbssb.js
|
|
235
235
|
var r = 39;
|
|
236
236
|
var o = 49;
|
|
237
237
|
var f2 = "\x1B[";
|
|
@@ -349,8 +349,12 @@ class HelpShown extends Exit {
|
|
|
349
349
|
|
|
350
350
|
class Completing {
|
|
351
351
|
options;
|
|
352
|
-
|
|
352
|
+
positionals;
|
|
353
|
+
candidates;
|
|
354
|
+
constructor(options, positionals = [], candidates = {}) {
|
|
353
355
|
this.options = options;
|
|
356
|
+
this.positionals = positionals;
|
|
357
|
+
this.candidates = candidates;
|
|
354
358
|
}
|
|
355
359
|
}
|
|
356
360
|
var completion = { active: false };
|
|
@@ -393,8 +397,10 @@ function exampleRows(examples, colors = paint(process2.stdout)) {
|
|
|
393
397
|
function optionRows(options, colors = paint(process2.stdout)) {
|
|
394
398
|
const rows = Object.entries(options).map(([name, option]) => {
|
|
395
399
|
const short = option.short === undefined ? " " : `${colors.cyan(`-${option.short}`)}, `;
|
|
396
|
-
const
|
|
397
|
-
|
|
400
|
+
const named = option.choices === undefined ? "<value>" : `<${option.choices.join("|")}>`;
|
|
401
|
+
const placeholder = option.optional === undefined ? named : `[${named}]`;
|
|
402
|
+
const value = option.type === "string" ? ` ${colors.dim(placeholder)}` : "";
|
|
403
|
+
return { flag: `${short}${colors.cyan(`--${name}`)}${value}`, width: `-x, --${name}`.length + (option.type === "string" ? placeholder.length + 1 : 0), description: option.description ?? "" };
|
|
398
404
|
});
|
|
399
405
|
const width = Math.max(0, ...rows.map((row) => row.width));
|
|
400
406
|
return rows.map((row) => row.description === "" ? ` ${row.flag}` : ` ${row.flag}${" ".repeat(width - row.width)} ${colors.dim(row.description)}`);
|
|
@@ -420,14 +426,13 @@ function applyOutput(color, plain, env = process2.env) {
|
|
|
420
426
|
function parse(args, usage, options, settings = {}) {
|
|
421
427
|
const output = Object.fromEntries(Object.entries(OUTPUT_OPTIONS).filter(([name]) => !(name in options)));
|
|
422
428
|
const all = { ...options, ...output, help: { type: "boolean", short: "h", description: "show this help" } };
|
|
429
|
+
const listing = listed(all, settings.allowNegative === true);
|
|
423
430
|
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);
|
|
431
|
+
throw new Completing(listing, settings.positionals, settings.candidates);
|
|
432
|
+
const { own, rest } = settings.trailing === true ? splitTrailing(args, all, settings.positionals?.length ?? 0) : splitRest(args, settings.rest === true);
|
|
428
433
|
let parsed;
|
|
429
434
|
try {
|
|
430
|
-
parsed = parseArgs({ args: joinDashValues(own, all), options: all, allowPositionals: true, allowNegative: settings.allowNegative === true, strict: true });
|
|
435
|
+
parsed = parseArgs({ args: joinDashValues(withBare(own, all), all), options: all, allowPositionals: true, allowNegative: settings.allowNegative === true, strict: true });
|
|
431
436
|
} catch (error) {
|
|
432
437
|
const message = parseError(error);
|
|
433
438
|
throw new UsageError(message, nearestOption(message, all, settings.allowNegative === true));
|
|
@@ -435,12 +440,84 @@ function parse(args, usage, options, settings = {}) {
|
|
|
435
440
|
const values = parsed.values;
|
|
436
441
|
applyOutput("color" in output ? values.color : undefined, "plain" in output ? values.plain : undefined);
|
|
437
442
|
if (values.help === true) {
|
|
438
|
-
console.log(wantsHelpJson() ? JSON.stringify(description(usage, settings)) : helpText(usage,
|
|
443
|
+
console.log(wantsHelpJson() ? JSON.stringify(description(usage, settings)) : helpText(usage, listing, undefined, settings));
|
|
439
444
|
throw new HelpShown;
|
|
440
445
|
}
|
|
446
|
+
checkChoices(parsed.values, all);
|
|
447
|
+
checkConflicts(parsed.values, all);
|
|
441
448
|
if (settings.positionals !== undefined)
|
|
442
449
|
checkPositionals(parsed.positionals, settings.positionals);
|
|
443
|
-
return { ...parsed, positionals: parsed.positionals, rest };
|
|
450
|
+
return { ...parsed, values: parsed.values, positionals: parsed.positionals, rest };
|
|
451
|
+
}
|
|
452
|
+
function splitRest(args, passes) {
|
|
453
|
+
const separator = passes ? args.indexOf("--") : -1;
|
|
454
|
+
return { own: separator === -1 ? args : args.slice(0, separator), rest: separator === -1 ? undefined : args.slice(separator + 1) };
|
|
455
|
+
}
|
|
456
|
+
function splitTrailing(args, options, count) {
|
|
457
|
+
const takesValue = (word) => !word.includes("=") && Object.entries(options).some(([name, { short, type }]) => type === "string" && (word === `--${name}` || word === `-${short}`));
|
|
458
|
+
let seen = 0;
|
|
459
|
+
if (count === 0)
|
|
460
|
+
return { own: args, rest: undefined };
|
|
461
|
+
for (let at = 0;at < args.length; at++) {
|
|
462
|
+
const word = args[at];
|
|
463
|
+
if (word === "--")
|
|
464
|
+
break;
|
|
465
|
+
if (word.startsWith("-")) {
|
|
466
|
+
if (takesValue(word))
|
|
467
|
+
at++;
|
|
468
|
+
continue;
|
|
469
|
+
}
|
|
470
|
+
seen++;
|
|
471
|
+
if (seen === count)
|
|
472
|
+
return { own: args.slice(0, at + 1), rest: args.slice(at + 1) };
|
|
473
|
+
}
|
|
474
|
+
return { own: args, rest: undefined };
|
|
475
|
+
}
|
|
476
|
+
function listed(options, allowNegative) {
|
|
477
|
+
if (!allowNegative)
|
|
478
|
+
return options;
|
|
479
|
+
return Object.fromEntries(Object.entries(options).flatMap(([name, option]) => {
|
|
480
|
+
if (option.type !== "boolean" || option.negative === undefined)
|
|
481
|
+
return [[name, option]];
|
|
482
|
+
return [...option.description === undefined ? [] : [[name, option]], [`no-${name}`, { type: "boolean", description: option.negative }]];
|
|
483
|
+
}));
|
|
484
|
+
}
|
|
485
|
+
function withBare(args, options) {
|
|
486
|
+
const bare = new Map(Object.entries(options).flatMap(([name, { short, optional }]) => optional === undefined ? [] : [[`--${name}`, optional], ...short === undefined ? [] : [[`-${short}`, optional]]]));
|
|
487
|
+
if (bare.size === 0)
|
|
488
|
+
return args;
|
|
489
|
+
const separator = args.indexOf("--");
|
|
490
|
+
const own = separator === -1 ? args : args.slice(0, separator);
|
|
491
|
+
return [...own.flatMap((arg, index) => {
|
|
492
|
+
const optional = bare.get(arg);
|
|
493
|
+
const next = own[index + 1];
|
|
494
|
+
const value = next !== undefined && (optional?.accepts === undefined ? !next.startsWith("-") : optional.accepts.test(next));
|
|
495
|
+
return optional === undefined || value ? [arg] : [arg, optional.bare];
|
|
496
|
+
}), ...separator === -1 ? [] : args.slice(separator)];
|
|
497
|
+
}
|
|
498
|
+
function checkConflicts(values, options) {
|
|
499
|
+
const given = (name) => values[name] !== undefined && values[name] !== false;
|
|
500
|
+
for (const [name, { conflicts = [] }] of Object.entries(options)) {
|
|
501
|
+
const other = given(name) ? conflicts.find(given) : undefined;
|
|
502
|
+
if (other !== undefined)
|
|
503
|
+
badUsage(`--${name} cannot be used with --${other}`);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
function checkChoices(values, options) {
|
|
507
|
+
for (const [name, option] of Object.entries(options)) {
|
|
508
|
+
if (option.choices === undefined)
|
|
509
|
+
continue;
|
|
510
|
+
const given = values[name];
|
|
511
|
+
for (const value of Array.isArray(given) ? given : [given]) {
|
|
512
|
+
if (typeof value === "string")
|
|
513
|
+
oneOf(value, name, option.choices);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
function oneOf(value, name, choices) {
|
|
518
|
+
if (!choices.includes(value))
|
|
519
|
+
badUsage(`${name.startsWith("<") ? name : `--${name}`} takes one of ${choices.join(", ")}, not ${value}`);
|
|
520
|
+
return value;
|
|
444
521
|
}
|
|
445
522
|
function checkPositionals(given, names) {
|
|
446
523
|
const variadic = names.at(-1)?.replace("]", "").endsWith("...") === true;
|
|
@@ -591,8 +668,9 @@ var LEVEL_OPTIONS = {
|
|
|
591
668
|
help: { type: "boolean", short: "h", description: "show this help" }
|
|
592
669
|
};
|
|
593
670
|
var TOP_OPTIONS = { ...LEVEL_OPTIONS, version: { type: "boolean", short: "v", description: "print the version" } };
|
|
594
|
-
async function commandRows(
|
|
595
|
-
const
|
|
671
|
+
async function commandRows(all, colors) {
|
|
672
|
+
const commands = shown(all);
|
|
673
|
+
const width = Math.max(0, ...Object.keys(commands).map((name) => name.length));
|
|
596
674
|
const groups = new Map([["Commands:", []]]);
|
|
597
675
|
const summaries = await Promise.all(Object.values(commands).map(async ({ summary }) => resolveSummary(summary)));
|
|
598
676
|
for (const [index, [name, { group }]] of Object.entries(commands).entries()) {
|
|
@@ -603,6 +681,9 @@ async function commandRows(commands, colors) {
|
|
|
603
681
|
}
|
|
604
682
|
return [...groups].filter(([, rows]) => rows.length > 0).flatMap(([heading, rows], index) => [...index === 0 ? [] : [""], colors.bold(heading), ...rows]);
|
|
605
683
|
}
|
|
684
|
+
function shown(commands) {
|
|
685
|
+
return Object.fromEntries(Object.entries(commands).filter(([, { hidden }]) => hidden !== true));
|
|
686
|
+
}
|
|
606
687
|
function takeOutputFlags(argv) {
|
|
607
688
|
let index = 0;
|
|
608
689
|
for (;index < argv.length; index++) {
|
|
@@ -694,6 +775,14 @@ async function dispatch(level, typed, { version, redact }) {
|
|
|
694
775
|
return reportThrown(prefix, error);
|
|
695
776
|
}
|
|
696
777
|
const [name, ...args] = argv;
|
|
778
|
+
const fallback = level.defaultCommand === undefined ? undefined : level.commands[level.defaultCommand];
|
|
779
|
+
if (name === undefined && level.runBare && fallback !== undefined && "run" in fallback) {
|
|
780
|
+
try {
|
|
781
|
+
return await fallback.run([]);
|
|
782
|
+
} catch (error) {
|
|
783
|
+
return reportThrown(`${prefix} ${level.defaultCommand}`, error, redact, false);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
697
786
|
if (name === undefined && level.path.length > 1) {
|
|
698
787
|
console.error(await help(await expand(level, [...level.path.slice(1), ...argv]), process4.stderr));
|
|
699
788
|
return 2;
|
|
@@ -708,6 +797,7 @@ async function dispatch(level, typed, { version, redact }) {
|
|
|
708
797
|
}
|
|
709
798
|
let command = level.commands[level.aliases?.[name] ?? name];
|
|
710
799
|
let rest = args;
|
|
800
|
+
let ran = name;
|
|
711
801
|
if (command === undefined && level.lazy.length > 0) {
|
|
712
802
|
level = await expand(level, [...level.path.slice(1), ...argv]);
|
|
713
803
|
command = level.commands[level.aliases?.[name] ?? name];
|
|
@@ -715,40 +805,77 @@ async function dispatch(level, typed, { version, redact }) {
|
|
|
715
805
|
if (command === undefined && level.defaultCommand !== undefined) {
|
|
716
806
|
command = level.commands[level.defaultCommand];
|
|
717
807
|
rest = argv;
|
|
808
|
+
ran = level.defaultCommand;
|
|
718
809
|
}
|
|
719
810
|
if (command === undefined) {
|
|
720
|
-
const meant = closest(name, Object.keys(level.commands));
|
|
811
|
+
const meant = closest(name, Object.keys(shown(level.commands)));
|
|
721
812
|
printError(prefix, `no command ${name}`, meant === undefined ? `run ${prefix} --help for its commands` : `did you mean ${meant}?`);
|
|
722
813
|
return 2;
|
|
723
814
|
}
|
|
724
815
|
if ("commands" in command) {
|
|
725
816
|
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 });
|
|
817
|
+
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
818
|
}
|
|
728
819
|
try {
|
|
729
820
|
return await command.run(rest);
|
|
730
821
|
} catch (error) {
|
|
731
|
-
return reportThrown(`${prefix} ${
|
|
822
|
+
return reportThrown(`${prefix} ${ran}`, error, redact, wantsJson(rest));
|
|
732
823
|
}
|
|
733
824
|
}
|
|
734
825
|
var COMPLETE = "__complete";
|
|
735
826
|
var FILES = "__files__";
|
|
736
|
-
async function
|
|
827
|
+
async function completingOf(run) {
|
|
737
828
|
completion.active = true;
|
|
738
829
|
try {
|
|
739
830
|
await run([]);
|
|
740
831
|
} catch (error) {
|
|
741
832
|
if (error instanceof Completing)
|
|
742
|
-
return error
|
|
833
|
+
return error;
|
|
743
834
|
} finally {
|
|
744
835
|
completion.active = false;
|
|
745
836
|
}
|
|
746
|
-
return {};
|
|
837
|
+
return new Completing({});
|
|
838
|
+
}
|
|
839
|
+
async function optionsOf(run) {
|
|
840
|
+
return (await completingOf(run)).options;
|
|
841
|
+
}
|
|
842
|
+
function candidateLine(candidate) {
|
|
843
|
+
return typeof candidate === "string" ? candidate : candidate.join("\t");
|
|
844
|
+
}
|
|
845
|
+
function positionalBeingTyped(before, { options, positionals }) {
|
|
846
|
+
const takesValue = (word) => !word.includes("=") && Object.entries(options).some(([name, { short, type }]) => type === "string" && (word === `--${name}` || word === `-${short}`));
|
|
847
|
+
let index = 0;
|
|
848
|
+
let at = 0;
|
|
849
|
+
while (at < before.length) {
|
|
850
|
+
const word = before[at];
|
|
851
|
+
if (word === "--") {
|
|
852
|
+
index += before.length - at - 1;
|
|
853
|
+
break;
|
|
854
|
+
}
|
|
855
|
+
if (!word.startsWith("-"))
|
|
856
|
+
index++;
|
|
857
|
+
else if (takesValue(word))
|
|
858
|
+
at++;
|
|
859
|
+
at++;
|
|
860
|
+
}
|
|
861
|
+
const last = positionals.at(-1);
|
|
862
|
+
if (index < positionals.length)
|
|
863
|
+
return positionals[index];
|
|
864
|
+
return last?.replace("]", "").endsWith("...") === true ? last : undefined;
|
|
747
865
|
}
|
|
748
866
|
async function leafCandidates(run, words) {
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
867
|
+
const completing = await completingOf(run);
|
|
868
|
+
if ((words.at(-1) ?? "").startsWith("-"))
|
|
869
|
+
return Object.entries(completing.options).map(([name, option]) => option.description === undefined ? `--${name}` : `--${name} ${option.description}`);
|
|
870
|
+
const flag = words.at(-2);
|
|
871
|
+
const option = flag?.startsWith("-") === true ? Object.entries(completing.options).find(([name, { short }]) => flag === `--${name}` || flag === `-${short}`)?.[1] : undefined;
|
|
872
|
+
if (option?.type === "string") {
|
|
873
|
+
const values = option.choices === undefined ? await option.candidates?.() : option.choices;
|
|
874
|
+
return values === undefined ? [FILES] : values.map(candidateLine);
|
|
875
|
+
}
|
|
876
|
+
const name = positionalBeingTyped(words.slice(0, -1), completing);
|
|
877
|
+
const values = name === undefined ? undefined : await completing.candidates[name]?.();
|
|
878
|
+
return values === undefined ? [FILES] : values.map(candidateLine);
|
|
752
879
|
}
|
|
753
880
|
async function candidates(scope, defaultCommand, words, before = []) {
|
|
754
881
|
const [word = "", ...rest] = words;
|
|
@@ -759,8 +886,13 @@ async function candidates(scope, defaultCommand, words, before = []) {
|
|
|
759
886
|
({ commands, aliases } = await expand(scope, [...before, ...words]));
|
|
760
887
|
command = commands[aliases?.[word] ?? word];
|
|
761
888
|
}
|
|
762
|
-
if (listing)
|
|
763
|
-
|
|
889
|
+
if (listing) {
|
|
890
|
+
const own = await Promise.all(Object.entries(shown(commands)).map(async ([name, { summary }]) => `${name} ${await resolveSummary(summary)}`));
|
|
891
|
+
const fallback = defaultCommand === undefined ? undefined : commands[defaultCommand];
|
|
892
|
+
const values = fallback !== undefined && "run" in fallback ? await leafCandidates(fallback.run, [word]) : [];
|
|
893
|
+
const valueOf = (line) => line.slice(0, line.includes("\t") ? line.indexOf("\t") : undefined);
|
|
894
|
+
return [...own, ...values.filter((line) => line !== FILES && !(valueOf(line) in commands))];
|
|
895
|
+
}
|
|
764
896
|
if (command === undefined) {
|
|
765
897
|
const fallback = defaultCommand === undefined ? undefined : commands[defaultCommand];
|
|
766
898
|
return fallback !== undefined && "run" in fallback ? leafCandidates(fallback.run, words) : [];
|
|
@@ -775,7 +907,7 @@ async function printCandidates(found) {
|
|
|
775
907
|
async function runCli(cli, argv = process4.argv.slice(2)) {
|
|
776
908
|
if (argv[0] === COMPLETE)
|
|
777
909
|
return printCandidates(candidates(scopeOf(cli.commands, cli.extraCommands, cli.aliases), cli.defaultCommand, argv.slice(1)));
|
|
778
|
-
return 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, examples: cli.examples, replaces: cli.replaces }, argv, { version: cli.version, redact: cli.redact });
|
|
910
|
+
return 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 });
|
|
779
911
|
}
|
|
780
912
|
async function runMain({ name, version, run, redact }, argv = process4.argv.slice(2)) {
|
|
781
913
|
if (argv[0] === COMPLETE)
|
|
@@ -799,11 +931,55 @@ var {Glob } = globalThis.Bun;
|
|
|
799
931
|
var SESSION_FILES = new Glob("*.json");
|
|
800
932
|
var ACCOUNT_DIRS = new Glob("*/sessions");
|
|
801
933
|
// package.json
|
|
802
|
-
var version = "0.
|
|
934
|
+
var version = "0.3.0";
|
|
935
|
+
|
|
936
|
+
// src/checks.ts
|
|
937
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
938
|
+
import { basename, join as join2 } from "path";
|
|
939
|
+
|
|
940
|
+
// src/completions.ts
|
|
941
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync } from "fs";
|
|
942
|
+
import { join } from "path";
|
|
943
|
+
import process6 from "process";
|
|
944
|
+
var MARKER = "# Generated by `cli completions fish`";
|
|
945
|
+
var FISH_FILE = /\.fish$/;
|
|
946
|
+
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'
|
|
947
|
+
set -l tokens (commandline -xpc 2>/dev/null; or commandline -opc)
|
|
948
|
+
set -l current (commandline -ct)
|
|
949
|
+
for line in ($bin __complete $tokens[2..-1] $current 2>/dev/null)
|
|
950
|
+
if test "$line" = __files__
|
|
951
|
+
__fish_complete_path $current
|
|
952
|
+
else
|
|
953
|
+
echo $line
|
|
954
|
+
end
|
|
955
|
+
end
|
|
956
|
+
end`;
|
|
957
|
+
function completionDir(home, env = process6.env) {
|
|
958
|
+
return join(env.XDG_CACHE_HOME ?? join(home, ".cache"), "fish", "kaynooo-cli");
|
|
959
|
+
}
|
|
960
|
+
function installedBins(host) {
|
|
961
|
+
return Object.values(TOOLS).flat().filter((bin) => host.which(bin) !== null);
|
|
962
|
+
}
|
|
963
|
+
function completionFile(bin) {
|
|
964
|
+
return `${MARKER}
|
|
965
|
+
${FUNCTION}
|
|
966
|
+
complete -c ${bin} -f -a '(__kaynooo_cli_complete ${bin})'
|
|
967
|
+
`;
|
|
968
|
+
}
|
|
969
|
+
async function installCompletions(host, dir) {
|
|
970
|
+
const bins = installedBins(host);
|
|
971
|
+
mkdirSync(dir, { recursive: true });
|
|
972
|
+
const removed = readdirSync(dir).filter((name) => FISH_FILE.test(name) && !bins.includes(name.replace(FISH_FILE, "")) && readFileSync(join(dir, name), "utf8").startsWith(MARKER));
|
|
973
|
+
for (const name of removed)
|
|
974
|
+
rmSync(join(dir, name));
|
|
975
|
+
await Promise.all(bins.map(async (bin) => Bun.write(join(dir, `${bin}.fish`), completionFile(bin))));
|
|
976
|
+
return { written: bins, removed: removed.map((name) => name.replace(FISH_FILE, "")) };
|
|
977
|
+
}
|
|
978
|
+
function missingCompletions(host, dir) {
|
|
979
|
+
return installedBins(host).filter((bin) => !existsSync(join(dir, `${bin}.fish`)));
|
|
980
|
+
}
|
|
803
981
|
|
|
804
982
|
// src/checks.ts
|
|
805
|
-
import { existsSync, readFileSync } from "fs";
|
|
806
|
-
import { basename, join } from "path";
|
|
807
983
|
var TOOLS = {
|
|
808
984
|
"@kaynooo/cli-base": ["base", "proof"],
|
|
809
985
|
"@kaynooo/cli-changelog": ["changelog"],
|
|
@@ -820,6 +996,7 @@ var TOOLS = {
|
|
|
820
996
|
"@kaynooo/cli-out": ["out"],
|
|
821
997
|
"@kaynooo/cli-pkg": ["pkg"],
|
|
822
998
|
"@kaynooo/cli-stack": ["stack"],
|
|
999
|
+
"@kaynooo/cli-view": ["view"],
|
|
823
1000
|
"@kaynooo/cli-wait": ["waitfor", "slot"]
|
|
824
1001
|
};
|
|
825
1002
|
var FORGE = "git.kaynooo.fr";
|
|
@@ -837,7 +1014,7 @@ function readJsonText(text) {
|
|
|
837
1014
|
}
|
|
838
1015
|
}
|
|
839
1016
|
function readJson(path) {
|
|
840
|
-
return
|
|
1017
|
+
return existsSync2(path) ? readJsonText(readFileSync2(path, "utf8")) : undefined;
|
|
841
1018
|
}
|
|
842
1019
|
function firstLine(text) {
|
|
843
1020
|
const trimmed = text.trim();
|
|
@@ -846,12 +1023,12 @@ function firstLine(text) {
|
|
|
846
1023
|
return end === -1 ? trimmed : trimmed.slice(0, end);
|
|
847
1024
|
}
|
|
848
1025
|
function installed(host, name) {
|
|
849
|
-
return readJson(
|
|
1026
|
+
return readJson(join2(host.globalModules, name, "package.json"));
|
|
850
1027
|
}
|
|
851
1028
|
function ownBin(host, name, bin) {
|
|
852
1029
|
const declared = installed(host, name)?.bin;
|
|
853
1030
|
const file = typeof declared === "string" ? declared : declared?.[bin];
|
|
854
|
-
return file === undefined ? undefined :
|
|
1031
|
+
return file === undefined ? undefined : join2(host.globalModules, name, file);
|
|
855
1032
|
}
|
|
856
1033
|
async function checkTool(host, name, bins) {
|
|
857
1034
|
const check = name.slice(name.indexOf("/") + 1);
|
|
@@ -860,7 +1037,7 @@ async function checkTool(host, name, bins) {
|
|
|
860
1037
|
return { check, ok: false, detail: "not installed", fix: `bun install -g ${name}` };
|
|
861
1038
|
const off = bins.filter((bin) => host.which(bin) === null);
|
|
862
1039
|
if (off.length > 0)
|
|
863
|
-
return { check, ok: false, detail: `${version}, ${off.join(", ")} not on the PATH`, fix: `add ${
|
|
1040
|
+
return { check, ok: false, detail: `${version}, ${off.join(", ")} not on the PATH`, fix: `add ${join2(host.globalModules, "..", "..", "..", "bin")} to the PATH` };
|
|
864
1041
|
const info = await host.run(["bun", "info", name, "version", "--no-cache"]);
|
|
865
1042
|
const answered = info.code === 0 ? firstLine(info.stdout.trim().split(`
|
|
866
1043
|
`).at(-1) ?? "") : "";
|
|
@@ -887,9 +1064,9 @@ async function checkTea(host) {
|
|
|
887
1064
|
return { check: "tea", ok: true, detail: `logged in to ${forge} as ${login.name}, Forgejo ${served}` };
|
|
888
1065
|
}
|
|
889
1066
|
function checkHooks(host) {
|
|
890
|
-
const settings = readJson(
|
|
1067
|
+
const settings = readJson(join2(host.home, ".claude", "settings.json")) ?? {};
|
|
891
1068
|
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" &&
|
|
1069
|
+
const mod = (settings.env?.CLAUDE_CODE_PLUGIN_DIRS ?? "").split(":").map((dir) => dir.replace(LEADING_TILDE, host.home)).find((dir) => basename(dir) === "cli" && existsSync2(join2(dir, "hooks", "hooks.json")));
|
|
893
1070
|
if (plugin && mod !== undefined)
|
|
894
1071
|
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
1072
|
if (plugin)
|
|
@@ -905,28 +1082,37 @@ function checkBun(host) {
|
|
|
905
1082
|
return { check: "bun", ok: false, detail: `${host.bunVersion}, the tools ask for ${[...new Set(unmet)].join(" and ")}`, fix: "bun upgrade" };
|
|
906
1083
|
return { check: "bun", ok: true, detail: floors.length === 0 ? host.bunVersion : `${host.bunVersion}, the tools ask for ${[...new Set(floors)].join(" and ")}` };
|
|
907
1084
|
}
|
|
1085
|
+
function checkCompletions(host) {
|
|
1086
|
+
if (host.which("fish") === null)
|
|
1087
|
+
return { check: "completions", ok: true, detail: "fish is not installed" };
|
|
1088
|
+
const dir = completionDir(host.home);
|
|
1089
|
+
const missing = missingCompletions(host, dir);
|
|
1090
|
+
if (missing.length > 0)
|
|
1091
|
+
return { check: "completions", ok: false, detail: `no fish completion in ${dir} for ${missing.join(", ")}`, fix: "cli completions fish --install" };
|
|
1092
|
+
return { check: "completions", ok: true, detail: dir };
|
|
1093
|
+
}
|
|
908
1094
|
async function runChecks(host) {
|
|
909
1095
|
const entries = Object.entries(TOOLS);
|
|
910
1096
|
const tools = [];
|
|
911
1097
|
for (let at = 0;at < entries.length; at += INFO_AT_ONCE)
|
|
912
1098
|
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)];
|
|
1099
|
+
return [...tools, await checkTea(host), checkHooks(host), checkBun(host), checkCompletions(host)];
|
|
914
1100
|
}
|
|
915
1101
|
|
|
916
1102
|
// src/host.ts
|
|
917
1103
|
import { homedir } from "os";
|
|
918
|
-
import { join as
|
|
919
|
-
import
|
|
1104
|
+
import { join as join3 } from "path";
|
|
1105
|
+
import process7 from "process";
|
|
920
1106
|
var RUN_TIMEOUT_MS = 20000;
|
|
921
1107
|
function hostHere() {
|
|
922
1108
|
const home = homedir();
|
|
923
1109
|
return {
|
|
924
1110
|
home,
|
|
925
|
-
globalModules:
|
|
1111
|
+
globalModules: join3(process7.env.BUN_INSTALL ?? join3(home, ".bun"), "install", "global", "node_modules"),
|
|
926
1112
|
bunVersion: Bun.version,
|
|
927
1113
|
which: (bin) => Bun.which(bin),
|
|
928
1114
|
run: async (argv, env) => {
|
|
929
|
-
const ran = await runCommand(argv, { timeoutMs: RUN_TIMEOUT_MS, ...env === undefined ? {} : { env: { ...
|
|
1115
|
+
const ran = await runCommand(argv, { timeoutMs: RUN_TIMEOUT_MS, ...env === undefined ? {} : { env: { ...process7.env, ...env } } });
|
|
930
1116
|
if (ran.timedOut)
|
|
931
1117
|
return { code: 1, stdout: "", stderr: `timed out after ${RUN_TIMEOUT_MS / 1000}s` };
|
|
932
1118
|
const of = (stream) => Buffer.concat(ran.chunks.filter((chunk) => chunk.stream === stream).map((chunk) => chunk.data)).toString();
|
|
@@ -945,7 +1131,7 @@ var OPTIONS = {
|
|
|
945
1131
|
json: jsonOption("[{check, ok, detail, fix?}]")
|
|
946
1132
|
};
|
|
947
1133
|
function print(results) {
|
|
948
|
-
const { green, red, dim } = paint(
|
|
1134
|
+
const { green, red, dim } = paint(process8.stdout);
|
|
949
1135
|
const width = Math.max(...results.map((result) => result.check.length));
|
|
950
1136
|
for (const { check, ok, detail, fix } of results)
|
|
951
1137
|
console.log(`${ok ? green("ok ") : red("FAIL")} ${check.padEnd(width)} ${detail}${fix === undefined ? "" : ` ${dim("fix:")} ${fix}`}`);
|
|
@@ -964,7 +1150,7 @@ async function main(args, host = hostHere()) {
|
|
|
964
1150
|
return results.every((result) => result.ok) ? 0 : 1;
|
|
965
1151
|
}
|
|
966
1152
|
if (import.meta.main)
|
|
967
|
-
|
|
1153
|
+
process8.exitCode = await runMain({ name: "doctor", version, run: async (args) => main(args) });
|
|
968
1154
|
export {
|
|
969
1155
|
main
|
|
970
1156
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kaynooo/cli-doctor",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.0",
|
|
5
5
|
"description": "doctor: checks the @kaynooo/cli-* install of a host: each bin's version against the registry, tea's login to Forgejo, the Claude Code hooks and the Bun floor; cli list: every bin in one line, read from its own help.",
|
|
6
6
|
"author": "Kaynooo <contact@kaynooo.fr>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -36,6 +36,6 @@
|
|
|
36
36
|
"doctor": "$npm_execpath src/cli.ts"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@kaynooo/cli-kit": "0.
|
|
39
|
+
"@kaynooo/cli-kit": "0.19.0"
|
|
40
40
|
}
|
|
41
41
|
}
|