@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/sf-cli.js CHANGED
@@ -2,13 +2,37 @@
2
2
  // @bun
3
3
 
4
4
  // src/sf-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"]);
@@ -1000,13 +1048,13 @@ var ACCOUNT_DIRS = new Glob("*/sessions");
1000
1048
  import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "fs";
1001
1049
  import { homedir as homedir5 } from "os";
1002
1050
  import { dirname as dirname7, join as join9, resolve as resolve4 } from "path";
1003
- import process15 from "process";
1051
+ import process17 from "process";
1004
1052
 
1005
1053
  // ../kit/src/picker.ts
1006
1054
  import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync } from "fs";
1007
1055
  import { homedir as homedir4 } from "os";
1008
1056
  import { dirname as dirname4, join as join6 } from "path";
1009
- import process13 from "process";
1057
+ import process15 from "process";
1010
1058
 
1011
1059
  // ../kit/src/picker/score.ts
1012
1060
  var MATCH = 16;
@@ -1109,6 +1157,14 @@ var KEYS = {
1109
1157
  "\x1B[B": "down",
1110
1158
  "\x1BOB": "down",
1111
1159
  "\x0E": "down",
1160
+ "\x1B[5~": "pageup",
1161
+ "\x1B[6~": "pagedown",
1162
+ "\x1B[H": "home",
1163
+ "\x1B[1~": "home",
1164
+ "\x1BOH": "home",
1165
+ "\x1B[F": "end",
1166
+ "\x1B[4~": "end",
1167
+ "\x1BOF": "end",
1112
1168
  "\r": "enter",
1113
1169
  "\n": "enter",
1114
1170
  "\x03": "cancel",
@@ -1117,7 +1173,10 @@ var KEYS = {
1117
1173
  "\b": "erase",
1118
1174
  "\x15": "clear",
1119
1175
  "\x17": "word",
1120
- "\t": "tab"
1176
+ "\t": "tab",
1177
+ "\x1F": "help",
1178
+ "\x1BOP": "help",
1179
+ "\x1B[11~": "help"
1121
1180
  };
1122
1181
  var CTRL_KEY = /^ctrl-[abd-gkln-z]$/;
1123
1182
  function repeat(value, count) {
@@ -1187,7 +1246,7 @@ function parseKeys(input, paste = { active: false }, ctrls = []) {
1187
1246
  });
1188
1247
  }
1189
1248
  function createState(rows, { multi = false, prompt = "> ", query = "", header = "", expect = [], searched = Number.POSITIVE_INFINITY } = {}) {
1190
- const state = { rows: [], items: [], styles: [], searchable: [], searched, header, expect, key: "", status: "", query, matches: [], cursor: 0, marked: new Set, multi, prompt, done: undefined };
1249
+ 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 };
1191
1250
  return withRows(state, rows);
1192
1251
  }
1193
1252
  function withRows(state, rows, header = state.header) {
@@ -1209,6 +1268,8 @@ function highlighted(state) {
1209
1268
  function press(state, key) {
1210
1269
  if (state.done !== undefined)
1211
1270
  return state;
1271
+ if (state.help)
1272
+ return { ...state, help: false };
1212
1273
  if (typeof key === "object") {
1213
1274
  if ("text" in key)
1214
1275
  return withQuery(state, state.query + key.text);
@@ -1219,8 +1280,18 @@ function press(state, key) {
1219
1280
  return move(state, -1);
1220
1281
  case "down":
1221
1282
  return move(state, 1);
1283
+ case "pageup":
1284
+ return move(state, -state.page);
1285
+ case "pagedown":
1286
+ return move(state, state.page);
1287
+ case "home":
1288
+ return move(state, -state.matches.length);
1289
+ case "end":
1290
+ return move(state, state.matches.length);
1222
1291
  case "cancel":
1223
1292
  return { ...state, done: "cancel" };
1293
+ case "help":
1294
+ return { ...state, help: true };
1224
1295
  case "erase":
1225
1296
  return withQuery(state, [...state.query].slice(0, -1).join(""));
1226
1297
  case "clear":
@@ -1249,11 +1320,11 @@ function chosen(state) {
1249
1320
 
1250
1321
  // ../kit/src/picker/terminal.ts
1251
1322
  import { openSync } from "fs";
1252
- import process12 from "process";
1323
+ import process14 from "process";
1253
1324
  import tty from "tty";
1254
1325
 
1255
1326
  // ../kit/src/picker/preview.ts
1256
- import process11 from "process";
1327
+ import process13 from "process";
1257
1328
 
1258
1329
  // ../kit/src/shell.ts
1259
1330
  var SEPARATORS = new Set([`
@@ -1322,7 +1393,7 @@ async function runPreview(template, line, signal, hidden = []) {
1322
1393
  ${previewCommand(template, line, hidden)}`], { stdin: "ignore", stdout: "pipe", stderr: "ignore", detached: true });
1323
1394
  const stop = () => {
1324
1395
  try {
1325
- process11.kill(-proc.pid, "SIGKILL");
1396
+ process13.kill(-proc.pid, "SIGKILL");
1326
1397
  } catch {
1327
1398
  proc.kill();
1328
1399
  }
@@ -1400,7 +1471,18 @@ function fitStyled(line, width, plain = identity) {
1400
1471
  function caretColumn(state, layout) {
1401
1472
  return Math.min(layout.listWidth, Bun.stringWidth(state.prompt) + Bun.stringWidth(state.query) + 1);
1402
1473
  }
1474
+ function helpLines(state, layout, theme) {
1475
+ const lines = [
1476
+ "type to filter, up, down, ctrl-p, ctrl-n, pgup, pgdn, home, end move",
1477
+ `enter picks${state.multi ? ", tab marks" : ""}, ctrl-u clears the query, ctrl-w a word, esc or ctrl-c cancels`,
1478
+ ...state.expect.length > 0 ? [`${state.expect.join(", ")} end the pick as this command asks`] : []
1479
+ ];
1480
+ const title = "keys, any key closes this help";
1481
+ return [{ text: theme.accent(title), width: title.length }, ...lines.map((line) => fit(` ${line}`, layout.listWidth, theme.label))];
1482
+ }
1403
1483
  function listLines(state, layout, theme) {
1484
+ if (state.help)
1485
+ return helpLines(state, layout, theme);
1404
1486
  const header = state.header === "" ? [] : state.header.split(`
1405
1487
  `).map((line) => fitStyled(line, layout.listWidth, theme.label));
1406
1488
  const rows = Math.max(1, layout.listHeight - 2 - header.length);
@@ -1409,7 +1491,8 @@ function listLines(state, layout, theme) {
1409
1491
  const marks = (state.marked.size > 0 ? ` (${state.marked.size} marked)` : "") + (state.status === "" ? "" : ` ${state.status}`);
1410
1492
  const promptWidth = Bun.stringWidth(state.prompt);
1411
1493
  const query = fit(state.query, Math.max(1, layout.listWidth - promptWidth), identity);
1412
- const count = fit(` ${state.matches.length}/${state.items.length}${marks}`, layout.listWidth, theme.label);
1494
+ const place = state.matches.length > rows ? ` (${state.cursor + 1} of ${state.matches.length})` : "";
1495
+ const count = fit(` ${state.matches.length}/${state.items.length}${place}${marks}`, layout.listWidth, theme.label);
1413
1496
  const lines = [{ text: theme.accent(state.prompt) + query.text, width: promptWidth + query.width }, count, ...header];
1414
1497
  if (state.matches.length === 0)
1415
1498
  lines.push(fit(" no match", layout.listWidth, theme.label));
@@ -1436,6 +1519,7 @@ function render(state, layout, theme, preview = []) {
1436
1519
 
1437
1520
  // ../kit/src/picker/terminal.ts
1438
1521
  var PREVIEW_DELAY_MS = 120;
1522
+ var ESCAPE_WAIT_MS = 30;
1439
1523
  var OPEN = { ctrl: "open" };
1440
1524
  var HIDE_CURSOR = "\x1B[?25l";
1441
1525
  var SHOW_CURSOR = "\x1B[?25h";
@@ -1453,7 +1537,7 @@ function openTerminal() {
1453
1537
  } catch {
1454
1538
  return fail("no terminal to read keys from (/dev/tty)");
1455
1539
  }
1456
- const out = process12.stderr;
1540
+ const out = process14.stderr;
1457
1541
  let resize;
1458
1542
  return {
1459
1543
  get columns() {
@@ -1491,20 +1575,29 @@ async function select(rows, settings, terminal = openTerminal()) {
1491
1575
  let shownLine;
1492
1576
  let finish;
1493
1577
  let restored = false;
1578
+ let escape;
1579
+ let caret;
1494
1580
  function draw(clear = false) {
1495
1581
  if (restored)
1496
1582
  return;
1497
1583
  const layout = layoutFor({ columns: terminal.columns, rows: terminal.rows }, settings.height, settings.preview !== undefined, settings.previewPosition);
1498
1584
  let frame = HIDE_CURSOR;
1499
1585
  if (reserved !== layout.height || clear) {
1586
+ frame += clear && caret !== undefined ? up(Math.floor((caret - 1) / terminal.columns)) : "";
1500
1587
  frame += CLEAR_BELOW + `
1501
1588
  `.repeat(layout.height - 1) + up(layout.height - 1);
1502
1589
  reserved = layout.height;
1503
1590
  }
1591
+ caret = caretColumn(state, layout);
1504
1592
  frame += `\r${render(state, layout, theme, preview).map((line) => `\x1B[2K${line}`).join(`
1505
- `)}${up(layout.height - 1)}\r\x1B[${caretColumn(state, layout)}G${SHOW_CURSOR}`;
1593
+ `)}${up(layout.height - 1)}\r\x1B[${caret}G${SHOW_CURSOR}`;
1506
1594
  terminal.write(frame);
1507
1595
  }
1596
+ function pageSize() {
1597
+ const headerLines = state.header === "" ? 0 : state.header.split(`
1598
+ `).length;
1599
+ return Math.max(1, layoutFor({ columns: terminal.columns, rows: terminal.rows }, settings.height, settings.preview !== undefined, settings.previewPosition).listHeight - 2 - headerLines);
1600
+ }
1508
1601
  function refreshPreview() {
1509
1602
  const at = highlighted(state);
1510
1603
  if (settings.preview === undefined || at === shownLine)
@@ -1534,6 +1627,7 @@ async function select(rows, settings, terminal = openTerminal()) {
1534
1627
  return;
1535
1628
  restored = true;
1536
1629
  clearTimeout(previewTimer);
1630
+ clearTimeout(escape);
1537
1631
  previewing.abort();
1538
1632
  terminal.write(`${CLEAR_BELOW}${PASTE_OFF}${SHOW_CURSOR}`);
1539
1633
  terminal.close();
@@ -1572,7 +1666,7 @@ async function select(rows, settings, terminal = openTerminal()) {
1572
1666
  if (key === OPEN || typeof key === "object" && "ctrl" in key && key.ctrl === reload?.key)
1573
1667
  await reloadRows();
1574
1668
  else
1575
- state = press(state, key);
1669
+ state = press({ ...state, page: pageSize() }, key);
1576
1670
  }
1577
1671
  } finally {
1578
1672
  draining = false;
@@ -1585,6 +1679,20 @@ async function select(rows, settings, terminal = openTerminal()) {
1585
1679
  draw();
1586
1680
  }
1587
1681
  function onChunk(chunk) {
1682
+ if (escape !== undefined) {
1683
+ clearTimeout(escape);
1684
+ escape = undefined;
1685
+ chunk = `\x1B${chunk}`;
1686
+ } else if (chunk === "\x1B") {
1687
+ escape = setTimeout(() => {
1688
+ escape = undefined;
1689
+ takeKeys("\x1B");
1690
+ }, ESCAPE_WAIT_MS);
1691
+ return;
1692
+ }
1693
+ takeKeys(chunk);
1694
+ }
1695
+ function takeKeys(chunk) {
1588
1696
  const keys = parseKeys(chunk, paste, [...settings.expect ?? [], ...reload?.key === undefined ? [] : [reload.key]]);
1589
1697
  if (busy && keys.includes("cancel")) {
1590
1698
  stop();
@@ -1596,9 +1704,9 @@ async function select(rows, settings, terminal = openTerminal()) {
1596
1704
  const ended = new Promise((resolve) => {
1597
1705
  finish = resolve;
1598
1706
  });
1599
- process12.once("exit", restore);
1707
+ process14.once("exit", restore);
1600
1708
  for (const signal of SIGNALS)
1601
- process12.once(signal, stop);
1709
+ process14.once(signal, stop);
1602
1710
  try {
1603
1711
  terminal.write(PASTE_ON);
1604
1712
  terminal.listen(onChunk, () => draw(true));
@@ -1611,8 +1719,8 @@ async function select(rows, settings, terminal = openTerminal()) {
1611
1719
  await ended;
1612
1720
  } finally {
1613
1721
  for (const signal of SIGNALS)
1614
- process12.off(signal, stop);
1615
- process12.off("exit", restore);
1722
+ process14.off(signal, stop);
1723
+ process14.off("exit", restore);
1616
1724
  restore();
1617
1725
  }
1618
1726
  return state.done === "accept" ? { rows: chosen(state), key: state.key, query: state.query } : undefined;
@@ -1650,7 +1758,7 @@ function heightFor(count) {
1650
1758
  return String(Math.max(15, Math.ceil((count + 4) / 0.6)));
1651
1759
  }
1652
1760
  function historyFile(name) {
1653
- return join6(process13.env.XDG_CACHE_HOME ?? join6(homedir4(), ".cache"), name, "history.json");
1761
+ return join6(process15.env.XDG_CACHE_HOME ?? join6(homedir4(), ".cache"), name, "history.json");
1654
1762
  }
1655
1763
  function history(name) {
1656
1764
  try {
@@ -1661,12 +1769,12 @@ function history(name) {
1661
1769
  }
1662
1770
  }
1663
1771
  function runs(name) {
1664
- return history(name)[process13.cwd()] ?? [];
1772
+ return history(name)[process15.cwd()] ?? [];
1665
1773
  }
1666
1774
  function record(name, run) {
1667
1775
  const all = history(name);
1668
1776
  const file = historyFile(name);
1669
- all[process13.cwd()] = [run, ...(all[process13.cwd()] ?? []).filter((earlier) => earlier.id !== run.id)].slice(0, REMEMBERED);
1777
+ all[process15.cwd()] = [run, ...(all[process15.cwd()] ?? []).filter((earlier) => earlier.id !== run.id)].slice(0, REMEMBERED);
1670
1778
  mkdirSync2(dirname4(file), { recursive: true });
1671
1779
  writeFileSync(file, JSON.stringify(all));
1672
1780
  }
@@ -1690,12 +1798,10 @@ function list(rows) {
1690
1798
  }
1691
1799
 
1692
1800
  // ../kit/src/prompt.ts
1693
- import process14 from "process";
1694
- var KEY_SEQUENCES2 = /\x1B\[[0-9;]*[A-Z~]|\x1BO[A-Z]|[\s\S]/giu;
1695
- var CONTROL3 = /^[\x00-\x1F\x7F]/;
1696
- var KEYS2 = { "\x1B[A": "up", "\x1BOA": "up", "\x1B[B": "down", "\x1BOB": "down", "\r": "enter", "\n": "enter", "\x03": "cancel", "\x1B": "cancel", "\x7F": "erase", "\b": "erase" };
1697
- function readKeys(input) {
1698
- return (input.match(KEY_SEQUENCES2) ?? []).flatMap((sequence) => KEYS2[sequence] ?? (CONTROL3.test(sequence) ? [] : [{ text: sequence }]));
1801
+ import process16 from "process";
1802
+ var PROMPT_KEYS = new Set(["up", "down", "pageup", "pagedown", "home", "end", "enter", "cancel", "erase"]);
1803
+ function readKeys(input, paste = { active: false }) {
1804
+ return parseKeys(input, paste).filter((key) => typeof key === "object" ? "text" in key : PROMPT_KEYS.has(key));
1699
1805
  }
1700
1806
  async function input2(question, { default: fallback, mask = false, validate, ...settings } = {}) {
1701
1807
  let value = "";
@@ -1729,27 +1835,34 @@ async function input2(question, { default: fallback, mask = false, validate, ...
1729
1835
  function answer(colors, question, state, picked) {
1730
1836
  return state === "enter" ? `${colors.green("\u2713")} ${question} ${colors.cyan(picked)}` : `${colors.red("\u2717")} ${question} ${colors.dim("cancelled")}`;
1731
1837
  }
1732
- async function ask(question, { hint, terminal = process14 }, render, press) {
1838
+ async function ask(question, { hint, terminal = process16 }, render, press) {
1733
1839
  const { stdin, stderr } = terminal;
1734
- if (stdin.isTTY !== true || stderr.isTTY !== true)
1840
+ if (!isInteractive(terminal))
1735
1841
  fail(`cannot ask "${question}" off a terminal`, 2, hint);
1736
1842
  const colors = paint(stderr);
1737
- let drawn = 0;
1843
+ let drawn = [];
1844
+ let current = "asking";
1738
1845
  let outcome;
1846
+ const columns = () => stderr.columns || 80;
1739
1847
  const draw = (state) => {
1740
- const frame = render(colors, state, { columns: stderr.columns || 80, rows: stderr.rows || 24 });
1741
- stderr.write(`${drawn > 1 ? `\x1B[${drawn - 1}A` : ""}\r\x1B[0J${frame.join(`
1848
+ const frame = render(colors, state, { columns: columns(), rows: stderr.rows || 24 });
1849
+ const rows = drawn.reduce((sum, line) => sum + Math.max(1, Math.ceil(Bun.stringWidth(line) / columns())), 0);
1850
+ stderr.write(`${rows > 1 ? `\x1B[${rows - 1}A` : ""}\r\x1B[0J${frame.join(`
1742
1851
  `)}`);
1743
- drawn = frame.length;
1852
+ drawn = frame;
1853
+ current = state;
1744
1854
  };
1855
+ const redraw = () => draw(current);
1856
+ const paste = { active: false };
1745
1857
  try {
1858
+ stderr.on?.("resize", redraw);
1746
1859
  stdin.setRawMode(true);
1747
1860
  stdin.resume();
1748
- stderr.write("\x1B[?25l");
1861
+ stderr.write("\x1B[?25l\x1B[?2004h");
1749
1862
  draw("asking");
1750
1863
  return await new Promise((resolve) => {
1751
1864
  function onData(data) {
1752
- for (const key of readKeys(data.toString())) {
1865
+ for (const key of readKeys(data.toString(), paste)) {
1753
1866
  const pressed = press(key);
1754
1867
  if (pressed !== undefined) {
1755
1868
  settle(pressed);
@@ -1771,9 +1884,10 @@ async function ask(question, { hint, terminal = process14 }, render, press) {
1771
1884
  stdin.on("end", onEnd);
1772
1885
  });
1773
1886
  } finally {
1887
+ stderr.off?.("resize", redraw);
1774
1888
  draw(outcome ?? "cancel");
1775
1889
  stderr.write(`
1776
- \x1B[?25h`);
1890
+ \x1B[?25h\x1B[?2004l`);
1777
1891
  stdin.setRawMode(false);
1778
1892
  stdin.pause();
1779
1893
  }
@@ -2137,13 +2251,12 @@ class Redactor {
2137
2251
  var LABEL_FORMAT = ["project", "project.config_files", "project.environment_file", "project.working_dir"].map((label) => `{{.Label "com.docker.compose.${label}"}}`).join("\t");
2138
2252
  var COMPOSE_FILE_VAR = /^\s*(?:export\s+)?COMPOSE_FILE\s*=\s*\S/m;
2139
2253
  var COMPOSE_FILES = ["compose.yaml", "compose.yml", "docker-compose.yaml", "docker-compose.yml"];
2140
- var SKIPPED2 = new Set(["node_modules", "vendor", "dist", "build", "target", "venv", "__pycache__"]);
2141
2254
  var ACTIONS = ["shell", "logs", "restart", "up", "stop", "build"];
2142
2255
  function from(dir, command) {
2143
2256
  return dir === "." ? command : ["env", "-C", dir, ...command];
2144
2257
  }
2145
2258
  function children(dir = ".") {
2146
- return readdirSync(dir, { withFileTypes: true }).filter((child) => child.isDirectory() && !child.name.startsWith(".") && !SKIPPED2.has(child.name)).map((child) => child.name).sort();
2259
+ return readdirSync(dir, { withFileTypes: true }).filter((child) => child.isDirectory() && !child.name.startsWith(".") && !SKIPPED_FOLDERS.has(child.name)).map((child) => child.name).sort();
2147
2260
  }
2148
2261
  function up2(dir) {
2149
2262
  const dirs = [];
@@ -2326,7 +2439,7 @@ async function consoleAt(dir) {
2326
2439
  const php = stack === undefined ? undefined : phpService((await services(stack)).map((service) => service.name));
2327
2440
  if (stack !== undefined && php !== undefined)
2328
2441
  return { dir, run: [...stack.run, "exec", php, "bin/console"], list: { ...stack.list, argv: [...stack.list.argv, "exec", "-T", php, "bin/console"] } };
2329
- return { dir, run: from(dir, ["php", "bin/console"]), list: { argv: ["php", "bin/console"], cwd: resolve4(dir), env: process15.env } };
2442
+ return { dir, run: from(dir, ["php", "bin/console"]), list: { argv: ["php", "bin/console"], cwd: resolve4(dir), env: process17.env } };
2330
2443
  }
2331
2444
  async function consoles() {
2332
2445
  const here = up2(".").find((dir) => existsSync5(join9(dir, "bin/console")));
@@ -2334,7 +2447,7 @@ async function consoles() {
2334
2447
  return Promise.all([...new Set(dirs)].map(consoleAt));
2335
2448
  }
2336
2449
  function cached(dir) {
2337
- return join9(process15.env.XDG_CACHE_HOME ?? join9(homedir5(), ".cache"), "sf", `${resolve4(dir).replaceAll("/", "%")}.json`);
2450
+ return join9(process17.env.XDG_CACHE_HOME ?? join9(homedir5(), ".cache"), "sf", `${resolve4(dir).replaceAll("/", "%")}.json`);
2338
2451
  }
2339
2452
  function fromCache(dir) {
2340
2453
  try {
@@ -2428,7 +2541,7 @@ async function answered(command, ask) {
2428
2541
  return { argv, given };
2429
2542
  }
2430
2543
  function interactive() {
2431
- return process15.stdin.isTTY && process15.stderr.isTTY;
2544
+ return isInteractive();
2432
2545
  }
2433
2546
  async function execute(emitting, typed, recorded, command) {
2434
2547
  if (emitting) {
@@ -2475,7 +2588,7 @@ async function hidden(flag, rest) {
2475
2588
  return 0;
2476
2589
  }
2477
2590
  if (refreshed)
2478
- Bun.spawn([process15.execPath, process15.argv[1], "--rows"], { stdio: ["ignore", "ignore", "ignore"] }).unref();
2591
+ Bun.spawn([process17.execPath, process17.argv[1], "--rows"], { stdio: ["ignore", "ignore", "ignore"] }).unref();
2479
2592
  list(byRecency(found, runs("sf"), key2).map((entry) => ({
2480
2593
  label: [key2(entry), signature(entry.command)].filter(Boolean).join(" "),
2481
2594
  hint: entry.command.description,
@@ -2535,7 +2648,7 @@ async function sf(args) {
2535
2648
  const picked = await pick(rows(found), {
2536
2649
  prompt: "sf> ",
2537
2650
  searched: 1,
2538
- preview: `${process15.execPath} ${process15.argv[1]} --show {1} {2}`,
2651
+ preview: `${process17.execPath} ${process17.argv[1]} --show {1} {2}`,
2539
2652
  reload: refreshed ? { onOpen: true, run: async () => ({ rows: rows(await entries2(true)) }) } : undefined
2540
2653
  });
2541
2654
  const entry = picked && (await entries2(false)).find((candidate) => candidate.app.dir === picked.hidden[0] && candidate.command.name === picked.hidden[1]);
@@ -2543,4 +2656,4 @@ async function sf(args) {
2543
2656
  }
2544
2657
 
2545
2658
  // src/sf-cli.ts
2546
- process16.exitCode = await runMain({ name: "sf", run: sf });
2659
+ process18.exitCode = await runMain({ name: "sf", run: sf });