@wrongstack/tui 0.7.0 → 0.7.2

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 CHANGED
@@ -313,1671 +313,1861 @@ function FleetPanel({ entries, totalCost, roster }) {
313
313
  }
314
314
  );
315
315
  }
316
-
317
- // src/markdown-table.ts
318
- function renderMarkdownTables(text, maxWidth) {
319
- if (!text.includes("|")) return text;
320
- const lines = text.split("\n");
321
- const out = [];
322
- let i = 0;
323
- while (i < lines.length) {
324
- const end = detectTable(lines, i);
325
- if (end > i) {
326
- out.push(renderTable(lines.slice(i, end), Math.max(20, maxWidth)));
327
- i = end;
328
- } else {
329
- out.push(lines[i] ?? "");
330
- i++;
331
- }
332
- }
333
- return out.join("\n");
334
- }
335
- var ROW_RE = /^\s*\|.*\|\s*$/;
336
- var SEP_RE = /^\s*\|[\s\-:|]+\|\s*$/;
337
- function detectTable(lines, start) {
338
- if (start + 1 >= lines.length) return start;
339
- if (!ROW_RE.test(lines[start] ?? "")) return start;
340
- const sep2 = lines[start + 1] ?? "";
341
- if (!SEP_RE.test(sep2) || !/-/.test(sep2)) return start;
342
- let end = start + 2;
343
- while (end < lines.length && ROW_RE.test(lines[end] ?? "")) end++;
344
- return end;
345
- }
346
- function parseCells(line) {
347
- const inner = line.trim().replace(/^\||\|$/g, "");
348
- const parts = [];
349
- let buf = "";
350
- for (let i = 0; i < inner.length; i++) {
351
- const ch = inner[i];
352
- if (ch === "\\" && inner[i + 1] === "|") {
353
- buf += "|";
354
- i++;
355
- continue;
356
- }
357
- if (ch === "|") {
358
- parts.push(buf);
359
- buf = "";
360
- continue;
361
- }
362
- buf += ch;
363
- }
364
- parts.push(buf);
365
- return parts.map((c) => c.trim());
366
- }
367
- function parseAlign(sep2) {
368
- const t = sep2.trim();
369
- const left = t.startsWith(":");
370
- const right = t.endsWith(":");
371
- if (left && right) return "center";
372
- if (right) return "right";
373
- return "left";
374
- }
375
- function renderTable(tableLines, maxWidth) {
376
- const header = parseCells(tableLines[0] ?? "");
377
- const sepCells = parseCells(tableLines[1] ?? "");
378
- const cols = header.length;
379
- const aligns = [];
380
- for (let c = 0; c < cols; c++) {
381
- aligns.push(parseAlign(sepCells[c] ?? ""));
382
- }
383
- const dataRows = tableLines.slice(2).map(parseCells);
384
- for (const row of dataRows) {
385
- while (row.length < cols) row.push("");
386
- row.length = cols;
387
- }
388
- const widths = computeWidths([header, ...dataRows], cols, maxWidth);
389
- const lines = [];
390
- lines.push(border("\u250C", "\u252C", "\u2510", widths));
391
- lines.push(...renderRow(header, widths, aligns));
392
- lines.push(border("\u251C", "\u253C", "\u2524", widths));
393
- for (const row of dataRows) {
394
- lines.push(...renderRow(row, widths, aligns));
395
- }
396
- lines.push(border("\u2514", "\u2534", "\u2518", widths));
397
- return lines.join("\n");
398
- }
399
- function computeWidths(allRows, cols, maxWidth) {
400
- const overhead = 3 * cols + 1;
401
- const avail = Math.max(cols * MIN_COL_WIDTH, maxWidth - overhead);
402
- const natural = new Array(cols).fill(0);
403
- for (const row of allRows) {
404
- for (let c = 0; c < cols; c++) {
405
- const cell = row[c] ?? "";
406
- const w = longestWord(cell);
407
- const total = cell.length;
408
- natural[c] = Math.max(natural[c], total);
409
- if (w > natural[c]) natural[c] = Math.min(total + 1, w);
410
- }
411
- }
412
- const sumNatural = natural.reduce((s, n) => s + n, 0);
413
- if (sumNatural <= avail) return natural;
414
- const widths = natural.slice();
415
- let sum = sumNatural;
416
- while (sum > avail) {
417
- let maxIdx = -1;
418
- let maxVal = MIN_COL_WIDTH;
419
- for (let i = 0; i < cols; i++) {
420
- const w = widths[i];
421
- if (w > maxVal) {
422
- maxVal = w;
423
- maxIdx = i;
424
- }
425
- }
426
- if (maxIdx < 0) break;
427
- widths[maxIdx]--;
428
- sum--;
429
- }
430
- return widths;
431
- }
432
- var MIN_COL_WIDTH = 4;
433
- function longestWord(s) {
434
- let max = 0;
435
- for (const w of s.split(/\s+/)) if (w.length > max) max = w.length;
436
- return max;
437
- }
438
- function border(left, mid, right, widths) {
439
- return left + widths.map((w) => "\u2500".repeat(w + 2)).join(mid) + right;
440
- }
441
- function renderRow(cells, widths, aligns) {
442
- const wrapped = cells.map((c, i) => wrapCell(c, widths[i] ?? MIN_COL_WIDTH));
443
- const height = Math.max(1, ...wrapped.map((w) => w.length));
444
- const out = [];
445
- for (let line = 0; line < height; line++) {
446
- const parts = [];
447
- for (let c = 0; c < widths.length; c++) {
448
- const w = widths[c] ?? MIN_COL_WIDTH;
449
- const text = wrapped[c]?.[line] ?? "";
450
- parts.push(padCell(text, w, aligns[c] ?? "left"));
451
- }
452
- out.push("\u2502 " + parts.join(" \u2502 ") + " \u2502");
453
- }
454
- return out;
455
- }
456
- function wrapCell(text, width) {
457
- if (text.length <= width) return [text];
458
- const out = [];
459
- const words = text.split(/(\s+)/);
460
- let cur = "";
461
- for (const word of words) {
462
- if (!word) continue;
463
- if (cur.length + word.length <= width) {
464
- cur += word;
465
- continue;
466
- }
467
- if (cur) {
468
- out.push(cur.trimEnd());
469
- cur = "";
470
- }
471
- if (word.length > width) {
472
- let rest = word;
473
- while (rest.length > width) {
474
- out.push(rest.slice(0, width));
475
- rest = rest.slice(width);
476
- }
477
- cur = rest;
478
- } else if (!/^\s+$/.test(word)) {
479
- cur = word;
480
- }
481
- }
482
- if (cur) out.push(cur.trimEnd());
483
- return out.length === 0 ? [""] : out;
484
- }
485
- function padCell(text, width, align) {
486
- if (text.length >= width) return text.slice(0, width);
487
- const pad = width - text.length;
488
- if (align === "right") return " ".repeat(pad) + text;
489
- if (align === "center") {
490
- const l = Math.floor(pad / 2);
491
- return " ".repeat(l) + text + " ".repeat(pad - l);
492
- }
493
- return text + " ".repeat(pad);
494
- }
495
- function History({ entries, streamingText, toolStream }) {
496
- const { stdout } = useStdout();
497
- const termWidth = stdout?.columns ?? 80;
498
- const tail = streamingText ? tailForDisplay(streamingText, MAX_STREAM_DISPLAY_CHARS) : "";
499
- const toolTail = toolStream?.text ? tailForDisplay(toolStream.text, MAX_STREAM_DISPLAY_CHARS) : "";
500
- return /* @__PURE__ */ jsxs(Fragment, { children: [
501
- /* @__PURE__ */ jsx(Static, { items: entries, children: (entry) => /* @__PURE__ */ jsx(Box, { marginBottom: entry.kind === "turn-summary" ? 1 : 0, children: /* @__PURE__ */ jsx(Entry, { entry, termWidth }) }, entry.id) }),
502
- tail ? /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginY: 1, children: [
503
- /* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
504
- /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "ASSISTANT: " }),
505
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "(streaming...)" })
506
- ] }),
507
- /* @__PURE__ */ jsx(Text, { color: "white", children: tail })
508
- ] }) : null,
509
- toolTail ? /* @__PURE__ */ jsx(
510
- ToolStreamBox,
511
- {
512
- name: toolStream.name,
513
- text: toolTail,
514
- startedAt: toolStream.startedAt,
515
- termWidth
516
- }
517
- ) : null
518
- ] });
519
- }
520
- var MAX_STREAM_DISPLAY_CHARS = 480;
521
- var MAX_STREAM_LINES = 8;
522
- function ToolStreamBox({
523
- name,
524
- text,
525
- startedAt,
526
- termWidth
316
+ function StatusBar({
317
+ model,
318
+ version,
319
+ state,
320
+ tokenCounter,
321
+ hint,
322
+ queueCount = 0,
323
+ yolo = false,
324
+ autonomy,
325
+ elapsedMs,
326
+ todos,
327
+ plan,
328
+ fleet,
329
+ fleetAgents,
330
+ git,
331
+ subagentCount = 0,
332
+ context,
333
+ projectName,
334
+ processCount,
335
+ hiddenItems,
336
+ eternalStage,
337
+ goalSummary
527
338
  }) {
528
- const [tick, setTick] = useState(0);
529
- useEffect(() => {
530
- const t = setInterval(() => setTick((n) => n + 1), 500);
531
- return () => clearInterval(t);
532
- }, []);
533
- const elapsedMs = Date.now() - startedAt;
534
- const lines = text.split("\n");
535
- const totalLines = lines.length;
536
- const hidden = Math.max(0, totalLines - MAX_STREAM_LINES);
537
- const visible = hidden > 0 ? lines.slice(hidden) : lines;
538
- const contentWidth = Math.max(20, Math.min(termWidth - 4, 100));
539
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 0, children: [
540
- /* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
541
- /* @__PURE__ */ jsx(Text, { color: "yellow", children: "\u25C6 " }),
542
- /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: name }),
543
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \u23F1 ${fmtDuration2(elapsedMs)}` }),
544
- hidden > 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` (${totalLines} lines, showing last ${MAX_STREAM_LINES})` }) : null
545
- ] }),
546
- /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [
547
- hidden > 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, italic: true, children: ` \u2026 ${hidden} more line${hidden === 1 ? "" : "s"} above` }) : null,
548
- visible.map((line, i) => {
549
- const key = i;
550
- const trimmed = line.length > contentWidth ? `${line.slice(0, contentWidth - 1)}\u2026` : line;
551
- return /* @__PURE__ */ jsx(Text, { dimColor: true, children: trimmed || " " }, key);
552
- })
553
- ] })
554
- ] });
555
- }
556
- function tailForDisplay(text, maxChars) {
557
- if (text.length <= maxChars) return text;
558
- const cut = text.length - maxChars;
559
- const nl = text.indexOf("\n", cut);
560
- if (nl !== -1 && nl < cut + 80) {
561
- return `\u2026 ${text.slice(nl + 1)}`;
562
- }
563
- return `\u2026 ${text.slice(cut)}`;
564
- }
565
- function DiffBlock({ rows, hidden }) {
566
- let gutterWidth = 1;
567
- for (const r of rows) {
568
- const n = r.kind === "del" ? r.oldLine : r.newLine;
569
- if (typeof n === "number") {
570
- const w = String(n).length;
571
- if (w > gutterWidth) gutterWidth = w;
572
- }
573
- }
574
- const blank = " ".repeat(gutterWidth);
575
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginLeft: 4, marginTop: 0, children: [
576
- rows.map((row, i) => {
577
- const key = i;
578
- if (row.kind === "hunk") {
579
- return /* @__PURE__ */ jsx(Text, { color: "cyan", dimColor: true, children: row.text }, key);
580
- }
581
- if (row.kind === "meta") {
582
- return /* @__PURE__ */ jsx(Text, { dimColor: true, children: `${blank} ${row.text}` }, key);
583
- }
584
- const lnNumber = row.kind === "del" ? row.oldLine : row.newLine;
585
- const lnText = typeof lnNumber === "number" ? String(lnNumber).padStart(gutterWidth, " ") : blank;
586
- if (row.kind === "ctx") {
587
- return /* @__PURE__ */ jsx(Text, { dimColor: true, children: `${lnText} ${row.text}` }, key);
588
- }
589
- const bg = row.kind === "add" ? "greenBright" : "redBright";
590
- return /* @__PURE__ */ jsxs(Text, { children: [
591
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: `${lnText} ` }),
592
- /* @__PURE__ */ jsx(Text, { backgroundColor: bg, color: "black", children: row.text })
593
- ] }, key);
594
- }),
595
- hidden > 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, italic: true, children: `${blank} \u2026 ${hidden} more line${hidden === 1 ? "" : "s"}` }) : null
596
- ] });
597
- }
598
- function Entry({
599
- entry,
600
- termWidth
601
- }) {
602
- switch (entry.kind) {
603
- case "user":
604
- return /* @__PURE__ */ jsxs(Text, { children: [
605
- /* @__PURE__ */ jsx(Text, { color: entry.queued ? "yellow" : "cyan", children: entry.queued ? "\u231B" : "\u203A" }),
606
- " ",
607
- /* @__PURE__ */ jsx(Text, { dimColor: entry.queued ?? false, children: entry.text }),
608
- entry.queued ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: " (queued)" }) : null
609
- ] });
610
- case "assistant":
611
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginY: 1, children: [
612
- /* @__PURE__ */ jsx(Box, { flexDirection: "row", children: /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "ASSISTANT: " }) }),
613
- /* @__PURE__ */ jsx(Text, { color: "white", children: renderMarkdownTables(entry.text, termWidth) })
614
- ] });
615
- case "tool": {
616
- const argSummary = formatToolArgs(entry.name, entry.input);
617
- const outLines = formatToolOutput(
618
- entry.name,
619
- entry.output,
620
- entry.ok,
621
- entry.outputBytes,
622
- entry.outputLines
623
- );
624
- const diff = entry.ok ? extractDiffPreview(entry.name, entry.output) : void 0;
625
- const sizeChip = (() => {
626
- if (!entry.ok) return "";
627
- const parts = [];
628
- if (entry.outputLines !== void 0 && entry.outputLines > 0) {
629
- parts.push(`${entry.outputLines} L`);
630
- }
631
- if (entry.outputBytes && entry.outputBytes > 0) {
632
- parts.push(fmtBytes2(entry.outputBytes));
633
- }
634
- if (entry.outputTokens && entry.outputTokens > 0) {
635
- parts.push(`\u2248${fmtTok(entry.outputTokens)} tok`);
636
- }
637
- return parts.join(" \xB7 ");
638
- })();
639
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
640
- /* @__PURE__ */ jsxs(Text, { children: [
641
- /* @__PURE__ */ jsx(Text, { color: entry.ok ? "green" : "red", children: entry.ok ? "\u25CF" : "\u2717" }),
642
- " ",
643
- /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: entry.name }),
644
- argSummary ? /* @__PURE__ */ jsxs(Fragment, { children: [
645
- /* @__PURE__ */ jsx(Text, { children: " " }),
646
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: argSummary })
339
+ const hiddenSet = new Set(hiddenItems);
340
+ const usage = tokenCounter?.total();
341
+ const cost = tokenCounter?.estimateCost();
342
+ const cache2 = tokenCounter?.cacheStats();
343
+ const { label: stateLabel, color: stateColor } = stateChip(state, fleet?.running ?? 0);
344
+ const hasSecondLine = yolo || autonomy && autonomy !== "off" || elapsedMs !== void 0 || git !== null && git !== void 0 || projectName !== void 0 && projectName.length > 0 || goalSummary !== null && goalSummary !== void 0;
345
+ const fleetHasActivity = fleet && (fleet.running > 0 || fleet.idle > 0 || fleet.pending > 0 || fleet.completed > 0) || subagentCount > 0;
346
+ const hasThirdLine = todos && (todos.pending > 0 || todos.inProgress > 0 || todos.completed > 0) || plan && (plan.open > 0 || plan.inProgress > 0 || plan.done > 0) || fleetHasActivity;
347
+ return /* @__PURE__ */ jsxs(
348
+ Box,
349
+ {
350
+ flexDirection: "column",
351
+ paddingX: 1,
352
+ borderStyle: "single",
353
+ borderTop: true,
354
+ borderBottom: false,
355
+ borderLeft: false,
356
+ borderRight: false,
357
+ children: [
358
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 2, children: [
359
+ version ? /* @__PURE__ */ jsxs(Fragment, { children: [
360
+ /* @__PURE__ */ jsxs(Text, { children: [
361
+ /* @__PURE__ */ jsx(Text, { color: "blue", bold: true, children: "WS" }),
362
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
363
+ " v",
364
+ version
365
+ ] })
366
+ ] }),
367
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" })
647
368
  ] }) : null,
648
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \xB7 ${fmtDuration2(entry.durationMs)}` }),
649
- sizeChip ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \xB7 ${sizeChip}` }) : null
369
+ /* @__PURE__ */ jsxs(Text, { color: stateColor, children: [
370
+ "\u25CF ",
371
+ stateLabel
372
+ ] }),
373
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
374
+ /* @__PURE__ */ jsx(Text, { color: "magenta", children: model }),
375
+ context && context.max > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
376
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
377
+ /* @__PURE__ */ jsx(ContextChip, { ctx: context })
378
+ ] }) : null,
379
+ usage ? /* @__PURE__ */ jsxs(Fragment, { children: [
380
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
381
+ /* @__PURE__ */ jsxs(Text, { children: [
382
+ "\u2191",
383
+ " ",
384
+ /* @__PURE__ */ jsx(Text, { color: "cyan", children: fmtTok(usage.input + (usage.cacheRead ?? 0) + (usage.cacheWrite ?? 0)) }),
385
+ " ",
386
+ "\u2193 ",
387
+ /* @__PURE__ */ jsx(Text, { color: "cyan", children: fmtTok(usage.output) })
388
+ ] })
389
+ ] }) : null,
390
+ cache2 && cache2.hitRatio > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
391
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
392
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
393
+ "cache ",
394
+ (cache2.hitRatio * 100).toFixed(0),
395
+ "%"
396
+ ] })
397
+ ] }) : null,
398
+ cost && cost.total > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
399
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
400
+ /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
401
+ "$",
402
+ cost.total.toFixed(4)
403
+ ] })
404
+ ] }) : null,
405
+ queueCount > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
406
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
407
+ /* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
408
+ "\u231B queued: ",
409
+ queueCount
410
+ ] })
411
+ ] }) : null,
412
+ typeof processCount === "number" && processCount > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
413
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
414
+ /* @__PURE__ */ jsxs(Text, { color: "red", children: [
415
+ "\u26A1 ",
416
+ processCount,
417
+ " process",
418
+ processCount === 1 ? "" : "es"
419
+ ] })
420
+ ] }) : null,
421
+ hint ? /* @__PURE__ */ jsxs(Fragment, { children: [
422
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
423
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: hint })
424
+ ] }) : null
650
425
  ] }),
651
- outLines.map((line, i) => (
652
- // biome-ignore lint/suspicious/noArrayIndexKey: tool output lines are static, index is stable
653
- /* @__PURE__ */ jsxs(Text, { children: [
654
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: i === outLines.length - 1 && !diff ? " \u2514\u2500 " : " \u251C\u2500 " }),
655
- /* @__PURE__ */ jsx(
426
+ hasSecondLine ? /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 2, children: [
427
+ yolo ? /* @__PURE__ */ jsx(Text, { color: "red", bold: true, children: "\u26A0 YOLO" }) : null,
428
+ autonomy && autonomy !== "off" ? /* @__PURE__ */ jsxs(Fragment, { children: [
429
+ yolo ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
430
+ /* @__PURE__ */ jsxs(
656
431
  Text,
657
432
  {
658
- color: !entry.ok || line.startsWith("!") ? "red" : void 0,
659
- dimColor: entry.ok && !line.startsWith("!"),
660
- children: line
433
+ color: autonomy === "eternal" ? "red" : autonomy === "auto" ? "yellow" : "cyan",
434
+ bold: true,
435
+ children: [
436
+ "\u221E ",
437
+ autonomy.toUpperCase()
438
+ ]
661
439
  }
662
440
  )
663
- ] }, i)
664
- )),
665
- diff ? /* @__PURE__ */ jsx(DiffBlock, { rows: diff.rows, hidden: diff.hidden }) : null
666
- ] });
667
- }
668
- case "info":
669
- return /* @__PURE__ */ jsx(Text, { dimColor: true, children: entry.text });
670
- case "warn":
671
- return /* @__PURE__ */ jsx(Text, { color: "yellow", children: entry.text });
672
- case "error":
673
- return /* @__PURE__ */ jsx(Text, { color: "red", children: entry.text });
674
- case "turn-summary":
675
- return /* @__PURE__ */ jsx(Text, { dimColor: true, children: entry.text });
676
- case "confirm":
677
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, marginY: 1, children: [
678
- /* @__PURE__ */ jsxs(Text, { bold: true, color: "yellow", children: [
679
- "\u26A0 Confirm: ",
680
- entry.toolName
681
- ] }),
682
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Waiting for y / n / a / d..." })
683
- ] });
684
- case "banner":
685
- return /* @__PURE__ */ jsx(Banner, { entry });
686
- case "subagent": {
687
- const lines = entry.text.split("\n");
688
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
689
- /* @__PURE__ */ jsxs(Text, { children: [
690
- /* @__PURE__ */ jsx(Text, { color: entry.agentColor, bold: true, children: `[${entry.agentLabel}]` }),
691
- /* @__PURE__ */ jsx(Text, { children: " " }),
692
- /* @__PURE__ */ jsx(Text, { color: entry.agentColor, children: entry.icon }),
693
- /* @__PURE__ */ jsx(Text, { children: " " }),
694
- /* @__PURE__ */ jsx(Text, { children: lines[0] ?? "" }),
695
- entry.detail ? /* @__PURE__ */ jsxs(Fragment, { children: [
696
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \xB7 " }),
697
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: entry.detail })
698
- ] }) : null
699
- ] }),
700
- lines.slice(1).map((line, i) => (
701
- // biome-ignore lint/suspicious/noArrayIndexKey: stable line index
702
- /* @__PURE__ */ jsxs(Text, { children: [
703
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: " " }),
704
- /* @__PURE__ */ jsx(Text, { children: line })
705
- ] }, i)
706
- ))
707
- ] });
708
- }
709
- }
710
- }
711
- function Banner({
712
- entry
713
- }) {
714
- const cwdShort = shortenPath(entry.cwd, 48);
715
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 2, paddingY: 0, children: [
716
- /* @__PURE__ */ jsxs(Text, { children: [
717
- /* @__PURE__ */ jsx(Text, { color: "magenta", bold: true, children: " \u259F\u259B " }),
718
- /* @__PURE__ */ jsx(Text, { color: "magenta", bold: true, children: "WrongStack" }),
719
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: " v" }),
720
- /* @__PURE__ */ jsx(Text, { children: entry.version })
721
- ] }),
722
- /* @__PURE__ */ jsx(Text, { dimColor: true, italic: true, children: " Built on the wrong stack. Shipped anyway." }),
723
- /* @__PURE__ */ jsxs(Text, { children: [
724
- /* @__PURE__ */ jsx(Text, { color: "cyan", children: " provider " }),
725
- /* @__PURE__ */ jsxs(Text, { children: [
726
- entry.provider,
727
- "/",
728
- entry.model
729
- ] })
730
- ] }),
731
- entry.family ? /* @__PURE__ */ jsxs(Text, { children: [
732
- /* @__PURE__ */ jsx(Text, { color: "cyan", children: " family " }),
733
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: entry.family })
734
- ] }) : null,
735
- entry.keyTail ? /* @__PURE__ */ jsxs(Text, { children: [
736
- /* @__PURE__ */ jsx(Text, { color: "cyan", children: " key " }),
737
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u25CF\u25CF\u25CF\u2026" }),
738
- /* @__PURE__ */ jsx(Text, { children: entry.keyTail })
739
- ] }) : null,
740
- /* @__PURE__ */ jsxs(Text, { children: [
741
- /* @__PURE__ */ jsx(Text, { color: "cyan", children: " cwd " }),
742
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: cwdShort })
441
+ ] }) : null,
442
+ eternalStage ? /* @__PURE__ */ jsxs(Fragment, { children: [
443
+ yolo || autonomy && autonomy !== "off" ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
444
+ /* @__PURE__ */ jsx(EternalStageChip, { stage: eternalStage })
445
+ ] }) : null,
446
+ elapsedMs !== void 0 && !hiddenSet.has("elapsed") ? /* @__PURE__ */ jsxs(Fragment, { children: [
447
+ yolo || autonomy && autonomy !== "off" || eternalStage ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
448
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
449
+ "\u23F1 ",
450
+ fmtElapsed(elapsedMs)
451
+ ] })
452
+ ] }) : null,
453
+ projectName ? /* @__PURE__ */ jsxs(Fragment, { children: [
454
+ yolo || elapsedMs !== void 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
455
+ /* @__PURE__ */ jsxs(Text, { color: "blue", children: [
456
+ "\u{1F4C1} ",
457
+ projectName
458
+ ] })
459
+ ] }) : null,
460
+ goalSummary ? /* @__PURE__ */ jsxs(Fragment, { children: [
461
+ yolo || elapsedMs !== void 0 || projectName ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
462
+ /* @__PURE__ */ jsxs(Text, { color: goalSummary.goalState === "active" ? "green" : goalSummary.goalState === "paused" ? "yellow" : goalSummary.goalState === "completed" ? "green" : "dim", children: [
463
+ "\u{1F3AF} ",
464
+ goalSummary.goal.length > 40 ? `${goalSummary.goal.slice(0, 37)}\u2026` : goalSummary.goal,
465
+ " [",
466
+ goalSummary.goalState,
467
+ "] (iter ",
468
+ goalSummary.iterations,
469
+ ")"
470
+ ] })
471
+ ] }) : null,
472
+ git ? /* @__PURE__ */ jsxs(Fragment, { children: [
473
+ yolo || elapsedMs !== void 0 || projectName ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
474
+ /* @__PURE__ */ jsxs(Text, { children: [
475
+ /* @__PURE__ */ jsxs(Text, { color: "magenta", children: [
476
+ "\u2387 ",
477
+ git.branch
478
+ ] }),
479
+ git.added > 0 ? /* @__PURE__ */ jsxs(Text, { color: "green", children: [
480
+ " +",
481
+ git.added
482
+ ] }) : null,
483
+ git.deleted > 0 ? /* @__PURE__ */ jsxs(Text, { color: "red", children: [
484
+ " -",
485
+ git.deleted
486
+ ] }) : null,
487
+ git.untracked > 0 ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
488
+ " ?",
489
+ git.untracked
490
+ ] }) : null
491
+ ] })
492
+ ] }) : null
493
+ ] }) : null,
494
+ hasThirdLine ? /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 2, children: [
495
+ todos && (todos.pending > 0 || todos.inProgress > 0 || todos.completed > 0) ? /* @__PURE__ */ jsxs(Text, { children: [
496
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "todos " }),
497
+ todos.inProgress > 0 ? /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
498
+ "\u231B",
499
+ todos.inProgress
500
+ ] }) : null,
501
+ todos.inProgress > 0 && (todos.pending > 0 || todos.completed > 0) ? " " : "",
502
+ todos.pending > 0 ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
503
+ "\u2610",
504
+ todos.pending
505
+ ] }) : null,
506
+ todos.pending > 0 && todos.completed > 0 ? " " : "",
507
+ todos.completed > 0 ? /* @__PURE__ */ jsxs(Text, { color: "green", children: [
508
+ "\u2713",
509
+ todos.completed
510
+ ] }) : null
511
+ ] }) : null,
512
+ plan && (plan.open > 0 || plan.inProgress > 0 || plan.done > 0) ? /* @__PURE__ */ jsxs(Fragment, { children: [
513
+ todos && (todos.pending > 0 || todos.inProgress > 0 || todos.completed > 0) ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
514
+ /* @__PURE__ */ jsxs(Text, { children: [
515
+ /* @__PURE__ */ jsx(Text, { color: "cyan", children: "\u{1F4CB} " }),
516
+ plan.inProgress > 0 ? /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
517
+ "\u231B",
518
+ plan.inProgress
519
+ ] }) : null,
520
+ plan.inProgress > 0 && (plan.open > 0 || plan.done > 0) ? " " : "",
521
+ plan.open > 0 ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
522
+ "\u2610",
523
+ plan.open
524
+ ] }) : null,
525
+ plan.open > 0 && plan.done > 0 ? " " : "",
526
+ plan.done > 0 ? /* @__PURE__ */ jsxs(Text, { color: "green", children: [
527
+ "\u2713",
528
+ plan.done
529
+ ] }) : null
530
+ ] })
531
+ ] }) : null,
532
+ fleetHasActivity ? /* @__PURE__ */ jsxs(Fragment, { children: [
533
+ todos && (todos.pending > 0 || todos.inProgress > 0 || todos.completed > 0) || plan && (plan.open > 0 || plan.inProgress > 0 || plan.done > 0) ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
534
+ fleet ? /* @__PURE__ */ jsxs(Text, { children: [
535
+ /* @__PURE__ */ jsx(Text, { color: "blue", children: "\u{1F310} " }),
536
+ fleet.running > 0 ? /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
537
+ "\u25B6",
538
+ fleet.running
539
+ ] }) : null,
540
+ fleet.running > 0 && (fleet.pending > 0 || fleet.idle > 0 || fleet.completed > 0) ? " " : "",
541
+ fleet.pending > 0 ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
542
+ "\u2610",
543
+ fleet.pending
544
+ ] }) : null,
545
+ fleet.pending > 0 && (fleet.idle > 0 || fleet.completed > 0) ? " " : "",
546
+ fleet.idle > 0 ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
547
+ "\xB7",
548
+ fleet.idle,
549
+ "idle"
550
+ ] }) : null,
551
+ fleet.idle > 0 && fleet.completed > 0 ? " " : "",
552
+ fleet.completed > 0 ? /* @__PURE__ */ jsxs(Text, { color: "green", children: [
553
+ "\u2713",
554
+ fleet.completed
555
+ ] }) : null
556
+ ] }) : /* @__PURE__ */ jsxs(Text, { color: "blue", children: [
557
+ "\u{1F310} ",
558
+ subagentCount,
559
+ " agent",
560
+ subagentCount === 1 ? "" : "s"
561
+ ] })
562
+ ] }) : null
563
+ ] }) : null,
564
+ fleetAgents && fleetAgents.length > 0 ? /* @__PURE__ */ jsx(Box, { flexDirection: "row", gap: 2, children: fleetAgents.map((a, i) => (
565
+ // biome-ignore lint/suspicious/noArrayIndexKey: agent list is stable per render
566
+ /* @__PURE__ */ jsxs(Text, { children: [
567
+ /* @__PURE__ */ jsx(Text, { color: a.color, bold: true, children: a.label }),
568
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " " }),
569
+ /* @__PURE__ */ jsx(Text, { color: a.running ? "yellow" : void 0, dimColor: !a.running, children: a.running ? "\u25B6" : "\xB7" }),
570
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " " }),
571
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: fmtElapsed(a.elapsedMs) }),
572
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \xB7 " }),
573
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
574
+ a.toolCalls,
575
+ "t"
576
+ ] }),
577
+ a.tool ? /* @__PURE__ */ jsxs(Fragment, { children: [
578
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \xB7 " }),
579
+ /* @__PURE__ */ jsx(Text, { color: "cyan", children: a.tool })
580
+ ] }) : null,
581
+ a.extensions && a.extensions > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
582
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \xB7 " }),
583
+ /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
584
+ "\u26A1\xD7",
585
+ a.extensions
586
+ ] })
587
+ ] }) : null
588
+ ] }, i)
589
+ )) }) : null
590
+ ]
591
+ }
592
+ );
593
+ }
594
+ function EternalStageChip({
595
+ stage
596
+ }) {
597
+ switch (stage.phase) {
598
+ case "idle":
599
+ return /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2B1C idle" });
600
+ case "decide":
601
+ return /* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
602
+ "\u2B07 decide: ",
603
+ stage.reason
604
+ ] });
605
+ case "execute":
606
+ return /* @__PURE__ */ jsxs(Text, { color: "green", children: [
607
+ "\u25B6 ",
608
+ /* @__PURE__ */ jsx(Text, { bold: true, children: "execute" }),
609
+ stage.task ? `(${stage.task})` : ""
610
+ ] });
611
+ case "reflect":
612
+ return /* @__PURE__ */ jsxs(Text, { color: stage.status === "success" ? "green" : stage.status === "failure" ? "red" : "yellow", children: [
613
+ "\u21A9 reflect: ",
614
+ stage.status
615
+ ] });
616
+ case "sleep":
617
+ return /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
618
+ "\u{1F4A4} sleep ",
619
+ Math.round(stage.ms / 1e3),
620
+ "s"
621
+ ] });
622
+ case "paused":
623
+ return /* @__PURE__ */ jsx(Text, { color: "yellow", children: "\u23F8 paused" });
624
+ case "stopped":
625
+ return /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u25A0 stopped" });
626
+ case "error":
627
+ return /* @__PURE__ */ jsxs(Text, { color: "red", children: [
628
+ "\u26A0 error: ",
629
+ stage.message
630
+ ] });
631
+ }
632
+ }
633
+ function ContextChip({ ctx }) {
634
+ const ratio = Math.max(0, Math.min(1, ctx.used / ctx.max));
635
+ const pct = Math.round(ratio * 100);
636
+ const color = ratio >= 0.85 ? "red" : ratio >= 0.65 ? "yellow" : "cyan";
637
+ return /* @__PURE__ */ jsxs(Text, { children: [
638
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "ctx " }),
639
+ /* @__PURE__ */ jsx(Text, { color, children: renderProgress(ratio, 10) }),
640
+ /* @__PURE__ */ jsxs(Text, { color, children: [
641
+ " ",
642
+ pct,
643
+ "%"
743
644
  ] }),
744
- /* @__PURE__ */ jsxs(Text, { children: [
745
- /* @__PURE__ */ jsx(Text, { color: "cyan", children: " hints " }),
746
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "/help \xB7 /init \xB7 /memory \xB7 /queue \xB7 /exit" })
645
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
646
+ " ",
647
+ "(",
648
+ fmtTok(ctx.used),
649
+ "/",
650
+ fmtTok(ctx.max),
651
+ ")"
747
652
  ] })
748
653
  ] });
749
654
  }
750
- function shortenPath(p, max) {
751
- if (p.length <= max) return p;
752
- return `\u2026${p.slice(p.length - (max - 1))}`;
655
+ function stateChip(state, fleetRunning) {
656
+ if (state === "idle" && fleetRunning > 0) {
657
+ return { label: `agents \u25B6${fleetRunning}`, color: "magenta" };
658
+ }
659
+ if (state === "idle") return { label: "idle", color: "cyan" };
660
+ if (state === "aborting") return { label: "aborting\u2026", color: "yellow" };
661
+ return { label: "thinking\u2026", color: "green" };
662
+ }
663
+ var FILLED = "\u2588";
664
+ var EMPTY = "\u2591";
665
+ function renderProgress(ratio, width) {
666
+ const clamped = Math.max(0, Math.min(1, ratio));
667
+ const filled = clamped === 0 ? 0 : Math.max(1, Math.round(clamped * width));
668
+ const capped = Math.min(width, filled);
669
+ return FILLED.repeat(capped) + EMPTY.repeat(width - capped);
753
670
  }
754
671
  function fmtTok(n) {
755
- if (!Number.isFinite(n) || n <= 0) return "0";
756
- if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
757
- if (n >= 1e3) return `${(n / 1e3).toFixed(n >= 1e4 ? 0 : 1)}k`;
758
- return String(n);
672
+ if (n < 1e3) return String(n);
673
+ if (n < 1e6) return `${(n / 1e3).toFixed(n < 1e4 ? 1 : 0)}k`;
674
+ return `${(n / 1e6).toFixed(1)}M`;
759
675
  }
760
- function fmtDuration2(ms) {
761
- if (ms < 1e3) return `${ms}ms`;
762
- if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
676
+ function fmtElapsed(ms) {
763
677
  const totalSec = Math.floor(ms / 1e3);
764
- return `${Math.floor(totalSec / 60)}m${totalSec % 60}s`;
678
+ const h = Math.floor(totalSec / 3600);
679
+ const m = Math.floor(totalSec % 3600 / 60);
680
+ const s = totalSec % 60;
681
+ if (h > 0) {
682
+ return `${h}:${pad2(m)}:${pad2(s)}`;
683
+ }
684
+ return `${pad2(m)}:${pad2(s)}`;
765
685
  }
766
- var ARG_BUDGET = 60;
767
- var OUT_BUDGET = 80;
768
- function formatToolArgs(toolName, input) {
769
- if (!input || typeof input !== "object") return "";
770
- const obj = input;
771
- switch (toolName) {
772
- case "read":
773
- case "write":
774
- case "edit":
775
- case "patch":
776
- case "document":
777
- case "list_dir":
778
- case "ls":
779
- case "tree": {
780
- const p = stringOf(obj["path"]) ?? stringOf(obj["file"]);
781
- return p ? shortenPath(p, ARG_BUDGET) : "";
782
- }
783
- case "grep":
784
- case "search":
785
- case "replace": {
786
- const pat = stringOf(obj["pattern"]) ?? stringOf(obj["query"]);
787
- const scope = stringOf(obj["path"]) ?? stringOf(obj["glob"]);
788
- const head = pat ? `"${truncMid(pat, 36)}"` : "";
789
- const tail = scope ? ` in ${shortenPath(scope, 28)}` : "";
790
- return `${head}${tail}` || (stringOf(obj["command"]) ?? "");
791
- }
792
- case "glob": {
793
- const pat = stringOf(obj["pattern"]) ?? stringOf(obj["glob"]);
794
- return pat ? `"${truncMid(pat, ARG_BUDGET - 2)}"` : "";
795
- }
796
- case "bash":
797
- case "shell":
798
- case "exec":
799
- case "install":
800
- case "git": {
801
- const cmd = stringOf(obj["command"]) ?? stringOf(obj["args"]);
802
- return cmd ? truncMid(cmd, ARG_BUDGET) : "";
803
- }
804
- case "diff": {
805
- const files = Array.isArray(obj["files"]) ? obj["files"] : void 0;
806
- if (files && files.length > 0) {
807
- const head = stringOf(files[0]) ?? "";
808
- const rest = files.length > 1 ? ` (+${files.length - 1})` : "";
809
- return head ? `${shortenPath(head, 50)}${rest}` : "";
810
- }
811
- const mode = stringOf(obj["mode"]);
812
- return mode ? `mode: ${mode}` : "";
813
- }
814
- case "fetch":
815
- case "webfetch":
816
- case "web_fetch": {
817
- const u = stringOf(obj["url"]);
818
- return u ? truncMid(u, ARG_BUDGET) : "";
819
- }
820
- case "todo": {
821
- const list = obj["todos"];
822
- if (Array.isArray(list)) return `${list.length} item${list.length === 1 ? "" : "s"}`;
823
- return "";
824
- }
825
- case "lint":
826
- case "format":
827
- case "typecheck":
828
- case "test":
829
- case "audit":
830
- case "outdated": {
831
- const files = obj["files"];
832
- if (Array.isArray(files) && files.length > 0) {
833
- const first = stringOf(files[0]);
834
- const more = files.length > 1 ? ` (+${files.length - 1})` : "";
835
- return first ? `${shortenPath(first, 50)}${more}` : `${files.length} files`;
836
- }
837
- const filter = stringOf(obj["filter"]) ?? stringOf(obj["pattern"]);
838
- return filter ? `"${truncMid(filter, ARG_BUDGET - 2)}"` : "";
839
- }
840
- case "json": {
841
- const file = stringOf(obj["file"]);
842
- const q = stringOf(obj["query"]);
843
- if (file) return q ? `${shortenPath(file, 40)} ${q}` : shortenPath(file, ARG_BUDGET);
844
- return q ? truncMid(q, ARG_BUDGET) : "";
686
+ function pad2(n) {
687
+ return n < 10 ? `0${n}` : String(n);
688
+ }
689
+ var STATUS = {
690
+ idle: { icon: "\u25CB", color: "gray" },
691
+ running: { icon: "\u25B6", color: "yellow" },
692
+ success: { icon: "\u2713", color: "green" },
693
+ failed: { icon: "\u2717", color: "red" },
694
+ timeout: { icon: "\u23F1", color: "yellow" },
695
+ stopped: { icon: "\u2298", color: "gray" }
696
+ };
697
+ var SPARK = ["\u2581", "\u2582", "\u2583", "\u2584", "\u2585", "\u2586", "\u2587", "\u2588"];
698
+ function bucketActivity(recentTools, now, bins = 12, binMs = 2e3) {
699
+ const out = new Array(bins).fill(0);
700
+ const windowStart = now - bins * binMs;
701
+ for (const t of recentTools) {
702
+ if (t.at < windowStart || t.at > now) continue;
703
+ let idx = Math.floor((t.at - windowStart) / binMs);
704
+ if (idx < 0) idx = 0;
705
+ if (idx >= bins) idx = bins - 1;
706
+ out[idx]++;
707
+ }
708
+ return out;
709
+ }
710
+ function sparkline(values) {
711
+ if (values.length === 0) return "";
712
+ const max = Math.max(1, ...values);
713
+ return values.map((v) => {
714
+ if (v <= 0) return SPARK[0];
715
+ const idx = Math.min(SPARK.length - 1, Math.ceil(v / max * (SPARK.length - 1)));
716
+ return SPARK[idx];
717
+ }).join("");
718
+ }
719
+ function fmtTokens(n) {
720
+ if (n < 1e3) return String(n);
721
+ if (n < 1e6) return `${(n / 1e3).toFixed(1)}k`;
722
+ return `${(n / 1e6).toFixed(1)}M`;
723
+ }
724
+ function FleetMonitor({
725
+ entries,
726
+ totalCost,
727
+ totalTokens,
728
+ maxConcurrent = 4,
729
+ nowTick
730
+ }) {
731
+ const all = Object.values(entries);
732
+ const running = all.filter((e) => e.status === "running");
733
+ const done = all.filter((e) => e.status === "success").length;
734
+ const failed = all.filter((e) => e.status === "failed" || e.status === "timeout").length;
735
+ const concurrencyRatio = maxConcurrent > 0 ? running.length / maxConcurrent : 0;
736
+ const maxTools = Math.max(1, ...all.map((e) => e.toolCalls));
737
+ const ordered = [...all].sort((a, b) => {
738
+ const ra = a.status === "running" ? 0 : a.status === "idle" ? 1 : 2;
739
+ const rb = b.status === "running" ? 0 : b.status === "idle" ? 1 : 2;
740
+ if (ra !== rb) return ra - rb;
741
+ return a.startedAt - b.startedAt;
742
+ });
743
+ const shown = ordered.slice(0, 8);
744
+ const events = [];
745
+ for (const e of all) {
746
+ events.push({ at: e.startedAt, icon: "\u25CF", color: "cyan", text: `${e.name} spawned` });
747
+ if (e.status !== "running" && e.status !== "idle") {
748
+ const s = STATUS[e.status];
749
+ events.push({
750
+ at: e.lastEventAt,
751
+ icon: s.icon,
752
+ color: s.color,
753
+ text: `${e.name} ${e.status} (${e.toolCalls}t)`
754
+ });
845
755
  }
846
- case "scaffold": {
847
- const tmpl = stringOf(obj["template"]) ?? stringOf(obj["type"]);
848
- const name = stringOf(obj["name"]);
849
- if (tmpl && name) return `${tmpl} \u2192 ${truncMid(name, ARG_BUDGET - tmpl.length - 4)}`;
850
- return name ?? tmpl ?? "";
756
+ if (e.budgetWarning) {
757
+ events.push({
758
+ at: e.budgetWarning.at,
759
+ icon: "\u26A1",
760
+ color: "yellow",
761
+ text: `${e.name} ${e.budgetWarning.kind} ${e.budgetWarning.used}/${e.budgetWarning.limit} \u2014 extending`
762
+ });
851
763
  }
852
- case "remember":
853
- case "forget":
854
- case "memory": {
855
- const key = stringOf(obj["key"]) ?? stringOf(obj["name"]);
856
- return key ? truncMid(key, ARG_BUDGET) : "";
764
+ }
765
+ events.sort((a, b) => b.at - a.at);
766
+ const timeline = events.slice(0, 6);
767
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
768
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
769
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "FLEET MONITOR" }),
770
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
771
+ /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
772
+ "\u25B6",
773
+ running.length
774
+ ] }),
775
+ /* @__PURE__ */ jsxs(Text, { color: "green", children: [
776
+ "\u2713",
777
+ done
778
+ ] }),
779
+ failed > 0 ? /* @__PURE__ */ jsxs(Text, { color: "red", children: [
780
+ "\u2717",
781
+ failed
782
+ ] }) : null,
783
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7 Ctrl+F to close" })
784
+ ] }),
785
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
786
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "concurrency" }),
787
+ /* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
788
+ "[",
789
+ renderProgress(concurrencyRatio, 10),
790
+ "]"
791
+ ] }),
792
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
793
+ running.length,
794
+ "/",
795
+ maxConcurrent
796
+ ] }),
797
+ totalTokens ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
798
+ " ",
799
+ fmtTokens(totalTokens.input),
800
+ "\u2191 ",
801
+ fmtTokens(totalTokens.output),
802
+ "\u2193"
803
+ ] }) : null,
804
+ /* @__PURE__ */ jsx(Text, { color: "green", children: ` $${totalCost.toFixed(3)}` })
805
+ ] }),
806
+ shown.length === 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No subagents yet \u2014 spawn with /fleet spawn or /fleet dispatch." }) : null,
807
+ shown.map((e) => {
808
+ const s = STATUS[e.status];
809
+ const elapsed = e.status === "running" ? fmtElapsed(Math.max(0, nowTick - e.startedAt)) : e.status;
810
+ const spark = sparkline(bucketActivity(e.recentTools, nowTick));
811
+ const tool = e.currentTool?.name ?? e.recentTools[e.recentTools.length - 1]?.name ?? "";
812
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
813
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
814
+ /* @__PURE__ */ jsx(Text, { color: s.color, bold: true, children: s.icon }),
815
+ /* @__PURE__ */ jsx(Text, { bold: true, children: e.name.padEnd(12).slice(0, 12) }),
816
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: elapsed.padEnd(7).slice(0, 7) }),
817
+ /* @__PURE__ */ jsx(Text, { color: "cyan", children: renderProgress(e.toolCalls / maxTools, 10) }),
818
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
819
+ "L",
820
+ e.iterations,
821
+ " ",
822
+ e.toolCalls,
823
+ "t"
824
+ ] }),
825
+ e.extensions && e.extensions > 0 ? /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
826
+ "\u26A1\xD7",
827
+ e.extensions
828
+ ] }) : null
829
+ ] }),
830
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
831
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " " }),
832
+ /* @__PURE__ */ jsx(Text, { color: "green", children: spark }),
833
+ tool ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: tool }) : null
834
+ ] })
835
+ ] }, e.id);
836
+ }),
837
+ timeline.length > 0 ? /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
838
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "timeline" }),
839
+ timeline.map((ev, i) => (
840
+ // biome-ignore lint/suspicious/noArrayIndexKey: timeline is rebuilt per render
841
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
842
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: `${fmtElapsed(Math.max(0, nowTick - ev.at))} ago`.padEnd(10) }),
843
+ /* @__PURE__ */ jsx(Text, { color: ev.color, children: ev.icon }),
844
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: ev.text })
845
+ ] }, i)
846
+ ))
847
+ ] }) : null
848
+ ] });
849
+ }
850
+
851
+ // src/markdown-table.ts
852
+ function renderMarkdownTables(text, maxWidth) {
853
+ if (!text.includes("|")) return text;
854
+ const lines = text.split("\n");
855
+ const out = [];
856
+ let i = 0;
857
+ while (i < lines.length) {
858
+ const end = detectTable(lines, i);
859
+ if (end > i) {
860
+ out.push(renderTable(lines.slice(i, end), Math.max(20, maxWidth)));
861
+ i = end;
862
+ } else {
863
+ out.push(lines[i] ?? "");
864
+ i++;
857
865
  }
858
- case "mode": {
859
- const m = stringOf(obj["mode"]) ?? stringOf(obj["name"]);
860
- return m ? truncMid(m, ARG_BUDGET) : "";
866
+ }
867
+ return out.join("\n");
868
+ }
869
+ var ROW_RE = /^\s*\|.*\|\s*$/;
870
+ var SEP_RE = /^\s*\|[\s\-:|]+\|\s*$/;
871
+ function detectTable(lines, start) {
872
+ if (start + 1 >= lines.length) return start;
873
+ if (!ROW_RE.test(lines[start] ?? "")) return start;
874
+ const sep2 = lines[start + 1] ?? "";
875
+ if (!SEP_RE.test(sep2) || !/-/.test(sep2)) return start;
876
+ let end = start + 2;
877
+ while (end < lines.length && ROW_RE.test(lines[end] ?? "")) end++;
878
+ return end;
879
+ }
880
+ function parseCells(line) {
881
+ const inner = line.trim().replace(/^\||\|$/g, "");
882
+ const parts = [];
883
+ let buf = "";
884
+ for (let i = 0; i < inner.length; i++) {
885
+ const ch = inner[i];
886
+ if (ch === "\\" && inner[i + 1] === "|") {
887
+ buf += "|";
888
+ i++;
889
+ continue;
861
890
  }
862
- case "logs": {
863
- const target = stringOf(obj["target"]) ?? stringOf(obj["service"]) ?? stringOf(obj["path"]);
864
- return target ? truncMid(target, ARG_BUDGET) : "";
891
+ if (ch === "|") {
892
+ parts.push(buf);
893
+ buf = "";
894
+ continue;
865
895
  }
896
+ buf += ch;
866
897
  }
867
- for (const key of ["path", "file", "url", "name", "query", "pattern", "command"]) {
868
- const v = stringOf(obj[key]);
869
- if (v) return truncMid(v, ARG_BUDGET);
898
+ parts.push(buf);
899
+ return parts.map((c) => c.trim());
900
+ }
901
+ function parseAlign(sep2) {
902
+ const t = sep2.trim();
903
+ const left = t.startsWith(":");
904
+ const right = t.endsWith(":");
905
+ if (left && right) return "center";
906
+ if (right) return "right";
907
+ return "left";
908
+ }
909
+ function renderTable(tableLines, maxWidth) {
910
+ const header = parseCells(tableLines[0] ?? "");
911
+ const sepCells = parseCells(tableLines[1] ?? "");
912
+ const cols = header.length;
913
+ const aligns = [];
914
+ for (let c = 0; c < cols; c++) {
915
+ aligns.push(parseAlign(sepCells[c] ?? ""));
870
916
  }
871
- try {
872
- return truncMid(JSON.stringify(obj), ARG_BUDGET);
873
- } catch {
874
- return "";
917
+ const dataRows = tableLines.slice(2).map(parseCells);
918
+ for (const row of dataRows) {
919
+ while (row.length < cols) row.push("");
920
+ row.length = cols;
921
+ }
922
+ const widths = computeWidths([header, ...dataRows], cols, maxWidth);
923
+ const lines = [];
924
+ lines.push(border("\u250C", "\u252C", "\u2510", widths));
925
+ lines.push(...renderRow(header, widths, aligns));
926
+ lines.push(border("\u251C", "\u253C", "\u2524", widths));
927
+ for (const row of dataRows) {
928
+ lines.push(...renderRow(row, widths, aligns));
875
929
  }
930
+ lines.push(border("\u2514", "\u2534", "\u2518", widths));
931
+ return lines.join("\n");
876
932
  }
877
- function formatToolOutput(toolName, output, ok, _outputBytes, outputLines) {
878
- if (!output) return ok ? [] : ["failed"];
879
- const text = output.trim();
880
- if (!text) return ok ? [] : ["failed"];
881
- const json = tryParseJson(text);
882
- if (toolName === "write") {
883
- if (json && typeof json === "object") {
884
- const o = json;
885
- const bytes = numOf(o["bytes_written"]) ?? numOf(o["bytes"]);
886
- const created = o["created"] === true;
887
- const tag = created ? "created" : "updated";
888
- if (bytes !== void 0) return [`${tag} \xB7 ${fmtBytes2(bytes)}`];
889
- return [tag];
933
+ function computeWidths(allRows, cols, maxWidth) {
934
+ const overhead = 3 * cols + 1;
935
+ const avail = Math.max(cols * MIN_COL_WIDTH, maxWidth - overhead);
936
+ const natural = new Array(cols).fill(0);
937
+ for (const row of allRows) {
938
+ for (let c = 0; c < cols; c++) {
939
+ const cell = row[c] ?? "";
940
+ const w = longestWord(cell);
941
+ const total = cell.length;
942
+ natural[c] = Math.max(natural[c], total);
943
+ if (w > natural[c]) natural[c] = Math.min(total + 1, w);
890
944
  }
891
945
  }
892
- if (toolName === "edit") {
893
- if (json && typeof json === "object") {
894
- const o = json;
895
- const reps = numOf(o["replacements"]);
896
- if (reps !== void 0) return [`${reps} replacement${reps === 1 ? "" : "s"}`];
946
+ const sumNatural = natural.reduce((s, n) => s + n, 0);
947
+ if (sumNatural <= avail) return natural;
948
+ const widths = natural.slice();
949
+ let sum = sumNatural;
950
+ while (sum > avail) {
951
+ let maxIdx = -1;
952
+ let maxVal = MIN_COL_WIDTH;
953
+ for (let i = 0; i < cols; i++) {
954
+ const w = widths[i];
955
+ if (w > maxVal) {
956
+ maxVal = w;
957
+ maxIdx = i;
958
+ }
897
959
  }
960
+ if (maxIdx < 0) break;
961
+ widths[maxIdx]--;
962
+ sum--;
898
963
  }
899
- if (toolName === "patch") {
900
- if (json && typeof json === "object") {
901
- const o = json;
902
- const applied = numOf(o["applied"]);
903
- const rejected = numOf(o["rejected"]);
904
- const files = Array.isArray(o["files"]) ? o["files"] : void 0;
905
- const lines = [];
906
- if (applied !== void 0 || rejected !== void 0) {
907
- const parts = [];
908
- if (applied !== void 0) parts.push(`${applied} applied`);
909
- if (rejected !== void 0 && rejected > 0) parts.push(`${rejected} rejected`);
910
- lines.push(parts.join(" \xB7 "));
911
- }
912
- if (files && files.length > 0) {
913
- const first = stringOf(files[0]) ?? "";
914
- const more = files.length > 1 ? ` (+${files.length - 1})` : "";
915
- lines.push(`${shortenPath(first, 60)}${more}`);
916
- }
917
- if (lines.length > 0) return lines;
918
- }
919
- }
920
- if (toolName === "replace") {
921
- if (json && typeof json === "object") {
922
- const o = json;
923
- const files = numOf(o["files_modified"]);
924
- const reps = numOf(o["total_replacements"]);
925
- if (files !== void 0 && reps !== void 0) {
926
- return [
927
- `${reps} replacement${reps === 1 ? "" : "s"} in ${files} file${files === 1 ? "" : "s"}`
928
- ];
929
- }
964
+ return widths;
965
+ }
966
+ var MIN_COL_WIDTH = 4;
967
+ function longestWord(s) {
968
+ let max = 0;
969
+ for (const w of s.split(/\s+/)) if (w.length > max) max = w.length;
970
+ return max;
971
+ }
972
+ function border(left, mid, right, widths) {
973
+ return left + widths.map((w) => "\u2500".repeat(w + 2)).join(mid) + right;
974
+ }
975
+ function renderRow(cells, widths, aligns) {
976
+ const wrapped = cells.map((c, i) => wrapCell(c, widths[i] ?? MIN_COL_WIDTH));
977
+ const height = Math.max(1, ...wrapped.map((w) => w.length));
978
+ const out = [];
979
+ for (let line = 0; line < height; line++) {
980
+ const parts = [];
981
+ for (let c = 0; c < widths.length; c++) {
982
+ const w = widths[c] ?? MIN_COL_WIDTH;
983
+ const text = wrapped[c]?.[line] ?? "";
984
+ parts.push(padCell(text, w, aligns[c] ?? "left"));
930
985
  }
986
+ out.push("\u2502 " + parts.join(" \u2502 ") + " \u2502");
931
987
  }
932
- if (toolName === "diff") {
933
- if (json && typeof json === "object") {
934
- const o = json;
935
- const files = Array.isArray(o["files"]) ? o["files"] : void 0;
936
- const truncated = o["truncated"] === true;
937
- const mode = stringOf(o["mode"]);
938
- const diff = stringOf(o["diff"]);
939
- if (!diff) return [files && files.length === 0 ? "no changes" : "empty diff"];
940
- const head = [];
941
- if (mode) head.push(mode);
942
- if (files && files.length > 0)
943
- head.push(`${files.length} file${files.length === 1 ? "" : "s"}`);
944
- if (truncated) head.push("truncated");
945
- return head.length > 0 ? [head.join(" \xB7 ")] : [];
988
+ return out;
989
+ }
990
+ function wrapCell(text, width) {
991
+ if (text.length <= width) return [text];
992
+ const out = [];
993
+ const words = text.split(/(\s+)/);
994
+ let cur = "";
995
+ for (const word of words) {
996
+ if (!word) continue;
997
+ if (cur.length + word.length <= width) {
998
+ cur += word;
999
+ continue;
946
1000
  }
947
- }
948
- if (toolName === "read") {
949
- if (outputLines !== void 0) return [];
950
- if (json && typeof json === "object") {
951
- const o = json;
952
- const bytes = numOf(o["bytes"]);
953
- if (bytes !== void 0) return [`${fmtBytes2(bytes)} read`];
1001
+ if (cur) {
1002
+ out.push(cur.trimEnd());
1003
+ cur = "";
954
1004
  }
955
- const range = scanNumberedRange(text);
956
- if (range.count > 0 && range.first !== void 0 && range.last !== void 0) {
957
- if (range.first === range.last) {
958
- return [`L${range.first} \xB7 ${fmtBytes2(text.length)}`];
1005
+ if (word.length > width) {
1006
+ let rest = word;
1007
+ while (rest.length > width) {
1008
+ out.push(rest.slice(0, width));
1009
+ rest = rest.slice(width);
959
1010
  }
960
- const contiguous = range.count === range.last - range.first + 1;
961
- const head = `L${range.first}\u2013${range.last}`;
962
- const tail = contiguous ? `${range.count} line${range.count === 1 ? "" : "s"}` : `${range.count} lines (gaps)`;
963
- return [`${head} \xB7 ${tail} \xB7 ${fmtBytes2(text.length)}`];
1011
+ cur = rest;
1012
+ } else if (!/^\s+$/.test(word)) {
1013
+ cur = word;
964
1014
  }
965
1015
  }
966
- if (toolName === "grep" || toolName === "glob") {
967
- if (json && typeof json === "object") {
968
- const o = json;
969
- const matches = Array.isArray(o["matches"]) ? o["matches"] : void 0;
970
- const count = numOf(o["count"]) ?? matches?.length;
971
- const truncated = o["truncated"] === true;
972
- if (count !== void 0) {
973
- if (count === 0) return ["no matches"];
974
- const lines = [
975
- `${count} match${count === 1 ? "" : "es"}${truncated ? " (truncated)" : ""}`
976
- ];
977
- const firstHit = matches && matches.length > 0 ? formatMatchHit(matches[0]) : void 0;
978
- if (firstHit) lines.push(firstHit);
979
- return lines;
980
- }
981
- }
1016
+ if (cur) out.push(cur.trimEnd());
1017
+ return out.length === 0 ? [""] : out;
1018
+ }
1019
+ function padCell(text, width, align) {
1020
+ if (text.length >= width) return text.slice(0, width);
1021
+ const pad = width - text.length;
1022
+ if (align === "right") return " ".repeat(pad) + text;
1023
+ if (align === "center") {
1024
+ const l = Math.floor(pad / 2);
1025
+ return " ".repeat(l) + text + " ".repeat(pad - l);
982
1026
  }
983
- if (toolName === "bash" || toolName === "shell") {
984
- if (json && typeof json === "object") {
985
- const o = json;
986
- const exit = numOf(o["exit_code"]) ?? numOf(o["exitCode"]);
987
- const stdout = stringOf(o["stdout"]) ?? "";
988
- const stderr = stringOf(o["stderr"]) ?? "";
989
- const stdoutLines = countLines(stdout);
990
- const stderrLines = countLines(stderr);
991
- const head = [];
992
- if (exit !== void 0) head.push(`exit ${exit}`);
993
- const lineParts = [];
994
- if (stdoutLines > 0) lineParts.push(`${stdoutLines} out`);
995
- if (stderrLines > 0) lineParts.push(`${stderrLines} err`);
996
- if (lineParts.length > 0) head.push(lineParts.join(" \xB7 "));
997
- const lines = [];
998
- if (head.length > 0) lines.push(head.join(" \xB7 "));
999
- const stdoutPreview = firstNonEmpty(stdout);
1000
- const stderrPreview = firstNonEmpty(stderr);
1001
- if (stdoutPreview) lines.push(`"${truncMid(stdoutPreview, 70)}"`);
1002
- if (stderrPreview && stderrPreview !== stdoutPreview) {
1003
- lines.push(`! "${truncMid(stderrPreview, 70)}"`);
1027
+ return text + " ".repeat(pad);
1028
+ }
1029
+ function History({ entries, streamingText, toolStream }) {
1030
+ const { stdout } = useStdout();
1031
+ const termWidth = stdout?.columns ?? 80;
1032
+ const tail = streamingText ? tailForDisplay(streamingText, MAX_STREAM_DISPLAY_CHARS) : "";
1033
+ const toolTail = toolStream?.text ? tailForDisplay(toolStream.text, MAX_STREAM_DISPLAY_CHARS) : "";
1034
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1035
+ /* @__PURE__ */ jsx(Static, { items: entries, children: (entry) => /* @__PURE__ */ jsx(Box, { marginBottom: entry.kind === "turn-summary" ? 1 : 0, children: /* @__PURE__ */ jsx(Entry, { entry, termWidth }) }, entry.id) }),
1036
+ tail ? /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginY: 1, children: [
1037
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
1038
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "ASSISTANT: " }),
1039
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "(streaming...)" })
1040
+ ] }),
1041
+ /* @__PURE__ */ jsx(Text, { color: "white", children: tail })
1042
+ ] }) : null,
1043
+ toolTail ? /* @__PURE__ */ jsx(
1044
+ ToolStreamBox,
1045
+ {
1046
+ name: toolStream.name,
1047
+ text: toolTail,
1048
+ startedAt: toolStream.startedAt,
1049
+ termWidth
1004
1050
  }
1005
- if (lines.length > 0) return lines;
1006
- }
1007
- }
1008
- if (toolName === "todo") {
1009
- return ok ? [] : [text.split("\n")[0] ?? ""];
1051
+ ) : null
1052
+ ] });
1053
+ }
1054
+ var MAX_STREAM_DISPLAY_CHARS = 480;
1055
+ var MAX_STREAM_LINES = 8;
1056
+ function ToolStreamBox({
1057
+ name,
1058
+ text,
1059
+ startedAt,
1060
+ termWidth
1061
+ }) {
1062
+ const [tick, setTick] = useState(0);
1063
+ useEffect(() => {
1064
+ const t = setInterval(() => setTick((n) => n + 1), 500);
1065
+ return () => clearInterval(t);
1066
+ }, []);
1067
+ const elapsedMs = Date.now() - startedAt;
1068
+ const lines = text.split("\n");
1069
+ const totalLines = lines.length;
1070
+ const hidden = Math.max(0, totalLines - MAX_STREAM_LINES);
1071
+ const visible = hidden > 0 ? lines.slice(hidden) : lines;
1072
+ const contentWidth = Math.max(20, Math.min(termWidth - 4, 100));
1073
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 0, children: [
1074
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
1075
+ /* @__PURE__ */ jsx(Text, { color: "yellow", children: "\u25C6 " }),
1076
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: name }),
1077
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \u23F1 ${fmtDuration2(elapsedMs)}` }),
1078
+ hidden > 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` (${totalLines} lines, showing last ${MAX_STREAM_LINES})` }) : null
1079
+ ] }),
1080
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [
1081
+ hidden > 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, italic: true, children: ` \u2026 ${hidden} more line${hidden === 1 ? "" : "s"} above` }) : null,
1082
+ visible.map((line, i) => {
1083
+ const key = i;
1084
+ const trimmed = line.length > contentWidth ? `${line.slice(0, contentWidth - 1)}\u2026` : line;
1085
+ return /* @__PURE__ */ jsx(Text, { dimColor: true, children: trimmed || " " }, key);
1086
+ })
1087
+ ] })
1088
+ ] });
1089
+ }
1090
+ function tailForDisplay(text, maxChars) {
1091
+ if (text.length <= maxChars) return text;
1092
+ const cut = text.length - maxChars;
1093
+ const nl = text.indexOf("\n", cut);
1094
+ if (nl !== -1 && nl < cut + 80) {
1095
+ return `\u2026 ${text.slice(nl + 1)}`;
1010
1096
  }
1011
- if (toolName === "fetch" || toolName === "webfetch" || toolName === "web_fetch") {
1012
- if (json && typeof json === "object") {
1013
- const o = json;
1014
- const status = numOf(o["status"]);
1015
- const ct = stringOf(o["content_type"]);
1016
- const url = stringOf(o["url"]);
1017
- const content = stringOf(o["content"]);
1018
- const head = [];
1019
- if (status !== void 0) head.push(`HTTP ${status}`);
1020
- if (ct) head.push(ct.split(";")[0] ?? ct);
1021
- if (content) head.push(fmtBytes2(Buffer.byteLength(content, "utf8")));
1022
- const lines = [];
1023
- if (head.length > 0) lines.push(head.join(" \xB7 "));
1024
- if (url && status !== void 0 && (status < 200 || status >= 400)) {
1025
- lines.push(shortenPath(url, 70));
1026
- }
1027
- if (lines.length > 0) return lines;
1097
+ return `\u2026 ${text.slice(cut)}`;
1098
+ }
1099
+ function DiffBlock({ rows, hidden }) {
1100
+ let gutterWidth = 1;
1101
+ for (const r of rows) {
1102
+ const n = r.kind === "del" ? r.oldLine : r.newLine;
1103
+ if (typeof n === "number") {
1104
+ const w = String(n).length;
1105
+ if (w > gutterWidth) gutterWidth = w;
1028
1106
  }
1029
1107
  }
1030
- if (toolName === "git") {
1031
- if (json && typeof json === "object") {
1032
- const o = json;
1033
- const exit = numOf(o["exitCode"]) ?? numOf(o["exit_code"]);
1034
- const stdout = stringOf(o["stdout"]) ?? "";
1035
- const stderr = stringOf(o["stderr"]) ?? "";
1036
- const head = [];
1037
- if (exit !== void 0) head.push(`exit ${exit}`);
1038
- const stdoutLines = countLines(stdout);
1039
- const stderrLines = countLines(stderr);
1040
- const lparts = [];
1041
- if (stdoutLines > 0) lparts.push(`${stdoutLines} out`);
1042
- if (stderrLines > 0) lparts.push(`${stderrLines} err`);
1043
- if (lparts.length > 0) head.push(lparts.join(" \xB7 "));
1044
- const lines = [];
1045
- if (head.length > 0) lines.push(head.join(" \xB7 "));
1046
- const preview = firstNonEmpty(stdout) ?? firstNonEmpty(stderr);
1047
- if (preview) lines.push(`"${truncMid(preview, 70)}"`);
1048
- if (lines.length > 0) return lines;
1049
- }
1050
- }
1051
- if (toolName === "lint") {
1052
- if (json && typeof json === "object") {
1053
- const o = json;
1054
- const linter = stringOf(o["linter"]);
1055
- const files = numOf(o["files_checked"]);
1056
- const errors = numOf(o["errors"]) ?? 0;
1057
- const warnings = numOf(o["warnings"]) ?? 0;
1058
- const fix = o["fix_applied"] === true;
1059
- const head = [];
1060
- if (linter && linter !== "none") head.push(linter);
1061
- head.push(`${errors} error${errors === 1 ? "" : "s"}`);
1062
- head.push(`${warnings} warning${warnings === 1 ? "" : "s"}`);
1063
- if (files !== void 0) head.push(`${files} file${files === 1 ? "" : "s"}`);
1064
- if (fix) head.push("fixed");
1065
- return [head.join(" \xB7 ")];
1066
- }
1067
- }
1068
- if (toolName === "format") {
1069
- if (json && typeof json === "object") {
1070
- const o = json;
1071
- const fixer = stringOf(o["fixer"]);
1072
- const checked = numOf(o["files_checked"]);
1073
- const changed = numOf(o["files_changed"]);
1074
- const head = [];
1075
- if (fixer && fixer !== "none") head.push(fixer);
1076
- if (changed !== void 0 && checked !== void 0) {
1077
- head.push(`${changed}/${checked} changed`);
1078
- } else if (changed !== void 0) {
1079
- head.push(`${changed} changed`);
1108
+ const blank = " ".repeat(gutterWidth);
1109
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginLeft: 4, marginTop: 0, children: [
1110
+ rows.map((row, i) => {
1111
+ const key = i;
1112
+ if (row.kind === "hunk") {
1113
+ return /* @__PURE__ */ jsx(Text, { color: "cyan", dimColor: true, children: row.text }, key);
1080
1114
  }
1081
- return head.length > 0 ? [head.join(" \xB7 ")] : [];
1082
- }
1083
- }
1084
- if (toolName === "typecheck") {
1085
- if (json && typeof json === "object") {
1086
- const o = json;
1087
- const exit = numOf(o["exit_code"]) ?? numOf(o["exitCode"]);
1088
- const errors = numOf(o["errors"]);
1089
- const head = [];
1090
- if (errors !== void 0) head.push(`${errors} error${errors === 1 ? "" : "s"}`);
1091
- if (exit !== void 0) head.push(`exit ${exit}`);
1092
- const stdout = stringOf(o["output"]) ?? stringOf(o["stdout"]) ?? "";
1093
- const lines = [];
1094
- if (head.length > 0) lines.push(head.join(" \xB7 "));
1095
- const preview = firstNonEmpty(stdout);
1096
- if (preview && (!errors || errors > 0)) lines.push(`"${truncMid(preview, 70)}"`);
1097
- if (lines.length > 0) return lines;
1098
- }
1099
- }
1100
- if (toolName === "test") {
1101
- if (json && typeof json === "object") {
1102
- const o = json;
1103
- const runner = stringOf(o["runner"]);
1104
- const total = numOf(o["tests_run"]) ?? 0;
1105
- const passed = numOf(o["passed"]) ?? 0;
1106
- const failed = numOf(o["failed"]) ?? 0;
1107
- const duration = numOf(o["duration_ms"]);
1108
- const head = [];
1109
- if (runner && runner !== "none") head.push(runner);
1110
- head.push(`${passed}/${total} passed`);
1111
- if (failed > 0) head.push(`${failed} failed`);
1112
- if (duration !== void 0) head.push(fmtDuration2(duration));
1113
- return [head.join(" \xB7 ")];
1114
- }
1115
- }
1116
- if (toolName === "audit") {
1117
- if (json && typeof json === "object") {
1118
- const o = json;
1119
- const total = numOf(o["total"]) ?? 0;
1120
- const summary = stringOf(o["summary"]);
1121
- if (total === 0) return ["no vulnerabilities"];
1122
- const head = `${total} vulnerabilit${total === 1 ? "y" : "ies"}`;
1123
- return summary && summary.toLowerCase() !== head.toLowerCase() ? [head, truncMid(summary, OUT_BUDGET)] : [head];
1124
- }
1125
- }
1126
- if (toolName === "outdated") {
1127
- if (json && typeof json === "object") {
1128
- const o = json;
1129
- const total = numOf(o["total"]) ?? 0;
1130
- const pkgs = Array.isArray(o["packages"]) ? o["packages"] : void 0;
1131
- if (total === 0) return ["all up to date"];
1132
- const lines = [`${total} outdated`];
1133
- if (pkgs && pkgs.length > 0) {
1134
- const first = pkgs[0];
1135
- if (first && typeof first === "object") {
1136
- const p = first;
1137
- const name = stringOf(p["name"]) ?? stringOf(p["package"]);
1138
- const cur = stringOf(p["current"]);
1139
- const wanted = stringOf(p["wanted"]) ?? stringOf(p["latest"]);
1140
- if (name && cur && wanted) lines.push(`${name}: ${cur} \u2192 ${wanted}`);
1141
- else if (name) lines.push(name);
1142
- }
1115
+ if (row.kind === "meta") {
1116
+ return /* @__PURE__ */ jsx(Text, { dimColor: true, children: `${blank} ${row.text}` }, key);
1143
1117
  }
1144
- return lines;
1118
+ const lnNumber = row.kind === "del" ? row.oldLine : row.newLine;
1119
+ const lnText = typeof lnNumber === "number" ? String(lnNumber).padStart(gutterWidth, " ") : blank;
1120
+ if (row.kind === "ctx") {
1121
+ return /* @__PURE__ */ jsx(Text, { dimColor: true, children: `${lnText} ${row.text}` }, key);
1122
+ }
1123
+ const bg = row.kind === "add" ? "greenBright" : "redBright";
1124
+ return /* @__PURE__ */ jsxs(Text, { children: [
1125
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: `${lnText} ` }),
1126
+ /* @__PURE__ */ jsx(Text, { backgroundColor: bg, color: "black", children: row.text })
1127
+ ] }, key);
1128
+ }),
1129
+ hidden > 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, italic: true, children: `${blank} \u2026 ${hidden} more line${hidden === 1 ? "" : "s"}` }) : null
1130
+ ] });
1131
+ }
1132
+ function Entry({
1133
+ entry,
1134
+ termWidth
1135
+ }) {
1136
+ switch (entry.kind) {
1137
+ case "user":
1138
+ return /* @__PURE__ */ jsxs(Text, { children: [
1139
+ /* @__PURE__ */ jsx(Text, { color: entry.queued ? "yellow" : "cyan", children: entry.queued ? "\u231B" : "\u203A" }),
1140
+ " ",
1141
+ /* @__PURE__ */ jsx(Text, { dimColor: entry.queued ?? false, children: entry.text }),
1142
+ entry.queued ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: " (queued)" }) : null
1143
+ ] });
1144
+ case "assistant":
1145
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginY: 1, children: [
1146
+ /* @__PURE__ */ jsx(Box, { flexDirection: "row", children: /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "ASSISTANT: " }) }),
1147
+ /* @__PURE__ */ jsx(Text, { color: "white", children: renderMarkdownTables(entry.text, termWidth) })
1148
+ ] });
1149
+ case "tool": {
1150
+ const argSummary = formatToolArgs(entry.name, entry.input);
1151
+ const outLines = formatToolOutput(
1152
+ entry.name,
1153
+ entry.output,
1154
+ entry.ok,
1155
+ entry.outputBytes,
1156
+ entry.outputLines
1157
+ );
1158
+ const diff = entry.ok ? extractDiffPreview(entry.name, entry.output) : void 0;
1159
+ const sizeChip = (() => {
1160
+ if (!entry.ok) return "";
1161
+ const parts = [];
1162
+ if (entry.outputLines !== void 0 && entry.outputLines > 0) {
1163
+ parts.push(`${entry.outputLines} L`);
1164
+ }
1165
+ if (entry.outputBytes && entry.outputBytes > 0) {
1166
+ parts.push(fmtBytes2(entry.outputBytes));
1167
+ }
1168
+ if (entry.outputTokens && entry.outputTokens > 0) {
1169
+ parts.push(`\u2248${fmtTok2(entry.outputTokens)} tok`);
1170
+ }
1171
+ return parts.join(" \xB7 ");
1172
+ })();
1173
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
1174
+ /* @__PURE__ */ jsxs(Text, { children: [
1175
+ /* @__PURE__ */ jsx(Text, { color: entry.ok ? "green" : "red", children: entry.ok ? "\u25CF" : "\u2717" }),
1176
+ " ",
1177
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: entry.name }),
1178
+ argSummary ? /* @__PURE__ */ jsxs(Fragment, { children: [
1179
+ /* @__PURE__ */ jsx(Text, { children: " " }),
1180
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: argSummary })
1181
+ ] }) : null,
1182
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \xB7 ${fmtDuration2(entry.durationMs)}` }),
1183
+ sizeChip ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \xB7 ${sizeChip}` }) : null
1184
+ ] }),
1185
+ outLines.map((line, i) => (
1186
+ // biome-ignore lint/suspicious/noArrayIndexKey: tool output lines are static, index is stable
1187
+ /* @__PURE__ */ jsxs(Text, { children: [
1188
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: i === outLines.length - 1 && !diff ? " \u2514\u2500 " : " \u251C\u2500 " }),
1189
+ /* @__PURE__ */ jsx(
1190
+ Text,
1191
+ {
1192
+ color: !entry.ok || line.startsWith("!") ? "red" : void 0,
1193
+ dimColor: entry.ok && !line.startsWith("!"),
1194
+ children: line
1195
+ }
1196
+ )
1197
+ ] }, i)
1198
+ )),
1199
+ diff ? /* @__PURE__ */ jsx(DiffBlock, { rows: diff.rows, hidden: diff.hidden }) : null
1200
+ ] });
1145
1201
  }
1146
- }
1147
- if (toolName === "tree") {
1148
- if (json && typeof json === "object") {
1149
- const o = json;
1150
- const files = numOf(o["total_files"]);
1151
- const dirs = numOf(o["total_dirs"]);
1152
- const truncated = o["truncated"] === true;
1153
- const parts = [];
1154
- if (files !== void 0) parts.push(`${files} file${files === 1 ? "" : "s"}`);
1155
- if (dirs !== void 0) parts.push(`${dirs} dir${dirs === 1 ? "" : "s"}`);
1156
- if (truncated) parts.push("truncated");
1157
- return parts.length > 0 ? [parts.join(" \xB7 ")] : [];
1202
+ case "info":
1203
+ return /* @__PURE__ */ jsx(Text, { dimColor: true, children: entry.text });
1204
+ case "warn":
1205
+ return /* @__PURE__ */ jsx(Text, { color: "yellow", children: entry.text });
1206
+ case "error":
1207
+ return /* @__PURE__ */ jsx(Text, { color: "red", children: entry.text });
1208
+ case "turn-summary":
1209
+ return /* @__PURE__ */ jsx(Text, { dimColor: true, children: entry.text });
1210
+ case "confirm":
1211
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, marginY: 1, children: [
1212
+ /* @__PURE__ */ jsxs(Text, { bold: true, color: "yellow", children: [
1213
+ "\u26A0 Confirm: ",
1214
+ entry.toolName
1215
+ ] }),
1216
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Waiting for y / n / a / d..." })
1217
+ ] });
1218
+ case "banner":
1219
+ return /* @__PURE__ */ jsx(Banner, { entry });
1220
+ case "subagent": {
1221
+ const lines = entry.text.split("\n");
1222
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
1223
+ /* @__PURE__ */ jsxs(Text, { children: [
1224
+ /* @__PURE__ */ jsx(Text, { color: entry.agentColor, bold: true, children: `[${entry.agentLabel}]` }),
1225
+ /* @__PURE__ */ jsx(Text, { children: " " }),
1226
+ /* @__PURE__ */ jsx(Text, { color: entry.agentColor, children: entry.icon }),
1227
+ /* @__PURE__ */ jsx(Text, { children: " " }),
1228
+ /* @__PURE__ */ jsx(Text, { children: lines[0] ?? "" }),
1229
+ entry.detail ? /* @__PURE__ */ jsxs(Fragment, { children: [
1230
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \xB7 " }),
1231
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: entry.detail })
1232
+ ] }) : null
1233
+ ] }),
1234
+ lines.slice(1).map((line, i) => (
1235
+ // biome-ignore lint/suspicious/noArrayIndexKey: stable line index
1236
+ /* @__PURE__ */ jsxs(Text, { children: [
1237
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " " }),
1238
+ /* @__PURE__ */ jsx(Text, { children: line })
1239
+ ] }, i)
1240
+ ))
1241
+ ] });
1158
1242
  }
1159
1243
  }
1160
- if (toolName === "json") {
1161
- if (json && typeof json === "object") {
1162
- const o = json;
1163
- const err = stringOf(o["error"]);
1164
- if (err) return [truncMid(err, OUT_BUDGET)];
1165
- const type = stringOf(o["type"]);
1166
- const keys = Array.isArray(o["keys"]) ? o["keys"] : void 0;
1167
- const parts = [];
1168
- if (type) parts.push(type);
1169
- if (keys) parts.push(`${keys.length} key${keys.length === 1 ? "" : "s"}`);
1170
- return parts.length > 0 ? [parts.join(" \xB7 ")] : [];
1244
+ }
1245
+ function Banner({
1246
+ entry
1247
+ }) {
1248
+ const cwdShort = shortenPath(entry.cwd, 48);
1249
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 2, paddingY: 0, children: [
1250
+ /* @__PURE__ */ jsxs(Text, { children: [
1251
+ /* @__PURE__ */ jsx(Text, { color: "magenta", bold: true, children: " \u259F\u259B " }),
1252
+ /* @__PURE__ */ jsx(Text, { color: "magenta", bold: true, children: "WrongStack" }),
1253
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " v" }),
1254
+ /* @__PURE__ */ jsx(Text, { children: entry.version })
1255
+ ] }),
1256
+ /* @__PURE__ */ jsx(Text, { dimColor: true, italic: true, children: " Built on the wrong stack. Shipped anyway." }),
1257
+ /* @__PURE__ */ jsxs(Text, { children: [
1258
+ /* @__PURE__ */ jsx(Text, { color: "cyan", children: " provider " }),
1259
+ /* @__PURE__ */ jsxs(Text, { children: [
1260
+ entry.provider,
1261
+ "/",
1262
+ entry.model
1263
+ ] })
1264
+ ] }),
1265
+ entry.family ? /* @__PURE__ */ jsxs(Text, { children: [
1266
+ /* @__PURE__ */ jsx(Text, { color: "cyan", children: " family " }),
1267
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: entry.family })
1268
+ ] }) : null,
1269
+ entry.keyTail ? /* @__PURE__ */ jsxs(Text, { children: [
1270
+ /* @__PURE__ */ jsx(Text, { color: "cyan", children: " key " }),
1271
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u25CF\u25CF\u25CF\u2026" }),
1272
+ /* @__PURE__ */ jsx(Text, { children: entry.keyTail })
1273
+ ] }) : null,
1274
+ /* @__PURE__ */ jsxs(Text, { children: [
1275
+ /* @__PURE__ */ jsx(Text, { color: "cyan", children: " cwd " }),
1276
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: cwdShort })
1277
+ ] }),
1278
+ /* @__PURE__ */ jsxs(Text, { children: [
1279
+ /* @__PURE__ */ jsx(Text, { color: "cyan", children: " hints " }),
1280
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "/help \xB7 /init \xB7 /memory \xB7 /queue \xB7 /exit" })
1281
+ ] })
1282
+ ] });
1283
+ }
1284
+ function shortenPath(p, max) {
1285
+ if (p.length <= max) return p;
1286
+ return `\u2026${p.slice(p.length - (max - 1))}`;
1287
+ }
1288
+ function fmtTok2(n) {
1289
+ if (!Number.isFinite(n) || n <= 0) return "0";
1290
+ if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
1291
+ if (n >= 1e3) return `${(n / 1e3).toFixed(n >= 1e4 ? 0 : 1)}k`;
1292
+ return String(n);
1293
+ }
1294
+ function fmtDuration2(ms) {
1295
+ if (ms < 1e3) return `${ms}ms`;
1296
+ if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
1297
+ const totalSec = Math.floor(ms / 1e3);
1298
+ return `${Math.floor(totalSec / 60)}m${totalSec % 60}s`;
1299
+ }
1300
+ var ARG_BUDGET = 60;
1301
+ var OUT_BUDGET = 80;
1302
+ function formatToolArgs(toolName, input) {
1303
+ if (!input || typeof input !== "object") return "";
1304
+ const obj = input;
1305
+ switch (toolName) {
1306
+ case "read":
1307
+ case "write":
1308
+ case "edit":
1309
+ case "patch":
1310
+ case "document":
1311
+ case "list_dir":
1312
+ case "ls":
1313
+ case "tree": {
1314
+ const p = stringOf(obj["path"]) ?? stringOf(obj["file"]);
1315
+ return p ? shortenPath(p, ARG_BUDGET) : "";
1316
+ }
1317
+ case "grep":
1318
+ case "search":
1319
+ case "replace": {
1320
+ const pat = stringOf(obj["pattern"]) ?? stringOf(obj["query"]);
1321
+ const scope = stringOf(obj["path"]) ?? stringOf(obj["glob"]);
1322
+ const head = pat ? `"${truncMid(pat, 36)}"` : "";
1323
+ const tail = scope ? ` in ${shortenPath(scope, 28)}` : "";
1324
+ return `${head}${tail}` || (stringOf(obj["command"]) ?? "");
1325
+ }
1326
+ case "glob": {
1327
+ const pat = stringOf(obj["pattern"]) ?? stringOf(obj["glob"]);
1328
+ return pat ? `"${truncMid(pat, ARG_BUDGET - 2)}"` : "";
1329
+ }
1330
+ case "bash":
1331
+ case "shell":
1332
+ case "exec":
1333
+ case "install":
1334
+ case "git": {
1335
+ const cmd = stringOf(obj["command"]) ?? stringOf(obj["args"]);
1336
+ return cmd ? truncMid(cmd, ARG_BUDGET) : "";
1337
+ }
1338
+ case "diff": {
1339
+ const files = Array.isArray(obj["files"]) ? obj["files"] : void 0;
1340
+ if (files && files.length > 0) {
1341
+ const head = stringOf(files[0]) ?? "";
1342
+ const rest = files.length > 1 ? ` (+${files.length - 1})` : "";
1343
+ return head ? `${shortenPath(head, 50)}${rest}` : "";
1344
+ }
1345
+ const mode = stringOf(obj["mode"]);
1346
+ return mode ? `mode: ${mode}` : "";
1347
+ }
1348
+ case "fetch":
1349
+ case "webfetch":
1350
+ case "web_fetch": {
1351
+ const u = stringOf(obj["url"]);
1352
+ return u ? truncMid(u, ARG_BUDGET) : "";
1353
+ }
1354
+ case "todo": {
1355
+ const list = obj["todos"];
1356
+ if (Array.isArray(list)) return `${list.length} item${list.length === 1 ? "" : "s"}`;
1357
+ return "";
1358
+ }
1359
+ case "lint":
1360
+ case "format":
1361
+ case "typecheck":
1362
+ case "test":
1363
+ case "audit":
1364
+ case "outdated": {
1365
+ const files = obj["files"];
1366
+ if (Array.isArray(files) && files.length > 0) {
1367
+ const first = stringOf(files[0]);
1368
+ const more = files.length > 1 ? ` (+${files.length - 1})` : "";
1369
+ return first ? `${shortenPath(first, 50)}${more}` : `${files.length} files`;
1370
+ }
1371
+ const filter = stringOf(obj["filter"]) ?? stringOf(obj["pattern"]);
1372
+ return filter ? `"${truncMid(filter, ARG_BUDGET - 2)}"` : "";
1373
+ }
1374
+ case "json": {
1375
+ const file = stringOf(obj["file"]);
1376
+ const q = stringOf(obj["query"]);
1377
+ if (file) return q ? `${shortenPath(file, 40)} ${q}` : shortenPath(file, ARG_BUDGET);
1378
+ return q ? truncMid(q, ARG_BUDGET) : "";
1379
+ }
1380
+ case "scaffold": {
1381
+ const tmpl = stringOf(obj["template"]) ?? stringOf(obj["type"]);
1382
+ const name = stringOf(obj["name"]);
1383
+ if (tmpl && name) return `${tmpl} \u2192 ${truncMid(name, ARG_BUDGET - tmpl.length - 4)}`;
1384
+ return name ?? tmpl ?? "";
1385
+ }
1386
+ case "remember":
1387
+ case "forget":
1388
+ case "memory": {
1389
+ const key = stringOf(obj["key"]) ?? stringOf(obj["name"]);
1390
+ return key ? truncMid(key, ARG_BUDGET) : "";
1391
+ }
1392
+ case "mode": {
1393
+ const m = stringOf(obj["mode"]) ?? stringOf(obj["name"]);
1394
+ return m ? truncMid(m, ARG_BUDGET) : "";
1395
+ }
1396
+ case "logs": {
1397
+ const target = stringOf(obj["target"]) ?? stringOf(obj["service"]) ?? stringOf(obj["path"]);
1398
+ return target ? truncMid(target, ARG_BUDGET) : "";
1171
1399
  }
1172
1400
  }
1173
- if (toolName === "install") {
1401
+ for (const key of ["path", "file", "url", "name", "query", "pattern", "command"]) {
1402
+ const v = stringOf(obj[key]);
1403
+ if (v) return truncMid(v, ARG_BUDGET);
1404
+ }
1405
+ try {
1406
+ return truncMid(JSON.stringify(obj), ARG_BUDGET);
1407
+ } catch {
1408
+ return "";
1409
+ }
1410
+ }
1411
+ function formatToolOutput(toolName, output, ok, _outputBytes, outputLines) {
1412
+ if (!output) return ok ? [] : ["failed"];
1413
+ const text = output.trim();
1414
+ if (!text) return ok ? [] : ["failed"];
1415
+ const json = tryParseJson(text);
1416
+ if (toolName === "write") {
1174
1417
  if (json && typeof json === "object") {
1175
1418
  const o = json;
1176
- const exit = numOf(o["exit_code"]) ?? numOf(o["exitCode"]);
1177
- const added = numOf(o["added"]);
1178
- const removed = numOf(o["removed"]);
1179
- const head = [];
1180
- if (exit !== void 0) head.push(`exit ${exit}`);
1181
- if (added !== void 0) head.push(`+${added}`);
1182
- if (removed !== void 0) head.push(`-${removed}`);
1183
- const stdout = stringOf(o["stdout"]) ?? stringOf(o["output"]) ?? "";
1184
- const lines = [];
1185
- if (head.length > 0) lines.push(head.join(" \xB7 "));
1186
- const preview = firstNonEmpty(stdout);
1187
- if (preview) lines.push(`"${truncMid(preview, 70)}"`);
1188
- if (lines.length > 0) return lines;
1419
+ const bytes = numOf(o["bytes_written"]) ?? numOf(o["bytes"]);
1420
+ const created = o["created"] === true;
1421
+ const tag = created ? "created" : "updated";
1422
+ if (bytes !== void 0) return [`${tag} \xB7 ${fmtBytes2(bytes)}`];
1423
+ return [tag];
1189
1424
  }
1190
1425
  }
1191
- if (toolName === "scaffold") {
1426
+ if (toolName === "edit") {
1192
1427
  if (json && typeof json === "object") {
1193
1428
  const o = json;
1194
- const created = Array.isArray(o["created"]) ? o["created"] : void 0;
1195
- const skipped = Array.isArray(o["skipped"]) ? o["skipped"] : void 0;
1196
- const parts = [];
1197
- if (created !== void 0) parts.push(`${created.length} created`);
1198
- if (skipped !== void 0 && skipped.length > 0) parts.push(`${skipped.length} skipped`);
1199
- if (parts.length > 0) return [parts.join(" \xB7 ")];
1429
+ const reps = numOf(o["replacements"]);
1430
+ if (reps !== void 0) return [`${reps} replacement${reps === 1 ? "" : "s"}`];
1200
1431
  }
1201
1432
  }
1202
- if (toolName === "remember" || toolName === "forget" || toolName === "memory") {
1203
- return ok ? [toolName === "forget" ? "removed" : "saved"] : [text.split("\n")[0] ?? ""];
1204
- }
1205
- if (toolName === "mode") {
1433
+ if (toolName === "patch") {
1206
1434
  if (json && typeof json === "object") {
1207
1435
  const o = json;
1208
- const mode = stringOf(o["mode"]) ?? stringOf(o["active"]) ?? stringOf(o["name"]);
1209
- if (mode) return [`mode: ${mode}`];
1436
+ const applied = numOf(o["applied"]);
1437
+ const rejected = numOf(o["rejected"]);
1438
+ const files = Array.isArray(o["files"]) ? o["files"] : void 0;
1439
+ const lines = [];
1440
+ if (applied !== void 0 || rejected !== void 0) {
1441
+ const parts = [];
1442
+ if (applied !== void 0) parts.push(`${applied} applied`);
1443
+ if (rejected !== void 0 && rejected > 0) parts.push(`${rejected} rejected`);
1444
+ lines.push(parts.join(" \xB7 "));
1445
+ }
1446
+ if (files && files.length > 0) {
1447
+ const first = stringOf(files[0]) ?? "";
1448
+ const more = files.length > 1 ? ` (+${files.length - 1})` : "";
1449
+ lines.push(`${shortenPath(first, 60)}${more}`);
1450
+ }
1451
+ if (lines.length > 0) return lines;
1210
1452
  }
1211
1453
  }
1212
- if (toolName === "search") {
1454
+ if (toolName === "replace") {
1213
1455
  if (json && typeof json === "object") {
1214
1456
  const o = json;
1215
- const matches = Array.isArray(o["matches"]) ? o["matches"] : Array.isArray(o["results"]) ? o["results"] : void 0;
1216
- const count = numOf(o["count"]) ?? matches?.length;
1217
- if (count !== void 0) {
1218
- if (count === 0) return ["no results"];
1219
- const lines = [`${count} result${count === 1 ? "" : "s"}`];
1220
- const firstHit = matches && matches.length > 0 ? formatMatchHit(matches[0]) : void 0;
1221
- if (firstHit) lines.push(firstHit);
1222
- return lines;
1457
+ const files = numOf(o["files_modified"]);
1458
+ const reps = numOf(o["total_replacements"]);
1459
+ if (files !== void 0 && reps !== void 0) {
1460
+ return [
1461
+ `${reps} replacement${reps === 1 ? "" : "s"} in ${files} file${files === 1 ? "" : "s"}`
1462
+ ];
1223
1463
  }
1224
1464
  }
1225
1465
  }
1226
- if (toolName === "logs") {
1227
- const lines = text.split("\n").filter((l) => l.trim());
1228
- if (lines.length === 0) return [];
1229
- const head = `${lines.length} line${lines.length === 1 ? "" : "s"}`;
1230
- const lastLine = lines[lines.length - 1];
1231
- return lastLine ? [head, `"${truncMid(lastLine.trim(), 70)}"`] : [head];
1232
- }
1233
- if (json && typeof json === "object" && !Array.isArray(json)) {
1234
- const summary = summarizeJsonObject(json);
1235
- if (summary) return [summary];
1236
- }
1237
- const collapsed = text.replace(/\s+/g, " ").trim();
1238
- return [truncMid(collapsed, GENERIC_BUDGET)];
1239
- }
1240
- var GENERIC_BUDGET = 240;
1241
- function summarizeJsonObject(obj) {
1242
- const keys = Object.keys(obj);
1243
- if (keys.length === 0) return null;
1244
- const priority = [
1245
- "ok",
1246
- "status",
1247
- "timedOut",
1248
- "stopReason",
1249
- "reason",
1250
- "error",
1251
- "message",
1252
- "result",
1253
- "summary",
1254
- "iterations",
1255
- "toolCalls",
1256
- "durationMs",
1257
- "subagentId",
1258
- "taskId"
1259
- ];
1260
- const ordered = [
1261
- ...priority.filter((k) => keys.includes(k)),
1262
- ...keys.filter((k) => !priority.includes(k))
1263
- ];
1264
- const parts = [];
1265
- let used = 0;
1266
- for (const key of ordered) {
1267
- const v = obj[key];
1268
- if (v === void 0 || v === null) continue;
1269
- const rendered = typeof v === "string" ? `${key}="${truncMid(v.replace(/\s+/g, " "), 80)}"` : typeof v === "number" || typeof v === "boolean" ? `${key}=${v}` : Array.isArray(v) ? `${key}=[${v.length}]` : `${key}={\u2026}`;
1270
- if (used + rendered.length > GENERIC_BUDGET) {
1271
- parts.push("\u2026");
1272
- break;
1273
- }
1274
- parts.push(rendered);
1275
- used += rendered.length + 3;
1276
- }
1277
- return parts.length > 0 ? parts.join(" \xB7 ") : null;
1278
- }
1279
- function firstNonEmpty(text) {
1280
- if (!text) return void 0;
1281
- const line = text.split("\n").find((l) => l.trim());
1282
- return line ? line.replace(/\s+/g, " ").trim() : void 0;
1283
- }
1284
- function formatMatchHit(hit) {
1285
- if (typeof hit === "string") return truncMid(hit, 70);
1286
- if (hit && typeof hit === "object") {
1287
- const o = hit;
1288
- const file = stringOf(o["file"]) ?? stringOf(o["path"]);
1289
- const line = numOf(o["line"]) ?? numOf(o["lineNumber"]);
1290
- const snippet = stringOf(o["text"]) ?? stringOf(o["match"]) ?? stringOf(o["preview"]);
1291
- if (file) {
1292
- const head = line !== void 0 ? `${shortenPath(file, 40)}:${line}` : shortenPath(file, 50);
1293
- return snippet ? `${head} ${truncMid(snippet.replace(/\s+/g, " "), 40)}` : head;
1466
+ if (toolName === "diff") {
1467
+ if (json && typeof json === "object") {
1468
+ const o = json;
1469
+ const files = Array.isArray(o["files"]) ? o["files"] : void 0;
1470
+ const truncated = o["truncated"] === true;
1471
+ const mode = stringOf(o["mode"]);
1472
+ const diff = stringOf(o["diff"]);
1473
+ if (!diff) return [files && files.length === 0 ? "no changes" : "empty diff"];
1474
+ const head = [];
1475
+ if (mode) head.push(mode);
1476
+ if (files && files.length > 0)
1477
+ head.push(`${files.length} file${files.length === 1 ? "" : "s"}`);
1478
+ if (truncated) head.push("truncated");
1479
+ return head.length > 0 ? [head.join(" \xB7 ")] : [];
1294
1480
  }
1295
- if (snippet) return truncMid(snippet, 70);
1296
1481
  }
1297
- return void 0;
1298
- }
1299
- var DIFF_MAX_LINES = 8;
1300
- function extractDiffPreview(toolName, output) {
1301
- if (!output) return void 0;
1302
- const text = output.trim();
1303
- if (!text) return void 0;
1304
- let diff;
1305
- if (toolName === "edit" || toolName === "diff") {
1306
- const parsed = tryParseJson(text);
1307
- if (parsed && typeof parsed === "object") {
1308
- diff = stringOf(parsed["diff"]);
1482
+ if (toolName === "read") {
1483
+ if (outputLines !== void 0) return [];
1484
+ if (json && typeof json === "object") {
1485
+ const o = json;
1486
+ const bytes = numOf(o["bytes"]);
1487
+ if (bytes !== void 0) return [`${fmtBytes2(bytes)} read`];
1309
1488
  }
1310
- } else if (toolName === "patch") {
1311
- const parsed = tryParseJson(text);
1312
- if (parsed && typeof parsed === "object") {
1313
- diff = stringOf(parsed["diff"]) ?? stringOf(parsed["stdout"]);
1314
- } else if (text.includes("@@") || text.startsWith("---")) {
1315
- diff = text;
1489
+ const range = scanNumberedRange(text);
1490
+ if (range.count > 0 && range.first !== void 0 && range.last !== void 0) {
1491
+ if (range.first === range.last) {
1492
+ return [`L${range.first} \xB7 ${fmtBytes2(text.length)}`];
1493
+ }
1494
+ const contiguous = range.count === range.last - range.first + 1;
1495
+ const head = `L${range.first}\u2013${range.last}`;
1496
+ const tail = contiguous ? `${range.count} line${range.count === 1 ? "" : "s"}` : `${range.count} lines (gaps)`;
1497
+ return [`${head} \xB7 ${tail} \xB7 ${fmtBytes2(text.length)}`];
1316
1498
  }
1317
1499
  }
1318
- if (!diff || !diff.trim() || diff.startsWith("(no-op")) return void 0;
1319
- return parseUnifiedDiff(diff, DIFF_MAX_LINES);
1320
- }
1321
- function parseUnifiedDiff(diff, maxLines) {
1322
- const all = [];
1323
- let oldLn = 0;
1324
- let newLn = 0;
1325
- for (const raw of diff.split("\n")) {
1326
- const line = raw.replace(/\r$/, "");
1327
- if (line.startsWith("+++") || line.startsWith("---")) continue;
1328
- if (line.startsWith("diff --git") || line.startsWith("index ")) continue;
1329
- if (line.startsWith("@@")) {
1330
- const m = line.match(/^@@\s+-(\d+)(?:,\d+)?\s+\+(\d+)(?:,\d+)?\s+@@/);
1331
- if (m) {
1332
- oldLn = Number.parseInt(m[1] ?? "0", 10) || 0;
1333
- newLn = Number.parseInt(m[2] ?? "0", 10) || 0;
1500
+ if (toolName === "grep" || toolName === "glob") {
1501
+ if (json && typeof json === "object") {
1502
+ const o = json;
1503
+ const matches = Array.isArray(o["matches"]) ? o["matches"] : void 0;
1504
+ const count = numOf(o["count"]) ?? matches?.length;
1505
+ const truncated = o["truncated"] === true;
1506
+ if (count !== void 0) {
1507
+ if (count === 0) return ["no matches"];
1508
+ const lines = [
1509
+ `${count} match${count === 1 ? "" : "es"}${truncated ? " (truncated)" : ""}`
1510
+ ];
1511
+ const firstHit = matches && matches.length > 0 ? formatMatchHit(matches[0]) : void 0;
1512
+ if (firstHit) lines.push(firstHit);
1513
+ return lines;
1334
1514
  }
1335
- all.push({ kind: "hunk", text: truncMid(line, 60) });
1336
- continue;
1337
- }
1338
- if (line.startsWith("+")) {
1339
- all.push({ kind: "add", text: truncMid(line, 100), newLine: newLn });
1340
- newLn++;
1341
- continue;
1342
- }
1343
- if (line.startsWith("-")) {
1344
- all.push({ kind: "del", text: truncMid(line, 100), oldLine: oldLn });
1345
- oldLn++;
1346
- continue;
1347
1515
  }
1348
- if (line.startsWith("\\ No newline")) {
1349
- all.push({ kind: "meta", text: line });
1350
- continue;
1516
+ }
1517
+ if (toolName === "bash" || toolName === "shell") {
1518
+ if (json && typeof json === "object") {
1519
+ const o = json;
1520
+ const exit = numOf(o["exit_code"]) ?? numOf(o["exitCode"]);
1521
+ const stdout = stringOf(o["stdout"]) ?? "";
1522
+ const stderr = stringOf(o["stderr"]) ?? "";
1523
+ const stdoutLines = countLines(stdout);
1524
+ const stderrLines = countLines(stderr);
1525
+ const head = [];
1526
+ if (exit !== void 0) head.push(`exit ${exit}`);
1527
+ const lineParts = [];
1528
+ if (stdoutLines > 0) lineParts.push(`${stdoutLines} out`);
1529
+ if (stderrLines > 0) lineParts.push(`${stderrLines} err`);
1530
+ if (lineParts.length > 0) head.push(lineParts.join(" \xB7 "));
1531
+ const lines = [];
1532
+ if (head.length > 0) lines.push(head.join(" \xB7 "));
1533
+ const stdoutPreview = firstNonEmpty(stdout);
1534
+ const stderrPreview = firstNonEmpty(stderr);
1535
+ if (stdoutPreview) lines.push(`"${truncMid(stdoutPreview, 70)}"`);
1536
+ if (stderrPreview && stderrPreview !== stdoutPreview) {
1537
+ lines.push(`! "${truncMid(stderrPreview, 70)}"`);
1538
+ }
1539
+ if (lines.length > 0) return lines;
1351
1540
  }
1352
- if (line.length === 0) continue;
1353
- all.push({ kind: "ctx", text: truncMid(line, 100), oldLine: oldLn, newLine: newLn });
1354
- oldLn++;
1355
- newLn++;
1356
1541
  }
1357
- if (all.length === 0) return { rows: [], hidden: 0 };
1358
- if (all.length <= maxLines) return { rows: all, hidden: 0 };
1359
- return { rows: all.slice(0, maxLines), hidden: all.length - maxLines };
1360
- }
1361
- function stringOf(v) {
1362
- return typeof v === "string" && v.length > 0 ? v : void 0;
1363
- }
1364
- function numOf(v) {
1365
- return typeof v === "number" && Number.isFinite(v) ? v : void 0;
1366
- }
1367
- function tryParseJson(s) {
1368
- const t = s.trimStart();
1369
- if (!t.startsWith("{") && !t.startsWith("[")) return void 0;
1370
- try {
1371
- return JSON.parse(s);
1372
- } catch {
1373
- return void 0;
1542
+ if (toolName === "todo") {
1543
+ return ok ? [] : [text.split("\n")[0] ?? ""];
1374
1544
  }
1375
- }
1376
- function scanNumberedRange(text) {
1377
- let first;
1378
- let last;
1379
- let count = 0;
1380
- for (const line of text.split("\n")) {
1381
- const m = line.match(/^\s*(\d+)→/);
1382
- if (m?.[1]) {
1383
- const n = Number.parseInt(m[1], 10);
1384
- if (Number.isFinite(n)) {
1385
- if (first === void 0) first = n;
1386
- last = n;
1387
- count++;
1545
+ if (toolName === "fetch" || toolName === "webfetch" || toolName === "web_fetch") {
1546
+ if (json && typeof json === "object") {
1547
+ const o = json;
1548
+ const status = numOf(o["status"]);
1549
+ const ct = stringOf(o["content_type"]);
1550
+ const url = stringOf(o["url"]);
1551
+ const content = stringOf(o["content"]);
1552
+ const head = [];
1553
+ if (status !== void 0) head.push(`HTTP ${status}`);
1554
+ if (ct) head.push(ct.split(";")[0] ?? ct);
1555
+ if (content) head.push(fmtBytes2(Buffer.byteLength(content, "utf8")));
1556
+ const lines = [];
1557
+ if (head.length > 0) lines.push(head.join(" \xB7 "));
1558
+ if (url && status !== void 0 && (status < 200 || status >= 400)) {
1559
+ lines.push(shortenPath(url, 70));
1388
1560
  }
1561
+ if (lines.length > 0) return lines;
1389
1562
  }
1390
1563
  }
1391
- return { first, last, count };
1392
- }
1393
- function countLines(text) {
1394
- if (!text) return 0;
1395
- return text.replace(/\n$/, "").split("\n").length;
1396
- }
1397
- function fmtBytes2(n) {
1398
- if (n < 1024) return `${n}B`;
1399
- if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)}KB`;
1400
- return `${(n / (1024 * 1024)).toFixed(1)}MB`;
1401
- }
1402
- function truncMid(s, max) {
1403
- if (s.length <= max) return s;
1404
- return `${s.slice(0, max - 1)}\u2026`;
1405
- }
1406
- function isHomeEnd(data) {
1407
- if (data === "\x1B[H" || data === "\x1B[1~" || data === "\x1BOH" || data === "\x1B[7~")
1408
- return "home";
1409
- if (data === "\x1B[F" || data === "\x1B[4~" || data === "\x1BOF" || data === "\x1B[8~")
1410
- return "end";
1411
- return null;
1412
- }
1413
- var EMPTY_KEY = {
1414
- upArrow: false,
1415
- downArrow: false,
1416
- leftArrow: false,
1417
- rightArrow: false,
1418
- return: false,
1419
- escape: false,
1420
- ctrl: false,
1421
- meta: false,
1422
- shift: false,
1423
- tab: false,
1424
- backspace: false,
1425
- delete: false,
1426
- pageUp: false,
1427
- pageDown: false,
1428
- home: false,
1429
- end: false
1430
- };
1431
- function Input({
1432
- prompt = "\u203A ",
1433
- value,
1434
- cursor,
1435
- placeholders,
1436
- disabled,
1437
- hint,
1438
- onKey
1439
- }) {
1440
- useInput((input, key) => {
1441
- if (disabled) return;
1442
- onKey(input, key);
1443
- });
1444
- const { stdin } = useStdin();
1445
- useEffect(() => {
1446
- if (!stdin || disabled) return;
1447
- const handleData = (data) => {
1448
- const kind = isHomeEnd(data.toString());
1449
- if (kind === "home") onKey("", { ...EMPTY_KEY, home: true });
1450
- else if (kind === "end") onKey("", { ...EMPTY_KEY, end: true });
1451
- };
1452
- stdin.on("data", handleData);
1453
- return () => {
1454
- stdin.off("data", handleData);
1455
- };
1456
- }, [stdin, disabled, onKey]);
1457
- const before = value.slice(0, cursor);
1458
- const at = value.slice(cursor, cursor + 1) || " ";
1459
- const after = value.slice(cursor + 1);
1460
- const promptColor = disabled ? "red" : "cyan";
1461
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
1462
- placeholders.map((p, i) => (
1463
- // biome-ignore lint/suspicious/noArrayIndexKey: placeholders are append-only, index is stable
1464
- /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1465
- " \u21B3 ",
1466
- p
1467
- ] }, i)
1468
- )),
1469
- /* @__PURE__ */ jsxs(Text, { children: [
1470
- /* @__PURE__ */ jsx(Text, { color: promptColor, children: prompt }),
1471
- before,
1472
- /* @__PURE__ */ jsx(Text, { inverse: true, children: at }),
1473
- after
1474
- ] }),
1475
- hint ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: hint }) : null
1476
- ] });
1477
- }
1478
- function fmtElapsed(ms) {
1479
- if (ms < 1e3) return `${ms}ms`;
1480
- if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
1481
- const m = Math.floor(ms / 6e4);
1482
- const s = Math.floor(ms % 6e4 / 1e3);
1483
- return `${m}m${s.toString().padStart(2, "0")}s`;
1484
- }
1485
- function fmtBytes3(n) {
1486
- if (n < 1024) return `${n}B`;
1487
- return `${(n / 1024).toFixed(1)}KB`;
1488
- }
1489
- function fmtRecentTool2(tool) {
1490
- const status = tool.ok === false ? "fail" : "ok";
1491
- const name = tool.name.length > 18 ? `${tool.name.slice(0, 17)}...` : tool.name;
1492
- const parts = [status, name];
1493
- if (typeof tool.durationMs === "number") parts.push(fmtElapsed(tool.durationMs));
1494
- if (typeof tool.outputBytes === "number" && tool.outputBytes > 0) parts.push(fmtBytes3(tool.outputBytes));
1495
- if (typeof tool.outputLines === "number" && tool.outputLines > 0) parts.push(`${tool.outputLines}L`);
1496
- return parts.join(" ");
1497
- }
1498
- function fmtRecentMessage2(message) {
1499
- const text = message.text.replace(/\s+/g, " ");
1500
- return text.length > 48 ? `${text.slice(0, 47)}...` : text;
1501
- }
1502
- function LiveActivityStrip({
1503
- entries,
1504
- nowTick,
1505
- maxRows = 4
1506
- }) {
1507
- const running = Object.values(entries).filter((e) => e.status === "running").sort((a, b) => a.startedAt - b.startedAt).slice(0, maxRows);
1508
- if (running.length === 0) return null;
1509
- const now = Date.now();
1510
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
1511
- running.map((e) => {
1512
- const toolElapsed = e.currentTool ? now - e.currentTool.startedAt : 0;
1513
- const taskElapsed = now - e.startedAt;
1514
- const toolSeg = e.currentTool ? `\u2192 ${e.currentTool.name} (${fmtElapsed(toolElapsed)})` : "idle between tools";
1515
- const recentTools = (e.recentTools ?? []).slice(-2).map(fmtRecentTool2).join(" | ");
1516
- const messageText = e.streamingText.trim() || (e.recentMessages ?? []).slice(-1).map(fmtRecentMessage2).join("");
1517
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
1518
- /* @__PURE__ */ jsx(Text, { color: "cyan", children: "\u25CF" }),
1519
- /* @__PURE__ */ jsx(Text, { children: e.name.slice(0, 14).padEnd(14) }),
1520
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
1521
- /* @__PURE__ */ jsx(Text, { color: e.currentTool ? "green" : "yellow", children: toolSeg }),
1522
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
1523
- /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1524
- e.iterations,
1525
- "it ",
1526
- e.toolCalls,
1527
- "tc \xB7 ",
1528
- fmtElapsed(taskElapsed)
1529
- ] }),
1530
- recentTools ? /* @__PURE__ */ jsxs(Fragment, { children: [
1531
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "|" }),
1532
- /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1533
- "last: ",
1534
- recentTools
1535
- ] })
1536
- ] }) : null,
1537
- messageText ? /* @__PURE__ */ jsxs(Fragment, { children: [
1538
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "|" }),
1539
- /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1540
- "msg: ",
1541
- fmtRecentMessage2({ text: messageText})
1542
- ] })
1543
- ] }) : null
1544
- ] }, e.id);
1545
- }),
1546
- Object.values(entries).filter((e) => e.status === "running").length > maxRows ? /* @__PURE__ */ jsx(Box, { paddingLeft: 2, children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1547
- "\u2026+",
1548
- Object.values(entries).filter((e) => e.status === "running").length - maxRows,
1549
- " more"
1550
- ] }) }) : null
1551
- ] });
1552
- }
1553
- var AUTONOMY_OPTIONS = [
1554
- { mode: "off", label: "OFF", description: "Agent stops after each turn (normal interactive mode)", color: "green" },
1555
- { mode: "suggest", label: "SUGGEST", description: "Shows next-step suggestions after each turn", color: "cyan" },
1556
- { mode: "auto", label: "AUTO", description: "Self-driving \u2014 agent picks next step and continues", color: "yellow" },
1557
- { mode: "eternal", label: "ETERNAL", description: "Goal-driven loop \u2014 requires /goal set first", color: "red" },
1558
- { mode: "eternal-parallel", label: "PARALLEL", description: "Fan-out 4\u20138 subagents per tick \u2014 requires /goal", color: "magenta" }
1559
- ];
1560
- function AutonomyPicker({ options, selected, hint }) {
1561
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
1562
- /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "\u2501\u2501 Autonomy Mode \u2501\u2501" }),
1563
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191/\u2193 navigate \xB7 Enter select \xB7 Esc cancel \xB7 Ctrl+C exit" }),
1564
- options.map((opt, i) => /* @__PURE__ */ jsxs(Text, { color: i === selected ? opt.color : void 0, inverse: i === selected, children: [
1565
- i === selected ? "\u203A " : " ",
1566
- /* @__PURE__ */ jsx(Text, { bold: true, children: opt.label.padEnd(12) }),
1567
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: opt.description })
1568
- ] }, opt.mode)),
1569
- hint ? /* @__PURE__ */ jsx(Text, { color: "yellow", children: hint }) : null
1570
- ] });
1571
- }
1572
- function ModelPicker({
1573
- step,
1574
- providerOptions,
1575
- modelOptions,
1576
- selected,
1577
- pickedProviderId,
1578
- hint
1579
- }) {
1580
- if (step === "provider") {
1581
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
1582
- /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "\u2501\u2501 Switch model \u2014 Step 1/2: Pick provider \u2501\u2501" }),
1583
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191/\u2193 navigate \xB7 Enter select \xB7 Esc cancel \xB7 Ctrl+C exit" }),
1584
- providerOptions.length === 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "(no providers with keys \u2014 add one via `wstack auth`)" }) : providerOptions.map((p, i) => /* @__PURE__ */ jsxs(Text, { color: i === selected ? "cyan" : void 0, inverse: i === selected, children: [
1585
- i === selected ? "\u203A " : " ",
1586
- /* @__PURE__ */ jsx(Text, { bold: true, children: p.id.padEnd(28) }),
1587
- /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1588
- " [",
1589
- p.family,
1590
- "]"
1591
- ] }),
1592
- /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1593
- " ",
1594
- p.models.length,
1595
- " model",
1596
- p.models.length === 1 ? "" : "s"
1597
- ] })
1598
- ] }, p.id)),
1599
- hint ? /* @__PURE__ */ jsx(Text, { color: "yellow", children: hint }) : null
1600
- ] });
1564
+ if (toolName === "git") {
1565
+ if (json && typeof json === "object") {
1566
+ const o = json;
1567
+ const exit = numOf(o["exitCode"]) ?? numOf(o["exit_code"]);
1568
+ const stdout = stringOf(o["stdout"]) ?? "";
1569
+ const stderr = stringOf(o["stderr"]) ?? "";
1570
+ const head = [];
1571
+ if (exit !== void 0) head.push(`exit ${exit}`);
1572
+ const stdoutLines = countLines(stdout);
1573
+ const stderrLines = countLines(stderr);
1574
+ const lparts = [];
1575
+ if (stdoutLines > 0) lparts.push(`${stdoutLines} out`);
1576
+ if (stderrLines > 0) lparts.push(`${stderrLines} err`);
1577
+ if (lparts.length > 0) head.push(lparts.join(" \xB7 "));
1578
+ const lines = [];
1579
+ if (head.length > 0) lines.push(head.join(" \xB7 "));
1580
+ const preview = firstNonEmpty(stdout) ?? firstNonEmpty(stderr);
1581
+ if (preview) lines.push(`"${truncMid(preview, 70)}"`);
1582
+ if (lines.length > 0) return lines;
1583
+ }
1601
1584
  }
1602
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
1603
- /* @__PURE__ */ jsxs(Text, { color: "cyan", bold: true, children: [
1604
- "\u2501\u2501 Switch model \u2014 Step 2/2: Pick model (",
1605
- pickedProviderId,
1606
- ") \u2501\u2501"
1607
- ] }),
1608
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191/\u2193 navigate \xB7 Enter select \xB7 Esc back \xB7 Ctrl+C exit" }),
1609
- modelOptions.length === 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "(no models known for this provider)" }) : modelOptions.map((id, i) => /* @__PURE__ */ jsxs(Text, { color: i === selected ? "cyan" : void 0, inverse: i === selected, children: [
1610
- i === selected ? "\u203A " : " ",
1611
- id
1612
- ] }, id)),
1613
- hint ? /* @__PURE__ */ jsx(Text, { color: "yellow", children: hint }) : null
1614
- ] });
1615
- }
1616
- function SlashMenu({ query, matches, selected }) {
1617
- const placeholder = query ? `/${query}` : "/";
1618
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
1619
- /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1620
- placeholder || "/",
1621
- " \u2014 \u2191/\u2193 select, Enter dispatch, Tab autocomplete, Esc close"
1622
- ] }),
1623
- matches.map((m, i) => /* @__PURE__ */ jsxs(Text, { color: i === selected ? "cyan" : void 0, inverse: i === selected, children: [
1624
- i === selected ? "\u203A " : " ",
1625
- /* @__PURE__ */ jsx(Text, { bold: true, children: m.name }),
1626
- m.argsHint ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1627
- " ",
1628
- m.argsHint
1629
- ] }) : null,
1630
- /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1631
- " \u2014 ",
1632
- m.description
1633
- ] })
1634
- ] }, m.name)),
1635
- matches.length === 0 && /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No matching commands" })
1636
- ] });
1637
- }
1638
- function StatusBar({
1639
- model,
1640
- state,
1641
- tokenCounter,
1642
- hint,
1643
- queueCount = 0,
1644
- yolo = false,
1645
- autonomy,
1646
- elapsedMs,
1647
- todos,
1648
- plan,
1649
- fleet,
1650
- fleetAgents,
1651
- git,
1652
- subagentCount = 0,
1653
- context,
1654
- projectName,
1655
- processCount,
1656
- hiddenItems,
1657
- eternalStage,
1658
- goalSummary
1659
- }) {
1660
- const hiddenSet = new Set(hiddenItems);
1661
- const usage = tokenCounter?.total();
1662
- const cost = tokenCounter?.estimateCost();
1663
- const cache2 = tokenCounter?.cacheStats();
1664
- const stateColor = state === "idle" ? "cyan" : state === "aborting" ? "yellow" : "green";
1665
- const stateLabel = state === "idle" ? "idle" : state === "aborting" ? "aborting\u2026" : "thinking\u2026";
1666
- const hasSecondLine = yolo || autonomy && autonomy !== "off" || elapsedMs !== void 0 || git !== null && git !== void 0 || projectName !== void 0 && projectName.length > 0 || goalSummary !== null && goalSummary !== void 0;
1667
- const fleetHasActivity = fleet && (fleet.running > 0 || fleet.idle > 0 || fleet.pending > 0 || fleet.completed > 0) || subagentCount > 0;
1668
- const hasThirdLine = todos && (todos.pending > 0 || todos.inProgress > 0 || todos.completed > 0) || plan && (plan.open > 0 || plan.inProgress > 0 || plan.done > 0) || fleetHasActivity;
1669
- return /* @__PURE__ */ jsxs(
1670
- Box,
1671
- {
1672
- flexDirection: "column",
1673
- paddingX: 1,
1674
- borderStyle: "single",
1675
- borderTop: true,
1676
- borderBottom: false,
1677
- borderLeft: false,
1678
- borderRight: false,
1679
- children: [
1680
- /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 2, children: [
1681
- /* @__PURE__ */ jsxs(Text, { color: stateColor, children: [
1682
- "\u25CF ",
1683
- stateLabel
1684
- ] }),
1685
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
1686
- /* @__PURE__ */ jsx(Text, { color: "magenta", children: model }),
1687
- context && context.max > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
1688
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
1689
- /* @__PURE__ */ jsx(ContextChip, { ctx: context })
1690
- ] }) : null,
1691
- usage ? /* @__PURE__ */ jsxs(Fragment, { children: [
1692
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
1693
- /* @__PURE__ */ jsxs(Text, { children: [
1694
- "\u2191",
1695
- " ",
1696
- /* @__PURE__ */ jsx(Text, { color: "cyan", children: fmtTok2(usage.input + (usage.cacheRead ?? 0) + (usage.cacheWrite ?? 0)) }),
1697
- " ",
1698
- "\u2193 ",
1699
- /* @__PURE__ */ jsx(Text, { color: "cyan", children: fmtTok2(usage.output) })
1700
- ] })
1701
- ] }) : null,
1702
- cache2 && cache2.hitRatio > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
1703
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
1704
- /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1705
- "cache ",
1706
- (cache2.hitRatio * 100).toFixed(0),
1707
- "%"
1708
- ] })
1709
- ] }) : null,
1710
- cost && cost.total > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
1711
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
1712
- /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
1713
- "$",
1714
- cost.total.toFixed(4)
1715
- ] })
1716
- ] }) : null,
1717
- queueCount > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
1718
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
1719
- /* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
1720
- "\u231B queued: ",
1721
- queueCount
1722
- ] })
1723
- ] }) : null,
1724
- typeof processCount === "number" && processCount > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
1725
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
1726
- /* @__PURE__ */ jsxs(Text, { color: "red", children: [
1727
- "\u26A1 ",
1728
- processCount,
1729
- " process",
1730
- processCount === 1 ? "" : "es"
1731
- ] })
1732
- ] }) : null,
1733
- hint ? /* @__PURE__ */ jsxs(Fragment, { children: [
1734
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
1735
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: hint })
1736
- ] }) : null
1737
- ] }),
1738
- hasSecondLine ? /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 2, children: [
1739
- yolo ? /* @__PURE__ */ jsx(Text, { color: "red", bold: true, children: "\u26A0 YOLO" }) : null,
1740
- autonomy && autonomy !== "off" ? /* @__PURE__ */ jsxs(Fragment, { children: [
1741
- yolo ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
1742
- /* @__PURE__ */ jsxs(
1743
- Text,
1744
- {
1745
- color: autonomy === "eternal" ? "red" : autonomy === "auto" ? "yellow" : "cyan",
1746
- bold: true,
1747
- children: [
1748
- "\u221E ",
1749
- autonomy.toUpperCase()
1750
- ]
1751
- }
1752
- )
1753
- ] }) : null,
1754
- eternalStage ? /* @__PURE__ */ jsxs(Fragment, { children: [
1755
- yolo || autonomy && autonomy !== "off" ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
1756
- /* @__PURE__ */ jsx(EternalStageChip, { stage: eternalStage })
1757
- ] }) : null,
1758
- elapsedMs !== void 0 && !hiddenSet.has("elapsed") ? /* @__PURE__ */ jsxs(Fragment, { children: [
1759
- yolo || autonomy && autonomy !== "off" || eternalStage ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
1760
- /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1761
- "\u23F1 ",
1762
- fmtElapsed2(elapsedMs)
1763
- ] })
1764
- ] }) : null,
1765
- projectName ? /* @__PURE__ */ jsxs(Fragment, { children: [
1766
- yolo || elapsedMs !== void 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
1767
- /* @__PURE__ */ jsxs(Text, { color: "blue", children: [
1768
- "\u{1F4C1} ",
1769
- projectName
1770
- ] })
1771
- ] }) : null,
1772
- goalSummary ? /* @__PURE__ */ jsxs(Fragment, { children: [
1773
- yolo || elapsedMs !== void 0 || projectName ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
1774
- /* @__PURE__ */ jsxs(Text, { color: goalSummary.goalState === "active" ? "green" : goalSummary.goalState === "paused" ? "yellow" : goalSummary.goalState === "completed" ? "green" : "dim", children: [
1775
- "\u{1F3AF} ",
1776
- goalSummary.goal.length > 40 ? `${goalSummary.goal.slice(0, 37)}\u2026` : goalSummary.goal,
1777
- " [",
1778
- goalSummary.goalState,
1779
- "] (iter ",
1780
- goalSummary.iterations,
1781
- ")"
1782
- ] })
1783
- ] }) : null,
1784
- git ? /* @__PURE__ */ jsxs(Fragment, { children: [
1785
- yolo || elapsedMs !== void 0 || projectName ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
1786
- /* @__PURE__ */ jsxs(Text, { children: [
1787
- /* @__PURE__ */ jsxs(Text, { color: "magenta", children: [
1788
- "\u2387 ",
1789
- git.branch
1790
- ] }),
1791
- git.added > 0 ? /* @__PURE__ */ jsxs(Text, { color: "green", children: [
1792
- " +",
1793
- git.added
1794
- ] }) : null,
1795
- git.deleted > 0 ? /* @__PURE__ */ jsxs(Text, { color: "red", children: [
1796
- " -",
1797
- git.deleted
1798
- ] }) : null,
1799
- git.untracked > 0 ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1800
- " ?",
1801
- git.untracked
1802
- ] }) : null
1803
- ] })
1804
- ] }) : null
1805
- ] }) : null,
1806
- hasThirdLine ? /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 2, children: [
1807
- todos && (todos.pending > 0 || todos.inProgress > 0 || todos.completed > 0) ? /* @__PURE__ */ jsxs(Text, { children: [
1808
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "todos " }),
1809
- todos.inProgress > 0 ? /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
1810
- "\u231B",
1811
- todos.inProgress
1812
- ] }) : null,
1813
- todos.inProgress > 0 && (todos.pending > 0 || todos.completed > 0) ? " " : "",
1814
- todos.pending > 0 ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1815
- "\u2610",
1816
- todos.pending
1817
- ] }) : null,
1818
- todos.pending > 0 && todos.completed > 0 ? " " : "",
1819
- todos.completed > 0 ? /* @__PURE__ */ jsxs(Text, { color: "green", children: [
1820
- "\u2713",
1821
- todos.completed
1822
- ] }) : null
1823
- ] }) : null,
1824
- plan && (plan.open > 0 || plan.inProgress > 0 || plan.done > 0) ? /* @__PURE__ */ jsxs(Fragment, { children: [
1825
- todos && (todos.pending > 0 || todos.inProgress > 0 || todos.completed > 0) ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
1826
- /* @__PURE__ */ jsxs(Text, { children: [
1827
- /* @__PURE__ */ jsx(Text, { color: "cyan", children: "\u{1F4CB} " }),
1828
- plan.inProgress > 0 ? /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
1829
- "\u231B",
1830
- plan.inProgress
1831
- ] }) : null,
1832
- plan.inProgress > 0 && (plan.open > 0 || plan.done > 0) ? " " : "",
1833
- plan.open > 0 ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1834
- "\u2610",
1835
- plan.open
1836
- ] }) : null,
1837
- plan.open > 0 && plan.done > 0 ? " " : "",
1838
- plan.done > 0 ? /* @__PURE__ */ jsxs(Text, { color: "green", children: [
1839
- "\u2713",
1840
- plan.done
1841
- ] }) : null
1842
- ] })
1843
- ] }) : null,
1844
- fleetHasActivity ? /* @__PURE__ */ jsxs(Fragment, { children: [
1845
- todos && (todos.pending > 0 || todos.inProgress > 0 || todos.completed > 0) || plan && (plan.open > 0 || plan.inProgress > 0 || plan.done > 0) ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
1846
- fleet ? /* @__PURE__ */ jsxs(Text, { children: [
1847
- /* @__PURE__ */ jsx(Text, { color: "blue", children: "\u{1F310} " }),
1848
- fleet.running > 0 ? /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
1849
- "\u25B6",
1850
- fleet.running
1851
- ] }) : null,
1852
- fleet.running > 0 && (fleet.pending > 0 || fleet.idle > 0 || fleet.completed > 0) ? " " : "",
1853
- fleet.pending > 0 ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1854
- "\u2610",
1855
- fleet.pending
1856
- ] }) : null,
1857
- fleet.pending > 0 && (fleet.idle > 0 || fleet.completed > 0) ? " " : "",
1858
- fleet.idle > 0 ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1859
- "\xB7",
1860
- fleet.idle,
1861
- "idle"
1862
- ] }) : null,
1863
- fleet.idle > 0 && fleet.completed > 0 ? " " : "",
1864
- fleet.completed > 0 ? /* @__PURE__ */ jsxs(Text, { color: "green", children: [
1865
- "\u2713",
1866
- fleet.completed
1867
- ] }) : null
1868
- ] }) : /* @__PURE__ */ jsxs(Text, { color: "blue", children: [
1869
- "\u{1F310} ",
1870
- subagentCount,
1871
- " agent",
1872
- subagentCount === 1 ? "" : "s"
1873
- ] })
1874
- ] }) : null
1875
- ] }) : null,
1876
- fleetAgents && fleetAgents.length > 0 ? /* @__PURE__ */ jsx(Box, { flexDirection: "row", gap: 2, children: fleetAgents.map((a, i) => (
1877
- // biome-ignore lint/suspicious/noArrayIndexKey: agent list is stable per render
1878
- /* @__PURE__ */ jsxs(Text, { children: [
1879
- /* @__PURE__ */ jsx(Text, { color: a.color, bold: true, children: a.label }),
1880
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: " " }),
1881
- /* @__PURE__ */ jsx(Text, { color: a.running ? "yellow" : void 0, dimColor: !a.running, children: a.running ? "\u25B6" : "\xB7" }),
1882
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: " " }),
1883
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: fmtElapsed2(a.elapsedMs) }),
1884
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \xB7 " }),
1885
- /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1886
- a.toolCalls,
1887
- "t"
1888
- ] })
1889
- ] }, i)
1890
- )) }) : null
1891
- ]
1585
+ if (toolName === "lint") {
1586
+ if (json && typeof json === "object") {
1587
+ const o = json;
1588
+ const linter = stringOf(o["linter"]);
1589
+ const files = numOf(o["files_checked"]);
1590
+ const errors = numOf(o["errors"]) ?? 0;
1591
+ const warnings = numOf(o["warnings"]) ?? 0;
1592
+ const fix = o["fix_applied"] === true;
1593
+ const head = [];
1594
+ if (linter && linter !== "none") head.push(linter);
1595
+ head.push(`${errors} error${errors === 1 ? "" : "s"}`);
1596
+ head.push(`${warnings} warning${warnings === 1 ? "" : "s"}`);
1597
+ if (files !== void 0) head.push(`${files} file${files === 1 ? "" : "s"}`);
1598
+ if (fix) head.push("fixed");
1599
+ return [head.join(" \xB7 ")];
1600
+ }
1601
+ }
1602
+ if (toolName === "format") {
1603
+ if (json && typeof json === "object") {
1604
+ const o = json;
1605
+ const fixer = stringOf(o["fixer"]);
1606
+ const checked = numOf(o["files_checked"]);
1607
+ const changed = numOf(o["files_changed"]);
1608
+ const head = [];
1609
+ if (fixer && fixer !== "none") head.push(fixer);
1610
+ if (changed !== void 0 && checked !== void 0) {
1611
+ head.push(`${changed}/${checked} changed`);
1612
+ } else if (changed !== void 0) {
1613
+ head.push(`${changed} changed`);
1614
+ }
1615
+ return head.length > 0 ? [head.join(" \xB7 ")] : [];
1616
+ }
1617
+ }
1618
+ if (toolName === "typecheck") {
1619
+ if (json && typeof json === "object") {
1620
+ const o = json;
1621
+ const exit = numOf(o["exit_code"]) ?? numOf(o["exitCode"]);
1622
+ const errors = numOf(o["errors"]);
1623
+ const head = [];
1624
+ if (errors !== void 0) head.push(`${errors} error${errors === 1 ? "" : "s"}`);
1625
+ if (exit !== void 0) head.push(`exit ${exit}`);
1626
+ const stdout = stringOf(o["output"]) ?? stringOf(o["stdout"]) ?? "";
1627
+ const lines = [];
1628
+ if (head.length > 0) lines.push(head.join(" \xB7 "));
1629
+ const preview = firstNonEmpty(stdout);
1630
+ if (preview && (!errors || errors > 0)) lines.push(`"${truncMid(preview, 70)}"`);
1631
+ if (lines.length > 0) return lines;
1632
+ }
1633
+ }
1634
+ if (toolName === "test") {
1635
+ if (json && typeof json === "object") {
1636
+ const o = json;
1637
+ const runner = stringOf(o["runner"]);
1638
+ const total = numOf(o["tests_run"]) ?? 0;
1639
+ const passed = numOf(o["passed"]) ?? 0;
1640
+ const failed = numOf(o["failed"]) ?? 0;
1641
+ const duration = numOf(o["duration_ms"]);
1642
+ const head = [];
1643
+ if (runner && runner !== "none") head.push(runner);
1644
+ head.push(`${passed}/${total} passed`);
1645
+ if (failed > 0) head.push(`${failed} failed`);
1646
+ if (duration !== void 0) head.push(fmtDuration2(duration));
1647
+ return [head.join(" \xB7 ")];
1648
+ }
1649
+ }
1650
+ if (toolName === "audit") {
1651
+ if (json && typeof json === "object") {
1652
+ const o = json;
1653
+ const total = numOf(o["total"]) ?? 0;
1654
+ const summary = stringOf(o["summary"]);
1655
+ if (total === 0) return ["no vulnerabilities"];
1656
+ const head = `${total} vulnerabilit${total === 1 ? "y" : "ies"}`;
1657
+ return summary && summary.toLowerCase() !== head.toLowerCase() ? [head, truncMid(summary, OUT_BUDGET)] : [head];
1658
+ }
1659
+ }
1660
+ if (toolName === "outdated") {
1661
+ if (json && typeof json === "object") {
1662
+ const o = json;
1663
+ const total = numOf(o["total"]) ?? 0;
1664
+ const pkgs = Array.isArray(o["packages"]) ? o["packages"] : void 0;
1665
+ if (total === 0) return ["all up to date"];
1666
+ const lines = [`${total} outdated`];
1667
+ if (pkgs && pkgs.length > 0) {
1668
+ const first = pkgs[0];
1669
+ if (first && typeof first === "object") {
1670
+ const p = first;
1671
+ const name = stringOf(p["name"]) ?? stringOf(p["package"]);
1672
+ const cur = stringOf(p["current"]);
1673
+ const wanted = stringOf(p["wanted"]) ?? stringOf(p["latest"]);
1674
+ if (name && cur && wanted) lines.push(`${name}: ${cur} \u2192 ${wanted}`);
1675
+ else if (name) lines.push(name);
1676
+ }
1677
+ }
1678
+ return lines;
1679
+ }
1680
+ }
1681
+ if (toolName === "tree") {
1682
+ if (json && typeof json === "object") {
1683
+ const o = json;
1684
+ const files = numOf(o["total_files"]);
1685
+ const dirs = numOf(o["total_dirs"]);
1686
+ const truncated = o["truncated"] === true;
1687
+ const parts = [];
1688
+ if (files !== void 0) parts.push(`${files} file${files === 1 ? "" : "s"}`);
1689
+ if (dirs !== void 0) parts.push(`${dirs} dir${dirs === 1 ? "" : "s"}`);
1690
+ if (truncated) parts.push("truncated");
1691
+ return parts.length > 0 ? [parts.join(" \xB7 ")] : [];
1692
+ }
1693
+ }
1694
+ if (toolName === "json") {
1695
+ if (json && typeof json === "object") {
1696
+ const o = json;
1697
+ const err = stringOf(o["error"]);
1698
+ if (err) return [truncMid(err, OUT_BUDGET)];
1699
+ const type = stringOf(o["type"]);
1700
+ const keys = Array.isArray(o["keys"]) ? o["keys"] : void 0;
1701
+ const parts = [];
1702
+ if (type) parts.push(type);
1703
+ if (keys) parts.push(`${keys.length} key${keys.length === 1 ? "" : "s"}`);
1704
+ return parts.length > 0 ? [parts.join(" \xB7 ")] : [];
1705
+ }
1706
+ }
1707
+ if (toolName === "install") {
1708
+ if (json && typeof json === "object") {
1709
+ const o = json;
1710
+ const exit = numOf(o["exit_code"]) ?? numOf(o["exitCode"]);
1711
+ const added = numOf(o["added"]);
1712
+ const removed = numOf(o["removed"]);
1713
+ const head = [];
1714
+ if (exit !== void 0) head.push(`exit ${exit}`);
1715
+ if (added !== void 0) head.push(`+${added}`);
1716
+ if (removed !== void 0) head.push(`-${removed}`);
1717
+ const stdout = stringOf(o["stdout"]) ?? stringOf(o["output"]) ?? "";
1718
+ const lines = [];
1719
+ if (head.length > 0) lines.push(head.join(" \xB7 "));
1720
+ const preview = firstNonEmpty(stdout);
1721
+ if (preview) lines.push(`"${truncMid(preview, 70)}"`);
1722
+ if (lines.length > 0) return lines;
1723
+ }
1724
+ }
1725
+ if (toolName === "scaffold") {
1726
+ if (json && typeof json === "object") {
1727
+ const o = json;
1728
+ const created = Array.isArray(o["created"]) ? o["created"] : void 0;
1729
+ const skipped = Array.isArray(o["skipped"]) ? o["skipped"] : void 0;
1730
+ const parts = [];
1731
+ if (created !== void 0) parts.push(`${created.length} created`);
1732
+ if (skipped !== void 0 && skipped.length > 0) parts.push(`${skipped.length} skipped`);
1733
+ if (parts.length > 0) return [parts.join(" \xB7 ")];
1734
+ }
1735
+ }
1736
+ if (toolName === "remember" || toolName === "forget" || toolName === "memory") {
1737
+ return ok ? [toolName === "forget" ? "removed" : "saved"] : [text.split("\n")[0] ?? ""];
1738
+ }
1739
+ if (toolName === "mode") {
1740
+ if (json && typeof json === "object") {
1741
+ const o = json;
1742
+ const mode = stringOf(o["mode"]) ?? stringOf(o["active"]) ?? stringOf(o["name"]);
1743
+ if (mode) return [`mode: ${mode}`];
1744
+ }
1745
+ }
1746
+ if (toolName === "search") {
1747
+ if (json && typeof json === "object") {
1748
+ const o = json;
1749
+ const matches = Array.isArray(o["matches"]) ? o["matches"] : Array.isArray(o["results"]) ? o["results"] : void 0;
1750
+ const count = numOf(o["count"]) ?? matches?.length;
1751
+ if (count !== void 0) {
1752
+ if (count === 0) return ["no results"];
1753
+ const lines = [`${count} result${count === 1 ? "" : "s"}`];
1754
+ const firstHit = matches && matches.length > 0 ? formatMatchHit(matches[0]) : void 0;
1755
+ if (firstHit) lines.push(firstHit);
1756
+ return lines;
1757
+ }
1758
+ }
1759
+ }
1760
+ if (toolName === "logs") {
1761
+ const lines = text.split("\n").filter((l) => l.trim());
1762
+ if (lines.length === 0) return [];
1763
+ const head = `${lines.length} line${lines.length === 1 ? "" : "s"}`;
1764
+ const lastLine = lines[lines.length - 1];
1765
+ return lastLine ? [head, `"${truncMid(lastLine.trim(), 70)}"`] : [head];
1766
+ }
1767
+ if (json && typeof json === "object" && !Array.isArray(json)) {
1768
+ const summary = summarizeJsonObject(json);
1769
+ if (summary) return [summary];
1770
+ }
1771
+ const collapsed = text.replace(/\s+/g, " ").trim();
1772
+ return [truncMid(collapsed, GENERIC_BUDGET)];
1773
+ }
1774
+ var GENERIC_BUDGET = 240;
1775
+ function summarizeJsonObject(obj) {
1776
+ const keys = Object.keys(obj);
1777
+ if (keys.length === 0) return null;
1778
+ const priority = [
1779
+ "ok",
1780
+ "status",
1781
+ "timedOut",
1782
+ "stopReason",
1783
+ "reason",
1784
+ "error",
1785
+ "message",
1786
+ "result",
1787
+ "summary",
1788
+ "iterations",
1789
+ "toolCalls",
1790
+ "durationMs",
1791
+ "subagentId",
1792
+ "taskId"
1793
+ ];
1794
+ const ordered = [
1795
+ ...priority.filter((k) => keys.includes(k)),
1796
+ ...keys.filter((k) => !priority.includes(k))
1797
+ ];
1798
+ const parts = [];
1799
+ let used = 0;
1800
+ for (const key of ordered) {
1801
+ const v = obj[key];
1802
+ if (v === void 0 || v === null) continue;
1803
+ const rendered = typeof v === "string" ? `${key}="${truncMid(v.replace(/\s+/g, " "), 80)}"` : typeof v === "number" || typeof v === "boolean" ? `${key}=${v}` : Array.isArray(v) ? `${key}=[${v.length}]` : `${key}={\u2026}`;
1804
+ if (used + rendered.length > GENERIC_BUDGET) {
1805
+ parts.push("\u2026");
1806
+ break;
1892
1807
  }
1893
- );
1808
+ parts.push(rendered);
1809
+ used += rendered.length + 3;
1810
+ }
1811
+ return parts.length > 0 ? parts.join(" \xB7 ") : null;
1894
1812
  }
1895
- function EternalStageChip({
1896
- stage
1897
- }) {
1898
- switch (stage.phase) {
1899
- case "idle":
1900
- return /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2B1C idle" });
1901
- case "decide":
1902
- return /* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
1903
- "\u2B07 decide: ",
1904
- stage.reason
1905
- ] });
1906
- case "execute":
1907
- return /* @__PURE__ */ jsxs(Text, { color: "green", children: [
1908
- "\u25B6 ",
1909
- /* @__PURE__ */ jsx(Text, { bold: true, children: "execute" }),
1910
- stage.task ? `(${stage.task})` : ""
1911
- ] });
1912
- case "reflect":
1913
- return /* @__PURE__ */ jsxs(Text, { color: stage.status === "success" ? "green" : stage.status === "failure" ? "red" : "yellow", children: [
1914
- "\u21A9 reflect: ",
1915
- stage.status
1916
- ] });
1917
- case "sleep":
1918
- return /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1919
- "\u{1F4A4} sleep ",
1920
- Math.round(stage.ms / 1e3),
1921
- "s"
1922
- ] });
1923
- case "paused":
1924
- return /* @__PURE__ */ jsx(Text, { color: "yellow", children: "\u23F8 paused" });
1925
- case "stopped":
1926
- return /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u25A0 stopped" });
1927
- case "error":
1928
- return /* @__PURE__ */ jsxs(Text, { color: "red", children: [
1929
- "\u26A0 error: ",
1930
- stage.message
1931
- ] });
1813
+ function firstNonEmpty(text) {
1814
+ if (!text) return void 0;
1815
+ const line = text.split("\n").find((l) => l.trim());
1816
+ return line ? line.replace(/\s+/g, " ").trim() : void 0;
1817
+ }
1818
+ function formatMatchHit(hit) {
1819
+ if (typeof hit === "string") return truncMid(hit, 70);
1820
+ if (hit && typeof hit === "object") {
1821
+ const o = hit;
1822
+ const file = stringOf(o["file"]) ?? stringOf(o["path"]);
1823
+ const line = numOf(o["line"]) ?? numOf(o["lineNumber"]);
1824
+ const snippet = stringOf(o["text"]) ?? stringOf(o["match"]) ?? stringOf(o["preview"]);
1825
+ if (file) {
1826
+ const head = line !== void 0 ? `${shortenPath(file, 40)}:${line}` : shortenPath(file, 50);
1827
+ return snippet ? `${head} ${truncMid(snippet.replace(/\s+/g, " "), 40)}` : head;
1828
+ }
1829
+ if (snippet) return truncMid(snippet, 70);
1932
1830
  }
1831
+ return void 0;
1933
1832
  }
1934
- function ContextChip({ ctx }) {
1935
- const ratio = Math.max(0, Math.min(1, ctx.used / ctx.max));
1936
- const pct = Math.round(ratio * 100);
1937
- const color = ratio >= 0.85 ? "red" : ratio >= 0.65 ? "yellow" : "cyan";
1938
- return /* @__PURE__ */ jsxs(Text, { children: [
1939
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "ctx " }),
1940
- /* @__PURE__ */ jsx(Text, { color, children: renderProgress(ratio, 10) }),
1941
- /* @__PURE__ */ jsxs(Text, { color, children: [
1942
- " ",
1943
- pct,
1944
- "%"
1833
+ var DIFF_MAX_LINES = 8;
1834
+ function extractDiffPreview(toolName, output) {
1835
+ if (!output) return void 0;
1836
+ const text = output.trim();
1837
+ if (!text) return void 0;
1838
+ let diff;
1839
+ if (toolName === "edit" || toolName === "diff") {
1840
+ const parsed = tryParseJson(text);
1841
+ if (parsed && typeof parsed === "object") {
1842
+ diff = stringOf(parsed["diff"]);
1843
+ }
1844
+ } else if (toolName === "patch") {
1845
+ const parsed = tryParseJson(text);
1846
+ if (parsed && typeof parsed === "object") {
1847
+ diff = stringOf(parsed["diff"]) ?? stringOf(parsed["stdout"]);
1848
+ } else if (text.includes("@@") || text.startsWith("---")) {
1849
+ diff = text;
1850
+ }
1851
+ }
1852
+ if (!diff || !diff.trim() || diff.startsWith("(no-op")) return void 0;
1853
+ return parseUnifiedDiff(diff, DIFF_MAX_LINES);
1854
+ }
1855
+ function parseUnifiedDiff(diff, maxLines) {
1856
+ const all = [];
1857
+ let oldLn = 0;
1858
+ let newLn = 0;
1859
+ for (const raw of diff.split("\n")) {
1860
+ const line = raw.replace(/\r$/, "");
1861
+ if (line.startsWith("+++") || line.startsWith("---")) continue;
1862
+ if (line.startsWith("diff --git") || line.startsWith("index ")) continue;
1863
+ if (line.startsWith("@@")) {
1864
+ const m = line.match(/^@@\s+-(\d+)(?:,\d+)?\s+\+(\d+)(?:,\d+)?\s+@@/);
1865
+ if (m) {
1866
+ oldLn = Number.parseInt(m[1] ?? "0", 10) || 0;
1867
+ newLn = Number.parseInt(m[2] ?? "0", 10) || 0;
1868
+ }
1869
+ all.push({ kind: "hunk", text: truncMid(line, 60) });
1870
+ continue;
1871
+ }
1872
+ if (line.startsWith("+")) {
1873
+ all.push({ kind: "add", text: truncMid(line, 100), newLine: newLn });
1874
+ newLn++;
1875
+ continue;
1876
+ }
1877
+ if (line.startsWith("-")) {
1878
+ all.push({ kind: "del", text: truncMid(line, 100), oldLine: oldLn });
1879
+ oldLn++;
1880
+ continue;
1881
+ }
1882
+ if (line.startsWith("\\ No newline")) {
1883
+ all.push({ kind: "meta", text: line });
1884
+ continue;
1885
+ }
1886
+ if (line.length === 0) continue;
1887
+ all.push({ kind: "ctx", text: truncMid(line, 100), oldLine: oldLn, newLine: newLn });
1888
+ oldLn++;
1889
+ newLn++;
1890
+ }
1891
+ if (all.length === 0) return { rows: [], hidden: 0 };
1892
+ if (all.length <= maxLines) return { rows: all, hidden: 0 };
1893
+ return { rows: all.slice(0, maxLines), hidden: all.length - maxLines };
1894
+ }
1895
+ function stringOf(v) {
1896
+ return typeof v === "string" && v.length > 0 ? v : void 0;
1897
+ }
1898
+ function numOf(v) {
1899
+ return typeof v === "number" && Number.isFinite(v) ? v : void 0;
1900
+ }
1901
+ function tryParseJson(s) {
1902
+ const t = s.trimStart();
1903
+ if (!t.startsWith("{") && !t.startsWith("[")) return void 0;
1904
+ try {
1905
+ return JSON.parse(s);
1906
+ } catch {
1907
+ return void 0;
1908
+ }
1909
+ }
1910
+ function scanNumberedRange(text) {
1911
+ let first;
1912
+ let last;
1913
+ let count = 0;
1914
+ for (const line of text.split("\n")) {
1915
+ const m = line.match(/^\s*(\d+)→/);
1916
+ if (m?.[1]) {
1917
+ const n = Number.parseInt(m[1], 10);
1918
+ if (Number.isFinite(n)) {
1919
+ if (first === void 0) first = n;
1920
+ last = n;
1921
+ count++;
1922
+ }
1923
+ }
1924
+ }
1925
+ return { first, last, count };
1926
+ }
1927
+ function countLines(text) {
1928
+ if (!text) return 0;
1929
+ return text.replace(/\n$/, "").split("\n").length;
1930
+ }
1931
+ function fmtBytes2(n) {
1932
+ if (n < 1024) return `${n}B`;
1933
+ if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)}KB`;
1934
+ return `${(n / (1024 * 1024)).toFixed(1)}MB`;
1935
+ }
1936
+ function truncMid(s, max) {
1937
+ if (s.length <= max) return s;
1938
+ return `${s.slice(0, max - 1)}\u2026`;
1939
+ }
1940
+ function isHomeEnd(data) {
1941
+ if (data === "\x1B[H" || data === "\x1B[1~" || data === "\x1BOH" || data === "\x1B[7~")
1942
+ return "home";
1943
+ if (data === "\x1B[F" || data === "\x1B[4~" || data === "\x1BOF" || data === "\x1B[8~")
1944
+ return "end";
1945
+ return null;
1946
+ }
1947
+ var EMPTY_KEY = {
1948
+ upArrow: false,
1949
+ downArrow: false,
1950
+ leftArrow: false,
1951
+ rightArrow: false,
1952
+ return: false,
1953
+ escape: false,
1954
+ ctrl: false,
1955
+ meta: false,
1956
+ shift: false,
1957
+ tab: false,
1958
+ backspace: false,
1959
+ delete: false,
1960
+ pageUp: false,
1961
+ pageDown: false,
1962
+ home: false,
1963
+ end: false
1964
+ };
1965
+ function Input({
1966
+ prompt = "\u203A ",
1967
+ value,
1968
+ cursor,
1969
+ placeholders,
1970
+ disabled,
1971
+ hint,
1972
+ onKey
1973
+ }) {
1974
+ useInput((input, key) => {
1975
+ if (disabled) return;
1976
+ onKey(input, key);
1977
+ });
1978
+ const { stdin } = useStdin();
1979
+ useEffect(() => {
1980
+ if (!stdin || disabled) return;
1981
+ const handleData = (data) => {
1982
+ const kind = isHomeEnd(data.toString());
1983
+ if (kind === "home") onKey("", { ...EMPTY_KEY, home: true });
1984
+ else if (kind === "end") onKey("", { ...EMPTY_KEY, end: true });
1985
+ };
1986
+ stdin.on("data", handleData);
1987
+ return () => {
1988
+ stdin.off("data", handleData);
1989
+ };
1990
+ }, [stdin, disabled, onKey]);
1991
+ const before = value.slice(0, cursor);
1992
+ const at = value.slice(cursor, cursor + 1) || " ";
1993
+ const after = value.slice(cursor + 1);
1994
+ const promptColor = disabled ? "red" : "cyan";
1995
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
1996
+ placeholders.map((p, i) => (
1997
+ // biome-ignore lint/suspicious/noArrayIndexKey: placeholders are append-only, index is stable
1998
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1999
+ " \u21B3 ",
2000
+ p
2001
+ ] }, i)
2002
+ )),
2003
+ /* @__PURE__ */ jsxs(Text, { children: [
2004
+ /* @__PURE__ */ jsx(Text, { color: promptColor, children: prompt }),
2005
+ before,
2006
+ /* @__PURE__ */ jsx(Text, { inverse: true, children: at }),
2007
+ after
1945
2008
  ] }),
1946
- /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1947
- " ",
1948
- "(",
1949
- fmtTok2(ctx.used),
1950
- "/",
1951
- fmtTok2(ctx.max),
1952
- ")"
1953
- ] })
2009
+ hint ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: hint }) : null
1954
2010
  ] });
1955
2011
  }
1956
- var FILLED = "\u2588";
1957
- var EMPTY = "\u2591";
1958
- function renderProgress(ratio, width) {
1959
- const clamped = Math.max(0, Math.min(1, ratio));
1960
- const filled = clamped === 0 ? 0 : Math.max(1, Math.round(clamped * width));
1961
- const capped = Math.min(width, filled);
1962
- return FILLED.repeat(capped) + EMPTY.repeat(width - capped);
2012
+ function fmtElapsed2(ms) {
2013
+ if (ms < 1e3) return `${ms}ms`;
2014
+ if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
2015
+ const m = Math.floor(ms / 6e4);
2016
+ const s = Math.floor(ms % 6e4 / 1e3);
2017
+ return `${m}m${s.toString().padStart(2, "0")}s`;
1963
2018
  }
1964
- function fmtTok2(n) {
1965
- if (n < 1e3) return String(n);
1966
- if (n < 1e6) return `${(n / 1e3).toFixed(n < 1e4 ? 1 : 0)}k`;
1967
- return `${(n / 1e6).toFixed(1)}M`;
2019
+ function fmtBytes3(n) {
2020
+ if (n < 1024) return `${n}B`;
2021
+ return `${(n / 1024).toFixed(1)}KB`;
1968
2022
  }
1969
- function fmtElapsed2(ms) {
1970
- const totalSec = Math.floor(ms / 1e3);
1971
- const h = Math.floor(totalSec / 3600);
1972
- const m = Math.floor(totalSec % 3600 / 60);
1973
- const s = totalSec % 60;
1974
- if (h > 0) {
1975
- return `${h}:${pad2(m)}:${pad2(s)}`;
2023
+ function fmtRecentTool2(tool) {
2024
+ const status = tool.ok === false ? "fail" : "ok";
2025
+ const name = tool.name.length > 18 ? `${tool.name.slice(0, 17)}...` : tool.name;
2026
+ const parts = [status, name];
2027
+ if (typeof tool.durationMs === "number") parts.push(fmtElapsed2(tool.durationMs));
2028
+ if (typeof tool.outputBytes === "number" && tool.outputBytes > 0) parts.push(fmtBytes3(tool.outputBytes));
2029
+ if (typeof tool.outputLines === "number" && tool.outputLines > 0) parts.push(`${tool.outputLines}L`);
2030
+ return parts.join(" ");
2031
+ }
2032
+ function fmtRecentMessage2(message) {
2033
+ const text = message.text.replace(/\s+/g, " ");
2034
+ return text.length > 48 ? `${text.slice(0, 47)}...` : text;
2035
+ }
2036
+ function LiveActivityStrip({
2037
+ entries,
2038
+ nowTick,
2039
+ maxRows = 4
2040
+ }) {
2041
+ const running = Object.values(entries).filter((e) => e.status === "running").sort((a, b) => a.startedAt - b.startedAt).slice(0, maxRows);
2042
+ if (running.length === 0) return null;
2043
+ const now = Date.now();
2044
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
2045
+ running.map((e) => {
2046
+ const toolElapsed = e.currentTool ? now - e.currentTool.startedAt : 0;
2047
+ const taskElapsed = now - e.startedAt;
2048
+ const toolSeg = e.currentTool ? `\u2192 ${e.currentTool.name} (${fmtElapsed2(toolElapsed)})` : "idle between tools";
2049
+ const recentTools = (e.recentTools ?? []).slice(-2).map(fmtRecentTool2).join(" | ");
2050
+ const messageText = e.streamingText.trim() || (e.recentMessages ?? []).slice(-1).map(fmtRecentMessage2).join("");
2051
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
2052
+ /* @__PURE__ */ jsx(Text, { color: "cyan", children: "\u25CF" }),
2053
+ /* @__PURE__ */ jsx(Text, { children: e.name.slice(0, 14).padEnd(14) }),
2054
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
2055
+ /* @__PURE__ */ jsx(Text, { color: e.currentTool ? "green" : "yellow", children: toolSeg }),
2056
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
2057
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
2058
+ e.iterations,
2059
+ "it ",
2060
+ e.toolCalls,
2061
+ "tc \xB7 ",
2062
+ fmtElapsed2(taskElapsed)
2063
+ ] }),
2064
+ recentTools ? /* @__PURE__ */ jsxs(Fragment, { children: [
2065
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "|" }),
2066
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
2067
+ "last: ",
2068
+ recentTools
2069
+ ] })
2070
+ ] }) : null,
2071
+ messageText ? /* @__PURE__ */ jsxs(Fragment, { children: [
2072
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "|" }),
2073
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
2074
+ "msg: ",
2075
+ fmtRecentMessage2({ text: messageText})
2076
+ ] })
2077
+ ] }) : null
2078
+ ] }, e.id);
2079
+ }),
2080
+ Object.values(entries).filter((e) => e.status === "running").length > maxRows ? /* @__PURE__ */ jsx(Box, { paddingLeft: 2, children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
2081
+ "\u2026+",
2082
+ Object.values(entries).filter((e) => e.status === "running").length - maxRows,
2083
+ " more"
2084
+ ] }) }) : null
2085
+ ] });
2086
+ }
2087
+ var AUTONOMY_OPTIONS = [
2088
+ { mode: "off", label: "OFF", description: "Agent stops after each turn (normal interactive mode)", color: "green" },
2089
+ { mode: "suggest", label: "SUGGEST", description: "Shows next-step suggestions after each turn", color: "cyan" },
2090
+ { mode: "auto", label: "AUTO", description: "Self-driving \u2014 agent picks next step and continues", color: "yellow" },
2091
+ { mode: "eternal", label: "ETERNAL", description: "Goal-driven loop \u2014 requires /goal set first", color: "red" },
2092
+ { mode: "eternal-parallel", label: "PARALLEL", description: "Fan-out 4\u20138 subagents per tick \u2014 requires /goal", color: "magenta" }
2093
+ ];
2094
+ function AutonomyPicker({ options, selected, hint }) {
2095
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
2096
+ /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "\u2501\u2501 Autonomy Mode \u2501\u2501" }),
2097
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191/\u2193 navigate \xB7 Enter select \xB7 Esc cancel \xB7 Ctrl+C exit" }),
2098
+ options.map((opt, i) => /* @__PURE__ */ jsxs(Text, { color: i === selected ? opt.color : void 0, inverse: i === selected, children: [
2099
+ i === selected ? "\u203A " : " ",
2100
+ /* @__PURE__ */ jsx(Text, { bold: true, children: opt.label.padEnd(12) }),
2101
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: opt.description })
2102
+ ] }, opt.mode)),
2103
+ hint ? /* @__PURE__ */ jsx(Text, { color: "yellow", children: hint }) : null
2104
+ ] });
2105
+ }
2106
+ function ModelPicker({
2107
+ step,
2108
+ providerOptions,
2109
+ modelOptions,
2110
+ selected,
2111
+ pickedProviderId,
2112
+ hint
2113
+ }) {
2114
+ if (step === "provider") {
2115
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
2116
+ /* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "\u2501\u2501 Switch model \u2014 Step 1/2: Pick provider \u2501\u2501" }),
2117
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191/\u2193 navigate \xB7 Enter select \xB7 Esc cancel \xB7 Ctrl+C exit" }),
2118
+ providerOptions.length === 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "(no providers with keys \u2014 add one via `wstack auth`)" }) : providerOptions.map((p, i) => /* @__PURE__ */ jsxs(Text, { color: i === selected ? "cyan" : void 0, inverse: i === selected, children: [
2119
+ i === selected ? "\u203A " : " ",
2120
+ /* @__PURE__ */ jsx(Text, { bold: true, children: p.id.padEnd(28) }),
2121
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
2122
+ " [",
2123
+ p.family,
2124
+ "]"
2125
+ ] }),
2126
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
2127
+ " ",
2128
+ p.models.length,
2129
+ " model",
2130
+ p.models.length === 1 ? "" : "s"
2131
+ ] })
2132
+ ] }, p.id)),
2133
+ hint ? /* @__PURE__ */ jsx(Text, { color: "yellow", children: hint }) : null
2134
+ ] });
1976
2135
  }
1977
- return `${pad2(m)}:${pad2(s)}`;
2136
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
2137
+ /* @__PURE__ */ jsxs(Text, { color: "cyan", bold: true, children: [
2138
+ "\u2501\u2501 Switch model \u2014 Step 2/2: Pick model (",
2139
+ pickedProviderId,
2140
+ ") \u2501\u2501"
2141
+ ] }),
2142
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2191/\u2193 navigate \xB7 Enter select \xB7 Esc back \xB7 Ctrl+C exit" }),
2143
+ modelOptions.length === 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "(no models known for this provider)" }) : modelOptions.map((id, i) => /* @__PURE__ */ jsxs(Text, { color: i === selected ? "cyan" : void 0, inverse: i === selected, children: [
2144
+ i === selected ? "\u203A " : " ",
2145
+ id
2146
+ ] }, id)),
2147
+ hint ? /* @__PURE__ */ jsx(Text, { color: "yellow", children: hint }) : null
2148
+ ] });
1978
2149
  }
1979
- function pad2(n) {
1980
- return n < 10 ? `0${n}` : String(n);
2150
+ function SlashMenu({ query, matches, selected }) {
2151
+ const placeholder = query ? `/${query}` : "/";
2152
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
2153
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
2154
+ placeholder || "/",
2155
+ " \u2014 \u2191/\u2193 select, Enter dispatch, Tab autocomplete, Esc close"
2156
+ ] }),
2157
+ matches.map((m, i) => /* @__PURE__ */ jsxs(Text, { color: i === selected ? "cyan" : void 0, inverse: i === selected, children: [
2158
+ i === selected ? "\u203A " : " ",
2159
+ /* @__PURE__ */ jsx(Text, { bold: true, children: m.name }),
2160
+ m.argsHint ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
2161
+ " ",
2162
+ m.argsHint
2163
+ ] }) : null,
2164
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
2165
+ " \u2014 ",
2166
+ m.description
2167
+ ] })
2168
+ ] }, m.name)),
2169
+ matches.length === 0 && /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No matching commands" })
2170
+ ] });
1981
2171
  }
1982
2172
  var IGNORED_DIRS = /* @__PURE__ */ new Set([
1983
2173
  "node_modules",
@@ -2057,6 +2247,19 @@ async function searchFiles(root, query, limit = 8) {
2057
2247
  scored.sort((a, b) => a.score - b.score);
2058
2248
  return scored.slice(0, limit).map((x) => x.path);
2059
2249
  }
2250
+
2251
+ // src/paste-accumulator.ts
2252
+ var BEGIN = "[200~";
2253
+ var END = "[201~";
2254
+ var BEGIN_RE = /\x1b?\[200~/g;
2255
+ var END_RE = /\x1b?\[201~/g;
2256
+ function feedPaste(accum, input) {
2257
+ if (accum === null && !input.includes(BEGIN)) return null;
2258
+ const piece = input.replace(BEGIN_RE, "").replace(END_RE, "");
2259
+ const next = (accum ?? "") + piece;
2260
+ if (input.includes(END)) return { accum: null, complete: next };
2261
+ return { accum: next, complete: null };
2262
+ }
2060
2263
  async function readGitInfo(cwd) {
2061
2264
  const [branchRes, numstatRes, statusRes] = await Promise.all([
2062
2265
  runGit(cwd, ["branch", "--show-current"]),
@@ -2732,12 +2935,36 @@ function reducer(state, action) {
2732
2935
  }
2733
2936
  };
2734
2937
  }
2938
+ case "fleetBudgetExtended": {
2939
+ const cur = state.fleet[action.id];
2940
+ if (!cur) return state;
2941
+ return {
2942
+ ...state,
2943
+ fleet: {
2944
+ ...state.fleet,
2945
+ [action.id]: {
2946
+ ...cur,
2947
+ // The director sends the authoritative cumulative count; trust it
2948
+ // over a local increment so a dropped event can't desync the badge.
2949
+ extensions: action.totalExtensions,
2950
+ lastEventAt: Date.now()
2951
+ }
2952
+ }
2953
+ };
2954
+ }
2735
2955
  case "fleetCost": {
2736
- return { ...state, fleetCost: action.cost };
2956
+ return {
2957
+ ...state,
2958
+ fleetCost: action.cost,
2959
+ fleetTokens: action.input !== void 0 || action.output !== void 0 ? { input: action.input ?? state.fleetTokens.input, output: action.output ?? state.fleetTokens.output } : state.fleetTokens
2960
+ };
2737
2961
  }
2738
2962
  case "setStreamFleet": {
2739
2963
  return { ...state, streamFleet: action.enabled };
2740
2964
  }
2965
+ case "toggleMonitor": {
2966
+ return { ...state, monitorOpen: !state.monitorOpen };
2967
+ }
2741
2968
  case "checkpointReceived": {
2742
2969
  const existing = state.checkpoints.find((c) => c.promptIndex === action.cp.promptIndex);
2743
2970
  if (existing) return state;
@@ -2859,7 +3086,7 @@ function App({
2859
3086
  }, [statuslineHiddenItems]);
2860
3087
  useEffect(() => {
2861
3088
  setStatuslineHiddenItems(hiddenItems);
2862
- }, [setStatuslineHiddenItems]);
3089
+ }, [setStatuslineHiddenItems, hiddenItems]);
2863
3090
  const projectRoot = agent.ctx.projectRoot;
2864
3091
  useEffect(() => {
2865
3092
  if (!projectRoot) return;
@@ -2925,7 +3152,9 @@ function App({
2925
3152
  contextChipVersion: 0,
2926
3153
  fleet: {},
2927
3154
  fleetCost: 0,
3155
+ fleetTokens: { input: 0, output: 0 },
2928
3156
  streamFleet: true,
3157
+ monitorOpen: false,
2929
3158
  checkpoints: [],
2930
3159
  rewindOverlay: null,
2931
3160
  eternalStage: null,
@@ -2935,7 +3164,10 @@ function App({
2935
3164
  if (builderRef.current === null) {
2936
3165
  builderRef.current = new InputBuilder({ store: attachments });
2937
3166
  }
3167
+ const pasteAccumRef = useRef(null);
3168
+ const pasteFlushTimerRef = useRef(null);
2938
3169
  const activeCtrlRef = useRef(null);
3170
+ const exitRequestedRef = useRef(false);
2939
3171
  const inputGateRef = useRef(false);
2940
3172
  const lastEnterAtRef = useRef(0);
2941
3173
  const projectName = React2.useMemo(() => {
@@ -3048,7 +3280,11 @@ function App({
3048
3280
  color: lbl.color,
3049
3281
  elapsedMs: Math.max(0, nowTick - e.startedAt),
3050
3282
  toolCalls: e.toolCalls,
3051
- running: e.status === "running"
3283
+ running: e.status === "running",
3284
+ // Last/current action, so the 4th line shows what each agent is
3285
+ // doing right now (e.g. "▶ 12s · 8t · bash") rather than just counts.
3286
+ tool: e.currentTool?.name,
3287
+ extensions: e.extensions
3052
3288
  };
3053
3289
  });
3054
3290
  }, [state.fleet, nowTick]);
@@ -3441,7 +3677,7 @@ function App({
3441
3677
  flushTimerRef.current = null;
3442
3678
  };
3443
3679
  const offDelta = events.on("provider.text_delta", (e) => {
3444
- const text = e.text.replace(/\x1b\[200~|\x1b\[201~/g, "");
3680
+ const text = e.text.replace(/\x1b?\[200~|\x1b?\[201~/g, "");
3445
3681
  streamingTextRef.current += text;
3446
3682
  pendingDeltaRef.current += text;
3447
3683
  if (!flushTimerRef.current) flushTimerRef.current = setTimeout(flush, FLUSH_MS);
@@ -3632,6 +3868,20 @@ function App({
3632
3868
  }
3633
3869
  });
3634
3870
  });
3871
+ const offBudgetExtended = events.on("subagent.budget_extended", (e) => {
3872
+ const lbl = labelFor(e.subagentId);
3873
+ dispatch({ type: "fleetBudgetExtended", id: e.subagentId, totalExtensions: e.totalExtensions });
3874
+ dispatch({
3875
+ type: "addEntry",
3876
+ entry: {
3877
+ kind: "subagent",
3878
+ agentLabel: lbl.label,
3879
+ agentColor: lbl.color,
3880
+ icon: "\u26A1",
3881
+ text: `extended ${e.kind} \u2192 ${e.newLimit} (\xD7${e.totalExtensions})`
3882
+ }
3883
+ });
3884
+ });
3635
3885
  const offIterationSummary = events.on("subagent.iteration_summary", (e) => {
3636
3886
  const lbl = labelFor(e.subagentId);
3637
3887
  const costStr = e.costUsd > 0 ? ` \xB7 ${e.costUsd.toFixed(3)}` : "";
@@ -3664,6 +3914,7 @@ function App({
3664
3914
  offStarted();
3665
3915
  offCompleted();
3666
3916
  offBudgetWarning();
3917
+ offBudgetExtended();
3667
3918
  offIterationSummary();
3668
3919
  offTool();
3669
3920
  };
@@ -3735,6 +3986,8 @@ function App({
3735
3986
  useEffect(() => {
3736
3987
  if (fleetStreamController) fleetStreamController.enabled = state.streamFleet;
3737
3988
  }, [state.streamFleet, fleetStreamController]);
3989
+ const lastEscAtRef = useRef(0);
3990
+ const ESC_DOUBLE_PRESS_MS = 1e3;
3738
3991
  useEffect(() => {
3739
3992
  const d = director;
3740
3993
  if (!d) return;
@@ -3775,7 +4028,7 @@ function App({
3775
4028
  });
3776
4029
  labelFor(s.id, meta?.name ?? s.name);
3777
4030
  }
3778
- dispatch({ type: "fleetCost", cost: d.snapshot().total.cost });
4031
+ dispatch({ type: "fleetCost", cost: d.snapshot().total.cost, input: d.snapshot().total.input, output: d.snapshot().total.output });
3779
4032
  const seen = new Set(Object.keys(status.subagents));
3780
4033
  const pending = /* @__PURE__ */ new Map();
3781
4034
  let flushTimer = null;
@@ -3885,7 +4138,7 @@ function App({
3885
4138
  break;
3886
4139
  }
3887
4140
  case "provider.response": {
3888
- dispatch({ type: "fleetCost", cost: d.snapshot().total.cost });
4141
+ dispatch({ type: "fleetCost", cost: d.snapshot().total.cost, input: d.snapshot().total.input, output: d.snapshot().total.output });
3889
4142
  break;
3890
4143
  }
3891
4144
  case "session.ended":
@@ -3929,7 +4182,7 @@ function App({
3929
4182
  iterations: payload.result.iterations,
3930
4183
  toolCalls: payload.result.toolCalls
3931
4184
  });
3932
- dispatch({ type: "fleetCost", cost: d.snapshot().total.cost });
4185
+ dispatch({ type: "fleetCost", cost: d.snapshot().total.cost, input: d.snapshot().total.input, output: d.snapshot().total.output });
3933
4186
  if (streamFlushTimer) {
3934
4187
  clearTimeout(streamFlushTimer);
3935
4188
  flushStreamBufs();
@@ -3949,14 +4202,16 @@ function App({
3949
4202
  const current = stateRef.current;
3950
4203
  if (current.interrupts >= 1) {
3951
4204
  getProcessRegistry().killAll({ force: true });
3952
- if (current.interrupts >= 2) {
3953
- process.exit(130);
3954
- }
3955
- try {
4205
+ if (exitRequestedRef.current) {
3956
4206
  process.exit(130);
3957
- } catch {
3958
4207
  }
4208
+ exitRequestedRef.current = true;
3959
4209
  dispatch({ type: "interrupt" });
4210
+ if (director) void director.terminateAll().catch(() => void 0);
4211
+ onExit(130);
4212
+ exit();
4213
+ const hardExit = setTimeout(() => process.exit(130), 400);
4214
+ hardExit.unref?.();
3960
4215
  return;
3961
4216
  }
3962
4217
  dispatch({ type: "interrupt" });
@@ -4008,6 +4263,37 @@ function App({
4008
4263
  });
4009
4264
  }
4010
4265
  } else {
4266
+ const fleetRunning = Object.values(current.fleet).filter(
4267
+ (e) => e.status === "running"
4268
+ ).length;
4269
+ const autonomyRunning = eternalLoopRunningRef.current || parallelLoopRunningRef.current || getEternalEngine?.()?.currentState === "running" || getParallelEngine?.()?.currentState === "running";
4270
+ if (autonomyRunning || fleetRunning > 0) {
4271
+ getEternalEngine?.()?.stop();
4272
+ getParallelEngine?.()?.stop();
4273
+ if (autonomyRunning) switchAutonomy?.("off");
4274
+ if (director) {
4275
+ const cap = new Promise((resolve) => {
4276
+ const t = setTimeout(resolve, 1500);
4277
+ t.unref?.();
4278
+ });
4279
+ void Promise.race([director.terminateAll().catch(() => void 0), cap]);
4280
+ }
4281
+ const killed2 = getProcessRegistry().killAll();
4282
+ const bits = [];
4283
+ if (autonomyRunning) bits.push("autonomy stopped");
4284
+ if (fleetRunning > 0)
4285
+ bits.push(`${fleetRunning} agent${fleetRunning === 1 ? "" : "s"} terminated`);
4286
+ if (killed2.length > 0)
4287
+ bits.push(`${killed2.length} process${killed2.length === 1 ? "" : "es"} killed`);
4288
+ dispatch({
4289
+ type: "addEntry",
4290
+ entry: {
4291
+ kind: "warn",
4292
+ text: `${bits.join(" + ") || "Background work stopped"}. Press Ctrl+C again to exit.`
4293
+ }
4294
+ });
4295
+ return;
4296
+ }
4011
4297
  const killed = getProcessRegistry().killAll();
4012
4298
  const procTag = killed.length > 0 ? ` Killed ${killed.length} process${killed.length === 1 ? "" : "es"}.` : "";
4013
4299
  dispatch({
@@ -4020,11 +4306,52 @@ function App({
4020
4306
  return () => {
4021
4307
  process.off("SIGINT", onSigint);
4022
4308
  };
4023
- }, [director]);
4309
+ }, [director, getEternalEngine, getParallelEngine, switchAutonomy, onExit, exit]);
4310
+ const commitPaste = async (full) => {
4311
+ const builder = builderRef.current;
4312
+ if (!builder || !full) return;
4313
+ if (builder.wouldCollapse(full) || full.includes("\n")) {
4314
+ const lineCount = full.split("\n").length;
4315
+ const ph = await builder.appendPaste(full);
4316
+ dispatch({ type: "addPlaceholder", ph: `${ph ?? "[pasted]"} (${lineCount} lines)` });
4317
+ return;
4318
+ }
4319
+ const { buffer, cursor } = draftRef.current;
4320
+ const next = buffer.slice(0, cursor) + full + buffer.slice(cursor);
4321
+ setDraft(next, cursor + full.length);
4322
+ };
4024
4323
  const handleKey = async (input, key) => {
4025
4324
  if (state.status === "aborting" && state.interrupts === 0) return;
4026
4325
  if (state.confirmQueue.length > 0) return;
4027
4326
  if (inputGateRef.current) return;
4327
+ if (key.escape) {
4328
+ const now = Date.now();
4329
+ if (state.buffer.length > 0 && now - lastEscAtRef.current < ESC_DOUBLE_PRESS_MS) {
4330
+ dispatch({ type: "clearInput" });
4331
+ lastEscAtRef.current = 0;
4332
+ return;
4333
+ }
4334
+ lastEscAtRef.current = now;
4335
+ }
4336
+ if (input) {
4337
+ const paste = feedPaste(pasteAccumRef.current, input);
4338
+ if (paste) {
4339
+ pasteAccumRef.current = paste.accum;
4340
+ if (pasteFlushTimerRef.current) clearTimeout(pasteFlushTimerRef.current);
4341
+ if (paste.complete !== null) {
4342
+ pasteFlushTimerRef.current = null;
4343
+ await commitPaste(paste.complete);
4344
+ return;
4345
+ }
4346
+ pasteFlushTimerRef.current = setTimeout(() => {
4347
+ pasteFlushTimerRef.current = null;
4348
+ const full = pasteAccumRef.current;
4349
+ pasteAccumRef.current = null;
4350
+ if (full) void commitPaste(full);
4351
+ }, 250);
4352
+ return;
4353
+ }
4354
+ }
4028
4355
  const isEnter = key.return || input === "\r" || input === "\n";
4029
4356
  if (state.modelPicker.open) {
4030
4357
  if (key.escape) {
@@ -4203,6 +4530,14 @@ function App({
4203
4530
  });
4204
4531
  return;
4205
4532
  }
4533
+ if (key.ctrl && input === "f") {
4534
+ dispatch({ type: "toggleMonitor" });
4535
+ return;
4536
+ }
4537
+ if (key.escape && state.monitorOpen) {
4538
+ dispatch({ type: "toggleMonitor" });
4539
+ return;
4540
+ }
4206
4541
  if (isEnter) {
4207
4542
  const now = Date.now();
4208
4543
  if (now - lastEnterAtRef.current < 50) return;
@@ -4320,40 +4655,23 @@ function App({
4320
4655
  return;
4321
4656
  }
4322
4657
  if (!input || key.ctrl || key.meta) return;
4323
- let bracketedPaste = false;
4324
- let cleanInput = input;
4325
- if (input.includes("\x1B[200~") || input.includes("\x1B[201~")) {
4326
- cleanInput = input.replace(/\x1b\[200~/g, "").replace(/\x1b\[201~/g, "");
4327
- bracketedPaste = true;
4328
- }
4329
- if (bracketedPaste || cleanInput.length > PASTE_THRESHOLD_CHARS) {
4330
- const builder = builderRef.current;
4331
- if (!builder) return;
4332
- const ph = await builder.appendPaste(cleanInput);
4333
- if (ph) {
4334
- const lineCount = cleanInput.split("\n").length;
4335
- dispatch({ type: "addPlaceholder", ph: `${ph} (${lineCount} lines)` });
4336
- } else if (cleanInput.includes("\n")) {
4337
- const lineCount = cleanInput.split("\n").length;
4338
- dispatch({ type: "addPlaceholder", ph: `[pasted] (${lineCount} lines)` });
4339
- } else {
4340
- const next2 = buffer.slice(0, cursor) + cleanInput + buffer.slice(cursor);
4341
- setDraft(next2, cursor + cleanInput.length);
4342
- }
4658
+ if (input.length > PASTE_THRESHOLD_CHARS) {
4659
+ await commitPaste(input);
4343
4660
  return;
4344
4661
  }
4345
- if (cleanInput.includes("\n")) {
4346
- const normalized = cleanInput.replace(/\r?\n/g, " ");
4662
+ if (input.includes("\n")) {
4663
+ const normalized = input.replace(/\r?\n/g, " ");
4347
4664
  const next2 = buffer.slice(0, cursor) + normalized + buffer.slice(cursor);
4348
4665
  setDraft(next2, cursor + normalized.length);
4349
4666
  return;
4350
4667
  }
4351
- const next = buffer.slice(0, cursor) + cleanInput + buffer.slice(cursor);
4352
- setDraft(next, cursor + cleanInput.length);
4668
+ const next = buffer.slice(0, cursor) + input + buffer.slice(cursor);
4669
+ setDraft(next, cursor + input.length);
4353
4670
  };
4354
4671
  const runBlocks = async (blocks) => {
4355
4672
  const ctrl = new AbortController();
4356
4673
  activeCtrlRef.current = ctrl;
4674
+ dispatch({ type: "resetInterrupts" });
4357
4675
  dispatch({ type: "status", status: "running" });
4358
4676
  try {
4359
4677
  const startedAt = Date.now();
@@ -4749,6 +5067,7 @@ User message:
4749
5067
  StatusBar,
4750
5068
  {
4751
5069
  model: `${liveProvider}/${liveModel}`,
5070
+ version: appVersion,
4752
5071
  state: state.status,
4753
5072
  tokenCounter,
4754
5073
  hint: renderRunningTools(state.runningTools) || state.hint,
@@ -4770,7 +5089,15 @@ User message:
4770
5089
  goalSummary: state.goalSummary
4771
5090
  }
4772
5091
  ),
4773
- director ? /* @__PURE__ */ jsx(FleetPanel, { entries: state.fleet, totalCost: state.fleetCost, roster: fleetRoster }) : null
5092
+ state.monitorOpen ? /* @__PURE__ */ jsx(
5093
+ FleetMonitor,
5094
+ {
5095
+ entries: state.fleet,
5096
+ totalCost: state.fleetCost,
5097
+ totalTokens: state.fleetTokens,
5098
+ nowTick
5099
+ }
5100
+ ) : director ? /* @__PURE__ */ jsx(FleetPanel, { entries: state.fleet, totalCost: state.fleetCost, roster: fleetRoster }) : null
4774
5101
  ] });
4775
5102
  }
4776
5103
  function renderRunningTools(running) {