@kaynooo/cli-docker 0.6.0 → 0.8.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/dx-cli.js CHANGED
@@ -2,13 +2,37 @@
2
2
  // @bun
3
3
 
4
4
  // src/dx-cli.ts
5
- import process16 from "process";
5
+ import process18 from "process";
6
6
  // ../kit/src/args.ts
7
- import process7 from "process";
7
+ import process9 from "process";
8
8
  import { parseArgs } from "util";
9
9
 
10
+ // ../kit/src/format.ts
11
+ import process7 from "process";
12
+
13
+ // ../kit/src/mode.ts
14
+ import process2 from "process";
15
+ var MODES = new Set(["terminal", "ci", "agent", "pipe"]);
16
+ var AGENT_VARIABLES = ["CLAUDECODE", "AI_AGENT"];
17
+ function isSet(value) {
18
+ return value !== undefined && value !== "" && value !== "0" && value.toLowerCase() !== "false";
19
+ }
20
+ function outputMode(stream = process2.stdout, env = process2.env) {
21
+ const chosen = env.KIT_MODE;
22
+ if (chosen !== undefined && MODES.has(chosen))
23
+ return chosen;
24
+ if (AGENT_VARIABLES.some((name) => isSet(env[name])))
25
+ return "agent";
26
+ if (stream.isTTY === true)
27
+ return "terminal";
28
+ return isSet(env.CI) ? "ci" : "pipe";
29
+ }
30
+ function isInteractive(streams = process2) {
31
+ return streams.stdin?.isTTY === true && streams.stderr?.isTTY === true;
32
+ }
33
+
10
34
  // ../kit/src/style.ts
11
- import process5 from "process";
35
+ import process6 from "process";
12
36
 
13
37
  // ../../node_modules/.bun/@kaynooo+utils@2.31.2+d0769e28dc4dad16/node_modules/@kaynooo/utils/dist/chunk-mtetydvc.js
14
38
  function x(s) {
@@ -275,13 +299,13 @@ var t = le();
275
299
  import { existsSync as existsSync2, readFileSync, statSync } from "fs";
276
300
  import { homedir as homedir2 } from "os";
277
301
  import { join as join3, resolve as resolve2 } from "path";
278
- import process4 from "process";
302
+ import process5 from "process";
279
303
 
280
304
  // ../kit/src/config.ts
281
305
  import { existsSync } from "fs";
282
306
  import { dirname, join, resolve } from "path";
283
- import process2 from "process";
284
- function findUp(names, from = process2.cwd(), stop = () => false) {
307
+ import process3 from "process";
308
+ function findUp(names, from = process3.cwd(), stop = () => false) {
285
309
  const wanted = typeof names === "string" ? [names] : names;
286
310
  for (let dir = resolve(from);; dir = dirname(dir)) {
287
311
  const found = wanted.map((name) => join(dir, name)).find((path) => existsSync(path));
@@ -295,9 +319,9 @@ function findUp(names, from = process2.cwd(), stop = () => false) {
295
319
  // ../kit/src/user-config.ts
296
320
  import { homedir } from "os";
297
321
  import { join as join2 } from "path";
298
- import process3 from "process";
322
+ import process4 from "process";
299
323
  function configBase(where = {}) {
300
- const xdg = where.xdg ?? (where.home === undefined ? process3.env.XDG_CONFIG_HOME : undefined);
324
+ const xdg = where.xdg ?? (where.home === undefined ? process4.env.XDG_CONFIG_HOME : undefined);
301
325
  return xdg !== undefined && xdg !== "" ? xdg : join2(where.home ?? homedir(), ".config");
302
326
  }
303
327
  function userConfigPath(bin, where = {}) {
@@ -330,10 +354,30 @@ function isColorSpec(spec) {
330
354
  return Number.isInteger(spec) && spec >= 0 && spec <= 255;
331
355
  return typeof spec === "string" && spec.trim() !== "" && spec.trim().split(SPACES).every((token) => STYLES.has(token) || HEX.test(token));
332
356
  }
333
- function tinted(colors, spec) {
357
+ var CUBE = [0, 95, 135, 175, 215, 255];
358
+ function cubeLevel(channel) {
359
+ return CUBE.reduce((best, value, level) => Math.abs(value - channel) < Math.abs(CUBE[best] - channel) ? level : best, 0);
360
+ }
361
+ function nearest256(hex) {
362
+ const rgb = [1, 3, 5].map((at) => Number.parseInt(hex.slice(at, at + 2), 16));
363
+ const levels = rgb.map(cubeLevel);
364
+ const cube = levels.map((level) => CUBE[level]);
365
+ const grey = Math.min(23, Math.max(0, Math.round((rgb.reduce((sum, channel) => sum + channel, 0) / 3 - 8) / 10)));
366
+ const greyValue = 8 + grey * 10;
367
+ const distance = (colour) => colour.reduce((sum, channel, at) => sum + (channel - rgb[at]) ** 2, 0);
368
+ return distance([greyValue, greyValue, greyValue]) < distance(cube) ? 232 + grey : 16 + 36 * levels[0] + 6 * levels[1] + levels[2];
369
+ }
370
+ function hasTrueColor(env) {
371
+ return env.COLORTERM === "truecolor" || env.COLORTERM === "24bit";
372
+ }
373
+ function tinted(colors, spec, env = process5.env) {
334
374
  if (typeof spec === "number")
335
375
  return colors.ansi256(spec);
336
- return spec.trim().split(SPACES).reduce((chain, token) => HEX.test(token) ? chain.hex(token) : chain[token] ?? chain, colors);
376
+ return spec.trim().split(SPACES).reduce((chain, token) => {
377
+ if (HEX.test(token))
378
+ return hasTrueColor(env) ? chain.hex(token) : chain.ansi256(nearest256(token));
379
+ return chain[token] ?? chain;
380
+ }, colors);
337
381
  }
338
382
  function isRecord(value) {
339
383
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -417,7 +461,7 @@ function warnOnce(message) {
417
461
  function isPath(chosen) {
418
462
  return chosen.includes("/") || chosen.endsWith(".json");
419
463
  }
420
- function loadTheme({ env = process4.env, from = process4.cwd(), home = homedir2(), warn = warnOnce } = {}) {
464
+ function loadTheme({ env = process5.env, from = process5.cwd(), home = homedir2(), warn = warnOnce } = {}) {
421
465
  const theme = { colors: { ...DEFAULT.colors }, glyphs: { ...DEFAULT.glyphs }, accents: [...DEFAULT.accents], bins: Object.create(null) };
422
466
  const sources = [];
423
467
  const chosen = env.KIT_THEME ?? "";
@@ -447,7 +491,7 @@ function loadTheme({ env = process4.env, from = process4.cwd(), home = homedir2(
447
491
  var current;
448
492
  var currentKey = "";
449
493
  function activeTheme() {
450
- const key = `${process4.cwd()}\x00${process4.env.KIT_THEME ?? ""}\x00${process4.env.XDG_CONFIG_HOME ?? ""}`;
494
+ const key = `${process5.cwd()}\x00${process5.env.KIT_THEME ?? ""}\x00${process5.env.XDG_CONFIG_HOME ?? ""}`;
451
495
  if (current === undefined || key !== currentKey) {
452
496
  current = loadTheme();
453
497
  currentKey = key;
@@ -456,20 +500,20 @@ function activeTheme() {
456
500
  }
457
501
 
458
502
  // ../kit/src/style.ts
459
- function takesControlCodes(stream, env = process5.env) {
460
- return stream.isTTY === true && env.NO_COLOR === undefined && env.NODE_DISABLE_COLORS === undefined && env.TERM !== "dumb";
503
+ function takesControlCodes(stream, env = process6.env) {
504
+ return stream.isTTY === true && env.NO_COLOR === undefined && env.NODE_DISABLE_COLORS === undefined && env.TERM !== "dumb" && outputMode(stream, env) === "terminal";
461
505
  }
462
- function paint(stream, env = process5.env) {
506
+ function paint(stream, env = process6.env) {
463
507
  return le({ stream: { isTTY: takesControlCodes(stream, env) }, env });
464
508
  }
465
- function role(name, stream, env = process5.env) {
509
+ function role(name, stream, env = process6.env) {
466
510
  const colors = paint(stream, env);
467
- return takesControlCodes(stream, env) ? tinted(colors, activeTheme().theme.colors[name]) : colors;
511
+ return takesControlCodes(stream, env) ? tinted(colors, activeTheme().theme.colors[name], env) : colors;
468
512
  }
469
513
  var STATUS_WORDS = {
470
514
  passed: ["passed", "success", "succeeded", "ok"],
471
515
  failed: ["failed", "failure", "error", "errored"],
472
- running: ["running", "in_progress", "started"],
516
+ running: ["running", "in_progress", "started", "holding"],
473
517
  queued: ["queued", "waiting", "pending", "created", "blocked", "scheduled"],
474
518
  skipped: ["skipped", "cancelled", "canceled", "manual"]
475
519
  };
@@ -481,32 +525,35 @@ function upTo(text, separator) {
481
525
  return end === -1 ? text : text.slice(0, end);
482
526
  }
483
527
  var ESCAPE = String.fromCharCode(27);
528
+ function jsonText(value, replacer, { stream = process7.stdout, env = process7.env } = {}) {
529
+ return JSON.stringify(value, replacer, outputMode(stream, env) === "agent" ? undefined : 2);
530
+ }
484
531
  function printJson(value) {
485
- console.log(JSON.stringify(value, undefined, 2));
532
+ console.log(jsonText(value));
486
533
  }
487
534
 
488
535
  // ../kit/src/help.ts
489
- import process6 from "process";
536
+ import process8 from "process";
490
537
  var OPTIONAL = /\[[^\]]+\]/g;
491
538
  var USAGE_PREFIX = /^usage:\s*/i;
492
539
  var FULL_STOP = /\.$/;
493
- function usageLine(usage, colors = paint(process6.stdout)) {
540
+ function usageLine(usage, colors = paint(process8.stdout)) {
494
541
  return `${colors.bold("Usage:")} ${usage.replace(USAGE_PREFIX, "").replaceAll(OPTIONAL, (part) => colors.dim(part))}`;
495
542
  }
496
- function helpText(usage, options, colors = paint(process6.stdout), { examples = [], replaces, exits = [] } = {}) {
543
+ function helpText(usage, options, colors = paint(process8.stdout), { examples = [], replaces, exits = [] } = {}) {
497
544
  return [usageLine(usage, colors), ...replacesRows(replaces, colors), "", colors.bold("Options:"), ...optionRows(options, colors), ...exitRows(exits, colors), ...exampleRows(examples, colors)].join(`
498
545
  `);
499
546
  }
500
- function exitRows(exits, colors = paint(process6.stdout)) {
547
+ function exitRows(exits, colors = paint(process8.stdout)) {
501
548
  if (exits.length === 0)
502
549
  return [];
503
550
  const width = Math.max(...exits.map(([code]) => String(code).length));
504
551
  return ["", colors.bold("Exits:"), ...exits.map(([code, meaning]) => ` ${colors.cyan(String(code).padEnd(width))} ${meaning}`)];
505
552
  }
506
- function replacesRows(replaces, colors = paint(process6.stdout)) {
507
- return replaces === undefined ? [] : ["", `${colors.bold("Instead of:")} ${replaces}`];
553
+ function replacesRows(replaces, colors = paint(process8.stdout)) {
554
+ return replaces === undefined || outputMode() === "agent" ? [] : ["", `${colors.bold("Instead of:")} ${replaces}`];
508
555
  }
509
- function wantsHelpJson(env = process6.env) {
556
+ function wantsHelpJson(env = process8.env) {
510
557
  return env.KIT_HELP === "json";
511
558
  }
512
559
  function description(usage, { examples = [], replaces, exits } = {}, options) {
@@ -525,12 +572,12 @@ function description(usage, { examples = [], replaces, exits } = {}, options) {
525
572
  ...options === undefined ? {} : { options: Object.entries(options).map(([name, { short, description }]) => ({ name, ...short === undefined ? {} : { short }, ...description === undefined ? {} : { description } })) }
526
573
  };
527
574
  }
528
- function exampleRows(examples, colors = paint(process6.stdout)) {
529
- if (examples.length === 0)
575
+ function exampleRows(examples, colors = paint(process8.stdout)) {
576
+ if (examples.length === 0 || outputMode() === "agent")
530
577
  return [];
531
578
  return ["", colors.bold("Examples:"), ...examples.map((example) => typeof example === "string" ? ` ${example}` : ` ${example[0]} ${colors.dim(`# ${example[1]}`)}`)];
532
579
  }
533
- function optionRows(options, colors = paint(process6.stdout)) {
580
+ function optionRows(options, colors = paint(process8.stdout)) {
534
581
  const rows = Object.entries(options).map(([name, option]) => {
535
582
  const short = option.short === undefined ? " " : `${colors.cyan(`-${option.short}`)}, `;
536
583
  const named = option.choices === undefined ? "<value>" : `<${option.choices.join("|")}>`;
@@ -580,7 +627,6 @@ var DASHES = /^-+/;
580
627
  function jsonOption(holds) {
581
628
  return { type: "boolean", description: holds === undefined ? "print JSON" : `print JSON: ${holds}` };
582
629
  }
583
-
584
630
  class Exit extends Error {
585
631
  code;
586
632
  hint;
@@ -624,7 +670,7 @@ var OUTPUT_OPTIONS = {
624
670
  color: { type: "string", description: "auto (on a terminal alone), always or never" },
625
671
  plain: { type: "boolean", description: "plain text, no colour or spinner: --color never" }
626
672
  };
627
- function applyOutput(color, plain, env = process7.env) {
673
+ function applyOutput(color, plain, env = process9.env) {
628
674
  const when = plain === true ? "never" : color ?? "auto";
629
675
  if (when === "auto")
630
676
  return;
@@ -801,13 +847,13 @@ var CLOSE_LINK = `${ESC}]8;;${ESC}\\`;
801
847
  // ../kit/src/cli.ts
802
848
  import { readFileSync as readFileSync2, realpathSync } from "fs";
803
849
  import { dirname as dirname3, join as join5 } from "path";
804
- import process9 from "process";
850
+ import process11 from "process";
805
851
 
806
852
  // ../kit/src/usage.ts
807
853
  import { appendFileSync, mkdirSync } from "fs";
808
854
  import { homedir as homedir3 } from "os";
809
855
  import { dirname as dirname2, isAbsolute, join as join4 } from "path";
810
- import process8 from "process";
856
+ import process10 from "process";
811
857
 
812
858
  // ../kit/src/logs.ts
813
859
  var DAY_MS = 24 * 60 * 60 * 1000;
@@ -821,14 +867,14 @@ var usageError;
821
867
  function noteUsageError(message) {
822
868
  usageError = message;
823
869
  }
824
- function stateHome(env = process8.env) {
870
+ function stateHome(env = process10.env) {
825
871
  const dir = env.XDG_STATE_HOME;
826
872
  return dir !== undefined && isAbsolute(dir) ? dir : join4(homedir3(), ".local", "state");
827
873
  }
828
- function usagePath(env = process8.env) {
874
+ function usagePath(env = process10.env) {
829
875
  return join4(stateHome(env), CLI_FOLDER, "usage.jsonl");
830
876
  }
831
- function usageRecorded(env = process8.env) {
877
+ function usageRecorded(env = process10.env) {
832
878
  const off = env.KAYNOOO_CLI_NO_USAGE;
833
879
  return env.NODE_ENV !== "test" && (off === undefined || off === "" || off === "0");
834
880
  }
@@ -842,7 +888,7 @@ function usageEntry(bin, command, argv, code, now = new Date) {
842
888
  usageError = undefined;
843
889
  return { at: now.toISOString(), bin, ...command === undefined ? {} : { command }, flags: flagNames(argv), code, ...unknown === undefined ? {} : { unknown } };
844
890
  }
845
- function recordUsage(entry, env = process8.env) {
891
+ function recordUsage(entry, env = process10.env) {
846
892
  if (!usageRecorded(env))
847
893
  return;
848
894
  try {
@@ -860,8 +906,8 @@ var LEVEL_OPTIONS = {
860
906
  };
861
907
  var TOP_OPTIONS = { ...LEVEL_OPTIONS, version: { type: "boolean", short: "v", description: "print the version" } };
862
908
  function printError(prefix, message, hint, cause) {
863
- const colors = paint(process9.stderr);
864
- const styled = takesControlCodes(process9.stderr);
909
+ const colors = paint(process11.stderr);
910
+ const styled = takesControlCodes(process11.stderr);
865
911
  const head = styled ? `${colors.red.bold("error")} ${colors.dim(`${prefix}:`)}` : `error: ${prefix}:`;
866
912
  const under = (label, text, tint) => text === undefined ? "" : `
867
913
  ${styled ? tint(label) : `${label}:`} ${text}`;
@@ -954,7 +1000,7 @@ function packageVersion(entry = Bun.main) {
954
1000
  return "0.0.0";
955
1001
  }
956
1002
  }
957
- async function runMain({ name, version, run, redact, usage }, argv = process9.argv.slice(2)) {
1003
+ async function runMain({ name, version, run, redact, usage }, argv = process11.argv.slice(2)) {
958
1004
  if (argv[0] === COMPLETE)
959
1005
  return printCandidates(leafCandidates(run, argv.slice(1)));
960
1006
  if (argv.length === 1 && (argv[0] === "--version" || argv[0] === "-v")) {
@@ -985,6 +1031,8 @@ function composeError({ code, stderr }) {
985
1031
  `).at(-1) ?? "";
986
1032
  return last === "" ? `exit ${code}` : last;
987
1033
  }
1034
+ // ../kit/src/folders.ts
1035
+ var SKIPPED_FOLDERS = new Set(["node_modules", "vendor", "dist", "build", "target", "coverage", "venv", "__pycache__"]);
988
1036
  // ../kit/src/secrets.ts
989
1037
  var SECRET_PART = /token|secret|passw|credential|private|dsn|apikey|accesskey|key$/i;
990
1038
  var SECRET_WORDS = new Set(["key", "pass", "pwd", "auth", "salt", "cookie", "session", "jwt", "ssn", "iban", "card", "cvv", "otp"]);
@@ -997,7 +1045,7 @@ var {Glob } = globalThis.Bun;
997
1045
  var SESSION_FILES = new Glob("*.json");
998
1046
  var ACCOUNT_DIRS = new Glob("*/sessions");
999
1047
  // src/dx.ts
1000
- import process15 from "process";
1048
+ import process17 from "process";
1001
1049
 
1002
1050
  // ../env/src/files.ts
1003
1051
  import { chmod, chown, readdir, realpath, rename, rm, stat, writeFile } from "fs/promises";
@@ -1353,7 +1401,7 @@ class Redactor {
1353
1401
  import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync } from "fs";
1354
1402
  import { homedir as homedir4 } from "os";
1355
1403
  import { dirname as dirname5, join as join7 } from "path";
1356
- import process13 from "process";
1404
+ import process15 from "process";
1357
1405
 
1358
1406
  // ../kit/src/picker/score.ts
1359
1407
  var MATCH = 16;
@@ -1456,6 +1504,14 @@ var KEYS = {
1456
1504
  "\x1B[B": "down",
1457
1505
  "\x1BOB": "down",
1458
1506
  "\x0E": "down",
1507
+ "\x1B[5~": "pageup",
1508
+ "\x1B[6~": "pagedown",
1509
+ "\x1B[H": "home",
1510
+ "\x1B[1~": "home",
1511
+ "\x1BOH": "home",
1512
+ "\x1B[F": "end",
1513
+ "\x1B[4~": "end",
1514
+ "\x1BOF": "end",
1459
1515
  "\r": "enter",
1460
1516
  "\n": "enter",
1461
1517
  "\x03": "cancel",
@@ -1464,7 +1520,10 @@ var KEYS = {
1464
1520
  "\b": "erase",
1465
1521
  "\x15": "clear",
1466
1522
  "\x17": "word",
1467
- "\t": "tab"
1523
+ "\t": "tab",
1524
+ "\x1F": "help",
1525
+ "\x1BOP": "help",
1526
+ "\x1B[11~": "help"
1468
1527
  };
1469
1528
  var CTRL_KEY = /^ctrl-[abd-gkln-z]$/;
1470
1529
  function repeat(value, count) {
@@ -1534,7 +1593,7 @@ function parseKeys(input, paste = { active: false }, ctrls = []) {
1534
1593
  });
1535
1594
  }
1536
1595
  function createState(rows, { multi = false, prompt = "> ", query = "", header = "", expect = [], searched = Number.POSITIVE_INFINITY } = {}) {
1537
- const state = { rows: [], items: [], styles: [], searchable: [], searched, header, expect, key: "", status: "", query, matches: [], cursor: 0, marked: new Set, multi, prompt, done: undefined };
1596
+ const state = { rows: [], items: [], styles: [], searchable: [], searched, header, expect, key: "", status: "", query, matches: [], cursor: 0, help: false, page: 10, marked: new Set, multi, prompt, done: undefined };
1538
1597
  return withRows(state, rows);
1539
1598
  }
1540
1599
  function withRows(state, rows, header = state.header) {
@@ -1556,6 +1615,8 @@ function highlighted(state) {
1556
1615
  function press(state, key) {
1557
1616
  if (state.done !== undefined)
1558
1617
  return state;
1618
+ if (state.help)
1619
+ return { ...state, help: false };
1559
1620
  if (typeof key === "object") {
1560
1621
  if ("text" in key)
1561
1622
  return withQuery(state, state.query + key.text);
@@ -1566,8 +1627,18 @@ function press(state, key) {
1566
1627
  return move(state, -1);
1567
1628
  case "down":
1568
1629
  return move(state, 1);
1630
+ case "pageup":
1631
+ return move(state, -state.page);
1632
+ case "pagedown":
1633
+ return move(state, state.page);
1634
+ case "home":
1635
+ return move(state, -state.matches.length);
1636
+ case "end":
1637
+ return move(state, state.matches.length);
1569
1638
  case "cancel":
1570
1639
  return { ...state, done: "cancel" };
1640
+ case "help":
1641
+ return { ...state, help: true };
1571
1642
  case "erase":
1572
1643
  return withQuery(state, [...state.query].slice(0, -1).join(""));
1573
1644
  case "clear":
@@ -1596,11 +1667,11 @@ function chosen(state) {
1596
1667
 
1597
1668
  // ../kit/src/picker/terminal.ts
1598
1669
  import { openSync } from "fs";
1599
- import process12 from "process";
1670
+ import process14 from "process";
1600
1671
  import tty from "tty";
1601
1672
 
1602
1673
  // ../kit/src/picker/preview.ts
1603
- import process11 from "process";
1674
+ import process13 from "process";
1604
1675
 
1605
1676
  // ../kit/src/shell.ts
1606
1677
  var SEPARATORS = new Set([`
@@ -1669,7 +1740,7 @@ async function runPreview(template, line, signal, hidden = []) {
1669
1740
  ${previewCommand(template, line, hidden)}`], { stdin: "ignore", stdout: "pipe", stderr: "ignore", detached: true });
1670
1741
  const stop = () => {
1671
1742
  try {
1672
- process11.kill(-proc.pid, "SIGKILL");
1743
+ process13.kill(-proc.pid, "SIGKILL");
1673
1744
  } catch {
1674
1745
  proc.kill();
1675
1746
  }
@@ -1747,7 +1818,18 @@ function fitStyled(line, width, plain = identity) {
1747
1818
  function caretColumn(state, layout) {
1748
1819
  return Math.min(layout.listWidth, Bun.stringWidth(state.prompt) + Bun.stringWidth(state.query) + 1);
1749
1820
  }
1821
+ function helpLines(state, layout, theme) {
1822
+ const lines = [
1823
+ "type to filter, up, down, ctrl-p, ctrl-n, pgup, pgdn, home, end move",
1824
+ `enter picks${state.multi ? ", tab marks" : ""}, ctrl-u clears the query, ctrl-w a word, esc or ctrl-c cancels`,
1825
+ ...state.expect.length > 0 ? [`${state.expect.join(", ")} end the pick as this command asks`] : []
1826
+ ];
1827
+ const title = "keys, any key closes this help";
1828
+ return [{ text: theme.accent(title), width: title.length }, ...lines.map((line) => fit(` ${line}`, layout.listWidth, theme.label))];
1829
+ }
1750
1830
  function listLines(state, layout, theme) {
1831
+ if (state.help)
1832
+ return helpLines(state, layout, theme);
1751
1833
  const header = state.header === "" ? [] : state.header.split(`
1752
1834
  `).map((line) => fitStyled(line, layout.listWidth, theme.label));
1753
1835
  const rows = Math.max(1, layout.listHeight - 2 - header.length);
@@ -1756,7 +1838,8 @@ function listLines(state, layout, theme) {
1756
1838
  const marks = (state.marked.size > 0 ? ` (${state.marked.size} marked)` : "") + (state.status === "" ? "" : ` ${state.status}`);
1757
1839
  const promptWidth = Bun.stringWidth(state.prompt);
1758
1840
  const query = fit(state.query, Math.max(1, layout.listWidth - promptWidth), identity);
1759
- const count = fit(` ${state.matches.length}/${state.items.length}${marks}`, layout.listWidth, theme.label);
1841
+ const place = state.matches.length > rows ? ` (${state.cursor + 1} of ${state.matches.length})` : "";
1842
+ const count = fit(` ${state.matches.length}/${state.items.length}${place}${marks}`, layout.listWidth, theme.label);
1760
1843
  const lines = [{ text: theme.accent(state.prompt) + query.text, width: promptWidth + query.width }, count, ...header];
1761
1844
  if (state.matches.length === 0)
1762
1845
  lines.push(fit(" no match", layout.listWidth, theme.label));
@@ -1783,6 +1866,7 @@ function render(state, layout, theme, preview = []) {
1783
1866
 
1784
1867
  // ../kit/src/picker/terminal.ts
1785
1868
  var PREVIEW_DELAY_MS = 120;
1869
+ var ESCAPE_WAIT_MS = 30;
1786
1870
  var OPEN = { ctrl: "open" };
1787
1871
  var HIDE_CURSOR = "\x1B[?25l";
1788
1872
  var SHOW_CURSOR = "\x1B[?25h";
@@ -1800,7 +1884,7 @@ function openTerminal() {
1800
1884
  } catch {
1801
1885
  return fail("no terminal to read keys from (/dev/tty)");
1802
1886
  }
1803
- const out = process12.stderr;
1887
+ const out = process14.stderr;
1804
1888
  let resize;
1805
1889
  return {
1806
1890
  get columns() {
@@ -1838,20 +1922,29 @@ async function select(rows, settings, terminal = openTerminal()) {
1838
1922
  let shownLine;
1839
1923
  let finish;
1840
1924
  let restored = false;
1925
+ let escape;
1926
+ let caret;
1841
1927
  function draw(clear = false) {
1842
1928
  if (restored)
1843
1929
  return;
1844
1930
  const layout = layoutFor({ columns: terminal.columns, rows: terminal.rows }, settings.height, settings.preview !== undefined, settings.previewPosition);
1845
1931
  let frame = HIDE_CURSOR;
1846
1932
  if (reserved !== layout.height || clear) {
1933
+ frame += clear && caret !== undefined ? up(Math.floor((caret - 1) / terminal.columns)) : "";
1847
1934
  frame += CLEAR_BELOW + `
1848
1935
  `.repeat(layout.height - 1) + up(layout.height - 1);
1849
1936
  reserved = layout.height;
1850
1937
  }
1938
+ caret = caretColumn(state, layout);
1851
1939
  frame += `\r${render(state, layout, theme, preview).map((line) => `\x1B[2K${line}`).join(`
1852
- `)}${up(layout.height - 1)}\r\x1B[${caretColumn(state, layout)}G${SHOW_CURSOR}`;
1940
+ `)}${up(layout.height - 1)}\r\x1B[${caret}G${SHOW_CURSOR}`;
1853
1941
  terminal.write(frame);
1854
1942
  }
1943
+ function pageSize() {
1944
+ const headerLines = state.header === "" ? 0 : state.header.split(`
1945
+ `).length;
1946
+ return Math.max(1, layoutFor({ columns: terminal.columns, rows: terminal.rows }, settings.height, settings.preview !== undefined, settings.previewPosition).listHeight - 2 - headerLines);
1947
+ }
1855
1948
  function refreshPreview() {
1856
1949
  const at = highlighted(state);
1857
1950
  if (settings.preview === undefined || at === shownLine)
@@ -1881,6 +1974,7 @@ async function select(rows, settings, terminal = openTerminal()) {
1881
1974
  return;
1882
1975
  restored = true;
1883
1976
  clearTimeout(previewTimer);
1977
+ clearTimeout(escape);
1884
1978
  previewing.abort();
1885
1979
  terminal.write(`${CLEAR_BELOW}${PASTE_OFF}${SHOW_CURSOR}`);
1886
1980
  terminal.close();
@@ -1919,7 +2013,7 @@ async function select(rows, settings, terminal = openTerminal()) {
1919
2013
  if (key === OPEN || typeof key === "object" && "ctrl" in key && key.ctrl === reload?.key)
1920
2014
  await reloadRows();
1921
2015
  else
1922
- state = press(state, key);
2016
+ state = press({ ...state, page: pageSize() }, key);
1923
2017
  }
1924
2018
  } finally {
1925
2019
  draining = false;
@@ -1932,6 +2026,20 @@ async function select(rows, settings, terminal = openTerminal()) {
1932
2026
  draw();
1933
2027
  }
1934
2028
  function onChunk(chunk) {
2029
+ if (escape !== undefined) {
2030
+ clearTimeout(escape);
2031
+ escape = undefined;
2032
+ chunk = `\x1B${chunk}`;
2033
+ } else if (chunk === "\x1B") {
2034
+ escape = setTimeout(() => {
2035
+ escape = undefined;
2036
+ takeKeys("\x1B");
2037
+ }, ESCAPE_WAIT_MS);
2038
+ return;
2039
+ }
2040
+ takeKeys(chunk);
2041
+ }
2042
+ function takeKeys(chunk) {
1935
2043
  const keys = parseKeys(chunk, paste, [...settings.expect ?? [], ...reload?.key === undefined ? [] : [reload.key]]);
1936
2044
  if (busy && keys.includes("cancel")) {
1937
2045
  stop();
@@ -1943,9 +2051,9 @@ async function select(rows, settings, terminal = openTerminal()) {
1943
2051
  const ended = new Promise((resolve) => {
1944
2052
  finish = resolve;
1945
2053
  });
1946
- process12.once("exit", restore);
2054
+ process14.once("exit", restore);
1947
2055
  for (const signal of SIGNALS)
1948
- process12.once(signal, stop);
2056
+ process14.once(signal, stop);
1949
2057
  try {
1950
2058
  terminal.write(PASTE_ON);
1951
2059
  terminal.listen(onChunk, () => draw(true));
@@ -1958,8 +2066,8 @@ async function select(rows, settings, terminal = openTerminal()) {
1958
2066
  await ended;
1959
2067
  } finally {
1960
2068
  for (const signal of SIGNALS)
1961
- process12.off(signal, stop);
1962
- process12.off("exit", restore);
2069
+ process14.off(signal, stop);
2070
+ process14.off("exit", restore);
1963
2071
  restore();
1964
2072
  }
1965
2073
  return state.done === "accept" ? { rows: chosen(state), key: state.key, query: state.query } : undefined;
@@ -1997,7 +2105,7 @@ function heightFor(count) {
1997
2105
  return String(Math.max(15, Math.ceil((count + 4) / 0.6)));
1998
2106
  }
1999
2107
  function historyFile(name) {
2000
- return join7(process13.env.XDG_CACHE_HOME ?? join7(homedir4(), ".cache"), name, "history.json");
2108
+ return join7(process15.env.XDG_CACHE_HOME ?? join7(homedir4(), ".cache"), name, "history.json");
2001
2109
  }
2002
2110
  function history(name) {
2003
2111
  try {
@@ -2008,12 +2116,12 @@ function history(name) {
2008
2116
  }
2009
2117
  }
2010
2118
  function runs(name) {
2011
- return history(name)[process13.cwd()] ?? [];
2119
+ return history(name)[process15.cwd()] ?? [];
2012
2120
  }
2013
2121
  function record(name, run) {
2014
2122
  const all = history(name);
2015
2123
  const file = historyFile(name);
2016
- all[process13.cwd()] = [run, ...(all[process13.cwd()] ?? []).filter((earlier) => earlier.id !== run.id)].slice(0, REMEMBERED);
2124
+ all[process15.cwd()] = [run, ...(all[process15.cwd()] ?? []).filter((earlier) => earlier.id !== run.id)].slice(0, REMEMBERED);
2017
2125
  mkdirSync2(dirname5(file), { recursive: true });
2018
2126
  writeFileSync(file, JSON.stringify(all));
2019
2127
  }
@@ -2037,7 +2145,7 @@ function list(rows) {
2037
2145
  }
2038
2146
 
2039
2147
  // src/picker.ts
2040
- import process14 from "process";
2148
+ import process16 from "process";
2041
2149
 
2042
2150
  // src/stack.ts
2043
2151
  import { existsSync as existsSync4, readdirSync, readFileSync as readFileSync4 } from "fs";
@@ -2045,13 +2153,12 @@ import { dirname as dirname6, join as join8, relative, resolve as resolve3 } fro
2045
2153
  var LABEL_FORMAT = ["project", "project.config_files", "project.environment_file", "project.working_dir"].map((label) => `{{.Label "com.docker.compose.${label}"}}`).join("\t");
2046
2154
  var COMPOSE_FILE_VAR = /^\s*(?:export\s+)?COMPOSE_FILE\s*=\s*\S/m;
2047
2155
  var COMPOSE_FILES = ["compose.yaml", "compose.yml", "docker-compose.yaml", "docker-compose.yml"];
2048
- var SKIPPED2 = new Set(["node_modules", "vendor", "dist", "build", "target", "venv", "__pycache__"]);
2049
2156
  var ACTIONS = ["shell", "logs", "restart", "up", "stop", "build"];
2050
2157
  function from(dir, command) {
2051
2158
  return dir === "." ? command : ["env", "-C", dir, ...command];
2052
2159
  }
2053
2160
  function children(dir = ".") {
2054
- return readdirSync(dir, { withFileTypes: true }).filter((child) => child.isDirectory() && !child.name.startsWith(".") && !SKIPPED2.has(child.name)).map((child) => child.name).sort();
2161
+ return readdirSync(dir, { withFileTypes: true }).filter((child) => child.isDirectory() && !child.name.startsWith(".") && !SKIPPED_FOLDERS.has(child.name)).map((child) => child.name).sort();
2055
2162
  }
2056
2163
  function up2(dir) {
2057
2164
  const dirs = [];
@@ -2207,7 +2314,7 @@ async function logsOf(stack, name) {
2207
2314
  var KEYS2 = { "ctrl-l": "logs", "ctrl-r": "restart", "ctrl-x": "stop", "ctrl-b": "build" };
2208
2315
  var KEY_LABELS = { "ctrl-l": "Logs", "ctrl-r": "Restart", "ctrl-x": "Stop", "ctrl-b": "Rebuild" };
2209
2316
  function row(service) {
2210
- const colors = paint(process14.stderr);
2317
+ const colors = paint(process16.stderr);
2211
2318
  const dir = service.stack.dir;
2212
2319
  const project = service.stack.project;
2213
2320
  const name = project === undefined ? service.name : `${colors.dim(`${project}:`)}${service.name}`;
@@ -2223,7 +2330,7 @@ async function pickService(services) {
2223
2330
  expect: Object.keys(KEYS2),
2224
2331
  searched: 1,
2225
2332
  height: heightFor(services.length),
2226
- preview: `${process14.execPath} ${process14.argv[1]} --show {1} {2} {4}`
2333
+ preview: `${process16.execPath} ${process16.argv[1]} --show {1} {2} {4}`
2227
2334
  });
2228
2335
  const service = services.find((candidate) => key(candidate) === picked?.hidden[2]);
2229
2336
  return picked === undefined || service === undefined ? undefined : { service, key: picked.key };
@@ -2243,7 +2350,7 @@ var options = {
2243
2350
  follow: { type: "boolean", negative: "print the logs once and exit instead of following them" }
2244
2351
  };
2245
2352
  function table2(services) {
2246
- const colors = paint(process15.stdout);
2353
+ const colors = paint(process17.stdout);
2247
2354
  const keys = services.map(key);
2248
2355
  const width = Math.max(...keys.map((name) => name.length));
2249
2356
  const states = Math.max(...services.map((service) => service.state.length));
@@ -2286,7 +2393,7 @@ async function hidden(flag, rest, services) {
2286
2393
  const service = await serviceNamed(dir, name, project);
2287
2394
  if (service === undefined)
2288
2395
  return 0;
2289
- const colors = paint(process15.stdout);
2396
+ const colors = paint(process17.stdout);
2290
2397
  console.log(`${colors[stateColor(service.state)](service.status || service.state)} ${colors.dim(service.image)}${service.ports ? ` ${colors.cyan(service.ports)}` : ""}
2291
2398
  `);
2292
2399
  console.log(await logsOf(service.stack, name));
@@ -2304,8 +2411,8 @@ async function followMasked(command) {
2304
2411
  const decoder = new TextDecoder;
2305
2412
  const proc = Bun.spawn(command, { stdio: ["inherit", "pipe", "inherit"] });
2306
2413
  for await (const chunk of proc.stdout)
2307
- process15.stdout.write(redactor.push(decoder.decode(chunk, { stream: true })));
2308
- process15.stdout.write(redactor.end());
2414
+ process17.stdout.write(redactor.push(decoder.decode(chunk, { stream: true })));
2415
+ process17.stdout.write(redactor.end());
2309
2416
  return proc.exited;
2310
2417
  }
2311
2418
  async function dx(args) {
@@ -2325,7 +2432,7 @@ async function dx(args) {
2325
2432
  let service;
2326
2433
  let action;
2327
2434
  if (named === undefined) {
2328
- const interactive = process15.stdin.isTTY && process15.stderr.isTTY && parsed.values.json !== true;
2435
+ const interactive = isInteractive() && parsed.values.json !== true;
2329
2436
  if (!interactive) {
2330
2437
  if (emit)
2331
2438
  fail("the picker needs a terminal", 1);
@@ -2367,4 +2474,4 @@ async function dx(args) {
2367
2474
  }
2368
2475
 
2369
2476
  // src/dx-cli.ts
2370
- process16.exitCode = await runMain({ name: "dx", run: dx });
2477
+ process18.exitCode = await runMain({ name: "dx", run: dx });