@kaynooo/cli-doctor 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/cli-bin.js +414 -112
- package/dist/cli.js +341 -101
- package/package.json +2 -2
package/dist/cli-bin.js
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
// @bun
|
|
3
3
|
|
|
4
4
|
// src/cli-bin.ts
|
|
5
|
-
import
|
|
5
|
+
import process21 from "process";
|
|
6
6
|
// ../kit/src/args.ts
|
|
7
|
-
import
|
|
7
|
+
import process9 from "process";
|
|
8
8
|
import { parseArgs } from "util";
|
|
9
9
|
|
|
10
10
|
// ../kit/src/format.ts
|
|
11
|
-
import
|
|
11
|
+
import process7 from "process";
|
|
12
12
|
|
|
13
13
|
// ../kit/src/mode.ts
|
|
14
14
|
import process2 from "process";
|
|
@@ -32,7 +32,7 @@ function isInteractive(streams = process2) {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
// ../kit/src/style.ts
|
|
35
|
-
import
|
|
35
|
+
import process6 from "process";
|
|
36
36
|
|
|
37
37
|
// ../../node_modules/.bun/@kaynooo+utils@2.31.2+d0769e28dc4dad16/node_modules/@kaynooo/utils/dist/chunk-mtetydvc.js
|
|
38
38
|
function x(s) {
|
|
@@ -295,28 +295,188 @@ function le(i = {}) {
|
|
|
295
295
|
}
|
|
296
296
|
var t = le();
|
|
297
297
|
|
|
298
|
+
// ../kit/src/theme.ts
|
|
299
|
+
import { existsSync as existsSync3, readFileSync, statSync } from "fs";
|
|
300
|
+
import { homedir as homedir2 } from "os";
|
|
301
|
+
import { join as join3, resolve as resolve2 } from "path";
|
|
302
|
+
import process5 from "process";
|
|
303
|
+
|
|
304
|
+
// ../kit/src/config.ts
|
|
305
|
+
import { existsSync } from "fs";
|
|
306
|
+
import { dirname, join, resolve } from "path";
|
|
307
|
+
import process3 from "process";
|
|
308
|
+
function findUp(names, from = process3.cwd(), stop = () => false) {
|
|
309
|
+
const wanted = typeof names === "string" ? [names] : names;
|
|
310
|
+
for (let dir = resolve(from);; dir = dirname(dir)) {
|
|
311
|
+
const found = wanted.map((name) => join(dir, name)).find((path) => existsSync(path));
|
|
312
|
+
if (found !== undefined)
|
|
313
|
+
return found;
|
|
314
|
+
if (stop(dir) || dirname(dir) === dir)
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
298
319
|
// ../kit/src/user-config.ts
|
|
320
|
+
import { existsSync as existsSync2 } from "fs";
|
|
299
321
|
import { homedir } from "os";
|
|
300
|
-
import { join } from "path";
|
|
301
|
-
import
|
|
322
|
+
import { join as join2 } from "path";
|
|
323
|
+
import process4 from "process";
|
|
302
324
|
function configBase(where = {}) {
|
|
303
|
-
const xdg = where.xdg ?? (where.home === undefined ?
|
|
304
|
-
return xdg !== undefined && xdg !== "" ? xdg :
|
|
325
|
+
const xdg = where.xdg ?? (where.home === undefined ? process4.env.XDG_CONFIG_HOME : undefined);
|
|
326
|
+
return xdg !== undefined && xdg !== "" ? xdg : join2(where.home ?? homedir(), ".config");
|
|
305
327
|
}
|
|
306
328
|
function userConfigPath(bin, where = {}) {
|
|
307
|
-
return
|
|
329
|
+
return join2(configBase(where), "kaynooo-cli", `${bin}.json`);
|
|
330
|
+
}
|
|
331
|
+
function userConfigReadPath(bin, legacy = [], where = {}) {
|
|
332
|
+
const current = userConfigPath(bin, where);
|
|
333
|
+
if (existsSync2(current))
|
|
334
|
+
return current;
|
|
335
|
+
const base = configBase(where);
|
|
336
|
+
return legacy.map((parts) => join2(base, ...parts)).find((path) => existsSync2(path)) ?? current;
|
|
308
337
|
}
|
|
309
338
|
|
|
310
339
|
// ../kit/src/theme.ts
|
|
340
|
+
var DEFAULT = {
|
|
341
|
+
colors: { accent: "cyan", id: "yellow", path: "blue", label: "dim", value: "bold", ok: "green", warn: "yellow", problem: "red", note: "dim", passed: "green", failed: "red", running: "cyan", queued: "yellow", skipped: "dim" },
|
|
342
|
+
glyphs: { ok: "\u2713", warn: "\u25B2", problem: "\u2717", note: "\u203A", passed: "\u2713", failed: "\u2717", running: "\u25CF", queued: "\u25CB", skipped: "-", bar: "\u2501" },
|
|
343
|
+
accents: ["cyan", "magenta", "green", "blue", "yellow", "redBright"],
|
|
344
|
+
bins: {}
|
|
345
|
+
};
|
|
346
|
+
var BRIGHT = { accent: "cyanBright", id: "yellowBright", path: "blueBright", label: "white", value: "bold whiteBright", ok: "greenBright", warn: "yellowBright", problem: "redBright", note: "white", passed: "greenBright", failed: "redBright", running: "cyanBright", queued: "yellowBright", skipped: "white" };
|
|
347
|
+
var MONO = { accent: "bold", id: "bold", path: "underline", label: "dim", value: "bold", ok: "bold", warn: "bold", problem: "bold", note: "dim", passed: "bold", failed: "bold", running: "bold", queued: "dim", skipped: "dim" };
|
|
348
|
+
var ASCII = { ok: "+", warn: "!", problem: "x", note: ">", passed: "+", failed: "x", running: "*", queued: "o", skipped: "-", bar: "#" };
|
|
349
|
+
var PRESETS = {
|
|
350
|
+
default: {},
|
|
351
|
+
"high-contrast": { colors: BRIGHT, accents: ["cyanBright", "magentaBright", "greenBright", "blueBright", "yellowBright", "redBright"] },
|
|
352
|
+
mono: { colors: MONO, accents: ["bold"] },
|
|
353
|
+
ascii: { glyphs: ASCII }
|
|
354
|
+
};
|
|
311
355
|
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"]);
|
|
356
|
+
var HEX = /^#[0-9a-f]{6}$/i;
|
|
357
|
+
var CONTROL = /\p{Cc}/u;
|
|
312
358
|
var MAX_FILE_BYTES = 64 * 1024;
|
|
359
|
+
var SPACES = /\s+/;
|
|
360
|
+
function isColorSpec(spec) {
|
|
361
|
+
if (typeof spec === "number")
|
|
362
|
+
return Number.isInteger(spec) && spec >= 0 && spec <= 255;
|
|
363
|
+
return typeof spec === "string" && spec.trim() !== "" && spec.trim().split(SPACES).every((token) => STYLES.has(token) || HEX.test(token));
|
|
364
|
+
}
|
|
365
|
+
function isRecord(value) {
|
|
366
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
367
|
+
}
|
|
368
|
+
function readThemeFile(path, warn) {
|
|
369
|
+
try {
|
|
370
|
+
const stat = statSync(path, { throwIfNoEntry: false });
|
|
371
|
+
if (stat === undefined)
|
|
372
|
+
return;
|
|
373
|
+
if (!stat.isFile() || stat.size > MAX_FILE_BYTES) {
|
|
374
|
+
warn(`${path} is not a regular file under ${MAX_FILE_BYTES / 1024} KiB, theme ignored`);
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
378
|
+
if (isRecord(parsed))
|
|
379
|
+
return parsed;
|
|
380
|
+
warn(`${path} is not a JSON object, theme ignored`);
|
|
381
|
+
} catch (error) {
|
|
382
|
+
warn(`${path} does not parse (${error instanceof Error ? error.message : String(error)}), theme ignored`);
|
|
383
|
+
}
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
function applyPreset(theme, name, warn, where) {
|
|
387
|
+
if (name === undefined)
|
|
388
|
+
return;
|
|
389
|
+
if (typeof name !== "string" || !Object.hasOwn(PRESETS, name)) {
|
|
390
|
+
warn(`${where}: no preset ${String(name)}, one of ${Object.keys(PRESETS).join(", ")}`);
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
const preset = PRESETS[name];
|
|
394
|
+
Object.assign(theme.colors, preset.colors);
|
|
395
|
+
Object.assign(theme.glyphs, preset.glyphs);
|
|
396
|
+
if (preset.accents !== undefined)
|
|
397
|
+
theme.accents = [...preset.accents];
|
|
398
|
+
return name;
|
|
399
|
+
}
|
|
400
|
+
function applyFile(theme, file, where, warn) {
|
|
401
|
+
if (isRecord(file.colors)) {
|
|
402
|
+
for (const [key, value] of Object.entries(file.colors)) {
|
|
403
|
+
if (!Object.hasOwn(DEFAULT.colors, key))
|
|
404
|
+
warn(`${where}: no colour ${key}, one of ${Object.keys(DEFAULT.colors).join(", ")}`);
|
|
405
|
+
else if (!isColorSpec(value))
|
|
406
|
+
warn(`${where}: colour ${key} is not an ANSI name, \`#rrggbb\` or a number up to 255`);
|
|
407
|
+
else
|
|
408
|
+
theme.colors[key] = value;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
if (isRecord(file.glyphs)) {
|
|
412
|
+
for (const [key, value] of Object.entries(file.glyphs)) {
|
|
413
|
+
if (!Object.hasOwn(DEFAULT.glyphs, key))
|
|
414
|
+
warn(`${where}: no glyph ${key}, one of ${Object.keys(DEFAULT.glyphs).join(", ")}`);
|
|
415
|
+
else if (typeof value !== "string" || CONTROL.test(value) || Bun.stringWidth(value) !== 1)
|
|
416
|
+
warn(`${where}: glyph ${key} must be one visible character wide, with no control character`);
|
|
417
|
+
else
|
|
418
|
+
theme.glyphs[key] = value;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
if (Array.isArray(file.accents) && file.accents.length > 0 && file.accents.every(isColorSpec))
|
|
422
|
+
theme.accents = [...file.accents];
|
|
423
|
+
else if (file.accents !== undefined)
|
|
424
|
+
warn(`${where}: accents must be a non-empty list of colours`);
|
|
425
|
+
if (isRecord(file.bins)) {
|
|
426
|
+
for (const [bin, value] of Object.entries(file.bins)) {
|
|
427
|
+
if (isColorSpec(value))
|
|
428
|
+
theme.bins[bin] = value;
|
|
429
|
+
else
|
|
430
|
+
warn(`${where}: bin ${bin} has no valid colour`);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
function endsWalk(home) {
|
|
435
|
+
return (dir) => dir === resolve2(home) || existsSync3(join3(dir, ".git"));
|
|
436
|
+
}
|
|
313
437
|
var warned = new Set;
|
|
438
|
+
function warnOnce(message) {
|
|
439
|
+
if (warned.has(message))
|
|
440
|
+
return;
|
|
441
|
+
warned.add(message);
|
|
442
|
+
console.error(`warning: theme ${message}`);
|
|
443
|
+
}
|
|
444
|
+
function isPath(chosen) {
|
|
445
|
+
return chosen.includes("/") || chosen.endsWith(".json");
|
|
446
|
+
}
|
|
447
|
+
function loadTheme({ env = process5.env, from = process5.cwd(), home = homedir2(), warn = warnOnce } = {}) {
|
|
448
|
+
const theme = { colors: { ...DEFAULT.colors }, glyphs: { ...DEFAULT.glyphs }, accents: [...DEFAULT.accents], bins: Object.create(null) };
|
|
449
|
+
const sources = [];
|
|
450
|
+
const chosen = env.KIT_THEME ?? "";
|
|
451
|
+
const wanted = chosen === "" ? [userConfigPath("theme", { home, xdg: env.XDG_CONFIG_HOME }), findUp(join3(".kaynooo", "theme.json"), from, endsWalk(home))] : [];
|
|
452
|
+
if (isPath(chosen))
|
|
453
|
+
wanted.push(resolve2(from, chosen));
|
|
454
|
+
const files = [];
|
|
455
|
+
for (const path of wanted) {
|
|
456
|
+
const file = path === undefined ? undefined : readThemeFile(path, warn);
|
|
457
|
+
if (path !== undefined && file !== undefined)
|
|
458
|
+
files.push({ path, file });
|
|
459
|
+
}
|
|
460
|
+
let preset = "default";
|
|
461
|
+
if (chosen !== "" && !isPath(chosen)) {
|
|
462
|
+
preset = applyPreset(theme, chosen, warn, "KIT_THEME") ?? preset;
|
|
463
|
+
} else {
|
|
464
|
+
const named = files.findLast(({ file }) => file.preset !== undefined);
|
|
465
|
+
if (named !== undefined)
|
|
466
|
+
preset = applyPreset(theme, named.file.preset, warn, named.path) ?? preset;
|
|
467
|
+
}
|
|
468
|
+
for (const { path, file } of files) {
|
|
469
|
+
applyFile(theme, file, path, warn);
|
|
470
|
+
sources.push(path);
|
|
471
|
+
}
|
|
472
|
+
return { theme, sources, preset };
|
|
473
|
+
}
|
|
314
474
|
|
|
315
475
|
// ../kit/src/style.ts
|
|
316
|
-
function takesControlCodes(stream, env =
|
|
476
|
+
function takesControlCodes(stream, env = process6.env) {
|
|
317
477
|
return stream.isTTY === true && env.NO_COLOR === undefined && env.NODE_DISABLE_COLORS === undefined && env.TERM !== "dumb" && outputMode(stream, env) === "terminal";
|
|
318
478
|
}
|
|
319
|
-
function paint(stream, env =
|
|
479
|
+
function paint(stream, env = process6.env) {
|
|
320
480
|
return le({ stream: { isTTY: takesControlCodes(stream, env) }, env });
|
|
321
481
|
}
|
|
322
482
|
var STATUS_WORDS = {
|
|
@@ -341,7 +501,7 @@ var ESCAPE = String.fromCharCode(27);
|
|
|
341
501
|
function textWidth(text) {
|
|
342
502
|
return Bun.stringWidth(text);
|
|
343
503
|
}
|
|
344
|
-
function table(rows, { cellWidth, header = false, align = [], fit, stream =
|
|
504
|
+
function table(rows, { cellWidth, header = false, align = [], fit, stream = process7.stdout, env = process7.env } = {}) {
|
|
345
505
|
const cells = cellWidth === undefined ? rows : rows.map((row) => row.map((cell) => truncate(cell.replaceAll(LINE_BREAKS, " "), cellWidth)));
|
|
346
506
|
if (outputMode(stream, env) === "agent")
|
|
347
507
|
return cells.map((row) => row.map((cell) => Bun.stripANSI(cell).replaceAll(/[\t\r\n]+/g, " ")).join("\t")).join(`
|
|
@@ -380,7 +540,7 @@ function narrow(rows, widths, limit) {
|
|
|
380
540
|
}
|
|
381
541
|
return rows.map((row) => row.map((cell, index) => textWidth(cell) > (widths[index] ?? 0) ? truncate(cell, widths[index] ?? 0) : cell));
|
|
382
542
|
}
|
|
383
|
-
function jsonText(value, replacer, { stream =
|
|
543
|
+
function jsonText(value, replacer, { stream = process7.stdout, env = process7.env } = {}) {
|
|
384
544
|
return JSON.stringify(value, replacer, outputMode(stream, env) === "agent" ? undefined : 2);
|
|
385
545
|
}
|
|
386
546
|
function printJson(value) {
|
|
@@ -388,27 +548,27 @@ function printJson(value) {
|
|
|
388
548
|
}
|
|
389
549
|
|
|
390
550
|
// ../kit/src/help.ts
|
|
391
|
-
import
|
|
551
|
+
import process8 from "process";
|
|
392
552
|
var OPTIONAL = /\[[^\]]+\]/g;
|
|
393
553
|
var USAGE_PREFIX = /^usage:\s*/i;
|
|
394
554
|
var FULL_STOP = /\.$/;
|
|
395
|
-
function usageLine(usage, colors = paint(
|
|
555
|
+
function usageLine(usage, colors = paint(process8.stdout)) {
|
|
396
556
|
return `${colors.bold("Usage:")} ${usage.replace(USAGE_PREFIX, "").replaceAll(OPTIONAL, (part) => colors.dim(part))}`;
|
|
397
557
|
}
|
|
398
|
-
function helpText(usage, options, colors = paint(
|
|
558
|
+
function helpText(usage, options, colors = paint(process8.stdout), { examples = [], replaces, exits = [] } = {}) {
|
|
399
559
|
return [usageLine(usage, colors), ...replacesRows(replaces, colors), "", colors.bold("Options:"), ...optionRows(options, colors), ...exitRows(exits, colors), ...exampleRows(examples, colors)].join(`
|
|
400
560
|
`);
|
|
401
561
|
}
|
|
402
|
-
function exitRows(exits, colors = paint(
|
|
562
|
+
function exitRows(exits, colors = paint(process8.stdout)) {
|
|
403
563
|
if (exits.length === 0)
|
|
404
564
|
return [];
|
|
405
565
|
const width = Math.max(...exits.map(([code]) => String(code).length));
|
|
406
566
|
return ["", colors.bold("Exits:"), ...exits.map(([code, meaning]) => ` ${colors.cyan(String(code).padEnd(width))} ${meaning}`)];
|
|
407
567
|
}
|
|
408
|
-
function replacesRows(replaces, colors = paint(
|
|
568
|
+
function replacesRows(replaces, colors = paint(process8.stdout)) {
|
|
409
569
|
return replaces === undefined || outputMode() === "agent" ? [] : ["", `${colors.bold("Instead of:")} ${replaces}`];
|
|
410
570
|
}
|
|
411
|
-
function wantsHelpJson(env =
|
|
571
|
+
function wantsHelpJson(env = process8.env) {
|
|
412
572
|
return env.KIT_HELP === "json";
|
|
413
573
|
}
|
|
414
574
|
function description(usage, { examples = [], replaces, exits } = {}, options) {
|
|
@@ -427,12 +587,12 @@ function description(usage, { examples = [], replaces, exits } = {}, options) {
|
|
|
427
587
|
...options === undefined ? {} : { options: Object.entries(options).map(([name, { short, description }]) => ({ name, ...short === undefined ? {} : { short }, ...description === undefined ? {} : { description } })) }
|
|
428
588
|
};
|
|
429
589
|
}
|
|
430
|
-
function exampleRows(examples, colors = paint(
|
|
590
|
+
function exampleRows(examples, colors = paint(process8.stdout)) {
|
|
431
591
|
if (examples.length === 0 || outputMode() === "agent")
|
|
432
592
|
return [];
|
|
433
593
|
return ["", colors.bold("Examples:"), ...examples.map((example) => typeof example === "string" ? ` ${example}` : ` ${example[0]} ${colors.dim(`# ${example[1]}`)}`)];
|
|
434
594
|
}
|
|
435
|
-
function optionRows(options, colors = paint(
|
|
595
|
+
function optionRows(options, colors = paint(process8.stdout)) {
|
|
436
596
|
const rows = Object.entries(options).map(([name, option]) => {
|
|
437
597
|
const short = option.short === undefined ? " " : `${colors.cyan(`-${option.short}`)}, `;
|
|
438
598
|
const named = option.choices === undefined ? "<value>" : `<${option.choices.join("|")}>`;
|
|
@@ -524,7 +684,7 @@ var OUTPUT_OPTIONS = {
|
|
|
524
684
|
color: { type: "string", description: "auto (on a terminal alone), always or never" },
|
|
525
685
|
plain: { type: "boolean", description: "plain text, no colour or spinner: --color never" }
|
|
526
686
|
};
|
|
527
|
-
function applyOutput(color, plain, env =
|
|
687
|
+
function applyOutput(color, plain, env = process9.env) {
|
|
528
688
|
const when = plain === true ? "never" : color ?? "auto";
|
|
529
689
|
if (when === "auto")
|
|
530
690
|
return;
|
|
@@ -695,7 +855,7 @@ function nearestOption(message, options, allowNegative) {
|
|
|
695
855
|
}
|
|
696
856
|
// ../kit/src/process.ts
|
|
697
857
|
import { constants } from "os";
|
|
698
|
-
import
|
|
858
|
+
import process10 from "process";
|
|
699
859
|
var KILL_GRACE_MS = 2000;
|
|
700
860
|
function exitCodeOf(code, signal) {
|
|
701
861
|
if (code !== null)
|
|
@@ -715,7 +875,7 @@ async function runCommand(argv, { cwd, env, stdin = "ignore", timeoutMs, signal,
|
|
|
715
875
|
return;
|
|
716
876
|
try {
|
|
717
877
|
if (grouped)
|
|
718
|
-
|
|
878
|
+
process10.kill(-proc.pid, killing);
|
|
719
879
|
else
|
|
720
880
|
proc.kill(killing);
|
|
721
881
|
} catch {}
|
|
@@ -789,15 +949,15 @@ var ESCAPE_CODE = new RegExp(`${ESC}(?:\\[[\\x30-\\x3F]*[\\x20-\\x2F]*[\\x40-\\x
|
|
|
789
949
|
var RESET_STYLE = `${ESC}[0m`;
|
|
790
950
|
var CLOSE_LINK = `${ESC}]8;;${ESC}\\`;
|
|
791
951
|
// ../kit/src/cli.ts
|
|
792
|
-
import { readFileSync, realpathSync } from "fs";
|
|
793
|
-
import { dirname as
|
|
794
|
-
import
|
|
952
|
+
import { readFileSync as readFileSync2, realpathSync } from "fs";
|
|
953
|
+
import { dirname as dirname3, join as join5 } from "path";
|
|
954
|
+
import process12 from "process";
|
|
795
955
|
|
|
796
956
|
// ../kit/src/usage.ts
|
|
797
957
|
import { appendFileSync, mkdirSync } from "fs";
|
|
798
|
-
import { homedir as
|
|
799
|
-
import { dirname, isAbsolute, join as
|
|
800
|
-
import
|
|
958
|
+
import { homedir as homedir3 } from "os";
|
|
959
|
+
import { dirname as dirname2, isAbsolute, join as join4 } from "path";
|
|
960
|
+
import process11 from "process";
|
|
801
961
|
|
|
802
962
|
// ../kit/src/logs.ts
|
|
803
963
|
var DAY_MS = 24 * 60 * 60 * 1000;
|
|
@@ -811,14 +971,14 @@ var usageError;
|
|
|
811
971
|
function noteUsageError(message) {
|
|
812
972
|
usageError = message;
|
|
813
973
|
}
|
|
814
|
-
function stateHome(env =
|
|
974
|
+
function stateHome(env = process11.env) {
|
|
815
975
|
const dir = env.XDG_STATE_HOME;
|
|
816
|
-
return dir !== undefined && isAbsolute(dir) ? dir :
|
|
976
|
+
return dir !== undefined && isAbsolute(dir) ? dir : join4(homedir3(), ".local", "state");
|
|
817
977
|
}
|
|
818
|
-
function usagePath(env =
|
|
819
|
-
return
|
|
978
|
+
function usagePath(env = process11.env) {
|
|
979
|
+
return join4(stateHome(env), CLI_FOLDER, "usage.jsonl");
|
|
820
980
|
}
|
|
821
|
-
function usageRecorded(env =
|
|
981
|
+
function usageRecorded(env = process11.env) {
|
|
822
982
|
const off = env.KAYNOOO_CLI_NO_USAGE;
|
|
823
983
|
return env.NODE_ENV !== "test" && (off === undefined || off === "" || off === "0");
|
|
824
984
|
}
|
|
@@ -832,12 +992,12 @@ function usageEntry(bin, command, argv, code, now = new Date) {
|
|
|
832
992
|
usageError = undefined;
|
|
833
993
|
return { at: now.toISOString(), bin, ...command === undefined ? {} : { command }, flags: flagNames(argv), code, ...unknown === undefined ? {} : { unknown } };
|
|
834
994
|
}
|
|
835
|
-
function recordUsage(entry, env =
|
|
995
|
+
function recordUsage(entry, env = process11.env) {
|
|
836
996
|
if (!usageRecorded(env))
|
|
837
997
|
return;
|
|
838
998
|
try {
|
|
839
999
|
const path = usagePath(env);
|
|
840
|
-
mkdirSync(
|
|
1000
|
+
mkdirSync(dirname2(path), { recursive: true, mode: 448 });
|
|
841
1001
|
appendFileSync(path, `${JSON.stringify(entry)}
|
|
842
1002
|
`, { mode: 384 });
|
|
843
1003
|
} catch {}
|
|
@@ -971,8 +1131,8 @@ async function help2({ path, header, summary: lazySummary, commands, defaultComm
|
|
|
971
1131
|
`);
|
|
972
1132
|
}
|
|
973
1133
|
function printError(prefix, message, hint, cause) {
|
|
974
|
-
const colors = paint(
|
|
975
|
-
const styled = takesControlCodes(
|
|
1134
|
+
const colors = paint(process12.stderr);
|
|
1135
|
+
const styled = takesControlCodes(process12.stderr);
|
|
976
1136
|
const head = styled ? `${colors.red.bold("error")} ${colors.dim(`${prefix}:`)}` : `error: ${prefix}:`;
|
|
977
1137
|
const under = (label, text, tint) => text === undefined ? "" : `
|
|
978
1138
|
${styled ? tint(label) : `${label}:`} ${text}`;
|
|
@@ -1015,11 +1175,11 @@ async function dispatch(level, typed, { version, redact }) {
|
|
|
1015
1175
|
}
|
|
1016
1176
|
}
|
|
1017
1177
|
if (name === undefined && level.path.length > 1) {
|
|
1018
|
-
console.error(await help2(await expand(level, [...level.path.slice(1), ...argv]),
|
|
1178
|
+
console.error(await help2(await expand(level, [...level.path.slice(1), ...argv]), process12.stderr));
|
|
1019
1179
|
return 2;
|
|
1020
1180
|
}
|
|
1021
1181
|
if (name === undefined || name === "help" || name === "--help" || name === "-h") {
|
|
1022
|
-
console.log(await help2(await expand(level, [...level.path.slice(1), ...argv]),
|
|
1182
|
+
console.log(await help2(await expand(level, [...level.path.slice(1), ...argv]), process12.stdout));
|
|
1023
1183
|
return 0;
|
|
1024
1184
|
}
|
|
1025
1185
|
if (level.path.length === 1 && (name === "--version" || name === "-v")) {
|
|
@@ -1136,17 +1296,17 @@ async function printCandidates(found) {
|
|
|
1136
1296
|
return 0;
|
|
1137
1297
|
}
|
|
1138
1298
|
function packageVersion(entry = Bun.main) {
|
|
1139
|
-
for (let dir =
|
|
1299
|
+
for (let dir = dirname3(realpathSync(entry));; dir = dirname3(dir)) {
|
|
1140
1300
|
try {
|
|
1141
|
-
const { version } = JSON.parse(
|
|
1301
|
+
const { version } = JSON.parse(readFileSync2(join5(dir, "package.json"), "utf8"));
|
|
1142
1302
|
if (typeof version === "string")
|
|
1143
1303
|
return version;
|
|
1144
1304
|
} catch {}
|
|
1145
|
-
if (
|
|
1305
|
+
if (dirname3(dir) === dir)
|
|
1146
1306
|
return "0.0.0";
|
|
1147
1307
|
}
|
|
1148
1308
|
}
|
|
1149
|
-
async function runCli(cli, argv =
|
|
1309
|
+
async function runCli(cli, argv = process12.argv.slice(2)) {
|
|
1150
1310
|
const version = cli.version ?? packageVersion();
|
|
1151
1311
|
if (argv[0] === COMPLETE)
|
|
1152
1312
|
return printCandidates(candidates(scopeOf(cli.commands, cli.extraCommands, cli.aliases), cli.defaultCommand, argv.slice(1)));
|
|
@@ -1173,7 +1333,7 @@ function commandPath(scope, argv) {
|
|
|
1173
1333
|
}
|
|
1174
1334
|
return path.length === 0 ? undefined : path.join(" ");
|
|
1175
1335
|
}
|
|
1176
|
-
async function runMain({ name, version, run, redact, usage }, argv =
|
|
1336
|
+
async function runMain({ name, version, run, redact, usage }, argv = process12.argv.slice(2)) {
|
|
1177
1337
|
if (argv[0] === COMPLETE)
|
|
1178
1338
|
return printCandidates(leafCandidates(run, argv.slice(1)));
|
|
1179
1339
|
if (argv.length === 1 && (argv[0] === "--version" || argv[0] === "-v")) {
|
|
@@ -1193,6 +1353,8 @@ async function runMain({ name, version, run, redact, usage }, argv = process10.a
|
|
|
1193
1353
|
}
|
|
1194
1354
|
return code;
|
|
1195
1355
|
}
|
|
1356
|
+
// ../kit/src/folders.ts
|
|
1357
|
+
var SKIPPED_FOLDERS = new Set(["node_modules", "vendor", "dist", "build", "target", "coverage", "venv", "__pycache__"]);
|
|
1196
1358
|
// ../kit/src/secrets.ts
|
|
1197
1359
|
var SECRET_WORDS = new Set(["key", "pass", "pwd", "auth", "salt", "cookie", "session", "jwt", "ssn", "iban", "card", "cvv", "otp"]);
|
|
1198
1360
|
// ../kit/src/sessions.ts
|
|
@@ -1200,40 +1362,40 @@ var {Glob } = globalThis.Bun;
|
|
|
1200
1362
|
var SESSION_FILES = new Glob("*.json");
|
|
1201
1363
|
var ACCOUNT_DIRS = new Glob("*/sessions");
|
|
1202
1364
|
// ../kit/src/workspace-root.ts
|
|
1203
|
-
import { existsSync, mkdirSync as mkdirSync2, readFileSync as
|
|
1204
|
-
import { homedir as
|
|
1205
|
-
import { dirname as
|
|
1206
|
-
import
|
|
1365
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync } from "fs";
|
|
1366
|
+
import { homedir as homedir4 } from "os";
|
|
1367
|
+
import { dirname as dirname4, join as join6, resolve as resolve3 } from "path";
|
|
1368
|
+
import process13 from "process";
|
|
1207
1369
|
function expandHome(path, home) {
|
|
1208
1370
|
if (path === "~")
|
|
1209
1371
|
return home;
|
|
1210
|
-
return path.startsWith("~/") ?
|
|
1372
|
+
return path.startsWith("~/") ? join6(home, path.slice(2)) : path;
|
|
1211
1373
|
}
|
|
1212
1374
|
function configured(env, home) {
|
|
1213
1375
|
const path = userConfigPath("config", { home, xdg: env.XDG_CONFIG_HOME });
|
|
1214
|
-
if (!
|
|
1376
|
+
if (!existsSync4(path))
|
|
1215
1377
|
return;
|
|
1216
1378
|
try {
|
|
1217
|
-
const parsed = JSON.parse(
|
|
1379
|
+
const parsed = JSON.parse(readFileSync3(path, "utf8"));
|
|
1218
1380
|
const folder = typeof parsed === "object" && parsed !== null ? parsed.workspace : undefined;
|
|
1219
1381
|
return typeof folder === "string" && folder !== "" ? folder : undefined;
|
|
1220
1382
|
} catch {
|
|
1221
1383
|
return;
|
|
1222
1384
|
}
|
|
1223
1385
|
}
|
|
1224
|
-
function workspaceRoot({ given, env =
|
|
1386
|
+
function workspaceRoot({ given, env = process13.env, home = homedir4() } = {}) {
|
|
1225
1387
|
const fromEnv = env.KAYNOOO_WORKSPACE === "" ? undefined : env.KAYNOOO_WORKSPACE;
|
|
1226
|
-
return
|
|
1388
|
+
return resolve3(expandHome(given ?? fromEnv ?? configured(env, home) ?? join6(home, "Workspace"), home));
|
|
1227
1389
|
}
|
|
1228
|
-
function workspaceChosen({ env =
|
|
1390
|
+
function workspaceChosen({ env = process13.env, home = homedir4() } = {}) {
|
|
1229
1391
|
return (env.KAYNOOO_WORKSPACE ?? "") !== "" || configured(env, home) !== undefined;
|
|
1230
1392
|
}
|
|
1231
|
-
function saveWorkspace(folder, { env =
|
|
1393
|
+
function saveWorkspace(folder, { env = process13.env, home = homedir4() } = {}) {
|
|
1232
1394
|
const path = userConfigPath("config", { home, xdg: env.XDG_CONFIG_HOME });
|
|
1233
1395
|
let current = {};
|
|
1234
|
-
if (
|
|
1396
|
+
if (existsSync4(path)) {
|
|
1235
1397
|
try {
|
|
1236
|
-
const parsed = JSON.parse(
|
|
1398
|
+
const parsed = JSON.parse(readFileSync3(path, "utf8"));
|
|
1237
1399
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
|
|
1238
1400
|
return false;
|
|
1239
1401
|
current = parsed;
|
|
@@ -1241,19 +1403,19 @@ function saveWorkspace(folder, { env = process11.env, home = homedir3() } = {})
|
|
|
1241
1403
|
return false;
|
|
1242
1404
|
}
|
|
1243
1405
|
}
|
|
1244
|
-
mkdirSync2(
|
|
1406
|
+
mkdirSync2(dirname4(path), { recursive: true });
|
|
1245
1407
|
writeFileSync(path, `${JSON.stringify({ ...current, workspace: folder }, undefined, 2)}
|
|
1246
1408
|
`);
|
|
1247
1409
|
return true;
|
|
1248
1410
|
}
|
|
1249
1411
|
// src/completions.ts
|
|
1250
|
-
import { existsSync as
|
|
1251
|
-
import { join as
|
|
1252
|
-
import
|
|
1412
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync3, readdirSync, readFileSync as readFileSync5, rmSync } from "fs";
|
|
1413
|
+
import { join as join8 } from "path";
|
|
1414
|
+
import process15 from "process";
|
|
1253
1415
|
|
|
1254
1416
|
// src/checks.ts
|
|
1255
|
-
import { existsSync as
|
|
1256
|
-
import { basename, join as
|
|
1417
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
|
|
1418
|
+
import { basename, join as join7 } from "path";
|
|
1257
1419
|
|
|
1258
1420
|
// src/catalog.ts
|
|
1259
1421
|
var HOOKS_REQUIRES = "@kaynooo/cli-guard";
|
|
@@ -1329,7 +1491,7 @@ function readJsonText(text) {
|
|
|
1329
1491
|
}
|
|
1330
1492
|
}
|
|
1331
1493
|
function readJson(path) {
|
|
1332
|
-
return
|
|
1494
|
+
return existsSync5(path) ? readJsonText(readFileSync4(path, "utf8")) : undefined;
|
|
1333
1495
|
}
|
|
1334
1496
|
function firstLine(text) {
|
|
1335
1497
|
const trimmed = text.trim();
|
|
@@ -1338,7 +1500,7 @@ function firstLine(text) {
|
|
|
1338
1500
|
return end === -1 ? trimmed : trimmed.slice(0, end);
|
|
1339
1501
|
}
|
|
1340
1502
|
function installed(host, name) {
|
|
1341
|
-
return readJson(
|
|
1503
|
+
return readJson(join7(host.globalModules, name, "package.json"));
|
|
1342
1504
|
}
|
|
1343
1505
|
function isInstalled(host, name) {
|
|
1344
1506
|
return installed(host, name)?.version !== undefined;
|
|
@@ -1346,7 +1508,7 @@ function isInstalled(host, name) {
|
|
|
1346
1508
|
function ownBin(host, name, bin) {
|
|
1347
1509
|
const declared = installed(host, name)?.bin;
|
|
1348
1510
|
const file = typeof declared === "string" ? declared : declared?.[bin];
|
|
1349
|
-
return file === undefined ? undefined :
|
|
1511
|
+
return file === undefined ? undefined : join7(host.globalModules, name, file);
|
|
1350
1512
|
}
|
|
1351
1513
|
async function checkTool(host, name, bins) {
|
|
1352
1514
|
const check = name.slice(name.indexOf("/") + 1);
|
|
@@ -1361,17 +1523,29 @@ async function checkTool(host, name, bins) {
|
|
|
1361
1523
|
if (latest !== "" && Bun.semver.order(version, latest) < 0)
|
|
1362
1524
|
return { check, ok: false, detail: `${version}, ${latest} released${off.length > 0 ? `, ${off.join(", ")} not on the PATH` : ""}`, fix: `bun install -g ${name}@${latest}` };
|
|
1363
1525
|
if (off.length > 0)
|
|
1364
|
-
return { check, ok: false, detail: `${version}, ${off.join(", ")} not on the PATH`, fix: `add ${
|
|
1526
|
+
return { check, ok: false, detail: `${version}, ${off.join(", ")} not on the PATH`, fix: `add ${join7(host.globalModules, "..", "..", "..", "bin")} to the PATH` };
|
|
1365
1527
|
if (latest === "")
|
|
1366
1528
|
return { check, ok: true, detail: `${version}, the registry did not answer: ${firstLine(info.stderr) || answered || "no version"}` };
|
|
1367
1529
|
return { check, ok: true, detail: version };
|
|
1368
1530
|
}
|
|
1531
|
+
function extraForgeHosts(home) {
|
|
1532
|
+
try {
|
|
1533
|
+
const path = userConfigReadPath("doctor", [], { home, xdg: process.env.XDG_CONFIG_HOME });
|
|
1534
|
+
if (!existsSync5(path))
|
|
1535
|
+
return [];
|
|
1536
|
+
const raw = Bun.JSONC.parse(readFileSync4(path, "utf8"));
|
|
1537
|
+
return Array.isArray(raw?.forgeHosts) ? raw.forgeHosts.filter((each) => typeof each === "string") : [];
|
|
1538
|
+
} catch {
|
|
1539
|
+
return [];
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1369
1542
|
async function checkTea(host) {
|
|
1370
1543
|
if (host.which("tea") === null)
|
|
1371
1544
|
return { check: "tea", ok: false, detail: "not installed, and ft goes through it", fix: "install tea from https://gitea.com/gitea/tea/releases" };
|
|
1372
1545
|
const logins = await host.run(["tea", "login", "list", "-o", "json"]);
|
|
1373
1546
|
const parsed = logins.code === 0 ? readJsonText(logins.stdout) : undefined;
|
|
1374
|
-
const
|
|
1547
|
+
const hosts = new Set([...FORGE_HOSTS, ...extraForgeHosts(host.home)]);
|
|
1548
|
+
const login = parsed?.find((each) => URL.canParse(each.url) && hosts.has(new URL(each.url).host));
|
|
1375
1549
|
if (login === undefined)
|
|
1376
1550
|
return { check: "tea", ok: false, detail: `no login to ${FORGE}`, fix: `tea login add --url https://${FORGE} --oauth` };
|
|
1377
1551
|
const version = await host.run(["tea", "api", "--login", login.name, "version"]);
|
|
@@ -1382,9 +1556,9 @@ async function checkTea(host) {
|
|
|
1382
1556
|
return { check: "tea", ok: true, detail: `logged in to ${forge} as ${login.name}, Forgejo ${served}` };
|
|
1383
1557
|
}
|
|
1384
1558
|
function hooksWiring(host) {
|
|
1385
|
-
const settings = readJson(
|
|
1559
|
+
const settings = readJson(join7(host.home, ".claude", "settings.json")) ?? {};
|
|
1386
1560
|
const plugin = settings.enabledPlugins?.[PLUGIN] === true;
|
|
1387
|
-
const mod = (settings.env?.CLAUDE_CODE_PLUGIN_DIRS ?? "").split(":").map((dir) => dir.replace(LEADING_TILDE, host.home)).find((dir) => basename(dir) === "cli" &&
|
|
1561
|
+
const mod = (settings.env?.CLAUDE_CODE_PLUGIN_DIRS ?? "").split(":").map((dir) => dir.replace(LEADING_TILDE, host.home)).find((dir) => basename(dir) === "cli" && existsSync5(join7(dir, "hooks", "hooks.json")));
|
|
1388
1562
|
return { plugin, mod };
|
|
1389
1563
|
}
|
|
1390
1564
|
function checkHooks(host) {
|
|
@@ -1427,7 +1601,7 @@ var FORGEJO = "@kaynooo/cli-forgejo";
|
|
|
1427
1601
|
var WORKSPACE = "@kaynooo/cli-workspace";
|
|
1428
1602
|
function checkWorkspace(host, env = process.env) {
|
|
1429
1603
|
const folder = workspaceRoot({ home: host.home, env });
|
|
1430
|
-
if (
|
|
1604
|
+
if (existsSync5(folder))
|
|
1431
1605
|
return { check: "workspace", ok: true, detail: folder };
|
|
1432
1606
|
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` };
|
|
1433
1607
|
}
|
|
@@ -1479,14 +1653,36 @@ var BASH_FUNCTION = `__kaynooo_cli_complete() {
|
|
|
1479
1653
|
done
|
|
1480
1654
|
COMPREPLY+=($(compgen -W "$words" -- "$cur"))
|
|
1481
1655
|
}`;
|
|
1656
|
+
var ZSH_FUNCTION = `__kaynooo_cli_complete() {
|
|
1657
|
+
local line
|
|
1658
|
+
local -a described plain
|
|
1659
|
+
for line in \${(f)"$("\${words[1]}" __complete "\${(@)words[2,CURRENT]}" 2>/dev/null)"}; do
|
|
1660
|
+
if [[ $line == __files__ ]]; then
|
|
1661
|
+
_files
|
|
1662
|
+
elif [[ $line == *$'\\t'* ]]; then
|
|
1663
|
+
described+=("\${\${line%%$'\\t'*}//:/\\\\:}:\${line#*$'\\t'}")
|
|
1664
|
+
else
|
|
1665
|
+
plain+=("$line")
|
|
1666
|
+
fi
|
|
1667
|
+
done
|
|
1668
|
+
(( \${#described} )) && _describe -V value described
|
|
1669
|
+
(( \${#plain} )) && compadd -- "\${plain[@]}"
|
|
1670
|
+
return 0
|
|
1671
|
+
}`;
|
|
1672
|
+
function zshScript(bins) {
|
|
1673
|
+
return `# Generated by \`cli completions zsh\`
|
|
1674
|
+
${ZSH_FUNCTION}
|
|
1675
|
+
compdef __kaynooo_cli_complete ${bins.join(" ")}
|
|
1676
|
+
`;
|
|
1677
|
+
}
|
|
1482
1678
|
function bashScript(bins) {
|
|
1483
1679
|
return `# Generated by \`cli completions bash\`
|
|
1484
1680
|
${BASH_FUNCTION}
|
|
1485
1681
|
complete -F __kaynooo_cli_complete ${bins.join(" ")}
|
|
1486
1682
|
`;
|
|
1487
1683
|
}
|
|
1488
|
-
function completionDir(home, env =
|
|
1489
|
-
return
|
|
1684
|
+
function completionDir(home, env = process15.env) {
|
|
1685
|
+
return join8(env.XDG_CACHE_HOME ?? join8(home, ".cache"), "fish", "kaynooo-cli");
|
|
1490
1686
|
}
|
|
1491
1687
|
function installedBins(host) {
|
|
1492
1688
|
return Object.values(TOOLS).flat().filter((bin) => host.which(bin) !== null);
|
|
@@ -1500,18 +1696,55 @@ complete -c ${bin} -f -a '(__kaynooo_cli_complete ${bin})'
|
|
|
1500
1696
|
async function installCompletions(host, dir) {
|
|
1501
1697
|
const bins = installedBins(host);
|
|
1502
1698
|
mkdirSync3(dir, { recursive: true });
|
|
1503
|
-
const removed = readdirSync(dir).filter((name) => FISH_FILE.test(name) && !bins.includes(name.replace(FISH_FILE, "")) &&
|
|
1699
|
+
const removed = readdirSync(dir).filter((name) => FISH_FILE.test(name) && !bins.includes(name.replace(FISH_FILE, "")) && readFileSync5(join8(dir, name), "utf8").startsWith(MARKER));
|
|
1504
1700
|
for (const name of removed)
|
|
1505
|
-
rmSync(
|
|
1506
|
-
await Promise.all(bins.map(async (bin) => Bun.write(
|
|
1701
|
+
rmSync(join8(dir, name));
|
|
1702
|
+
await Promise.all(bins.map(async (bin) => Bun.write(join8(dir, `${bin}.fish`), completionFile(bin))));
|
|
1507
1703
|
return { written: bins, removed: removed.map((name) => name.replace(FISH_FILE, "")) };
|
|
1508
1704
|
}
|
|
1509
1705
|
function missingCompletions(host, dir) {
|
|
1510
|
-
return installedBins(host).filter((bin) => !
|
|
1706
|
+
return installedBins(host).filter((bin) => !existsSync6(join8(dir, `${bin}.fish`)));
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
// src/config-report.ts
|
|
1710
|
+
import { existsSync as existsSync7, readFileSync as readFileSync6 } from "fs";
|
|
1711
|
+
import { join as join9, resolve as resolve4 } from "path";
|
|
1712
|
+
import process16 from "process";
|
|
1713
|
+
var USER_FILES = [["config", []], ["theme", []], ["doctor", []], ["ft", [["ft", "config.json"]]], ["db", [["db", "config.json"]]], ["pkg", []], ["pkg-excluded", []], ["repos", []]];
|
|
1714
|
+
var PROJECT_FILES = [join9(".kaynooo", "theme.json"), ".pkgrc.json"];
|
|
1715
|
+
var ENV = ["KAYNOOO_WORKSPACE", "KAYNOOO_CLI_NO_USAGE", "KIT_MODE", "KIT_THEME", "KIT_HELP", "CI", "CLAUDECODE", "AI_AGENT", "NO_COLOR", "FORCE_COLOR", "NODE_DISABLE_COLORS", "COLORTERM", "TERM", "BROWSER", "EDITOR", "VISUAL", "SLOT_DIR", "XDG_CONFIG_HOME", "XDG_CACHE_HOME", "XDG_STATE_HOME", "XDG_RUNTIME_DIR", "BUN_INSTALL", "COMPOSE_FILE", "COMPOSE_PATH_SEPARATOR", "CLAUDE_CONFIG_DIR", "CLAUDE_CODE_TMPDIR", "GITHUB_TOKEN", "DISCORD_WEBHOOK"];
|
|
1716
|
+
var SECRETS = new Set(["GITHUB_TOKEN", "DISCORD_WEBHOOK"]);
|
|
1717
|
+
function fileStatus(path) {
|
|
1718
|
+
if (!existsSync7(path))
|
|
1719
|
+
return "absent";
|
|
1720
|
+
try {
|
|
1721
|
+
Bun.JSONC.parse(readFileSync6(path, "utf8"));
|
|
1722
|
+
return "set";
|
|
1723
|
+
} catch {
|
|
1724
|
+
return "invalid";
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
function configReport(host, env = process16.env, from = process16.cwd()) {
|
|
1728
|
+
const sources = USER_FILES.map(([bin, legacy]) => {
|
|
1729
|
+
const where = userConfigReadPath(bin, legacy, { home: host.home, xdg: env.XDG_CONFIG_HOME });
|
|
1730
|
+
return { kind: "file", name: `${bin}.json`, where, status: fileStatus(where) };
|
|
1731
|
+
});
|
|
1732
|
+
for (const name of PROJECT_FILES) {
|
|
1733
|
+
const where = findUp(name, from, (dir) => dir === resolve4(host.home) || existsSync7(join9(dir, ".git")));
|
|
1734
|
+
sources.push({ kind: "file", name, where: where ?? `${name} (none found upward from ${from})`, status: where === undefined ? "absent" : fileStatus(where) });
|
|
1735
|
+
}
|
|
1736
|
+
for (const name of ENV) {
|
|
1737
|
+
const value = env[name];
|
|
1738
|
+
sources.push({ kind: "env", name, where: "environment", status: value === undefined || value === "" ? "absent" : "set", ...value === undefined || value === "" ? {} : { value: SECRETS.has(name) ? "(hidden)" : value } });
|
|
1739
|
+
}
|
|
1740
|
+
const { sources: themes, preset } = loadTheme({ env, from, home: host.home, warn: () => {
|
|
1741
|
+
return;
|
|
1742
|
+
} });
|
|
1743
|
+
return { workspace: workspaceRoot({ env, home: host.home }), theme: { preset, sources: themes }, sources };
|
|
1511
1744
|
}
|
|
1512
1745
|
|
|
1513
1746
|
// src/doctor.ts
|
|
1514
|
-
import
|
|
1747
|
+
import process20 from "process";
|
|
1515
1748
|
|
|
1516
1749
|
// src/fix.ts
|
|
1517
1750
|
var INSTALL = /^bun install -g (\S+)$/;
|
|
@@ -1551,19 +1784,19 @@ async function applyFixes(host, steps, dry) {
|
|
|
1551
1784
|
}
|
|
1552
1785
|
|
|
1553
1786
|
// src/host.ts
|
|
1554
|
-
import { homedir as
|
|
1555
|
-
import { join as
|
|
1556
|
-
import
|
|
1787
|
+
import { homedir as homedir5 } from "os";
|
|
1788
|
+
import { join as join10 } from "path";
|
|
1789
|
+
import process17 from "process";
|
|
1557
1790
|
var RUN_TIMEOUT_MS = 20000;
|
|
1558
1791
|
function hostHere() {
|
|
1559
|
-
const home =
|
|
1792
|
+
const home = homedir5();
|
|
1560
1793
|
return {
|
|
1561
1794
|
home,
|
|
1562
|
-
globalModules:
|
|
1795
|
+
globalModules: join10(process17.env.BUN_INSTALL ?? join10(home, ".bun"), "install", "global", "node_modules"),
|
|
1563
1796
|
bunVersion: Bun.version,
|
|
1564
1797
|
which: (bin) => Bun.which(bin),
|
|
1565
1798
|
run: async (argv, env) => {
|
|
1566
|
-
const ran = await runCommand(argv, { timeoutMs: RUN_TIMEOUT_MS, ...env === undefined ? {} : { env: { ...
|
|
1799
|
+
const ran = await runCommand(argv, { timeoutMs: RUN_TIMEOUT_MS, ...env === undefined ? {} : { env: { ...process17.env, ...env } } });
|
|
1567
1800
|
if (ran.timedOut)
|
|
1568
1801
|
return { code: 1, stdout: "", stderr: `timed out after ${RUN_TIMEOUT_MS / 1000}s` };
|
|
1569
1802
|
const of = (stream) => Buffer.concat(ran.chunks.filter((chunk) => chunk.stream === stream).map((chunk) => chunk.data)).toString();
|
|
@@ -1573,19 +1806,53 @@ function hostHere() {
|
|
|
1573
1806
|
}
|
|
1574
1807
|
|
|
1575
1808
|
// src/install.ts
|
|
1576
|
-
import
|
|
1809
|
+
import process19 from "process";
|
|
1577
1810
|
|
|
1578
1811
|
// ../kit/src/prompt.ts
|
|
1579
|
-
import
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
var
|
|
1583
|
-
var
|
|
1812
|
+
import process18 from "process";
|
|
1813
|
+
|
|
1814
|
+
// ../kit/src/picker/score.ts
|
|
1815
|
+
var BOUNDARY_AFTER = new Set([" ", "/", "-", "_", ".", ":", "\\", "(", "[", ",", "\t"]);
|
|
1816
|
+
var NONE = Number.NEGATIVE_INFINITY;
|
|
1817
|
+
|
|
1818
|
+
// ../kit/src/picker/state.ts
|
|
1819
|
+
var IS_CONTROL = /[\x00-\x1F\x7F-\x9F]/;
|
|
1584
1820
|
var PASTE_START = "\x1B[200~";
|
|
1585
1821
|
var PASTE_END = "\x1B[201~";
|
|
1586
|
-
var
|
|
1587
|
-
|
|
1822
|
+
var KEY_SEQUENCES = /\x1B\[[0-9;]*[A-Z~]|\x1BO[A-Z]|\x1B[\s\S]|[\s\S]/gu;
|
|
1823
|
+
var KEYS = {
|
|
1824
|
+
"\x1B[A": "up",
|
|
1825
|
+
"\x1BOA": "up",
|
|
1826
|
+
"\x10": "up",
|
|
1827
|
+
"\x1B[B": "down",
|
|
1828
|
+
"\x1BOB": "down",
|
|
1829
|
+
"\x0E": "down",
|
|
1830
|
+
"\x1B[5~": "pageup",
|
|
1831
|
+
"\x1B[6~": "pagedown",
|
|
1832
|
+
"\x1B[H": "home",
|
|
1833
|
+
"\x1B[1~": "home",
|
|
1834
|
+
"\x1BOH": "home",
|
|
1835
|
+
"\x1B[F": "end",
|
|
1836
|
+
"\x1B[4~": "end",
|
|
1837
|
+
"\x1BOF": "end",
|
|
1838
|
+
"\r": "enter",
|
|
1839
|
+
"\n": "enter",
|
|
1840
|
+
"\x03": "cancel",
|
|
1841
|
+
"\x1B": "cancel",
|
|
1842
|
+
"\x7F": "erase",
|
|
1843
|
+
"\b": "erase",
|
|
1844
|
+
"\x15": "clear",
|
|
1845
|
+
"\x17": "word",
|
|
1846
|
+
"\t": "tab",
|
|
1847
|
+
"\x1F": "help",
|
|
1848
|
+
"\x1BOP": "help",
|
|
1849
|
+
"\x1B[11~": "help"
|
|
1850
|
+
};
|
|
1851
|
+
function parseKeys(input, paste = { active: false }, ctrls = []) {
|
|
1852
|
+
const codes = new Map(ctrls.map((name) => [String.fromCharCode(name.codePointAt(name.length - 1) - 96), name]));
|
|
1588
1853
|
return (input.match(KEY_SEQUENCES) ?? []).flatMap((sequence) => {
|
|
1854
|
+
const named = codes.get(sequence);
|
|
1855
|
+
const key = named === undefined ? KEYS[sequence] : { ctrl: named };
|
|
1589
1856
|
if (sequence === PASTE_START || sequence === PASTE_END) {
|
|
1590
1857
|
paste.active = sequence === PASTE_START;
|
|
1591
1858
|
return [];
|
|
@@ -1593,9 +1860,19 @@ function readKeys(input, paste = { active: false }) {
|
|
|
1593
1860
|
if (paste.active && (sequence === "\r" || sequence === `
|
|
1594
1861
|
`))
|
|
1595
1862
|
return [];
|
|
1596
|
-
|
|
1863
|
+
if (key !== undefined)
|
|
1864
|
+
return [key];
|
|
1865
|
+
return IS_CONTROL.test(sequence) || sequence.startsWith("\x1B") ? [] : [{ text: sequence }];
|
|
1597
1866
|
});
|
|
1598
1867
|
}
|
|
1868
|
+
|
|
1869
|
+
// ../kit/src/prompt.ts
|
|
1870
|
+
var WORDS = /\s+/;
|
|
1871
|
+
var MAX_ROWS = 10;
|
|
1872
|
+
var PROMPT_KEYS = new Set(["up", "down", "pageup", "pagedown", "home", "end", "enter", "cancel", "erase"]);
|
|
1873
|
+
function readKeys(input, paste = { active: false }) {
|
|
1874
|
+
return parseKeys(input, paste).filter((key) => typeof key === "object" ? "text" in key : PROMPT_KEYS.has(key));
|
|
1875
|
+
}
|
|
1599
1876
|
function flip(checked, value) {
|
|
1600
1877
|
const next = new Set(checked);
|
|
1601
1878
|
if (!next.delete(value))
|
|
@@ -1735,7 +2012,7 @@ function asking(colors, question, typed, keys) {
|
|
|
1735
2012
|
function answer(colors, question, state, picked) {
|
|
1736
2013
|
return state === "enter" ? `${colors.green("\u2713")} ${question} ${colors.cyan(picked)}` : `${colors.red("\u2717")} ${question} ${colors.dim("cancelled")}`;
|
|
1737
2014
|
}
|
|
1738
|
-
async function ask(question, { hint, terminal =
|
|
2015
|
+
async function ask(question, { hint, terminal = process18 }, render, press) {
|
|
1739
2016
|
const { stdin, stderr } = terminal;
|
|
1740
2017
|
if (!isInteractive(terminal))
|
|
1741
2018
|
fail(`cannot ask "${question}" off a terminal`, 2, hint);
|
|
@@ -1870,7 +2147,7 @@ async function askWorkspace(host) {
|
|
|
1870
2147
|
async function install(host, options, recheck, choosing = ask2) {
|
|
1871
2148
|
const all = candidates2(host);
|
|
1872
2149
|
const pool = all.filter((each) => !each.installed);
|
|
1873
|
-
const { dim, green, red, yellow } = paint(
|
|
2150
|
+
const { dim, green, red, yellow } = paint(process19.stdout);
|
|
1874
2151
|
if (options.list) {
|
|
1875
2152
|
for (const each of pool)
|
|
1876
2153
|
console.log(`${each.label.padEnd(12)} ${dim(each.hint)}`);
|
|
@@ -1978,7 +2255,7 @@ var OPTIONS = {
|
|
|
1978
2255
|
function print(results) {
|
|
1979
2256
|
if (results.length === 0)
|
|
1980
2257
|
return;
|
|
1981
|
-
const { green, red, dim } = paint(
|
|
2258
|
+
const { green, red, dim } = paint(process20.stdout);
|
|
1982
2259
|
const width = Math.max(...results.map((result) => result.check.length));
|
|
1983
2260
|
for (const { check, ok, detail, fix } of results)
|
|
1984
2261
|
console.log(`${ok ? green("ok ") : red("FAIL")} ${check.padEnd(width)} ${detail}${fix === undefined ? "" : ` ${dim("fix:")} ${fix}`}`);
|
|
@@ -1988,7 +2265,7 @@ function print(results) {
|
|
|
1988
2265
|
${dim("fix them all:")} ${INSTALL2}${installs.join(" ")}`);
|
|
1989
2266
|
}
|
|
1990
2267
|
function printOutcomes(outcomes, log) {
|
|
1991
|
-
const { green, red, dim } = paint(
|
|
2268
|
+
const { green, red, dim } = paint(process20.stdout);
|
|
1992
2269
|
const marks = { ran: green("ran "), failed: red("failed "), "dry-run": dim("would "), "not run": dim("not run") };
|
|
1993
2270
|
for (const { command, status, detail } of outcomes)
|
|
1994
2271
|
log(`${marks[status]} ${command}${detail === undefined ? "" : ` ${dim(detail)}`}`);
|
|
@@ -2028,7 +2305,7 @@ after the fixes:`);
|
|
|
2028
2305
|
const absent = Object.keys(TOOLS).filter((name) => !isInstalled(host, name)).length;
|
|
2029
2306
|
if (!json && only.length === 0 && absent > 0)
|
|
2030
2307
|
console.log(`
|
|
2031
|
-
${paint(
|
|
2308
|
+
${paint(process20.stdout).dim(`${absent} packages not installed: doctor --install`)}`);
|
|
2032
2309
|
return results.every((result) => result.ok) ? 0 : 1;
|
|
2033
2310
|
}
|
|
2034
2311
|
|
|
@@ -2057,7 +2334,7 @@ var LIST_OPTIONS = {
|
|
|
2057
2334
|
json: jsonOption("[{bin, package, installed, synopsis?, summary?, replaces?, examples?}]")
|
|
2058
2335
|
};
|
|
2059
2336
|
function print2(listed) {
|
|
2060
|
-
const { cyan, dim } = paint(
|
|
2337
|
+
const { cyan, dim } = paint(process21.stdout);
|
|
2061
2338
|
const width = Math.max(...listed.map((each) => each.bin.length));
|
|
2062
2339
|
for (const each of listed) {
|
|
2063
2340
|
const what = !each.installed ? dim(`not installed: bun install -g ${each.package}`) : `${each.summary ?? ""}${each.replaces === undefined ? "" : dim(`; instead of ${each.replaces}`)}`;
|
|
@@ -2073,26 +2350,24 @@ async function list(args, host = hostHere()) {
|
|
|
2073
2350
|
print2(listed);
|
|
2074
2351
|
return 0;
|
|
2075
2352
|
}
|
|
2076
|
-
var COMPLETIONS_USAGE = `cli completions <fish|bash> [--install] [--dir <folder>]: shell completions for every installed bin of the @kaynooo/cli-* packages
|
|
2353
|
+
var COMPLETIONS_USAGE = `cli completions <fish|bash|zsh> [--install] [--dir <folder>]: shell completions for every installed bin of the @kaynooo/cli-* packages
|
|
2077
2354
|
|
|
2078
2355
|
Each bin completes itself through \`<bin> __complete\`; this writes the glue a shell needs per bin.
|
|
2079
2356
|
fish: without --install the files print on stdout, with it they go to ~/.cache/fish/kaynooo-cli (or $XDG_CACHE_HOME), one <bin>.fish each, the stale ones this command wrote taken out. That folder has to be in fish_complete_path, first so a completion fish ships for the same name does not win: \`set -p fish_complete_path ~/.cache/fish/kaynooo-cli\`.
|
|
2080
2357
|
bash: one script on stdout, sourced from ~/.bashrc with \`source <(cli completions bash)\`; --install and --dir are fish's.
|
|
2081
|
-
zsh:
|
|
2358
|
+
zsh: one script on stdout like bash's, after compinit in ~/.zshrc: \`autoload -U +X compinit && compinit\`, then \`source <(cli completions zsh)\` (written without zsh at hand to run it, so untested).`;
|
|
2082
2359
|
var COMPLETIONS_OPTIONS = {
|
|
2083
2360
|
install: { type: "boolean", description: "write the files to the folder instead of printing them" },
|
|
2084
2361
|
dir: { type: "string", short: "d", description: "the folder to write to, instead of ~/.cache/fish/kaynooo-cli" }
|
|
2085
2362
|
};
|
|
2086
2363
|
async function completions(args, host = hostHere()) {
|
|
2087
|
-
const { values, positionals } = parse(args, COMPLETIONS_USAGE, COMPLETIONS_OPTIONS, { positionals: ["<shell>"], exits: [[0, "it printed or wrote them"], [2, "a usage error or a shell without a script"]], candidates: { "<shell>": () => ["fish", "bash"] }, examples: ["cli completions fish", ["cli completions fish --install", "write one file per bin"], ["source <(cli completions bash)", "in ~/.bashrc"]] });
|
|
2088
|
-
if (positionals[0]
|
|
2089
|
-
badUsage("
|
|
2090
|
-
if (positionals[0]
|
|
2091
|
-
badUsage("completions exist for fish and bash: cli completions fish");
|
|
2092
|
-
if (positionals[0] === "bash") {
|
|
2364
|
+
const { values, positionals } = parse(args, COMPLETIONS_USAGE, COMPLETIONS_OPTIONS, { positionals: ["<shell>"], exits: [[0, "it printed or wrote them"], [2, "a usage error or a shell without a script"]], candidates: { "<shell>": () => ["fish", "bash", "zsh"] }, examples: ["cli completions fish", ["cli completions fish --install", "write one file per bin"], ["source <(cli completions bash)", "in ~/.bashrc"], ["source <(cli completions zsh)", "in ~/.zshrc after compinit"]] });
|
|
2365
|
+
if (positionals[0] !== "fish" && positionals[0] !== "bash" && positionals[0] !== "zsh")
|
|
2366
|
+
badUsage("completions exist for fish, bash and zsh: cli completions fish");
|
|
2367
|
+
if (positionals[0] === "bash" || positionals[0] === "zsh") {
|
|
2093
2368
|
if (values.install === true || values.dir !== undefined)
|
|
2094
|
-
badUsage(
|
|
2095
|
-
console.log(bashScript(installedBins(host)).trimEnd());
|
|
2369
|
+
badUsage(`--install and --dir are for fish: source the ${positionals[0]} script instead`);
|
|
2370
|
+
console.log((positionals[0] === "zsh" ? zshScript : bashScript)(installedBins(host)).trimEnd());
|
|
2096
2371
|
return 0;
|
|
2097
2372
|
}
|
|
2098
2373
|
if (values.install !== true) {
|
|
@@ -2113,6 +2388,31 @@ var STATS_OPTIONS = {
|
|
|
2113
2388
|
since: { type: "string", description: "only the runs this recent (7d, 12h)" },
|
|
2114
2389
|
json: jsonOption("{runs, bins: [{bin, runs, failed, usageErrors}], commands: [{bin, command, runs, failed, usageErrors}], usageErrors: [{bin, command?, unknown, count}]}")
|
|
2115
2390
|
};
|
|
2391
|
+
var CONFIG_USAGE = `cli config [--json]: where every bin looks for its config and which environment variables are set
|
|
2392
|
+
|
|
2393
|
+
Lists each user and project config file with the path read and whether it is there (set, absent, or invalid when it does not parse), then the main environment variables that change a bin's behaviour (docs/config.md has the rest); a token or webhook shows as hidden. Ends on the workspace folder and the theme in force. docs/config.md in the repository describes every key.
|
|
2394
|
+
Exits 0 when it printed the report, 2 on a usage error.`;
|
|
2395
|
+
var CONFIG_OPTIONS = {
|
|
2396
|
+
json: jsonOption("{workspace, theme: {preset, sources}, sources: [{kind, name, where, status, value?}]}")
|
|
2397
|
+
};
|
|
2398
|
+
function config2(args, host = hostHere()) {
|
|
2399
|
+
const { values } = parse(args, CONFIG_USAGE, CONFIG_OPTIONS, { positionals: [], examples: ["cli config", ["cli config --json", "for a script"]] });
|
|
2400
|
+
const report = configReport(host);
|
|
2401
|
+
if (values.json === true) {
|
|
2402
|
+
printJson(report);
|
|
2403
|
+
return 0;
|
|
2404
|
+
}
|
|
2405
|
+
const { dim } = paint(process21.stdout);
|
|
2406
|
+
console.log(table(report.sources.filter((each) => each.kind === "file").map((each) => [each.name, each.status === "absent" ? dim(each.status) : each.status, dim(each.where)])));
|
|
2407
|
+
const variables = report.sources.filter((each) => each.kind === "env" && each.status === "set");
|
|
2408
|
+
console.log(variables.length === 0 ? `
|
|
2409
|
+
${dim("no variable set")}` : `
|
|
2410
|
+
${table(variables.map((each) => [each.name, each.value ?? ""]))}`);
|
|
2411
|
+
console.log(`
|
|
2412
|
+
workspace ${report.workspace}
|
|
2413
|
+
theme ${report.theme.preset}${report.theme.sources.length === 0 ? "" : `, from ${report.theme.sources.join(", ")}`}`);
|
|
2414
|
+
return 0;
|
|
2415
|
+
}
|
|
2116
2416
|
async function stats(args, path = usagePath()) {
|
|
2117
2417
|
const { values } = parse(args, STATS_USAGE, STATS_OPTIONS, { positionals: [], examples: ["cli stats", ["cli stats --since 7d", "the last week"]] });
|
|
2118
2418
|
const window = values.since === undefined ? undefined : duration(values.since, "since", 0, "h");
|
|
@@ -2140,7 +2440,7 @@ ${table(summary.usageErrors.map((each) => [`${each.bin}${each.command === undefi
|
|
|
2140
2440
|
return 0;
|
|
2141
2441
|
}
|
|
2142
2442
|
if (import.meta.main) {
|
|
2143
|
-
|
|
2443
|
+
process21.exitCode = await runCli({
|
|
2144
2444
|
name: "cli",
|
|
2145
2445
|
about: "the bins of the @kaynooo/cli-* packages, described by their own help",
|
|
2146
2446
|
replaces: "running each bin's --help to find the one that fits",
|
|
@@ -2148,6 +2448,7 @@ if (import.meta.main) {
|
|
|
2148
2448
|
commands: {
|
|
2149
2449
|
doctor: { summary: "checks this host's install of the bins, one line per check, the command that fixes a failing one beside it", run: async (args) => main(args) },
|
|
2150
2450
|
list: { summary: "every bin in one line: what it does and what it is reached for instead of", run: async (args) => list(args) },
|
|
2451
|
+
config: { summary: "where every bin looks for its config and which environment variables are set", run: async (args) => Promise.resolve(config2(args)) },
|
|
2151
2452
|
stats: { summary: "how often each bin ran and the options usage errors did not know", run: async (args) => stats(args) },
|
|
2152
2453
|
completions: { summary: "fish completions for every installed bin, printed or written to the folder fish reads", run: async (args) => completions(args) }
|
|
2153
2454
|
}
|
|
@@ -2155,6 +2456,7 @@ if (import.meta.main) {
|
|
|
2155
2456
|
}
|
|
2156
2457
|
export {
|
|
2157
2458
|
completions,
|
|
2459
|
+
config2 as config,
|
|
2158
2460
|
list,
|
|
2159
2461
|
stats
|
|
2160
2462
|
};
|