@kaynooo/cli-doctor 0.5.2 → 0.7.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 +2 -2
- package/dist/cli-bin.js +140 -23
- package/dist/cli.js +34 -23
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -38,10 +38,10 @@ Each bin completes itself through `<bin> __complete`. fish gets a file per bin (
|
|
|
38
38
|
## cli stats
|
|
39
39
|
|
|
40
40
|
```sh
|
|
41
|
-
cli stats [--since 7d] [--json] # runs per bin, failures, and the options usage errors did not know
|
|
41
|
+
cli stats [--since 7d] [--json] # runs per bin and per command, failures (exit 1) apart from usage errors (exit 2), and the options usage errors did not know
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
Every bin records each run in `~/.local/state/kaynooo-cli/usage.jsonl`: the bin, its command, the flag names it saw (never values or arguments), the exit code and, on a usage error, the unknown option. `KAYNOOO_CLI_NO_USAGE=1` turns it off; `guard` never records.
|
|
44
|
+
Every bin records each run in `~/.local/state/kaynooo-cli/usage.jsonl`: the bin, its command, the flag names it saw (never values or arguments), the exit code and, on a usage error, the unknown option (never its message). `KAYNOOO_CLI_NO_USAGE=1` turns it off; `guard` never records.
|
|
45
45
|
|
|
46
46
|
## cli list
|
|
47
47
|
|
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 process13 from "process";
|
|
6
6
|
// ../kit/src/args.ts
|
|
7
7
|
import process5 from "process";
|
|
8
8
|
import { parseArgs } from "util";
|
|
@@ -808,26 +808,38 @@ async function readUsage(path = usagePath(), since = 0) {
|
|
|
808
808
|
}
|
|
809
809
|
});
|
|
810
810
|
}
|
|
811
|
+
function tally(counts, code) {
|
|
812
|
+
counts.runs++;
|
|
813
|
+
counts.failed += code === 0 || code === 2 ? 0 : 1;
|
|
814
|
+
counts.usageErrors += code === 2 ? 1 : 0;
|
|
815
|
+
}
|
|
811
816
|
function summarizeUsage(entries) {
|
|
812
817
|
const bins = new Map;
|
|
813
|
-
const
|
|
818
|
+
const commands = new Map;
|
|
819
|
+
const unknowns = new Map;
|
|
814
820
|
for (const entry of entries) {
|
|
815
821
|
const bin = bins.get(entry.bin) ?? { runs: 0, failed: 0, usageErrors: 0 };
|
|
816
|
-
bin.
|
|
817
|
-
bin.failed += entry.code === 0 ? 0 : 1;
|
|
818
|
-
bin.usageErrors += entry.code === 2 ? 1 : 0;
|
|
822
|
+
tally(bin, entry.code);
|
|
819
823
|
bins.set(entry.bin, bin);
|
|
820
|
-
if (entry.
|
|
824
|
+
if (entry.command !== undefined) {
|
|
825
|
+
const command = commands.get(`${entry.bin} ${entry.command}`) ?? { bin: entry.bin, command: entry.command, runs: 0, failed: 0, usageErrors: 0 };
|
|
826
|
+
tally(command, entry.code);
|
|
827
|
+
commands.set(`${entry.bin} ${entry.command}`, command);
|
|
828
|
+
}
|
|
829
|
+
if (entry.code !== 2)
|
|
830
|
+
continue;
|
|
831
|
+
if (entry.unknown !== undefined) {
|
|
821
832
|
const key = `${entry.bin} ${entry.command ?? ""} ${entry.unknown}`;
|
|
822
|
-
const
|
|
823
|
-
|
|
824
|
-
|
|
833
|
+
const unknown = unknowns.get(key) ?? { bin: entry.bin, ...entry.command === undefined ? {} : { command: entry.command }, unknown: entry.unknown, count: 0 };
|
|
834
|
+
unknown.count++;
|
|
835
|
+
unknowns.set(key, unknown);
|
|
825
836
|
}
|
|
826
837
|
}
|
|
827
838
|
return {
|
|
828
839
|
runs: entries.length,
|
|
829
840
|
bins: [...bins].map(([bin, counts]) => ({ bin, ...counts })).toSorted((a, b) => b.runs - a.runs),
|
|
830
|
-
|
|
841
|
+
commands: [...commands.values()].toSorted((a, b) => b.runs - a.runs),
|
|
842
|
+
usageErrors: [...unknowns.values()].toSorted((a, b) => b.count - a.count)
|
|
831
843
|
};
|
|
832
844
|
}
|
|
833
845
|
|
|
@@ -931,7 +943,7 @@ function reportThrown(prefix, error, redact = (text) => text, json = false) {
|
|
|
931
943
|
const help = error instanceof UsageError ? `run ${prefix} --help for its options` : undefined;
|
|
932
944
|
const named = error instanceof Error && !(error instanceof Exit) && error.name !== "Error" ? `${error.name}: ${error.message}` : undefined;
|
|
933
945
|
const cause = error instanceof Error && error.cause instanceof Error ? redact(error.cause.message) : undefined;
|
|
934
|
-
if (error instanceof
|
|
946
|
+
if (error instanceof Exit && error.code === 2)
|
|
935
947
|
noteUsageError(error.message);
|
|
936
948
|
const report = json ? printJsonError : (message, hint, causeText) => printError(prefix, message, hint, causeText);
|
|
937
949
|
report(redact(named ?? (error instanceof Error ? error.message : String(error))), error instanceof Exit ? error.hint ?? help : undefined, cause);
|
|
@@ -1148,11 +1160,10 @@ import process10 from "process";
|
|
|
1148
1160
|
import { existsSync, readFileSync as readFileSync2 } from "fs";
|
|
1149
1161
|
import { basename, join as join3 } from "path";
|
|
1150
1162
|
var TOOLS = {
|
|
1151
|
-
"@kaynooo/cli-audit": ["audit"],
|
|
1152
1163
|
"@kaynooo/cli-base": ["base", "proof"],
|
|
1153
1164
|
"@kaynooo/cli-changelog": ["changelog"],
|
|
1154
1165
|
"@kaynooo/cli-check": ["check", "fix", "errs", "j"],
|
|
1155
|
-
"@kaynooo/cli-code": ["peek", "todo-scan", "hotspots"],
|
|
1166
|
+
"@kaynooo/cli-code": ["src", "peek", "todo-scan", "hotspots"],
|
|
1156
1167
|
"@kaynooo/cli-db": ["db"],
|
|
1157
1168
|
"@kaynooo/cli-docker": ["dc", "dlog", "dx", "sf"],
|
|
1158
1169
|
"@kaynooo/cli-doctor": ["doctor", "cli"],
|
|
@@ -1167,12 +1178,11 @@ var TOOLS = {
|
|
|
1167
1178
|
"@kaynooo/cli-pick": ["pick"],
|
|
1168
1179
|
"@kaynooo/cli-pkg": ["pkg"],
|
|
1169
1180
|
"@kaynooo/cli-probe": ["probe"],
|
|
1170
|
-
"@kaynooo/cli-proc": ["pid", "ports", "procs"],
|
|
1171
|
-
"@kaynooo/cli-repos": ["repos"],
|
|
1181
|
+
"@kaynooo/cli-proc": ["proc", "pid", "ports", "procs"],
|
|
1172
1182
|
"@kaynooo/cli-stack": ["stack"],
|
|
1173
|
-
"@kaynooo/cli-sweep": ["sweep"],
|
|
1174
1183
|
"@kaynooo/cli-view": ["view"],
|
|
1175
|
-
"@kaynooo/cli-wait": ["waitfor", "slot"]
|
|
1184
|
+
"@kaynooo/cli-wait": ["waitfor", "slot"],
|
|
1185
|
+
"@kaynooo/cli-workspace": ["ws", "repos", "sweep", "audit"]
|
|
1176
1186
|
};
|
|
1177
1187
|
var FORGE = "git.kaynooo.fr";
|
|
1178
1188
|
var FORGE_HOSTS = new Set([FORGE, "git.kevyn.fr"]);
|
|
@@ -1343,6 +1353,46 @@ function missingCompletions(host, dir) {
|
|
|
1343
1353
|
return installedBins(host).filter((bin) => !existsSync2(join4(dir, `${bin}.fish`)));
|
|
1344
1354
|
}
|
|
1345
1355
|
|
|
1356
|
+
// src/doctor.ts
|
|
1357
|
+
import process12 from "process";
|
|
1358
|
+
|
|
1359
|
+
// src/fix.ts
|
|
1360
|
+
var INSTALL = /^bun install -g (\S+)$/;
|
|
1361
|
+
var RUNNABLE = /^(?:cli completions fish --install|claude plugin (?:install \S+ --marketplace \S+|disable \S+))$/;
|
|
1362
|
+
function planFixes(results) {
|
|
1363
|
+
const steps = [];
|
|
1364
|
+
const installs = [];
|
|
1365
|
+
for (const { ok, fix } of results) {
|
|
1366
|
+
if (ok || fix === undefined)
|
|
1367
|
+
continue;
|
|
1368
|
+
const install = INSTALL.exec(fix);
|
|
1369
|
+
if (install !== null)
|
|
1370
|
+
installs.push(install[1] ?? "");
|
|
1371
|
+
else
|
|
1372
|
+
steps.push({ command: fix, argv: RUNNABLE.test(fix) ? fix.split(" ") : undefined });
|
|
1373
|
+
}
|
|
1374
|
+
if (installs.length === 0)
|
|
1375
|
+
return steps;
|
|
1376
|
+
const command = `bun install -g ${installs.join(" ")}`;
|
|
1377
|
+
return [{ command, argv: command.split(" ") }, ...steps];
|
|
1378
|
+
}
|
|
1379
|
+
async function applyFixes(host, steps, dry) {
|
|
1380
|
+
const outcomes = [];
|
|
1381
|
+
for (const { command, argv } of steps) {
|
|
1382
|
+
if (argv === undefined) {
|
|
1383
|
+
outcomes.push({ command, status: "not run", detail: "not a command doctor runs: do it by hand" });
|
|
1384
|
+
continue;
|
|
1385
|
+
}
|
|
1386
|
+
if (dry) {
|
|
1387
|
+
outcomes.push({ command, status: "dry-run" });
|
|
1388
|
+
continue;
|
|
1389
|
+
}
|
|
1390
|
+
const ran = await host.run(argv);
|
|
1391
|
+
outcomes.push(ran.code === 0 ? { command, status: "ran" } : { command, status: "failed", detail: `exit ${ran.code}: ${firstLine(ran.stderr || ran.stdout)}` });
|
|
1392
|
+
}
|
|
1393
|
+
return outcomes;
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1346
1396
|
// src/host.ts
|
|
1347
1397
|
import { homedir as homedir2 } from "os";
|
|
1348
1398
|
import { join as join5 } from "path";
|
|
@@ -1365,6 +1415,67 @@ function hostHere() {
|
|
|
1365
1415
|
};
|
|
1366
1416
|
}
|
|
1367
1417
|
|
|
1418
|
+
// src/doctor.ts
|
|
1419
|
+
var USAGE = `doctor [--only <check>] [--quiet] [--fix [--dry-run]] [--json]: checks this host's @kaynooo/cli-* install, one line per check, the command that fixes a failing one beside it
|
|
1420
|
+
|
|
1421
|
+
Each @kaynooo/cli-* package installed globally with its bins on the PATH and no older than the registry's latest; tea logged in to git.kaynooo.fr and reaching it; the Claude Code hooks wired through the cli-hooks plugin or the cli mod, not both; Bun at least the version the tools ask for; fish completions for every bin.
|
|
1422
|
+
--fix runs the printed fix: lines it may (global installs of the packages, the completions, the hooks plugin), the installs in one call, then checks again; a line that installs a system package or only describes a step is printed as not run. --dry-run shows what --fix would run.
|
|
1423
|
+
--quiet prints only the failing checks, nothing when all pass. --only limits the run to the checks named, e.g. --only tea --only bun.`;
|
|
1424
|
+
var EXITS = [[0, "every check passes (after --fix, the ones it fixed too)"], [1, "a check fails"], [2, "a usage error"]];
|
|
1425
|
+
var INSTALL2 = "bun install -g ";
|
|
1426
|
+
var OPTIONS = {
|
|
1427
|
+
json: jsonOption("[{check, ok, detail, fix?}], as the checks stand after --fix; the fixes it ran go to stderr"),
|
|
1428
|
+
only: { type: "string", multiple: true, choices: CHECK_NAMES, description: "run only this check (a package such as cli-forgejo, tea, hooks, bun or completions), repeatable" },
|
|
1429
|
+
quiet: { type: "boolean", short: "q", description: "print only the failing checks" },
|
|
1430
|
+
fix: { type: "boolean", description: "run the fix: lines doctor may run, then check again" },
|
|
1431
|
+
"dry-run": { type: "boolean", description: "with --fix, show what would run without running it (alone, it implies --fix)" }
|
|
1432
|
+
};
|
|
1433
|
+
function print(results) {
|
|
1434
|
+
if (results.length === 0)
|
|
1435
|
+
return;
|
|
1436
|
+
const { green, red, dim } = paint(process12.stdout);
|
|
1437
|
+
const width = Math.max(...results.map((result) => result.check.length));
|
|
1438
|
+
for (const { check, ok, detail, fix } of results)
|
|
1439
|
+
console.log(`${ok ? green("ok ") : red("FAIL")} ${check.padEnd(width)} ${detail}${fix === undefined ? "" : ` ${dim("fix:")} ${fix}`}`);
|
|
1440
|
+
const installs = results.flatMap((result) => result.fix?.startsWith(INSTALL2) === true ? [result.fix.slice(INSTALL2.length)] : []);
|
|
1441
|
+
if (installs.length > 1)
|
|
1442
|
+
console.log(`
|
|
1443
|
+
${dim("fix them all:")} ${INSTALL2}${installs.join(" ")}`);
|
|
1444
|
+
}
|
|
1445
|
+
function printOutcomes(outcomes, log) {
|
|
1446
|
+
const { green, red, dim } = paint(process12.stdout);
|
|
1447
|
+
const marks = { ran: green("ran "), failed: red("failed "), "dry-run": dim("would "), "not run": dim("not run") };
|
|
1448
|
+
for (const { command, status, detail } of outcomes)
|
|
1449
|
+
log(`${marks[status]} ${command}${detail === undefined ? "" : ` ${dim(detail)}`}`);
|
|
1450
|
+
}
|
|
1451
|
+
async function main(args, host = hostHere()) {
|
|
1452
|
+
const { values } = parse(args, USAGE, OPTIONS, { positionals: [], exits: EXITS, replaces: "checking each bin's version, tea's login and the hooks by hand", examples: ["doctor", ["doctor --json", "the checks for a script"], ["doctor -q", "silent unless something fails"], ["doctor --fix --dry-run", "what --fix would run"]] });
|
|
1453
|
+
const only = values.only ?? [];
|
|
1454
|
+
const dry = values["dry-run"] === true;
|
|
1455
|
+
const json = values.json === true;
|
|
1456
|
+
let results = await runChecks(host, only);
|
|
1457
|
+
let fixed = false;
|
|
1458
|
+
if ((values.fix === true || dry) && results.some((result) => !result.ok)) {
|
|
1459
|
+
if (!json)
|
|
1460
|
+
print(values.quiet === true ? results.filter((result) => !result.ok) : results);
|
|
1461
|
+
const steps = planFixes(results);
|
|
1462
|
+
printOutcomes(await applyFixes(host, steps, dry), json ? console.error : console.log);
|
|
1463
|
+
if (dry)
|
|
1464
|
+
return 1;
|
|
1465
|
+
results = await runChecks(host, only);
|
|
1466
|
+
fixed = true;
|
|
1467
|
+
}
|
|
1468
|
+
const shown = values.quiet === true ? results.filter((result) => !result.ok) : results;
|
|
1469
|
+
if (fixed && !json && shown.length > 0)
|
|
1470
|
+
console.log(`
|
|
1471
|
+
after the fixes:`);
|
|
1472
|
+
if (json)
|
|
1473
|
+
printJson(shown);
|
|
1474
|
+
else
|
|
1475
|
+
print(shown);
|
|
1476
|
+
return results.every((result) => result.ok) ? 0 : 1;
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1368
1479
|
// src/list.ts
|
|
1369
1480
|
var USAGE_WORD = /^Usage:\s*/;
|
|
1370
1481
|
async function describeBin(host, bin, name) {
|
|
@@ -1389,8 +1500,8 @@ Exits 0 when it printed the list, 2 on a usage error.`;
|
|
|
1389
1500
|
var LIST_OPTIONS = {
|
|
1390
1501
|
json: jsonOption("[{bin, package, installed, synopsis?, summary?, replaces?, examples?}]")
|
|
1391
1502
|
};
|
|
1392
|
-
function
|
|
1393
|
-
const { cyan, dim } = paint(
|
|
1503
|
+
function print2(listed) {
|
|
1504
|
+
const { cyan, dim } = paint(process13.stdout);
|
|
1394
1505
|
const width = Math.max(...listed.map((each) => each.bin.length));
|
|
1395
1506
|
for (const each of listed) {
|
|
1396
1507
|
const what = !each.installed ? dim(`not installed: bun install -g ${each.package}`) : `${each.summary ?? ""}${each.replaces === undefined ? "" : dim(`; instead of ${each.replaces}`)}`;
|
|
@@ -1403,7 +1514,7 @@ async function list(args, host = hostHere()) {
|
|
|
1403
1514
|
if (values.json === true)
|
|
1404
1515
|
printJson(listed);
|
|
1405
1516
|
else
|
|
1406
|
-
|
|
1517
|
+
print2(listed);
|
|
1407
1518
|
return 0;
|
|
1408
1519
|
}
|
|
1409
1520
|
var COMPLETIONS_USAGE = `cli completions <fish|bash> [--install] [--dir <folder>]: shell completions for every installed bin of the @kaynooo/cli-* packages
|
|
@@ -1440,11 +1551,11 @@ async function completions(args, host = hostHere()) {
|
|
|
1440
1551
|
}
|
|
1441
1552
|
var STATS_USAGE = `cli stats [--since 7d] [--json]: how often each bin ran, and the usage errors by the option it did not know
|
|
1442
1553
|
|
|
1443
|
-
Every bin records each run in ~/.local/state/kaynooo-cli/usage.jsonl (or $XDG_STATE_HOME): the bin, its command, the names of the flags it saw (never a value or an argument), the exit code and, on a usage error, the unknown option. Set KAYNOOO_CLI_NO_USAGE=1 to stop recording; \`guard\` never records.
|
|
1554
|
+
Every bin records each run in ~/.local/state/kaynooo-cli/usage.jsonl (or $XDG_STATE_HOME): the bin, its command, the names of the flags it saw (never a value or an argument), the exit code and and, on a usage error, the unknown option (never the message, which carries the values it refused). failed counts exits other than 0 and 2, usage errors are exit 2 (misuse, or a check that could not be made). Set KAYNOOO_CLI_NO_USAGE=1 to stop recording; \`guard\` never records.
|
|
1444
1555
|
Exits 0 when it printed the summary, 2 on a usage error.`;
|
|
1445
1556
|
var STATS_OPTIONS = {
|
|
1446
1557
|
since: { type: "string", description: "only the runs this recent (7d, 12h)" },
|
|
1447
|
-
json: jsonOption("{runs, bins: [{bin, runs, failed, usageErrors}], usageErrors: [{bin, command?, unknown, count}]}")
|
|
1558
|
+
json: jsonOption("{runs, bins: [{bin, runs, failed, usageErrors}], commands: [{bin, command, runs, failed, usageErrors}], usageErrors: [{bin, command?, unknown, count}]}")
|
|
1448
1559
|
};
|
|
1449
1560
|
async function stats(args, path = usagePath()) {
|
|
1450
1561
|
const { values } = parse(args, STATS_USAGE, STATS_OPTIONS, { positionals: [], examples: ["cli stats", ["cli stats --since 7d", "the last week"]] });
|
|
@@ -1461,6 +1572,11 @@ async function stats(args, path = usagePath()) {
|
|
|
1461
1572
|
console.log(`${summary.runs} runs
|
|
1462
1573
|
|
|
1463
1574
|
${table(summary.bins.map((each) => [each.bin, String(each.runs), `${each.failed} failed`, `${each.usageErrors} usage errors`]))}`);
|
|
1575
|
+
const busy = summary.commands.filter((each) => each.failed + each.usageErrors > 0).toSorted((a, b) => b.failed + b.usageErrors - (a.failed + a.usageErrors)).slice(0, 15);
|
|
1576
|
+
if (busy.length > 0)
|
|
1577
|
+
console.log(`
|
|
1578
|
+
commands with failures or usage errors
|
|
1579
|
+
${table(busy.map((each) => [`${each.bin} ${each.command}`, String(each.runs), `${each.failed} failed`, `${each.usageErrors} usage errors`]))}`);
|
|
1464
1580
|
if (summary.usageErrors.length > 0)
|
|
1465
1581
|
console.log(`
|
|
1466
1582
|
unknown options
|
|
@@ -1468,12 +1584,13 @@ ${table(summary.usageErrors.map((each) => [`${each.bin}${each.command === undefi
|
|
|
1468
1584
|
return 0;
|
|
1469
1585
|
}
|
|
1470
1586
|
if (import.meta.main) {
|
|
1471
|
-
|
|
1587
|
+
process13.exitCode = await runCli({
|
|
1472
1588
|
name: "cli",
|
|
1473
1589
|
about: "the bins of the @kaynooo/cli-* packages, described by their own help",
|
|
1474
1590
|
replaces: "running each bin's --help to find the one that fits",
|
|
1475
1591
|
examples: ["cli list", ["cli list --json", "with each bin's synopsis and examples"]],
|
|
1476
1592
|
commands: {
|
|
1593
|
+
doctor: { summary: "checks this host's install of the bins, one line per check, the command that fixes a failing one beside it", run: async (args) => main(args) },
|
|
1477
1594
|
list: { summary: "every bin in one line: what it does and what it is reached for instead of", run: async (args) => list(args) },
|
|
1478
1595
|
stats: { summary: "how often each bin ran and the options usage errors did not know", run: async (args) => stats(args) },
|
|
1479
1596
|
completions: { summary: "fish completions for every installed bin, printed or written to the folder fish reads", run: async (args) => completions(args) }
|
package/dist/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// @bun
|
|
3
3
|
|
|
4
4
|
// src/cli.ts
|
|
5
|
-
import
|
|
5
|
+
import process13 from "process";
|
|
6
6
|
// ../kit/src/args.ts
|
|
7
7
|
import process5 from "process";
|
|
8
8
|
import { parseArgs } from "util";
|
|
@@ -808,26 +808,38 @@ async function readUsage(path = usagePath(), since = 0) {
|
|
|
808
808
|
}
|
|
809
809
|
});
|
|
810
810
|
}
|
|
811
|
+
function tally(counts, code) {
|
|
812
|
+
counts.runs++;
|
|
813
|
+
counts.failed += code === 0 || code === 2 ? 0 : 1;
|
|
814
|
+
counts.usageErrors += code === 2 ? 1 : 0;
|
|
815
|
+
}
|
|
811
816
|
function summarizeUsage(entries) {
|
|
812
817
|
const bins = new Map;
|
|
813
|
-
const
|
|
818
|
+
const commands = new Map;
|
|
819
|
+
const unknowns = new Map;
|
|
814
820
|
for (const entry of entries) {
|
|
815
821
|
const bin = bins.get(entry.bin) ?? { runs: 0, failed: 0, usageErrors: 0 };
|
|
816
|
-
bin.
|
|
817
|
-
bin.failed += entry.code === 0 ? 0 : 1;
|
|
818
|
-
bin.usageErrors += entry.code === 2 ? 1 : 0;
|
|
822
|
+
tally(bin, entry.code);
|
|
819
823
|
bins.set(entry.bin, bin);
|
|
820
|
-
if (entry.
|
|
824
|
+
if (entry.command !== undefined) {
|
|
825
|
+
const command = commands.get(`${entry.bin} ${entry.command}`) ?? { bin: entry.bin, command: entry.command, runs: 0, failed: 0, usageErrors: 0 };
|
|
826
|
+
tally(command, entry.code);
|
|
827
|
+
commands.set(`${entry.bin} ${entry.command}`, command);
|
|
828
|
+
}
|
|
829
|
+
if (entry.code !== 2)
|
|
830
|
+
continue;
|
|
831
|
+
if (entry.unknown !== undefined) {
|
|
821
832
|
const key = `${entry.bin} ${entry.command ?? ""} ${entry.unknown}`;
|
|
822
|
-
const
|
|
823
|
-
|
|
824
|
-
|
|
833
|
+
const unknown = unknowns.get(key) ?? { bin: entry.bin, ...entry.command === undefined ? {} : { command: entry.command }, unknown: entry.unknown, count: 0 };
|
|
834
|
+
unknown.count++;
|
|
835
|
+
unknowns.set(key, unknown);
|
|
825
836
|
}
|
|
826
837
|
}
|
|
827
838
|
return {
|
|
828
839
|
runs: entries.length,
|
|
829
840
|
bins: [...bins].map(([bin, counts]) => ({ bin, ...counts })).toSorted((a, b) => b.runs - a.runs),
|
|
830
|
-
|
|
841
|
+
commands: [...commands.values()].toSorted((a, b) => b.runs - a.runs),
|
|
842
|
+
usageErrors: [...unknowns.values()].toSorted((a, b) => b.count - a.count)
|
|
831
843
|
};
|
|
832
844
|
}
|
|
833
845
|
|
|
@@ -931,7 +943,7 @@ function reportThrown(prefix, error, redact = (text) => text, json = false) {
|
|
|
931
943
|
const help = error instanceof UsageError ? `run ${prefix} --help for its options` : undefined;
|
|
932
944
|
const named = error instanceof Error && !(error instanceof Exit) && error.name !== "Error" ? `${error.name}: ${error.message}` : undefined;
|
|
933
945
|
const cause = error instanceof Error && error.cause instanceof Error ? redact(error.cause.message) : undefined;
|
|
934
|
-
if (error instanceof
|
|
946
|
+
if (error instanceof Exit && error.code === 2)
|
|
935
947
|
noteUsageError(error.message);
|
|
936
948
|
const report = json ? printJsonError : (message, hint, causeText) => printError(prefix, message, hint, causeText);
|
|
937
949
|
report(redact(named ?? (error instanceof Error ? error.message : String(error))), error instanceof Exit ? error.hint ?? help : undefined, cause);
|
|
@@ -1139,6 +1151,9 @@ var SECRET_WORDS = new Set(["key", "pass", "pwd", "auth", "salt", "cookie", "ses
|
|
|
1139
1151
|
var {Glob } = globalThis.Bun;
|
|
1140
1152
|
var SESSION_FILES = new Glob("*.json");
|
|
1141
1153
|
var ACCOUNT_DIRS = new Glob("*/sessions");
|
|
1154
|
+
// src/doctor.ts
|
|
1155
|
+
import process12 from "process";
|
|
1156
|
+
|
|
1142
1157
|
// src/checks.ts
|
|
1143
1158
|
import { existsSync as existsSync2, readFileSync as readFileSync3 } from "fs";
|
|
1144
1159
|
import { basename, join as join4 } from "path";
|
|
@@ -1207,11 +1222,10 @@ function missingCompletions(host, dir) {
|
|
|
1207
1222
|
|
|
1208
1223
|
// src/checks.ts
|
|
1209
1224
|
var TOOLS = {
|
|
1210
|
-
"@kaynooo/cli-audit": ["audit"],
|
|
1211
1225
|
"@kaynooo/cli-base": ["base", "proof"],
|
|
1212
1226
|
"@kaynooo/cli-changelog": ["changelog"],
|
|
1213
1227
|
"@kaynooo/cli-check": ["check", "fix", "errs", "j"],
|
|
1214
|
-
"@kaynooo/cli-code": ["peek", "todo-scan", "hotspots"],
|
|
1228
|
+
"@kaynooo/cli-code": ["src", "peek", "todo-scan", "hotspots"],
|
|
1215
1229
|
"@kaynooo/cli-db": ["db"],
|
|
1216
1230
|
"@kaynooo/cli-docker": ["dc", "dlog", "dx", "sf"],
|
|
1217
1231
|
"@kaynooo/cli-doctor": ["doctor", "cli"],
|
|
@@ -1226,12 +1240,11 @@ var TOOLS = {
|
|
|
1226
1240
|
"@kaynooo/cli-pick": ["pick"],
|
|
1227
1241
|
"@kaynooo/cli-pkg": ["pkg"],
|
|
1228
1242
|
"@kaynooo/cli-probe": ["probe"],
|
|
1229
|
-
"@kaynooo/cli-proc": ["pid", "ports", "procs"],
|
|
1230
|
-
"@kaynooo/cli-repos": ["repos"],
|
|
1243
|
+
"@kaynooo/cli-proc": ["proc", "pid", "ports", "procs"],
|
|
1231
1244
|
"@kaynooo/cli-stack": ["stack"],
|
|
1232
|
-
"@kaynooo/cli-sweep": ["sweep"],
|
|
1233
1245
|
"@kaynooo/cli-view": ["view"],
|
|
1234
|
-
"@kaynooo/cli-wait": ["waitfor", "slot"]
|
|
1246
|
+
"@kaynooo/cli-wait": ["waitfor", "slot"],
|
|
1247
|
+
"@kaynooo/cli-workspace": ["ws", "repos", "sweep", "audit"]
|
|
1235
1248
|
};
|
|
1236
1249
|
var FORGE = "git.kaynooo.fr";
|
|
1237
1250
|
var FORGE_HOSTS = new Set([FORGE, "git.kevyn.fr"]);
|
|
@@ -1402,7 +1415,7 @@ function hostHere() {
|
|
|
1402
1415
|
};
|
|
1403
1416
|
}
|
|
1404
1417
|
|
|
1405
|
-
// src/
|
|
1418
|
+
// src/doctor.ts
|
|
1406
1419
|
var USAGE = `doctor [--only <check>] [--quiet] [--fix [--dry-run]] [--json]: checks this host's @kaynooo/cli-* install, one line per check, the command that fixes a failing one beside it
|
|
1407
1420
|
|
|
1408
1421
|
Each @kaynooo/cli-* package installed globally with its bins on the PATH and no older than the registry's latest; tea logged in to git.kaynooo.fr and reaching it; the Claude Code hooks wired through the cli-hooks plugin or the cli mod, not both; Bun at least the version the tools ask for; fish completions for every bin.
|
|
@@ -1462,8 +1475,6 @@ after the fixes:`);
|
|
|
1462
1475
|
print(shown);
|
|
1463
1476
|
return results.every((result) => result.ok) ? 0 : 1;
|
|
1464
1477
|
}
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
main
|
|
1469
|
-
};
|
|
1478
|
+
|
|
1479
|
+
// src/cli.ts
|
|
1480
|
+
process13.exitCode = await runMain({ name: "doctor", run: async (args) => main(args) });
|
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.7.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",
|
|
@@ -33,9 +33,10 @@
|
|
|
33
33
|
},
|
|
34
34
|
"scripts": {
|
|
35
35
|
"build": "$npm_execpath build src/cli.ts src/cli-bin.ts --target bun --outdir dist",
|
|
36
|
+
"cli": "$npm_execpath src/cli-bin.ts",
|
|
36
37
|
"doctor": "$npm_execpath src/cli.ts"
|
|
37
38
|
},
|
|
38
39
|
"devDependencies": {
|
|
39
|
-
"@kaynooo/cli-kit": "0.21.
|
|
40
|
+
"@kaynooo/cli-kit": "0.21.1"
|
|
40
41
|
}
|
|
41
42
|
}
|