@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-bin.js
CHANGED
|
@@ -2,16 +2,37 @@
|
|
|
2
2
|
// @bun
|
|
3
3
|
|
|
4
4
|
// src/cli-bin.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,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
|
|
1166
|
-
import { join as
|
|
1167
|
-
import
|
|
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
|
|
1171
|
-
import { basename, join as
|
|
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
|
|
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(
|
|
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 :
|
|
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 ${
|
|
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(
|
|
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" &&
|
|
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 =
|
|
1396
|
-
return
|
|
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
|
-
|
|
1410
|
-
const removed = readdirSync(dir).filter((name) => FISH_FILE.test(name) && !bins.includes(name.replace(FISH_FILE, "")) &&
|
|
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(
|
|
1413
|
-
await Promise.all(bins.map(async (bin) => Bun.write(
|
|
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) => !
|
|
1511
|
+
return installedBins(host).filter((bin) => !existsSync3(join6(dir, `${bin}.fish`)));
|
|
1418
1512
|
}
|
|
1419
1513
|
|
|
1420
1514
|
// src/doctor.ts
|
|
1421
|
-
import
|
|
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
|
|
1462
|
-
import { join as
|
|
1463
|
-
import
|
|
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 =
|
|
1560
|
+
const home = homedir4();
|
|
1467
1561
|
return {
|
|
1468
1562
|
home,
|
|
1469
|
-
globalModules:
|
|
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: { ...
|
|
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
|
|
1577
|
+
import process16 from "process";
|
|
1484
1578
|
|
|
1485
1579
|
// ../kit/src/prompt.ts
|
|
1486
|
-
import
|
|
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 =
|
|
1722
|
+
async function ask(question, { hint, terminal = process15 }, render, press) {
|
|
1583
1723
|
const { stdin, stderr } = terminal;
|
|
1584
|
-
if (
|
|
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).
|
|
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 (
|
|
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(
|
|
1683
|
-
if (
|
|
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 =
|
|
1686
|
-
const picked = await checklist("
|
|
1687
|
-
|
|
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
|
|
1690
|
-
|
|
1691
|
-
|
|
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" && !
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
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", "
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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",
|