@kaynooo/cli-doctor 0.5.1 → 0.6.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 +33 -16
- package/dist/cli.js +26 -14
- package/package.json +2 -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
|
@@ -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);
|
|
@@ -1211,16 +1223,16 @@ async function checkTool(host, name, bins) {
|
|
|
1211
1223
|
if (version === undefined)
|
|
1212
1224
|
return { check, ok: false, detail: "not installed", fix: `bun install -g ${name}` };
|
|
1213
1225
|
const off = bins.filter((bin) => host.which(bin) === null);
|
|
1214
|
-
if (off.length > 0)
|
|
1215
|
-
return { check, ok: false, detail: `${version}, ${off.join(", ")} not on the PATH`, fix: `add ${join3(host.globalModules, "..", "..", "..", "bin")} to the PATH` };
|
|
1216
1226
|
const info = await host.run(["bun", "info", name, "version", "--no-cache"]);
|
|
1217
1227
|
const answered = info.code === 0 ? firstLine(info.stdout.trim().split(`
|
|
1218
1228
|
`).at(-1) ?? "") : "";
|
|
1219
1229
|
const latest = VERSION.test(answered) ? answered : "";
|
|
1230
|
+
if (latest !== "" && Bun.semver.order(version, latest) < 0)
|
|
1231
|
+
return { check, ok: false, detail: `${version}, ${latest} released${off.length > 0 ? `, ${off.join(", ")} not on the PATH` : ""}`, fix: `bun install -g ${name}@${latest}` };
|
|
1232
|
+
if (off.length > 0)
|
|
1233
|
+
return { check, ok: false, detail: `${version}, ${off.join(", ")} not on the PATH`, fix: `add ${join3(host.globalModules, "..", "..", "..", "bin")} to the PATH` };
|
|
1220
1234
|
if (latest === "")
|
|
1221
1235
|
return { check, ok: true, detail: `${version}, the registry did not answer: ${firstLine(info.stderr) || answered || "no version"}` };
|
|
1222
|
-
if (Bun.semver.order(version, latest) < 0)
|
|
1223
|
-
return { check, ok: false, detail: `${version}, ${latest} released`, fix: `bun install -g ${name}@${latest}` };
|
|
1224
1236
|
return { check, ok: true, detail: version };
|
|
1225
1237
|
}
|
|
1226
1238
|
async function checkTea(host) {
|
|
@@ -1440,11 +1452,11 @@ async function completions(args, host = hostHere()) {
|
|
|
1440
1452
|
}
|
|
1441
1453
|
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
1454
|
|
|
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.
|
|
1455
|
+
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
1456
|
Exits 0 when it printed the summary, 2 on a usage error.`;
|
|
1445
1457
|
var STATS_OPTIONS = {
|
|
1446
1458
|
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}]}")
|
|
1459
|
+
json: jsonOption("{runs, bins: [{bin, runs, failed, usageErrors}], commands: [{bin, command, runs, failed, usageErrors}], usageErrors: [{bin, command?, unknown, count}]}")
|
|
1448
1460
|
};
|
|
1449
1461
|
async function stats(args, path = usagePath()) {
|
|
1450
1462
|
const { values } = parse(args, STATS_USAGE, STATS_OPTIONS, { positionals: [], examples: ["cli stats", ["cli stats --since 7d", "the last week"]] });
|
|
@@ -1461,6 +1473,11 @@ async function stats(args, path = usagePath()) {
|
|
|
1461
1473
|
console.log(`${summary.runs} runs
|
|
1462
1474
|
|
|
1463
1475
|
${table(summary.bins.map((each) => [each.bin, String(each.runs), `${each.failed} failed`, `${each.usageErrors} usage errors`]))}`);
|
|
1476
|
+
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);
|
|
1477
|
+
if (busy.length > 0)
|
|
1478
|
+
console.log(`
|
|
1479
|
+
commands with failures or usage errors
|
|
1480
|
+
${table(busy.map((each) => [`${each.bin} ${each.command}`, String(each.runs), `${each.failed} failed`, `${each.usageErrors} usage errors`]))}`);
|
|
1464
1481
|
if (summary.usageErrors.length > 0)
|
|
1465
1482
|
console.log(`
|
|
1466
1483
|
unknown options
|
package/dist/cli.js
CHANGED
|
@@ -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);
|
|
@@ -1270,16 +1282,16 @@ async function checkTool(host, name, bins) {
|
|
|
1270
1282
|
if (version === undefined)
|
|
1271
1283
|
return { check, ok: false, detail: "not installed", fix: `bun install -g ${name}` };
|
|
1272
1284
|
const off = bins.filter((bin) => host.which(bin) === null);
|
|
1273
|
-
if (off.length > 0)
|
|
1274
|
-
return { check, ok: false, detail: `${version}, ${off.join(", ")} not on the PATH`, fix: `add ${join4(host.globalModules, "..", "..", "..", "bin")} to the PATH` };
|
|
1275
1285
|
const info = await host.run(["bun", "info", name, "version", "--no-cache"]);
|
|
1276
1286
|
const answered = info.code === 0 ? firstLine(info.stdout.trim().split(`
|
|
1277
1287
|
`).at(-1) ?? "") : "";
|
|
1278
1288
|
const latest = VERSION.test(answered) ? answered : "";
|
|
1289
|
+
if (latest !== "" && Bun.semver.order(version, latest) < 0)
|
|
1290
|
+
return { check, ok: false, detail: `${version}, ${latest} released${off.length > 0 ? `, ${off.join(", ")} not on the PATH` : ""}`, fix: `bun install -g ${name}@${latest}` };
|
|
1291
|
+
if (off.length > 0)
|
|
1292
|
+
return { check, ok: false, detail: `${version}, ${off.join(", ")} not on the PATH`, fix: `add ${join4(host.globalModules, "..", "..", "..", "bin")} to the PATH` };
|
|
1279
1293
|
if (latest === "")
|
|
1280
1294
|
return { check, ok: true, detail: `${version}, the registry did not answer: ${firstLine(info.stderr) || answered || "no version"}` };
|
|
1281
|
-
if (Bun.semver.order(version, latest) < 0)
|
|
1282
|
-
return { check, ok: false, detail: `${version}, ${latest} released`, fix: `bun install -g ${name}@${latest}` };
|
|
1283
1295
|
return { check, ok: true, detail: version };
|
|
1284
1296
|
}
|
|
1285
1297
|
async function checkTea(host) {
|
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.6.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.21.
|
|
39
|
+
"@kaynooo/cli-kit": "0.21.1"
|
|
40
40
|
}
|
|
41
41
|
}
|