beecork 2.4.2 → 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/dist/index.js +686 -264
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -196,7 +196,7 @@ function selfUpdate(timeoutMs = 12e4) {
|
|
|
196
196
|
return new Promise((resolve2) => {
|
|
197
197
|
const npm = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
198
198
|
const child = spawn(npm, ["install", "-g", "beecork@latest"], { stdio: ["ignore", "pipe", "pipe"] });
|
|
199
|
-
let
|
|
199
|
+
let out3 = "", done = false;
|
|
200
200
|
const finish = (r) => {
|
|
201
201
|
if (done) return;
|
|
202
202
|
done = true;
|
|
@@ -208,13 +208,13 @@ function selfUpdate(timeoutMs = 12e4) {
|
|
|
208
208
|
child.kill("SIGKILL");
|
|
209
209
|
} catch {
|
|
210
210
|
}
|
|
211
|
-
finish({ ok: false, output: `${
|
|
211
|
+
finish({ ok: false, output: `${out3.trim()}
|
|
212
212
|
(update timed out after ${timeoutMs}ms \u2014 run manually: npm install -g beecork@latest)`.trim() });
|
|
213
213
|
}, timeoutMs);
|
|
214
|
-
child.stdout?.on("data", (d) =>
|
|
215
|
-
child.stderr?.on("data", (d) =>
|
|
214
|
+
child.stdout?.on("data", (d) => out3 += d);
|
|
215
|
+
child.stderr?.on("data", (d) => out3 += d);
|
|
216
216
|
child.on("error", (e) => finish({ ok: false, output: e.message }));
|
|
217
|
-
child.on("close", (code) => finish({ ok: code === 0, output:
|
|
217
|
+
child.on("close", (code) => finish({ ok: code === 0, output: out3.trim() }));
|
|
218
218
|
});
|
|
219
219
|
}
|
|
220
220
|
|
|
@@ -256,27 +256,65 @@ function lineDiff(oldText, newText) {
|
|
|
256
256
|
lcs[i2][j2] = a[i2] === b[j2] ? lcs[i2 + 1][j2 + 1] + 1 : Math.max(lcs[i2 + 1][j2], lcs[i2][j2 + 1]);
|
|
257
257
|
}
|
|
258
258
|
}
|
|
259
|
-
const
|
|
259
|
+
const out3 = [];
|
|
260
260
|
let i = 0;
|
|
261
261
|
let j = 0;
|
|
262
262
|
while (i < m && j < n) {
|
|
263
263
|
if (a[i] === b[j]) {
|
|
264
|
-
|
|
264
|
+
out3.push(" " + a[i]);
|
|
265
265
|
i++;
|
|
266
266
|
j++;
|
|
267
267
|
} else if (lcs[i + 1][j] >= lcs[i][j + 1]) {
|
|
268
|
-
|
|
268
|
+
out3.push("- " + a[i]);
|
|
269
269
|
i++;
|
|
270
270
|
} else {
|
|
271
|
-
|
|
271
|
+
out3.push("+ " + b[j]);
|
|
272
272
|
j++;
|
|
273
273
|
}
|
|
274
274
|
}
|
|
275
|
-
while (i < m)
|
|
276
|
-
while (j < n)
|
|
277
|
-
return
|
|
275
|
+
while (i < m) out3.push("- " + a[i++]);
|
|
276
|
+
while (j < n) out3.push("+ " + b[j++]);
|
|
277
|
+
return out3.join("\n");
|
|
278
278
|
}
|
|
279
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
|
+
|
|
280
318
|
// src/ui.ts
|
|
281
319
|
var useColor = process.env.NO_COLOR ? false : process.env.FORCE_COLOR ? true : Boolean(process.stdout.isTTY);
|
|
282
320
|
var paint = (code) => (s) => useColor ? `\x1B[${code}m${s}\x1B[0m` : s;
|
|
@@ -294,12 +332,12 @@ var color = {
|
|
|
294
332
|
};
|
|
295
333
|
var isPrintableCodePoint = (c) => c >= 32 && c !== 127 && !(c >= 128 && c <= 159);
|
|
296
334
|
function stripControl(s) {
|
|
297
|
-
let
|
|
335
|
+
let out3 = "";
|
|
298
336
|
for (const ch of s) {
|
|
299
337
|
const c = ch.codePointAt(0);
|
|
300
|
-
if (c === 9 || c === 10 || isPrintableCodePoint(c))
|
|
338
|
+
if (c === 9 || c === 10 || isPrintableCodePoint(c)) out3 += ch;
|
|
301
339
|
}
|
|
302
|
-
return
|
|
340
|
+
return out3;
|
|
303
341
|
}
|
|
304
342
|
var stripAnsi = (s) => s.replace(/\x1b\[[0-9;]*m/g, "");
|
|
305
343
|
function charWidth(cp) {
|
|
@@ -418,19 +456,19 @@ function startSpinner(label) {
|
|
|
418
456
|
if (!process.stdout.isTTY || !useColor) return () => {
|
|
419
457
|
};
|
|
420
458
|
let i = 0;
|
|
421
|
-
const
|
|
459
|
+
const draw = () => {
|
|
422
460
|
if (steeringOnScreen) return;
|
|
423
|
-
process.stdout.write("
|
|
461
|
+
process.stdout.write(ansi.cr + " " + color.brand(SPINNER[i = (i + 1) % SPINNER.length]) + " " + color.dim(label));
|
|
424
462
|
};
|
|
425
|
-
process.stdout.write(
|
|
426
|
-
|
|
427
|
-
const timer = setInterval(
|
|
463
|
+
process.stdout.write(ansi.hideCursor);
|
|
464
|
+
draw();
|
|
465
|
+
const timer = setInterval(draw, 80);
|
|
428
466
|
let stopped = false;
|
|
429
467
|
return () => {
|
|
430
468
|
if (stopped) return;
|
|
431
469
|
stopped = true;
|
|
432
470
|
clearInterval(timer);
|
|
433
|
-
process.stdout.write(
|
|
471
|
+
process.stdout.write(ansi.cr + ansi.clearLine);
|
|
434
472
|
};
|
|
435
473
|
}
|
|
436
474
|
function markLines(width) {
|
|
@@ -465,21 +503,21 @@ function printBanner(model, sources) {
|
|
|
465
503
|
" | |_) | __/ __/ (_| (_) | | | < ",
|
|
466
504
|
" |_.__/ \\___|\\___|\\___\\___/|_| |_|\\_\\"
|
|
467
505
|
];
|
|
468
|
-
const
|
|
469
|
-
const markW = Math.max(0, ...
|
|
506
|
+
const mark2 = markLines(24);
|
|
507
|
+
const markW = Math.max(0, ...mark2.map((l) => l.length));
|
|
470
508
|
const wordW = Math.max(...word.map((l) => l.length));
|
|
471
|
-
const
|
|
509
|
+
const cols2 = process.stdout.columns || 80;
|
|
472
510
|
console.log();
|
|
473
|
-
if (
|
|
474
|
-
const h = Math.max(
|
|
475
|
-
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);
|
|
476
514
|
const wPad = Math.floor((h - word.length) / 2);
|
|
477
515
|
for (let i = 0; i < h; i++) {
|
|
478
|
-
const m = (
|
|
516
|
+
const m = (mark2[i - mPad] ?? "").padEnd(markW);
|
|
479
517
|
const w = word[i - wPad] ?? "";
|
|
480
518
|
console.log(" " + color.brand(`${m} ${w}`));
|
|
481
519
|
}
|
|
482
|
-
} else if (
|
|
520
|
+
} else if (cols2 >= wordW + 2) {
|
|
483
521
|
for (const w of word) console.log(" " + color.brand(w));
|
|
484
522
|
} else {
|
|
485
523
|
console.log(" " + color.brand("\u{1F41D} beecork"));
|
|
@@ -500,7 +538,7 @@ function printBanner(model, sources) {
|
|
|
500
538
|
const plain = (l, v) => l ? l.padEnd(lw) + " " + v : v;
|
|
501
539
|
const bw = Math.max(...rows2.map(([l, v]) => plain(l, v).length));
|
|
502
540
|
const row = ([l, v]) => l ? color.bold(l.padEnd(lw)) + color.dim(" " + v) : color.dim(v);
|
|
503
|
-
if (
|
|
541
|
+
if (cols2 < bw + 6) {
|
|
504
542
|
for (const r of rows2) console.log(" " + row(r));
|
|
505
543
|
} else {
|
|
506
544
|
console.log(" " + color.dim("\u256D\u2500" + "\u2500".repeat(bw) + "\u2500\u256E"));
|
|
@@ -518,6 +556,39 @@ import { readFile as readFile4 } from "node:fs/promises";
|
|
|
518
556
|
|
|
519
557
|
// src/input.ts
|
|
520
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
|
|
521
592
|
var out = (s) => process.stdout.write(s);
|
|
522
593
|
var active = null;
|
|
523
594
|
var started = false;
|
|
@@ -525,13 +596,13 @@ function initInput() {
|
|
|
525
596
|
if (started || !process.stdin.isTTY) return;
|
|
526
597
|
started = true;
|
|
527
598
|
process.stdin.setRawMode(true);
|
|
528
|
-
out(
|
|
599
|
+
out(ansi.bracketedPasteOff);
|
|
529
600
|
emitKeypressEvents(process.stdin);
|
|
530
601
|
process.stdin.on("keypress", (str, key) => active?.(str, key));
|
|
531
602
|
}
|
|
532
603
|
function teardownInput() {
|
|
533
604
|
if (started && process.stdin.isTTY) {
|
|
534
|
-
out(
|
|
605
|
+
out(ansi.bracketedPasteOn + ansi.showCursor);
|
|
535
606
|
process.stdin.setRawMode(false);
|
|
536
607
|
process.stdin.pause();
|
|
537
608
|
}
|
|
@@ -553,27 +624,27 @@ function readPrompt(opts) {
|
|
|
553
624
|
...(opts.skills ?? []).map((n) => ({ name: "/" + n, desc: "skill" }))
|
|
554
625
|
];
|
|
555
626
|
return new Promise((resolve2) => {
|
|
556
|
-
let
|
|
557
|
-
let
|
|
627
|
+
let buf2 = "";
|
|
628
|
+
let cur2 = 0;
|
|
558
629
|
let hist = history.length;
|
|
559
|
-
let
|
|
560
|
-
let
|
|
561
|
-
const
|
|
562
|
-
let
|
|
563
|
-
let
|
|
564
|
-
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;
|
|
565
636
|
const matches = () => {
|
|
566
637
|
if (opts.mask) return [];
|
|
567
|
-
const m =
|
|
638
|
+
const m = buf2.match(/^\/(\S*)$/);
|
|
568
639
|
if (!m) return [];
|
|
569
640
|
const pre = "/" + m[1];
|
|
570
641
|
return all.filter((c) => c.name.startsWith(pre)).slice(0, MENU_MAX);
|
|
571
642
|
};
|
|
572
|
-
const menu = () =>
|
|
643
|
+
const menu = () => menuHidden2 ? [] : matches();
|
|
573
644
|
const highlight = () => {
|
|
574
|
-
if (opts.mask) return "*".repeat(
|
|
575
|
-
const m =
|
|
576
|
-
if (!m) return
|
|
645
|
+
if (opts.mask) return "*".repeat(buf2.length);
|
|
646
|
+
const m = buf2.match(/^(\/\S*)([\s\S]*)$/);
|
|
647
|
+
if (!m) return buf2;
|
|
577
648
|
const [, token, rest] = m;
|
|
578
649
|
const known = all.some((c) => c.name === token);
|
|
579
650
|
const partial = all.some((c) => c.name.startsWith(token));
|
|
@@ -581,7 +652,6 @@ function readPrompt(opts) {
|
|
|
581
652
|
return styled + rest;
|
|
582
653
|
};
|
|
583
654
|
let lastCurRow = 0;
|
|
584
|
-
const rowColOf = (s) => ({ row: (s.match(/\n/g) || []).length, col: s.length - (s.lastIndexOf("\n") + 1) });
|
|
585
655
|
function drawBlock() {
|
|
586
656
|
const prompt = opts.promptString();
|
|
587
657
|
const promptW = stripAnsi(prompt).length;
|
|
@@ -591,199 +661,189 @@ function readPrompt(opts) {
|
|
|
591
661
|
for (let i = 1; i < lines.length; i++) out("\n" + indent + lines[i]);
|
|
592
662
|
return { promptW };
|
|
593
663
|
}
|
|
594
|
-
function
|
|
664
|
+
function render2() {
|
|
595
665
|
const mm = menu();
|
|
596
|
-
if (
|
|
597
|
-
out(
|
|
598
|
-
if (lastCurRow > 0) out(
|
|
599
|
-
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);
|
|
600
670
|
const { promptW } = drawBlock();
|
|
601
|
-
const
|
|
671
|
+
const cols2 = Math.max(1, process.stdout.columns || 80);
|
|
602
672
|
for (let i = 0; i < mm.length; i++) {
|
|
603
673
|
const m = mm[i];
|
|
604
674
|
const name = m.name.padEnd(10);
|
|
605
|
-
const maxDesc = Math.max(0,
|
|
675
|
+
const maxDesc = Math.max(0, cols2 - name.length - 4);
|
|
606
676
|
const desc = m.desc.length > maxDesc ? m.desc.slice(0, Math.max(0, maxDesc - 1)) + "\u2026" : m.desc;
|
|
607
|
-
out("\n" + (i ===
|
|
608
|
-
}
|
|
609
|
-
const text = opts.mask ? "*".repeat(
|
|
610
|
-
const
|
|
611
|
-
const
|
|
612
|
-
const
|
|
613
|
-
|
|
614
|
-
|
|
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");
|
|
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);
|
|
621
685
|
lastCurRow = curPhysRow;
|
|
622
686
|
}
|
|
623
687
|
function finish(result) {
|
|
624
688
|
restore();
|
|
625
|
-
if (
|
|
626
|
-
clearTimeout(
|
|
627
|
-
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
out(
|
|
631
|
-
if (lastCurRow > 0) out(
|
|
632
|
-
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);
|
|
633
697
|
drawBlock();
|
|
634
698
|
out("\n");
|
|
635
699
|
if (result.type === "line" && result.value.trim() && !opts.mask) history.push(result.value);
|
|
636
700
|
resolve2(result);
|
|
637
701
|
}
|
|
638
|
-
function
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
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;
|
|
644
708
|
}
|
|
645
|
-
function
|
|
709
|
+
function onKey2(str, key) {
|
|
646
710
|
const mm = menu();
|
|
647
|
-
|
|
648
|
-
if (
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
if (
|
|
653
|
-
|
|
654
|
-
|
|
711
|
+
burstLen2++;
|
|
712
|
+
if (burstTimer2) clearTimeout(burstTimer2);
|
|
713
|
+
burstTimer2 = setTimeout(() => {
|
|
714
|
+
burstTimer2 = null;
|
|
715
|
+
burstLen2 = 0;
|
|
716
|
+
if (pendingRender2) {
|
|
717
|
+
pendingRender2 = false;
|
|
718
|
+
render2();
|
|
655
719
|
}
|
|
656
|
-
},
|
|
720
|
+
}, BURST_IDLE_MS2);
|
|
657
721
|
if (isEnter(key)) {
|
|
658
|
-
if (key?.shift || key?.meta ||
|
|
659
|
-
|
|
722
|
+
if (key?.shift || key?.meta || burstLen2 > 1) {
|
|
723
|
+
insert2("\n");
|
|
660
724
|
return;
|
|
661
725
|
}
|
|
662
|
-
return finish({ type: "line", value:
|
|
726
|
+
return finish({ type: "line", value: buf2 });
|
|
663
727
|
}
|
|
664
728
|
if (key?.ctrl && key.name === "c") {
|
|
665
|
-
if (
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
729
|
+
if (buf2) {
|
|
730
|
+
buf2 = "";
|
|
731
|
+
cur2 = 0;
|
|
732
|
+
render2();
|
|
669
733
|
} else finish({ type: "quit" });
|
|
670
734
|
return;
|
|
671
735
|
}
|
|
672
736
|
if (key?.ctrl && key.name === "d") {
|
|
673
|
-
if (!
|
|
737
|
+
if (!buf2) finish({ type: "eof" });
|
|
674
738
|
return;
|
|
675
739
|
}
|
|
676
740
|
if (key?.name === "tab" && key.shift) {
|
|
677
741
|
opts.onShiftTab?.();
|
|
678
|
-
|
|
742
|
+
render2();
|
|
679
743
|
return;
|
|
680
744
|
}
|
|
681
745
|
if (key?.name === "tab") {
|
|
682
746
|
if (mm.length) {
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
747
|
+
buf2 = mm[sel2].name + " ";
|
|
748
|
+
cur2 = buf2.length;
|
|
749
|
+
menuHidden2 = false;
|
|
750
|
+
render2();
|
|
687
751
|
}
|
|
688
752
|
return;
|
|
689
753
|
}
|
|
690
754
|
if (key?.name === "up") {
|
|
691
755
|
if (mm.length) {
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
} else if (
|
|
756
|
+
sel2 = (sel2 - 1 + mm.length) % mm.length;
|
|
757
|
+
render2();
|
|
758
|
+
} else if (buf2.includes("\n")) moveVert(-1);
|
|
695
759
|
else histPrev();
|
|
696
760
|
return;
|
|
697
761
|
}
|
|
698
762
|
if (key?.name === "down") {
|
|
699
763
|
if (mm.length) {
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
} else if (
|
|
764
|
+
sel2 = (sel2 + 1) % mm.length;
|
|
765
|
+
render2();
|
|
766
|
+
} else if (buf2.includes("\n")) moveVert(1);
|
|
703
767
|
else histNext();
|
|
704
768
|
return;
|
|
705
769
|
}
|
|
706
770
|
if (key?.name === "left") {
|
|
707
|
-
if (
|
|
708
|
-
|
|
771
|
+
if (cur2 > 0) cur2--;
|
|
772
|
+
render2();
|
|
709
773
|
return;
|
|
710
774
|
}
|
|
711
775
|
if (key?.name === "right") {
|
|
712
|
-
if (
|
|
713
|
-
|
|
776
|
+
if (cur2 < buf2.length) cur2++;
|
|
777
|
+
render2();
|
|
714
778
|
return;
|
|
715
779
|
}
|
|
716
780
|
if (key?.name === "home" || key?.ctrl && key.name === "a") {
|
|
717
|
-
|
|
718
|
-
|
|
781
|
+
cur2 = 0;
|
|
782
|
+
render2();
|
|
719
783
|
return;
|
|
720
784
|
}
|
|
721
785
|
if (key?.name === "end" || key?.ctrl && key.name === "e") {
|
|
722
|
-
|
|
723
|
-
|
|
786
|
+
cur2 = buf2.length;
|
|
787
|
+
render2();
|
|
724
788
|
return;
|
|
725
789
|
}
|
|
726
790
|
if (key?.ctrl && key.name === "u") {
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
791
|
+
buf2 = buf2.slice(cur2);
|
|
792
|
+
cur2 = 0;
|
|
793
|
+
menuHidden2 = false;
|
|
794
|
+
render2();
|
|
731
795
|
return;
|
|
732
796
|
}
|
|
733
797
|
if (key?.name === "backspace") {
|
|
734
|
-
if (
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
798
|
+
if (cur2 > 0) {
|
|
799
|
+
buf2 = buf2.slice(0, cur2 - 1) + buf2.slice(cur2);
|
|
800
|
+
cur2--;
|
|
801
|
+
menuHidden2 = false;
|
|
802
|
+
render2();
|
|
739
803
|
}
|
|
740
804
|
return;
|
|
741
805
|
}
|
|
742
806
|
if (key?.name === "delete") {
|
|
743
|
-
if (
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
807
|
+
if (cur2 < buf2.length) {
|
|
808
|
+
buf2 = buf2.slice(0, cur2) + buf2.slice(cur2 + 1);
|
|
809
|
+
menuHidden2 = false;
|
|
810
|
+
render2();
|
|
747
811
|
}
|
|
748
812
|
return;
|
|
749
813
|
}
|
|
750
814
|
if (key?.name === "escape") {
|
|
751
815
|
if (mm.length) {
|
|
752
|
-
|
|
753
|
-
|
|
816
|
+
menuHidden2 = true;
|
|
817
|
+
render2();
|
|
754
818
|
}
|
|
755
819
|
return;
|
|
756
820
|
}
|
|
757
821
|
if (str && !key?.ctrl && !key?.meta && [...str].length === 1) {
|
|
758
|
-
if (isPrintableCodePoint(str.codePointAt(0)))
|
|
822
|
+
if (isPrintableCodePoint(str.codePointAt(0))) insert2(str);
|
|
759
823
|
}
|
|
760
824
|
}
|
|
761
825
|
function moveVert(dir) {
|
|
762
|
-
const
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
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();
|
|
826
|
+
const idx = moveVertIndex(buf2, cur2, dir);
|
|
827
|
+
if (idx === null) return;
|
|
828
|
+
cur2 = idx;
|
|
829
|
+
render2();
|
|
770
830
|
}
|
|
771
831
|
function histPrev() {
|
|
772
832
|
if (history.length === 0) return;
|
|
773
833
|
hist = Math.max(0, hist - 1);
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
834
|
+
buf2 = history[hist] ?? "";
|
|
835
|
+
cur2 = buf2.length;
|
|
836
|
+
render2();
|
|
777
837
|
}
|
|
778
838
|
function histNext() {
|
|
779
839
|
if (hist >= history.length) return;
|
|
780
840
|
hist += 1;
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
841
|
+
buf2 = hist === history.length ? "" : history[hist];
|
|
842
|
+
cur2 = buf2.length;
|
|
843
|
+
render2();
|
|
784
844
|
}
|
|
785
|
-
const restore = pushKeyHandler(
|
|
786
|
-
|
|
845
|
+
const restore = pushKeyHandler(onKey2);
|
|
846
|
+
render2();
|
|
787
847
|
});
|
|
788
848
|
}
|
|
789
849
|
function readChoice(prompt) {
|
|
@@ -804,36 +864,36 @@ function readChoice(prompt) {
|
|
|
804
864
|
function selectMenu(opts) {
|
|
805
865
|
if (!process.stdin.isTTY || opts.items.length === 0) return Promise.resolve(null);
|
|
806
866
|
return new Promise((resolve2) => {
|
|
807
|
-
let
|
|
867
|
+
let sel2 = Math.max(0, Math.min(opts.initial ?? 0, opts.items.length - 1));
|
|
808
868
|
let drawn = 0;
|
|
809
|
-
function
|
|
810
|
-
out(
|
|
811
|
-
if (drawn > 0) out(
|
|
812
|
-
out(
|
|
869
|
+
function render2() {
|
|
870
|
+
out(ansi.hideCursor);
|
|
871
|
+
if (drawn > 0) out(ansi.up(drawn));
|
|
872
|
+
out(ansi.cr + ansi.clearToEnd);
|
|
813
873
|
out(color.dim(opts.title) + "\n");
|
|
814
874
|
opts.items.forEach((it, i) => {
|
|
815
|
-
const row = i ===
|
|
875
|
+
const row = i === sel2 ? color.green("\u203A " + it.label) : " " + it.label;
|
|
816
876
|
out(row + (it.hint ? color.dim(" " + it.hint) : "") + "\n");
|
|
817
877
|
});
|
|
818
878
|
drawn = opts.items.length + 1;
|
|
819
879
|
}
|
|
820
880
|
function finish(v) {
|
|
821
881
|
restore();
|
|
822
|
-
if (drawn > 0) out(
|
|
823
|
-
out(
|
|
882
|
+
if (drawn > 0) out(ansi.up(drawn) + ansi.cr + ansi.clearToEnd);
|
|
883
|
+
out(ansi.showCursor);
|
|
824
884
|
resolve2(v);
|
|
825
885
|
}
|
|
826
886
|
const restore = pushKeyHandler((_str, key) => {
|
|
827
887
|
if (key?.name === "up") {
|
|
828
|
-
|
|
829
|
-
|
|
888
|
+
sel2 = (sel2 - 1 + opts.items.length) % opts.items.length;
|
|
889
|
+
render2();
|
|
830
890
|
} else if (key?.name === "down") {
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
} 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);
|
|
834
894
|
else if (key?.name === "escape" || key?.name === "q" || key?.ctrl && key.name === "c") finish(null);
|
|
835
895
|
});
|
|
836
|
-
|
|
896
|
+
render2();
|
|
837
897
|
});
|
|
838
898
|
}
|
|
839
899
|
|
|
@@ -843,46 +903,46 @@ function renderFileBox(path, startLine, lines, hasMore) {
|
|
|
843
903
|
const p = stripControl(path);
|
|
844
904
|
const numW = String(startLine + Math.max(0, lines.length - 1)).length;
|
|
845
905
|
const header = startLine === 1 && !hasMore ? `${lines.length} line${lines.length === 1 ? "" : "s"}` : `lines ${startLine}\u2013${startLine + lines.length - 1}${hasMore ? " (+ more)" : ""}`;
|
|
846
|
-
const
|
|
847
|
-
lines.forEach((l, i) =>
|
|
848
|
-
if (hasMore)
|
|
849
|
-
|
|
850
|
-
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";
|
|
851
911
|
}
|
|
852
912
|
function renderListing(path, names) {
|
|
853
913
|
const safe = names.map(stripControl);
|
|
854
|
-
const
|
|
914
|
+
const out3 = [color.dim("\u256D\u2500 ") + color.bold(stripControl(path)) + color.dim(` ${safe.length} entr${safe.length === 1 ? "y" : "ies"}`)];
|
|
855
915
|
if (safe.length === 0) {
|
|
856
|
-
|
|
916
|
+
out3.push(color.dim("\u2502 ") + color.dim("(empty)"));
|
|
857
917
|
} else {
|
|
858
918
|
const avail = BOX_W() - 2;
|
|
859
919
|
const colW = Math.min(Math.max(...safe.map((n) => n.length)) + 2, 40);
|
|
860
|
-
const
|
|
861
|
-
const rows2 = Math.ceil(safe.length /
|
|
920
|
+
const cols2 = Math.max(1, Math.floor(avail / colW));
|
|
921
|
+
const rows2 = Math.ceil(safe.length / cols2);
|
|
862
922
|
for (let r = 0; r < rows2; r++) {
|
|
863
923
|
let line = "";
|
|
864
|
-
for (let c = 0; c <
|
|
924
|
+
for (let c = 0; c < cols2; c++) {
|
|
865
925
|
const idx = c * rows2 + r;
|
|
866
926
|
if (idx >= safe.length) continue;
|
|
867
927
|
const n = safe[idx];
|
|
868
928
|
line += (n.endsWith("/") ? color.cyan(n) : n) + " ".repeat(Math.max(1, colW - n.length));
|
|
869
929
|
}
|
|
870
|
-
|
|
930
|
+
out3.push(color.dim("\u2502 ") + line.replace(/\s+$/, ""));
|
|
871
931
|
}
|
|
872
932
|
}
|
|
873
|
-
|
|
874
|
-
return
|
|
933
|
+
out3.push(color.dim("\u2570\u2500"));
|
|
934
|
+
return out3.join("\n") + "\n";
|
|
875
935
|
}
|
|
876
936
|
function renderTree(path, items, truncated) {
|
|
877
937
|
const dirs = items.filter((it) => it.isDir).length;
|
|
878
|
-
const
|
|
938
|
+
const out3 = [color.dim("\u256D\u2500 ") + color.bold(stripControl(path)) + color.dim(` ${items.length} entries \xB7 ${dirs} folders`)];
|
|
879
939
|
for (const it of items) {
|
|
880
940
|
const nm = stripControl(it.name);
|
|
881
|
-
|
|
941
|
+
out3.push(color.dim("\u2502 ") + color.dim(it.prefix) + (it.isDir ? color.cyan(nm) : nm));
|
|
882
942
|
}
|
|
883
|
-
if (truncated)
|
|
884
|
-
|
|
885
|
-
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";
|
|
886
946
|
}
|
|
887
947
|
var SHOW_MARK = "";
|
|
888
948
|
var SHOW_KINDS = ["file", "dir", "tree"];
|
|
@@ -991,14 +1051,14 @@ function renderTable(rows2) {
|
|
|
991
1051
|
})
|
|
992
1052
|
);
|
|
993
1053
|
const width = Array.from({ length: ncol }, (_, c) => Math.max(...styled.map((r) => visLen(r[c]))));
|
|
994
|
-
const
|
|
1054
|
+
const out3 = styled.map((r) => {
|
|
995
1055
|
const cells = r.map((cell, c) => cell + " ".repeat(Math.max(0, width[c] - visLen(cell))));
|
|
996
1056
|
return " " + cells.join(color.dim(" \u2502 "));
|
|
997
1057
|
});
|
|
998
|
-
return
|
|
1058
|
+
return out3.join("\n") + "\n";
|
|
999
1059
|
}
|
|
1000
1060
|
function createMarkdownStream(write) {
|
|
1001
|
-
let
|
|
1061
|
+
let buf2 = "";
|
|
1002
1062
|
let scanFrom = 0;
|
|
1003
1063
|
let inCode = false;
|
|
1004
1064
|
let first = true;
|
|
@@ -1037,19 +1097,19 @@ function createMarkdownStream(write) {
|
|
|
1037
1097
|
}
|
|
1038
1098
|
return {
|
|
1039
1099
|
push(text) {
|
|
1040
|
-
|
|
1100
|
+
buf2 += text;
|
|
1041
1101
|
let i;
|
|
1042
|
-
while ((i =
|
|
1043
|
-
emit(
|
|
1044
|
-
|
|
1102
|
+
while ((i = buf2.indexOf("\n", scanFrom)) >= 0) {
|
|
1103
|
+
emit(buf2.slice(0, i));
|
|
1104
|
+
buf2 = buf2.slice(i + 1);
|
|
1045
1105
|
scanFrom = 0;
|
|
1046
1106
|
}
|
|
1047
|
-
scanFrom =
|
|
1107
|
+
scanFrom = buf2.length;
|
|
1048
1108
|
},
|
|
1049
1109
|
end() {
|
|
1050
|
-
if (
|
|
1051
|
-
emit(
|
|
1052
|
-
|
|
1110
|
+
if (buf2) {
|
|
1111
|
+
emit(buf2);
|
|
1112
|
+
buf2 = "";
|
|
1053
1113
|
}
|
|
1054
1114
|
flushTable();
|
|
1055
1115
|
if (inCode) {
|
|
@@ -1471,13 +1531,13 @@ async function atomicWrite(abs, content) {
|
|
|
1471
1531
|
}
|
|
1472
1532
|
var READ_PREFIX = /^ *\d+ {2}/;
|
|
1473
1533
|
function allIndexOf(hay, needle) {
|
|
1474
|
-
const
|
|
1534
|
+
const out3 = [];
|
|
1475
1535
|
let i = hay.indexOf(needle);
|
|
1476
1536
|
while (i !== -1) {
|
|
1477
|
-
|
|
1537
|
+
out3.push(i);
|
|
1478
1538
|
i = hay.indexOf(needle, i + 1);
|
|
1479
1539
|
}
|
|
1480
|
-
return
|
|
1540
|
+
return out3;
|
|
1481
1541
|
}
|
|
1482
1542
|
function stripReadPrefix(text) {
|
|
1483
1543
|
const lines = text.split("\n");
|
|
@@ -1972,11 +2032,11 @@ ${res.closest}` : `Re-read the file and copy the exact text (including whitespac
|
|
|
1972
2032
|
${stderr}` : "") || "(no output)";
|
|
1973
2033
|
} catch (err) {
|
|
1974
2034
|
const e = err;
|
|
1975
|
-
const
|
|
2035
|
+
const out3 = `${e.stdout ?? ""}${e.stderr ? `
|
|
1976
2036
|
[stderr]
|
|
1977
2037
|
${e.stderr}` : ""}`.trim();
|
|
1978
|
-
return `Error: command failed${
|
|
1979
|
-
${
|
|
2038
|
+
return `Error: command failed${out3 ? `:
|
|
2039
|
+
${out3}` : `: ${String(e.message ?? err)}`}`;
|
|
1980
2040
|
}
|
|
1981
2041
|
}
|
|
1982
2042
|
},
|
|
@@ -2221,14 +2281,14 @@ function validateArgs(tool, args) {
|
|
|
2221
2281
|
async function runVerify(signal) {
|
|
2222
2282
|
try {
|
|
2223
2283
|
const { stdout, stderr } = await runShell(config.verifyCommand, { timeout: config.verifyTimeoutMs, maxBuffer: config.maxToolBuffer, signal });
|
|
2224
|
-
const
|
|
2225
|
-
return `passed \u2713${
|
|
2226
|
-
${
|
|
2284
|
+
const out3 = `${stdout}${stderr}`.trim();
|
|
2285
|
+
return `passed \u2713${out3 ? `
|
|
2286
|
+
${out3.slice(-800)}` : ""}`;
|
|
2227
2287
|
} catch (err) {
|
|
2228
2288
|
const e = err;
|
|
2229
|
-
const
|
|
2289
|
+
const out3 = `${e.stdout ?? ""}${e.stderr ?? ""}`.trim() || String(e.message ?? err);
|
|
2230
2290
|
return `FAILED \u2717
|
|
2231
|
-
${
|
|
2291
|
+
${out3.slice(-1500)}`;
|
|
2232
2292
|
}
|
|
2233
2293
|
}
|
|
2234
2294
|
|
|
@@ -2261,12 +2321,12 @@ function parseSSELine(line) {
|
|
|
2261
2321
|
}
|
|
2262
2322
|
const delta = parsed.choices?.[0]?.delta;
|
|
2263
2323
|
if (!delta) return null;
|
|
2264
|
-
const
|
|
2265
|
-
if (delta.content)
|
|
2266
|
-
if (delta.tool_calls)
|
|
2267
|
-
if (typeof delta.reasoning === "string" && delta.reasoning)
|
|
2268
|
-
if (Array.isArray(delta.reasoning_details) && delta.reasoning_details.length)
|
|
2269
|
-
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;
|
|
2270
2330
|
}
|
|
2271
2331
|
function buildRequestBody(opts) {
|
|
2272
2332
|
const { model, messages, includeTools, effort, reasoningSupported, extra, tools } = opts;
|
|
@@ -2672,7 +2732,7 @@ async function saveSession(messages) {
|
|
|
2672
2732
|
}
|
|
2673
2733
|
function sanitizeSession(raw) {
|
|
2674
2734
|
if (!Array.isArray(raw)) return null;
|
|
2675
|
-
const
|
|
2735
|
+
const out3 = [];
|
|
2676
2736
|
for (const m of raw) {
|
|
2677
2737
|
if (!m || typeof m !== "object") return null;
|
|
2678
2738
|
const role = m.role;
|
|
@@ -2685,9 +2745,9 @@ function sanitizeSession(raw) {
|
|
|
2685
2745
|
if (Array.isArray(tc)) msg.tool_calls = tc;
|
|
2686
2746
|
const tcid = m.tool_call_id;
|
|
2687
2747
|
if (typeof tcid === "string") msg.tool_call_id = tcid;
|
|
2688
|
-
|
|
2748
|
+
out3.push(msg);
|
|
2689
2749
|
}
|
|
2690
|
-
return dropIncompleteToolTail(
|
|
2750
|
+
return dropIncompleteToolTail(out3);
|
|
2691
2751
|
}
|
|
2692
2752
|
function dropIncompleteToolTail(messages) {
|
|
2693
2753
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
@@ -2730,15 +2790,15 @@ async function loadLatestSession() {
|
|
|
2730
2790
|
async function listSessions() {
|
|
2731
2791
|
try {
|
|
2732
2792
|
const files = (await readdir2(sessionsDir())).filter((f) => f.endsWith(".json"));
|
|
2733
|
-
const
|
|
2793
|
+
const out3 = [];
|
|
2734
2794
|
for (const f of files) {
|
|
2735
2795
|
const msgs = await readSession(f);
|
|
2736
2796
|
if (!msgs || !msgs.length) continue;
|
|
2737
2797
|
const firstUser = msgs.find((m) => m.role === "user");
|
|
2738
2798
|
const preview = (firstUser?.content ?? "").replace(/\s+/g, " ").trim().slice(0, 60);
|
|
2739
|
-
|
|
2799
|
+
out3.push({ file: f, when: Number(f.replace(".json", "")) || 0, count: msgs.length, preview });
|
|
2740
2800
|
}
|
|
2741
|
-
return
|
|
2801
|
+
return out3.sort((a, b) => b.when - a.when);
|
|
2742
2802
|
} catch {
|
|
2743
2803
|
return [];
|
|
2744
2804
|
}
|
|
@@ -2978,7 +3038,7 @@ ${content}` }];
|
|
|
2978
3038
|
}
|
|
2979
3039
|
return [...messages, { role: "user", content }];
|
|
2980
3040
|
}
|
|
2981
|
-
async function runTurn(messages, userInput, ask, approvedTools, approvedGuardKeys, signal,
|
|
3041
|
+
async function runTurn(messages, userInput, ask, approvedTools, approvedGuardKeys, signal, steering2) {
|
|
2982
3042
|
messages.push({ role: "user", content: userInput });
|
|
2983
3043
|
const snapshot = messages.slice();
|
|
2984
3044
|
try {
|
|
@@ -2992,7 +3052,7 @@ async function runTurn(messages, userInput, ask, approvedTools, approvedGuardKey
|
|
|
2992
3052
|
console.error(color.red(`
|
|
2993
3053
|
[compaction failed: ${err.message} \u2014 continuing]`) + "\n");
|
|
2994
3054
|
}
|
|
2995
|
-
if (
|
|
3055
|
+
if (steering2?.length) messages = applySteering(messages, steering2.splice(0));
|
|
2996
3056
|
const message = await callModel(messages, true, signal);
|
|
2997
3057
|
messages.push(message);
|
|
2998
3058
|
if (!hasContent2(message)) {
|
|
@@ -3006,7 +3066,7 @@ async function runTurn(messages, userInput, ask, approvedTools, approvedGuardKey
|
|
|
3006
3066
|
await handleToolCall(call, messages, step, deps);
|
|
3007
3067
|
}
|
|
3008
3068
|
} else {
|
|
3009
|
-
answered = !
|
|
3069
|
+
answered = !steering2?.length;
|
|
3010
3070
|
}
|
|
3011
3071
|
}
|
|
3012
3072
|
if (signal?.aborted) {
|
|
@@ -3078,62 +3138,395 @@ async function runtimeContext() {
|
|
|
3078
3138
|
});
|
|
3079
3139
|
}
|
|
3080
3140
|
|
|
3081
|
-
// src/
|
|
3141
|
+
// src/chrome.ts
|
|
3082
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;
|
|
3083
3146
|
var active2 = false;
|
|
3084
|
-
var
|
|
3085
|
-
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;
|
|
3086
3160
|
var branch = "";
|
|
3087
|
-
var tokensOf =
|
|
3088
|
-
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
|
+
}
|
|
3089
3197
|
function pollGit() {
|
|
3090
|
-
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) => {
|
|
3091
3199
|
if (err) {
|
|
3092
3200
|
branch = "";
|
|
3093
3201
|
return;
|
|
3094
3202
|
}
|
|
3095
|
-
const b = String(
|
|
3096
|
-
execFile2("git", ["status", "--porcelain"], { timeout: 1500, windowsHide: true }, (e2,
|
|
3097
|
-
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() ? "*" : "");
|
|
3098
3206
|
});
|
|
3099
3207
|
});
|
|
3100
3208
|
}
|
|
3101
|
-
function
|
|
3102
|
-
const
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
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)));
|
|
3213
|
+
}
|
|
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);
|
|
3108
3256
|
}
|
|
3109
|
-
function
|
|
3257
|
+
function render() {
|
|
3110
3258
|
if (!active2) return;
|
|
3111
|
-
|
|
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;
|
|
3112
3283
|
}
|
|
3113
3284
|
function onResize() {
|
|
3114
3285
|
if (!active2) return;
|
|
3115
|
-
|
|
3116
|
-
|
|
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
|
+
}
|
|
3117
3486
|
}
|
|
3118
|
-
function
|
|
3119
|
-
if (active2 || !process.stdout.isTTY
|
|
3487
|
+
function startChrome(opts) {
|
|
3488
|
+
if (active2 || !process.stdout.isTTY) return;
|
|
3120
3489
|
active2 = true;
|
|
3121
|
-
tokensOf = tokens;
|
|
3122
|
-
|
|
3490
|
+
tokensOf = opts.tokens;
|
|
3491
|
+
allItems = opts.items;
|
|
3492
|
+
onInterrupt = opts.onInterrupt;
|
|
3493
|
+
out2(ansi.bracketedPasteOff);
|
|
3123
3494
|
pollGit();
|
|
3124
|
-
|
|
3125
|
-
|
|
3495
|
+
restoreKeys = pushKeyHandler(onKey);
|
|
3496
|
+
render();
|
|
3497
|
+
statusTimer = setInterval(refreshChrome, config.statuslineRefreshMs);
|
|
3126
3498
|
gitTimer = setInterval(pollGit, 5e3);
|
|
3127
3499
|
process.stdout.on("resize", onResize);
|
|
3128
3500
|
}
|
|
3129
|
-
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() {
|
|
3130
3518
|
if (!active2) return;
|
|
3131
3519
|
active2 = false;
|
|
3132
|
-
|
|
3133
|
-
|
|
3520
|
+
restoreKeys?.();
|
|
3521
|
+
restoreKeys = null;
|
|
3134
3522
|
process.stdout.removeListener("resize", onResize);
|
|
3135
|
-
|
|
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);
|
|
3136
3528
|
}
|
|
3529
|
+
var chromeEnabled = () => config.statuslineEnabled && !!process.stdout.isTTY;
|
|
3137
3530
|
|
|
3138
3531
|
// src/commands.ts
|
|
3139
3532
|
import { writeFile as writeFile4, mkdir as mkdir4, chmod as chmod3 } from "node:fs/promises";
|
|
@@ -3187,6 +3580,13 @@ ${extra}` : "");
|
|
|
3187
3580
|
}
|
|
3188
3581
|
|
|
3189
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
|
+
}
|
|
3190
3590
|
var SLASH_COMMANDS = [
|
|
3191
3591
|
{ name: "/model", desc: "switch model (menu; /model <term> searches)" },
|
|
3192
3592
|
{ name: "/effort", desc: "set reasoning effort (off/low/medium/high/max)" },
|
|
@@ -3271,13 +3671,13 @@ async function handleCommand(input, messages) {
|
|
|
3271
3671
|
}
|
|
3272
3672
|
} else if (cmd === "/clear") {
|
|
3273
3673
|
messages.splice(1);
|
|
3274
|
-
if (process.stdout.isTTY) process.stdout.write(
|
|
3674
|
+
if (process.stdout.isTTY) process.stdout.write(ansi.clearScreen + ansi.clearScrollback + ansi.home);
|
|
3275
3675
|
console.log(color.dim("conversation cleared (kept the system prompt, your model + settings).") + "\n");
|
|
3276
3676
|
} else if (cmd === "/resume") {
|
|
3277
3677
|
const sessions = process.stdin.isTTY ? await listSessions() : [];
|
|
3278
3678
|
let restored = [];
|
|
3279
3679
|
if (sessions.length > 1) {
|
|
3280
|
-
const choice = await
|
|
3680
|
+
const choice = await pick({
|
|
3281
3681
|
title: "resume which session? \u2014 \u2191/\u2193 then Enter (Esc to cancel)",
|
|
3282
3682
|
items: sessions.map((s) => ({
|
|
3283
3683
|
label: s.preview || "(no first message)",
|
|
@@ -3330,7 +3730,7 @@ async function handleCommand(input, messages) {
|
|
|
3330
3730
|
}
|
|
3331
3731
|
async function pickModel() {
|
|
3332
3732
|
if (!process.stdin.isTTY) return showRecommended();
|
|
3333
|
-
const choice = await
|
|
3733
|
+
const choice = await pick({
|
|
3334
3734
|
title: "switch model \u2014 \u2191/\u2193 then Enter (Esc to cancel)",
|
|
3335
3735
|
initial: Math.max(0, RECOMMENDED_MODELS.findIndex((m) => m.slug === state.model)),
|
|
3336
3736
|
items: RECOMMENDED_MODELS.map((m) => ({
|
|
@@ -3353,7 +3753,7 @@ async function pickEffort() {
|
|
|
3353
3753
|
console.log(color.cyan(`reasoning effort: ${state.reasoningEffort}`) + color.dim(` (levels: ${EFFORTS.join(" / ")})`) + "\n");
|
|
3354
3754
|
return;
|
|
3355
3755
|
}
|
|
3356
|
-
const choice = await
|
|
3756
|
+
const choice = await pick({
|
|
3357
3757
|
title: "reasoning effort \u2014 \u2191/\u2193 then Enter (Esc to cancel)",
|
|
3358
3758
|
initial: Math.max(0, EFFORTS.indexOf(state.reasoningEffort)),
|
|
3359
3759
|
items: EFFORTS.map((e) => ({
|
|
@@ -3388,7 +3788,7 @@ async function searchModels(term) {
|
|
|
3388
3788
|
}
|
|
3389
3789
|
const priceOf = (m) => m.pricing?.prompt != null ? `$${(parseFloat(m.pricing.prompt) * 1e6).toFixed(2)}/1M` : "?";
|
|
3390
3790
|
if (process.stdin.isTTY) {
|
|
3391
|
-
const choice = await
|
|
3791
|
+
const choice = await pick({
|
|
3392
3792
|
title: `models matching "${term}" \u2014 \u2191/\u2193 then Enter (Esc to cancel)`,
|
|
3393
3793
|
items: matches.map((m) => ({
|
|
3394
3794
|
label: m.id,
|
|
@@ -3497,7 +3897,7 @@ ${instr.trusted}`;
|
|
|
3497
3897
|
};
|
|
3498
3898
|
const tty = !!process.stdin.isTTY;
|
|
3499
3899
|
process.on("exit", killAllTasks);
|
|
3500
|
-
process.on("exit",
|
|
3900
|
+
process.on("exit", stopChrome);
|
|
3501
3901
|
if (tty) {
|
|
3502
3902
|
initInput();
|
|
3503
3903
|
process.on("exit", teardownInput);
|
|
@@ -3516,6 +3916,7 @@ ${instr.trusted}`;
|
|
|
3516
3916
|
});
|
|
3517
3917
|
}
|
|
3518
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);
|
|
3519
3920
|
printBanner(state.model, instr.sources.map(tildify));
|
|
3520
3921
|
if (config.dangerouslySkipPermissions) {
|
|
3521
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");
|
|
@@ -3565,10 +3966,19 @@ ${instr.trusted}`;
|
|
|
3565
3966
|
}
|
|
3566
3967
|
});
|
|
3567
3968
|
}
|
|
3568
|
-
|
|
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
|
+
});
|
|
3569
3975
|
while (true) {
|
|
3570
3976
|
let userInput;
|
|
3571
|
-
if (
|
|
3977
|
+
if (chromeEnabled()) {
|
|
3978
|
+
const r = await nextLine();
|
|
3979
|
+
if (r.type === "quit") break;
|
|
3980
|
+
userInput = r.value;
|
|
3981
|
+
} else if (tty) {
|
|
3572
3982
|
const r = await readPrompt({
|
|
3573
3983
|
promptString,
|
|
3574
3984
|
commands: SLASH_COMMANDS,
|
|
@@ -3606,16 +4016,27 @@ ${instr.trusted}`;
|
|
|
3606
4016
|
continue;
|
|
3607
4017
|
}
|
|
3608
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
|
+
}
|
|
3609
4030
|
activeTurn = new AbortController();
|
|
3610
4031
|
let modeChangedMidTurn = false;
|
|
3611
|
-
const
|
|
4032
|
+
const steering2 = [];
|
|
3612
4033
|
let typed = "";
|
|
3613
|
-
const redrawSteer = () => process.stdout.write(
|
|
4034
|
+
const redrawSteer = () => process.stdout.write(ansi.cr + ansi.clearLine + color.cyan("\xBB ") + color.dim(typed));
|
|
3614
4035
|
const clearSteer = () => {
|
|
3615
|
-
process.stdout.write(
|
|
4036
|
+
process.stdout.write(ansi.cr + ansi.clearLine);
|
|
3616
4037
|
setSteeringActive(false);
|
|
3617
4038
|
};
|
|
3618
|
-
const
|
|
4039
|
+
const restoreKeys2 = tty ? pushKeyHandler((str, key) => {
|
|
3619
4040
|
if (key && (key.name === "escape" || key.ctrl && key.name === "c")) {
|
|
3620
4041
|
activeTurn?.abort();
|
|
3621
4042
|
return;
|
|
@@ -3630,7 +4051,7 @@ ${instr.trusted}`;
|
|
|
3630
4051
|
typed = "";
|
|
3631
4052
|
clearSteer();
|
|
3632
4053
|
if (note) {
|
|
3633
|
-
|
|
4054
|
+
steering2.push(note);
|
|
3634
4055
|
console.log(color.dim(`\xBB queued for next step: ${note}`));
|
|
3635
4056
|
}
|
|
3636
4057
|
return;
|
|
@@ -3650,9 +4071,9 @@ ${instr.trusted}`;
|
|
|
3650
4071
|
}) : () => {
|
|
3651
4072
|
};
|
|
3652
4073
|
try {
|
|
3653
|
-
messages = await runTurn(messages, userInput, ask, approvedTools, approvedGuardKeys, activeTurn.signal,
|
|
4074
|
+
messages = await runTurn(messages, userInput, ask, approvedTools, approvedGuardKeys, activeTurn.signal, steering2);
|
|
3654
4075
|
} finally {
|
|
3655
|
-
|
|
4076
|
+
restoreKeys2();
|
|
3656
4077
|
setSteeringActive(false);
|
|
3657
4078
|
activeTurn = null;
|
|
3658
4079
|
if (modeChangedMidTurn) console.log(color.yellow(`\u25B8 mode: ${modeLabel(state.mode)}`));
|
|
@@ -3660,6 +4081,7 @@ ${instr.trusted}`;
|
|
|
3660
4081
|
if (bg > 0) console.log(color.dim(`\u25B8 ${bg} background task${bg === 1 ? "" : "s"} running \u2014 check_task / stop_task`));
|
|
3661
4082
|
}
|
|
3662
4083
|
}
|
|
4084
|
+
stopChrome();
|
|
3663
4085
|
teardownInput();
|
|
3664
4086
|
rl?.close();
|
|
3665
4087
|
await persist();
|