beecork 2.4.1 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/index.js +711 -280
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -114,9 +114,10 @@ var config = {
|
|
|
114
114
|
// Sub-agent (explore tool)
|
|
115
115
|
subAgentMaxSteps: num("SUBAGENT_MAX_STEPS", 15),
|
|
116
116
|
// child explorer's step budget (bounds cost/latency)
|
|
117
|
-
// Live status line (bottom row: model · effort · git branch · ~tokens · bg tasks)
|
|
118
|
-
|
|
119
|
-
//
|
|
117
|
+
// Live status line (bottom row: model · effort · git branch · ~tokens · bg tasks). DEFAULT OFF —
|
|
118
|
+
// the interim scroll-region version clashes with the inline input (banner loss / flicker); it's
|
|
119
|
+
// being replaced by a proper pinned-input UI. Opt in with STATUSLINE=1.
|
|
120
|
+
statuslineEnabled: ["1", "true", "on", "yes"].includes((process.env.STATUSLINE ?? "").trim().toLowerCase()),
|
|
120
121
|
statuslineRefreshMs: num("STATUSLINE_REFRESH_MS", 2e3),
|
|
121
122
|
// bar refresh interval
|
|
122
123
|
// Integrations / modes
|
|
@@ -195,7 +196,7 @@ function selfUpdate(timeoutMs = 12e4) {
|
|
|
195
196
|
return new Promise((resolve2) => {
|
|
196
197
|
const npm = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
197
198
|
const child = spawn(npm, ["install", "-g", "beecork@latest"], { stdio: ["ignore", "pipe", "pipe"] });
|
|
198
|
-
let
|
|
199
|
+
let out3 = "", done = false;
|
|
199
200
|
const finish = (r) => {
|
|
200
201
|
if (done) return;
|
|
201
202
|
done = true;
|
|
@@ -207,13 +208,13 @@ function selfUpdate(timeoutMs = 12e4) {
|
|
|
207
208
|
child.kill("SIGKILL");
|
|
208
209
|
} catch {
|
|
209
210
|
}
|
|
210
|
-
finish({ ok: false, output: `${
|
|
211
|
+
finish({ ok: false, output: `${out3.trim()}
|
|
211
212
|
(update timed out after ${timeoutMs}ms \u2014 run manually: npm install -g beecork@latest)`.trim() });
|
|
212
213
|
}, timeoutMs);
|
|
213
|
-
child.stdout?.on("data", (d) =>
|
|
214
|
-
child.stderr?.on("data", (d) =>
|
|
214
|
+
child.stdout?.on("data", (d) => out3 += d);
|
|
215
|
+
child.stderr?.on("data", (d) => out3 += d);
|
|
215
216
|
child.on("error", (e) => finish({ ok: false, output: e.message }));
|
|
216
|
-
child.on("close", (code) => finish({ ok: code === 0, output:
|
|
217
|
+
child.on("close", (code) => finish({ ok: code === 0, output: out3.trim() }));
|
|
217
218
|
});
|
|
218
219
|
}
|
|
219
220
|
|
|
@@ -255,27 +256,65 @@ function lineDiff(oldText, newText) {
|
|
|
255
256
|
lcs[i2][j2] = a[i2] === b[j2] ? lcs[i2 + 1][j2 + 1] + 1 : Math.max(lcs[i2 + 1][j2], lcs[i2][j2 + 1]);
|
|
256
257
|
}
|
|
257
258
|
}
|
|
258
|
-
const
|
|
259
|
+
const out3 = [];
|
|
259
260
|
let i = 0;
|
|
260
261
|
let j = 0;
|
|
261
262
|
while (i < m && j < n) {
|
|
262
263
|
if (a[i] === b[j]) {
|
|
263
|
-
|
|
264
|
+
out3.push(" " + a[i]);
|
|
264
265
|
i++;
|
|
265
266
|
j++;
|
|
266
267
|
} else if (lcs[i + 1][j] >= lcs[i][j + 1]) {
|
|
267
|
-
|
|
268
|
+
out3.push("- " + a[i]);
|
|
268
269
|
i++;
|
|
269
270
|
} else {
|
|
270
|
-
|
|
271
|
+
out3.push("+ " + b[j]);
|
|
271
272
|
j++;
|
|
272
273
|
}
|
|
273
274
|
}
|
|
274
|
-
while (i < m)
|
|
275
|
-
while (j < n)
|
|
276
|
-
return
|
|
275
|
+
while (i < m) out3.push("- " + a[i++]);
|
|
276
|
+
while (j < n) out3.push("+ " + b[j++]);
|
|
277
|
+
return out3.join("\n");
|
|
277
278
|
}
|
|
278
279
|
|
|
280
|
+
// src/ansi.ts
|
|
281
|
+
var ESC = "\x1B";
|
|
282
|
+
var CSI = "\x1B[";
|
|
283
|
+
var ansi = {
|
|
284
|
+
// --- cursor visibility ---
|
|
285
|
+
hideCursor: CSI + "?25l",
|
|
286
|
+
showCursor: CSI + "?25h",
|
|
287
|
+
// --- save / restore cursor (position + attributes) ---
|
|
288
|
+
saveCursor: ESC + "7",
|
|
289
|
+
restoreCursor: ESC + "8",
|
|
290
|
+
// --- clearing ---
|
|
291
|
+
clearLine: CSI + "2K",
|
|
292
|
+
// entire current line (cursor row unchanged)
|
|
293
|
+
clearToEnd: CSI + "J",
|
|
294
|
+
// cursor → end of screen
|
|
295
|
+
clearScreen: CSI + "2J",
|
|
296
|
+
// whole viewport
|
|
297
|
+
clearScrollback: CSI + "3J",
|
|
298
|
+
// scrollback buffer (xterm extension)
|
|
299
|
+
cr: "\r",
|
|
300
|
+
// carriage return (column 1, same row)
|
|
301
|
+
home: CSI + "H",
|
|
302
|
+
// row 1, col 1
|
|
303
|
+
// --- motion (n omitted → terminal default of 1) ---
|
|
304
|
+
up: (n = 1) => CSI + n + "A",
|
|
305
|
+
forward: (n = 1) => CSI + n + "C",
|
|
306
|
+
moveTo: (row, col = 1) => CSI + row + ";" + col + "H",
|
|
307
|
+
// --- scroll region (DECSTBM); 1-based, inclusive. reset → full screen ---
|
|
308
|
+
setRegion: (top, bottom) => CSI + top + ";" + bottom + "r",
|
|
309
|
+
resetRegion: CSI + "r",
|
|
310
|
+
// --- bracketed paste mode ---
|
|
311
|
+
bracketedPasteOn: CSI + "?2004h",
|
|
312
|
+
bracketedPasteOff: CSI + "?2004l",
|
|
313
|
+
// --- reverse video (used to draw the pinned input's block cursor) ---
|
|
314
|
+
reverse: CSI + "7m",
|
|
315
|
+
reverseOff: CSI + "27m"
|
|
316
|
+
};
|
|
317
|
+
|
|
279
318
|
// src/ui.ts
|
|
280
319
|
var useColor = process.env.NO_COLOR ? false : process.env.FORCE_COLOR ? true : Boolean(process.stdout.isTTY);
|
|
281
320
|
var paint = (code) => (s) => useColor ? `\x1B[${code}m${s}\x1B[0m` : s;
|
|
@@ -293,12 +332,12 @@ var color = {
|
|
|
293
332
|
};
|
|
294
333
|
var isPrintableCodePoint = (c) => c >= 32 && c !== 127 && !(c >= 128 && c <= 159);
|
|
295
334
|
function stripControl(s) {
|
|
296
|
-
let
|
|
335
|
+
let out3 = "";
|
|
297
336
|
for (const ch of s) {
|
|
298
337
|
const c = ch.codePointAt(0);
|
|
299
|
-
if (c === 9 || c === 10 || isPrintableCodePoint(c))
|
|
338
|
+
if (c === 9 || c === 10 || isPrintableCodePoint(c)) out3 += ch;
|
|
300
339
|
}
|
|
301
|
-
return
|
|
340
|
+
return out3;
|
|
302
341
|
}
|
|
303
342
|
var stripAnsi = (s) => s.replace(/\x1b\[[0-9;]*m/g, "");
|
|
304
343
|
function charWidth(cp) {
|
|
@@ -417,19 +456,19 @@ function startSpinner(label) {
|
|
|
417
456
|
if (!process.stdout.isTTY || !useColor) return () => {
|
|
418
457
|
};
|
|
419
458
|
let i = 0;
|
|
420
|
-
const
|
|
459
|
+
const draw = () => {
|
|
421
460
|
if (steeringOnScreen) return;
|
|
422
|
-
process.stdout.write("
|
|
461
|
+
process.stdout.write(ansi.cr + " " + color.brand(SPINNER[i = (i + 1) % SPINNER.length]) + " " + color.dim(label));
|
|
423
462
|
};
|
|
424
|
-
process.stdout.write(
|
|
425
|
-
|
|
426
|
-
const timer = setInterval(
|
|
463
|
+
process.stdout.write(ansi.hideCursor);
|
|
464
|
+
draw();
|
|
465
|
+
const timer = setInterval(draw, 80);
|
|
427
466
|
let stopped = false;
|
|
428
467
|
return () => {
|
|
429
468
|
if (stopped) return;
|
|
430
469
|
stopped = true;
|
|
431
470
|
clearInterval(timer);
|
|
432
|
-
process.stdout.write(
|
|
471
|
+
process.stdout.write(ansi.cr + ansi.clearLine);
|
|
433
472
|
};
|
|
434
473
|
}
|
|
435
474
|
function markLines(width) {
|
|
@@ -464,21 +503,21 @@ function printBanner(model, sources) {
|
|
|
464
503
|
" | |_) | __/ __/ (_| (_) | | | < ",
|
|
465
504
|
" |_.__/ \\___|\\___|\\___\\___/|_| |_|\\_\\"
|
|
466
505
|
];
|
|
467
|
-
const
|
|
468
|
-
const markW = Math.max(0, ...
|
|
506
|
+
const mark2 = markLines(24);
|
|
507
|
+
const markW = Math.max(0, ...mark2.map((l) => l.length));
|
|
469
508
|
const wordW = Math.max(...word.map((l) => l.length));
|
|
470
|
-
const
|
|
509
|
+
const cols2 = process.stdout.columns || 80;
|
|
471
510
|
console.log();
|
|
472
|
-
if (
|
|
473
|
-
const h = Math.max(
|
|
474
|
-
const mPad = Math.floor((h -
|
|
511
|
+
if (cols2 >= markW + 3 + wordW + 2) {
|
|
512
|
+
const h = Math.max(mark2.length, word.length);
|
|
513
|
+
const mPad = Math.floor((h - mark2.length) / 2);
|
|
475
514
|
const wPad = Math.floor((h - word.length) / 2);
|
|
476
515
|
for (let i = 0; i < h; i++) {
|
|
477
|
-
const m = (
|
|
516
|
+
const m = (mark2[i - mPad] ?? "").padEnd(markW);
|
|
478
517
|
const w = word[i - wPad] ?? "";
|
|
479
518
|
console.log(" " + color.brand(`${m} ${w}`));
|
|
480
519
|
}
|
|
481
|
-
} else if (
|
|
520
|
+
} else if (cols2 >= wordW + 2) {
|
|
482
521
|
for (const w of word) console.log(" " + color.brand(w));
|
|
483
522
|
} else {
|
|
484
523
|
console.log(" " + color.brand("\u{1F41D} beecork"));
|
|
@@ -499,7 +538,7 @@ function printBanner(model, sources) {
|
|
|
499
538
|
const plain = (l, v) => l ? l.padEnd(lw) + " " + v : v;
|
|
500
539
|
const bw = Math.max(...rows2.map(([l, v]) => plain(l, v).length));
|
|
501
540
|
const row = ([l, v]) => l ? color.bold(l.padEnd(lw)) + color.dim(" " + v) : color.dim(v);
|
|
502
|
-
if (
|
|
541
|
+
if (cols2 < bw + 6) {
|
|
503
542
|
for (const r of rows2) console.log(" " + row(r));
|
|
504
543
|
} else {
|
|
505
544
|
console.log(" " + color.dim("\u256D\u2500" + "\u2500".repeat(bw) + "\u2500\u256E"));
|
|
@@ -517,6 +556,39 @@ import { readFile as readFile4 } from "node:fs/promises";
|
|
|
517
556
|
|
|
518
557
|
// src/input.ts
|
|
519
558
|
import { emitKeypressEvents } from "node:readline";
|
|
559
|
+
|
|
560
|
+
// src/layout.ts
|
|
561
|
+
function rowColOf(s) {
|
|
562
|
+
return { row: (s.match(/\n/g) || []).length, col: s.length - (s.lastIndexOf("\n") + 1) };
|
|
563
|
+
}
|
|
564
|
+
function inputLayout(text, before, promptW, cols2) {
|
|
565
|
+
const c = Math.max(1, cols2);
|
|
566
|
+
const physRows = text.split("\n").map((l) => Math.max(1, Math.ceil((promptW + displayWidth(l)) / c)));
|
|
567
|
+
const totalPhys = physRows.reduce((a, b) => a + b, 0);
|
|
568
|
+
const curLogicalRow = (before.match(/\n/g) || []).length;
|
|
569
|
+
const curCol = promptW + displayWidth(before.slice(before.lastIndexOf("\n") + 1));
|
|
570
|
+
const physBefore = physRows.slice(0, curLogicalRow).reduce((a, b) => a + b, 0);
|
|
571
|
+
const curPhysRow = physBefore + Math.floor(curCol / c);
|
|
572
|
+
const curPhysCol = curCol % c;
|
|
573
|
+
return { totalPhys, curPhysRow, curPhysCol };
|
|
574
|
+
}
|
|
575
|
+
function moveVertIndex(buf2, cur2, dir) {
|
|
576
|
+
const lines = buf2.split("\n");
|
|
577
|
+
const { row, col } = rowColOf(buf2.slice(0, cur2));
|
|
578
|
+
const target = row + dir;
|
|
579
|
+
if (target < 0 || target >= lines.length) return null;
|
|
580
|
+
let idx = 0;
|
|
581
|
+
for (let i = 0; i < target; i++) idx += lines[i].length + 1;
|
|
582
|
+
return idx + Math.min(col, lines[target].length);
|
|
583
|
+
}
|
|
584
|
+
function windowStart(text, cur2, avail) {
|
|
585
|
+
let start = 0;
|
|
586
|
+
if (displayWidth(text.slice(0, cur2)) >= avail)
|
|
587
|
+
while (start < cur2 && displayWidth(text.slice(start, cur2)) >= avail) start++;
|
|
588
|
+
return start;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
// src/input.ts
|
|
520
592
|
var out = (s) => process.stdout.write(s);
|
|
521
593
|
var active = null;
|
|
522
594
|
var started = false;
|
|
@@ -524,13 +596,13 @@ function initInput() {
|
|
|
524
596
|
if (started || !process.stdin.isTTY) return;
|
|
525
597
|
started = true;
|
|
526
598
|
process.stdin.setRawMode(true);
|
|
527
|
-
out(
|
|
599
|
+
out(ansi.bracketedPasteOff);
|
|
528
600
|
emitKeypressEvents(process.stdin);
|
|
529
601
|
process.stdin.on("keypress", (str, key) => active?.(str, key));
|
|
530
602
|
}
|
|
531
603
|
function teardownInput() {
|
|
532
604
|
if (started && process.stdin.isTTY) {
|
|
533
|
-
out(
|
|
605
|
+
out(ansi.bracketedPasteOn + ansi.showCursor);
|
|
534
606
|
process.stdin.setRawMode(false);
|
|
535
607
|
process.stdin.pause();
|
|
536
608
|
}
|
|
@@ -552,27 +624,27 @@ function readPrompt(opts) {
|
|
|
552
624
|
...(opts.skills ?? []).map((n) => ({ name: "/" + n, desc: "skill" }))
|
|
553
625
|
];
|
|
554
626
|
return new Promise((resolve2) => {
|
|
555
|
-
let
|
|
556
|
-
let
|
|
627
|
+
let buf2 = "";
|
|
628
|
+
let cur2 = 0;
|
|
557
629
|
let hist = history.length;
|
|
558
|
-
let
|
|
559
|
-
let
|
|
560
|
-
const
|
|
561
|
-
let
|
|
562
|
-
let
|
|
563
|
-
let
|
|
630
|
+
let sel2 = 0;
|
|
631
|
+
let menuHidden2 = false;
|
|
632
|
+
const BURST_IDLE_MS2 = 8;
|
|
633
|
+
let burstLen2 = 0;
|
|
634
|
+
let burstTimer2 = null;
|
|
635
|
+
let pendingRender2 = false;
|
|
564
636
|
const matches = () => {
|
|
565
637
|
if (opts.mask) return [];
|
|
566
|
-
const m =
|
|
638
|
+
const m = buf2.match(/^\/(\S*)$/);
|
|
567
639
|
if (!m) return [];
|
|
568
640
|
const pre = "/" + m[1];
|
|
569
641
|
return all.filter((c) => c.name.startsWith(pre)).slice(0, MENU_MAX);
|
|
570
642
|
};
|
|
571
|
-
const menu = () =>
|
|
643
|
+
const menu = () => menuHidden2 ? [] : matches();
|
|
572
644
|
const highlight = () => {
|
|
573
|
-
if (opts.mask) return "*".repeat(
|
|
574
|
-
const m =
|
|
575
|
-
if (!m) return
|
|
645
|
+
if (opts.mask) return "*".repeat(buf2.length);
|
|
646
|
+
const m = buf2.match(/^(\/\S*)([\s\S]*)$/);
|
|
647
|
+
if (!m) return buf2;
|
|
576
648
|
const [, token, rest] = m;
|
|
577
649
|
const known = all.some((c) => c.name === token);
|
|
578
650
|
const partial = all.some((c) => c.name.startsWith(token));
|
|
@@ -580,7 +652,6 @@ function readPrompt(opts) {
|
|
|
580
652
|
return styled + rest;
|
|
581
653
|
};
|
|
582
654
|
let lastCurRow = 0;
|
|
583
|
-
const rowColOf = (s) => ({ row: (s.match(/\n/g) || []).length, col: s.length - (s.lastIndexOf("\n") + 1) });
|
|
584
655
|
function drawBlock() {
|
|
585
656
|
const prompt = opts.promptString();
|
|
586
657
|
const promptW = stripAnsi(prompt).length;
|
|
@@ -590,199 +661,189 @@ function readPrompt(opts) {
|
|
|
590
661
|
for (let i = 1; i < lines.length; i++) out("\n" + indent + lines[i]);
|
|
591
662
|
return { promptW };
|
|
592
663
|
}
|
|
593
|
-
function
|
|
664
|
+
function render2() {
|
|
594
665
|
const mm = menu();
|
|
595
|
-
if (
|
|
596
|
-
out(
|
|
597
|
-
if (lastCurRow > 0) out(
|
|
598
|
-
out(
|
|
666
|
+
if (sel2 >= mm.length) sel2 = Math.max(0, mm.length - 1);
|
|
667
|
+
out(ansi.hideCursor);
|
|
668
|
+
if (lastCurRow > 0) out(ansi.up(lastCurRow));
|
|
669
|
+
out(ansi.cr + ansi.clearToEnd);
|
|
599
670
|
const { promptW } = drawBlock();
|
|
600
|
-
const
|
|
671
|
+
const cols2 = Math.max(1, process.stdout.columns || 80);
|
|
601
672
|
for (let i = 0; i < mm.length; i++) {
|
|
602
673
|
const m = mm[i];
|
|
603
674
|
const name = m.name.padEnd(10);
|
|
604
|
-
const maxDesc = Math.max(0,
|
|
675
|
+
const maxDesc = Math.max(0, cols2 - name.length - 4);
|
|
605
676
|
const desc = m.desc.length > maxDesc ? m.desc.slice(0, Math.max(0, maxDesc - 1)) + "\u2026" : m.desc;
|
|
606
|
-
out("\n" + (i ===
|
|
607
|
-
}
|
|
608
|
-
const text = opts.mask ? "*".repeat(
|
|
609
|
-
const
|
|
610
|
-
const
|
|
611
|
-
const
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
const physBefore = physRows.slice(0, curLogicalRow).reduce((a, b) => a + b, 0);
|
|
615
|
-
const curPhysRow = physBefore + Math.floor(curCol / cols);
|
|
616
|
-
const curPhysCol = curCol % cols;
|
|
617
|
-
const lastDrawnRow = totalInputPhys - 1 + mm.length;
|
|
618
|
-
if (lastDrawnRow > curPhysRow) out(`\x1B[${lastDrawnRow - curPhysRow}A`);
|
|
619
|
-
out("\r" + (curPhysCol > 0 ? `\x1B[${curPhysCol}C` : "") + "\x1B[?25h");
|
|
677
|
+
out("\n" + (i === sel2 ? color.green("\u203A " + name) + " " + color.dim(desc) : color.dim(" " + name + " " + desc)));
|
|
678
|
+
}
|
|
679
|
+
const text = opts.mask ? "*".repeat(buf2.length) : buf2;
|
|
680
|
+
const before = opts.mask ? text : buf2.slice(0, cur2);
|
|
681
|
+
const { totalPhys, curPhysRow, curPhysCol } = inputLayout(text, before, promptW, cols2);
|
|
682
|
+
const lastDrawnRow = totalPhys - 1 + mm.length;
|
|
683
|
+
if (lastDrawnRow > curPhysRow) out(ansi.up(lastDrawnRow - curPhysRow));
|
|
684
|
+
out(ansi.cr + (curPhysCol > 0 ? ansi.forward(curPhysCol) : "") + ansi.showCursor);
|
|
620
685
|
lastCurRow = curPhysRow;
|
|
621
686
|
}
|
|
622
687
|
function finish(result) {
|
|
623
688
|
restore();
|
|
624
|
-
if (
|
|
625
|
-
clearTimeout(
|
|
626
|
-
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
out(
|
|
630
|
-
if (lastCurRow > 0) out(
|
|
631
|
-
out(
|
|
689
|
+
if (burstTimer2) {
|
|
690
|
+
clearTimeout(burstTimer2);
|
|
691
|
+
burstTimer2 = null;
|
|
692
|
+
}
|
|
693
|
+
pendingRender2 = false;
|
|
694
|
+
out(ansi.hideCursor);
|
|
695
|
+
if (lastCurRow > 0) out(ansi.up(lastCurRow));
|
|
696
|
+
out(ansi.cr + ansi.clearToEnd);
|
|
632
697
|
drawBlock();
|
|
633
698
|
out("\n");
|
|
634
699
|
if (result.type === "line" && result.value.trim() && !opts.mask) history.push(result.value);
|
|
635
700
|
resolve2(result);
|
|
636
701
|
}
|
|
637
|
-
function
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
702
|
+
function insert2(s) {
|
|
703
|
+
buf2 = buf2.slice(0, cur2) + s + buf2.slice(cur2);
|
|
704
|
+
cur2 += s.length;
|
|
705
|
+
menuHidden2 = false;
|
|
706
|
+
sel2 = 0;
|
|
707
|
+
pendingRender2 = true;
|
|
643
708
|
}
|
|
644
|
-
function
|
|
709
|
+
function onKey2(str, key) {
|
|
645
710
|
const mm = menu();
|
|
646
|
-
|
|
647
|
-
if (
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
if (
|
|
652
|
-
|
|
653
|
-
|
|
711
|
+
burstLen2++;
|
|
712
|
+
if (burstTimer2) clearTimeout(burstTimer2);
|
|
713
|
+
burstTimer2 = setTimeout(() => {
|
|
714
|
+
burstTimer2 = null;
|
|
715
|
+
burstLen2 = 0;
|
|
716
|
+
if (pendingRender2) {
|
|
717
|
+
pendingRender2 = false;
|
|
718
|
+
render2();
|
|
654
719
|
}
|
|
655
|
-
},
|
|
720
|
+
}, BURST_IDLE_MS2);
|
|
656
721
|
if (isEnter(key)) {
|
|
657
|
-
if (key?.shift || key?.meta ||
|
|
658
|
-
|
|
722
|
+
if (key?.shift || key?.meta || burstLen2 > 1) {
|
|
723
|
+
insert2("\n");
|
|
659
724
|
return;
|
|
660
725
|
}
|
|
661
|
-
return finish({ type: "line", value:
|
|
726
|
+
return finish({ type: "line", value: buf2 });
|
|
662
727
|
}
|
|
663
728
|
if (key?.ctrl && key.name === "c") {
|
|
664
|
-
if (
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
729
|
+
if (buf2) {
|
|
730
|
+
buf2 = "";
|
|
731
|
+
cur2 = 0;
|
|
732
|
+
render2();
|
|
668
733
|
} else finish({ type: "quit" });
|
|
669
734
|
return;
|
|
670
735
|
}
|
|
671
736
|
if (key?.ctrl && key.name === "d") {
|
|
672
|
-
if (!
|
|
737
|
+
if (!buf2) finish({ type: "eof" });
|
|
673
738
|
return;
|
|
674
739
|
}
|
|
675
740
|
if (key?.name === "tab" && key.shift) {
|
|
676
741
|
opts.onShiftTab?.();
|
|
677
|
-
|
|
742
|
+
render2();
|
|
678
743
|
return;
|
|
679
744
|
}
|
|
680
745
|
if (key?.name === "tab") {
|
|
681
746
|
if (mm.length) {
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
747
|
+
buf2 = mm[sel2].name + " ";
|
|
748
|
+
cur2 = buf2.length;
|
|
749
|
+
menuHidden2 = false;
|
|
750
|
+
render2();
|
|
686
751
|
}
|
|
687
752
|
return;
|
|
688
753
|
}
|
|
689
754
|
if (key?.name === "up") {
|
|
690
755
|
if (mm.length) {
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
} else if (
|
|
756
|
+
sel2 = (sel2 - 1 + mm.length) % mm.length;
|
|
757
|
+
render2();
|
|
758
|
+
} else if (buf2.includes("\n")) moveVert(-1);
|
|
694
759
|
else histPrev();
|
|
695
760
|
return;
|
|
696
761
|
}
|
|
697
762
|
if (key?.name === "down") {
|
|
698
763
|
if (mm.length) {
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
} else if (
|
|
764
|
+
sel2 = (sel2 + 1) % mm.length;
|
|
765
|
+
render2();
|
|
766
|
+
} else if (buf2.includes("\n")) moveVert(1);
|
|
702
767
|
else histNext();
|
|
703
768
|
return;
|
|
704
769
|
}
|
|
705
770
|
if (key?.name === "left") {
|
|
706
|
-
if (
|
|
707
|
-
|
|
771
|
+
if (cur2 > 0) cur2--;
|
|
772
|
+
render2();
|
|
708
773
|
return;
|
|
709
774
|
}
|
|
710
775
|
if (key?.name === "right") {
|
|
711
|
-
if (
|
|
712
|
-
|
|
776
|
+
if (cur2 < buf2.length) cur2++;
|
|
777
|
+
render2();
|
|
713
778
|
return;
|
|
714
779
|
}
|
|
715
780
|
if (key?.name === "home" || key?.ctrl && key.name === "a") {
|
|
716
|
-
|
|
717
|
-
|
|
781
|
+
cur2 = 0;
|
|
782
|
+
render2();
|
|
718
783
|
return;
|
|
719
784
|
}
|
|
720
785
|
if (key?.name === "end" || key?.ctrl && key.name === "e") {
|
|
721
|
-
|
|
722
|
-
|
|
786
|
+
cur2 = buf2.length;
|
|
787
|
+
render2();
|
|
723
788
|
return;
|
|
724
789
|
}
|
|
725
790
|
if (key?.ctrl && key.name === "u") {
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
791
|
+
buf2 = buf2.slice(cur2);
|
|
792
|
+
cur2 = 0;
|
|
793
|
+
menuHidden2 = false;
|
|
794
|
+
render2();
|
|
730
795
|
return;
|
|
731
796
|
}
|
|
732
797
|
if (key?.name === "backspace") {
|
|
733
|
-
if (
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
798
|
+
if (cur2 > 0) {
|
|
799
|
+
buf2 = buf2.slice(0, cur2 - 1) + buf2.slice(cur2);
|
|
800
|
+
cur2--;
|
|
801
|
+
menuHidden2 = false;
|
|
802
|
+
render2();
|
|
738
803
|
}
|
|
739
804
|
return;
|
|
740
805
|
}
|
|
741
806
|
if (key?.name === "delete") {
|
|
742
|
-
if (
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
807
|
+
if (cur2 < buf2.length) {
|
|
808
|
+
buf2 = buf2.slice(0, cur2) + buf2.slice(cur2 + 1);
|
|
809
|
+
menuHidden2 = false;
|
|
810
|
+
render2();
|
|
746
811
|
}
|
|
747
812
|
return;
|
|
748
813
|
}
|
|
749
814
|
if (key?.name === "escape") {
|
|
750
815
|
if (mm.length) {
|
|
751
|
-
|
|
752
|
-
|
|
816
|
+
menuHidden2 = true;
|
|
817
|
+
render2();
|
|
753
818
|
}
|
|
754
819
|
return;
|
|
755
820
|
}
|
|
756
821
|
if (str && !key?.ctrl && !key?.meta && [...str].length === 1) {
|
|
757
|
-
if (isPrintableCodePoint(str.codePointAt(0)))
|
|
822
|
+
if (isPrintableCodePoint(str.codePointAt(0))) insert2(str);
|
|
758
823
|
}
|
|
759
824
|
}
|
|
760
825
|
function moveVert(dir) {
|
|
761
|
-
const
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
let idx = 0;
|
|
766
|
-
for (let i = 0; i < target; i++) idx += lines[i].length + 1;
|
|
767
|
-
cur = idx + Math.min(col, lines[target].length);
|
|
768
|
-
render();
|
|
826
|
+
const idx = moveVertIndex(buf2, cur2, dir);
|
|
827
|
+
if (idx === null) return;
|
|
828
|
+
cur2 = idx;
|
|
829
|
+
render2();
|
|
769
830
|
}
|
|
770
831
|
function histPrev() {
|
|
771
832
|
if (history.length === 0) return;
|
|
772
833
|
hist = Math.max(0, hist - 1);
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
834
|
+
buf2 = history[hist] ?? "";
|
|
835
|
+
cur2 = buf2.length;
|
|
836
|
+
render2();
|
|
776
837
|
}
|
|
777
838
|
function histNext() {
|
|
778
839
|
if (hist >= history.length) return;
|
|
779
840
|
hist += 1;
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
841
|
+
buf2 = hist === history.length ? "" : history[hist];
|
|
842
|
+
cur2 = buf2.length;
|
|
843
|
+
render2();
|
|
783
844
|
}
|
|
784
|
-
const restore = pushKeyHandler(
|
|
785
|
-
|
|
845
|
+
const restore = pushKeyHandler(onKey2);
|
|
846
|
+
render2();
|
|
786
847
|
});
|
|
787
848
|
}
|
|
788
849
|
function readChoice(prompt) {
|
|
@@ -803,36 +864,36 @@ function readChoice(prompt) {
|
|
|
803
864
|
function selectMenu(opts) {
|
|
804
865
|
if (!process.stdin.isTTY || opts.items.length === 0) return Promise.resolve(null);
|
|
805
866
|
return new Promise((resolve2) => {
|
|
806
|
-
let
|
|
867
|
+
let sel2 = Math.max(0, Math.min(opts.initial ?? 0, opts.items.length - 1));
|
|
807
868
|
let drawn = 0;
|
|
808
|
-
function
|
|
809
|
-
out(
|
|
810
|
-
if (drawn > 0) out(
|
|
811
|
-
out(
|
|
869
|
+
function render2() {
|
|
870
|
+
out(ansi.hideCursor);
|
|
871
|
+
if (drawn > 0) out(ansi.up(drawn));
|
|
872
|
+
out(ansi.cr + ansi.clearToEnd);
|
|
812
873
|
out(color.dim(opts.title) + "\n");
|
|
813
874
|
opts.items.forEach((it, i) => {
|
|
814
|
-
const row = i ===
|
|
875
|
+
const row = i === sel2 ? color.green("\u203A " + it.label) : " " + it.label;
|
|
815
876
|
out(row + (it.hint ? color.dim(" " + it.hint) : "") + "\n");
|
|
816
877
|
});
|
|
817
878
|
drawn = opts.items.length + 1;
|
|
818
879
|
}
|
|
819
880
|
function finish(v) {
|
|
820
881
|
restore();
|
|
821
|
-
if (drawn > 0) out(
|
|
822
|
-
out(
|
|
882
|
+
if (drawn > 0) out(ansi.up(drawn) + ansi.cr + ansi.clearToEnd);
|
|
883
|
+
out(ansi.showCursor);
|
|
823
884
|
resolve2(v);
|
|
824
885
|
}
|
|
825
886
|
const restore = pushKeyHandler((_str, key) => {
|
|
826
887
|
if (key?.name === "up") {
|
|
827
|
-
|
|
828
|
-
|
|
888
|
+
sel2 = (sel2 - 1 + opts.items.length) % opts.items.length;
|
|
889
|
+
render2();
|
|
829
890
|
} else if (key?.name === "down") {
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
} else if (isEnter(key)) finish(opts.items[
|
|
891
|
+
sel2 = (sel2 + 1) % opts.items.length;
|
|
892
|
+
render2();
|
|
893
|
+
} else if (isEnter(key)) finish(opts.items[sel2].value);
|
|
833
894
|
else if (key?.name === "escape" || key?.name === "q" || key?.ctrl && key.name === "c") finish(null);
|
|
834
895
|
});
|
|
835
|
-
|
|
896
|
+
render2();
|
|
836
897
|
});
|
|
837
898
|
}
|
|
838
899
|
|
|
@@ -842,46 +903,46 @@ function renderFileBox(path, startLine, lines, hasMore) {
|
|
|
842
903
|
const p = stripControl(path);
|
|
843
904
|
const numW = String(startLine + Math.max(0, lines.length - 1)).length;
|
|
844
905
|
const header = startLine === 1 && !hasMore ? `${lines.length} line${lines.length === 1 ? "" : "s"}` : `lines ${startLine}\u2013${startLine + lines.length - 1}${hasMore ? " (+ more)" : ""}`;
|
|
845
|
-
const
|
|
846
|
-
lines.forEach((l, i) =>
|
|
847
|
-
if (hasMore)
|
|
848
|
-
|
|
849
|
-
return
|
|
906
|
+
const out3 = [color.dim("\u256D\u2500 ") + color.bold(p) + color.dim(` ${header}`)];
|
|
907
|
+
lines.forEach((l, i) => out3.push(color.dim("\u2502 ") + color.dim(String(startLine + i).padStart(numW)) + " " + stripControl(l)));
|
|
908
|
+
if (hasMore) out3.push(color.dim("\u2502 ") + color.dim(`\u2026 more (show ${p} offset ${startLine + lines.length})`));
|
|
909
|
+
out3.push(color.dim("\u2570\u2500"));
|
|
910
|
+
return out3.join("\n") + "\n";
|
|
850
911
|
}
|
|
851
912
|
function renderListing(path, names) {
|
|
852
913
|
const safe = names.map(stripControl);
|
|
853
|
-
const
|
|
914
|
+
const out3 = [color.dim("\u256D\u2500 ") + color.bold(stripControl(path)) + color.dim(` ${safe.length} entr${safe.length === 1 ? "y" : "ies"}`)];
|
|
854
915
|
if (safe.length === 0) {
|
|
855
|
-
|
|
916
|
+
out3.push(color.dim("\u2502 ") + color.dim("(empty)"));
|
|
856
917
|
} else {
|
|
857
918
|
const avail = BOX_W() - 2;
|
|
858
919
|
const colW = Math.min(Math.max(...safe.map((n) => n.length)) + 2, 40);
|
|
859
|
-
const
|
|
860
|
-
const rows2 = Math.ceil(safe.length /
|
|
920
|
+
const cols2 = Math.max(1, Math.floor(avail / colW));
|
|
921
|
+
const rows2 = Math.ceil(safe.length / cols2);
|
|
861
922
|
for (let r = 0; r < rows2; r++) {
|
|
862
923
|
let line = "";
|
|
863
|
-
for (let c = 0; c <
|
|
924
|
+
for (let c = 0; c < cols2; c++) {
|
|
864
925
|
const idx = c * rows2 + r;
|
|
865
926
|
if (idx >= safe.length) continue;
|
|
866
927
|
const n = safe[idx];
|
|
867
928
|
line += (n.endsWith("/") ? color.cyan(n) : n) + " ".repeat(Math.max(1, colW - n.length));
|
|
868
929
|
}
|
|
869
|
-
|
|
930
|
+
out3.push(color.dim("\u2502 ") + line.replace(/\s+$/, ""));
|
|
870
931
|
}
|
|
871
932
|
}
|
|
872
|
-
|
|
873
|
-
return
|
|
933
|
+
out3.push(color.dim("\u2570\u2500"));
|
|
934
|
+
return out3.join("\n") + "\n";
|
|
874
935
|
}
|
|
875
936
|
function renderTree(path, items, truncated) {
|
|
876
937
|
const dirs = items.filter((it) => it.isDir).length;
|
|
877
|
-
const
|
|
938
|
+
const out3 = [color.dim("\u256D\u2500 ") + color.bold(stripControl(path)) + color.dim(` ${items.length} entries \xB7 ${dirs} folders`)];
|
|
878
939
|
for (const it of items) {
|
|
879
940
|
const nm = stripControl(it.name);
|
|
880
|
-
|
|
941
|
+
out3.push(color.dim("\u2502 ") + color.dim(it.prefix) + (it.isDir ? color.cyan(nm) : nm));
|
|
881
942
|
}
|
|
882
|
-
if (truncated)
|
|
883
|
-
|
|
884
|
-
return
|
|
943
|
+
if (truncated) out3.push(color.dim("\u2502 ") + color.dim("\u2026 (truncated)"));
|
|
944
|
+
out3.push(color.dim("\u2570\u2500"));
|
|
945
|
+
return out3.join("\n") + "\n";
|
|
885
946
|
}
|
|
886
947
|
var SHOW_MARK = "";
|
|
887
948
|
var SHOW_KINDS = ["file", "dir", "tree"];
|
|
@@ -990,14 +1051,14 @@ function renderTable(rows2) {
|
|
|
990
1051
|
})
|
|
991
1052
|
);
|
|
992
1053
|
const width = Array.from({ length: ncol }, (_, c) => Math.max(...styled.map((r) => visLen(r[c]))));
|
|
993
|
-
const
|
|
1054
|
+
const out3 = styled.map((r) => {
|
|
994
1055
|
const cells = r.map((cell, c) => cell + " ".repeat(Math.max(0, width[c] - visLen(cell))));
|
|
995
1056
|
return " " + cells.join(color.dim(" \u2502 "));
|
|
996
1057
|
});
|
|
997
|
-
return
|
|
1058
|
+
return out3.join("\n") + "\n";
|
|
998
1059
|
}
|
|
999
1060
|
function createMarkdownStream(write) {
|
|
1000
|
-
let
|
|
1061
|
+
let buf2 = "";
|
|
1001
1062
|
let scanFrom = 0;
|
|
1002
1063
|
let inCode = false;
|
|
1003
1064
|
let first = true;
|
|
@@ -1036,19 +1097,19 @@ function createMarkdownStream(write) {
|
|
|
1036
1097
|
}
|
|
1037
1098
|
return {
|
|
1038
1099
|
push(text) {
|
|
1039
|
-
|
|
1100
|
+
buf2 += text;
|
|
1040
1101
|
let i;
|
|
1041
|
-
while ((i =
|
|
1042
|
-
emit(
|
|
1043
|
-
|
|
1102
|
+
while ((i = buf2.indexOf("\n", scanFrom)) >= 0) {
|
|
1103
|
+
emit(buf2.slice(0, i));
|
|
1104
|
+
buf2 = buf2.slice(i + 1);
|
|
1044
1105
|
scanFrom = 0;
|
|
1045
1106
|
}
|
|
1046
|
-
scanFrom =
|
|
1107
|
+
scanFrom = buf2.length;
|
|
1047
1108
|
},
|
|
1048
1109
|
end() {
|
|
1049
|
-
if (
|
|
1050
|
-
emit(
|
|
1051
|
-
|
|
1110
|
+
if (buf2) {
|
|
1111
|
+
emit(buf2);
|
|
1112
|
+
buf2 = "";
|
|
1052
1113
|
}
|
|
1053
1114
|
flushTable();
|
|
1054
1115
|
if (inCode) {
|
|
@@ -1249,7 +1310,7 @@ import { homedir as homedir3 } from "node:os";
|
|
|
1249
1310
|
import { basename } from "node:path";
|
|
1250
1311
|
function pathGuard(args) {
|
|
1251
1312
|
const { abs, inRoot } = resolveInRoot(String(args.path ?? "."));
|
|
1252
|
-
return inRoot ? {} : { needsApproval: true, reason: `path is outside the project root: ${abs}` };
|
|
1313
|
+
return inRoot ? {} : { needsApproval: true, reason: `path is outside the project root: ${abs}`, cacheKey: `path:${abs}` };
|
|
1253
1314
|
}
|
|
1254
1315
|
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;
|
|
1255
1316
|
function isSecretPath(userPath) {
|
|
@@ -1470,13 +1531,13 @@ async function atomicWrite(abs, content) {
|
|
|
1470
1531
|
}
|
|
1471
1532
|
var READ_PREFIX = /^ *\d+ {2}/;
|
|
1472
1533
|
function allIndexOf(hay, needle) {
|
|
1473
|
-
const
|
|
1534
|
+
const out3 = [];
|
|
1474
1535
|
let i = hay.indexOf(needle);
|
|
1475
1536
|
while (i !== -1) {
|
|
1476
|
-
|
|
1537
|
+
out3.push(i);
|
|
1477
1538
|
i = hay.indexOf(needle, i + 1);
|
|
1478
1539
|
}
|
|
1479
|
-
return
|
|
1540
|
+
return out3;
|
|
1480
1541
|
}
|
|
1481
1542
|
function stripReadPrefix(text) {
|
|
1482
1543
|
const lines = text.split("\n");
|
|
@@ -1971,11 +2032,11 @@ ${res.closest}` : `Re-read the file and copy the exact text (including whitespac
|
|
|
1971
2032
|
${stderr}` : "") || "(no output)";
|
|
1972
2033
|
} catch (err) {
|
|
1973
2034
|
const e = err;
|
|
1974
|
-
const
|
|
2035
|
+
const out3 = `${e.stdout ?? ""}${e.stderr ? `
|
|
1975
2036
|
[stderr]
|
|
1976
2037
|
${e.stderr}` : ""}`.trim();
|
|
1977
|
-
return `Error: command failed${
|
|
1978
|
-
${
|
|
2038
|
+
return `Error: command failed${out3 ? `:
|
|
2039
|
+
${out3}` : `: ${String(e.message ?? err)}`}`;
|
|
1979
2040
|
}
|
|
1980
2041
|
}
|
|
1981
2042
|
},
|
|
@@ -2220,14 +2281,14 @@ function validateArgs(tool, args) {
|
|
|
2220
2281
|
async function runVerify(signal) {
|
|
2221
2282
|
try {
|
|
2222
2283
|
const { stdout, stderr } = await runShell(config.verifyCommand, { timeout: config.verifyTimeoutMs, maxBuffer: config.maxToolBuffer, signal });
|
|
2223
|
-
const
|
|
2224
|
-
return `passed \u2713${
|
|
2225
|
-
${
|
|
2284
|
+
const out3 = `${stdout}${stderr}`.trim();
|
|
2285
|
+
return `passed \u2713${out3 ? `
|
|
2286
|
+
${out3.slice(-800)}` : ""}`;
|
|
2226
2287
|
} catch (err) {
|
|
2227
2288
|
const e = err;
|
|
2228
|
-
const
|
|
2289
|
+
const out3 = `${e.stdout ?? ""}${e.stderr ?? ""}`.trim() || String(e.message ?? err);
|
|
2229
2290
|
return `FAILED \u2717
|
|
2230
|
-
${
|
|
2291
|
+
${out3.slice(-1500)}`;
|
|
2231
2292
|
}
|
|
2232
2293
|
}
|
|
2233
2294
|
|
|
@@ -2260,12 +2321,12 @@ function parseSSELine(line) {
|
|
|
2260
2321
|
}
|
|
2261
2322
|
const delta = parsed.choices?.[0]?.delta;
|
|
2262
2323
|
if (!delta) return null;
|
|
2263
|
-
const
|
|
2264
|
-
if (delta.content)
|
|
2265
|
-
if (delta.tool_calls)
|
|
2266
|
-
if (typeof delta.reasoning === "string" && delta.reasoning)
|
|
2267
|
-
if (Array.isArray(delta.reasoning_details) && delta.reasoning_details.length)
|
|
2268
|
-
return
|
|
2324
|
+
const out3 = {};
|
|
2325
|
+
if (delta.content) out3.content = delta.content;
|
|
2326
|
+
if (delta.tool_calls) out3.toolCalls = delta.tool_calls;
|
|
2327
|
+
if (typeof delta.reasoning === "string" && delta.reasoning) out3.reasoning = delta.reasoning;
|
|
2328
|
+
if (Array.isArray(delta.reasoning_details) && delta.reasoning_details.length) out3.reasoningDetails = delta.reasoning_details;
|
|
2329
|
+
return out3;
|
|
2269
2330
|
}
|
|
2270
2331
|
function buildRequestBody(opts) {
|
|
2271
2332
|
const { model, messages, includeTools, effort, reasoningSupported, extra, tools } = opts;
|
|
@@ -2388,7 +2449,7 @@ async function callModel(messages, includeTools = true, signal, opts) {
|
|
|
2388
2449
|
stopSpinner();
|
|
2389
2450
|
if (!quiet) {
|
|
2390
2451
|
if (!printedText) {
|
|
2391
|
-
process.stdout.write("\n" + color.cyan("bee: "));
|
|
2452
|
+
process.stdout.write((printedReasoning ? "\n\n" : "\n") + color.cyan("bee: "));
|
|
2392
2453
|
printedText = true;
|
|
2393
2454
|
}
|
|
2394
2455
|
const safe = stripControl(ev.content);
|
|
@@ -2671,7 +2732,7 @@ async function saveSession(messages) {
|
|
|
2671
2732
|
}
|
|
2672
2733
|
function sanitizeSession(raw) {
|
|
2673
2734
|
if (!Array.isArray(raw)) return null;
|
|
2674
|
-
const
|
|
2735
|
+
const out3 = [];
|
|
2675
2736
|
for (const m of raw) {
|
|
2676
2737
|
if (!m || typeof m !== "object") return null;
|
|
2677
2738
|
const role = m.role;
|
|
@@ -2684,9 +2745,9 @@ function sanitizeSession(raw) {
|
|
|
2684
2745
|
if (Array.isArray(tc)) msg.tool_calls = tc;
|
|
2685
2746
|
const tcid = m.tool_call_id;
|
|
2686
2747
|
if (typeof tcid === "string") msg.tool_call_id = tcid;
|
|
2687
|
-
|
|
2748
|
+
out3.push(msg);
|
|
2688
2749
|
}
|
|
2689
|
-
return dropIncompleteToolTail(
|
|
2750
|
+
return dropIncompleteToolTail(out3);
|
|
2690
2751
|
}
|
|
2691
2752
|
function dropIncompleteToolTail(messages) {
|
|
2692
2753
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
@@ -2729,15 +2790,15 @@ async function loadLatestSession() {
|
|
|
2729
2790
|
async function listSessions() {
|
|
2730
2791
|
try {
|
|
2731
2792
|
const files = (await readdir2(sessionsDir())).filter((f) => f.endsWith(".json"));
|
|
2732
|
-
const
|
|
2793
|
+
const out3 = [];
|
|
2733
2794
|
for (const f of files) {
|
|
2734
2795
|
const msgs = await readSession(f);
|
|
2735
2796
|
if (!msgs || !msgs.length) continue;
|
|
2736
2797
|
const firstUser = msgs.find((m) => m.role === "user");
|
|
2737
2798
|
const preview = (firstUser?.content ?? "").replace(/\s+/g, " ").trim().slice(0, 60);
|
|
2738
|
-
|
|
2799
|
+
out3.push({ file: f, when: Number(f.replace(".json", "")) || 0, count: msgs.length, preview });
|
|
2739
2800
|
}
|
|
2740
|
-
return
|
|
2801
|
+
return out3.sort((a, b) => b.when - a.when);
|
|
2741
2802
|
} catch {
|
|
2742
2803
|
return [];
|
|
2743
2804
|
}
|
|
@@ -2796,7 +2857,7 @@ var SYSTEM_PROMPT = `You are beecork, a coding assistant working in a terminal o
|
|
|
2796
2857
|
# Safety
|
|
2797
2858
|
- Be careful with anything that deletes or overwrites. Don't do destructive things unless the user clearly asked.
|
|
2798
2859
|
- Don't commit or push to git, publish, deploy, or take other outward or hard-to-undo actions unless the user explicitly asked.`;
|
|
2799
|
-
async function askApproval(ask, call, reason) {
|
|
2860
|
+
async function askApproval(ask, call, reason, offerAlways = true) {
|
|
2800
2861
|
let args = {};
|
|
2801
2862
|
try {
|
|
2802
2863
|
args = JSON.parse(call.function.arguments);
|
|
@@ -2818,8 +2879,9 @@ async function askApproval(ask, call, reason) {
|
|
|
2818
2879
|
} else {
|
|
2819
2880
|
console.log(color.yellow(` ${stripControl(call.function.arguments)}`));
|
|
2820
2881
|
}
|
|
2821
|
-
const
|
|
2822
|
-
|
|
2882
|
+
const prompt = offerAlways ? " allow? [y]es / [n]o / [a]lways: " : " allow? [y]es / [n]o: ";
|
|
2883
|
+
const answer = (await ask(color.yellow(prompt))).trim().toLowerCase();
|
|
2884
|
+
if (offerAlways && (answer === "a" || answer === "always")) return "always";
|
|
2823
2885
|
if (answer === "y" || answer === "yes") return "once";
|
|
2824
2886
|
return "deny";
|
|
2825
2887
|
}
|
|
@@ -2830,8 +2892,9 @@ function decideApproval(tool, args, ctx) {
|
|
|
2830
2892
|
if (ctx.dangerouslySkip) return { action: "run" };
|
|
2831
2893
|
const guard = tool?.guard?.(args);
|
|
2832
2894
|
if (guard?.needsApproval) {
|
|
2895
|
+
if (guard.cacheKey && ctx.approvedGuardKeys?.has(guard.cacheKey)) return { action: "run" };
|
|
2833
2896
|
if (ctx.autoApprove) return { action: "deny", kind: "headless", reason: guard.reason ?? "blocked" };
|
|
2834
|
-
return { action: "ask", cacheable: false, reason: guard.reason };
|
|
2897
|
+
return guard.cacheKey ? { action: "ask", cacheable: true, reason: guard.reason, guardKey: guard.cacheKey } : { action: "ask", cacheable: false, reason: guard.reason };
|
|
2835
2898
|
}
|
|
2836
2899
|
if (tool?.needsApproval && !ctx.autoApprove && ctx.mode !== "auto") {
|
|
2837
2900
|
if (tool.alwaysAsk) return { action: "ask", cacheable: false };
|
|
@@ -2867,7 +2930,7 @@ async function handleAskUser(args) {
|
|
|
2867
2930
|
return askUserMessage(question, choice, true);
|
|
2868
2931
|
}
|
|
2869
2932
|
async function handleToolCall(call, messages, step, deps) {
|
|
2870
|
-
const { approvedTools, callCounts, ask, signal } = deps;
|
|
2933
|
+
const { approvedTools, approvedGuardKeys, callCounts, ask, signal } = deps;
|
|
2871
2934
|
const pushTool = (content) => messages.push({ role: "tool", tool_call_id: call.id, content });
|
|
2872
2935
|
const sig = `${call.function.name}:${call.function.arguments}`;
|
|
2873
2936
|
const seen = (callCounts.get(sig) ?? 0) + 1;
|
|
@@ -2892,6 +2955,7 @@ async function handleToolCall(call, messages, step, deps) {
|
|
|
2892
2955
|
mode: state.mode,
|
|
2893
2956
|
autoApprove: config.autoApprove,
|
|
2894
2957
|
approvedTools,
|
|
2958
|
+
approvedGuardKeys,
|
|
2895
2959
|
toolName: call.function.name,
|
|
2896
2960
|
dangerouslySkip: config.dangerouslySkipPermissions
|
|
2897
2961
|
});
|
|
@@ -2906,15 +2970,19 @@ async function handleToolCall(call, messages, step, deps) {
|
|
|
2906
2970
|
return;
|
|
2907
2971
|
}
|
|
2908
2972
|
if (decision.action === "ask") {
|
|
2909
|
-
const answer = await askApproval(ask, call, decision.
|
|
2973
|
+
const answer = await askApproval(ask, call, decision.reason, decision.cacheable);
|
|
2910
2974
|
if (answer === "deny") {
|
|
2911
2975
|
console.log(color.red(" \u21B3 denied") + "\n");
|
|
2912
|
-
pushTool(decision.
|
|
2976
|
+
pushTool(decision.reason ? `The user DENIED this (${decision.reason}). Do not retry, and do not route around it with run_bash/cat or another tool.` : "The user DENIED permission to run this tool. Do not retry it.");
|
|
2913
2977
|
return;
|
|
2914
2978
|
}
|
|
2915
|
-
if (
|
|
2916
|
-
|
|
2917
|
-
|
|
2979
|
+
if (answer === "always" && decision.cacheable) {
|
|
2980
|
+
if (decision.guardKey) {
|
|
2981
|
+
approvedGuardKeys.add(decision.guardKey);
|
|
2982
|
+
} else {
|
|
2983
|
+
approvedTools.add(call.function.name);
|
|
2984
|
+
void addProjectApproval(call.function.name);
|
|
2985
|
+
}
|
|
2918
2986
|
}
|
|
2919
2987
|
}
|
|
2920
2988
|
if (config.traceFile) trace.push({ tool: call.function.name, args: call.function.arguments, step });
|
|
@@ -2970,13 +3038,13 @@ ${content}` }];
|
|
|
2970
3038
|
}
|
|
2971
3039
|
return [...messages, { role: "user", content }];
|
|
2972
3040
|
}
|
|
2973
|
-
async function runTurn(messages, userInput, ask, approvedTools, signal,
|
|
3041
|
+
async function runTurn(messages, userInput, ask, approvedTools, approvedGuardKeys, signal, steering2) {
|
|
2974
3042
|
messages.push({ role: "user", content: userInput });
|
|
2975
3043
|
const snapshot = messages.slice();
|
|
2976
3044
|
try {
|
|
2977
3045
|
let answered = false;
|
|
2978
3046
|
const callCounts = /* @__PURE__ */ new Map();
|
|
2979
|
-
const deps = { approvedTools, callCounts, ask, signal };
|
|
3047
|
+
const deps = { approvedTools, approvedGuardKeys, callCounts, ask, signal };
|
|
2980
3048
|
for (let step = 0; step < config.maxSteps && !answered && !signal?.aborted; step++) {
|
|
2981
3049
|
try {
|
|
2982
3050
|
messages = await compactIfNeeded(messages, signal);
|
|
@@ -2984,7 +3052,7 @@ async function runTurn(messages, userInput, ask, approvedTools, signal, steering
|
|
|
2984
3052
|
console.error(color.red(`
|
|
2985
3053
|
[compaction failed: ${err.message} \u2014 continuing]`) + "\n");
|
|
2986
3054
|
}
|
|
2987
|
-
if (
|
|
3055
|
+
if (steering2?.length) messages = applySteering(messages, steering2.splice(0));
|
|
2988
3056
|
const message = await callModel(messages, true, signal);
|
|
2989
3057
|
messages.push(message);
|
|
2990
3058
|
if (!hasContent2(message)) {
|
|
@@ -2998,7 +3066,7 @@ async function runTurn(messages, userInput, ask, approvedTools, signal, steering
|
|
|
2998
3066
|
await handleToolCall(call, messages, step, deps);
|
|
2999
3067
|
}
|
|
3000
3068
|
} else {
|
|
3001
|
-
answered = !
|
|
3069
|
+
answered = !steering2?.length;
|
|
3002
3070
|
}
|
|
3003
3071
|
}
|
|
3004
3072
|
if (signal?.aborted) {
|
|
@@ -3070,62 +3138,395 @@ async function runtimeContext() {
|
|
|
3070
3138
|
});
|
|
3071
3139
|
}
|
|
3072
3140
|
|
|
3073
|
-
// src/
|
|
3141
|
+
// src/chrome.ts
|
|
3074
3142
|
import { execFile as execFile2 } from "node:child_process";
|
|
3143
|
+
var out2 = (s) => process.stdout.write(s);
|
|
3144
|
+
var rows = () => process.stdout.rows ?? 24;
|
|
3145
|
+
var cols = () => process.stdout.columns ?? 80;
|
|
3075
3146
|
var active2 = false;
|
|
3076
|
-
var
|
|
3077
|
-
var
|
|
3147
|
+
var turnActive = false;
|
|
3148
|
+
var buf = "";
|
|
3149
|
+
var cur = 0;
|
|
3150
|
+
var allItems = [];
|
|
3151
|
+
var sel = 0;
|
|
3152
|
+
var menuHidden = false;
|
|
3153
|
+
var menuH = 0;
|
|
3154
|
+
var prevMenuH = 0;
|
|
3155
|
+
var lastRegionB = -1;
|
|
3156
|
+
var pickItems = null;
|
|
3157
|
+
var pickSel = 0;
|
|
3158
|
+
var pickTitle = "";
|
|
3159
|
+
var pickResolve = null;
|
|
3078
3160
|
var branch = "";
|
|
3079
|
-
var tokensOf =
|
|
3080
|
-
var
|
|
3161
|
+
var tokensOf = () => 0;
|
|
3162
|
+
var onResult = null;
|
|
3163
|
+
var onInterrupt = null;
|
|
3164
|
+
var steering = [];
|
|
3165
|
+
var restoreKeys = null;
|
|
3166
|
+
var gitTimer = null;
|
|
3167
|
+
var statusTimer = null;
|
|
3168
|
+
var lastRows = 0;
|
|
3169
|
+
var BURST_IDLE_MS = 8;
|
|
3170
|
+
var burstLen = 0;
|
|
3171
|
+
var burstTimer = null;
|
|
3172
|
+
var pendingRender = false;
|
|
3173
|
+
var statusRow = () => Math.max(1, rows());
|
|
3174
|
+
var borderBottomRow = () => Math.max(1, rows() - 1);
|
|
3175
|
+
var inputRow = () => Math.max(1, rows() - 2);
|
|
3176
|
+
var borderTopRow = () => Math.max(1, rows() - 3);
|
|
3177
|
+
var mark = () => color.green("\u203A ");
|
|
3178
|
+
var PW = 2;
|
|
3179
|
+
function modeSegment() {
|
|
3180
|
+
return state.mode === "auto" ? color.yellow("auto-approve") : state.mode === "readonly" ? color.cyan("read-only") : color.green("normal");
|
|
3181
|
+
}
|
|
3182
|
+
function statusText() {
|
|
3183
|
+
const model = state.model.split("/").pop() ?? state.model;
|
|
3184
|
+
const tok = tokensOf();
|
|
3185
|
+
const ctxK = Math.round(config.maxContextTokens / 1e3);
|
|
3186
|
+
const parts = [
|
|
3187
|
+
modeSegment(),
|
|
3188
|
+
color.cyan(model),
|
|
3189
|
+
color.dim(state.reasoningEffort),
|
|
3190
|
+
...branch ? [color.green(branch)] : [],
|
|
3191
|
+
color.dim(`~${Math.round(tok / 1e3)}k/${ctxK}k`)
|
|
3192
|
+
];
|
|
3193
|
+
const bg = runningTaskCount();
|
|
3194
|
+
if (bg > 0) parts.push(color.yellow(`${bg} task${bg === 1 ? "" : "s"}`));
|
|
3195
|
+
return parts.join(color.dim(" \xB7 "));
|
|
3196
|
+
}
|
|
3081
3197
|
function pollGit() {
|
|
3082
|
-
execFile2("git", ["rev-parse", "--abbrev-ref", "HEAD"], { timeout: 1500, windowsHide: true }, (err,
|
|
3198
|
+
execFile2("git", ["rev-parse", "--abbrev-ref", "HEAD"], { timeout: 1500, windowsHide: true }, (err, o) => {
|
|
3083
3199
|
if (err) {
|
|
3084
3200
|
branch = "";
|
|
3085
3201
|
return;
|
|
3086
3202
|
}
|
|
3087
|
-
const b = String(
|
|
3088
|
-
execFile2("git", ["status", "--porcelain"], { timeout: 1500, windowsHide: true }, (e2,
|
|
3089
|
-
branch = b + (!e2 && String(
|
|
3203
|
+
const b = String(o).trim();
|
|
3204
|
+
execFile2("git", ["status", "--porcelain"], { timeout: 1500, windowsHide: true }, (e2, o2) => {
|
|
3205
|
+
branch = b + (!e2 && String(o2).trim() ? "*" : "");
|
|
3090
3206
|
});
|
|
3091
3207
|
});
|
|
3092
3208
|
}
|
|
3093
|
-
function
|
|
3094
|
-
const
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
const bg = runningTaskCount();
|
|
3098
|
-
if (bg > 0) parts.push(`${bg} task${bg === 1 ? "" : "s"}`);
|
|
3099
|
-
return parts.join(" \xB7 ");
|
|
3209
|
+
function drawStatus() {
|
|
3210
|
+
const s = statusText();
|
|
3211
|
+
const vis = stripAnsi(s);
|
|
3212
|
+
out2(ansi.moveTo(statusRow()) + ansi.clearLine + (vis.length <= cols() ? s : stripAnsi(s).slice(0, cols() - 1)));
|
|
3100
3213
|
}
|
|
3101
|
-
function
|
|
3214
|
+
function drawBorder(row) {
|
|
3215
|
+
out2(ansi.moveTo(row) + ansi.clearLine + color.dim("\u2500".repeat(Math.max(1, cols()))));
|
|
3216
|
+
}
|
|
3217
|
+
var closePick = () => {
|
|
3218
|
+
pickItems = null;
|
|
3219
|
+
pickSel = 0;
|
|
3220
|
+
pickTitle = "";
|
|
3221
|
+
};
|
|
3222
|
+
function chromePick(items, initial = 0, title = "") {
|
|
3223
|
+
return new Promise((resolve2) => {
|
|
3224
|
+
if (!active2 || !items.length) {
|
|
3225
|
+
resolve2(null);
|
|
3226
|
+
return;
|
|
3227
|
+
}
|
|
3228
|
+
pickItems = items;
|
|
3229
|
+
pickSel = Math.min(Math.max(0, initial), items.length - 1);
|
|
3230
|
+
pickTitle = title;
|
|
3231
|
+
pickResolve = resolve2;
|
|
3232
|
+
render();
|
|
3233
|
+
});
|
|
3234
|
+
}
|
|
3235
|
+
var flat = (s) => s.replace(/\n/g, "\u23CE");
|
|
3236
|
+
function drawInput() {
|
|
3237
|
+
const disp = flat(buf);
|
|
3238
|
+
const start = windowStart(disp, cur, Math.max(1, cols() - PW));
|
|
3239
|
+
const shown = disp.slice(start);
|
|
3240
|
+
const ci = cur - start;
|
|
3241
|
+
let body;
|
|
3242
|
+
if (!buf) {
|
|
3243
|
+
body = ansi.reverse + " " + ansi.reverseOff + color.dim("type a message \xB7 / for commands \xB7 Shift+Tab mode");
|
|
3244
|
+
} else {
|
|
3245
|
+
const at = ci < shown.length ? shown[ci] : " ";
|
|
3246
|
+
body = shown.slice(0, ci) + ansi.reverse + at + ansi.reverseOff + (ci < shown.length ? shown.slice(ci + 1) : "");
|
|
3247
|
+
}
|
|
3248
|
+
out2(ansi.moveTo(inputRow()) + ansi.clearLine + mark() + body);
|
|
3249
|
+
}
|
|
3250
|
+
function currentMenu() {
|
|
3251
|
+
if (menuHidden || turnActive) return [];
|
|
3252
|
+
const m = buf.match(/^\/(\S*)$/);
|
|
3253
|
+
if (!m) return [];
|
|
3254
|
+
const pre = "/" + m[1];
|
|
3255
|
+
return allItems.filter((c) => c.name.startsWith(pre)).slice(0, 8);
|
|
3256
|
+
}
|
|
3257
|
+
function render() {
|
|
3102
3258
|
if (!active2) return;
|
|
3103
|
-
|
|
3259
|
+
const picker = pickItems;
|
|
3260
|
+
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 }));
|
|
3261
|
+
if (!picker && sel >= list.length) sel = Math.max(0, list.length - 1);
|
|
3262
|
+
menuH = list.length;
|
|
3263
|
+
out2(ansi.hideCursor + ansi.saveCursor);
|
|
3264
|
+
const regionB = Math.max(1, rows() - 4 - menuH);
|
|
3265
|
+
if (regionB !== lastRegionB) {
|
|
3266
|
+
out2(ansi.setRegion(1, regionB));
|
|
3267
|
+
lastRegionB = regionB;
|
|
3268
|
+
}
|
|
3269
|
+
for (let r = borderTopRow() - prevMenuH; r < borderTopRow() - menuH; r++) out2(ansi.moveTo(r) + ansi.clearLine);
|
|
3270
|
+
prevMenuH = menuH;
|
|
3271
|
+
const base = borderTopRow() - menuH;
|
|
3272
|
+
for (let i = 0; i < list.length; i++) {
|
|
3273
|
+
out2(ansi.moveTo(base + i) + ansi.clearLine + (list[i].on ? color.green("\u203A ") + list[i].text : color.dim(" ") + list[i].text));
|
|
3274
|
+
}
|
|
3275
|
+
drawBorder(borderTopRow());
|
|
3276
|
+
if (picker) out2(ansi.moveTo(inputRow()) + ansi.clearLine + mark() + color.dim(pickTitle || "\u2191/\u2193 to choose \xB7 Enter to select \xB7 Esc to cancel"));
|
|
3277
|
+
else drawInput();
|
|
3278
|
+
drawBorder(borderBottomRow());
|
|
3279
|
+
drawStatus();
|
|
3280
|
+
out2(ansi.restoreCursor);
|
|
3281
|
+
lastRows = rows();
|
|
3282
|
+
pendingRender = false;
|
|
3104
3283
|
}
|
|
3105
3284
|
function onResize() {
|
|
3106
3285
|
if (!active2) return;
|
|
3107
|
-
|
|
3108
|
-
|
|
3286
|
+
const newRows = rows();
|
|
3287
|
+
if (lastRows > 0 && newRows > lastRows) {
|
|
3288
|
+
out2(ansi.saveCursor);
|
|
3289
|
+
const oldTop = Math.max(1, lastRows - 3 - prevMenuH);
|
|
3290
|
+
for (let r = oldTop; r <= lastRows; r++) out2(ansi.moveTo(r) + ansi.clearLine);
|
|
3291
|
+
out2(ansi.restoreCursor);
|
|
3292
|
+
}
|
|
3293
|
+
lastRegionB = -1;
|
|
3294
|
+
render();
|
|
3295
|
+
}
|
|
3296
|
+
function refreshChrome() {
|
|
3297
|
+
if (!active2) return;
|
|
3298
|
+
out2(ansi.hideCursor + ansi.saveCursor);
|
|
3299
|
+
drawStatus();
|
|
3300
|
+
out2(ansi.restoreCursor);
|
|
3301
|
+
}
|
|
3302
|
+
var edited = () => {
|
|
3303
|
+
menuHidden = false;
|
|
3304
|
+
sel = 0;
|
|
3305
|
+
};
|
|
3306
|
+
function insert(s) {
|
|
3307
|
+
buf = buf.slice(0, cur) + s + buf.slice(cur);
|
|
3308
|
+
cur += s.length;
|
|
3309
|
+
edited();
|
|
3310
|
+
pendingRender = true;
|
|
3311
|
+
}
|
|
3312
|
+
function resetBurst() {
|
|
3313
|
+
if (burstTimer) {
|
|
3314
|
+
clearTimeout(burstTimer);
|
|
3315
|
+
burstTimer = null;
|
|
3316
|
+
}
|
|
3317
|
+
burstLen = 0;
|
|
3318
|
+
pendingRender = false;
|
|
3319
|
+
}
|
|
3320
|
+
function onKey(str, key) {
|
|
3321
|
+
if (pickItems) {
|
|
3322
|
+
const n = pickItems.length;
|
|
3323
|
+
if (key?.name === "up") {
|
|
3324
|
+
pickSel = (pickSel - 1 + n) % n;
|
|
3325
|
+
render();
|
|
3326
|
+
return;
|
|
3327
|
+
}
|
|
3328
|
+
if (key?.name === "down") {
|
|
3329
|
+
pickSel = (pickSel + 1) % n;
|
|
3330
|
+
render();
|
|
3331
|
+
return;
|
|
3332
|
+
}
|
|
3333
|
+
if (key?.name === "return" || key?.name === "enter") {
|
|
3334
|
+
const v = pickItems[pickSel].value;
|
|
3335
|
+
closePick();
|
|
3336
|
+
render();
|
|
3337
|
+
pickResolve?.(v);
|
|
3338
|
+
pickResolve = null;
|
|
3339
|
+
return;
|
|
3340
|
+
}
|
|
3341
|
+
if (key?.name === "escape" || key?.ctrl && key.name === "c") {
|
|
3342
|
+
closePick();
|
|
3343
|
+
render();
|
|
3344
|
+
pickResolve?.(null);
|
|
3345
|
+
pickResolve = null;
|
|
3346
|
+
return;
|
|
3347
|
+
}
|
|
3348
|
+
return;
|
|
3349
|
+
}
|
|
3350
|
+
burstLen++;
|
|
3351
|
+
if (burstTimer) clearTimeout(burstTimer);
|
|
3352
|
+
burstTimer = setTimeout(() => {
|
|
3353
|
+
burstTimer = null;
|
|
3354
|
+
burstLen = 0;
|
|
3355
|
+
if (pendingRender) render();
|
|
3356
|
+
}, BURST_IDLE_MS);
|
|
3357
|
+
const mm = currentMenu();
|
|
3358
|
+
if (key?.ctrl && key.name === "c") {
|
|
3359
|
+
if (buf) {
|
|
3360
|
+
buf = "";
|
|
3361
|
+
cur = 0;
|
|
3362
|
+
edited();
|
|
3363
|
+
resetBurst();
|
|
3364
|
+
render();
|
|
3365
|
+
return;
|
|
3366
|
+
}
|
|
3367
|
+
if (turnActive) onInterrupt?.();
|
|
3368
|
+
else {
|
|
3369
|
+
const r = onResult;
|
|
3370
|
+
onResult = null;
|
|
3371
|
+
r?.({ type: "quit" });
|
|
3372
|
+
}
|
|
3373
|
+
return;
|
|
3374
|
+
}
|
|
3375
|
+
if (key?.name === "escape") {
|
|
3376
|
+
if (mm.length) {
|
|
3377
|
+
menuHidden = true;
|
|
3378
|
+
render();
|
|
3379
|
+
} else if (buf) {
|
|
3380
|
+
buf = "";
|
|
3381
|
+
cur = 0;
|
|
3382
|
+
render();
|
|
3383
|
+
}
|
|
3384
|
+
return;
|
|
3385
|
+
}
|
|
3386
|
+
if (key?.name === "tab" && key.shift) {
|
|
3387
|
+
state.mode = nextMode(state.mode);
|
|
3388
|
+
render();
|
|
3389
|
+
return;
|
|
3390
|
+
}
|
|
3391
|
+
if (key?.name === "tab") {
|
|
3392
|
+
if (mm.length) {
|
|
3393
|
+
buf = mm[sel].name + " ";
|
|
3394
|
+
cur = buf.length;
|
|
3395
|
+
edited();
|
|
3396
|
+
render();
|
|
3397
|
+
}
|
|
3398
|
+
return;
|
|
3399
|
+
}
|
|
3400
|
+
if (key?.name === "up") {
|
|
3401
|
+
if (mm.length) {
|
|
3402
|
+
sel = (sel - 1 + mm.length) % mm.length;
|
|
3403
|
+
render();
|
|
3404
|
+
}
|
|
3405
|
+
return;
|
|
3406
|
+
}
|
|
3407
|
+
if (key?.name === "down") {
|
|
3408
|
+
if (mm.length) {
|
|
3409
|
+
sel = (sel + 1) % mm.length;
|
|
3410
|
+
render();
|
|
3411
|
+
}
|
|
3412
|
+
return;
|
|
3413
|
+
}
|
|
3414
|
+
if (key?.name === "return" || key?.name === "enter") {
|
|
3415
|
+
if (key?.shift || key?.meta || burstLen > 1) {
|
|
3416
|
+
insert("\n");
|
|
3417
|
+
return;
|
|
3418
|
+
}
|
|
3419
|
+
resetBurst();
|
|
3420
|
+
const line = buf;
|
|
3421
|
+
buf = "";
|
|
3422
|
+
cur = 0;
|
|
3423
|
+
edited();
|
|
3424
|
+
render();
|
|
3425
|
+
if (turnActive) {
|
|
3426
|
+
if (line.trim()) steering.push(line.trim());
|
|
3427
|
+
return;
|
|
3428
|
+
}
|
|
3429
|
+
if (line.trim()) out2("\n" + mark() + line + "\n");
|
|
3430
|
+
const r = onResult;
|
|
3431
|
+
onResult = null;
|
|
3432
|
+
r?.({ type: "line", value: line });
|
|
3433
|
+
return;
|
|
3434
|
+
}
|
|
3435
|
+
if (key?.name === "backspace") {
|
|
3436
|
+
if (cur > 0) {
|
|
3437
|
+
buf = buf.slice(0, cur - 1) + buf.slice(cur);
|
|
3438
|
+
cur--;
|
|
3439
|
+
edited();
|
|
3440
|
+
render();
|
|
3441
|
+
}
|
|
3442
|
+
return;
|
|
3443
|
+
}
|
|
3444
|
+
if (key?.name === "delete") {
|
|
3445
|
+
if (cur < buf.length) {
|
|
3446
|
+
buf = buf.slice(0, cur) + buf.slice(cur + 1);
|
|
3447
|
+
edited();
|
|
3448
|
+
render();
|
|
3449
|
+
}
|
|
3450
|
+
return;
|
|
3451
|
+
}
|
|
3452
|
+
if (key?.name === "left") {
|
|
3453
|
+
if (cur > 0) {
|
|
3454
|
+
cur--;
|
|
3455
|
+
render();
|
|
3456
|
+
}
|
|
3457
|
+
return;
|
|
3458
|
+
}
|
|
3459
|
+
if (key?.name === "right") {
|
|
3460
|
+
if (cur < buf.length) {
|
|
3461
|
+
cur++;
|
|
3462
|
+
render();
|
|
3463
|
+
}
|
|
3464
|
+
return;
|
|
3465
|
+
}
|
|
3466
|
+
if (key && (key.name === "home" || key.ctrl && key.name === "a")) {
|
|
3467
|
+
cur = 0;
|
|
3468
|
+
render();
|
|
3469
|
+
return;
|
|
3470
|
+
}
|
|
3471
|
+
if (key && (key.name === "end" || key.ctrl && key.name === "e")) {
|
|
3472
|
+
cur = buf.length;
|
|
3473
|
+
render();
|
|
3474
|
+
return;
|
|
3475
|
+
}
|
|
3476
|
+
if (key?.ctrl && key.name === "u") {
|
|
3477
|
+
buf = buf.slice(cur);
|
|
3478
|
+
cur = 0;
|
|
3479
|
+
edited();
|
|
3480
|
+
render();
|
|
3481
|
+
return;
|
|
3482
|
+
}
|
|
3483
|
+
if (str && !key?.ctrl && !key?.meta && [...str].length === 1 && isPrintableCodePoint(str.codePointAt(0))) {
|
|
3484
|
+
insert(str);
|
|
3485
|
+
}
|
|
3109
3486
|
}
|
|
3110
|
-
function
|
|
3111
|
-
if (active2 || !process.stdout.isTTY
|
|
3487
|
+
function startChrome(opts) {
|
|
3488
|
+
if (active2 || !process.stdout.isTTY) return;
|
|
3112
3489
|
active2 = true;
|
|
3113
|
-
tokensOf = tokens;
|
|
3114
|
-
|
|
3490
|
+
tokensOf = opts.tokens;
|
|
3491
|
+
allItems = opts.items;
|
|
3492
|
+
onInterrupt = opts.onInterrupt;
|
|
3493
|
+
out2(ansi.bracketedPasteOff);
|
|
3115
3494
|
pollGit();
|
|
3116
|
-
|
|
3117
|
-
|
|
3495
|
+
restoreKeys = pushKeyHandler(onKey);
|
|
3496
|
+
render();
|
|
3497
|
+
statusTimer = setInterval(refreshChrome, config.statuslineRefreshMs);
|
|
3118
3498
|
gitTimer = setInterval(pollGit, 5e3);
|
|
3119
3499
|
process.stdout.on("resize", onResize);
|
|
3120
3500
|
}
|
|
3121
|
-
function
|
|
3501
|
+
function nextLine() {
|
|
3502
|
+
return new Promise((resolve2) => {
|
|
3503
|
+
turnActive = false;
|
|
3504
|
+
render();
|
|
3505
|
+
onResult = resolve2;
|
|
3506
|
+
});
|
|
3507
|
+
}
|
|
3508
|
+
function beginTurn() {
|
|
3509
|
+
steering.length = 0;
|
|
3510
|
+
turnActive = true;
|
|
3511
|
+
return steering;
|
|
3512
|
+
}
|
|
3513
|
+
function endTurn() {
|
|
3514
|
+
turnActive = false;
|
|
3515
|
+
render();
|
|
3516
|
+
}
|
|
3517
|
+
function stopChrome() {
|
|
3122
3518
|
if (!active2) return;
|
|
3123
3519
|
active2 = false;
|
|
3124
|
-
|
|
3125
|
-
|
|
3520
|
+
restoreKeys?.();
|
|
3521
|
+
restoreKeys = null;
|
|
3126
3522
|
process.stdout.removeListener("resize", onResize);
|
|
3127
|
-
|
|
3523
|
+
if (statusTimer) clearInterval(statusTimer);
|
|
3524
|
+
if (gitTimer) clearInterval(gitTimer);
|
|
3525
|
+
resetBurst();
|
|
3526
|
+
out2(ansi.resetRegion);
|
|
3527
|
+
for (const r of [statusRow(), borderBottomRow(), inputRow(), borderTopRow()]) out2(ansi.moveTo(r) + ansi.clearLine);
|
|
3128
3528
|
}
|
|
3529
|
+
var chromeEnabled = () => config.statuslineEnabled && !!process.stdout.isTTY;
|
|
3129
3530
|
|
|
3130
3531
|
// src/commands.ts
|
|
3131
3532
|
import { writeFile as writeFile4, mkdir as mkdir4, chmod as chmod3 } from "node:fs/promises";
|
|
@@ -3179,6 +3580,13 @@ ${extra}` : "");
|
|
|
3179
3580
|
}
|
|
3180
3581
|
|
|
3181
3582
|
// src/commands.ts
|
|
3583
|
+
async function pick(opts) {
|
|
3584
|
+
if (chromeEnabled()) {
|
|
3585
|
+
const v = await chromePick(opts.items.map((it) => ({ label: it.label, hint: it.hint, value: it.value })), opts.initial, opts.title);
|
|
3586
|
+
return v == null ? null : v;
|
|
3587
|
+
}
|
|
3588
|
+
return selectMenu(opts);
|
|
3589
|
+
}
|
|
3182
3590
|
var SLASH_COMMANDS = [
|
|
3183
3591
|
{ name: "/model", desc: "switch model (menu; /model <term> searches)" },
|
|
3184
3592
|
{ name: "/effort", desc: "set reasoning effort (off/low/medium/high/max)" },
|
|
@@ -3263,13 +3671,13 @@ async function handleCommand(input, messages) {
|
|
|
3263
3671
|
}
|
|
3264
3672
|
} else if (cmd === "/clear") {
|
|
3265
3673
|
messages.splice(1);
|
|
3266
|
-
if (process.stdout.isTTY) process.stdout.write(
|
|
3674
|
+
if (process.stdout.isTTY) process.stdout.write(ansi.clearScreen + ansi.clearScrollback + ansi.home);
|
|
3267
3675
|
console.log(color.dim("conversation cleared (kept the system prompt, your model + settings).") + "\n");
|
|
3268
3676
|
} else if (cmd === "/resume") {
|
|
3269
3677
|
const sessions = process.stdin.isTTY ? await listSessions() : [];
|
|
3270
3678
|
let restored = [];
|
|
3271
3679
|
if (sessions.length > 1) {
|
|
3272
|
-
const choice = await
|
|
3680
|
+
const choice = await pick({
|
|
3273
3681
|
title: "resume which session? \u2014 \u2191/\u2193 then Enter (Esc to cancel)",
|
|
3274
3682
|
items: sessions.map((s) => ({
|
|
3275
3683
|
label: s.preview || "(no first message)",
|
|
@@ -3322,7 +3730,7 @@ async function handleCommand(input, messages) {
|
|
|
3322
3730
|
}
|
|
3323
3731
|
async function pickModel() {
|
|
3324
3732
|
if (!process.stdin.isTTY) return showRecommended();
|
|
3325
|
-
const choice = await
|
|
3733
|
+
const choice = await pick({
|
|
3326
3734
|
title: "switch model \u2014 \u2191/\u2193 then Enter (Esc to cancel)",
|
|
3327
3735
|
initial: Math.max(0, RECOMMENDED_MODELS.findIndex((m) => m.slug === state.model)),
|
|
3328
3736
|
items: RECOMMENDED_MODELS.map((m) => ({
|
|
@@ -3345,7 +3753,7 @@ async function pickEffort() {
|
|
|
3345
3753
|
console.log(color.cyan(`reasoning effort: ${state.reasoningEffort}`) + color.dim(` (levels: ${EFFORTS.join(" / ")})`) + "\n");
|
|
3346
3754
|
return;
|
|
3347
3755
|
}
|
|
3348
|
-
const choice = await
|
|
3756
|
+
const choice = await pick({
|
|
3349
3757
|
title: "reasoning effort \u2014 \u2191/\u2193 then Enter (Esc to cancel)",
|
|
3350
3758
|
initial: Math.max(0, EFFORTS.indexOf(state.reasoningEffort)),
|
|
3351
3759
|
items: EFFORTS.map((e) => ({
|
|
@@ -3380,7 +3788,7 @@ async function searchModels(term) {
|
|
|
3380
3788
|
}
|
|
3381
3789
|
const priceOf = (m) => m.pricing?.prompt != null ? `$${(parseFloat(m.pricing.prompt) * 1e6).toFixed(2)}/1M` : "?";
|
|
3382
3790
|
if (process.stdin.isTTY) {
|
|
3383
|
-
const choice = await
|
|
3791
|
+
const choice = await pick({
|
|
3384
3792
|
title: `models matching "${term}" \u2014 \u2191/\u2193 then Enter (Esc to cancel)`,
|
|
3385
3793
|
items: matches.map((m) => ({
|
|
3386
3794
|
label: m.id,
|
|
@@ -3469,6 +3877,7 @@ ${instr.trusted}`;
|
|
|
3469
3877
|
}
|
|
3470
3878
|
let messages = [{ role: "system", content: systemContent }];
|
|
3471
3879
|
const approvedTools = /* @__PURE__ */ new Set();
|
|
3880
|
+
const approvedGuardKeys = /* @__PURE__ */ new Set();
|
|
3472
3881
|
for (const t of settings.alwaysAllow) approvedTools.add(t);
|
|
3473
3882
|
for (const t of projectApprovals) approvedTools.add(t);
|
|
3474
3883
|
let saved = false;
|
|
@@ -3488,7 +3897,7 @@ ${instr.trusted}`;
|
|
|
3488
3897
|
};
|
|
3489
3898
|
const tty = !!process.stdin.isTTY;
|
|
3490
3899
|
process.on("exit", killAllTasks);
|
|
3491
|
-
process.on("exit",
|
|
3900
|
+
process.on("exit", stopChrome);
|
|
3492
3901
|
if (tty) {
|
|
3493
3902
|
initInput();
|
|
3494
3903
|
process.on("exit", teardownInput);
|
|
@@ -3507,6 +3916,7 @@ ${instr.trusted}`;
|
|
|
3507
3916
|
});
|
|
3508
3917
|
}
|
|
3509
3918
|
const rl = tty ? null : createInterface({ input: process.stdin, output: process.stdout, completer });
|
|
3919
|
+
if (chromeEnabled()) process.stdout.write(ansi.clearScreen + ansi.clearScrollback + ansi.home);
|
|
3510
3920
|
printBanner(state.model, instr.sources.map(tildify));
|
|
3511
3921
|
if (config.dangerouslySkipPermissions) {
|
|
3512
3922
|
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");
|
|
@@ -3556,10 +3966,19 @@ ${instr.trusted}`;
|
|
|
3556
3966
|
}
|
|
3557
3967
|
});
|
|
3558
3968
|
}
|
|
3559
|
-
|
|
3969
|
+
if (chromeEnabled())
|
|
3970
|
+
startChrome({
|
|
3971
|
+
tokens: () => estimateTokens(messages),
|
|
3972
|
+
items: [...SLASH_COMMANDS, ...skills.map((s) => ({ name: "/" + s.name, desc: "skill" }))],
|
|
3973
|
+
onInterrupt: () => activeTurn?.abort()
|
|
3974
|
+
});
|
|
3560
3975
|
while (true) {
|
|
3561
3976
|
let userInput;
|
|
3562
|
-
if (
|
|
3977
|
+
if (chromeEnabled()) {
|
|
3978
|
+
const r = await nextLine();
|
|
3979
|
+
if (r.type === "quit") break;
|
|
3980
|
+
userInput = r.value;
|
|
3981
|
+
} else if (tty) {
|
|
3563
3982
|
const r = await readPrompt({
|
|
3564
3983
|
promptString,
|
|
3565
3984
|
commands: SLASH_COMMANDS,
|
|
@@ -3597,16 +4016,27 @@ ${instr.trusted}`;
|
|
|
3597
4016
|
continue;
|
|
3598
4017
|
}
|
|
3599
4018
|
}
|
|
4019
|
+
if (chromeEnabled()) {
|
|
4020
|
+
activeTurn = new AbortController();
|
|
4021
|
+
const steering3 = beginTurn();
|
|
4022
|
+
try {
|
|
4023
|
+
messages = await runTurn(messages, userInput, ask, approvedTools, approvedGuardKeys, activeTurn.signal, steering3);
|
|
4024
|
+
} finally {
|
|
4025
|
+
activeTurn = null;
|
|
4026
|
+
endTurn();
|
|
4027
|
+
}
|
|
4028
|
+
continue;
|
|
4029
|
+
}
|
|
3600
4030
|
activeTurn = new AbortController();
|
|
3601
4031
|
let modeChangedMidTurn = false;
|
|
3602
|
-
const
|
|
4032
|
+
const steering2 = [];
|
|
3603
4033
|
let typed = "";
|
|
3604
|
-
const redrawSteer = () => process.stdout.write(
|
|
4034
|
+
const redrawSteer = () => process.stdout.write(ansi.cr + ansi.clearLine + color.cyan("\xBB ") + color.dim(typed));
|
|
3605
4035
|
const clearSteer = () => {
|
|
3606
|
-
process.stdout.write(
|
|
4036
|
+
process.stdout.write(ansi.cr + ansi.clearLine);
|
|
3607
4037
|
setSteeringActive(false);
|
|
3608
4038
|
};
|
|
3609
|
-
const
|
|
4039
|
+
const restoreKeys2 = tty ? pushKeyHandler((str, key) => {
|
|
3610
4040
|
if (key && (key.name === "escape" || key.ctrl && key.name === "c")) {
|
|
3611
4041
|
activeTurn?.abort();
|
|
3612
4042
|
return;
|
|
@@ -3621,7 +4051,7 @@ ${instr.trusted}`;
|
|
|
3621
4051
|
typed = "";
|
|
3622
4052
|
clearSteer();
|
|
3623
4053
|
if (note) {
|
|
3624
|
-
|
|
4054
|
+
steering2.push(note);
|
|
3625
4055
|
console.log(color.dim(`\xBB queued for next step: ${note}`));
|
|
3626
4056
|
}
|
|
3627
4057
|
return;
|
|
@@ -3641,9 +4071,9 @@ ${instr.trusted}`;
|
|
|
3641
4071
|
}) : () => {
|
|
3642
4072
|
};
|
|
3643
4073
|
try {
|
|
3644
|
-
messages = await runTurn(messages, userInput, ask, approvedTools, activeTurn.signal,
|
|
4074
|
+
messages = await runTurn(messages, userInput, ask, approvedTools, approvedGuardKeys, activeTurn.signal, steering2);
|
|
3645
4075
|
} finally {
|
|
3646
|
-
|
|
4076
|
+
restoreKeys2();
|
|
3647
4077
|
setSteeringActive(false);
|
|
3648
4078
|
activeTurn = null;
|
|
3649
4079
|
if (modeChangedMidTurn) console.log(color.yellow(`\u25B8 mode: ${modeLabel(state.mode)}`));
|
|
@@ -3651,6 +4081,7 @@ ${instr.trusted}`;
|
|
|
3651
4081
|
if (bg > 0) console.log(color.dim(`\u25B8 ${bg} background task${bg === 1 ? "" : "s"} running \u2014 check_task / stop_task`));
|
|
3652
4082
|
}
|
|
3653
4083
|
}
|
|
4084
|
+
stopChrome();
|
|
3654
4085
|
teardownInput();
|
|
3655
4086
|
rl?.close();
|
|
3656
4087
|
await persist();
|