@kaynooo/cli-doctor 0.8.0 → 0.10.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 (3) hide show
  1. package/dist/cli-bin.js +277 -102
  2. package/dist/cli.js +276 -101
  3. package/package.json +2 -2
package/dist/cli-bin.js CHANGED
@@ -2,16 +2,37 @@
2
2
  // @bun
3
3
 
4
4
  // src/cli-bin.ts
5
- import process15 from "process";
5
+ import process18 from "process";
6
6
  // ../kit/src/args.ts
7
- import process5 from "process";
7
+ import process7 from "process";
8
8
  import { parseArgs } from "util";
9
9
 
10
10
  // ../kit/src/format.ts
11
- import process3 from "process";
11
+ import process5 from "process";
12
12
 
13
- // ../kit/src/style.ts
13
+ // ../kit/src/mode.ts
14
14
  import process2 from "process";
15
+ var MODES = new Set(["terminal", "ci", "agent", "pipe"]);
16
+ var AGENT_VARIABLES = ["CLAUDECODE", "AI_AGENT"];
17
+ function isSet(value) {
18
+ return value !== undefined && value !== "" && value !== "0" && value.toLowerCase() !== "false";
19
+ }
20
+ function outputMode(stream = process2.stdout, env = process2.env) {
21
+ const chosen = env.KIT_MODE;
22
+ if (chosen !== undefined && MODES.has(chosen))
23
+ return chosen;
24
+ if (AGENT_VARIABLES.some((name) => isSet(env[name])))
25
+ return "agent";
26
+ if (stream.isTTY === true)
27
+ return "terminal";
28
+ return isSet(env.CI) ? "ci" : "pipe";
29
+ }
30
+ function isInteractive(streams = process2) {
31
+ return streams.stdin?.isTTY === true && streams.stderr?.isTTY === true;
32
+ }
33
+
34
+ // ../kit/src/style.ts
35
+ import process4 from "process";
15
36
 
16
37
  // ../../node_modules/.bun/@kaynooo+utils@2.31.2+d0769e28dc4dad16/node_modules/@kaynooo/utils/dist/chunk-mtetydvc.js
17
38
  function x(s) {
@@ -274,22 +295,34 @@ function le(i = {}) {
274
295
  }
275
296
  var t = le();
276
297
 
298
+ // ../kit/src/user-config.ts
299
+ import { homedir } from "os";
300
+ import { join } from "path";
301
+ import process3 from "process";
302
+ function configBase(where = {}) {
303
+ const xdg = where.xdg ?? (where.home === undefined ? process3.env.XDG_CONFIG_HOME : undefined);
304
+ return xdg !== undefined && xdg !== "" ? xdg : join(where.home ?? homedir(), ".config");
305
+ }
306
+ function userConfigPath(bin, where = {}) {
307
+ return join(configBase(where), "kaynooo-cli", `${bin}.json`);
308
+ }
309
+
277
310
  // ../kit/src/theme.ts
278
311
  var STYLES = new Set(["bold", "dim", "italic", "underline", "inverse", "strikethrough", "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white", "gray", "grey", "blackBright", "redBright", "greenBright", "yellowBright", "blueBright", "magentaBright", "cyanBright", "whiteBright"]);
279
312
  var MAX_FILE_BYTES = 64 * 1024;
280
313
  var warned = new Set;
281
314
 
282
315
  // ../kit/src/style.ts
283
- function takesControlCodes(stream, env = process2.env) {
284
- return stream.isTTY === true && env.NO_COLOR === undefined && env.NODE_DISABLE_COLORS === undefined && env.TERM !== "dumb";
316
+ function takesControlCodes(stream, env = process4.env) {
317
+ return stream.isTTY === true && env.NO_COLOR === undefined && env.NODE_DISABLE_COLORS === undefined && env.TERM !== "dumb" && outputMode(stream, env) === "terminal";
285
318
  }
286
- function paint(stream, env = process2.env) {
319
+ function paint(stream, env = process4.env) {
287
320
  return le({ stream: { isTTY: takesControlCodes(stream, env) }, env });
288
321
  }
289
322
  var STATUS_WORDS = {
290
323
  passed: ["passed", "success", "succeeded", "ok"],
291
324
  failed: ["failed", "failure", "error", "errored"],
292
- running: ["running", "in_progress", "started"],
325
+ running: ["running", "in_progress", "started", "holding"],
293
326
  queued: ["queued", "waiting", "pending", "created", "blocked", "scheduled"],
294
327
  skipped: ["skipped", "cancelled", "canceled", "manual"]
295
328
  };
@@ -308,8 +341,11 @@ var ESCAPE = String.fromCharCode(27);
308
341
  function textWidth(text) {
309
342
  return Bun.stringWidth(text);
310
343
  }
311
- function table(rows, { cellWidth, header = false, align = [], fit, stream = process3.stdout } = {}) {
344
+ function table(rows, { cellWidth, header = false, align = [], fit, stream = process5.stdout, env = process5.env } = {}) {
312
345
  const cells = cellWidth === undefined ? rows : rows.map((row) => row.map((cell) => truncate(cell.replaceAll(LINE_BREAKS, " "), cellWidth)));
346
+ if (outputMode(stream, env) === "agent")
347
+ return cells.map((row) => row.map((cell) => Bun.stripANSI(cell).replaceAll(/[\t\r\n]+/g, " ")).join("\t")).join(`
348
+ `);
313
349
  const widths = [];
314
350
  for (const row of cells) {
315
351
  for (const [index, cell] of row.entries())
@@ -344,32 +380,35 @@ function narrow(rows, widths, limit) {
344
380
  }
345
381
  return rows.map((row) => row.map((cell, index) => textWidth(cell) > (widths[index] ?? 0) ? truncate(cell, widths[index] ?? 0) : cell));
346
382
  }
383
+ function jsonText(value, replacer, { stream = process5.stdout, env = process5.env } = {}) {
384
+ return JSON.stringify(value, replacer, outputMode(stream, env) === "agent" ? undefined : 2);
385
+ }
347
386
  function printJson(value) {
348
- console.log(JSON.stringify(value, undefined, 2));
387
+ console.log(jsonText(value));
349
388
  }
350
389
 
351
390
  // ../kit/src/help.ts
352
- import process4 from "process";
391
+ import process6 from "process";
353
392
  var OPTIONAL = /\[[^\]]+\]/g;
354
393
  var USAGE_PREFIX = /^usage:\s*/i;
355
394
  var FULL_STOP = /\.$/;
356
- function usageLine(usage, colors = paint(process4.stdout)) {
395
+ function usageLine(usage, colors = paint(process6.stdout)) {
357
396
  return `${colors.bold("Usage:")} ${usage.replace(USAGE_PREFIX, "").replaceAll(OPTIONAL, (part) => colors.dim(part))}`;
358
397
  }
359
- function helpText(usage, options, colors = paint(process4.stdout), { examples = [], replaces, exits = [] } = {}) {
398
+ function helpText(usage, options, colors = paint(process6.stdout), { examples = [], replaces, exits = [] } = {}) {
360
399
  return [usageLine(usage, colors), ...replacesRows(replaces, colors), "", colors.bold("Options:"), ...optionRows(options, colors), ...exitRows(exits, colors), ...exampleRows(examples, colors)].join(`
361
400
  `);
362
401
  }
363
- function exitRows(exits, colors = paint(process4.stdout)) {
402
+ function exitRows(exits, colors = paint(process6.stdout)) {
364
403
  if (exits.length === 0)
365
404
  return [];
366
405
  const width = Math.max(...exits.map(([code]) => String(code).length));
367
406
  return ["", colors.bold("Exits:"), ...exits.map(([code, meaning]) => ` ${colors.cyan(String(code).padEnd(width))} ${meaning}`)];
368
407
  }
369
- function replacesRows(replaces, colors = paint(process4.stdout)) {
370
- return replaces === undefined ? [] : ["", `${colors.bold("Instead of:")} ${replaces}`];
408
+ function replacesRows(replaces, colors = paint(process6.stdout)) {
409
+ return replaces === undefined || outputMode() === "agent" ? [] : ["", `${colors.bold("Instead of:")} ${replaces}`];
371
410
  }
372
- function wantsHelpJson(env = process4.env) {
411
+ function wantsHelpJson(env = process6.env) {
373
412
  return env.KIT_HELP === "json";
374
413
  }
375
414
  function description(usage, { examples = [], replaces, exits } = {}, options) {
@@ -388,12 +427,12 @@ function description(usage, { examples = [], replaces, exits } = {}, options) {
388
427
  ...options === undefined ? {} : { options: Object.entries(options).map(([name, { short, description }]) => ({ name, ...short === undefined ? {} : { short }, ...description === undefined ? {} : { description } })) }
389
428
  };
390
429
  }
391
- function exampleRows(examples, colors = paint(process4.stdout)) {
392
- if (examples.length === 0)
430
+ function exampleRows(examples, colors = paint(process6.stdout)) {
431
+ if (examples.length === 0 || outputMode() === "agent")
393
432
  return [];
394
433
  return ["", colors.bold("Examples:"), ...examples.map((example) => typeof example === "string" ? ` ${example}` : ` ${example[0]} ${colors.dim(`# ${example[1]}`)}`)];
395
434
  }
396
- function optionRows(options, colors = paint(process4.stdout)) {
435
+ function optionRows(options, colors = paint(process6.stdout)) {
397
436
  const rows = Object.entries(options).map(([name, option]) => {
398
437
  const short = option.short === undefined ? " " : `${colors.cyan(`-${option.short}`)}, `;
399
438
  const named = option.choices === undefined ? "<value>" : `<${option.choices.join("|")}>`;
@@ -486,7 +525,7 @@ var OUTPUT_OPTIONS = {
486
525
  color: { type: "string", description: "auto (on a terminal alone), always or never" },
487
526
  plain: { type: "boolean", description: "plain text, no colour or spinner: --color never" }
488
527
  };
489
- function applyOutput(color, plain, env = process5.env) {
528
+ function applyOutput(color, plain, env = process7.env) {
490
529
  const when = plain === true ? "never" : color ?? "auto";
491
530
  if (when === "auto")
492
531
  return;
@@ -657,7 +696,7 @@ function nearestOption(message, options, allowNegative) {
657
696
  }
658
697
  // ../kit/src/process.ts
659
698
  import { constants } from "os";
660
- import process6 from "process";
699
+ import process8 from "process";
661
700
  var KILL_GRACE_MS = 2000;
662
701
  function exitCodeOf(code, signal) {
663
702
  if (code !== null)
@@ -677,7 +716,7 @@ async function runCommand(argv, { cwd, env, stdin = "ignore", timeoutMs, signal,
677
716
  return;
678
717
  try {
679
718
  if (grouped)
680
- process6.kill(-proc.pid, killing);
719
+ process8.kill(-proc.pid, killing);
681
720
  else
682
721
  proc.kill(killing);
683
722
  } catch {}
@@ -752,14 +791,14 @@ var RESET_STYLE = `${ESC}[0m`;
752
791
  var CLOSE_LINK = `${ESC}]8;;${ESC}\\`;
753
792
  // ../kit/src/cli.ts
754
793
  import { readFileSync, realpathSync } from "fs";
755
- import { dirname as dirname2, join as join2 } from "path";
756
- import process8 from "process";
794
+ import { dirname as dirname2, join as join3 } from "path";
795
+ import process10 from "process";
757
796
 
758
797
  // ../kit/src/usage.ts
759
798
  import { appendFileSync, mkdirSync } from "fs";
760
- import { homedir } from "os";
761
- import { dirname, isAbsolute, join } from "path";
762
- import process7 from "process";
799
+ import { homedir as homedir2 } from "os";
800
+ import { dirname, isAbsolute, join as join2 } from "path";
801
+ import process9 from "process";
763
802
 
764
803
  // ../kit/src/logs.ts
765
804
  var DAY_MS = 24 * 60 * 60 * 1000;
@@ -773,14 +812,14 @@ var usageError;
773
812
  function noteUsageError(message) {
774
813
  usageError = message;
775
814
  }
776
- function stateHome(env = process7.env) {
815
+ function stateHome(env = process9.env) {
777
816
  const dir = env.XDG_STATE_HOME;
778
- return dir !== undefined && isAbsolute(dir) ? dir : join(homedir(), ".local", "state");
817
+ return dir !== undefined && isAbsolute(dir) ? dir : join2(homedir2(), ".local", "state");
779
818
  }
780
- function usagePath(env = process7.env) {
781
- return join(stateHome(env), CLI_FOLDER, "usage.jsonl");
819
+ function usagePath(env = process9.env) {
820
+ return join2(stateHome(env), CLI_FOLDER, "usage.jsonl");
782
821
  }
783
- function usageRecorded(env = process7.env) {
822
+ function usageRecorded(env = process9.env) {
784
823
  const off = env.KAYNOOO_CLI_NO_USAGE;
785
824
  return env.NODE_ENV !== "test" && (off === undefined || off === "" || off === "0");
786
825
  }
@@ -794,7 +833,7 @@ function usageEntry(bin, command, argv, code, now = new Date) {
794
833
  usageError = undefined;
795
834
  return { at: now.toISOString(), bin, ...command === undefined ? {} : { command }, flags: flagNames(argv), code, ...unknown === undefined ? {} : { unknown } };
796
835
  }
797
- function recordUsage(entry, env = process7.env) {
836
+ function recordUsage(entry, env = process9.env) {
798
837
  if (!usageRecorded(env))
799
838
  return;
800
839
  try {
@@ -933,8 +972,8 @@ async function help2({ path, header, summary: lazySummary, commands, defaultComm
933
972
  `);
934
973
  }
935
974
  function printError(prefix, message, hint, cause) {
936
- const colors = paint(process8.stderr);
937
- const styled = takesControlCodes(process8.stderr);
975
+ const colors = paint(process10.stderr);
976
+ const styled = takesControlCodes(process10.stderr);
938
977
  const head = styled ? `${colors.red.bold("error")} ${colors.dim(`${prefix}:`)}` : `error: ${prefix}:`;
939
978
  const under = (label, text, tint) => text === undefined ? "" : `
940
979
  ${styled ? tint(label) : `${label}:`} ${text}`;
@@ -977,11 +1016,11 @@ async function dispatch(level, typed, { version, redact }) {
977
1016
  }
978
1017
  }
979
1018
  if (name === undefined && level.path.length > 1) {
980
- console.error(await help2(await expand(level, [...level.path.slice(1), ...argv]), process8.stderr));
1019
+ console.error(await help2(await expand(level, [...level.path.slice(1), ...argv]), process10.stderr));
981
1020
  return 2;
982
1021
  }
983
1022
  if (name === undefined || name === "help" || name === "--help" || name === "-h") {
984
- console.log(await help2(await expand(level, [...level.path.slice(1), ...argv]), process8.stdout));
1023
+ console.log(await help2(await expand(level, [...level.path.slice(1), ...argv]), process10.stdout));
985
1024
  return 0;
986
1025
  }
987
1026
  if (level.path.length === 1 && (name === "--version" || name === "-v")) {
@@ -1100,7 +1139,7 @@ async function printCandidates(found) {
1100
1139
  function packageVersion(entry = Bun.main) {
1101
1140
  for (let dir = dirname2(realpathSync(entry));; dir = dirname2(dir)) {
1102
1141
  try {
1103
- const { version } = JSON.parse(readFileSync(join2(dir, "package.json"), "utf8"));
1142
+ const { version } = JSON.parse(readFileSync(join3(dir, "package.json"), "utf8"));
1104
1143
  if (typeof version === "string")
1105
1144
  return version;
1106
1145
  } catch {}
@@ -1108,7 +1147,7 @@ function packageVersion(entry = Bun.main) {
1108
1147
  return "0.0.0";
1109
1148
  }
1110
1149
  }
1111
- async function runCli(cli, argv = process8.argv.slice(2)) {
1150
+ async function runCli(cli, argv = process10.argv.slice(2)) {
1112
1151
  const version = cli.version ?? packageVersion();
1113
1152
  if (argv[0] === COMPLETE)
1114
1153
  return printCandidates(candidates(scopeOf(cli.commands, cli.extraCommands, cli.aliases), cli.defaultCommand, argv.slice(1)));
@@ -1135,7 +1174,7 @@ function commandPath(scope, argv) {
1135
1174
  }
1136
1175
  return path.length === 0 ? undefined : path.join(" ");
1137
1176
  }
1138
- async function runMain({ name, version, run, redact, usage }, argv = process8.argv.slice(2)) {
1177
+ async function runMain({ name, version, run, redact, usage }, argv = process10.argv.slice(2)) {
1139
1178
  if (argv[0] === COMPLETE)
1140
1179
  return printCandidates(leafCandidates(run, argv.slice(1)));
1141
1180
  if (argv.length === 1 && (argv[0] === "--version" || argv[0] === "-v")) {
@@ -1161,14 +1200,61 @@ var SECRET_WORDS = new Set(["key", "pass", "pwd", "auth", "salt", "cookie", "ses
1161
1200
  var {Glob } = globalThis.Bun;
1162
1201
  var SESSION_FILES = new Glob("*.json");
1163
1202
  var ACCOUNT_DIRS = new Glob("*/sessions");
1203
+ // ../kit/src/workspace-root.ts
1204
+ import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
1205
+ import { homedir as homedir3 } from "os";
1206
+ import { dirname as dirname3, join as join4, resolve } from "path";
1207
+ import process11 from "process";
1208
+ function expandHome(path, home) {
1209
+ if (path === "~")
1210
+ return home;
1211
+ return path.startsWith("~/") ? join4(home, path.slice(2)) : path;
1212
+ }
1213
+ function configured(env, home) {
1214
+ const path = userConfigPath("config", { home, xdg: env.XDG_CONFIG_HOME });
1215
+ if (!existsSync(path))
1216
+ return;
1217
+ try {
1218
+ const parsed = JSON.parse(readFileSync2(path, "utf8"));
1219
+ const folder = typeof parsed === "object" && parsed !== null ? parsed.workspace : undefined;
1220
+ return typeof folder === "string" && folder !== "" ? folder : undefined;
1221
+ } catch {
1222
+ return;
1223
+ }
1224
+ }
1225
+ function workspaceRoot({ given, env = process11.env, home = homedir3() } = {}) {
1226
+ const fromEnv = env.KAYNOOO_WORKSPACE === "" ? undefined : env.KAYNOOO_WORKSPACE;
1227
+ return resolve(expandHome(given ?? fromEnv ?? configured(env, home) ?? join4(home, "Workspace"), home));
1228
+ }
1229
+ function workspaceChosen({ env = process11.env, home = homedir3() } = {}) {
1230
+ return (env.KAYNOOO_WORKSPACE ?? "") !== "" || configured(env, home) !== undefined;
1231
+ }
1232
+ function saveWorkspace(folder, { env = process11.env, home = homedir3() } = {}) {
1233
+ const path = userConfigPath("config", { home, xdg: env.XDG_CONFIG_HOME });
1234
+ let current = {};
1235
+ if (existsSync(path)) {
1236
+ try {
1237
+ const parsed = JSON.parse(readFileSync2(path, "utf8"));
1238
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
1239
+ return false;
1240
+ current = parsed;
1241
+ } catch {
1242
+ return false;
1243
+ }
1244
+ }
1245
+ mkdirSync2(dirname3(path), { recursive: true });
1246
+ writeFileSync(path, `${JSON.stringify({ ...current, workspace: folder }, undefined, 2)}
1247
+ `);
1248
+ return true;
1249
+ }
1164
1250
  // src/completions.ts
1165
- import { existsSync as existsSync2, mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync3, rmSync } from "fs";
1166
- import { join as join4 } from "path";
1167
- import process10 from "process";
1251
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3, readdirSync, readFileSync as readFileSync4, rmSync } from "fs";
1252
+ import { join as join6 } from "path";
1253
+ import process13 from "process";
1168
1254
 
1169
1255
  // src/checks.ts
1170
- import { existsSync, readFileSync as readFileSync2 } from "fs";
1171
- import { basename, join as join3 } from "path";
1256
+ import { existsSync as existsSync2, readFileSync as readFileSync3 } from "fs";
1257
+ import { basename, join as join5 } from "path";
1172
1258
 
1173
1259
  // src/catalog.ts
1174
1260
  var HOOKS_REQUIRES = "@kaynooo/cli-guard";
@@ -1244,7 +1330,7 @@ function readJsonText(text) {
1244
1330
  }
1245
1331
  }
1246
1332
  function readJson(path) {
1247
- return existsSync(path) ? readJsonText(readFileSync2(path, "utf8")) : undefined;
1333
+ return existsSync2(path) ? readJsonText(readFileSync3(path, "utf8")) : undefined;
1248
1334
  }
1249
1335
  function firstLine(text) {
1250
1336
  const trimmed = text.trim();
@@ -1253,7 +1339,7 @@ function firstLine(text) {
1253
1339
  return end === -1 ? trimmed : trimmed.slice(0, end);
1254
1340
  }
1255
1341
  function installed(host, name) {
1256
- return readJson(join3(host.globalModules, name, "package.json"));
1342
+ return readJson(join5(host.globalModules, name, "package.json"));
1257
1343
  }
1258
1344
  function isInstalled(host, name) {
1259
1345
  return installed(host, name)?.version !== undefined;
@@ -1261,7 +1347,7 @@ function isInstalled(host, name) {
1261
1347
  function ownBin(host, name, bin) {
1262
1348
  const declared = installed(host, name)?.bin;
1263
1349
  const file = typeof declared === "string" ? declared : declared?.[bin];
1264
- return file === undefined ? undefined : join3(host.globalModules, name, file);
1350
+ return file === undefined ? undefined : join5(host.globalModules, name, file);
1265
1351
  }
1266
1352
  async function checkTool(host, name, bins) {
1267
1353
  const check = name.slice(name.indexOf("/") + 1);
@@ -1276,7 +1362,7 @@ async function checkTool(host, name, bins) {
1276
1362
  if (latest !== "" && Bun.semver.order(version, latest) < 0)
1277
1363
  return { check, ok: false, detail: `${version}, ${latest} released${off.length > 0 ? `, ${off.join(", ")} not on the PATH` : ""}`, fix: `bun install -g ${name}@${latest}` };
1278
1364
  if (off.length > 0)
1279
- return { check, ok: false, detail: `${version}, ${off.join(", ")} not on the PATH`, fix: `add ${join3(host.globalModules, "..", "..", "..", "bin")} to the PATH` };
1365
+ return { check, ok: false, detail: `${version}, ${off.join(", ")} not on the PATH`, fix: `add ${join5(host.globalModules, "..", "..", "..", "bin")} to the PATH` };
1280
1366
  if (latest === "")
1281
1367
  return { check, ok: true, detail: `${version}, the registry did not answer: ${firstLine(info.stderr) || answered || "no version"}` };
1282
1368
  return { check, ok: true, detail: version };
@@ -1297,9 +1383,9 @@ async function checkTea(host) {
1297
1383
  return { check: "tea", ok: true, detail: `logged in to ${forge} as ${login.name}, Forgejo ${served}` };
1298
1384
  }
1299
1385
  function hooksWiring(host) {
1300
- const settings = readJson(join3(host.home, ".claude", "settings.json")) ?? {};
1386
+ const settings = readJson(join5(host.home, ".claude", "settings.json")) ?? {};
1301
1387
  const plugin = settings.enabledPlugins?.[PLUGIN] === true;
1302
- const mod = (settings.env?.CLAUDE_CODE_PLUGIN_DIRS ?? "").split(":").map((dir) => dir.replace(LEADING_TILDE, host.home)).find((dir) => basename(dir) === "cli" && existsSync(join3(dir, "hooks", "hooks.json")));
1388
+ const mod = (settings.env?.CLAUDE_CODE_PLUGIN_DIRS ?? "").split(":").map((dir) => dir.replace(LEADING_TILDE, host.home)).find((dir) => basename(dir) === "cli" && existsSync2(join5(dir, "hooks", "hooks.json")));
1303
1389
  return { plugin, mod };
1304
1390
  }
1305
1391
  function checkHooks(host) {
@@ -1337,8 +1423,15 @@ function checkNeeds(host) {
1337
1423
  return { check: "needs", ok: true, detail: "every tool the installed packages need is on the PATH" };
1338
1424
  }
1339
1425
  var toolCheck = (name) => name.slice(name.indexOf("/") + 1);
1340
- var OTHER_CHECKS = ["tea", "hooks", "needs", "bun", "completions"];
1426
+ var OTHER_CHECKS = ["tea", "hooks", "needs", "bun", "completions", "workspace"];
1341
1427
  var FORGEJO = "@kaynooo/cli-forgejo";
1428
+ var WORKSPACE = "@kaynooo/cli-workspace";
1429
+ function checkWorkspace(host, env = process.env) {
1430
+ const folder = workspaceRoot({ home: host.home, env });
1431
+ if (existsSync2(folder))
1432
+ return { check: "workspace", ok: true, detail: folder };
1433
+ return { check: "workspace", ok: false, detail: `${folder} does not exist`, fix: `mkdir -p ${folder}, or set "workspace" in ~/.config/kaynooo-cli/config.json to your folder` };
1434
+ }
1342
1435
  var CHECK_NAMES = [...Object.keys(TOOLS).map(toolCheck), ...OTHER_CHECKS];
1343
1436
  async function runChecks(host, only = []) {
1344
1437
  const wanted = (check) => only.length === 0 || only.includes(check);
@@ -1354,7 +1447,8 @@ async function runChecks(host, only = []) {
1354
1447
  ...wanted("hooks") && hooks ? [checkHooks(host)] : [],
1355
1448
  ...wanted("needs") ? [checkNeeds(host)] : [],
1356
1449
  ...wanted("bun") ? [checkBun(host)] : [],
1357
- ...wanted("completions") ? [checkCompletions(host)] : []
1450
+ ...wanted("completions") ? [checkCompletions(host)] : [],
1451
+ ...wanted("workspace") && (only.length > 0 || isInstalled(host, WORKSPACE) || isInstalled(host, "@kaynooo/cli-pkg")) ? [checkWorkspace(host)] : []
1358
1452
  ];
1359
1453
  }
1360
1454
 
@@ -1392,8 +1486,8 @@ ${BASH_FUNCTION}
1392
1486
  complete -F __kaynooo_cli_complete ${bins.join(" ")}
1393
1487
  `;
1394
1488
  }
1395
- function completionDir(home, env = process10.env) {
1396
- return join4(env.XDG_CACHE_HOME ?? join4(home, ".cache"), "fish", "kaynooo-cli");
1489
+ function completionDir(home, env = process13.env) {
1490
+ return join6(env.XDG_CACHE_HOME ?? join6(home, ".cache"), "fish", "kaynooo-cli");
1397
1491
  }
1398
1492
  function installedBins(host) {
1399
1493
  return Object.values(TOOLS).flat().filter((bin) => host.which(bin) !== null);
@@ -1406,19 +1500,19 @@ complete -c ${bin} -f -a '(__kaynooo_cli_complete ${bin})'
1406
1500
  }
1407
1501
  async function installCompletions(host, dir) {
1408
1502
  const bins = installedBins(host);
1409
- mkdirSync2(dir, { recursive: true });
1410
- const removed = readdirSync(dir).filter((name) => FISH_FILE.test(name) && !bins.includes(name.replace(FISH_FILE, "")) && readFileSync3(join4(dir, name), "utf8").startsWith(MARKER));
1503
+ mkdirSync3(dir, { recursive: true });
1504
+ const removed = readdirSync(dir).filter((name) => FISH_FILE.test(name) && !bins.includes(name.replace(FISH_FILE, "")) && readFileSync4(join6(dir, name), "utf8").startsWith(MARKER));
1411
1505
  for (const name of removed)
1412
- rmSync(join4(dir, name));
1413
- await Promise.all(bins.map(async (bin) => Bun.write(join4(dir, `${bin}.fish`), completionFile(bin))));
1506
+ rmSync(join6(dir, name));
1507
+ await Promise.all(bins.map(async (bin) => Bun.write(join6(dir, `${bin}.fish`), completionFile(bin))));
1414
1508
  return { written: bins, removed: removed.map((name) => name.replace(FISH_FILE, "")) };
1415
1509
  }
1416
1510
  function missingCompletions(host, dir) {
1417
- return installedBins(host).filter((bin) => !existsSync2(join4(dir, `${bin}.fish`)));
1511
+ return installedBins(host).filter((bin) => !existsSync3(join6(dir, `${bin}.fish`)));
1418
1512
  }
1419
1513
 
1420
1514
  // src/doctor.ts
1421
- import process14 from "process";
1515
+ import process17 from "process";
1422
1516
 
1423
1517
  // src/fix.ts
1424
1518
  var INSTALL = /^bun install -g (\S+)$/;
@@ -1458,19 +1552,19 @@ async function applyFixes(host, steps, dry) {
1458
1552
  }
1459
1553
 
1460
1554
  // src/host.ts
1461
- import { homedir as homedir2 } from "os";
1462
- import { join as join5 } from "path";
1463
- import process11 from "process";
1555
+ import { homedir as homedir4 } from "os";
1556
+ import { join as join7 } from "path";
1557
+ import process14 from "process";
1464
1558
  var RUN_TIMEOUT_MS = 20000;
1465
1559
  function hostHere() {
1466
- const home = homedir2();
1560
+ const home = homedir4();
1467
1561
  return {
1468
1562
  home,
1469
- globalModules: join5(process11.env.BUN_INSTALL ?? join5(home, ".bun"), "install", "global", "node_modules"),
1563
+ globalModules: join7(process14.env.BUN_INSTALL ?? join7(home, ".bun"), "install", "global", "node_modules"),
1470
1564
  bunVersion: Bun.version,
1471
1565
  which: (bin) => Bun.which(bin),
1472
1566
  run: async (argv, env) => {
1473
- const ran = await runCommand(argv, { timeoutMs: RUN_TIMEOUT_MS, ...env === undefined ? {} : { env: { ...process11.env, ...env } } });
1567
+ const ran = await runCommand(argv, { timeoutMs: RUN_TIMEOUT_MS, ...env === undefined ? {} : { env: { ...process14.env, ...env } } });
1474
1568
  if (ran.timedOut)
1475
1569
  return { code: 1, stdout: "", stderr: `timed out after ${RUN_TIMEOUT_MS / 1000}s` };
1476
1570
  const of = (stream) => Buffer.concat(ran.chunks.filter((chunk) => chunk.stream === stream).map((chunk) => chunk.data)).toString();
@@ -1480,10 +1574,10 @@ function hostHere() {
1480
1574
  }
1481
1575
 
1482
1576
  // src/install.ts
1483
- import process13 from "process";
1577
+ import process16 from "process";
1484
1578
 
1485
1579
  // ../kit/src/prompt.ts
1486
- import process12 from "process";
1580
+ import process15 from "process";
1487
1581
  var KEY_SEQUENCES = /\x1B\[[0-9;]*[A-Z~]|\x1BO[A-Z]|[\s\S]/giu;
1488
1582
  var CONTROL = /^[\x00-\x1F\x7F]/;
1489
1583
  var WORDS = /\s+/;
@@ -1521,6 +1615,52 @@ async function checklist(question, choices, { initial = new Set, min = 0, toggle
1521
1615
  });
1522
1616
  return accepted ? checked : undefined;
1523
1617
  }
1618
+ async function confirm(question, { default: fallback, ...settings } = {}) {
1619
+ let answered = fallback;
1620
+ const choices = { true: "Y/n", false: "y/N", undefined: "y/n" }[String(fallback)];
1621
+ const accepted = await ask(question, settings, (colors, state) => [state === "asking" ? asking(colors, question, "", choices) : answer(colors, question, state, answered ? "yes" : "no")], (key) => {
1622
+ if (key === "cancel")
1623
+ return "cancel";
1624
+ if (key === "enter")
1625
+ return answered === undefined ? undefined : "enter";
1626
+ const letter = typeof key === "object" ? key.text.toLowerCase() : "";
1627
+ if (letter !== "y" && letter !== "n")
1628
+ return;
1629
+ answered = letter === "y";
1630
+ return "enter";
1631
+ });
1632
+ return accepted ? answered : undefined;
1633
+ }
1634
+ async function input2(question, { default: fallback, mask = false, validate, ...settings } = {}) {
1635
+ let value = "";
1636
+ let invalid;
1637
+ const taken = () => value === "" ? fallback ?? "" : value;
1638
+ const shown = () => mask ? "*".repeat([...taken()].length) : taken();
1639
+ const accepted = await ask(question, settings, (colors, state, { columns }) => {
1640
+ if (state !== "asking")
1641
+ return [answer(colors, question, state, shown())];
1642
+ const characters = [...shown()];
1643
+ const room = Math.max(columns - question.length - 5, 1);
1644
+ const tail = characters.length > room ? `\u2026${characters.slice(1 - room).join("")}` : shown();
1645
+ const typed = value === "" ? colors.dim(tail) : tail;
1646
+ return [`${colors.cyan("?")} ${question} ${typed}${colors.dim("\u2588")}`, ...invalid === undefined ? [] : [` ${colors.red(invalid)}`]];
1647
+ }, (key) => {
1648
+ if (key === "cancel")
1649
+ return "cancel";
1650
+ invalid = undefined;
1651
+ if (key === "enter") {
1652
+ invalid = validate?.(taken());
1653
+ return invalid === undefined ? "enter" : undefined;
1654
+ }
1655
+ if (key === "erase")
1656
+ value = [...value].slice(0, -1).join("");
1657
+ else if (typeof key === "object")
1658
+ value += key.text;
1659
+ return;
1660
+ });
1661
+ return accepted ? taken() : undefined;
1662
+ }
1663
+
1524
1664
  class List {
1525
1665
  choices;
1526
1666
  query = "";
@@ -1579,9 +1719,9 @@ function asking(colors, question, typed, keys) {
1579
1719
  function answer(colors, question, state, picked) {
1580
1720
  return state === "enter" ? `${colors.green("\u2713")} ${question} ${colors.cyan(picked)}` : `${colors.red("\u2717")} ${question} ${colors.dim("cancelled")}`;
1581
1721
  }
1582
- async function ask(question, { hint, terminal = process12 }, render, press) {
1722
+ async function ask(question, { hint, terminal = process15 }, render, press) {
1583
1723
  const { stdin, stderr } = terminal;
1584
- if (stdin.isTTY !== true || stderr.isTTY !== true)
1724
+ if (!isInteractive(terminal))
1585
1725
  fail(`cannot ask "${question}" off a terminal`, 2, hint);
1586
1726
  const colors = paint(stderr);
1587
1727
  let drawn = 0;
@@ -1631,18 +1771,19 @@ async function ask(question, { hint, terminal = process12 }, render, press) {
1631
1771
 
1632
1772
  // src/install.ts
1633
1773
  var HOOKS = "hooks";
1774
+ var PLUGIN_UNINSTALL = ["claude", "plugin", "uninstall", "cli-hooks"];
1634
1775
  var PLUGIN_INSTALL = ["claude", "plugin", "install", "cli-hooks", "--marketplace", `https://${FORGE}/kaynooo/cli.git`];
1635
1776
  var CLI_PREFIX = /^cli-/;
1636
1777
  var shortName = (name) => name.slice(name.indexOf("/") + 1).replace(CLI_PREFIX, "");
1637
1778
  function candidates2(host) {
1638
- const packages = Object.entries(TOOLS).filter(([name]) => !isInstalled(host, name)).map(([name, bins]) => {
1779
+ const packages = Object.entries(TOOLS).map(([name, bins]) => {
1639
1780
  const needs = (NEEDS[name] ?? []).map((need) => need.bin);
1640
- return { id: name, names: [shortName(name), name, `cli-${shortName(name)}`, ...bins], label: shortName(name), hint: `${bins.join(" ")}: ${ABOUT[name] ?? ""}${needs.length === 0 ? "" : `, needs ${needs.join(", ")}`}`, packages: [name] };
1781
+ return { id: name, names: [shortName(name), name, `cli-${shortName(name)}`, ...bins], label: shortName(name), hint: `${bins.join(" ")}: ${ABOUT[name] ?? ""}${needs.length === 0 ? "" : `, needs ${needs.join(", ")}`}`, packages: [name], installed: isInstalled(host, name) };
1641
1782
  });
1642
1783
  const { plugin, mod } = hooksWiring(host);
1643
- if (plugin || mod !== undefined)
1784
+ if (mod !== undefined && !plugin)
1644
1785
  return packages;
1645
- return [...packages, { id: HOOKS, names: [HOOKS, "plugin", "cli-hooks"], label: "hooks", hint: `the Claude Code plugin running guard on every tool call, needs ${HOOKS_REQUIRES}`, packages: isInstalled(host, HOOKS_REQUIRES) ? [] : [HOOKS_REQUIRES] }];
1786
+ return [...packages, { id: HOOKS, names: [HOOKS, "plugin", "cli-hooks"], label: "hooks", hint: `the Claude Code plugin running guard on every tool call, needs ${HOOKS_REQUIRES}`, packages: isInstalled(host, HOOKS_REQUIRES) ? [] : [HOOKS_REQUIRES], installed: plugin }];
1646
1787
  }
1647
1788
  function choose(pool, names) {
1648
1789
  const chosen = new Set;
@@ -1679,16 +1820,33 @@ function plan(host, picked, force) {
1679
1820
  function checksFor(packages, plugin) {
1680
1821
  return [...packages.map((name) => name.slice(name.indexOf("/") + 1)), ...plugin ? ["hooks"] : [], "needs", "completions", ...packages.includes("@kaynooo/cli-forgejo") ? ["tea"] : []];
1681
1822
  }
1682
- async function ask2(pool) {
1683
- if (process13.stdin.isTTY !== true || process13.stderr.isTTY !== true)
1823
+ async function ask2(all) {
1824
+ if (!isInteractive())
1684
1825
  badUsage("name what to install, or all: doctor --install forgejo check hooks (doctor --install --list shows the choices)");
1685
- const choices = pool.map((each) => ({ value: each, label: each.label, hint: each.hint }));
1686
- const picked = await checklist("install which?", choices, { hint: "name them instead: doctor --install forgejo check" });
1687
- return picked === undefined ? [] : [...picked];
1826
+ const choices = all.map((each) => ({ value: each, label: each.label, hint: `${each.installed ? "installed, " : ""}${each.hint}` }));
1827
+ const picked = await checklist("installed packages", choices, { initial: new Set(all.filter((each) => each.installed)), hint: "check to install, uncheck to remove: doctor --install forgejo check names them" });
1828
+ if (picked === undefined)
1829
+ return;
1830
+ const remove = all.filter((each) => !picked.has(each) && each.installed);
1831
+ if (remove.length > 0 && await confirm(`remove ${remove.map((each) => each.label).join(", ")}?`, { default: false }) !== true)
1832
+ return { add: all.filter((each) => picked.has(each) && !each.installed), remove: [] };
1833
+ return { add: all.filter((each) => picked.has(each) && !each.installed), remove };
1834
+ }
1835
+ function removal(all, remove) {
1836
+ const keepsGuard = all.some((each) => each.id === HOOKS && each.installed && !remove.includes(each));
1837
+ return { packages: [...new Set(remove.flatMap((each) => each.id === HOOKS ? [] : each.packages))].filter((name) => !(keepsGuard && name === HOOKS_REQUIRES)), plugin: remove.some((each) => each.id === HOOKS) };
1688
1838
  }
1689
- async function install(host, options, recheck) {
1690
- const pool = candidates2(host);
1691
- const { dim, green, red, yellow } = paint(process13.stdout);
1839
+ async function askWorkspace(host) {
1840
+ if (!isInteractive() || workspaceChosen({ home: host.home }))
1841
+ return;
1842
+ const folder = await input2("folder holding your repositories (repos, sweep and pkg bump look there)", { default: "~/Workspace" });
1843
+ if (folder !== undefined && folder !== "" && !saveWorkspace(folder, { home: host.home }))
1844
+ console.error(`could not write the workspace into ${userConfigPath("config", { home: host.home })}: it does not parse, fix it by hand`);
1845
+ }
1846
+ async function install(host, options, recheck, choosing = ask2) {
1847
+ const all = candidates2(host);
1848
+ const pool = all.filter((each) => !each.installed);
1849
+ const { dim, green, red, yellow } = paint(process16.stdout);
1692
1850
  if (options.list) {
1693
1851
  for (const each of pool)
1694
1852
  console.log(`${each.label.padEnd(12)} ${dim(each.hint)}`);
@@ -1696,20 +1854,23 @@ async function install(host, options, recheck) {
1696
1854
  console.log("everything is installed");
1697
1855
  return 0;
1698
1856
  }
1699
- if (pool.length === 0) {
1857
+ if (pool.length === 0 && options.names.length > 0) {
1700
1858
  console.log("everything is installed");
1701
1859
  return 0;
1702
1860
  }
1703
- const unknown = options.names.filter((name) => name !== "all" && !Object.values(candidatesAll(host)).some((each) => each.names.includes(name)));
1861
+ const unknown = options.names.filter((name) => name !== "all" && !all.some((each) => each.names.includes(name)));
1704
1862
  if (unknown.length > 0)
1705
1863
  badUsage(`nothing to install is named ${unknown.join(", ")}: doctor --install --list`);
1706
- const picked = options.names.length === 0 ? await ask2(pool) : choose(pool, options.names);
1864
+ const chosen = options.names.length === 0 ? await choosing(all) : { add: choose(pool, options.names), remove: [] };
1865
+ const picked = chosen?.add ?? [];
1707
1866
  const skipped = options.names.filter((name) => name !== "all" && !pool.some((each) => each.names.includes(name)));
1708
1867
  for (const name of skipped)
1709
1868
  console.log(`${dim("already")} ${name} is installed`);
1710
- if (picked.length === 0) {
1869
+ const removing = removal(all, chosen?.remove ?? []);
1870
+ const removes = removing.packages.length > 0 || removing.plugin;
1871
+ if (picked.length === 0 && !removes) {
1711
1872
  if (skipped.length === 0)
1712
- console.log("nothing picked");
1873
+ console.log("nothing changed");
1713
1874
  return 0;
1714
1875
  }
1715
1876
  const planned = plan(host, picked, options.force);
@@ -1717,13 +1878,20 @@ async function install(host, options, recheck) {
1717
1878
  console.log(`${red("blocked")} ${each.package} needs ${each.need} (${each.why}): ${each.how}; --force installs it anyway`);
1718
1879
  for (const each of planned.warnings)
1719
1880
  console.log(`${yellow("warn ")} ${each.package}: ${each.need} is missing, ${each.why}`);
1720
- if (planned.packages.length === 0 && !planned.plugin)
1881
+ if (planned.packages.length === 0 && !planned.plugin && !removes)
1721
1882
  return 1;
1883
+ if (!options.dryRun && planned.packages.some((name) => name === "@kaynooo/cli-workspace" || name === "@kaynooo/cli-pkg"))
1884
+ await askWorkspace(host);
1722
1885
  const command = ["bun", "install", "-g", ...planned.packages];
1723
1886
  if (planned.packages.length > 0)
1724
1887
  console.log(`${options.dryRun ? dim("would ") : green("install")} ${command.join(" ")}`);
1725
1888
  if (planned.plugin)
1726
1889
  console.log(`${options.dryRun ? dim("would ") : green("install")} ${PLUGIN_INSTALL.join(" ")}`);
1890
+ const uninstall = ["bun", "remove", "-g", ...removing.packages];
1891
+ if (removing.packages.length > 0)
1892
+ console.log(`${options.dryRun ? dim("would ") : green("remove ")} ${uninstall.join(" ")}`);
1893
+ if (removing.plugin)
1894
+ console.log(`${options.dryRun ? dim("would ") : green("remove ")} ${PLUGIN_UNINSTALL.join(" ")}`);
1727
1895
  if (options.dryRun)
1728
1896
  return planned.blocked.length > 0 ? 1 : 0;
1729
1897
  if (planned.packages.length > 0) {
@@ -1742,16 +1910,23 @@ async function install(host, options, recheck) {
1742
1910
  console.error(`${red("failed")} ${PLUGIN_INSTALL.join(" ")}: exit ${ran.code}: ${firstLine(ran.stderr || ran.stdout)}`);
1743
1911
  }
1744
1912
  }
1913
+ if (removing.packages.length > 0) {
1914
+ const ran = await host.run(uninstall);
1915
+ if (ran.code !== 0) {
1916
+ console.error(`${red("failed")} bun remove -g: exit ${ran.code}: ${firstLine(ran.stderr || ran.stdout)}`);
1917
+ return 1;
1918
+ }
1919
+ }
1920
+ if (removing.plugin && host.which("claude") !== null) {
1921
+ const ran = await host.run(PLUGIN_UNINSTALL);
1922
+ if (ran.code !== 0)
1923
+ console.error(`${red("failed")} ${PLUGIN_UNINSTALL.join(" ")}: exit ${ran.code}: ${firstLine(ran.stderr || ran.stdout)}`);
1924
+ }
1745
1925
  console.log(`
1746
1926
  after the install:`);
1747
1927
  const code = await recheck(checksFor(planned.packages, planned.plugin));
1748
1928
  return planned.blocked.length > 0 ? 1 : code;
1749
1929
  }
1750
- function candidatesAll(host) {
1751
- const open = candidates2(host);
1752
- const done = Object.entries(TOOLS).filter(([name]) => isInstalled(host, name)).map(([name, bins]) => ({ id: name, names: [shortName(name), name, `cli-${shortName(name)}`, ...bins], label: shortName(name), hint: "", packages: [name] }));
1753
- return [...open, ...done];
1754
- }
1755
1930
 
1756
1931
  // src/doctor.ts
1757
1932
  var USAGE = `doctor [--only <check>] [--quiet] [--fix | --install [<name>...|all]] [--list] [--dry-run] [--force] [--json]: checks this host's @kaynooo/cli-* install, one line per check, the command that fixes a failing one beside it, and installs the packages it picks
@@ -1768,14 +1943,14 @@ var OPTIONS = {
1768
1943
  quiet: { type: "boolean", short: "q", description: "print only the failing checks" },
1769
1944
  fix: { type: "boolean", description: "run the fix: lines doctor may run, then check again" },
1770
1945
  "dry-run": { type: "boolean", description: "with --fix or --install, show what would run without running it (alone, it implies --fix)" },
1771
- install: { type: "boolean", description: "install the packages named, or picked from a checklist: forgejo, ft, hooks, all" },
1946
+ install: { type: "boolean", description: "install the packages named, or managed from a checklist, installed ones checked and unchecking removing them: forgejo, ft, hooks, all" },
1772
1947
  list: { type: "boolean", description: "with --install, print the choices" },
1773
1948
  force: { type: "boolean", description: "with --install, install a package even though a tool it requires is missing" }
1774
1949
  };
1775
1950
  function print(results) {
1776
1951
  if (results.length === 0)
1777
1952
  return;
1778
- const { green, red, dim } = paint(process14.stdout);
1953
+ const { green, red, dim } = paint(process17.stdout);
1779
1954
  const width = Math.max(...results.map((result) => result.check.length));
1780
1955
  for (const { check, ok, detail, fix } of results)
1781
1956
  console.log(`${ok ? green("ok ") : red("FAIL")} ${check.padEnd(width)} ${detail}${fix === undefined ? "" : ` ${dim("fix:")} ${fix}`}`);
@@ -1785,13 +1960,13 @@ function print(results) {
1785
1960
  ${dim("fix them all:")} ${INSTALL2}${installs.join(" ")}`);
1786
1961
  }
1787
1962
  function printOutcomes(outcomes, log) {
1788
- const { green, red, dim } = paint(process14.stdout);
1963
+ const { green, red, dim } = paint(process17.stdout);
1789
1964
  const marks = { ran: green("ran "), failed: red("failed "), "dry-run": dim("would "), "not run": dim("not run") };
1790
1965
  for (const { command, status, detail } of outcomes)
1791
1966
  log(`${marks[status]} ${command}${detail === undefined ? "" : ` ${dim(detail)}`}`);
1792
1967
  }
1793
1968
  async function main(args, host = hostHere()) {
1794
- const { values, positionals } = parse(args, USAGE, OPTIONS, { positionals: ["[name...]"], 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"], ["doctor --install", "pick the packages to add"], ["doctor --install forgejo check hooks", "three named ones"], ["doctor --install all --dry-run", "what installing everything would run"]] });
1969
+ const { values, positionals } = parse(args, USAGE, OPTIONS, { positionals: ["[name...]"], 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"], ["doctor --install", "check to install, uncheck to remove"], ["doctor --install forgejo check hooks", "three named ones"], ["doctor --install all --dry-run", "what installing everything would run"]] });
1795
1970
  const only = values.only ?? [];
1796
1971
  if (values.install !== true && (positionals.length > 0 || values.list === true || values.force === true))
1797
1972
  badUsage("names, --list and --force go with --install");
@@ -1825,7 +2000,7 @@ after the fixes:`);
1825
2000
  const absent = Object.keys(TOOLS).filter((name) => !isInstalled(host, name)).length;
1826
2001
  if (!json && only.length === 0 && absent > 0)
1827
2002
  console.log(`
1828
- ${paint(process14.stdout).dim(`${absent} packages not installed: doctor --install`)}`);
2003
+ ${paint(process17.stdout).dim(`${absent} packages not installed: doctor --install`)}`);
1829
2004
  return results.every((result) => result.ok) ? 0 : 1;
1830
2005
  }
1831
2006
 
@@ -1854,7 +2029,7 @@ var LIST_OPTIONS = {
1854
2029
  json: jsonOption("[{bin, package, installed, synopsis?, summary?, replaces?, examples?}]")
1855
2030
  };
1856
2031
  function print2(listed) {
1857
- const { cyan, dim } = paint(process15.stdout);
2032
+ const { cyan, dim } = paint(process18.stdout);
1858
2033
  const width = Math.max(...listed.map((each) => each.bin.length));
1859
2034
  for (const each of listed) {
1860
2035
  const what = !each.installed ? dim(`not installed: bun install -g ${each.package}`) : `${each.summary ?? ""}${each.replaces === undefined ? "" : dim(`; instead of ${each.replaces}`)}`;
@@ -1937,7 +2112,7 @@ ${table(summary.usageErrors.map((each) => [`${each.bin}${each.command === undefi
1937
2112
  return 0;
1938
2113
  }
1939
2114
  if (import.meta.main) {
1940
- process15.exitCode = await runCli({
2115
+ process18.exitCode = await runCli({
1941
2116
  name: "cli",
1942
2117
  about: "the bins of the @kaynooo/cli-* packages, described by their own help",
1943
2118
  replaces: "running each bin's --help to find the one that fits",