@kaynooo/cli-workspace 0.2.0 → 0.3.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/sweep-cli.js CHANGED
@@ -2,7 +2,7 @@
2
2
  // @bun
3
3
 
4
4
  // src/sweep-cli.ts
5
- import process13 from "process";
5
+ import process15 from "process";
6
6
 
7
7
  // ../kit/src/advisory.ts
8
8
  var SEVERITIES = ["low", "moderate", "high", "critical"];
@@ -13,14 +13,14 @@ function isIgnored(advisory, ignore) {
13
13
  return ignore.includes(advisory.ghsa ?? "") || ignore.includes(String(advisory.id));
14
14
  }
15
15
  // ../kit/src/args.ts
16
- import process5 from "process";
16
+ import process8 from "process";
17
17
  import { parseArgs } from "util";
18
18
 
19
19
  // ../kit/src/format.ts
20
- import process3 from "process";
20
+ import process6 from "process";
21
21
 
22
22
  // ../kit/src/style.ts
23
- import process2 from "process";
23
+ import process5 from "process";
24
24
 
25
25
  // ../../node_modules/.bun/@kaynooo+utils@2.31.2+d0769e28dc4dad16/node_modules/@kaynooo/utils/dist/chunk-mtetydvc.js
26
26
  function x(s) {
@@ -283,52 +283,236 @@ function le(i = {}) {
283
283
  }
284
284
  var t = le();
285
285
 
286
- // ../kit/src/style.ts
287
- var MARKS = {
288
- ok: { symbol: "\u2713", word: "ok:", paint: (colors) => colors.green },
289
- warn: { symbol: "!", word: "warning:", paint: (colors) => colors.yellow },
290
- problem: { symbol: "\u2717", word: "error:", paint: (colors) => colors.red },
291
- note: { symbol: "\xB7", word: "note:", paint: (colors) => colors.dim }
286
+ // ../kit/src/theme.ts
287
+ import { existsSync as existsSync3, readFileSync, statSync } from "fs";
288
+ import { homedir as homedir2 } from "os";
289
+ import { join as join3, resolve as resolve2 } from "path";
290
+ import process4 from "process";
291
+
292
+ // ../kit/src/config.ts
293
+ import { existsSync } from "fs";
294
+ import { dirname, join, resolve } from "path";
295
+ import process2 from "process";
296
+ function findUp(names, from = process2.cwd(), stop = () => false) {
297
+ const wanted = typeof names === "string" ? [names] : names;
298
+ for (let dir = resolve(from);; dir = dirname(dir)) {
299
+ const found = wanted.map((name) => join(dir, name)).find((path) => existsSync(path));
300
+ if (found !== undefined)
301
+ return found;
302
+ if (stop(dir) || dirname(dir) === dir)
303
+ return;
304
+ }
305
+ }
306
+
307
+ // ../kit/src/user-config.ts
308
+ import { existsSync as existsSync2 } from "fs";
309
+ import { homedir } from "os";
310
+ import { join as join2 } from "path";
311
+ import process3 from "process";
312
+ function configBase(where = {}) {
313
+ const xdg = where.xdg ?? (where.home === undefined ? process3.env.XDG_CONFIG_HOME : undefined);
314
+ return xdg !== undefined && xdg !== "" ? xdg : join2(where.home ?? homedir(), ".config");
315
+ }
316
+ function userConfigPath(bin, where = {}) {
317
+ return join2(configBase(where), "kaynooo-cli", `${bin}.json`);
318
+ }
319
+ function userConfigReadPath(bin, legacy = [], where = {}) {
320
+ const current = userConfigPath(bin, where);
321
+ if (existsSync2(current))
322
+ return current;
323
+ const base = configBase(where);
324
+ return legacy.map((parts) => join2(base, ...parts)).find((path) => existsSync2(path)) ?? current;
325
+ }
326
+
327
+ // ../kit/src/theme.ts
328
+ var DEFAULT = {
329
+ 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" },
330
+ glyphs: { ok: "\u2713", warn: "\u25B2", problem: "\u2717", note: "\u203A", passed: "\u2713", failed: "\u2717", running: "\u25CF", queued: "\u25CB", skipped: "-", bar: "\u2501" },
331
+ accents: ["cyan", "magenta", "green", "blue", "yellow", "redBright"],
332
+ bins: {}
292
333
  };
293
- function takesControlCodes(stream, env = process2.env) {
334
+ 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" };
335
+ 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" };
336
+ var ASCII = { ok: "+", warn: "!", problem: "x", note: ">", passed: "+", failed: "x", running: "*", queued: "o", skipped: "-", bar: "#" };
337
+ var PRESETS = {
338
+ default: {},
339
+ "high-contrast": { colors: BRIGHT, accents: ["cyanBright", "magentaBright", "greenBright", "blueBright", "yellowBright", "redBright"] },
340
+ mono: { colors: MONO, accents: ["bold"] },
341
+ ascii: { glyphs: ASCII }
342
+ };
343
+ 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"]);
344
+ var HEX = /^#[0-9a-f]{6}$/i;
345
+ var CONTROL = /\p{Cc}/u;
346
+ var MAX_FILE_BYTES = 64 * 1024;
347
+ var SPACES = /\s+/;
348
+ function isColorSpec(spec) {
349
+ if (typeof spec === "number")
350
+ return Number.isInteger(spec) && spec >= 0 && spec <= 255;
351
+ return typeof spec === "string" && spec.trim() !== "" && spec.trim().split(SPACES).every((token) => STYLES.has(token) || HEX.test(token));
352
+ }
353
+ function tinted(colors, spec) {
354
+ if (typeof spec === "number")
355
+ return colors.ansi256(spec);
356
+ return spec.trim().split(SPACES).reduce((chain, token) => HEX.test(token) ? chain.hex(token) : chain[token] ?? chain, colors);
357
+ }
358
+ function isRecord(value) {
359
+ return typeof value === "object" && value !== null && !Array.isArray(value);
360
+ }
361
+ function readThemeFile(path, warn) {
362
+ try {
363
+ const stat = statSync(path, { throwIfNoEntry: false });
364
+ if (stat === undefined)
365
+ return;
366
+ if (!stat.isFile() || stat.size > MAX_FILE_BYTES) {
367
+ warn(`${path} is not a regular file under ${MAX_FILE_BYTES / 1024} KiB, theme ignored`);
368
+ return;
369
+ }
370
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
371
+ if (isRecord(parsed))
372
+ return parsed;
373
+ warn(`${path} is not a JSON object, theme ignored`);
374
+ } catch (error) {
375
+ warn(`${path} does not parse (${error instanceof Error ? error.message : String(error)}), theme ignored`);
376
+ }
377
+ return;
378
+ }
379
+ function applyPreset(theme, name, warn, where) {
380
+ if (name === undefined)
381
+ return;
382
+ if (typeof name !== "string" || !Object.hasOwn(PRESETS, name)) {
383
+ warn(`${where}: no preset ${String(name)}, one of ${Object.keys(PRESETS).join(", ")}`);
384
+ return;
385
+ }
386
+ const preset = PRESETS[name];
387
+ Object.assign(theme.colors, preset.colors);
388
+ Object.assign(theme.glyphs, preset.glyphs);
389
+ if (preset.accents !== undefined)
390
+ theme.accents = [...preset.accents];
391
+ return name;
392
+ }
393
+ function applyFile(theme, file, where, warn) {
394
+ if (isRecord(file.colors)) {
395
+ for (const [key, value] of Object.entries(file.colors)) {
396
+ if (!Object.hasOwn(DEFAULT.colors, key))
397
+ warn(`${where}: no colour ${key}, one of ${Object.keys(DEFAULT.colors).join(", ")}`);
398
+ else if (!isColorSpec(value))
399
+ warn(`${where}: colour ${key} is not an ANSI name, \`#rrggbb\` or a number up to 255`);
400
+ else
401
+ theme.colors[key] = value;
402
+ }
403
+ }
404
+ if (isRecord(file.glyphs)) {
405
+ for (const [key, value] of Object.entries(file.glyphs)) {
406
+ if (!Object.hasOwn(DEFAULT.glyphs, key))
407
+ warn(`${where}: no glyph ${key}, one of ${Object.keys(DEFAULT.glyphs).join(", ")}`);
408
+ else if (typeof value !== "string" || CONTROL.test(value) || Bun.stringWidth(value) !== 1)
409
+ warn(`${where}: glyph ${key} must be one visible character wide, with no control character`);
410
+ else
411
+ theme.glyphs[key] = value;
412
+ }
413
+ }
414
+ if (Array.isArray(file.accents) && file.accents.length > 0 && file.accents.every(isColorSpec))
415
+ theme.accents = [...file.accents];
416
+ else if (file.accents !== undefined)
417
+ warn(`${where}: accents must be a non-empty list of colours`);
418
+ if (isRecord(file.bins)) {
419
+ for (const [bin, value] of Object.entries(file.bins)) {
420
+ if (isColorSpec(value))
421
+ theme.bins[bin] = value;
422
+ else
423
+ warn(`${where}: bin ${bin} has no valid colour`);
424
+ }
425
+ }
426
+ }
427
+ function endsWalk(home) {
428
+ return (dir) => dir === resolve2(home) || existsSync3(join3(dir, ".git"));
429
+ }
430
+ var warned = new Set;
431
+ function warnOnce(message) {
432
+ if (warned.has(message))
433
+ return;
434
+ warned.add(message);
435
+ console.error(`warning: theme ${message}`);
436
+ }
437
+ function isPath(chosen) {
438
+ return chosen.includes("/") || chosen.endsWith(".json");
439
+ }
440
+ function loadTheme({ env = process4.env, from = process4.cwd(), home = homedir2(), warn = warnOnce } = {}) {
441
+ const theme = { colors: { ...DEFAULT.colors }, glyphs: { ...DEFAULT.glyphs }, accents: [...DEFAULT.accents], bins: Object.create(null) };
442
+ const sources = [];
443
+ const chosen = env.KIT_THEME ?? "";
444
+ const wanted = chosen === "" ? [userConfigPath("theme", { home, xdg: env.XDG_CONFIG_HOME }), findUp(join3(".kaynooo", "theme.json"), from, endsWalk(home))] : [];
445
+ if (isPath(chosen))
446
+ wanted.push(resolve2(from, chosen));
447
+ const files = [];
448
+ for (const path of wanted) {
449
+ const file = path === undefined ? undefined : readThemeFile(path, warn);
450
+ if (path !== undefined && file !== undefined)
451
+ files.push({ path, file });
452
+ }
453
+ let preset = "default";
454
+ if (chosen !== "" && !isPath(chosen)) {
455
+ preset = applyPreset(theme, chosen, warn, "KIT_THEME") ?? preset;
456
+ } else {
457
+ const named = files.findLast(({ file }) => file.preset !== undefined);
458
+ if (named !== undefined)
459
+ preset = applyPreset(theme, named.file.preset, warn, named.path) ?? preset;
460
+ }
461
+ for (const { path, file } of files) {
462
+ applyFile(theme, file, path, warn);
463
+ sources.push(path);
464
+ }
465
+ return { theme, sources, preset };
466
+ }
467
+ var current;
468
+ var currentKey = "";
469
+ function activeTheme() {
470
+ const key = `${process4.cwd()}\x00${process4.env.KIT_THEME ?? ""}\x00${process4.env.XDG_CONFIG_HOME ?? ""}`;
471
+ if (current === undefined || key !== currentKey) {
472
+ current = loadTheme();
473
+ currentKey = key;
474
+ }
475
+ return current;
476
+ }
477
+
478
+ // ../kit/src/style.ts
479
+ var MARK_WORDS = { ok: "ok:", warn: "warning:", problem: "error:", note: "note:" };
480
+ function takesControlCodes(stream, env = process5.env) {
294
481
  return stream.isTTY === true && env.NO_COLOR === undefined && env.NODE_DISABLE_COLORS === undefined && env.TERM !== "dumb";
295
482
  }
296
- function paint(stream, env = process2.env) {
483
+ function paint(stream, env = process5.env) {
297
484
  return le({ stream: { isTTY: takesControlCodes(stream, env) }, env });
298
485
  }
299
- function marked(mark, text, stream, detail, env = process2.env) {
486
+ function marked(mark, text, stream, detail, env = process5.env) {
300
487
  const colors = paint(stream, env);
301
- const { symbol, word, paint: tint } = MARKS[mark];
302
- const head = takesControlCodes(stream, env) ? tint(colors)(symbol) : word;
488
+ let head = MARK_WORDS[mark];
489
+ if (takesControlCodes(stream, env)) {
490
+ const { theme } = activeTheme();
491
+ head = tinted(colors.bold, theme.colors[mark])(theme.glyphs[mark]);
492
+ }
303
493
  return `${head} ${text}${detail === undefined ? "" : ` ${colors.dim(detail)}`}`;
304
494
  }
305
495
  function ok(text, detail) {
306
- console.log(marked("ok", text, process2.stdout, detail));
496
+ console.log(marked("ok", text, process5.stdout, detail));
307
497
  }
308
498
  function note(text, detail) {
309
- console.log(marked("note", text, process2.stdout, detail));
499
+ console.log(marked("note", text, process5.stdout, detail));
310
500
  }
311
501
  function problem(text, detail) {
312
- console.error(marked("problem", text, process2.stderr, detail));
313
- }
314
- var ROLES = {
315
- accent: (colors) => colors.cyan,
316
- id: (colors) => colors.yellow,
317
- path: (colors) => colors.blue,
318
- label: (colors) => colors.dim,
319
- value: (colors) => colors.bold
320
- };
321
- function role(name, stream, env = process2.env) {
322
- return ROLES[name](paint(stream, env));
323
- }
324
- var STATUSES = {
325
- passed: { symbol: "\u2713", words: ["passed", "success", "succeeded", "ok"], paint: (colors) => colors.green },
326
- failed: { symbol: "\u2717", words: ["failed", "failure", "error", "errored"], paint: (colors) => colors.red },
327
- running: { symbol: "\u25CF", words: ["running", "in_progress", "started"], paint: (colors) => colors.cyan },
328
- queued: { symbol: "\u25CB", words: ["queued", "waiting", "pending", "created", "blocked", "scheduled"], paint: (colors) => colors.yellow },
329
- skipped: { symbol: "-", words: ["skipped", "cancelled", "canceled", "manual"], paint: (colors) => colors.dim }
502
+ console.error(marked("problem", text, process5.stderr, detail));
503
+ }
504
+ function role(name, stream, env = process5.env) {
505
+ const colors = paint(stream, env);
506
+ return takesControlCodes(stream, env) ? tinted(colors, activeTheme().theme.colors[name]) : colors;
507
+ }
508
+ var STATUS_WORDS = {
509
+ passed: ["passed", "success", "succeeded", "ok"],
510
+ failed: ["failed", "failure", "error", "errored"],
511
+ running: ["running", "in_progress", "started"],
512
+ queued: ["queued", "waiting", "pending", "created", "blocked", "scheduled"],
513
+ skipped: ["skipped", "cancelled", "canceled", "manual"]
330
514
  };
331
- var STATUS_OF = new Map(Object.entries(STATUSES).flatMap(([status, { words }]) => words.map((word) => [word, status])));
515
+ var STATUS_OF = new Map(Object.entries(STATUS_WORDS).flatMap(([status, words]) => words.map((word) => [word, status])));
332
516
  // ../../node_modules/.bun/@kaynooo+utils@2.31.2+d0769e28dc4dad16/node_modules/@kaynooo/utils/dist/chunk-hrsswv1w.js
333
517
  function $e(t, e = false, i = 1) {
334
518
  let n = typeof e === "object" ? e : { units: e ? "si" : "iec", dp: i }, { units: s = "iec", dp: u = 1 } = n, r = s === "si" ? 1000 : 1024;
@@ -378,7 +562,7 @@ var ESCAPE = String.fromCharCode(27);
378
562
  function textWidth(text) {
379
563
  return Bun.stringWidth(text);
380
564
  }
381
- function table(rows, { cellWidth, header = false, align = [], fit } = {}) {
565
+ function table(rows, { cellWidth, header = false, align = [], fit, stream = process6.stdout } = {}) {
382
566
  const cells = cellWidth === undefined ? rows : rows.map((row) => row.map((cell) => truncate(cell.replaceAll(LINE_BREAKS, " "), cellWidth)));
383
567
  const widths = [];
384
568
  for (const row of cells) {
@@ -386,7 +570,7 @@ function table(rows, { cellWidth, header = false, align = [], fit } = {}) {
386
570
  widths[index] = Math.max(widths[index] ?? 0, textWidth(cell));
387
571
  }
388
572
  const shown = fit === undefined ? cells : narrow(cells, widths, fit);
389
- const colors = paint(process3.stdout);
573
+ const colors = paint(stream);
390
574
  const lines = shown.map((row) => row.map((cell, index) => {
391
575
  const width = widths[index] ?? 0;
392
576
  const gap = " ".repeat(Math.max(0, width - textWidth(cell)));
@@ -394,7 +578,8 @@ function table(rows, { cellWidth, header = false, align = [], fit } = {}) {
394
578
  return `${gap}${cell}`;
395
579
  return index === row.length - 1 ? cell : `${cell}${gap}`;
396
580
  }).join(" ").trimEnd());
397
- return lines.map((line, index) => header && index === 0 ? colors.bold(line) : line).join(`
581
+ const rule = header && takesControlCodes(stream) && lines.length > 1 ? [colors.dim("\u2500".repeat(Math.max(...lines.map(textWidth))))] : [];
582
+ return lines.flatMap((line, index) => header && index === 0 ? [colors.bold(line), ...rule] : [line]).join(`
398
583
  `);
399
584
  }
400
585
  function narrow(rows, widths, limit) {
@@ -418,27 +603,27 @@ function printJson(value) {
418
603
  }
419
604
 
420
605
  // ../kit/src/help.ts
421
- import process4 from "process";
606
+ import process7 from "process";
422
607
  var OPTIONAL = /\[[^\]]+\]/g;
423
608
  var USAGE_PREFIX = /^usage:\s*/i;
424
609
  var FULL_STOP = /\.$/;
425
- function usageLine(usage, colors = paint(process4.stdout)) {
610
+ function usageLine(usage, colors = paint(process7.stdout)) {
426
611
  return `${colors.bold("Usage:")} ${usage.replace(USAGE_PREFIX, "").replaceAll(OPTIONAL, (part) => colors.dim(part))}`;
427
612
  }
428
- function helpText(usage, options, colors = paint(process4.stdout), { examples = [], replaces, exits = [] } = {}) {
613
+ function helpText(usage, options, colors = paint(process7.stdout), { examples = [], replaces, exits = [] } = {}) {
429
614
  return [usageLine(usage, colors), ...replacesRows(replaces, colors), "", colors.bold("Options:"), ...optionRows(options, colors), ...exitRows(exits, colors), ...exampleRows(examples, colors)].join(`
430
615
  `);
431
616
  }
432
- function exitRows(exits, colors = paint(process4.stdout)) {
617
+ function exitRows(exits, colors = paint(process7.stdout)) {
433
618
  if (exits.length === 0)
434
619
  return [];
435
620
  const width = Math.max(...exits.map(([code]) => String(code).length));
436
621
  return ["", colors.bold("Exits:"), ...exits.map(([code, meaning]) => ` ${colors.cyan(String(code).padEnd(width))} ${meaning}`)];
437
622
  }
438
- function replacesRows(replaces, colors = paint(process4.stdout)) {
623
+ function replacesRows(replaces, colors = paint(process7.stdout)) {
439
624
  return replaces === undefined ? [] : ["", `${colors.bold("Instead of:")} ${replaces}`];
440
625
  }
441
- function wantsHelpJson(env = process4.env) {
626
+ function wantsHelpJson(env = process7.env) {
442
627
  return env.KIT_HELP === "json";
443
628
  }
444
629
  function description(usage, { examples = [], replaces, exits } = {}, options) {
@@ -457,12 +642,12 @@ function description(usage, { examples = [], replaces, exits } = {}, options) {
457
642
  ...options === undefined ? {} : { options: Object.entries(options).map(([name, { short, description }]) => ({ name, ...short === undefined ? {} : { short }, ...description === undefined ? {} : { description } })) }
458
643
  };
459
644
  }
460
- function exampleRows(examples, colors = paint(process4.stdout)) {
645
+ function exampleRows(examples, colors = paint(process7.stdout)) {
461
646
  if (examples.length === 0)
462
647
  return [];
463
648
  return ["", colors.bold("Examples:"), ...examples.map((example) => typeof example === "string" ? ` ${example}` : ` ${example[0]} ${colors.dim(`# ${example[1]}`)}`)];
464
649
  }
465
- function optionRows(options, colors = paint(process4.stdout)) {
650
+ function optionRows(options, colors = paint(process7.stdout)) {
466
651
  const rows = Object.entries(options).map(([name, option]) => {
467
652
  const short = option.short === undefined ? " " : `${colors.cyan(`-${option.short}`)}, `;
468
653
  const named = option.choices === undefined ? "<value>" : `<${option.choices.join("|")}>`;
@@ -555,7 +740,7 @@ var OUTPUT_OPTIONS = {
555
740
  color: { type: "string", description: "auto (on a terminal alone), always or never" },
556
741
  plain: { type: "boolean", description: "plain text, no colour or spinner: --color never" }
557
742
  };
558
- function applyOutput(color, plain, env = process5.env) {
743
+ function applyOutput(color, plain, env = process8.env) {
559
744
  const when = plain === true ? "never" : color ?? "auto";
560
745
  if (when === "auto")
561
746
  return;
@@ -726,7 +911,7 @@ function nearestOption(message, options, allowNegative) {
726
911
  }
727
912
  // ../kit/src/process.ts
728
913
  import { constants } from "os";
729
- import process6 from "process";
914
+ import process9 from "process";
730
915
  var KILL_GRACE_MS = 2000;
731
916
  function exitCodeOf(code, signal) {
732
917
  if (code !== null)
@@ -746,7 +931,7 @@ async function runCommand(argv, { cwd, env, stdin = "ignore", timeoutMs, signal,
746
931
  return;
747
932
  try {
748
933
  if (grouped)
749
- process6.kill(-proc.pid, killing);
934
+ process9.kill(-proc.pid, killing);
750
935
  else
751
936
  proc.kill(killing);
752
937
  } catch {}
@@ -821,10 +1006,10 @@ var RESET_STYLE = `${ESC}[0m`;
821
1006
  var CLOSE_LINK = `${ESC}]8;;${ESC}\\`;
822
1007
 
823
1008
  // ../kit/src/tool.ts
824
- import process7 from "process";
1009
+ import process10 from "process";
825
1010
  function cwdGone() {
826
1011
  try {
827
- process7.cwd();
1012
+ process10.cwd();
828
1013
  return false;
829
1014
  } catch {
830
1015
  return true;
@@ -846,7 +1031,7 @@ function spawnFailure(command, error) {
846
1031
  async function runTool(label, argv, { timeoutMs, cwd, onStart } = {}) {
847
1032
  let ran;
848
1033
  try {
849
- ran = await runCommand(argv, { env: process7.env, ...timeoutMs === undefined ? {} : { timeoutMs }, ...cwd === undefined ? {} : { cwd }, ...onStart === undefined ? {} : { onStart } });
1034
+ ran = await runCommand(argv, { env: process10.env, ...timeoutMs === undefined ? {} : { timeoutMs }, ...cwd === undefined ? {} : { cwd }, ...onStart === undefined ? {} : { onStart } });
850
1035
  } catch (error) {
851
1036
  throw spawnFailure(label, error);
852
1037
  }
@@ -861,15 +1046,15 @@ async function runTool(label, argv, { timeoutMs, cwd, onStart } = {}) {
861
1046
  return { code: ran.code, ...streams };
862
1047
  }
863
1048
  // ../kit/src/cli.ts
864
- import { readFileSync, realpathSync } from "fs";
865
- import { dirname as dirname2, join as join2 } from "path";
866
- import process9 from "process";
1049
+ import { readFileSync as readFileSync2, realpathSync } from "fs";
1050
+ import { dirname as dirname3, join as join5 } from "path";
1051
+ import process12 from "process";
867
1052
 
868
1053
  // ../kit/src/usage.ts
869
1054
  import { appendFileSync, mkdirSync } from "fs";
870
- import { homedir } from "os";
871
- import { dirname, isAbsolute, join } from "path";
872
- import process8 from "process";
1055
+ import { homedir as homedir3 } from "os";
1056
+ import { dirname as dirname2, isAbsolute, join as join4 } from "path";
1057
+ import process11 from "process";
873
1058
 
874
1059
  // ../kit/src/logs.ts
875
1060
  var DAY_MS = 24 * 60 * 60 * 1000;
@@ -883,14 +1068,14 @@ var usageError;
883
1068
  function noteUsageError(message) {
884
1069
  usageError = message;
885
1070
  }
886
- function stateHome(env = process8.env) {
1071
+ function stateHome(env = process11.env) {
887
1072
  const dir = env.XDG_STATE_HOME;
888
- return dir !== undefined && isAbsolute(dir) ? dir : join(homedir(), ".local", "state");
1073
+ return dir !== undefined && isAbsolute(dir) ? dir : join4(homedir3(), ".local", "state");
889
1074
  }
890
- function usagePath(env = process8.env) {
891
- return join(stateHome(env), CLI_FOLDER, "usage.jsonl");
1075
+ function usagePath(env = process11.env) {
1076
+ return join4(stateHome(env), CLI_FOLDER, "usage.jsonl");
892
1077
  }
893
- function usageRecorded(env = process8.env) {
1078
+ function usageRecorded(env = process11.env) {
894
1079
  const off = env.KAYNOOO_CLI_NO_USAGE;
895
1080
  return env.NODE_ENV !== "test" && (off === undefined || off === "" || off === "0");
896
1081
  }
@@ -904,12 +1089,12 @@ function usageEntry(bin, command, argv, code, now = new Date) {
904
1089
  usageError = undefined;
905
1090
  return { at: now.toISOString(), bin, ...command === undefined ? {} : { command }, flags: flagNames(argv), code, ...unknown === undefined ? {} : { unknown } };
906
1091
  }
907
- function recordUsage(entry, env = process8.env) {
1092
+ function recordUsage(entry, env = process11.env) {
908
1093
  if (!usageRecorded(env))
909
1094
  return;
910
1095
  try {
911
1096
  const path = usagePath(env);
912
- mkdirSync(dirname(path), { recursive: true, mode: 448 });
1097
+ mkdirSync(dirname2(path), { recursive: true, mode: 448 });
913
1098
  appendFileSync(path, `${JSON.stringify(entry)}
914
1099
  `, { mode: 384 });
915
1100
  } catch {}
@@ -995,8 +1180,8 @@ async function help2({ path, header, summary: lazySummary, commands, defaultComm
995
1180
  `);
996
1181
  }
997
1182
  function printError(prefix, message, hint, cause) {
998
- const colors = paint(process9.stderr);
999
- const styled = takesControlCodes(process9.stderr);
1183
+ const colors = paint(process12.stderr);
1184
+ const styled = takesControlCodes(process12.stderr);
1000
1185
  const head = styled ? `${colors.red.bold("error")} ${colors.dim(`${prefix}:`)}` : `error: ${prefix}:`;
1001
1186
  const under = (label, text, tint) => text === undefined ? "" : `
1002
1187
  ${styled ? tint(label) : `${label}:`} ${text}`;
@@ -1039,11 +1224,11 @@ async function dispatch(level, typed, { version, redact }) {
1039
1224
  }
1040
1225
  }
1041
1226
  if (name === undefined && level.path.length > 1) {
1042
- console.error(await help2(await expand(level, [...level.path.slice(1), ...argv]), process9.stderr));
1227
+ console.error(await help2(await expand(level, [...level.path.slice(1), ...argv]), process12.stderr));
1043
1228
  return 2;
1044
1229
  }
1045
1230
  if (name === undefined || name === "help" || name === "--help" || name === "-h") {
1046
- console.log(await help2(await expand(level, [...level.path.slice(1), ...argv]), process9.stdout));
1231
+ console.log(await help2(await expand(level, [...level.path.slice(1), ...argv]), process12.stdout));
1047
1232
  return 0;
1048
1233
  }
1049
1234
  if (level.path.length === 1 && (name === "--version" || name === "-v")) {
@@ -1160,17 +1345,17 @@ async function printCandidates(found) {
1160
1345
  return 0;
1161
1346
  }
1162
1347
  function packageVersion(entry = Bun.main) {
1163
- for (let dir = dirname2(realpathSync(entry));; dir = dirname2(dir)) {
1348
+ for (let dir = dirname3(realpathSync(entry));; dir = dirname3(dir)) {
1164
1349
  try {
1165
- const { version } = JSON.parse(readFileSync(join2(dir, "package.json"), "utf8"));
1350
+ const { version } = JSON.parse(readFileSync2(join5(dir, "package.json"), "utf8"));
1166
1351
  if (typeof version === "string")
1167
1352
  return version;
1168
1353
  } catch {}
1169
- if (dirname2(dir) === dir)
1354
+ if (dirname3(dir) === dir)
1170
1355
  return "0.0.0";
1171
1356
  }
1172
1357
  }
1173
- async function runCli(cli, argv = process9.argv.slice(2)) {
1358
+ async function runCli(cli, argv = process12.argv.slice(2)) {
1174
1359
  const version = cli.version ?? packageVersion();
1175
1360
  if (argv[0] === COMPLETE)
1176
1361
  return printCandidates(candidates(scopeOf(cli.commands, cli.extraCommands, cli.aliases), cli.defaultCommand, argv.slice(1)));
@@ -1197,7 +1382,7 @@ function commandPath(scope, argv) {
1197
1382
  }
1198
1383
  return path.length === 0 ? undefined : path.join(" ");
1199
1384
  }
1200
- async function runMain({ name, version, run, redact, usage }, argv = process9.argv.slice(2)) {
1385
+ async function runMain({ name, version, run, redact, usage }, argv = process12.argv.slice(2)) {
1201
1386
  if (argv[0] === COMPLETE)
1202
1387
  return printCandidates(leafCandidates(run, argv.slice(1)));
1203
1388
  if (argv.length === 1 && (argv[0] === "--version" || argv[0] === "-v")) {
@@ -1233,37 +1418,18 @@ function parseSize(text, bareUnit = 1) {
1233
1418
  const suffix = match[2].toLowerCase();
1234
1419
  return Math.round(Number(match[1]) * (suffix === "" ? bareUnit : 1024 ** POWERS[suffix]));
1235
1420
  }
1236
- // ../kit/src/user-config.ts
1237
- import { existsSync } from "fs";
1238
- import { homedir as homedir2 } from "os";
1239
- import { join as join3 } from "path";
1240
- import process10 from "process";
1241
- function configBase(where = {}) {
1242
- const xdg = where.xdg ?? (where.home === undefined ? process10.env.XDG_CONFIG_HOME : undefined);
1243
- return xdg !== undefined && xdg !== "" ? xdg : join3(where.home ?? homedir2(), ".config");
1244
- }
1245
- function userConfigPath(bin, where = {}) {
1246
- return join3(configBase(where), "kaynooo-cli", `${bin}.json`);
1247
- }
1248
- function userConfigReadPath(bin, legacy = [], where = {}) {
1249
- const current = userConfigPath(bin, where);
1250
- if (existsSync(current))
1251
- return current;
1252
- const base = configBase(where);
1253
- return legacy.map((parts) => join3(base, ...parts)).find((path) => existsSync(path)) ?? current;
1254
- }
1255
1421
  // src/sweep.ts
1256
- import { existsSync as existsSync4, statSync } from "fs";
1257
- import { homedir as homedir4 } from "os";
1258
- import { join as join7, relative, resolve } from "path";
1422
+ import { existsSync as existsSync6, statSync as statSync2 } from "fs";
1423
+ import { homedir as homedir5 } from "os";
1424
+ import { join as join9, relative, resolve as resolve3 } from "path";
1259
1425
 
1260
1426
  // ../kit/src/prompt.ts
1261
- import process12 from "process";
1427
+ import process14 from "process";
1262
1428
  var KEY_SEQUENCES = /\x1B\[[0-9;]*[A-Z~]|\x1BO[A-Z]|[\s\S]/giu;
1263
- var CONTROL = /^[\x00-\x1F\x7F]/;
1429
+ var CONTROL2 = /^[\x00-\x1F\x7F]/;
1264
1430
  var KEYS = { "\x1B[A": "up", "\x1BOA": "up", "\x1B[B": "down", "\x1BOB": "down", "\r": "enter", "\n": "enter", "\x03": "cancel", "\x1B": "cancel", "\x7F": "erase", "\b": "erase" };
1265
1431
  function readKeys(input) {
1266
- return (input.match(KEY_SEQUENCES) ?? []).flatMap((sequence) => KEYS[sequence] ?? (CONTROL.test(sequence) ? [] : [{ text: sequence }]));
1432
+ return (input.match(KEY_SEQUENCES) ?? []).flatMap((sequence) => KEYS[sequence] ?? (CONTROL2.test(sequence) ? [] : [{ text: sequence }]));
1267
1433
  }
1268
1434
  async function confirm(question, { default: fallback, ...settings } = {}) {
1269
1435
  let answered = fallback;
@@ -1287,7 +1453,7 @@ function asking(colors, question, typed, keys) {
1287
1453
  function answer(colors, question, state, picked) {
1288
1454
  return state === "enter" ? `${colors.green("\u2713")} ${question} ${colors.cyan(picked)}` : `${colors.red("\u2717")} ${question} ${colors.dim("cancelled")}`;
1289
1455
  }
1290
- async function ask(question, { hint, terminal = process12 }, render, press) {
1456
+ async function ask(question, { hint, terminal = process14 }, render, press) {
1291
1457
  const { stdin, stderr } = terminal;
1292
1458
  if (stdin.isTTY !== true || stderr.isTTY !== true)
1293
1459
  fail(`cannot ask "${question}" off a terminal`, 2, hint);
@@ -1338,19 +1504,19 @@ async function ask(question, { hint, terminal = process12 }, render, press) {
1338
1504
  }
1339
1505
 
1340
1506
  // src/apply.ts
1341
- import { existsSync as existsSync3 } from "fs";
1507
+ import { existsSync as existsSync5 } from "fs";
1342
1508
  import { lstat as lstat3, rm } from "fs/promises";
1343
- import { basename as basename2, join as join6 } from "path";
1509
+ import { basename as basename2, join as join8 } from "path";
1344
1510
 
1345
1511
  // src/sweep-scan.ts
1346
- import { existsSync as existsSync2 } from "fs";
1512
+ import { existsSync as existsSync4 } from "fs";
1347
1513
  import { lstat as lstat2, readdir as readdir2, realpath } from "fs/promises";
1348
- import { homedir as homedir3 } from "os";
1349
- import { basename, dirname as dirname3, join as join5 } from "path";
1514
+ import { homedir as homedir4 } from "os";
1515
+ import { basename, dirname as dirname4, join as join7 } from "path";
1350
1516
 
1351
1517
  // src/size.ts
1352
1518
  import { lstat, readdir } from "fs/promises";
1353
- import { join as join4 } from "path";
1519
+ import { join as join6 } from "path";
1354
1520
  async function pooled(items, limit, work) {
1355
1521
  const results = Array.from({ length: items.length });
1356
1522
  let next = 0;
@@ -1387,7 +1553,7 @@ async function sizeOf(folder) {
1387
1553
  return;
1388
1554
  }
1389
1555
  await Promise.all(entries.map(async (entry) => {
1390
- const path = join4(dir, entry.name);
1556
+ const path = join6(dir, entry.name);
1391
1557
  if (entry.isDirectory())
1392
1558
  return visit(path);
1393
1559
  try {
@@ -1411,8 +1577,8 @@ var FOLDER_NAMES = new Set(["node_modules", ".nuxt", ".next", ".output", "dist",
1411
1577
  var MAX_DEPTH = 3;
1412
1578
  var PROJECT_CACHE_NAMES = new Set([".turbo", ".nuxt", ".next", ".svelte-kit"]);
1413
1579
  var GLOBAL_CACHES = [".bun/install/cache", ".npm/_cacache", ".cache/pip", ".cache/uv", ".cache/ms-playwright", ".cache/puppeteer", ".cache/go-build", ".cache/typescript", ".cargo/registry"];
1414
- function isCachePath(path, home = homedir3()) {
1415
- return GLOBAL_CACHES.some((cache) => join5(home, cache) === path) || basename(path) === ".cache" && basename(dirname3(path)) === "node_modules";
1580
+ function isCachePath(path, home = homedir4()) {
1581
+ return GLOBAL_CACHES.some((cache) => join7(home, cache) === path) || basename(path) === ".cache" && basename(dirname4(path)) === "node_modules";
1416
1582
  }
1417
1583
  async function listing(dir) {
1418
1584
  try {
@@ -1423,7 +1589,7 @@ async function listing(dir) {
1423
1589
  }
1424
1590
  async function addGlobalCaches(found, home) {
1425
1591
  for (const cache of GLOBAL_CACHES) {
1426
- const path = join5(home, cache);
1592
+ const path = join7(home, cache);
1427
1593
  try {
1428
1594
  const stat = await lstat2(path);
1429
1595
  if (stat.isSymbolicLink())
@@ -1433,12 +1599,12 @@ async function addGlobalCaches(found, home) {
1433
1599
  } catch {}
1434
1600
  }
1435
1601
  }
1436
- async function findCandidates(root, home = homedir3(), withGlobal = true) {
1602
+ async function findCandidates(root, home = homedir4(), withGlobal = true) {
1437
1603
  const found = { candidates: [], skipped: [] };
1438
1604
  const visit = async (dir, depth) => {
1439
1605
  const entries = await listing(dir);
1440
1606
  for (const entry of entries) {
1441
- const path = join5(dir, entry.name);
1607
+ const path = join7(dir, entry.name);
1442
1608
  if (entry.name === ".git")
1443
1609
  continue;
1444
1610
  if (entry.name === ".worktrees" || entry.name === "worktrees" && basename(dir) === ".claude") {
@@ -1448,13 +1614,13 @@ async function findCandidates(root, home = homedir3(), withGlobal = true) {
1448
1614
  }
1449
1615
  for (const worktree of await listing(path)) {
1450
1616
  if (worktree.isDirectory())
1451
- found.candidates.push({ path: join5(path, worktree.name), kind: "worktree" });
1617
+ found.candidates.push({ path: join7(path, worktree.name), kind: "worktree" });
1452
1618
  else if (worktree.isSymbolicLink())
1453
- found.skipped.push({ path: join5(path, worktree.name), reason: "symlink" });
1619
+ found.skipped.push({ path: join7(path, worktree.name), reason: "symlink" });
1454
1620
  }
1455
1621
  continue;
1456
1622
  }
1457
- if (FOLDER_NAMES.has(entry.name) && (entry.name !== "target" || existsSync2(join5(dir, "Cargo.toml")))) {
1623
+ if (FOLDER_NAMES.has(entry.name) && (entry.name !== "target" || existsSync4(join7(dir, "Cargo.toml")))) {
1458
1624
  if (entry.isSymbolicLink()) {
1459
1625
  found.skipped.push({ path, reason: "symlink" });
1460
1626
  } else if (entry.isDirectory()) {
@@ -1462,9 +1628,9 @@ async function findCandidates(root, home = homedir3(), withGlobal = true) {
1462
1628
  if (entry.name === "node_modules") {
1463
1629
  const cache = (await listing(path)).find((inner) => inner.name === ".cache");
1464
1630
  if (cache?.isSymbolicLink())
1465
- found.skipped.push({ path: join5(path, ".cache"), reason: "symlink" });
1631
+ found.skipped.push({ path: join7(path, ".cache"), reason: "symlink" });
1466
1632
  else if (cache?.isDirectory())
1467
- found.candidates.push({ path: join5(path, ".cache"), kind: "cache", inside: path });
1633
+ found.candidates.push({ path: join7(path, ".cache"), kind: "cache", inside: path });
1468
1634
  }
1469
1635
  }
1470
1636
  continue;
@@ -1535,12 +1701,12 @@ var worktreesOf = memo(async (repo) => {
1535
1701
  return branches;
1536
1702
  });
1537
1703
  async function judgeFolder(path, kind, inside) {
1538
- const parent = dirname3(path);
1704
+ const parent = dirname4(path);
1539
1705
  const name = basename(path);
1540
1706
  const located = await toplevelOf(parent);
1541
1707
  if (located === "failed")
1542
1708
  return { skip: "git failed" };
1543
- if (existsSync2(join5(path, ".git")))
1709
+ if (existsSync4(join7(path, ".git")))
1544
1710
  return { skip: "holds a git repository" };
1545
1711
  if (located === "none")
1546
1712
  return name === "node_modules" || inside !== undefined ? { entry: { path, kind, committedAt: undefined, action: "remove", inside } } : { skip: "not in a git repository" };
@@ -1552,7 +1718,7 @@ async function judgeFolder(path, kind, inside) {
1552
1718
  return { entry: { path, kind, committedAt: await lastCommitOf(located.top), action: "remove", inside } };
1553
1719
  }
1554
1720
  async function judgeWorktree(path) {
1555
- const located = await toplevelOf(dirname3(path));
1721
+ const located = await toplevelOf(dirname4(path));
1556
1722
  if (located === "failed")
1557
1723
  return { skip: "git failed" };
1558
1724
  if (located === "none")
@@ -1608,7 +1774,7 @@ async function removeFolder(entry) {
1608
1774
  const stat = await lstat3(entry.path);
1609
1775
  if (stat.isSymbolicLink() || !stat.isDirectory() || !(FOLDER_NAMES.has(basename2(entry.path)) || entry.kind === "cache" && isCachePath(entry.path)))
1610
1776
  return { path: entry.path, removed: false, error: "no longer a plain folder" };
1611
- if (existsSync3(join6(entry.path, ".git")))
1777
+ if (existsSync5(join8(entry.path, ".git")))
1612
1778
  return { path: entry.path, removed: false, error: "holds a git repository" };
1613
1779
  await rm(entry.path, { recursive: true, force: true });
1614
1780
  return { path: entry.path, removed: true };
@@ -1639,6 +1805,8 @@ async function applyAll(entries, done) {
1639
1805
  }
1640
1806
 
1641
1807
  // src/sweep.ts
1808
+ var LARGE = 100 * 1024 ** 2;
1809
+ var BIG = 1024 ** 3;
1642
1810
  var USAGE = `sweep [options] [root]: lists the regenerable folders (node_modules, .output, dist, build, target beside a Cargo.toml), the caches (.nuxt, .next, .turbo, .svelte-kit and node_modules/.cache per project; ~/.bun/install/cache, ~/.npm/_cacache, ~/.cache/{pip,uv,ms-playwright,puppeteer,go-build,typescript} and ~/.cargo/registry, exact paths only, listed only when no root is given or with --only caches) and the .worktrees/* and .claude/worktrees/* entries under root (~/Workspace by default, ${MAX_DEPTH} levels down), largest first with the age of the repo's last commit; --only worktrees, folders or caches keeps one kind
1643
1811
 
1644
1812
  Dry unless --apply, which deletes after a confirm showing the total (read from the terminal; off one, pass --yes). A folder is deleted for good, not trashed. Never touched: a symlink, a folder git tracks files in, a build folder git does not ignore. A worktree is removed with git worktree remove, never forced, and only when it is clean and every commit of its branch is on another branch or a remote; the others stay listed as kept and count for nothing in the total. With --docker the Docker usage comes after the list, left to dclean.
@@ -1668,8 +1836,8 @@ async function sweep(args) {
1668
1836
  const { values, positionals } = parse(args, USAGE, OPTIONS, { positionals: ["[root]"], replaces: "find ~/Workspace -name node_modules, du -sh and rm -rf by hand", examples: ["sweep", "sweep ~/Workspace --older 30d --min 100M --apply", "sweep --only worktrees --apply"] });
1669
1837
  if (positionals.length > 1)
1670
1838
  badUsage("give one root");
1671
- const root = resolve(positionals[0] ?? join7(homedir4(), "Workspace"));
1672
- if (!existsSync4(root) || !statSync(root).isDirectory())
1839
+ const root = resolve3(positionals[0] ?? join9(homedir5(), "Workspace"));
1840
+ if (!existsSync6(root) || !statSync2(root).isDirectory())
1673
1841
  fail(`${root} is not a folder`);
1674
1842
  if (values.only !== undefined && !Object.hasOwn(ONLY_KINDS, values.only))
1675
1843
  badUsage(`--only takes worktrees, folders or caches, not ${values.only}`);
@@ -1678,7 +1846,7 @@ async function sweep(args) {
1678
1846
  if (minBytes === undefined)
1679
1847
  badUsage(`--min takes a size such as 500K, 10M or 1G, not ${values.min}`);
1680
1848
  const now = Date.now();
1681
- const found = await findCandidates(root, homedir4(), positionals.length === 0 || values.only === "caches");
1849
+ const found = await findCandidates(root, homedir5(), positionals.length === 0 || values.only === "caches");
1682
1850
  const wanted = values.only === undefined ? found.candidates : found.candidates.filter((candidate) => candidate.kind === ONLY_KINDS[values.only]);
1683
1851
  const { entries: all, skipped } = await inspect({ candidates: wanted, skipped: found.skipped });
1684
1852
  const entries = all.filter((entry) => entry.bytes >= minBytes && (olderMs === undefined || entry.committedAt !== undefined && now - entry.committedAt >= olderMs)).sort((a, b) => b.bytes - a.bytes || a.path.localeCompare(b.path));
@@ -1730,10 +1898,17 @@ function print(root, entries, skipped, reclaimable, now) {
1730
1898
  console.log("nothing to sweep");
1731
1899
  return;
1732
1900
  }
1733
- console.log(table([["size", "age", "what", "path", "note"], ...entries.map((entry) => [$e(entry.bytes), entry.committedAt === undefined ? "-" : formatAge(entry.committedAt, now), entry.kind, relative(root, entry.path), entry.action === "keep" ? `kept: ${entry.reason}` : ""])], { header: true, align: ["right", "right", "left", "left", "left"] }));
1901
+ const colors = paint(process.stdout);
1902
+ const path = role("path", process.stdout);
1903
+ const sized = (bytes) => {
1904
+ if (bytes >= BIG)
1905
+ return colors.red($e(bytes));
1906
+ return bytes >= LARGE ? colors.yellow($e(bytes)) : $e(bytes);
1907
+ };
1908
+ console.log(table([["size", "age", "what", "path", "note"], ...entries.map((entry) => [sized(entry.bytes), entry.committedAt === undefined ? colors.dim("-") : colors.dim(formatAge(entry.committedAt, now)), entry.kind, path(relative(root, entry.path)), entry.action === "keep" ? colors.dim(`kept: ${entry.reason}`) : ""])], { header: true, align: ["right", "right", "left", "left", "left"] }));
1734
1909
  console.log(`
1735
1910
  ${$e(reclaimable)} reclaimable${skipped === 0 ? "" : `, ${skipped} skipped (symlinks, tracked or not ignored by git)`}`);
1736
1911
  }
1737
1912
 
1738
1913
  // src/sweep-cli.ts
1739
- process13.exitCode = await runMain({ name: "sweep", run: sweep });
1914
+ process15.exitCode = await runMain({ name: "sweep", run: sweep });