@kaynooo/cli-doctor 0.3.0 → 0.5.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.
Files changed (4) hide show
  1. package/README.md +27 -1
  2. package/dist/cli-bin.js +398 -114
  3. package/dist/cli.js +431 -118
  4. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -2,14 +2,16 @@
2
2
  // @bun
3
3
 
4
4
  // src/cli.ts
5
- import process8 from "process";
6
-
5
+ import process12 from "process";
7
6
  // ../kit/src/args.ts
8
- import process2 from "process";
7
+ import process5 from "process";
9
8
  import { parseArgs } from "util";
10
9
 
10
+ // ../kit/src/format.ts
11
+ import process3 from "process";
12
+
11
13
  // ../kit/src/style.ts
12
- import process from "process";
14
+ import process2 from "process";
13
15
 
14
16
  // ../../node_modules/.bun/@kaynooo+utils@2.31.2+d0769e28dc4dad16/node_modules/@kaynooo/utils/dist/chunk-mtetydvc.js
15
17
  function x(s) {
@@ -273,10 +275,10 @@ function le(i = {}) {
273
275
  var t = le();
274
276
 
275
277
  // ../kit/src/style.ts
276
- function takesControlCodes(stream, env = process.env) {
278
+ function takesControlCodes(stream, env = process2.env) {
277
279
  return stream.isTTY === true && env.NO_COLOR === undefined && env.NODE_DISABLE_COLORS === undefined && env.TERM !== "dumb";
278
280
  }
279
- function paint(stream, env = process.env) {
281
+ function paint(stream, env = process2.env) {
280
282
  return le({ stream: { isTTY: takesControlCodes(stream, env) }, env });
281
283
  }
282
284
  var STATUSES = {
@@ -293,11 +295,110 @@ function upTo(text, separator) {
293
295
  const end = text.indexOf(separator);
294
296
  return end === -1 ? text : text.slice(0, end);
295
297
  }
298
+ function truncate(text, width) {
299
+ return text.length <= width ? text : `${text.slice(0, Math.max(0, width - 1))}\u2026`;
300
+ }
301
+ var LINE_BREAKS = /\s*\n\s*/g;
296
302
  var ESCAPE = String.fromCharCode(27);
303
+ function textWidth(text) {
304
+ return Bun.stringWidth(text);
305
+ }
306
+ function table(rows, { cellWidth, header = false, align = [], fit } = {}) {
307
+ const cells = cellWidth === undefined ? rows : rows.map((row) => row.map((cell) => truncate(cell.replaceAll(LINE_BREAKS, " "), cellWidth)));
308
+ const widths = [];
309
+ for (const row of cells) {
310
+ for (const [index, cell] of row.entries())
311
+ widths[index] = Math.max(widths[index] ?? 0, textWidth(cell));
312
+ }
313
+ const shown = fit === undefined ? cells : narrow(cells, widths, fit);
314
+ const colors = paint(process3.stdout);
315
+ const lines = shown.map((row) => row.map((cell, index) => {
316
+ const width = widths[index] ?? 0;
317
+ const gap = " ".repeat(Math.max(0, width - textWidth(cell)));
318
+ if (align[index] === "right")
319
+ return `${gap}${cell}`;
320
+ return index === row.length - 1 ? cell : `${cell}${gap}`;
321
+ }).join(" ").trimEnd());
322
+ return lines.map((line, index) => header && index === 0 ? colors.bold(line) : line).join(`
323
+ `);
324
+ }
325
+ function narrow(rows, widths, limit) {
326
+ const used = () => widths.reduce((sum, width) => sum + width, 0) + 2 * Math.max(0, widths.length - 1);
327
+ const cuttable = (index) => rows.every((row) => !(row[index] ?? "").includes(ESCAPE));
328
+ const floor = 8;
329
+ while (used() > limit) {
330
+ let widest = -1;
331
+ for (const [index, width] of widths.entries()) {
332
+ if (cuttable(index) && width > floor && width > (widths[widest] ?? 0))
333
+ widest = index;
334
+ }
335
+ if (widest === -1)
336
+ break;
337
+ widths[widest] = Math.max(floor, (widths[widest] ?? 0) - (used() - limit));
338
+ }
339
+ return rows.map((row) => row.map((cell, index) => textWidth(cell) > (widths[index] ?? 0) ? truncate(cell, widths[index] ?? 0) : cell));
340
+ }
297
341
  function printJson(value) {
298
342
  console.log(JSON.stringify(value, undefined, 2));
299
343
  }
300
344
 
345
+ // ../kit/src/help.ts
346
+ import process4 from "process";
347
+ var OPTIONAL = /\[[^\]]+\]/g;
348
+ var USAGE_PREFIX = /^usage:\s*/i;
349
+ var FULL_STOP = /\.$/;
350
+ function usageLine(usage, colors = paint(process4.stdout)) {
351
+ return `${colors.bold("Usage:")} ${usage.replace(USAGE_PREFIX, "").replaceAll(OPTIONAL, (part) => colors.dim(part))}`;
352
+ }
353
+ function helpText(usage, options, colors = paint(process4.stdout), { examples = [], replaces, exits = [] } = {}) {
354
+ return [usageLine(usage, colors), ...replacesRows(replaces, colors), "", colors.bold("Options:"), ...optionRows(options, colors), ...exitRows(exits, colors), ...exampleRows(examples, colors)].join(`
355
+ `);
356
+ }
357
+ function exitRows(exits, colors = paint(process4.stdout)) {
358
+ if (exits.length === 0)
359
+ return [];
360
+ const width = Math.max(...exits.map(([code]) => String(code).length));
361
+ return ["", colors.bold("Exits:"), ...exits.map(([code, meaning]) => ` ${colors.cyan(String(code).padEnd(width))} ${meaning}`)];
362
+ }
363
+ function replacesRows(replaces, colors = paint(process4.stdout)) {
364
+ return replaces === undefined ? [] : ["", `${colors.bold("Instead of:")} ${replaces}`];
365
+ }
366
+ function wantsHelpJson(env = process4.env) {
367
+ return env.KIT_HELP === "json";
368
+ }
369
+ function description(usage, { examples = [], replaces, exits } = {}, options) {
370
+ const lines = usage.replace(USAGE_PREFIX, "").split(`
371
+ `);
372
+ const first = lines[0];
373
+ const colon = first.indexOf(": ");
374
+ const prose = lines[lines.indexOf("") + 1] ?? "";
375
+ const fallback = lines.includes("") ? `${prose.charAt(0).toLowerCase()}${prose.slice(1)}`.replace(FULL_STOP, "") : "";
376
+ return {
377
+ synopsis: colon === -1 ? first : first.slice(0, colon),
378
+ summary: colon === -1 ? fallback : first.slice(colon + 2),
379
+ ...replaces === undefined ? {} : { replaces },
380
+ examples: examples.map((example) => typeof example === "string" ? { line: example } : { line: example[0], note: example[1] }),
381
+ ...exits === undefined ? {} : { exits: exits.map(([code, meaning]) => ({ code, meaning })) },
382
+ ...options === undefined ? {} : { options: Object.entries(options).map(([name, { short, description }]) => ({ name, ...short === undefined ? {} : { short }, ...description === undefined ? {} : { description } })) }
383
+ };
384
+ }
385
+ function exampleRows(examples, colors = paint(process4.stdout)) {
386
+ if (examples.length === 0)
387
+ return [];
388
+ return ["", colors.bold("Examples:"), ...examples.map((example) => typeof example === "string" ? ` ${example}` : ` ${example[0]} ${colors.dim(`# ${example[1]}`)}`)];
389
+ }
390
+ function optionRows(options, colors = paint(process4.stdout)) {
391
+ const rows = Object.entries(options).map(([name, option]) => {
392
+ const short = option.short === undefined ? " " : `${colors.cyan(`-${option.short}`)}, `;
393
+ const named = option.choices === undefined ? "<value>" : `<${option.choices.join("|")}>`;
394
+ const placeholder = option.optional === undefined ? named : `[${named}]`;
395
+ const value = option.type === "string" ? ` ${colors.dim(placeholder)}` : "";
396
+ return { flag: `${short}${colors.cyan(`--${name}`)}${value}`, width: `-x, --${name}`.length + (option.type === "string" ? placeholder.length + 1 : 0), description: option.description ?? "" };
397
+ });
398
+ const width = Math.max(0, ...rows.map((row) => row.width));
399
+ return rows.map((row) => row.description === "" ? ` ${row.flag}` : ` ${row.flag}${" ".repeat(width - row.width)} ${colors.dim(row.description)}`);
400
+ }
401
+
301
402
  // ../kit/src/text.ts
302
403
  var ANSI = new RegExp(`${String.fromCharCode(27)}\\[[\\d;?]*[a-z]`, "gi");
303
404
  function distance(a, b) {
@@ -317,8 +418,21 @@ function closest(name, names) {
317
418
  const [best] = names.map((known) => ({ known, far: distance(name, known) })).sort((a, b) => a.far - b.far);
318
419
  return best !== undefined && best.far <= (name.length < 4 ? 1 : 2) ? best.known : undefined;
319
420
  }
421
+ // ../kit/src/input.ts
422
+ var DURATION = /^(\d+(?:\.\d+)?)(ms|[smhd])?$/;
423
+ var UNIT_MS = { ms: 1, s: 1000, m: 60000, h: 3600000, d: 86400000 };
424
+ function duration(value, name, fallback, unit = "s") {
425
+ if (value === undefined)
426
+ return fallback;
427
+ const match = DURATION.exec(value.trim());
428
+ const ms = match === null ? 0 : Number(match[1]) * UNIT_MS[match[2] ?? unit];
429
+ if (ms < 1)
430
+ badUsage(`--${name} takes a duration such as 30s, 5m, 1h or 7d, not ${value}`);
431
+ return ms;
432
+ }
320
433
 
321
434
  // ../kit/src/args.ts
435
+ var DASHES = /^-+/;
322
436
  function jsonOption(holds) {
323
437
  return { type: "boolean", description: holds === undefined ? "print JSON" : `print JSON: ${holds}` };
324
438
  }
@@ -358,58 +472,11 @@ class Completing {
358
472
  }
359
473
  }
360
474
  var completion = { active: false };
361
- var OPTIONAL = /\[[^\]]+\]/g;
362
- var USAGE_PREFIX = /^usage:\s*/i;
363
- var FULL_STOP = /\.$/;
364
- var DASHES = /^-+/;
365
- function usageLine(usage, colors = paint(process2.stdout)) {
366
- return `${colors.bold("Usage:")} ${usage.replace(USAGE_PREFIX, "").replaceAll(OPTIONAL, (part) => colors.dim(part))}`;
367
- }
368
- function helpText(usage, options, colors = paint(process2.stdout), { examples = [], replaces } = {}) {
369
- return [usageLine(usage, colors), ...replacesRows(replaces, colors), "", colors.bold("Options:"), ...optionRows(options, colors), ...exampleRows(examples, colors)].join(`
370
- `);
371
- }
372
- function replacesRows(replaces, colors = paint(process2.stdout)) {
373
- return replaces === undefined ? [] : ["", `${colors.bold("Instead of:")} ${replaces}`];
374
- }
375
- function wantsHelpJson(env = process2.env) {
376
- return env.KIT_HELP === "json";
377
- }
378
- function description(usage, { examples = [], replaces } = {}) {
379
- const lines = usage.replace(USAGE_PREFIX, "").split(`
380
- `);
381
- const first = lines[0];
382
- const colon = first.indexOf(": ");
383
- const prose = lines[lines.indexOf("") + 1] ?? "";
384
- const fallback = lines.includes("") ? `${prose.charAt(0).toLowerCase()}${prose.slice(1)}`.replace(FULL_STOP, "") : "";
385
- return {
386
- synopsis: colon === -1 ? first : first.slice(0, colon),
387
- summary: colon === -1 ? fallback : first.slice(colon + 2),
388
- ...replaces === undefined ? {} : { replaces },
389
- examples: examples.map((example) => typeof example === "string" ? { line: example } : { line: example[0], note: example[1] })
390
- };
391
- }
392
- function exampleRows(examples, colors = paint(process2.stdout)) {
393
- if (examples.length === 0)
394
- return [];
395
- return ["", colors.bold("Examples:"), ...examples.map((example) => typeof example === "string" ? ` ${example}` : ` ${example[0]} ${colors.dim(`# ${example[1]}`)}`)];
396
- }
397
- function optionRows(options, colors = paint(process2.stdout)) {
398
- const rows = Object.entries(options).map(([name, option]) => {
399
- const short = option.short === undefined ? " " : `${colors.cyan(`-${option.short}`)}, `;
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 ?? "" };
404
- });
405
- const width = Math.max(0, ...rows.map((row) => row.width));
406
- return rows.map((row) => row.description === "" ? ` ${row.flag}` : ` ${row.flag}${" ".repeat(width - row.width)} ${colors.dim(row.description)}`);
407
- }
408
475
  var OUTPUT_OPTIONS = {
409
476
  color: { type: "string", description: "auto (on a terminal alone), always or never" },
410
477
  plain: { type: "boolean", description: "plain text, no colour or spinner: --color never" }
411
478
  };
412
- function applyOutput(color, plain, env = process2.env) {
479
+ function applyOutput(color, plain, env = process5.env) {
413
480
  const when = plain === true ? "never" : color ?? "auto";
414
481
  if (when === "auto")
415
482
  return;
@@ -432,7 +499,7 @@ function parse(args, usage, options, settings = {}) {
432
499
  const { own, rest } = settings.trailing === true ? splitTrailing(args, all, settings.positionals?.length ?? 0) : splitRest(args, settings.rest === true);
433
500
  let parsed;
434
501
  try {
435
- parsed = parseArgs({ args: joinDashValues(withBare(own, all), all), options: all, allowPositionals: true, allowNegative: settings.allowNegative === true, strict: true });
502
+ parsed = parseArgs({ args: joinDashValues(withBare(withAliases(own, all), all), all), options: all, allowPositionals: true, allowNegative: settings.allowNegative === true, strict: true });
436
503
  } catch (error) {
437
504
  const message = parseError(error);
438
505
  throw new UsageError(message, nearestOption(message, all, settings.allowNegative === true));
@@ -440,7 +507,7 @@ function parse(args, usage, options, settings = {}) {
440
507
  const values = parsed.values;
441
508
  applyOutput("color" in output ? values.color : undefined, "plain" in output ? values.plain : undefined);
442
509
  if (values.help === true) {
443
- console.log(wantsHelpJson() ? JSON.stringify(description(usage, settings)) : helpText(usage, listing, undefined, settings));
510
+ console.log(wantsHelpJson() ? JSON.stringify(description(usage, settings, listing)) : helpText(usage, listing, undefined, settings));
444
511
  throw new HelpShown;
445
512
  }
446
513
  checkChoices(parsed.values, all);
@@ -482,6 +549,18 @@ function listed(options, allowNegative) {
482
549
  return [...option.description === undefined ? [] : [[name, option]], [`no-${name}`, { type: "boolean", description: option.negative }]];
483
550
  }));
484
551
  }
552
+ function withAliases(args, options) {
553
+ const named = new Map(Object.entries(options).flatMap(([name, { aliases = [] }]) => aliases.map((alias) => [`--${alias}`, `--${name}`])));
554
+ if (named.size === 0)
555
+ return args;
556
+ const separator = args.indexOf("--");
557
+ const own = separator === -1 ? args : args.slice(0, separator);
558
+ return [...own.map((arg) => {
559
+ const at = arg.indexOf("=");
560
+ const current = named.get(at === -1 ? arg : arg.slice(0, at));
561
+ return current === undefined ? arg : `${current}${at === -1 ? "" : arg.slice(at)}`;
562
+ }), ...separator === -1 ? [] : args.slice(separator)];
563
+ }
485
564
  function withBare(args, options) {
486
565
  const bare = new Map(Object.entries(options).flatMap(([name, { short, optional }]) => optional === undefined ? [] : [[`--${name}`, optional], ...short === undefined ? [] : [[`-${short}`, optional]]]));
487
566
  if (bare.size === 0)
@@ -568,7 +647,7 @@ function nearestOption(message, options, allowNegative) {
568
647
  }
569
648
  // ../kit/src/process.ts
570
649
  import { constants } from "os";
571
- import process3 from "process";
650
+ import process6 from "process";
572
651
  var KILL_GRACE_MS = 2000;
573
652
  function exitCodeOf(code, signal) {
574
653
  if (code !== null)
@@ -588,7 +667,7 @@ async function runCommand(argv, { cwd, env, stdin = "ignore", timeoutMs, signal,
588
667
  return;
589
668
  try {
590
669
  if (grouped)
591
- process3.kill(-proc.pid, killing);
670
+ process6.kill(-proc.pid, killing);
592
671
  else
593
672
  proc.kill(killing);
594
673
  } catch {}
@@ -662,7 +741,97 @@ var ESCAPE_CODE = new RegExp(`${ESC}(?:\\[[\\x30-\\x3F]*[\\x20-\\x2F]*[\\x40-\\x
662
741
  var RESET_STYLE = `${ESC}[0m`;
663
742
  var CLOSE_LINK = `${ESC}]8;;${ESC}\\`;
664
743
  // ../kit/src/cli.ts
665
- import process4 from "process";
744
+ import { readFileSync, realpathSync } from "fs";
745
+ import { dirname as dirname2, join as join2 } from "path";
746
+ import process8 from "process";
747
+
748
+ // ../kit/src/usage.ts
749
+ import { appendFileSync, mkdirSync } from "fs";
750
+ import { homedir } from "os";
751
+ import { dirname, isAbsolute, join } from "path";
752
+ import process7 from "process";
753
+
754
+ // ../kit/src/logs.ts
755
+ var DAY_MS = 24 * 60 * 60 * 1000;
756
+ var CLI_FOLDER = "kaynooo-cli";
757
+
758
+ // ../kit/src/usage.ts
759
+ var UNKNOWN_OPTION2 = /unknown option '(--[a-z][a-z0-9-]*)[=']/;
760
+ var FLAG_NAME = /^(?:--[a-z][a-z0-9-]*|-[a-zA-Z])$/;
761
+ var LONGEST_NAME = 40;
762
+ var usageError;
763
+ function noteUsageError(message) {
764
+ usageError = message;
765
+ }
766
+ function stateHome(env = process7.env) {
767
+ const dir = env.XDG_STATE_HOME;
768
+ return dir !== undefined && isAbsolute(dir) ? dir : join(homedir(), ".local", "state");
769
+ }
770
+ function usagePath(env = process7.env) {
771
+ return join(stateHome(env), CLI_FOLDER, "usage.jsonl");
772
+ }
773
+ function usageRecorded(env = process7.env) {
774
+ const off = env.KAYNOOO_CLI_NO_USAGE;
775
+ return env.NODE_ENV !== "test" && (off === undefined || off === "" || off === "0");
776
+ }
777
+ function flagNames(argv) {
778
+ const end = argv.indexOf("--");
779
+ const names = (end === -1 ? argv : argv.slice(0, end)).filter((word) => word.startsWith("-") && word !== "-").map((word) => word.includes("=") ? word.slice(0, word.indexOf("=")) : word);
780
+ return [...new Set(names)].filter((name) => name.length <= LONGEST_NAME && FLAG_NAME.test(name));
781
+ }
782
+ function usageEntry(bin, command, argv, code, now = new Date) {
783
+ const unknown = code === 2 && usageError !== undefined ? UNKNOWN_OPTION2.exec(usageError)?.[1] : undefined;
784
+ usageError = undefined;
785
+ return { at: now.toISOString(), bin, ...command === undefined ? {} : { command }, flags: flagNames(argv), code, ...unknown === undefined ? {} : { unknown } };
786
+ }
787
+ function recordUsage(entry, env = process7.env) {
788
+ if (!usageRecorded(env))
789
+ return;
790
+ try {
791
+ const path = usagePath(env);
792
+ mkdirSync(dirname(path), { recursive: true, mode: 448 });
793
+ appendFileSync(path, `${JSON.stringify(entry)}
794
+ `, { mode: 384 });
795
+ } catch {}
796
+ }
797
+ async function readUsage(path = usagePath(), since = 0) {
798
+ const file = Bun.file(path);
799
+ if (!await file.exists())
800
+ return [];
801
+ return (await file.text()).split(`
802
+ `).flatMap((line) => {
803
+ try {
804
+ const entry = JSON.parse(line);
805
+ return Date.parse(entry.at) >= since ? [entry] : [];
806
+ } catch {
807
+ return [];
808
+ }
809
+ });
810
+ }
811
+ function summarizeUsage(entries) {
812
+ const bins = new Map;
813
+ const errors = new Map;
814
+ for (const entry of entries) {
815
+ const bin = bins.get(entry.bin) ?? { runs: 0, failed: 0, usageErrors: 0 };
816
+ bin.runs++;
817
+ bin.failed += entry.code === 0 ? 0 : 1;
818
+ bin.usageErrors += entry.code === 2 ? 1 : 0;
819
+ bins.set(entry.bin, bin);
820
+ if (entry.code === 2 && entry.unknown !== undefined) {
821
+ const key = `${entry.bin} ${entry.command ?? ""} ${entry.unknown}`;
822
+ const error = errors.get(key) ?? { bin: entry.bin, ...entry.command === undefined ? {} : { command: entry.command }, unknown: entry.unknown, count: 0 };
823
+ error.count++;
824
+ errors.set(key, error);
825
+ }
826
+ }
827
+ return {
828
+ runs: entries.length,
829
+ bins: [...bins].map(([bin, counts]) => ({ bin, ...counts })).toSorted((a, b) => b.runs - a.runs),
830
+ usageErrors: [...errors.values()].toSorted((a, b) => b.count - a.count)
831
+ };
832
+ }
833
+
834
+ // ../kit/src/cli.ts
666
835
  var LEVEL_OPTIONS = {
667
836
  ...OUTPUT_OPTIONS,
668
837
  help: { type: "boolean", short: "h", description: "show this help" }
@@ -715,7 +884,7 @@ async function expand(scope, words) {
715
884
  }
716
885
  return { ...scope, commands, lazy: [] };
717
886
  }
718
- async function help({ path, header, summary: lazySummary, commands, defaultCommand, examples, replaces }, stream) {
887
+ async function help2({ path, header, summary: lazySummary, commands, defaultCommand, examples, replaces }, stream) {
719
888
  const colors = paint(stream);
720
889
  const prefix = path.join(" ");
721
890
  const summary = await resolveSummary(lazySummary);
@@ -742,8 +911,8 @@ async function help({ path, header, summary: lazySummary, commands, defaultComma
742
911
  `);
743
912
  }
744
913
  function printError(prefix, message, hint, cause) {
745
- const colors = paint(process4.stderr);
746
- const styled = takesControlCodes(process4.stderr);
914
+ const colors = paint(process8.stderr);
915
+ const styled = takesControlCodes(process8.stderr);
747
916
  const head = styled ? `${colors.red.bold("error")} ${colors.dim(`${prefix}:`)}` : `error: ${prefix}:`;
748
917
  const under = (label, text, tint) => text === undefined ? "" : `
749
918
  ${styled ? tint(label) : `${label}:`} ${text}`;
@@ -762,6 +931,8 @@ function reportThrown(prefix, error, redact = (text) => text, json = false) {
762
931
  const help = error instanceof UsageError ? `run ${prefix} --help for its options` : undefined;
763
932
  const named = error instanceof Error && !(error instanceof Exit) && error.name !== "Error" ? `${error.name}: ${error.message}` : undefined;
764
933
  const cause = error instanceof Error && error.cause instanceof Error ? redact(error.cause.message) : undefined;
934
+ if (error instanceof UsageError)
935
+ noteUsageError(error.message);
765
936
  const report = json ? printJsonError : (message, hint, causeText) => printError(prefix, message, hint, causeText);
766
937
  report(redact(named ?? (error instanceof Error ? error.message : String(error))), error instanceof Exit ? error.hint ?? help : undefined, cause);
767
938
  return error instanceof Exit ? error.code : 1;
@@ -784,11 +955,11 @@ async function dispatch(level, typed, { version, redact }) {
784
955
  }
785
956
  }
786
957
  if (name === undefined && level.path.length > 1) {
787
- console.error(await help(await expand(level, [...level.path.slice(1), ...argv]), process4.stderr));
958
+ console.error(await help2(await expand(level, [...level.path.slice(1), ...argv]), process8.stderr));
788
959
  return 2;
789
960
  }
790
961
  if (name === undefined || name === "help" || name === "--help" || name === "-h") {
791
- console.log(await help(await expand(level, [...level.path.slice(1), ...argv]), process4.stdout));
962
+ console.log(await help2(await expand(level, [...level.path.slice(1), ...argv]), process8.stdout));
792
963
  return 0;
793
964
  }
794
965
  if (level.path.length === 1 && (name === "--version" || name === "-v")) {
@@ -904,43 +1075,78 @@ async function printCandidates(found) {
904
1075
  console.log(line);
905
1076
  return 0;
906
1077
  }
907
- async function runCli(cli, argv = process4.argv.slice(2)) {
1078
+ function packageVersion(entry = Bun.main) {
1079
+ for (let dir = dirname2(realpathSync(entry));; dir = dirname2(dir)) {
1080
+ try {
1081
+ const { version } = JSON.parse(readFileSync(join2(dir, "package.json"), "utf8"));
1082
+ if (typeof version === "string")
1083
+ return version;
1084
+ } catch {}
1085
+ if (dirname2(dir) === dir)
1086
+ return "0.0.0";
1087
+ }
1088
+ }
1089
+ async function runCli(cli, argv = process8.argv.slice(2)) {
1090
+ const version = cli.version ?? packageVersion();
908
1091
  if (argv[0] === COMPLETE)
909
1092
  return printCandidates(candidates(scopeOf(cli.commands, cli.extraCommands, cli.aliases), cli.defaultCommand, argv.slice(1)));
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 });
1093
+ const code = await dispatch({ path: [cli.name], header: (colors, about) => `${colors.bold(cli.name)} ${colors.dim(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, redact: cli.redact });
1094
+ if (cli.usage !== false) {
1095
+ try {
1096
+ recordUsage(usageEntry(cli.name, commandPath(scopeOf(cli.commands, cli.extraCommands, cli.aliases), argv), argv, code));
1097
+ } catch {}
1098
+ }
1099
+ return code;
1100
+ }
1101
+ function commandPath(scope, argv) {
1102
+ const path = [];
1103
+ let level = scope;
1104
+ for (const word of argv.filter((each) => !each.startsWith("-"))) {
1105
+ const name = level.aliases !== undefined && Object.hasOwn(level.aliases, word) ? level.aliases[word] ?? word : word;
1106
+ const command = Object.hasOwn(level.commands, name) ? level.commands[name] : undefined;
1107
+ if (command === undefined)
1108
+ break;
1109
+ path.push(word);
1110
+ if (!("commands" in command))
1111
+ break;
1112
+ level = scopeOf(command.commands, command.extraCommands, command.aliases);
1113
+ }
1114
+ return path.length === 0 ? undefined : path.join(" ");
911
1115
  }
912
- async function runMain({ name, version, run, redact }, argv = process4.argv.slice(2)) {
1116
+ async function runMain({ name, version, run, redact, usage }, argv = process8.argv.slice(2)) {
913
1117
  if (argv[0] === COMPLETE)
914
1118
  return printCandidates(leafCandidates(run, argv.slice(1)));
915
1119
  if (argv.length === 1 && (argv[0] === "--version" || argv[0] === "-v")) {
916
- console.log(version);
1120
+ console.log(version ?? packageVersion());
917
1121
  return 0;
918
1122
  }
1123
+ let code;
919
1124
  try {
920
- return await run(argv);
1125
+ code = await run(argv);
921
1126
  } catch (error) {
922
- return reportThrown(name, error, redact, wantsJson(argv));
1127
+ code = reportThrown(name, error, redact, wantsJson(argv));
1128
+ }
1129
+ if (usage !== false) {
1130
+ try {
1131
+ recordUsage(usageEntry(name, undefined, argv, code));
1132
+ } catch {}
923
1133
  }
1134
+ return code;
924
1135
  }
925
- // ../kit/src/logs.ts
926
- var DAY_MS = 24 * 60 * 60 * 1000;
927
1136
  // ../kit/src/secrets.ts
928
1137
  var SECRET_WORDS = new Set(["key", "pass", "pwd", "auth", "salt", "cookie", "session", "jwt", "ssn", "iban", "card", "cvv", "otp"]);
929
1138
  // ../kit/src/sessions.ts
930
1139
  var {Glob } = globalThis.Bun;
931
1140
  var SESSION_FILES = new Glob("*.json");
932
1141
  var ACCOUNT_DIRS = new Glob("*/sessions");
933
- // package.json
934
- var version = "0.3.0";
935
-
936
1142
  // src/checks.ts
937
- import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
938
- import { basename, join as join2 } from "path";
1143
+ import { existsSync as existsSync2, readFileSync as readFileSync3 } from "fs";
1144
+ import { basename, join as join4 } from "path";
939
1145
 
940
1146
  // src/completions.ts
941
- import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync } from "fs";
942
- import { join } from "path";
943
- import process6 from "process";
1147
+ import { existsSync, mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync2, rmSync } from "fs";
1148
+ import { join as join3 } from "path";
1149
+ import process10 from "process";
944
1150
  var MARKER = "# Generated by `cli completions fish`";
945
1151
  var FISH_FILE = /\.fish$/;
946
1152
  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'
@@ -954,8 +1160,28 @@ var FUNCTION = `functions -q __kaynooo_cli_complete; or function __kaynooo_cli_c
954
1160
  end
955
1161
  end
956
1162
  end`;
957
- function completionDir(home, env = process6.env) {
958
- return join(env.XDG_CACHE_HOME ?? join(home, ".cache"), "fish", "kaynooo-cli");
1163
+ var BASH_FUNCTION = `__kaynooo_cli_complete() {
1164
+ local IFS=$'\\n' cur=\${COMP_WORDS[COMP_CWORD]} line words=
1165
+ local -a lines
1166
+ COMPREPLY=()
1167
+ lines=($("\${COMP_WORDS[0]}" __complete "\${COMP_WORDS[@]:1:COMP_CWORD}" 2>/dev/null))
1168
+ for line in "\${lines[@]}"; do
1169
+ if [[ $line == __files__ ]]; then
1170
+ COMPREPLY+=($(compgen -f -- "$cur"))
1171
+ else
1172
+ words+="\${line%%$'\\t'*}"$'\\n'
1173
+ fi
1174
+ done
1175
+ COMPREPLY+=($(compgen -W "$words" -- "$cur"))
1176
+ }`;
1177
+ function bashScript(bins) {
1178
+ return `# Generated by \`cli completions bash\`
1179
+ ${BASH_FUNCTION}
1180
+ complete -F __kaynooo_cli_complete ${bins.join(" ")}
1181
+ `;
1182
+ }
1183
+ function completionDir(home, env = process10.env) {
1184
+ return join3(env.XDG_CACHE_HOME ?? join3(home, ".cache"), "fish", "kaynooo-cli");
959
1185
  }
960
1186
  function installedBins(host) {
961
1187
  return Object.values(TOOLS).flat().filter((bin) => host.which(bin) !== null);
@@ -968,34 +1194,42 @@ complete -c ${bin} -f -a '(__kaynooo_cli_complete ${bin})'
968
1194
  }
969
1195
  async function installCompletions(host, dir) {
970
1196
  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));
1197
+ mkdirSync2(dir, { recursive: true });
1198
+ const removed = readdirSync(dir).filter((name) => FISH_FILE.test(name) && !bins.includes(name.replace(FISH_FILE, "")) && readFileSync2(join3(dir, name), "utf8").startsWith(MARKER));
973
1199
  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))));
1200
+ rmSync(join3(dir, name));
1201
+ await Promise.all(bins.map(async (bin) => Bun.write(join3(dir, `${bin}.fish`), completionFile(bin))));
976
1202
  return { written: bins, removed: removed.map((name) => name.replace(FISH_FILE, "")) };
977
1203
  }
978
1204
  function missingCompletions(host, dir) {
979
- return installedBins(host).filter((bin) => !existsSync(join(dir, `${bin}.fish`)));
1205
+ return installedBins(host).filter((bin) => !existsSync(join3(dir, `${bin}.fish`)));
980
1206
  }
981
1207
 
982
1208
  // src/checks.ts
983
1209
  var TOOLS = {
1210
+ "@kaynooo/cli-audit": ["audit"],
984
1211
  "@kaynooo/cli-base": ["base", "proof"],
985
1212
  "@kaynooo/cli-changelog": ["changelog"],
986
- "@kaynooo/cli-check": ["check", "fix", "errs"],
1213
+ "@kaynooo/cli-check": ["check", "fix", "errs", "j"],
987
1214
  "@kaynooo/cli-code": ["peek", "todo-scan", "hotspots"],
988
1215
  "@kaynooo/cli-db": ["db"],
1216
+ "@kaynooo/cli-docker": ["dc", "dlog", "dx", "sf"],
989
1217
  "@kaynooo/cli-doctor": ["doctor", "cli"],
990
1218
  "@kaynooo/cli-edit": ["sub"],
991
1219
  "@kaynooo/cli-env": ["ev", "redact"],
992
1220
  "@kaynooo/cli-forgejo": ["ft"],
993
- "@kaynooo/cli-git": ["gst", "gc"],
1221
+ "@kaynooo/cli-git": ["gst", "gc", "gb"],
994
1222
  "@kaynooo/cli-gitlab": ["gl"],
995
1223
  "@kaynooo/cli-guard": ["guard"],
996
1224
  "@kaynooo/cli-out": ["out"],
1225
+ "@kaynooo/cli-dev": ["dev"],
1226
+ "@kaynooo/cli-pick": ["pick"],
997
1227
  "@kaynooo/cli-pkg": ["pkg"],
1228
+ "@kaynooo/cli-probe": ["probe"],
1229
+ "@kaynooo/cli-proc": ["pid", "ports", "procs"],
1230
+ "@kaynooo/cli-repos": ["repos"],
998
1231
  "@kaynooo/cli-stack": ["stack"],
1232
+ "@kaynooo/cli-sweep": ["sweep"],
999
1233
  "@kaynooo/cli-view": ["view"],
1000
1234
  "@kaynooo/cli-wait": ["waitfor", "slot"]
1001
1235
  };
@@ -1014,7 +1248,7 @@ function readJsonText(text) {
1014
1248
  }
1015
1249
  }
1016
1250
  function readJson(path) {
1017
- return existsSync2(path) ? readJsonText(readFileSync2(path, "utf8")) : undefined;
1251
+ return existsSync2(path) ? readJsonText(readFileSync3(path, "utf8")) : undefined;
1018
1252
  }
1019
1253
  function firstLine(text) {
1020
1254
  const trimmed = text.trim();
@@ -1023,12 +1257,12 @@ function firstLine(text) {
1023
1257
  return end === -1 ? trimmed : trimmed.slice(0, end);
1024
1258
  }
1025
1259
  function installed(host, name) {
1026
- return readJson(join2(host.globalModules, name, "package.json"));
1260
+ return readJson(join4(host.globalModules, name, "package.json"));
1027
1261
  }
1028
1262
  function ownBin(host, name, bin) {
1029
1263
  const declared = installed(host, name)?.bin;
1030
1264
  const file = typeof declared === "string" ? declared : declared?.[bin];
1031
- return file === undefined ? undefined : join2(host.globalModules, name, file);
1265
+ return file === undefined ? undefined : join4(host.globalModules, name, file);
1032
1266
  }
1033
1267
  async function checkTool(host, name, bins) {
1034
1268
  const check = name.slice(name.indexOf("/") + 1);
@@ -1037,7 +1271,7 @@ async function checkTool(host, name, bins) {
1037
1271
  return { check, ok: false, detail: "not installed", fix: `bun install -g ${name}` };
1038
1272
  const off = bins.filter((bin) => host.which(bin) === null);
1039
1273
  if (off.length > 0)
1040
- return { check, ok: false, detail: `${version}, ${off.join(", ")} not on the PATH`, fix: `add ${join2(host.globalModules, "..", "..", "..", "bin")} to the PATH` };
1274
+ return { check, ok: false, detail: `${version}, ${off.join(", ")} not on the PATH`, fix: `add ${join4(host.globalModules, "..", "..", "..", "bin")} to the PATH` };
1041
1275
  const info = await host.run(["bun", "info", name, "version", "--no-cache"]);
1042
1276
  const answered = info.code === 0 ? firstLine(info.stdout.trim().split(`
1043
1277
  `).at(-1) ?? "") : "";
@@ -1064,9 +1298,9 @@ async function checkTea(host) {
1064
1298
  return { check: "tea", ok: true, detail: `logged in to ${forge} as ${login.name}, Forgejo ${served}` };
1065
1299
  }
1066
1300
  function checkHooks(host) {
1067
- const settings = readJson(join2(host.home, ".claude", "settings.json")) ?? {};
1301
+ const settings = readJson(join4(host.home, ".claude", "settings.json")) ?? {};
1068
1302
  const plugin = settings.enabledPlugins?.[PLUGIN] === true;
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")));
1303
+ const mod = (settings.env?.CLAUDE_CODE_PLUGIN_DIRS ?? "").split(":").map((dir) => dir.replace(LEADING_TILDE, host.home)).find((dir) => basename(dir) === "cli" && existsSync2(join4(dir, "hooks", "hooks.json")));
1070
1304
  if (plugin && mod !== undefined)
1071
1305
  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}` };
1072
1306
  if (plugin)
@@ -1091,28 +1325,75 @@ function checkCompletions(host) {
1091
1325
  return { check: "completions", ok: false, detail: `no fish completion in ${dir} for ${missing.join(", ")}`, fix: "cli completions fish --install" };
1092
1326
  return { check: "completions", ok: true, detail: dir };
1093
1327
  }
1094
- async function runChecks(host) {
1095
- const entries = Object.entries(TOOLS);
1328
+ var toolCheck = (name) => name.slice(name.indexOf("/") + 1);
1329
+ var OTHER_CHECKS = ["tea", "hooks", "bun", "completions"];
1330
+ var CHECK_NAMES = [...Object.keys(TOOLS).map(toolCheck), ...OTHER_CHECKS];
1331
+ async function runChecks(host, only = []) {
1332
+ const wanted = (check) => only.length === 0 || only.includes(check);
1333
+ const entries = Object.entries(TOOLS).filter(([name]) => wanted(toolCheck(name)));
1096
1334
  const tools = [];
1097
1335
  for (let at = 0;at < entries.length; at += INFO_AT_ONCE)
1098
1336
  tools.push(...await Promise.all(entries.slice(at, at + INFO_AT_ONCE).map(async ([name, bins]) => checkTool(host, name, bins))));
1099
- return [...tools, await checkTea(host), checkHooks(host), checkBun(host), checkCompletions(host)];
1337
+ return [
1338
+ ...tools,
1339
+ ...wanted("tea") ? [await checkTea(host)] : [],
1340
+ ...wanted("hooks") ? [checkHooks(host)] : [],
1341
+ ...wanted("bun") ? [checkBun(host)] : [],
1342
+ ...wanted("completions") ? [checkCompletions(host)] : []
1343
+ ];
1344
+ }
1345
+
1346
+ // src/fix.ts
1347
+ var INSTALL = /^bun install -g (\S+)$/;
1348
+ var RUNNABLE = /^(?:cli completions fish --install|claude plugin (?:install \S+ --marketplace \S+|disable \S+))$/;
1349
+ function planFixes(results) {
1350
+ const steps = [];
1351
+ const installs = [];
1352
+ for (const { ok, fix } of results) {
1353
+ if (ok || fix === undefined)
1354
+ continue;
1355
+ const install = INSTALL.exec(fix);
1356
+ if (install !== null)
1357
+ installs.push(install[1] ?? "");
1358
+ else
1359
+ steps.push({ command: fix, argv: RUNNABLE.test(fix) ? fix.split(" ") : undefined });
1360
+ }
1361
+ if (installs.length === 0)
1362
+ return steps;
1363
+ const command = `bun install -g ${installs.join(" ")}`;
1364
+ return [{ command, argv: command.split(" ") }, ...steps];
1365
+ }
1366
+ async function applyFixes(host, steps, dry) {
1367
+ const outcomes = [];
1368
+ for (const { command, argv } of steps) {
1369
+ if (argv === undefined) {
1370
+ outcomes.push({ command, status: "not run", detail: "not a command doctor runs: do it by hand" });
1371
+ continue;
1372
+ }
1373
+ if (dry) {
1374
+ outcomes.push({ command, status: "dry-run" });
1375
+ continue;
1376
+ }
1377
+ const ran = await host.run(argv);
1378
+ outcomes.push(ran.code === 0 ? { command, status: "ran" } : { command, status: "failed", detail: `exit ${ran.code}: ${firstLine(ran.stderr || ran.stdout)}` });
1379
+ }
1380
+ return outcomes;
1100
1381
  }
1101
1382
 
1102
1383
  // src/host.ts
1103
- import { homedir } from "os";
1104
- import { join as join3 } from "path";
1105
- import process7 from "process";
1384
+ import { homedir as homedir2 } from "os";
1385
+ import { join as join5 } from "path";
1386
+ import process11 from "process";
1106
1387
  var RUN_TIMEOUT_MS = 20000;
1107
1388
  function hostHere() {
1108
- const home = homedir();
1389
+ const home = homedir2();
1109
1390
  return {
1110
1391
  home,
1111
- globalModules: join3(process7.env.BUN_INSTALL ?? join3(home, ".bun"), "install", "global", "node_modules"),
1392
+ globalModules: join5(process11.env.BUN_INSTALL ?? join5(home, ".bun"), "install", "global", "node_modules"),
1112
1393
  bunVersion: Bun.version,
1113
1394
  which: (bin) => Bun.which(bin),
1114
1395
  run: async (argv, env) => {
1115
- const ran = await runCommand(argv, { timeoutMs: RUN_TIMEOUT_MS, ...env === undefined ? {} : { env: { ...process7.env, ...env } } });
1396
+ const ran = await runCommand(argv, { timeoutMs: RUN_TIMEOUT_MS, ...env === undefined ? {} : { env: { ...process11.env, ...env } } });
1116
1397
  if (ran.timedOut)
1117
1398
  return { code: 1, stdout: "", stderr: `timed out after ${RUN_TIMEOUT_MS / 1000}s` };
1118
1399
  const of = (stream) => Buffer.concat(ran.chunks.filter((chunk) => chunk.stream === stream).map((chunk) => chunk.data)).toString();
@@ -1122,35 +1403,67 @@ function hostHere() {
1122
1403
  }
1123
1404
 
1124
1405
  // src/cli.ts
1125
- var USAGE = `doctor [--json]: checks this host's @kaynooo/cli-* install, one line per check, the command that fixes a failing one beside it
1406
+ 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
1126
1407
 
1127
- 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.
1128
- Exits 0 when every check passes, 1 when one fails, 2 on a usage error.`;
1129
- var INSTALL = "bun install -g ";
1408
+ 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.
1409
+ --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.
1410
+ --quiet prints only the failing checks, nothing when all pass. --only limits the run to the checks named, e.g. --only tea --only bun.`;
1411
+ var EXITS = [[0, "every check passes (after --fix, the ones it fixed too)"], [1, "a check fails"], [2, "a usage error"]];
1412
+ var INSTALL2 = "bun install -g ";
1130
1413
  var OPTIONS = {
1131
- json: jsonOption("[{check, ok, detail, fix?}]")
1414
+ json: jsonOption("[{check, ok, detail, fix?}], as the checks stand after --fix; the fixes it ran go to stderr"),
1415
+ 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" },
1416
+ quiet: { type: "boolean", short: "q", description: "print only the failing checks" },
1417
+ fix: { type: "boolean", description: "run the fix: lines doctor may run, then check again" },
1418
+ "dry-run": { type: "boolean", description: "with --fix, show what would run without running it (alone, it implies --fix)" }
1132
1419
  };
1133
1420
  function print(results) {
1134
- const { green, red, dim } = paint(process8.stdout);
1421
+ if (results.length === 0)
1422
+ return;
1423
+ const { green, red, dim } = paint(process12.stdout);
1135
1424
  const width = Math.max(...results.map((result) => result.check.length));
1136
1425
  for (const { check, ok, detail, fix } of results)
1137
1426
  console.log(`${ok ? green("ok ") : red("FAIL")} ${check.padEnd(width)} ${detail}${fix === undefined ? "" : ` ${dim("fix:")} ${fix}`}`);
1138
- const installs = results.flatMap((result) => result.fix?.startsWith(INSTALL) === true ? [result.fix.slice(INSTALL.length)] : []);
1427
+ const installs = results.flatMap((result) => result.fix?.startsWith(INSTALL2) === true ? [result.fix.slice(INSTALL2.length)] : []);
1139
1428
  if (installs.length > 1)
1140
1429
  console.log(`
1141
- ${dim("fix them all:")} ${INSTALL}${installs.join(" ")}`);
1430
+ ${dim("fix them all:")} ${INSTALL2}${installs.join(" ")}`);
1431
+ }
1432
+ function printOutcomes(outcomes, log) {
1433
+ const { green, red, dim } = paint(process12.stdout);
1434
+ const marks = { ran: green("ran "), failed: red("failed "), "dry-run": dim("would "), "not run": dim("not run") };
1435
+ for (const { command, status, detail } of outcomes)
1436
+ log(`${marks[status]} ${command}${detail === undefined ? "" : ` ${dim(detail)}`}`);
1142
1437
  }
1143
1438
  async function main(args, host = hostHere()) {
1144
- const { values } = parse(args, USAGE, OPTIONS, { positionals: [], replaces: "checking each bin's version, tea's login and the hooks by hand", examples: ["doctor", ["doctor --json", "the checks for a script"]] });
1145
- const results = await runChecks(host);
1146
- if (values.json === true)
1147
- printJson(results);
1439
+ 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"]] });
1440
+ const only = values.only ?? [];
1441
+ const dry = values["dry-run"] === true;
1442
+ const json = values.json === true;
1443
+ let results = await runChecks(host, only);
1444
+ let fixed = false;
1445
+ if ((values.fix === true || dry) && results.some((result) => !result.ok)) {
1446
+ if (!json)
1447
+ print(values.quiet === true ? results.filter((result) => !result.ok) : results);
1448
+ const steps = planFixes(results);
1449
+ printOutcomes(await applyFixes(host, steps, dry), json ? console.error : console.log);
1450
+ if (dry)
1451
+ return 1;
1452
+ results = await runChecks(host, only);
1453
+ fixed = true;
1454
+ }
1455
+ const shown = values.quiet === true ? results.filter((result) => !result.ok) : results;
1456
+ if (fixed && !json && shown.length > 0)
1457
+ console.log(`
1458
+ after the fixes:`);
1459
+ if (json)
1460
+ printJson(shown);
1148
1461
  else
1149
- print(results);
1462
+ print(shown);
1150
1463
  return results.every((result) => result.ok) ? 0 : 1;
1151
1464
  }
1152
1465
  if (import.meta.main)
1153
- process8.exitCode = await runMain({ name: "doctor", version, run: async (args) => main(args) });
1466
+ process12.exitCode = await runMain({ name: "doctor", run: async (args) => main(args) });
1154
1467
  export {
1155
1468
  main
1156
1469
  };