beecork 2.4.2 → 2.5.1

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.
Files changed (2) hide show
  1. package/dist/index.js +751 -277
  2. package/package.json +2 -1
package/dist/index.js CHANGED
@@ -135,9 +135,10 @@ var config = {
135
135
  };
136
136
 
137
137
  // src/update.ts
138
- import { readFile, writeFile, mkdir } from "node:fs/promises";
138
+ import { readFile, writeFile, mkdir, realpath } from "node:fs/promises";
139
139
  import { homedir as homedir2 } from "node:os";
140
- import { join, dirname } from "node:path";
140
+ import { join, dirname, basename, delimiter } from "node:path";
141
+ import { fileURLToPath } from "node:url";
141
142
  import { spawn } from "node:child_process";
142
143
  var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
143
144
  var cacheFile = () => join(homedir2(), ".beecork", "update-check.json");
@@ -149,6 +150,48 @@ async function currentVersion() {
149
150
  return "0.0.0";
150
151
  }
151
152
  }
153
+ function runningPkgRoot() {
154
+ return fileURLToPath(new URL("..", import.meta.url));
155
+ }
156
+ function prefixFromPkgRoot(pkgRoot) {
157
+ const nm = dirname(pkgRoot);
158
+ if (basename(nm) !== "node_modules") return null;
159
+ const up = dirname(nm);
160
+ return basename(up) === "lib" ? dirname(up) : up;
161
+ }
162
+ function installPrefix() {
163
+ return prefixFromPkgRoot(runningPkgRoot());
164
+ }
165
+ async function findShadowingInstalls() {
166
+ let running;
167
+ try {
168
+ running = await realpath(runningPkgRoot());
169
+ } catch {
170
+ running = runningPkgRoot();
171
+ }
172
+ const names = process.platform === "win32" ? ["beecork.cmd", "beecork"] : ["beecork"];
173
+ const seenRoots = /* @__PURE__ */ new Set([running]);
174
+ const others = [];
175
+ for (const dir of (process.env.PATH ?? "").split(delimiter).filter(Boolean)) {
176
+ for (const name of names) {
177
+ try {
178
+ const bin = join(dir, name);
179
+ const root = dirname(dirname(await realpath(bin)));
180
+ if (seenRoots.has(root)) continue;
181
+ seenRoots.add(root);
182
+ const pkg = JSON.parse(await readFile(join(root, "package.json"), "utf8"));
183
+ if (String(pkg.name) === "beecork") others.push({ bin, version: String(pkg.version ?? "?").replace(/[^\w.+-]/g, "") });
184
+ } catch {
185
+ }
186
+ }
187
+ }
188
+ return others;
189
+ }
190
+ async function shadowWarning() {
191
+ const others = await findShadowingInstalls();
192
+ if (!others.length) return null;
193
+ return "\u26A0 Another beecork is on your PATH and may shadow the version you just installed:\n" + others.map((o) => ` ${o.bin} (v${o.version})`).join("\n") + "\n Remove the stale one(s) so `beecork` always runs the newest install (may need sudo):\n" + others.map((o) => ` rm ${o.bin}`).join("\n");
194
+ }
152
195
  function isNewer(a, b) {
153
196
  const pa = a.split(".").map((n) => parseInt(n, 10) || 0);
154
197
  const pb = b.split(".").map((n) => parseInt(n, 10) || 0);
@@ -195,8 +238,10 @@ async function checkForUpdate(current) {
195
238
  function selfUpdate(timeoutMs = 12e4) {
196
239
  return new Promise((resolve2) => {
197
240
  const npm = process.platform === "win32" ? "npm.cmd" : "npm";
198
- const child = spawn(npm, ["install", "-g", "beecork@latest"], { stdio: ["ignore", "pipe", "pipe"] });
199
- let out2 = "", done = false;
241
+ const prefix = installPrefix();
242
+ const args = ["install", "-g", "beecork@latest", ...prefix ? ["--prefix", prefix] : []];
243
+ const child = spawn(npm, args, { stdio: ["ignore", "pipe", "pipe"] });
244
+ let out3 = "", done = false;
200
245
  const finish = (r) => {
201
246
  if (done) return;
202
247
  done = true;
@@ -208,13 +253,13 @@ function selfUpdate(timeoutMs = 12e4) {
208
253
  child.kill("SIGKILL");
209
254
  } catch {
210
255
  }
211
- finish({ ok: false, output: `${out2.trim()}
256
+ finish({ ok: false, output: `${out3.trim()}
212
257
  (update timed out after ${timeoutMs}ms \u2014 run manually: npm install -g beecork@latest)`.trim() });
213
258
  }, timeoutMs);
214
- child.stdout?.on("data", (d) => out2 += d);
215
- child.stderr?.on("data", (d) => out2 += d);
259
+ child.stdout?.on("data", (d) => out3 += d);
260
+ child.stderr?.on("data", (d) => out3 += d);
216
261
  child.on("error", (e) => finish({ ok: false, output: e.message }));
217
- child.on("close", (code) => finish({ ok: code === 0, output: out2.trim() }));
262
+ child.on("close", (code) => finish({ ok: code === 0, output: out3.trim() }));
218
263
  });
219
264
  }
220
265
 
@@ -256,27 +301,65 @@ function lineDiff(oldText, newText) {
256
301
  lcs[i2][j2] = a[i2] === b[j2] ? lcs[i2 + 1][j2 + 1] + 1 : Math.max(lcs[i2 + 1][j2], lcs[i2][j2 + 1]);
257
302
  }
258
303
  }
259
- const out2 = [];
304
+ const out3 = [];
260
305
  let i = 0;
261
306
  let j = 0;
262
307
  while (i < m && j < n) {
263
308
  if (a[i] === b[j]) {
264
- out2.push(" " + a[i]);
309
+ out3.push(" " + a[i]);
265
310
  i++;
266
311
  j++;
267
312
  } else if (lcs[i + 1][j] >= lcs[i][j + 1]) {
268
- out2.push("- " + a[i]);
313
+ out3.push("- " + a[i]);
269
314
  i++;
270
315
  } else {
271
- out2.push("+ " + b[j]);
316
+ out3.push("+ " + b[j]);
272
317
  j++;
273
318
  }
274
319
  }
275
- while (i < m) out2.push("- " + a[i++]);
276
- while (j < n) out2.push("+ " + b[j++]);
277
- return out2.join("\n");
320
+ while (i < m) out3.push("- " + a[i++]);
321
+ while (j < n) out3.push("+ " + b[j++]);
322
+ return out3.join("\n");
278
323
  }
279
324
 
325
+ // src/ansi.ts
326
+ var ESC = "\x1B";
327
+ var CSI = "\x1B[";
328
+ var ansi = {
329
+ // --- cursor visibility ---
330
+ hideCursor: CSI + "?25l",
331
+ showCursor: CSI + "?25h",
332
+ // --- save / restore cursor (position + attributes) ---
333
+ saveCursor: ESC + "7",
334
+ restoreCursor: ESC + "8",
335
+ // --- clearing ---
336
+ clearLine: CSI + "2K",
337
+ // entire current line (cursor row unchanged)
338
+ clearToEnd: CSI + "J",
339
+ // cursor → end of screen
340
+ clearScreen: CSI + "2J",
341
+ // whole viewport
342
+ clearScrollback: CSI + "3J",
343
+ // scrollback buffer (xterm extension)
344
+ cr: "\r",
345
+ // carriage return (column 1, same row)
346
+ home: CSI + "H",
347
+ // row 1, col 1
348
+ // --- motion (n omitted → terminal default of 1) ---
349
+ up: (n = 1) => CSI + n + "A",
350
+ forward: (n = 1) => CSI + n + "C",
351
+ moveTo: (row, col = 1) => CSI + row + ";" + col + "H",
352
+ // --- scroll region (DECSTBM); 1-based, inclusive. reset → full screen ---
353
+ setRegion: (top, bottom) => CSI + top + ";" + bottom + "r",
354
+ resetRegion: CSI + "r",
355
+ // --- bracketed paste mode ---
356
+ bracketedPasteOn: CSI + "?2004h",
357
+ bracketedPasteOff: CSI + "?2004l",
358
+ // --- reverse video (used to draw the pinned input's block cursor) ---
359
+ reverse: CSI + "7m",
360
+ reverseOff: CSI + "27m"
361
+ };
362
+
280
363
  // src/ui.ts
281
364
  var useColor = process.env.NO_COLOR ? false : process.env.FORCE_COLOR ? true : Boolean(process.stdout.isTTY);
282
365
  var paint = (code) => (s) => useColor ? `\x1B[${code}m${s}\x1B[0m` : s;
@@ -294,12 +377,12 @@ var color = {
294
377
  };
295
378
  var isPrintableCodePoint = (c) => c >= 32 && c !== 127 && !(c >= 128 && c <= 159);
296
379
  function stripControl(s) {
297
- let out2 = "";
380
+ let out3 = "";
298
381
  for (const ch of s) {
299
382
  const c = ch.codePointAt(0);
300
- if (c === 9 || c === 10 || isPrintableCodePoint(c)) out2 += ch;
383
+ if (c === 9 || c === 10 || isPrintableCodePoint(c)) out3 += ch;
301
384
  }
302
- return out2;
385
+ return out3;
303
386
  }
304
387
  var stripAnsi = (s) => s.replace(/\x1b\[[0-9;]*m/g, "");
305
388
  function charWidth(cp) {
@@ -418,19 +501,19 @@ function startSpinner(label) {
418
501
  if (!process.stdout.isTTY || !useColor) return () => {
419
502
  };
420
503
  let i = 0;
421
- const draw2 = () => {
504
+ const draw = () => {
422
505
  if (steeringOnScreen) return;
423
- process.stdout.write("\r " + color.brand(SPINNER[i = (i + 1) % SPINNER.length]) + " " + color.dim(label));
506
+ process.stdout.write(ansi.cr + " " + color.brand(SPINNER[i = (i + 1) % SPINNER.length]) + " " + color.dim(label));
424
507
  };
425
- process.stdout.write("\x1B[?25l");
426
- draw2();
427
- const timer = setInterval(draw2, 80);
508
+ process.stdout.write(ansi.hideCursor);
509
+ draw();
510
+ const timer = setInterval(draw, 80);
428
511
  let stopped = false;
429
512
  return () => {
430
513
  if (stopped) return;
431
514
  stopped = true;
432
515
  clearInterval(timer);
433
- process.stdout.write("\r\x1B[2K");
516
+ process.stdout.write(ansi.cr + ansi.clearLine);
434
517
  };
435
518
  }
436
519
  function markLines(width) {
@@ -456,8 +539,9 @@ function markLines(width) {
456
539
  }
457
540
  return lines.filter((l) => l.length > 0);
458
541
  }
459
- function printBanner(model, sources) {
542
+ function printBanner(model, version, sources) {
460
543
  const safeModel = stripControl(model);
544
+ const safeVersion = stripControl(version);
461
545
  const word = [
462
546
  " _ _ ",
463
547
  " | |__ ___ ___ ___ ___ _ __| | __",
@@ -465,21 +549,21 @@ function printBanner(model, sources) {
465
549
  " | |_) | __/ __/ (_| (_) | | | < ",
466
550
  " |_.__/ \\___|\\___|\\___\\___/|_| |_|\\_\\"
467
551
  ];
468
- const mark = markLines(24);
469
- const markW = Math.max(0, ...mark.map((l) => l.length));
552
+ const mark2 = markLines(24);
553
+ const markW = Math.max(0, ...mark2.map((l) => l.length));
470
554
  const wordW = Math.max(...word.map((l) => l.length));
471
- const cols = process.stdout.columns || 80;
555
+ const cols2 = process.stdout.columns || 80;
472
556
  console.log();
473
- if (cols >= markW + 3 + wordW + 2) {
474
- const h = Math.max(mark.length, word.length);
475
- const mPad = Math.floor((h - mark.length) / 2);
557
+ if (cols2 >= markW + 3 + wordW + 2) {
558
+ const h = Math.max(mark2.length, word.length);
559
+ const mPad = Math.floor((h - mark2.length) / 2);
476
560
  const wPad = Math.floor((h - word.length) / 2);
477
561
  for (let i = 0; i < h; i++) {
478
- const m = (mark[i - mPad] ?? "").padEnd(markW);
562
+ const m = (mark2[i - mPad] ?? "").padEnd(markW);
479
563
  const w = word[i - wPad] ?? "";
480
564
  console.log(" " + color.brand(`${m} ${w}`));
481
565
  }
482
- } else if (cols >= wordW + 2) {
566
+ } else if (cols2 >= wordW + 2) {
483
567
  for (const w of word) console.log(" " + color.brand(w));
484
568
  } else {
485
569
  console.log(" " + color.brand("\u{1F41D} beecork"));
@@ -489,7 +573,7 @@ function printBanner(model, sources) {
489
573
  const mem = sources.filter((s) => s.endsWith("memory.md"));
490
574
  const cwd = tildify(process.cwd());
491
575
  const rows2 = [
492
- ["", "\u{1F41D} a tiny CLI coding agent"],
576
+ ["", `\u{1F41D} a tiny CLI coding agent \xB7 v${safeVersion}`],
493
577
  ["dir", cwd],
494
578
  ["model", safeModel],
495
579
  ["cork.md", cork.length ? cork.join(", ") : "none"],
@@ -500,7 +584,7 @@ function printBanner(model, sources) {
500
584
  const plain = (l, v) => l ? l.padEnd(lw) + " " + v : v;
501
585
  const bw = Math.max(...rows2.map(([l, v]) => plain(l, v).length));
502
586
  const row = ([l, v]) => l ? color.bold(l.padEnd(lw)) + color.dim(" " + v) : color.dim(v);
503
- if (cols < bw + 6) {
587
+ if (cols2 < bw + 6) {
504
588
  for (const r of rows2) console.log(" " + row(r));
505
589
  } else {
506
590
  console.log(" " + color.dim("\u256D\u2500" + "\u2500".repeat(bw) + "\u2500\u256E"));
@@ -518,6 +602,39 @@ import { readFile as readFile4 } from "node:fs/promises";
518
602
 
519
603
  // src/input.ts
520
604
  import { emitKeypressEvents } from "node:readline";
605
+
606
+ // src/layout.ts
607
+ function rowColOf(s) {
608
+ return { row: (s.match(/\n/g) || []).length, col: s.length - (s.lastIndexOf("\n") + 1) };
609
+ }
610
+ function inputLayout(text, before, promptW, cols2) {
611
+ const c = Math.max(1, cols2);
612
+ const physRows = text.split("\n").map((l) => Math.max(1, Math.ceil((promptW + displayWidth(l)) / c)));
613
+ const totalPhys = physRows.reduce((a, b) => a + b, 0);
614
+ const curLogicalRow = (before.match(/\n/g) || []).length;
615
+ const curCol = promptW + displayWidth(before.slice(before.lastIndexOf("\n") + 1));
616
+ const physBefore = physRows.slice(0, curLogicalRow).reduce((a, b) => a + b, 0);
617
+ const curPhysRow = physBefore + Math.floor(curCol / c);
618
+ const curPhysCol = curCol % c;
619
+ return { totalPhys, curPhysRow, curPhysCol };
620
+ }
621
+ function moveVertIndex(buf2, cur2, dir) {
622
+ const lines = buf2.split("\n");
623
+ const { row, col } = rowColOf(buf2.slice(0, cur2));
624
+ const target = row + dir;
625
+ if (target < 0 || target >= lines.length) return null;
626
+ let idx = 0;
627
+ for (let i = 0; i < target; i++) idx += lines[i].length + 1;
628
+ return idx + Math.min(col, lines[target].length);
629
+ }
630
+ function windowStart(text, cur2, avail) {
631
+ let start = 0;
632
+ if (displayWidth(text.slice(0, cur2)) >= avail)
633
+ while (start < cur2 && displayWidth(text.slice(start, cur2)) >= avail) start++;
634
+ return start;
635
+ }
636
+
637
+ // src/input.ts
521
638
  var out = (s) => process.stdout.write(s);
522
639
  var active = null;
523
640
  var started = false;
@@ -525,13 +642,13 @@ function initInput() {
525
642
  if (started || !process.stdin.isTTY) return;
526
643
  started = true;
527
644
  process.stdin.setRawMode(true);
528
- out("\x1B[?2004l");
645
+ out(ansi.bracketedPasteOff);
529
646
  emitKeypressEvents(process.stdin);
530
647
  process.stdin.on("keypress", (str, key) => active?.(str, key));
531
648
  }
532
649
  function teardownInput() {
533
650
  if (started && process.stdin.isTTY) {
534
- out("\x1B[?2004h\x1B[?25h");
651
+ out(ansi.bracketedPasteOn + ansi.showCursor);
535
652
  process.stdin.setRawMode(false);
536
653
  process.stdin.pause();
537
654
  }
@@ -553,27 +670,27 @@ function readPrompt(opts) {
553
670
  ...(opts.skills ?? []).map((n) => ({ name: "/" + n, desc: "skill" }))
554
671
  ];
555
672
  return new Promise((resolve2) => {
556
- let buf = "";
557
- let cur = 0;
673
+ let buf2 = "";
674
+ let cur2 = 0;
558
675
  let hist = history.length;
559
- let sel = 0;
560
- let menuHidden = false;
561
- const BURST_IDLE_MS = 8;
562
- let burstLen = 0;
563
- let burstTimer = null;
564
- let pendingRender = false;
676
+ let sel2 = 0;
677
+ let menuHidden2 = false;
678
+ const BURST_IDLE_MS2 = 8;
679
+ let burstLen2 = 0;
680
+ let burstTimer2 = null;
681
+ let pendingRender2 = false;
565
682
  const matches = () => {
566
683
  if (opts.mask) return [];
567
- const m = buf.match(/^\/(\S*)$/);
684
+ const m = buf2.match(/^\/(\S*)$/);
568
685
  if (!m) return [];
569
686
  const pre = "/" + m[1];
570
687
  return all.filter((c) => c.name.startsWith(pre)).slice(0, MENU_MAX);
571
688
  };
572
- const menu = () => menuHidden ? [] : matches();
689
+ const menu = () => menuHidden2 ? [] : matches();
573
690
  const highlight = () => {
574
- if (opts.mask) return "*".repeat(buf.length);
575
- const m = buf.match(/^(\/\S*)([\s\S]*)$/);
576
- if (!m) return buf;
691
+ if (opts.mask) return "*".repeat(buf2.length);
692
+ const m = buf2.match(/^(\/\S*)([\s\S]*)$/);
693
+ if (!m) return buf2;
577
694
  const [, token, rest] = m;
578
695
  const known = all.some((c) => c.name === token);
579
696
  const partial = all.some((c) => c.name.startsWith(token));
@@ -581,7 +698,6 @@ function readPrompt(opts) {
581
698
  return styled + rest;
582
699
  };
583
700
  let lastCurRow = 0;
584
- const rowColOf = (s) => ({ row: (s.match(/\n/g) || []).length, col: s.length - (s.lastIndexOf("\n") + 1) });
585
701
  function drawBlock() {
586
702
  const prompt = opts.promptString();
587
703
  const promptW = stripAnsi(prompt).length;
@@ -591,199 +707,189 @@ function readPrompt(opts) {
591
707
  for (let i = 1; i < lines.length; i++) out("\n" + indent + lines[i]);
592
708
  return { promptW };
593
709
  }
594
- function render() {
710
+ function render2() {
595
711
  const mm = menu();
596
- if (sel >= mm.length) sel = Math.max(0, mm.length - 1);
597
- out("\x1B[?25l");
598
- if (lastCurRow > 0) out(`\x1B[${lastCurRow}A`);
599
- out("\r\x1B[J");
712
+ if (sel2 >= mm.length) sel2 = Math.max(0, mm.length - 1);
713
+ out(ansi.hideCursor);
714
+ if (lastCurRow > 0) out(ansi.up(lastCurRow));
715
+ out(ansi.cr + ansi.clearToEnd);
600
716
  const { promptW } = drawBlock();
601
- const cols = Math.max(1, process.stdout.columns || 80);
717
+ const cols2 = Math.max(1, process.stdout.columns || 80);
602
718
  for (let i = 0; i < mm.length; i++) {
603
719
  const m = mm[i];
604
720
  const name = m.name.padEnd(10);
605
- const maxDesc = Math.max(0, cols - name.length - 4);
721
+ const maxDesc = Math.max(0, cols2 - name.length - 4);
606
722
  const desc = m.desc.length > maxDesc ? m.desc.slice(0, Math.max(0, maxDesc - 1)) + "\u2026" : m.desc;
607
- out("\n" + (i === sel ? color.green("\u203A " + name) + " " + color.dim(desc) : color.dim(" " + name + " " + desc)));
608
- }
609
- const text = opts.mask ? "*".repeat(buf.length) : buf;
610
- const physRows = text.split("\n").map((l) => Math.max(1, Math.ceil((promptW + displayWidth(l)) / cols)));
611
- const totalInputPhys = physRows.reduce((a, b) => a + b, 0);
612
- const before = opts.mask ? text : buf.slice(0, cur);
613
- const curLogicalRow = (before.match(/\n/g) || []).length;
614
- const curCol = promptW + displayWidth(before.slice(before.lastIndexOf("\n") + 1));
615
- const physBefore = physRows.slice(0, curLogicalRow).reduce((a, b) => a + b, 0);
616
- const curPhysRow = physBefore + Math.floor(curCol / cols);
617
- const curPhysCol = curCol % cols;
618
- const lastDrawnRow = totalInputPhys - 1 + mm.length;
619
- if (lastDrawnRow > curPhysRow) out(`\x1B[${lastDrawnRow - curPhysRow}A`);
620
- out("\r" + (curPhysCol > 0 ? `\x1B[${curPhysCol}C` : "") + "\x1B[?25h");
723
+ out("\n" + (i === sel2 ? color.green("\u203A " + name) + " " + color.dim(desc) : color.dim(" " + name + " " + desc)));
724
+ }
725
+ const text = opts.mask ? "*".repeat(buf2.length) : buf2;
726
+ const before = opts.mask ? text : buf2.slice(0, cur2);
727
+ const { totalPhys, curPhysRow, curPhysCol } = inputLayout(text, before, promptW, cols2);
728
+ const lastDrawnRow = totalPhys - 1 + mm.length;
729
+ if (lastDrawnRow > curPhysRow) out(ansi.up(lastDrawnRow - curPhysRow));
730
+ out(ansi.cr + (curPhysCol > 0 ? ansi.forward(curPhysCol) : "") + ansi.showCursor);
621
731
  lastCurRow = curPhysRow;
622
732
  }
623
733
  function finish(result) {
624
734
  restore();
625
- if (burstTimer) {
626
- clearTimeout(burstTimer);
627
- burstTimer = null;
628
- }
629
- pendingRender = false;
630
- out("\x1B[?25l");
631
- if (lastCurRow > 0) out(`\x1B[${lastCurRow}A`);
632
- out("\r\x1B[J");
735
+ if (burstTimer2) {
736
+ clearTimeout(burstTimer2);
737
+ burstTimer2 = null;
738
+ }
739
+ pendingRender2 = false;
740
+ out(ansi.hideCursor);
741
+ if (lastCurRow > 0) out(ansi.up(lastCurRow));
742
+ out(ansi.cr + ansi.clearToEnd);
633
743
  drawBlock();
634
744
  out("\n");
635
745
  if (result.type === "line" && result.value.trim() && !opts.mask) history.push(result.value);
636
746
  resolve2(result);
637
747
  }
638
- function insert(s) {
639
- buf = buf.slice(0, cur) + s + buf.slice(cur);
640
- cur += s.length;
641
- menuHidden = false;
642
- sel = 0;
643
- pendingRender = true;
748
+ function insert2(s) {
749
+ buf2 = buf2.slice(0, cur2) + s + buf2.slice(cur2);
750
+ cur2 += s.length;
751
+ menuHidden2 = false;
752
+ sel2 = 0;
753
+ pendingRender2 = true;
644
754
  }
645
- function onKey(str, key) {
755
+ function onKey2(str, key) {
646
756
  const mm = menu();
647
- burstLen++;
648
- if (burstTimer) clearTimeout(burstTimer);
649
- burstTimer = setTimeout(() => {
650
- burstTimer = null;
651
- burstLen = 0;
652
- if (pendingRender) {
653
- pendingRender = false;
654
- render();
757
+ burstLen2++;
758
+ if (burstTimer2) clearTimeout(burstTimer2);
759
+ burstTimer2 = setTimeout(() => {
760
+ burstTimer2 = null;
761
+ burstLen2 = 0;
762
+ if (pendingRender2) {
763
+ pendingRender2 = false;
764
+ render2();
655
765
  }
656
- }, BURST_IDLE_MS);
766
+ }, BURST_IDLE_MS2);
657
767
  if (isEnter(key)) {
658
- if (key?.shift || key?.meta || burstLen > 1) {
659
- insert("\n");
768
+ if (key?.shift || key?.meta || burstLen2 > 1) {
769
+ insert2("\n");
660
770
  return;
661
771
  }
662
- return finish({ type: "line", value: buf });
772
+ return finish({ type: "line", value: buf2 });
663
773
  }
664
774
  if (key?.ctrl && key.name === "c") {
665
- if (buf) {
666
- buf = "";
667
- cur = 0;
668
- render();
775
+ if (buf2) {
776
+ buf2 = "";
777
+ cur2 = 0;
778
+ render2();
669
779
  } else finish({ type: "quit" });
670
780
  return;
671
781
  }
672
782
  if (key?.ctrl && key.name === "d") {
673
- if (!buf) finish({ type: "eof" });
783
+ if (!buf2) finish({ type: "eof" });
674
784
  return;
675
785
  }
676
786
  if (key?.name === "tab" && key.shift) {
677
787
  opts.onShiftTab?.();
678
- render();
788
+ render2();
679
789
  return;
680
790
  }
681
791
  if (key?.name === "tab") {
682
792
  if (mm.length) {
683
- buf = mm[sel].name + " ";
684
- cur = buf.length;
685
- menuHidden = false;
686
- render();
793
+ buf2 = mm[sel2].name + " ";
794
+ cur2 = buf2.length;
795
+ menuHidden2 = false;
796
+ render2();
687
797
  }
688
798
  return;
689
799
  }
690
800
  if (key?.name === "up") {
691
801
  if (mm.length) {
692
- sel = (sel - 1 + mm.length) % mm.length;
693
- render();
694
- } else if (buf.includes("\n")) moveVert(-1);
802
+ sel2 = (sel2 - 1 + mm.length) % mm.length;
803
+ render2();
804
+ } else if (buf2.includes("\n")) moveVert(-1);
695
805
  else histPrev();
696
806
  return;
697
807
  }
698
808
  if (key?.name === "down") {
699
809
  if (mm.length) {
700
- sel = (sel + 1) % mm.length;
701
- render();
702
- } else if (buf.includes("\n")) moveVert(1);
810
+ sel2 = (sel2 + 1) % mm.length;
811
+ render2();
812
+ } else if (buf2.includes("\n")) moveVert(1);
703
813
  else histNext();
704
814
  return;
705
815
  }
706
816
  if (key?.name === "left") {
707
- if (cur > 0) cur--;
708
- render();
817
+ if (cur2 > 0) cur2--;
818
+ render2();
709
819
  return;
710
820
  }
711
821
  if (key?.name === "right") {
712
- if (cur < buf.length) cur++;
713
- render();
822
+ if (cur2 < buf2.length) cur2++;
823
+ render2();
714
824
  return;
715
825
  }
716
826
  if (key?.name === "home" || key?.ctrl && key.name === "a") {
717
- cur = 0;
718
- render();
827
+ cur2 = 0;
828
+ render2();
719
829
  return;
720
830
  }
721
831
  if (key?.name === "end" || key?.ctrl && key.name === "e") {
722
- cur = buf.length;
723
- render();
832
+ cur2 = buf2.length;
833
+ render2();
724
834
  return;
725
835
  }
726
836
  if (key?.ctrl && key.name === "u") {
727
- buf = buf.slice(cur);
728
- cur = 0;
729
- menuHidden = false;
730
- render();
837
+ buf2 = buf2.slice(cur2);
838
+ cur2 = 0;
839
+ menuHidden2 = false;
840
+ render2();
731
841
  return;
732
842
  }
733
843
  if (key?.name === "backspace") {
734
- if (cur > 0) {
735
- buf = buf.slice(0, cur - 1) + buf.slice(cur);
736
- cur--;
737
- menuHidden = false;
738
- render();
844
+ if (cur2 > 0) {
845
+ buf2 = buf2.slice(0, cur2 - 1) + buf2.slice(cur2);
846
+ cur2--;
847
+ menuHidden2 = false;
848
+ render2();
739
849
  }
740
850
  return;
741
851
  }
742
852
  if (key?.name === "delete") {
743
- if (cur < buf.length) {
744
- buf = buf.slice(0, cur) + buf.slice(cur + 1);
745
- menuHidden = false;
746
- render();
853
+ if (cur2 < buf2.length) {
854
+ buf2 = buf2.slice(0, cur2) + buf2.slice(cur2 + 1);
855
+ menuHidden2 = false;
856
+ render2();
747
857
  }
748
858
  return;
749
859
  }
750
860
  if (key?.name === "escape") {
751
861
  if (mm.length) {
752
- menuHidden = true;
753
- render();
862
+ menuHidden2 = true;
863
+ render2();
754
864
  }
755
865
  return;
756
866
  }
757
867
  if (str && !key?.ctrl && !key?.meta && [...str].length === 1) {
758
- if (isPrintableCodePoint(str.codePointAt(0))) insert(str);
868
+ if (isPrintableCodePoint(str.codePointAt(0))) insert2(str);
759
869
  }
760
870
  }
761
871
  function moveVert(dir) {
762
- const lines = buf.split("\n");
763
- const { row, col } = rowColOf(buf.slice(0, cur));
764
- const target = row + dir;
765
- if (target < 0 || target >= lines.length) return;
766
- let idx = 0;
767
- for (let i = 0; i < target; i++) idx += lines[i].length + 1;
768
- cur = idx + Math.min(col, lines[target].length);
769
- render();
872
+ const idx = moveVertIndex(buf2, cur2, dir);
873
+ if (idx === null) return;
874
+ cur2 = idx;
875
+ render2();
770
876
  }
771
877
  function histPrev() {
772
878
  if (history.length === 0) return;
773
879
  hist = Math.max(0, hist - 1);
774
- buf = history[hist] ?? "";
775
- cur = buf.length;
776
- render();
880
+ buf2 = history[hist] ?? "";
881
+ cur2 = buf2.length;
882
+ render2();
777
883
  }
778
884
  function histNext() {
779
885
  if (hist >= history.length) return;
780
886
  hist += 1;
781
- buf = hist === history.length ? "" : history[hist];
782
- cur = buf.length;
783
- render();
887
+ buf2 = hist === history.length ? "" : history[hist];
888
+ cur2 = buf2.length;
889
+ render2();
784
890
  }
785
- const restore = pushKeyHandler(onKey);
786
- render();
891
+ const restore = pushKeyHandler(onKey2);
892
+ render2();
787
893
  });
788
894
  }
789
895
  function readChoice(prompt) {
@@ -804,36 +910,36 @@ function readChoice(prompt) {
804
910
  function selectMenu(opts) {
805
911
  if (!process.stdin.isTTY || opts.items.length === 0) return Promise.resolve(null);
806
912
  return new Promise((resolve2) => {
807
- let sel = Math.max(0, Math.min(opts.initial ?? 0, opts.items.length - 1));
913
+ let sel2 = Math.max(0, Math.min(opts.initial ?? 0, opts.items.length - 1));
808
914
  let drawn = 0;
809
- function render() {
810
- out("\x1B[?25l");
811
- if (drawn > 0) out(`\x1B[${drawn}A`);
812
- out("\r\x1B[J");
915
+ function render2() {
916
+ out(ansi.hideCursor);
917
+ if (drawn > 0) out(ansi.up(drawn));
918
+ out(ansi.cr + ansi.clearToEnd);
813
919
  out(color.dim(opts.title) + "\n");
814
920
  opts.items.forEach((it, i) => {
815
- const row = i === sel ? color.green("\u203A " + it.label) : " " + it.label;
921
+ const row = i === sel2 ? color.green("\u203A " + it.label) : " " + it.label;
816
922
  out(row + (it.hint ? color.dim(" " + it.hint) : "") + "\n");
817
923
  });
818
924
  drawn = opts.items.length + 1;
819
925
  }
820
926
  function finish(v) {
821
927
  restore();
822
- if (drawn > 0) out(`\x1B[${drawn}A\r\x1B[J`);
823
- out("\x1B[?25h");
928
+ if (drawn > 0) out(ansi.up(drawn) + ansi.cr + ansi.clearToEnd);
929
+ out(ansi.showCursor);
824
930
  resolve2(v);
825
931
  }
826
932
  const restore = pushKeyHandler((_str, key) => {
827
933
  if (key?.name === "up") {
828
- sel = (sel - 1 + opts.items.length) % opts.items.length;
829
- render();
934
+ sel2 = (sel2 - 1 + opts.items.length) % opts.items.length;
935
+ render2();
830
936
  } else if (key?.name === "down") {
831
- sel = (sel + 1) % opts.items.length;
832
- render();
833
- } else if (isEnter(key)) finish(opts.items[sel].value);
937
+ sel2 = (sel2 + 1) % opts.items.length;
938
+ render2();
939
+ } else if (isEnter(key)) finish(opts.items[sel2].value);
834
940
  else if (key?.name === "escape" || key?.name === "q" || key?.ctrl && key.name === "c") finish(null);
835
941
  });
836
- render();
942
+ render2();
837
943
  });
838
944
  }
839
945
 
@@ -843,46 +949,46 @@ function renderFileBox(path, startLine, lines, hasMore) {
843
949
  const p = stripControl(path);
844
950
  const numW = String(startLine + Math.max(0, lines.length - 1)).length;
845
951
  const header = startLine === 1 && !hasMore ? `${lines.length} line${lines.length === 1 ? "" : "s"}` : `lines ${startLine}\u2013${startLine + lines.length - 1}${hasMore ? " (+ more)" : ""}`;
846
- const out2 = [color.dim("\u256D\u2500 ") + color.bold(p) + color.dim(` ${header}`)];
847
- lines.forEach((l, i) => out2.push(color.dim("\u2502 ") + color.dim(String(startLine + i).padStart(numW)) + " " + stripControl(l)));
848
- if (hasMore) out2.push(color.dim("\u2502 ") + color.dim(`\u2026 more (show ${p} offset ${startLine + lines.length})`));
849
- out2.push(color.dim("\u2570\u2500"));
850
- return out2.join("\n") + "\n";
952
+ const out3 = [color.dim("\u256D\u2500 ") + color.bold(p) + color.dim(` ${header}`)];
953
+ lines.forEach((l, i) => out3.push(color.dim("\u2502 ") + color.dim(String(startLine + i).padStart(numW)) + " " + stripControl(l)));
954
+ if (hasMore) out3.push(color.dim("\u2502 ") + color.dim(`\u2026 more (show ${p} offset ${startLine + lines.length})`));
955
+ out3.push(color.dim("\u2570\u2500"));
956
+ return out3.join("\n") + "\n";
851
957
  }
852
958
  function renderListing(path, names) {
853
959
  const safe = names.map(stripControl);
854
- const out2 = [color.dim("\u256D\u2500 ") + color.bold(stripControl(path)) + color.dim(` ${safe.length} entr${safe.length === 1 ? "y" : "ies"}`)];
960
+ const out3 = [color.dim("\u256D\u2500 ") + color.bold(stripControl(path)) + color.dim(` ${safe.length} entr${safe.length === 1 ? "y" : "ies"}`)];
855
961
  if (safe.length === 0) {
856
- out2.push(color.dim("\u2502 ") + color.dim("(empty)"));
962
+ out3.push(color.dim("\u2502 ") + color.dim("(empty)"));
857
963
  } else {
858
964
  const avail = BOX_W() - 2;
859
965
  const colW = Math.min(Math.max(...safe.map((n) => n.length)) + 2, 40);
860
- const cols = Math.max(1, Math.floor(avail / colW));
861
- const rows2 = Math.ceil(safe.length / cols);
966
+ const cols2 = Math.max(1, Math.floor(avail / colW));
967
+ const rows2 = Math.ceil(safe.length / cols2);
862
968
  for (let r = 0; r < rows2; r++) {
863
969
  let line = "";
864
- for (let c = 0; c < cols; c++) {
970
+ for (let c = 0; c < cols2; c++) {
865
971
  const idx = c * rows2 + r;
866
972
  if (idx >= safe.length) continue;
867
973
  const n = safe[idx];
868
974
  line += (n.endsWith("/") ? color.cyan(n) : n) + " ".repeat(Math.max(1, colW - n.length));
869
975
  }
870
- out2.push(color.dim("\u2502 ") + line.replace(/\s+$/, ""));
976
+ out3.push(color.dim("\u2502 ") + line.replace(/\s+$/, ""));
871
977
  }
872
978
  }
873
- out2.push(color.dim("\u2570\u2500"));
874
- return out2.join("\n") + "\n";
979
+ out3.push(color.dim("\u2570\u2500"));
980
+ return out3.join("\n") + "\n";
875
981
  }
876
982
  function renderTree(path, items, truncated) {
877
983
  const dirs = items.filter((it) => it.isDir).length;
878
- const out2 = [color.dim("\u256D\u2500 ") + color.bold(stripControl(path)) + color.dim(` ${items.length} entries \xB7 ${dirs} folders`)];
984
+ const out3 = [color.dim("\u256D\u2500 ") + color.bold(stripControl(path)) + color.dim(` ${items.length} entries \xB7 ${dirs} folders`)];
879
985
  for (const it of items) {
880
986
  const nm = stripControl(it.name);
881
- out2.push(color.dim("\u2502 ") + color.dim(it.prefix) + (it.isDir ? color.cyan(nm) : nm));
987
+ out3.push(color.dim("\u2502 ") + color.dim(it.prefix) + (it.isDir ? color.cyan(nm) : nm));
882
988
  }
883
- if (truncated) out2.push(color.dim("\u2502 ") + color.dim("\u2026 (truncated)"));
884
- out2.push(color.dim("\u2570\u2500"));
885
- return out2.join("\n") + "\n";
989
+ if (truncated) out3.push(color.dim("\u2502 ") + color.dim("\u2026 (truncated)"));
990
+ out3.push(color.dim("\u2570\u2500"));
991
+ return out3.join("\n") + "\n";
886
992
  }
887
993
  var SHOW_MARK = "";
888
994
  var SHOW_KINDS = ["file", "dir", "tree"];
@@ -991,14 +1097,14 @@ function renderTable(rows2) {
991
1097
  })
992
1098
  );
993
1099
  const width = Array.from({ length: ncol }, (_, c) => Math.max(...styled.map((r) => visLen(r[c]))));
994
- const out2 = styled.map((r) => {
1100
+ const out3 = styled.map((r) => {
995
1101
  const cells = r.map((cell, c) => cell + " ".repeat(Math.max(0, width[c] - visLen(cell))));
996
1102
  return " " + cells.join(color.dim(" \u2502 "));
997
1103
  });
998
- return out2.join("\n") + "\n";
1104
+ return out3.join("\n") + "\n";
999
1105
  }
1000
1106
  function createMarkdownStream(write) {
1001
- let buf = "";
1107
+ let buf2 = "";
1002
1108
  let scanFrom = 0;
1003
1109
  let inCode = false;
1004
1110
  let first = true;
@@ -1037,19 +1143,19 @@ function createMarkdownStream(write) {
1037
1143
  }
1038
1144
  return {
1039
1145
  push(text) {
1040
- buf += text;
1146
+ buf2 += text;
1041
1147
  let i;
1042
- while ((i = buf.indexOf("\n", scanFrom)) >= 0) {
1043
- emit(buf.slice(0, i));
1044
- buf = buf.slice(i + 1);
1148
+ while ((i = buf2.indexOf("\n", scanFrom)) >= 0) {
1149
+ emit(buf2.slice(0, i));
1150
+ buf2 = buf2.slice(i + 1);
1045
1151
  scanFrom = 0;
1046
1152
  }
1047
- scanFrom = buf.length;
1153
+ scanFrom = buf2.length;
1048
1154
  },
1049
1155
  end() {
1050
- if (buf) {
1051
- emit(buf);
1052
- buf = "";
1156
+ if (buf2) {
1157
+ emit(buf2);
1158
+ buf2 = "";
1053
1159
  }
1054
1160
  flushTable();
1055
1161
  if (inCode) {
@@ -1247,7 +1353,7 @@ async function runExplorer(task, focus, signal) {
1247
1353
 
1248
1354
  // src/safety.ts
1249
1355
  import { homedir as homedir3 } from "node:os";
1250
- import { basename } from "node:path";
1356
+ import { basename as basename2 } from "node:path";
1251
1357
  function pathGuard(args) {
1252
1358
  const { abs, inRoot } = resolveInRoot(String(args.path ?? "."));
1253
1359
  return inRoot ? {} : { needsApproval: true, reason: `path is outside the project root: ${abs}`, cacheKey: `path:${abs}` };
@@ -1255,7 +1361,7 @@ function pathGuard(args) {
1255
1361
  var SECRET_FILE = /(^|\/)(\.env(\.[\w.-]+)?|[\w.-]*\.(env|pem|key|secret|pfx|p12|jks|keystore)|id_(rsa|ed25519|ecdsa|dsa)|credentials|\.git-credentials|\.pgpass|\.npmrc|\.netrc)$/i;
1256
1362
  function isSecretPath(userPath) {
1257
1363
  const { abs } = resolveInRoot(userPath);
1258
- return SECRET_FILE.test(abs) || SECRET_FILE.test(basename(abs));
1364
+ return SECRET_FILE.test(abs) || SECRET_FILE.test(basename2(abs));
1259
1365
  }
1260
1366
  function secretGuard(args) {
1261
1367
  const p = pathGuard(args);
@@ -1471,13 +1577,13 @@ async function atomicWrite(abs, content) {
1471
1577
  }
1472
1578
  var READ_PREFIX = /^ *\d+ {2}/;
1473
1579
  function allIndexOf(hay, needle) {
1474
- const out2 = [];
1580
+ const out3 = [];
1475
1581
  let i = hay.indexOf(needle);
1476
1582
  while (i !== -1) {
1477
- out2.push(i);
1583
+ out3.push(i);
1478
1584
  i = hay.indexOf(needle, i + 1);
1479
1585
  }
1480
- return out2;
1586
+ return out3;
1481
1587
  }
1482
1588
  function stripReadPrefix(text) {
1483
1589
  const lines = text.split("\n");
@@ -1972,11 +2078,11 @@ ${res.closest}` : `Re-read the file and copy the exact text (including whitespac
1972
2078
  ${stderr}` : "") || "(no output)";
1973
2079
  } catch (err) {
1974
2080
  const e = err;
1975
- const out2 = `${e.stdout ?? ""}${e.stderr ? `
2081
+ const out3 = `${e.stdout ?? ""}${e.stderr ? `
1976
2082
  [stderr]
1977
2083
  ${e.stderr}` : ""}`.trim();
1978
- return `Error: command failed${out2 ? `:
1979
- ${out2}` : `: ${String(e.message ?? err)}`}`;
2084
+ return `Error: command failed${out3 ? `:
2085
+ ${out3}` : `: ${String(e.message ?? err)}`}`;
1980
2086
  }
1981
2087
  }
1982
2088
  },
@@ -2221,14 +2327,14 @@ function validateArgs(tool, args) {
2221
2327
  async function runVerify(signal) {
2222
2328
  try {
2223
2329
  const { stdout, stderr } = await runShell(config.verifyCommand, { timeout: config.verifyTimeoutMs, maxBuffer: config.maxToolBuffer, signal });
2224
- const out2 = `${stdout}${stderr}`.trim();
2225
- return `passed \u2713${out2 ? `
2226
- ${out2.slice(-800)}` : ""}`;
2330
+ const out3 = `${stdout}${stderr}`.trim();
2331
+ return `passed \u2713${out3 ? `
2332
+ ${out3.slice(-800)}` : ""}`;
2227
2333
  } catch (err) {
2228
2334
  const e = err;
2229
- const out2 = `${e.stdout ?? ""}${e.stderr ?? ""}`.trim() || String(e.message ?? err);
2335
+ const out3 = `${e.stdout ?? ""}${e.stderr ?? ""}`.trim() || String(e.message ?? err);
2230
2336
  return `FAILED \u2717
2231
- ${out2.slice(-1500)}`;
2337
+ ${out3.slice(-1500)}`;
2232
2338
  }
2233
2339
  }
2234
2340
 
@@ -2261,12 +2367,12 @@ function parseSSELine(line) {
2261
2367
  }
2262
2368
  const delta = parsed.choices?.[0]?.delta;
2263
2369
  if (!delta) return null;
2264
- const out2 = {};
2265
- if (delta.content) out2.content = delta.content;
2266
- if (delta.tool_calls) out2.toolCalls = delta.tool_calls;
2267
- if (typeof delta.reasoning === "string" && delta.reasoning) out2.reasoning = delta.reasoning;
2268
- if (Array.isArray(delta.reasoning_details) && delta.reasoning_details.length) out2.reasoningDetails = delta.reasoning_details;
2269
- return out2;
2370
+ const out3 = {};
2371
+ if (delta.content) out3.content = delta.content;
2372
+ if (delta.tool_calls) out3.toolCalls = delta.tool_calls;
2373
+ if (typeof delta.reasoning === "string" && delta.reasoning) out3.reasoning = delta.reasoning;
2374
+ if (Array.isArray(delta.reasoning_details) && delta.reasoning_details.length) out3.reasoningDetails = delta.reasoning_details;
2375
+ return out3;
2270
2376
  }
2271
2377
  function buildRequestBody(opts) {
2272
2378
  const { model, messages, includeTools, effort, reasoningSupported, extra, tools } = opts;
@@ -2672,7 +2778,7 @@ async function saveSession(messages) {
2672
2778
  }
2673
2779
  function sanitizeSession(raw) {
2674
2780
  if (!Array.isArray(raw)) return null;
2675
- const out2 = [];
2781
+ const out3 = [];
2676
2782
  for (const m of raw) {
2677
2783
  if (!m || typeof m !== "object") return null;
2678
2784
  const role = m.role;
@@ -2685,9 +2791,9 @@ function sanitizeSession(raw) {
2685
2791
  if (Array.isArray(tc)) msg.tool_calls = tc;
2686
2792
  const tcid = m.tool_call_id;
2687
2793
  if (typeof tcid === "string") msg.tool_call_id = tcid;
2688
- out2.push(msg);
2794
+ out3.push(msg);
2689
2795
  }
2690
- return dropIncompleteToolTail(out2);
2796
+ return dropIncompleteToolTail(out3);
2691
2797
  }
2692
2798
  function dropIncompleteToolTail(messages) {
2693
2799
  for (let i = messages.length - 1; i >= 0; i--) {
@@ -2730,15 +2836,15 @@ async function loadLatestSession() {
2730
2836
  async function listSessions() {
2731
2837
  try {
2732
2838
  const files = (await readdir2(sessionsDir())).filter((f) => f.endsWith(".json"));
2733
- const out2 = [];
2839
+ const out3 = [];
2734
2840
  for (const f of files) {
2735
2841
  const msgs = await readSession(f);
2736
2842
  if (!msgs || !msgs.length) continue;
2737
2843
  const firstUser = msgs.find((m) => m.role === "user");
2738
2844
  const preview = (firstUser?.content ?? "").replace(/\s+/g, " ").trim().slice(0, 60);
2739
- out2.push({ file: f, when: Number(f.replace(".json", "")) || 0, count: msgs.length, preview });
2845
+ out3.push({ file: f, when: Number(f.replace(".json", "")) || 0, count: msgs.length, preview });
2740
2846
  }
2741
- return out2.sort((a, b) => b.when - a.when);
2847
+ return out3.sort((a, b) => b.when - a.when);
2742
2848
  } catch {
2743
2849
  return [];
2744
2850
  }
@@ -2978,7 +3084,7 @@ ${content}` }];
2978
3084
  }
2979
3085
  return [...messages, { role: "user", content }];
2980
3086
  }
2981
- async function runTurn(messages, userInput, ask, approvedTools, approvedGuardKeys, signal, steering) {
3087
+ async function runTurn(messages, userInput, ask, approvedTools, approvedGuardKeys, signal, steering2) {
2982
3088
  messages.push({ role: "user", content: userInput });
2983
3089
  const snapshot = messages.slice();
2984
3090
  try {
@@ -2992,7 +3098,7 @@ async function runTurn(messages, userInput, ask, approvedTools, approvedGuardKey
2992
3098
  console.error(color.red(`
2993
3099
  [compaction failed: ${err.message} \u2014 continuing]`) + "\n");
2994
3100
  }
2995
- if (steering?.length) messages = applySteering(messages, steering.splice(0));
3101
+ if (steering2?.length) messages = applySteering(messages, steering2.splice(0));
2996
3102
  const message = await callModel(messages, true, signal);
2997
3103
  messages.push(message);
2998
3104
  if (!hasContent2(message)) {
@@ -3006,7 +3112,7 @@ async function runTurn(messages, userInput, ask, approvedTools, approvedGuardKey
3006
3112
  await handleToolCall(call, messages, step, deps);
3007
3113
  }
3008
3114
  } else {
3009
- answered = !steering?.length;
3115
+ answered = !steering2?.length;
3010
3116
  }
3011
3117
  }
3012
3118
  if (signal?.aborted) {
@@ -3078,62 +3184,395 @@ async function runtimeContext() {
3078
3184
  });
3079
3185
  }
3080
3186
 
3081
- // src/statusline.ts
3187
+ // src/chrome.ts
3082
3188
  import { execFile as execFile2 } from "node:child_process";
3189
+ var out2 = (s) => process.stdout.write(s);
3190
+ var rows = () => process.stdout.rows ?? 24;
3191
+ var cols = () => process.stdout.columns ?? 80;
3083
3192
  var active2 = false;
3084
- var drawTimer = null;
3085
- var gitTimer = null;
3193
+ var turnActive = false;
3194
+ var buf = "";
3195
+ var cur = 0;
3196
+ var allItems = [];
3197
+ var sel = 0;
3198
+ var menuHidden = false;
3199
+ var menuH = 0;
3200
+ var prevMenuH = 0;
3201
+ var lastRegionB = -1;
3202
+ var pickItems = null;
3203
+ var pickSel = 0;
3204
+ var pickTitle = "";
3205
+ var pickResolve = null;
3086
3206
  var branch = "";
3087
- var tokensOf = null;
3088
- var rows = () => process.stdout.rows ?? 24;
3207
+ var tokensOf = () => 0;
3208
+ var onResult = null;
3209
+ var onInterrupt = null;
3210
+ var steering = [];
3211
+ var restoreKeys = null;
3212
+ var gitTimer = null;
3213
+ var statusTimer = null;
3214
+ var lastRows = 0;
3215
+ var BURST_IDLE_MS = 8;
3216
+ var burstLen = 0;
3217
+ var burstTimer = null;
3218
+ var pendingRender = false;
3219
+ var statusRow = () => Math.max(1, rows());
3220
+ var borderBottomRow = () => Math.max(1, rows() - 1);
3221
+ var inputRow = () => Math.max(1, rows() - 2);
3222
+ var borderTopRow = () => Math.max(1, rows() - 3);
3223
+ var mark = () => color.green("\u203A ");
3224
+ var PW = 2;
3225
+ function modeSegment() {
3226
+ return state.mode === "auto" ? color.yellow("auto-approve") : state.mode === "readonly" ? color.cyan("read-only") : color.green("normal");
3227
+ }
3228
+ function statusText() {
3229
+ const model = state.model.split("/").pop() ?? state.model;
3230
+ const tok = tokensOf();
3231
+ const ctxK = Math.round(config.maxContextTokens / 1e3);
3232
+ const parts = [
3233
+ modeSegment(),
3234
+ color.cyan(model),
3235
+ color.dim(state.reasoningEffort),
3236
+ ...branch ? [color.green(branch)] : [],
3237
+ color.dim(`~${Math.round(tok / 1e3)}k/${ctxK}k`)
3238
+ ];
3239
+ const bg = runningTaskCount();
3240
+ if (bg > 0) parts.push(color.yellow(`${bg} task${bg === 1 ? "" : "s"}`));
3241
+ return parts.join(color.dim(" \xB7 "));
3242
+ }
3089
3243
  function pollGit() {
3090
- execFile2("git", ["rev-parse", "--abbrev-ref", "HEAD"], { timeout: 1500, windowsHide: true }, (err, out2) => {
3244
+ execFile2("git", ["rev-parse", "--abbrev-ref", "HEAD"], { timeout: 1500, windowsHide: true }, (err, o) => {
3091
3245
  if (err) {
3092
3246
  branch = "";
3093
3247
  return;
3094
3248
  }
3095
- const b = String(out2).trim();
3096
- execFile2("git", ["status", "--porcelain"], { timeout: 1500, windowsHide: true }, (e2, out22) => {
3097
- branch = b + (!e2 && String(out22).trim() ? "*" : "");
3249
+ const b = String(o).trim();
3250
+ execFile2("git", ["status", "--porcelain"], { timeout: 1500, windowsHide: true }, (e2, o2) => {
3251
+ branch = b + (!e2 && String(o2).trim() ? "*" : "");
3098
3252
  });
3099
3253
  });
3100
3254
  }
3101
- function segments() {
3102
- const parts = [state.model.split("/").pop() ?? state.model, state.reasoningEffort];
3103
- if (branch) parts.push(branch);
3104
- if (tokensOf) parts.push(`~${Math.round(tokensOf() / 1e3)}k`);
3105
- const bg = runningTaskCount();
3106
- if (bg > 0) parts.push(`${bg} task${bg === 1 ? "" : "s"}`);
3107
- return parts.join(" \xB7 ");
3255
+ function drawStatus() {
3256
+ const s = statusText();
3257
+ const vis = stripAnsi(s);
3258
+ out2(ansi.moveTo(statusRow()) + ansi.clearLine + (vis.length <= cols() ? s : stripAnsi(s).slice(0, cols() - 1)));
3108
3259
  }
3109
- function draw() {
3260
+ function drawBorder(row) {
3261
+ out2(ansi.moveTo(row) + ansi.clearLine + color.dim("\u2500".repeat(Math.max(1, cols()))));
3262
+ }
3263
+ var closePick = () => {
3264
+ pickItems = null;
3265
+ pickSel = 0;
3266
+ pickTitle = "";
3267
+ };
3268
+ function chromePick(items, initial = 0, title = "") {
3269
+ return new Promise((resolve2) => {
3270
+ if (!active2 || !items.length) {
3271
+ resolve2(null);
3272
+ return;
3273
+ }
3274
+ pickItems = items;
3275
+ pickSel = Math.min(Math.max(0, initial), items.length - 1);
3276
+ pickTitle = title;
3277
+ pickResolve = resolve2;
3278
+ render();
3279
+ });
3280
+ }
3281
+ var flat = (s) => s.replace(/\n/g, "\u23CE");
3282
+ function drawInput() {
3283
+ const disp = flat(buf);
3284
+ const start = windowStart(disp, cur, Math.max(1, cols() - PW));
3285
+ const shown = disp.slice(start);
3286
+ const ci = cur - start;
3287
+ let body;
3288
+ if (!buf) {
3289
+ body = ansi.reverse + " " + ansi.reverseOff + color.dim("type a message \xB7 / for commands \xB7 Shift+Tab mode");
3290
+ } else {
3291
+ const at = ci < shown.length ? shown[ci] : " ";
3292
+ body = shown.slice(0, ci) + ansi.reverse + at + ansi.reverseOff + (ci < shown.length ? shown.slice(ci + 1) : "");
3293
+ }
3294
+ out2(ansi.moveTo(inputRow()) + ansi.clearLine + mark() + body);
3295
+ }
3296
+ function currentMenu() {
3297
+ if (menuHidden || turnActive) return [];
3298
+ const m = buf.match(/^\/(\S*)$/);
3299
+ if (!m) return [];
3300
+ const pre = "/" + m[1];
3301
+ return allItems.filter((c) => c.name.startsWith(pre)).slice(0, 8);
3302
+ }
3303
+ function render() {
3110
3304
  if (!active2) return;
3111
- process.stdout.write(`\x1B7\x1B[${rows()};1H\x1B[2K${color.dim(segments())}\x1B8`);
3305
+ const picker = pickItems;
3306
+ const list = picker ? picker.slice(0, 12).map((it, i) => ({ text: it.label + (it.hint ? " " + color.dim(it.hint) : ""), on: i === pickSel })) : currentMenu().map((it, i) => ({ text: it.name.padEnd(12) + " " + it.desc, on: i === sel }));
3307
+ if (!picker && sel >= list.length) sel = Math.max(0, list.length - 1);
3308
+ menuH = list.length;
3309
+ out2(ansi.hideCursor + ansi.saveCursor);
3310
+ const regionB = Math.max(1, rows() - 4 - menuH);
3311
+ if (regionB !== lastRegionB) {
3312
+ out2(ansi.setRegion(1, regionB));
3313
+ lastRegionB = regionB;
3314
+ }
3315
+ for (let r = borderTopRow() - prevMenuH; r < borderTopRow() - menuH; r++) out2(ansi.moveTo(r) + ansi.clearLine);
3316
+ prevMenuH = menuH;
3317
+ const base = borderTopRow() - menuH;
3318
+ for (let i = 0; i < list.length; i++) {
3319
+ out2(ansi.moveTo(base + i) + ansi.clearLine + (list[i].on ? color.green("\u203A ") + list[i].text : color.dim(" ") + list[i].text));
3320
+ }
3321
+ drawBorder(borderTopRow());
3322
+ if (picker) out2(ansi.moveTo(inputRow()) + ansi.clearLine + mark() + color.dim(pickTitle || "\u2191/\u2193 to choose \xB7 Enter to select \xB7 Esc to cancel"));
3323
+ else drawInput();
3324
+ drawBorder(borderBottomRow());
3325
+ drawStatus();
3326
+ out2(ansi.restoreCursor);
3327
+ lastRows = rows();
3328
+ pendingRender = false;
3112
3329
  }
3113
3330
  function onResize() {
3114
3331
  if (!active2) return;
3115
- process.stdout.write(`\x1B[1;${rows() - 1}r`);
3116
- draw();
3332
+ const newRows = rows();
3333
+ if (lastRows > 0 && newRows > lastRows) {
3334
+ out2(ansi.saveCursor);
3335
+ const oldTop = Math.max(1, lastRows - 3 - prevMenuH);
3336
+ for (let r = oldTop; r <= lastRows; r++) out2(ansi.moveTo(r) + ansi.clearLine);
3337
+ out2(ansi.restoreCursor);
3338
+ }
3339
+ lastRegionB = -1;
3340
+ render();
3341
+ }
3342
+ function refreshChrome() {
3343
+ if (!active2) return;
3344
+ out2(ansi.hideCursor + ansi.saveCursor);
3345
+ drawStatus();
3346
+ out2(ansi.restoreCursor);
3347
+ }
3348
+ var edited = () => {
3349
+ menuHidden = false;
3350
+ sel = 0;
3351
+ };
3352
+ function insert(s) {
3353
+ buf = buf.slice(0, cur) + s + buf.slice(cur);
3354
+ cur += s.length;
3355
+ edited();
3356
+ pendingRender = true;
3357
+ }
3358
+ function resetBurst() {
3359
+ if (burstTimer) {
3360
+ clearTimeout(burstTimer);
3361
+ burstTimer = null;
3362
+ }
3363
+ burstLen = 0;
3364
+ pendingRender = false;
3365
+ }
3366
+ function onKey(str, key) {
3367
+ if (pickItems) {
3368
+ const n = pickItems.length;
3369
+ if (key?.name === "up") {
3370
+ pickSel = (pickSel - 1 + n) % n;
3371
+ render();
3372
+ return;
3373
+ }
3374
+ if (key?.name === "down") {
3375
+ pickSel = (pickSel + 1) % n;
3376
+ render();
3377
+ return;
3378
+ }
3379
+ if (key?.name === "return" || key?.name === "enter") {
3380
+ const v = pickItems[pickSel].value;
3381
+ closePick();
3382
+ render();
3383
+ pickResolve?.(v);
3384
+ pickResolve = null;
3385
+ return;
3386
+ }
3387
+ if (key?.name === "escape" || key?.ctrl && key.name === "c") {
3388
+ closePick();
3389
+ render();
3390
+ pickResolve?.(null);
3391
+ pickResolve = null;
3392
+ return;
3393
+ }
3394
+ return;
3395
+ }
3396
+ burstLen++;
3397
+ if (burstTimer) clearTimeout(burstTimer);
3398
+ burstTimer = setTimeout(() => {
3399
+ burstTimer = null;
3400
+ burstLen = 0;
3401
+ if (pendingRender) render();
3402
+ }, BURST_IDLE_MS);
3403
+ const mm = currentMenu();
3404
+ if (key?.ctrl && key.name === "c") {
3405
+ if (buf) {
3406
+ buf = "";
3407
+ cur = 0;
3408
+ edited();
3409
+ resetBurst();
3410
+ render();
3411
+ return;
3412
+ }
3413
+ if (turnActive) onInterrupt?.();
3414
+ else {
3415
+ const r = onResult;
3416
+ onResult = null;
3417
+ r?.({ type: "quit" });
3418
+ }
3419
+ return;
3420
+ }
3421
+ if (key?.name === "escape") {
3422
+ if (mm.length) {
3423
+ menuHidden = true;
3424
+ render();
3425
+ } else if (buf) {
3426
+ buf = "";
3427
+ cur = 0;
3428
+ render();
3429
+ }
3430
+ return;
3431
+ }
3432
+ if (key?.name === "tab" && key.shift) {
3433
+ state.mode = nextMode(state.mode);
3434
+ render();
3435
+ return;
3436
+ }
3437
+ if (key?.name === "tab") {
3438
+ if (mm.length) {
3439
+ buf = mm[sel].name + " ";
3440
+ cur = buf.length;
3441
+ edited();
3442
+ render();
3443
+ }
3444
+ return;
3445
+ }
3446
+ if (key?.name === "up") {
3447
+ if (mm.length) {
3448
+ sel = (sel - 1 + mm.length) % mm.length;
3449
+ render();
3450
+ }
3451
+ return;
3452
+ }
3453
+ if (key?.name === "down") {
3454
+ if (mm.length) {
3455
+ sel = (sel + 1) % mm.length;
3456
+ render();
3457
+ }
3458
+ return;
3459
+ }
3460
+ if (key?.name === "return" || key?.name === "enter") {
3461
+ if (key?.shift || key?.meta || burstLen > 1) {
3462
+ insert("\n");
3463
+ return;
3464
+ }
3465
+ resetBurst();
3466
+ const line = buf;
3467
+ buf = "";
3468
+ cur = 0;
3469
+ edited();
3470
+ render();
3471
+ if (turnActive) {
3472
+ if (line.trim()) steering.push(line.trim());
3473
+ return;
3474
+ }
3475
+ if (line.trim()) out2("\n" + mark() + line + "\n");
3476
+ const r = onResult;
3477
+ onResult = null;
3478
+ r?.({ type: "line", value: line });
3479
+ return;
3480
+ }
3481
+ if (key?.name === "backspace") {
3482
+ if (cur > 0) {
3483
+ buf = buf.slice(0, cur - 1) + buf.slice(cur);
3484
+ cur--;
3485
+ edited();
3486
+ render();
3487
+ }
3488
+ return;
3489
+ }
3490
+ if (key?.name === "delete") {
3491
+ if (cur < buf.length) {
3492
+ buf = buf.slice(0, cur) + buf.slice(cur + 1);
3493
+ edited();
3494
+ render();
3495
+ }
3496
+ return;
3497
+ }
3498
+ if (key?.name === "left") {
3499
+ if (cur > 0) {
3500
+ cur--;
3501
+ render();
3502
+ }
3503
+ return;
3504
+ }
3505
+ if (key?.name === "right") {
3506
+ if (cur < buf.length) {
3507
+ cur++;
3508
+ render();
3509
+ }
3510
+ return;
3511
+ }
3512
+ if (key && (key.name === "home" || key.ctrl && key.name === "a")) {
3513
+ cur = 0;
3514
+ render();
3515
+ return;
3516
+ }
3517
+ if (key && (key.name === "end" || key.ctrl && key.name === "e")) {
3518
+ cur = buf.length;
3519
+ render();
3520
+ return;
3521
+ }
3522
+ if (key?.ctrl && key.name === "u") {
3523
+ buf = buf.slice(cur);
3524
+ cur = 0;
3525
+ edited();
3526
+ render();
3527
+ return;
3528
+ }
3529
+ if (str && !key?.ctrl && !key?.meta && [...str].length === 1 && isPrintableCodePoint(str.codePointAt(0))) {
3530
+ insert(str);
3531
+ }
3117
3532
  }
3118
- function startStatusline(tokens) {
3119
- if (active2 || !process.stdout.isTTY || !config.statuslineEnabled) return;
3533
+ function startChrome(opts) {
3534
+ if (active2 || !process.stdout.isTTY) return;
3120
3535
  active2 = true;
3121
- tokensOf = tokens;
3122
- process.stdout.write(`\x1B[1;${rows() - 1}r`);
3536
+ tokensOf = opts.tokens;
3537
+ allItems = opts.items;
3538
+ onInterrupt = opts.onInterrupt;
3539
+ out2(ansi.bracketedPasteOff);
3123
3540
  pollGit();
3124
- draw();
3125
- drawTimer = setInterval(draw, config.statuslineRefreshMs);
3541
+ restoreKeys = pushKeyHandler(onKey);
3542
+ render();
3543
+ statusTimer = setInterval(refreshChrome, config.statuslineRefreshMs);
3126
3544
  gitTimer = setInterval(pollGit, 5e3);
3127
3545
  process.stdout.on("resize", onResize);
3128
3546
  }
3129
- function stopStatusline() {
3547
+ function nextLine() {
3548
+ return new Promise((resolve2) => {
3549
+ turnActive = false;
3550
+ render();
3551
+ onResult = resolve2;
3552
+ });
3553
+ }
3554
+ function beginTurn() {
3555
+ steering.length = 0;
3556
+ turnActive = true;
3557
+ return steering;
3558
+ }
3559
+ function endTurn() {
3560
+ turnActive = false;
3561
+ render();
3562
+ }
3563
+ function stopChrome() {
3130
3564
  if (!active2) return;
3131
3565
  active2 = false;
3132
- if (drawTimer) clearInterval(drawTimer);
3133
- if (gitTimer) clearInterval(gitTimer);
3566
+ restoreKeys?.();
3567
+ restoreKeys = null;
3134
3568
  process.stdout.removeListener("resize", onResize);
3135
- process.stdout.write(`\x1B[r\x1B[${rows()};1H\x1B[2K`);
3569
+ if (statusTimer) clearInterval(statusTimer);
3570
+ if (gitTimer) clearInterval(gitTimer);
3571
+ resetBurst();
3572
+ out2(ansi.resetRegion);
3573
+ for (const r of [statusRow(), borderBottomRow(), inputRow(), borderTopRow()]) out2(ansi.moveTo(r) + ansi.clearLine);
3136
3574
  }
3575
+ var chromeEnabled = () => config.statuslineEnabled && !!process.stdout.isTTY;
3137
3576
 
3138
3577
  // src/commands.ts
3139
3578
  import { writeFile as writeFile4, mkdir as mkdir4, chmod as chmod3 } from "node:fs/promises";
@@ -3187,6 +3626,13 @@ ${extra}` : "");
3187
3626
  }
3188
3627
 
3189
3628
  // src/commands.ts
3629
+ async function pick(opts) {
3630
+ if (chromeEnabled()) {
3631
+ const v = await chromePick(opts.items.map((it) => ({ label: it.label, hint: it.hint, value: it.value })), opts.initial, opts.title);
3632
+ return v == null ? null : v;
3633
+ }
3634
+ return selectMenu(opts);
3635
+ }
3190
3636
  var SLASH_COMMANDS = [
3191
3637
  { name: "/model", desc: "switch model (menu; /model <term> searches)" },
3192
3638
  { name: "/effort", desc: "set reasoning effort (off/low/medium/high/max)" },
@@ -3264,20 +3710,23 @@ async function handleCommand(input, messages) {
3264
3710
  } else if (cmd === "/update") {
3265
3711
  console.log(color.dim("updating beecork\u2026 (npm install -g beecork@latest)"));
3266
3712
  const { ok, output } = await selfUpdate();
3267
- if (ok) console.log(color.green("\u2713 updated \u2014 restart beecork to use the new version.") + "\n");
3268
- else {
3713
+ if (ok) {
3714
+ console.log(color.green("\u2713 updated \u2014 restart beecork to use the new version.") + "\n");
3715
+ const w = await shadowWarning();
3716
+ if (w) console.log(color.yellow(w) + "\n");
3717
+ } else {
3269
3718
  console.log(color.red("update failed: ") + output.split("\n").filter(Boolean).slice(-1)[0]);
3270
3719
  console.log(color.dim(" run manually: npm install -g beecork (may need sudo / your version manager)") + "\n");
3271
3720
  }
3272
3721
  } else if (cmd === "/clear") {
3273
3722
  messages.splice(1);
3274
- if (process.stdout.isTTY) process.stdout.write("\x1B[2J\x1B[3J\x1B[H");
3723
+ if (process.stdout.isTTY) process.stdout.write(ansi.clearScreen + ansi.clearScrollback + ansi.home);
3275
3724
  console.log(color.dim("conversation cleared (kept the system prompt, your model + settings).") + "\n");
3276
3725
  } else if (cmd === "/resume") {
3277
3726
  const sessions = process.stdin.isTTY ? await listSessions() : [];
3278
3727
  let restored = [];
3279
3728
  if (sessions.length > 1) {
3280
- const choice = await selectMenu({
3729
+ const choice = await pick({
3281
3730
  title: "resume which session? \u2014 \u2191/\u2193 then Enter (Esc to cancel)",
3282
3731
  items: sessions.map((s) => ({
3283
3732
  label: s.preview || "(no first message)",
@@ -3330,7 +3779,7 @@ async function handleCommand(input, messages) {
3330
3779
  }
3331
3780
  async function pickModel() {
3332
3781
  if (!process.stdin.isTTY) return showRecommended();
3333
- const choice = await selectMenu({
3782
+ const choice = await pick({
3334
3783
  title: "switch model \u2014 \u2191/\u2193 then Enter (Esc to cancel)",
3335
3784
  initial: Math.max(0, RECOMMENDED_MODELS.findIndex((m) => m.slug === state.model)),
3336
3785
  items: RECOMMENDED_MODELS.map((m) => ({
@@ -3353,7 +3802,7 @@ async function pickEffort() {
3353
3802
  console.log(color.cyan(`reasoning effort: ${state.reasoningEffort}`) + color.dim(` (levels: ${EFFORTS.join(" / ")})`) + "\n");
3354
3803
  return;
3355
3804
  }
3356
- const choice = await selectMenu({
3805
+ const choice = await pick({
3357
3806
  title: "reasoning effort \u2014 \u2191/\u2193 then Enter (Esc to cancel)",
3358
3807
  initial: Math.max(0, EFFORTS.indexOf(state.reasoningEffort)),
3359
3808
  items: EFFORTS.map((e) => ({
@@ -3388,7 +3837,7 @@ async function searchModels(term) {
3388
3837
  }
3389
3838
  const priceOf = (m) => m.pricing?.prompt != null ? `$${(parseFloat(m.pricing.prompt) * 1e6).toFixed(2)}/1M` : "?";
3390
3839
  if (process.stdin.isTTY) {
3391
- const choice = await selectMenu({
3840
+ const choice = await pick({
3392
3841
  title: `models matching "${term}" \u2014 \u2191/\u2193 then Enter (Esc to cancel)`,
3393
3842
  items: matches.map((m) => ({
3394
3843
  label: m.id,
@@ -3435,8 +3884,11 @@ async function main() {
3435
3884
  if (process.argv[2] === "update") {
3436
3885
  console.log("Updating beecork\u2026");
3437
3886
  const { ok, output } = await selfUpdate();
3438
- if (ok) console.log(color.green("\u2713 beecork updated. Run `beecork` to use the new version."));
3439
- else {
3887
+ if (ok) {
3888
+ console.log(color.green("\u2713 beecork updated. Run `beecork` to use the new version."));
3889
+ const w = await shadowWarning();
3890
+ if (w) console.log("\n" + color.yellow(w));
3891
+ } else {
3440
3892
  console.error(color.red("Update failed:") + "\n" + output);
3441
3893
  console.error(color.dim("\nTry manually: npm install -g beecork (you may need sudo, or your Node version manager)."));
3442
3894
  process.exitCode = 1;
@@ -3497,7 +3949,7 @@ ${instr.trusted}`;
3497
3949
  };
3498
3950
  const tty = !!process.stdin.isTTY;
3499
3951
  process.on("exit", killAllTasks);
3500
- process.on("exit", stopStatusline);
3952
+ process.on("exit", stopChrome);
3501
3953
  if (tty) {
3502
3954
  initInput();
3503
3955
  process.on("exit", teardownInput);
@@ -3516,12 +3968,13 @@ ${instr.trusted}`;
3516
3968
  });
3517
3969
  }
3518
3970
  const rl = tty ? null : createInterface({ input: process.stdin, output: process.stdout, completer });
3519
- printBanner(state.model, instr.sources.map(tildify));
3971
+ if (chromeEnabled()) process.stdout.write(ansi.clearScreen + ansi.clearScrollback + ansi.home);
3972
+ const version = await currentVersion();
3973
+ printBanner(state.model, version, instr.sources.map(tildify));
3520
3974
  if (config.dangerouslySkipPermissions) {
3521
3975
  console.log(color.red("\u26A0 --dangerously-skip-permissions is ON: the approval gate is OFF. Out-of-root paths and risky") + "\n" + color.red(" shell commands will RUN unprompted. Use only in a disposable sandbox. (read-only mode + catastrophic-") + "\n" + color.red(" command refusal still apply.)") + "\n");
3522
3976
  }
3523
3977
  if (tty) {
3524
- const version = await currentVersion();
3525
3978
  const newer = await checkForUpdate(version);
3526
3979
  if (newer) console.log(color.dim(`\u25B8 beecork ${stripControl(newer)} is available (you have ${version}) \u2014 update: npm install -g beecork`) + "\n");
3527
3980
  }
@@ -3565,10 +4018,19 @@ ${instr.trusted}`;
3565
4018
  }
3566
4019
  });
3567
4020
  }
3568
- startStatusline(() => estimateTokens(messages));
4021
+ if (chromeEnabled())
4022
+ startChrome({
4023
+ tokens: () => estimateTokens(messages),
4024
+ items: [...SLASH_COMMANDS, ...skills.map((s) => ({ name: "/" + s.name, desc: "skill" }))],
4025
+ onInterrupt: () => activeTurn?.abort()
4026
+ });
3569
4027
  while (true) {
3570
4028
  let userInput;
3571
- if (tty) {
4029
+ if (chromeEnabled()) {
4030
+ const r = await nextLine();
4031
+ if (r.type === "quit") break;
4032
+ userInput = r.value;
4033
+ } else if (tty) {
3572
4034
  const r = await readPrompt({
3573
4035
  promptString,
3574
4036
  commands: SLASH_COMMANDS,
@@ -3606,16 +4068,27 @@ ${instr.trusted}`;
3606
4068
  continue;
3607
4069
  }
3608
4070
  }
4071
+ if (chromeEnabled()) {
4072
+ activeTurn = new AbortController();
4073
+ const steering3 = beginTurn();
4074
+ try {
4075
+ messages = await runTurn(messages, userInput, ask, approvedTools, approvedGuardKeys, activeTurn.signal, steering3);
4076
+ } finally {
4077
+ activeTurn = null;
4078
+ endTurn();
4079
+ }
4080
+ continue;
4081
+ }
3609
4082
  activeTurn = new AbortController();
3610
4083
  let modeChangedMidTurn = false;
3611
- const steering = [];
4084
+ const steering2 = [];
3612
4085
  let typed = "";
3613
- const redrawSteer = () => process.stdout.write("\r\x1B[2K" + color.cyan("\xBB ") + color.dim(typed));
4086
+ const redrawSteer = () => process.stdout.write(ansi.cr + ansi.clearLine + color.cyan("\xBB ") + color.dim(typed));
3614
4087
  const clearSteer = () => {
3615
- process.stdout.write("\r\x1B[2K");
4088
+ process.stdout.write(ansi.cr + ansi.clearLine);
3616
4089
  setSteeringActive(false);
3617
4090
  };
3618
- const restoreKeys = tty ? pushKeyHandler((str, key) => {
4091
+ const restoreKeys2 = tty ? pushKeyHandler((str, key) => {
3619
4092
  if (key && (key.name === "escape" || key.ctrl && key.name === "c")) {
3620
4093
  activeTurn?.abort();
3621
4094
  return;
@@ -3630,7 +4103,7 @@ ${instr.trusted}`;
3630
4103
  typed = "";
3631
4104
  clearSteer();
3632
4105
  if (note) {
3633
- steering.push(note);
4106
+ steering2.push(note);
3634
4107
  console.log(color.dim(`\xBB queued for next step: ${note}`));
3635
4108
  }
3636
4109
  return;
@@ -3650,9 +4123,9 @@ ${instr.trusted}`;
3650
4123
  }) : () => {
3651
4124
  };
3652
4125
  try {
3653
- messages = await runTurn(messages, userInput, ask, approvedTools, approvedGuardKeys, activeTurn.signal, steering);
4126
+ messages = await runTurn(messages, userInput, ask, approvedTools, approvedGuardKeys, activeTurn.signal, steering2);
3654
4127
  } finally {
3655
- restoreKeys();
4128
+ restoreKeys2();
3656
4129
  setSteeringActive(false);
3657
4130
  activeTurn = null;
3658
4131
  if (modeChangedMidTurn) console.log(color.yellow(`\u25B8 mode: ${modeLabel(state.mode)}`));
@@ -3660,6 +4133,7 @@ ${instr.trusted}`;
3660
4133
  if (bg > 0) console.log(color.dim(`\u25B8 ${bg} background task${bg === 1 ? "" : "s"} running \u2014 check_task / stop_task`));
3661
4134
  }
3662
4135
  }
4136
+ stopChrome();
3663
4137
  teardownInput();
3664
4138
  rl?.close();
3665
4139
  await persist();