@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.
- package/dist/cli-bin.js +277 -102
- package/dist/cli.js +276 -101
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -2,16 +2,37 @@
|
|
|
2
2
|
// @bun
|
|
3
3
|
|
|
4
4
|
// src/cli.ts
|
|
5
|
-
import
|
|
5
|
+
import process18 from "process";
|
|
6
6
|
// ../kit/src/args.ts
|
|
7
|
-
import
|
|
7
|
+
import process7 from "process";
|
|
8
8
|
import { parseArgs } from "util";
|
|
9
9
|
|
|
10
10
|
// ../kit/src/format.ts
|
|
11
|
-
import
|
|
11
|
+
import process5 from "process";
|
|
12
12
|
|
|
13
|
-
// ../kit/src/
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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(
|
|
387
|
+
console.log(jsonText(value));
|
|
349
388
|
}
|
|
350
389
|
|
|
351
390
|
// ../kit/src/help.ts
|
|
352
|
-
import
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 =
|
|
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(
|
|
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(
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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
|
|
756
|
-
import
|
|
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
|
|
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 =
|
|
815
|
+
function stateHome(env = process9.env) {
|
|
777
816
|
const dir = env.XDG_STATE_HOME;
|
|
778
|
-
return dir !== undefined && isAbsolute(dir) ? dir :
|
|
817
|
+
return dir !== undefined && isAbsolute(dir) ? dir : join2(homedir2(), ".local", "state");
|
|
779
818
|
}
|
|
780
|
-
function usagePath(env =
|
|
781
|
-
return
|
|
819
|
+
function usagePath(env = process9.env) {
|
|
820
|
+
return join2(stateHome(env), CLI_FOLDER, "usage.jsonl");
|
|
782
821
|
}
|
|
783
|
-
function usageRecorded(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 =
|
|
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(
|
|
937
|
-
const styled = takesControlCodes(
|
|
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]),
|
|
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]),
|
|
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(
|
|
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 =
|
|
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 =
|
|
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,12 +1200,59 @@ 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/doctor.ts
|
|
1165
|
-
import
|
|
1251
|
+
import process17 from "process";
|
|
1166
1252
|
|
|
1167
1253
|
// src/checks.ts
|
|
1168
|
-
import { existsSync as
|
|
1169
|
-
import { basename, join as
|
|
1254
|
+
import { existsSync as existsSync3, readFileSync as readFileSync4 } from "fs";
|
|
1255
|
+
import { basename, join as join6 } from "path";
|
|
1170
1256
|
|
|
1171
1257
|
// src/catalog.ts
|
|
1172
1258
|
var HOOKS_REQUIRES = "@kaynooo/cli-guard";
|
|
@@ -1202,9 +1288,9 @@ var NEEDS = {
|
|
|
1202
1288
|
};
|
|
1203
1289
|
|
|
1204
1290
|
// src/completions.ts
|
|
1205
|
-
import { existsSync, mkdirSync as
|
|
1206
|
-
import { join as
|
|
1207
|
-
import
|
|
1291
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync3, readdirSync, readFileSync as readFileSync3, rmSync } from "fs";
|
|
1292
|
+
import { join as join5 } from "path";
|
|
1293
|
+
import process13 from "process";
|
|
1208
1294
|
var MARKER = "# Generated by `cli completions fish`";
|
|
1209
1295
|
var FISH_FILE = /\.fish$/;
|
|
1210
1296
|
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'
|
|
@@ -1238,8 +1324,8 @@ ${BASH_FUNCTION}
|
|
|
1238
1324
|
complete -F __kaynooo_cli_complete ${bins.join(" ")}
|
|
1239
1325
|
`;
|
|
1240
1326
|
}
|
|
1241
|
-
function completionDir(home, env =
|
|
1242
|
-
return
|
|
1327
|
+
function completionDir(home, env = process13.env) {
|
|
1328
|
+
return join5(env.XDG_CACHE_HOME ?? join5(home, ".cache"), "fish", "kaynooo-cli");
|
|
1243
1329
|
}
|
|
1244
1330
|
function installedBins(host) {
|
|
1245
1331
|
return Object.values(TOOLS).flat().filter((bin) => host.which(bin) !== null);
|
|
@@ -1252,15 +1338,15 @@ complete -c ${bin} -f -a '(__kaynooo_cli_complete ${bin})'
|
|
|
1252
1338
|
}
|
|
1253
1339
|
async function installCompletions(host, dir) {
|
|
1254
1340
|
const bins = installedBins(host);
|
|
1255
|
-
|
|
1256
|
-
const removed = readdirSync(dir).filter((name) => FISH_FILE.test(name) && !bins.includes(name.replace(FISH_FILE, "")) &&
|
|
1341
|
+
mkdirSync3(dir, { recursive: true });
|
|
1342
|
+
const removed = readdirSync(dir).filter((name) => FISH_FILE.test(name) && !bins.includes(name.replace(FISH_FILE, "")) && readFileSync3(join5(dir, name), "utf8").startsWith(MARKER));
|
|
1257
1343
|
for (const name of removed)
|
|
1258
|
-
rmSync(
|
|
1259
|
-
await Promise.all(bins.map(async (bin) => Bun.write(
|
|
1344
|
+
rmSync(join5(dir, name));
|
|
1345
|
+
await Promise.all(bins.map(async (bin) => Bun.write(join5(dir, `${bin}.fish`), completionFile(bin))));
|
|
1260
1346
|
return { written: bins, removed: removed.map((name) => name.replace(FISH_FILE, "")) };
|
|
1261
1347
|
}
|
|
1262
1348
|
function missingCompletions(host, dir) {
|
|
1263
|
-
return installedBins(host).filter((bin) => !
|
|
1349
|
+
return installedBins(host).filter((bin) => !existsSync2(join5(dir, `${bin}.fish`)));
|
|
1264
1350
|
}
|
|
1265
1351
|
|
|
1266
1352
|
// src/checks.ts
|
|
@@ -1304,7 +1390,7 @@ function readJsonText(text) {
|
|
|
1304
1390
|
}
|
|
1305
1391
|
}
|
|
1306
1392
|
function readJson(path) {
|
|
1307
|
-
return
|
|
1393
|
+
return existsSync3(path) ? readJsonText(readFileSync4(path, "utf8")) : undefined;
|
|
1308
1394
|
}
|
|
1309
1395
|
function firstLine(text) {
|
|
1310
1396
|
const trimmed = text.trim();
|
|
@@ -1313,7 +1399,7 @@ function firstLine(text) {
|
|
|
1313
1399
|
return end === -1 ? trimmed : trimmed.slice(0, end);
|
|
1314
1400
|
}
|
|
1315
1401
|
function installed(host, name) {
|
|
1316
|
-
return readJson(
|
|
1402
|
+
return readJson(join6(host.globalModules, name, "package.json"));
|
|
1317
1403
|
}
|
|
1318
1404
|
function isInstalled(host, name) {
|
|
1319
1405
|
return installed(host, name)?.version !== undefined;
|
|
@@ -1321,7 +1407,7 @@ function isInstalled(host, name) {
|
|
|
1321
1407
|
function ownBin(host, name, bin) {
|
|
1322
1408
|
const declared = installed(host, name)?.bin;
|
|
1323
1409
|
const file = typeof declared === "string" ? declared : declared?.[bin];
|
|
1324
|
-
return file === undefined ? undefined :
|
|
1410
|
+
return file === undefined ? undefined : join6(host.globalModules, name, file);
|
|
1325
1411
|
}
|
|
1326
1412
|
async function checkTool(host, name, bins) {
|
|
1327
1413
|
const check = name.slice(name.indexOf("/") + 1);
|
|
@@ -1336,7 +1422,7 @@ async function checkTool(host, name, bins) {
|
|
|
1336
1422
|
if (latest !== "" && Bun.semver.order(version, latest) < 0)
|
|
1337
1423
|
return { check, ok: false, detail: `${version}, ${latest} released${off.length > 0 ? `, ${off.join(", ")} not on the PATH` : ""}`, fix: `bun install -g ${name}@${latest}` };
|
|
1338
1424
|
if (off.length > 0)
|
|
1339
|
-
return { check, ok: false, detail: `${version}, ${off.join(", ")} not on the PATH`, fix: `add ${
|
|
1425
|
+
return { check, ok: false, detail: `${version}, ${off.join(", ")} not on the PATH`, fix: `add ${join6(host.globalModules, "..", "..", "..", "bin")} to the PATH` };
|
|
1340
1426
|
if (latest === "")
|
|
1341
1427
|
return { check, ok: true, detail: `${version}, the registry did not answer: ${firstLine(info.stderr) || answered || "no version"}` };
|
|
1342
1428
|
return { check, ok: true, detail: version };
|
|
@@ -1357,9 +1443,9 @@ async function checkTea(host) {
|
|
|
1357
1443
|
return { check: "tea", ok: true, detail: `logged in to ${forge} as ${login.name}, Forgejo ${served}` };
|
|
1358
1444
|
}
|
|
1359
1445
|
function hooksWiring(host) {
|
|
1360
|
-
const settings = readJson(
|
|
1446
|
+
const settings = readJson(join6(host.home, ".claude", "settings.json")) ?? {};
|
|
1361
1447
|
const plugin = settings.enabledPlugins?.[PLUGIN] === true;
|
|
1362
|
-
const mod = (settings.env?.CLAUDE_CODE_PLUGIN_DIRS ?? "").split(":").map((dir) => dir.replace(LEADING_TILDE, host.home)).find((dir) => basename(dir) === "cli" &&
|
|
1448
|
+
const mod = (settings.env?.CLAUDE_CODE_PLUGIN_DIRS ?? "").split(":").map((dir) => dir.replace(LEADING_TILDE, host.home)).find((dir) => basename(dir) === "cli" && existsSync3(join6(dir, "hooks", "hooks.json")));
|
|
1363
1449
|
return { plugin, mod };
|
|
1364
1450
|
}
|
|
1365
1451
|
function checkHooks(host) {
|
|
@@ -1397,8 +1483,15 @@ function checkNeeds(host) {
|
|
|
1397
1483
|
return { check: "needs", ok: true, detail: "every tool the installed packages need is on the PATH" };
|
|
1398
1484
|
}
|
|
1399
1485
|
var toolCheck = (name) => name.slice(name.indexOf("/") + 1);
|
|
1400
|
-
var OTHER_CHECKS = ["tea", "hooks", "needs", "bun", "completions"];
|
|
1486
|
+
var OTHER_CHECKS = ["tea", "hooks", "needs", "bun", "completions", "workspace"];
|
|
1401
1487
|
var FORGEJO = "@kaynooo/cli-forgejo";
|
|
1488
|
+
var WORKSPACE = "@kaynooo/cli-workspace";
|
|
1489
|
+
function checkWorkspace(host, env = process.env) {
|
|
1490
|
+
const folder = workspaceRoot({ home: host.home, env });
|
|
1491
|
+
if (existsSync3(folder))
|
|
1492
|
+
return { check: "workspace", ok: true, detail: folder };
|
|
1493
|
+
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` };
|
|
1494
|
+
}
|
|
1402
1495
|
var CHECK_NAMES = [...Object.keys(TOOLS).map(toolCheck), ...OTHER_CHECKS];
|
|
1403
1496
|
async function runChecks(host, only = []) {
|
|
1404
1497
|
const wanted = (check) => only.length === 0 || only.includes(check);
|
|
@@ -1414,7 +1507,8 @@ async function runChecks(host, only = []) {
|
|
|
1414
1507
|
...wanted("hooks") && hooks ? [checkHooks(host)] : [],
|
|
1415
1508
|
...wanted("needs") ? [checkNeeds(host)] : [],
|
|
1416
1509
|
...wanted("bun") ? [checkBun(host)] : [],
|
|
1417
|
-
...wanted("completions") ? [checkCompletions(host)] : []
|
|
1510
|
+
...wanted("completions") ? [checkCompletions(host)] : [],
|
|
1511
|
+
...wanted("workspace") && (only.length > 0 || isInstalled(host, WORKSPACE) || isInstalled(host, "@kaynooo/cli-pkg")) ? [checkWorkspace(host)] : []
|
|
1418
1512
|
];
|
|
1419
1513
|
}
|
|
1420
1514
|
|
|
@@ -1456,19 +1550,19 @@ async function applyFixes(host, steps, dry) {
|
|
|
1456
1550
|
}
|
|
1457
1551
|
|
|
1458
1552
|
// src/host.ts
|
|
1459
|
-
import { homedir as
|
|
1460
|
-
import { join as
|
|
1461
|
-
import
|
|
1553
|
+
import { homedir as homedir4 } from "os";
|
|
1554
|
+
import { join as join7 } from "path";
|
|
1555
|
+
import process14 from "process";
|
|
1462
1556
|
var RUN_TIMEOUT_MS = 20000;
|
|
1463
1557
|
function hostHere() {
|
|
1464
|
-
const home =
|
|
1558
|
+
const home = homedir4();
|
|
1465
1559
|
return {
|
|
1466
1560
|
home,
|
|
1467
|
-
globalModules:
|
|
1561
|
+
globalModules: join7(process14.env.BUN_INSTALL ?? join7(home, ".bun"), "install", "global", "node_modules"),
|
|
1468
1562
|
bunVersion: Bun.version,
|
|
1469
1563
|
which: (bin) => Bun.which(bin),
|
|
1470
1564
|
run: async (argv, env) => {
|
|
1471
|
-
const ran = await runCommand(argv, { timeoutMs: RUN_TIMEOUT_MS, ...env === undefined ? {} : { env: { ...
|
|
1565
|
+
const ran = await runCommand(argv, { timeoutMs: RUN_TIMEOUT_MS, ...env === undefined ? {} : { env: { ...process14.env, ...env } } });
|
|
1472
1566
|
if (ran.timedOut)
|
|
1473
1567
|
return { code: 1, stdout: "", stderr: `timed out after ${RUN_TIMEOUT_MS / 1000}s` };
|
|
1474
1568
|
const of = (stream) => Buffer.concat(ran.chunks.filter((chunk) => chunk.stream === stream).map((chunk) => chunk.data)).toString();
|
|
@@ -1478,10 +1572,10 @@ function hostHere() {
|
|
|
1478
1572
|
}
|
|
1479
1573
|
|
|
1480
1574
|
// src/install.ts
|
|
1481
|
-
import
|
|
1575
|
+
import process16 from "process";
|
|
1482
1576
|
|
|
1483
1577
|
// ../kit/src/prompt.ts
|
|
1484
|
-
import
|
|
1578
|
+
import process15 from "process";
|
|
1485
1579
|
var KEY_SEQUENCES = /\x1B\[[0-9;]*[A-Z~]|\x1BO[A-Z]|[\s\S]/giu;
|
|
1486
1580
|
var CONTROL = /^[\x00-\x1F\x7F]/;
|
|
1487
1581
|
var WORDS = /\s+/;
|
|
@@ -1519,6 +1613,52 @@ async function checklist(question, choices, { initial = new Set, min = 0, toggle
|
|
|
1519
1613
|
});
|
|
1520
1614
|
return accepted ? checked : undefined;
|
|
1521
1615
|
}
|
|
1616
|
+
async function confirm(question, { default: fallback, ...settings } = {}) {
|
|
1617
|
+
let answered = fallback;
|
|
1618
|
+
const choices = { true: "Y/n", false: "y/N", undefined: "y/n" }[String(fallback)];
|
|
1619
|
+
const accepted = await ask(question, settings, (colors, state) => [state === "asking" ? asking(colors, question, "", choices) : answer(colors, question, state, answered ? "yes" : "no")], (key) => {
|
|
1620
|
+
if (key === "cancel")
|
|
1621
|
+
return "cancel";
|
|
1622
|
+
if (key === "enter")
|
|
1623
|
+
return answered === undefined ? undefined : "enter";
|
|
1624
|
+
const letter = typeof key === "object" ? key.text.toLowerCase() : "";
|
|
1625
|
+
if (letter !== "y" && letter !== "n")
|
|
1626
|
+
return;
|
|
1627
|
+
answered = letter === "y";
|
|
1628
|
+
return "enter";
|
|
1629
|
+
});
|
|
1630
|
+
return accepted ? answered : undefined;
|
|
1631
|
+
}
|
|
1632
|
+
async function input2(question, { default: fallback, mask = false, validate, ...settings } = {}) {
|
|
1633
|
+
let value = "";
|
|
1634
|
+
let invalid;
|
|
1635
|
+
const taken = () => value === "" ? fallback ?? "" : value;
|
|
1636
|
+
const shown = () => mask ? "*".repeat([...taken()].length) : taken();
|
|
1637
|
+
const accepted = await ask(question, settings, (colors, state, { columns }) => {
|
|
1638
|
+
if (state !== "asking")
|
|
1639
|
+
return [answer(colors, question, state, shown())];
|
|
1640
|
+
const characters = [...shown()];
|
|
1641
|
+
const room = Math.max(columns - question.length - 5, 1);
|
|
1642
|
+
const tail = characters.length > room ? `\u2026${characters.slice(1 - room).join("")}` : shown();
|
|
1643
|
+
const typed = value === "" ? colors.dim(tail) : tail;
|
|
1644
|
+
return [`${colors.cyan("?")} ${question} ${typed}${colors.dim("\u2588")}`, ...invalid === undefined ? [] : [` ${colors.red(invalid)}`]];
|
|
1645
|
+
}, (key) => {
|
|
1646
|
+
if (key === "cancel")
|
|
1647
|
+
return "cancel";
|
|
1648
|
+
invalid = undefined;
|
|
1649
|
+
if (key === "enter") {
|
|
1650
|
+
invalid = validate?.(taken());
|
|
1651
|
+
return invalid === undefined ? "enter" : undefined;
|
|
1652
|
+
}
|
|
1653
|
+
if (key === "erase")
|
|
1654
|
+
value = [...value].slice(0, -1).join("");
|
|
1655
|
+
else if (typeof key === "object")
|
|
1656
|
+
value += key.text;
|
|
1657
|
+
return;
|
|
1658
|
+
});
|
|
1659
|
+
return accepted ? taken() : undefined;
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1522
1662
|
class List {
|
|
1523
1663
|
choices;
|
|
1524
1664
|
query = "";
|
|
@@ -1577,9 +1717,9 @@ function asking(colors, question, typed, keys) {
|
|
|
1577
1717
|
function answer(colors, question, state, picked) {
|
|
1578
1718
|
return state === "enter" ? `${colors.green("\u2713")} ${question} ${colors.cyan(picked)}` : `${colors.red("\u2717")} ${question} ${colors.dim("cancelled")}`;
|
|
1579
1719
|
}
|
|
1580
|
-
async function ask(question, { hint, terminal =
|
|
1720
|
+
async function ask(question, { hint, terminal = process15 }, render, press) {
|
|
1581
1721
|
const { stdin, stderr } = terminal;
|
|
1582
|
-
if (
|
|
1722
|
+
if (!isInteractive(terminal))
|
|
1583
1723
|
fail(`cannot ask "${question}" off a terminal`, 2, hint);
|
|
1584
1724
|
const colors = paint(stderr);
|
|
1585
1725
|
let drawn = 0;
|
|
@@ -1629,18 +1769,19 @@ async function ask(question, { hint, terminal = process12 }, render, press) {
|
|
|
1629
1769
|
|
|
1630
1770
|
// src/install.ts
|
|
1631
1771
|
var HOOKS = "hooks";
|
|
1772
|
+
var PLUGIN_UNINSTALL = ["claude", "plugin", "uninstall", "cli-hooks"];
|
|
1632
1773
|
var PLUGIN_INSTALL = ["claude", "plugin", "install", "cli-hooks", "--marketplace", `https://${FORGE}/kaynooo/cli.git`];
|
|
1633
1774
|
var CLI_PREFIX = /^cli-/;
|
|
1634
1775
|
var shortName = (name) => name.slice(name.indexOf("/") + 1).replace(CLI_PREFIX, "");
|
|
1635
1776
|
function candidates2(host) {
|
|
1636
|
-
const packages = Object.entries(TOOLS).
|
|
1777
|
+
const packages = Object.entries(TOOLS).map(([name, bins]) => {
|
|
1637
1778
|
const needs = (NEEDS[name] ?? []).map((need) => need.bin);
|
|
1638
|
-
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] };
|
|
1779
|
+
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) };
|
|
1639
1780
|
});
|
|
1640
1781
|
const { plugin, mod } = hooksWiring(host);
|
|
1641
|
-
if (
|
|
1782
|
+
if (mod !== undefined && !plugin)
|
|
1642
1783
|
return packages;
|
|
1643
|
-
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] }];
|
|
1784
|
+
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 }];
|
|
1644
1785
|
}
|
|
1645
1786
|
function choose(pool, names) {
|
|
1646
1787
|
const chosen = new Set;
|
|
@@ -1677,16 +1818,33 @@ function plan(host, picked, force) {
|
|
|
1677
1818
|
function checksFor(packages, plugin) {
|
|
1678
1819
|
return [...packages.map((name) => name.slice(name.indexOf("/") + 1)), ...plugin ? ["hooks"] : [], "needs", "completions", ...packages.includes("@kaynooo/cli-forgejo") ? ["tea"] : []];
|
|
1679
1820
|
}
|
|
1680
|
-
async function ask2(
|
|
1681
|
-
if (
|
|
1821
|
+
async function ask2(all) {
|
|
1822
|
+
if (!isInteractive())
|
|
1682
1823
|
badUsage("name what to install, or all: doctor --install forgejo check hooks (doctor --install --list shows the choices)");
|
|
1683
|
-
const choices =
|
|
1684
|
-
const picked = await checklist("
|
|
1685
|
-
|
|
1824
|
+
const choices = all.map((each) => ({ value: each, label: each.label, hint: `${each.installed ? "installed, " : ""}${each.hint}` }));
|
|
1825
|
+
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" });
|
|
1826
|
+
if (picked === undefined)
|
|
1827
|
+
return;
|
|
1828
|
+
const remove = all.filter((each) => !picked.has(each) && each.installed);
|
|
1829
|
+
if (remove.length > 0 && await confirm(`remove ${remove.map((each) => each.label).join(", ")}?`, { default: false }) !== true)
|
|
1830
|
+
return { add: all.filter((each) => picked.has(each) && !each.installed), remove: [] };
|
|
1831
|
+
return { add: all.filter((each) => picked.has(each) && !each.installed), remove };
|
|
1832
|
+
}
|
|
1833
|
+
function removal(all, remove) {
|
|
1834
|
+
const keepsGuard = all.some((each) => each.id === HOOKS && each.installed && !remove.includes(each));
|
|
1835
|
+
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) };
|
|
1686
1836
|
}
|
|
1687
|
-
async function
|
|
1688
|
-
|
|
1689
|
-
|
|
1837
|
+
async function askWorkspace(host) {
|
|
1838
|
+
if (!isInteractive() || workspaceChosen({ home: host.home }))
|
|
1839
|
+
return;
|
|
1840
|
+
const folder = await input2("folder holding your repositories (repos, sweep and pkg bump look there)", { default: "~/Workspace" });
|
|
1841
|
+
if (folder !== undefined && folder !== "" && !saveWorkspace(folder, { home: host.home }))
|
|
1842
|
+
console.error(`could not write the workspace into ${userConfigPath("config", { home: host.home })}: it does not parse, fix it by hand`);
|
|
1843
|
+
}
|
|
1844
|
+
async function install(host, options, recheck, choosing = ask2) {
|
|
1845
|
+
const all = candidates2(host);
|
|
1846
|
+
const pool = all.filter((each) => !each.installed);
|
|
1847
|
+
const { dim, green, red, yellow } = paint(process16.stdout);
|
|
1690
1848
|
if (options.list) {
|
|
1691
1849
|
for (const each of pool)
|
|
1692
1850
|
console.log(`${each.label.padEnd(12)} ${dim(each.hint)}`);
|
|
@@ -1694,20 +1852,23 @@ async function install(host, options, recheck) {
|
|
|
1694
1852
|
console.log("everything is installed");
|
|
1695
1853
|
return 0;
|
|
1696
1854
|
}
|
|
1697
|
-
if (pool.length === 0) {
|
|
1855
|
+
if (pool.length === 0 && options.names.length > 0) {
|
|
1698
1856
|
console.log("everything is installed");
|
|
1699
1857
|
return 0;
|
|
1700
1858
|
}
|
|
1701
|
-
const unknown = options.names.filter((name) => name !== "all" && !
|
|
1859
|
+
const unknown = options.names.filter((name) => name !== "all" && !all.some((each) => each.names.includes(name)));
|
|
1702
1860
|
if (unknown.length > 0)
|
|
1703
1861
|
badUsage(`nothing to install is named ${unknown.join(", ")}: doctor --install --list`);
|
|
1704
|
-
const
|
|
1862
|
+
const chosen = options.names.length === 0 ? await choosing(all) : { add: choose(pool, options.names), remove: [] };
|
|
1863
|
+
const picked = chosen?.add ?? [];
|
|
1705
1864
|
const skipped = options.names.filter((name) => name !== "all" && !pool.some((each) => each.names.includes(name)));
|
|
1706
1865
|
for (const name of skipped)
|
|
1707
1866
|
console.log(`${dim("already")} ${name} is installed`);
|
|
1708
|
-
|
|
1867
|
+
const removing = removal(all, chosen?.remove ?? []);
|
|
1868
|
+
const removes = removing.packages.length > 0 || removing.plugin;
|
|
1869
|
+
if (picked.length === 0 && !removes) {
|
|
1709
1870
|
if (skipped.length === 0)
|
|
1710
|
-
console.log("nothing
|
|
1871
|
+
console.log("nothing changed");
|
|
1711
1872
|
return 0;
|
|
1712
1873
|
}
|
|
1713
1874
|
const planned = plan(host, picked, options.force);
|
|
@@ -1715,13 +1876,20 @@ async function install(host, options, recheck) {
|
|
|
1715
1876
|
console.log(`${red("blocked")} ${each.package} needs ${each.need} (${each.why}): ${each.how}; --force installs it anyway`);
|
|
1716
1877
|
for (const each of planned.warnings)
|
|
1717
1878
|
console.log(`${yellow("warn ")} ${each.package}: ${each.need} is missing, ${each.why}`);
|
|
1718
|
-
if (planned.packages.length === 0 && !planned.plugin)
|
|
1879
|
+
if (planned.packages.length === 0 && !planned.plugin && !removes)
|
|
1719
1880
|
return 1;
|
|
1881
|
+
if (!options.dryRun && planned.packages.some((name) => name === "@kaynooo/cli-workspace" || name === "@kaynooo/cli-pkg"))
|
|
1882
|
+
await askWorkspace(host);
|
|
1720
1883
|
const command = ["bun", "install", "-g", ...planned.packages];
|
|
1721
1884
|
if (planned.packages.length > 0)
|
|
1722
1885
|
console.log(`${options.dryRun ? dim("would ") : green("install")} ${command.join(" ")}`);
|
|
1723
1886
|
if (planned.plugin)
|
|
1724
1887
|
console.log(`${options.dryRun ? dim("would ") : green("install")} ${PLUGIN_INSTALL.join(" ")}`);
|
|
1888
|
+
const uninstall = ["bun", "remove", "-g", ...removing.packages];
|
|
1889
|
+
if (removing.packages.length > 0)
|
|
1890
|
+
console.log(`${options.dryRun ? dim("would ") : green("remove ")} ${uninstall.join(" ")}`);
|
|
1891
|
+
if (removing.plugin)
|
|
1892
|
+
console.log(`${options.dryRun ? dim("would ") : green("remove ")} ${PLUGIN_UNINSTALL.join(" ")}`);
|
|
1725
1893
|
if (options.dryRun)
|
|
1726
1894
|
return planned.blocked.length > 0 ? 1 : 0;
|
|
1727
1895
|
if (planned.packages.length > 0) {
|
|
@@ -1740,16 +1908,23 @@ async function install(host, options, recheck) {
|
|
|
1740
1908
|
console.error(`${red("failed")} ${PLUGIN_INSTALL.join(" ")}: exit ${ran.code}: ${firstLine(ran.stderr || ran.stdout)}`);
|
|
1741
1909
|
}
|
|
1742
1910
|
}
|
|
1911
|
+
if (removing.packages.length > 0) {
|
|
1912
|
+
const ran = await host.run(uninstall);
|
|
1913
|
+
if (ran.code !== 0) {
|
|
1914
|
+
console.error(`${red("failed")} bun remove -g: exit ${ran.code}: ${firstLine(ran.stderr || ran.stdout)}`);
|
|
1915
|
+
return 1;
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
if (removing.plugin && host.which("claude") !== null) {
|
|
1919
|
+
const ran = await host.run(PLUGIN_UNINSTALL);
|
|
1920
|
+
if (ran.code !== 0)
|
|
1921
|
+
console.error(`${red("failed")} ${PLUGIN_UNINSTALL.join(" ")}: exit ${ran.code}: ${firstLine(ran.stderr || ran.stdout)}`);
|
|
1922
|
+
}
|
|
1743
1923
|
console.log(`
|
|
1744
1924
|
after the install:`);
|
|
1745
1925
|
const code = await recheck(checksFor(planned.packages, planned.plugin));
|
|
1746
1926
|
return planned.blocked.length > 0 ? 1 : code;
|
|
1747
1927
|
}
|
|
1748
|
-
function candidatesAll(host) {
|
|
1749
|
-
const open = candidates2(host);
|
|
1750
|
-
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] }));
|
|
1751
|
-
return [...open, ...done];
|
|
1752
|
-
}
|
|
1753
1928
|
|
|
1754
1929
|
// src/doctor.ts
|
|
1755
1930
|
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
|
|
@@ -1766,14 +1941,14 @@ var OPTIONS = {
|
|
|
1766
1941
|
quiet: { type: "boolean", short: "q", description: "print only the failing checks" },
|
|
1767
1942
|
fix: { type: "boolean", description: "run the fix: lines doctor may run, then check again" },
|
|
1768
1943
|
"dry-run": { type: "boolean", description: "with --fix or --install, show what would run without running it (alone, it implies --fix)" },
|
|
1769
|
-
install: { type: "boolean", description: "install the packages named, or
|
|
1944
|
+
install: { type: "boolean", description: "install the packages named, or managed from a checklist, installed ones checked and unchecking removing them: forgejo, ft, hooks, all" },
|
|
1770
1945
|
list: { type: "boolean", description: "with --install, print the choices" },
|
|
1771
1946
|
force: { type: "boolean", description: "with --install, install a package even though a tool it requires is missing" }
|
|
1772
1947
|
};
|
|
1773
1948
|
function print(results) {
|
|
1774
1949
|
if (results.length === 0)
|
|
1775
1950
|
return;
|
|
1776
|
-
const { green, red, dim } = paint(
|
|
1951
|
+
const { green, red, dim } = paint(process17.stdout);
|
|
1777
1952
|
const width = Math.max(...results.map((result) => result.check.length));
|
|
1778
1953
|
for (const { check, ok, detail, fix } of results)
|
|
1779
1954
|
console.log(`${ok ? green("ok ") : red("FAIL")} ${check.padEnd(width)} ${detail}${fix === undefined ? "" : ` ${dim("fix:")} ${fix}`}`);
|
|
@@ -1783,13 +1958,13 @@ function print(results) {
|
|
|
1783
1958
|
${dim("fix them all:")} ${INSTALL2}${installs.join(" ")}`);
|
|
1784
1959
|
}
|
|
1785
1960
|
function printOutcomes(outcomes, log) {
|
|
1786
|
-
const { green, red, dim } = paint(
|
|
1961
|
+
const { green, red, dim } = paint(process17.stdout);
|
|
1787
1962
|
const marks = { ran: green("ran "), failed: red("failed "), "dry-run": dim("would "), "not run": dim("not run") };
|
|
1788
1963
|
for (const { command, status, detail } of outcomes)
|
|
1789
1964
|
log(`${marks[status]} ${command}${detail === undefined ? "" : ` ${dim(detail)}`}`);
|
|
1790
1965
|
}
|
|
1791
1966
|
async function main(args, host = hostHere()) {
|
|
1792
|
-
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", "
|
|
1967
|
+
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"]] });
|
|
1793
1968
|
const only = values.only ?? [];
|
|
1794
1969
|
if (values.install !== true && (positionals.length > 0 || values.list === true || values.force === true))
|
|
1795
1970
|
badUsage("names, --list and --force go with --install");
|
|
@@ -1823,9 +1998,9 @@ after the fixes:`);
|
|
|
1823
1998
|
const absent = Object.keys(TOOLS).filter((name) => !isInstalled(host, name)).length;
|
|
1824
1999
|
if (!json && only.length === 0 && absent > 0)
|
|
1825
2000
|
console.log(`
|
|
1826
|
-
${paint(
|
|
2001
|
+
${paint(process17.stdout).dim(`${absent} packages not installed: doctor --install`)}`);
|
|
1827
2002
|
return results.every((result) => result.ok) ? 0 : 1;
|
|
1828
2003
|
}
|
|
1829
2004
|
|
|
1830
2005
|
// src/cli.ts
|
|
1831
|
-
|
|
2006
|
+
process18.exitCode = await runMain({ name: "doctor", run: async (args) => main(args) });
|