@wrongstack/tui 0.6.7 → 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,1658 +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;
1145
- }
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 ")] : [];
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
+ ] });
1158
1201
  }
1159
- }
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 ")] : [];
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
+ ] });
1171
1242
  }
1172
1243
  }
1173
- if (toolName === "install") {
1174
- if (json && typeof json === "object") {
1175
- 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;
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) : "";
1189
1399
  }
1190
1400
  }
1191
- if (toolName === "scaffold") {
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") {
1192
1417
  if (json && typeof json === "object") {
1193
1418
  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 ")];
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];
1200
1424
  }
1201
1425
  }
1202
- if (toolName === "remember" || toolName === "forget" || toolName === "memory") {
1203
- return ok ? [toolName === "forget" ? "removed" : "saved"] : [text.split("\n")[0] ?? ""];
1204
- }
1205
- if (toolName === "mode") {
1426
+ if (toolName === "edit") {
1206
1427
  if (json && typeof json === "object") {
1207
1428
  const o = json;
1208
- const mode = stringOf(o["mode"]) ?? stringOf(o["active"]) ?? stringOf(o["name"]);
1209
- if (mode) return [`mode: ${mode}`];
1429
+ const reps = numOf(o["replacements"]);
1430
+ if (reps !== void 0) return [`${reps} replacement${reps === 1 ? "" : "s"}`];
1210
1431
  }
1211
1432
  }
1212
- if (toolName === "search") {
1433
+ if (toolName === "patch") {
1213
1434
  if (json && typeof json === "object") {
1214
1435
  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;
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}`);
1223
1450
  }
1451
+ if (lines.length > 0) return lines;
1224
1452
  }
1225
1453
  }
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];
1454
+ if (toolName === "replace") {
1455
+ if (json && typeof json === "object") {
1456
+ const o = json;
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
+ ];
1463
+ }
1464
+ }
1236
1465
  }
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
- }) {
1659
- const hiddenSet = new Set(hiddenItems);
1660
- const usage = tokenCounter?.total();
1661
- const cost = tokenCounter?.estimateCost();
1662
- const cache2 = tokenCounter?.cacheStats();
1663
- const stateColor = state === "idle" ? "cyan" : state === "aborting" ? "yellow" : "green";
1664
- const stateLabel = state === "idle" ? "idle" : state === "aborting" ? "aborting\u2026" : "thinking\u2026";
1665
- const hasSecondLine = yolo || autonomy && autonomy !== "off" || elapsedMs !== void 0 || git !== null && git !== void 0 || projectName !== void 0 && projectName.length > 0;
1666
- const fleetHasActivity = fleet && (fleet.running > 0 || fleet.idle > 0 || fleet.pending > 0 || fleet.completed > 0) || subagentCount > 0;
1667
- const hasThirdLine = todos && (todos.pending > 0 || todos.inProgress > 0 || todos.completed > 0) || plan && (plan.open > 0 || plan.inProgress > 0 || plan.done > 0) || fleetHasActivity;
1668
- return /* @__PURE__ */ jsxs(
1669
- Box,
1670
- {
1671
- flexDirection: "column",
1672
- paddingX: 1,
1673
- borderStyle: "single",
1674
- borderTop: true,
1675
- borderBottom: false,
1676
- borderLeft: false,
1677
- borderRight: false,
1678
- children: [
1679
- /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 2, children: [
1680
- /* @__PURE__ */ jsxs(Text, { color: stateColor, children: [
1681
- "\u25CF ",
1682
- stateLabel
1683
- ] }),
1684
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
1685
- /* @__PURE__ */ jsx(Text, { color: "magenta", children: model }),
1686
- context && context.max > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
1687
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
1688
- /* @__PURE__ */ jsx(ContextChip, { ctx: context })
1689
- ] }) : null,
1690
- usage ? /* @__PURE__ */ jsxs(Fragment, { children: [
1691
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
1692
- /* @__PURE__ */ jsxs(Text, { children: [
1693
- "\u2191",
1694
- " ",
1695
- /* @__PURE__ */ jsx(Text, { color: "cyan", children: fmtTok2(usage.input + (usage.cacheRead ?? 0) + (usage.cacheWrite ?? 0)) }),
1696
- " ",
1697
- "\u2193 ",
1698
- /* @__PURE__ */ jsx(Text, { color: "cyan", children: fmtTok2(usage.output) })
1699
- ] })
1700
- ] }) : null,
1701
- cache2 && cache2.hitRatio > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
1702
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
1703
- /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1704
- "cache ",
1705
- (cache2.hitRatio * 100).toFixed(0),
1706
- "%"
1707
- ] })
1708
- ] }) : null,
1709
- cost && cost.total > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
1710
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
1711
- /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
1712
- "$",
1713
- cost.total.toFixed(4)
1714
- ] })
1715
- ] }) : null,
1716
- queueCount > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
1717
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
1718
- /* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
1719
- "\u231B queued: ",
1720
- queueCount
1721
- ] })
1722
- ] }) : null,
1723
- typeof processCount === "number" && processCount > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
1724
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
1725
- /* @__PURE__ */ jsxs(Text, { color: "red", children: [
1726
- "\u26A1 ",
1727
- processCount,
1728
- " process",
1729
- processCount === 1 ? "" : "es"
1730
- ] })
1731
- ] }) : null,
1732
- hint ? /* @__PURE__ */ jsxs(Fragment, { children: [
1733
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
1734
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: hint })
1735
- ] }) : null
1736
- ] }),
1737
- hasSecondLine ? /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 2, children: [
1738
- yolo ? /* @__PURE__ */ jsx(Text, { color: "red", bold: true, children: "\u26A0 YOLO" }) : null,
1739
- autonomy && autonomy !== "off" ? /* @__PURE__ */ jsxs(Fragment, { children: [
1740
- yolo ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
1741
- /* @__PURE__ */ jsxs(
1742
- Text,
1743
- {
1744
- color: autonomy === "eternal" ? "red" : autonomy === "auto" ? "yellow" : "cyan",
1745
- bold: true,
1746
- children: [
1747
- "\u221E ",
1748
- autonomy.toUpperCase()
1749
- ]
1750
- }
1751
- )
1752
- ] }) : null,
1753
- eternalStage ? /* @__PURE__ */ jsxs(Fragment, { children: [
1754
- yolo || autonomy && autonomy !== "off" ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
1755
- /* @__PURE__ */ jsx(EternalStageChip, { stage: eternalStage })
1756
- ] }) : null,
1757
- elapsedMs !== void 0 && !hiddenSet.has("elapsed") ? /* @__PURE__ */ jsxs(Fragment, { children: [
1758
- yolo || autonomy && autonomy !== "off" || eternalStage ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
1759
- /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1760
- "\u23F1 ",
1761
- fmtElapsed2(elapsedMs)
1762
- ] })
1763
- ] }) : null,
1764
- projectName ? /* @__PURE__ */ jsxs(Fragment, { children: [
1765
- yolo || elapsedMs !== void 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
1766
- /* @__PURE__ */ jsxs(Text, { color: "blue", children: [
1767
- "\u{1F4C1} ",
1768
- projectName
1769
- ] })
1770
- ] }) : null,
1771
- git ? /* @__PURE__ */ jsxs(Fragment, { children: [
1772
- yolo || elapsedMs !== void 0 || projectName ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
1773
- /* @__PURE__ */ jsxs(Text, { children: [
1774
- /* @__PURE__ */ jsxs(Text, { color: "magenta", children: [
1775
- "\u2387 ",
1776
- git.branch
1777
- ] }),
1778
- git.added > 0 ? /* @__PURE__ */ jsxs(Text, { color: "green", children: [
1779
- " +",
1780
- git.added
1781
- ] }) : null,
1782
- git.deleted > 0 ? /* @__PURE__ */ jsxs(Text, { color: "red", children: [
1783
- " -",
1784
- git.deleted
1785
- ] }) : null,
1786
- git.untracked > 0 ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1787
- " ?",
1788
- git.untracked
1789
- ] }) : null
1790
- ] })
1791
- ] }) : null
1792
- ] }) : null,
1793
- hasThirdLine ? /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 2, children: [
1794
- todos && (todos.pending > 0 || todos.inProgress > 0 || todos.completed > 0) ? /* @__PURE__ */ jsxs(Text, { children: [
1795
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "todos " }),
1796
- todos.inProgress > 0 ? /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
1797
- "\u231B",
1798
- todos.inProgress
1799
- ] }) : null,
1800
- todos.inProgress > 0 && (todos.pending > 0 || todos.completed > 0) ? " " : "",
1801
- todos.pending > 0 ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1802
- "\u2610",
1803
- todos.pending
1804
- ] }) : null,
1805
- todos.pending > 0 && todos.completed > 0 ? " " : "",
1806
- todos.completed > 0 ? /* @__PURE__ */ jsxs(Text, { color: "green", children: [
1807
- "\u2713",
1808
- todos.completed
1809
- ] }) : null
1810
- ] }) : null,
1811
- plan && (plan.open > 0 || plan.inProgress > 0 || plan.done > 0) ? /* @__PURE__ */ jsxs(Fragment, { children: [
1812
- todos && (todos.pending > 0 || todos.inProgress > 0 || todos.completed > 0) ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }) : null,
1813
- /* @__PURE__ */ jsxs(Text, { children: [
1814
- /* @__PURE__ */ jsx(Text, { color: "cyan", children: "\u{1F4CB} " }),
1815
- plan.inProgress > 0 ? /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
1816
- "\u231B",
1817
- plan.inProgress
1818
- ] }) : null,
1819
- plan.inProgress > 0 && (plan.open > 0 || plan.done > 0) ? " " : "",
1820
- plan.open > 0 ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1821
- "\u2610",
1822
- plan.open
1823
- ] }) : null,
1824
- plan.open > 0 && plan.done > 0 ? " " : "",
1825
- plan.done > 0 ? /* @__PURE__ */ jsxs(Text, { color: "green", children: [
1826
- "\u2713",
1827
- plan.done
1828
- ] }) : null
1829
- ] })
1830
- ] }) : null,
1831
- fleetHasActivity ? /* @__PURE__ */ jsxs(Fragment, { children: [
1832
- 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,
1833
- fleet ? /* @__PURE__ */ jsxs(Text, { children: [
1834
- /* @__PURE__ */ jsx(Text, { color: "blue", children: "\u{1F310} " }),
1835
- fleet.running > 0 ? /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
1836
- "\u25B6",
1837
- fleet.running
1838
- ] }) : null,
1839
- fleet.running > 0 && (fleet.pending > 0 || fleet.idle > 0 || fleet.completed > 0) ? " " : "",
1840
- fleet.pending > 0 ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1841
- "\u2610",
1842
- fleet.pending
1843
- ] }) : null,
1844
- fleet.pending > 0 && (fleet.idle > 0 || fleet.completed > 0) ? " " : "",
1845
- fleet.idle > 0 ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1846
- "\xB7",
1847
- fleet.idle,
1848
- "idle"
1849
- ] }) : null,
1850
- fleet.idle > 0 && fleet.completed > 0 ? " " : "",
1851
- fleet.completed > 0 ? /* @__PURE__ */ jsxs(Text, { color: "green", children: [
1852
- "\u2713",
1853
- fleet.completed
1854
- ] }) : null
1855
- ] }) : /* @__PURE__ */ jsxs(Text, { color: "blue", children: [
1856
- "\u{1F310} ",
1857
- subagentCount,
1858
- " agent",
1859
- subagentCount === 1 ? "" : "s"
1860
- ] })
1861
- ] }) : null
1862
- ] }) : null,
1863
- fleetAgents && fleetAgents.length > 0 ? /* @__PURE__ */ jsx(Box, { flexDirection: "row", gap: 2, children: fleetAgents.map((a, i) => (
1864
- // biome-ignore lint/suspicious/noArrayIndexKey: agent list is stable per render
1865
- /* @__PURE__ */ jsxs(Text, { children: [
1866
- /* @__PURE__ */ jsx(Text, { color: a.color, bold: true, children: a.label }),
1867
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: " " }),
1868
- /* @__PURE__ */ jsx(Text, { color: a.running ? "yellow" : void 0, dimColor: !a.running, children: a.running ? "\u25B6" : "\xB7" }),
1869
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: " " }),
1870
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: fmtElapsed2(a.elapsedMs) }),
1871
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: " \xB7 " }),
1872
- /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1873
- a.toolCalls,
1874
- "t"
1875
- ] })
1876
- ] }, i)
1877
- )) }) : null
1878
- ]
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;
1879
1807
  }
1880
- );
1808
+ parts.push(rendered);
1809
+ used += rendered.length + 3;
1810
+ }
1811
+ return parts.length > 0 ? parts.join(" \xB7 ") : null;
1881
1812
  }
1882
- function EternalStageChip({
1883
- stage
1884
- }) {
1885
- switch (stage.phase) {
1886
- case "idle":
1887
- return /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2B1C idle" });
1888
- case "decide":
1889
- return /* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
1890
- "\u2B07 decide: ",
1891
- stage.reason
1892
- ] });
1893
- case "execute":
1894
- return /* @__PURE__ */ jsxs(Text, { color: "green", children: [
1895
- "\u25B6 ",
1896
- /* @__PURE__ */ jsx(Text, { bold: true, children: "execute" }),
1897
- stage.task ? `(${stage.task})` : ""
1898
- ] });
1899
- case "reflect":
1900
- return /* @__PURE__ */ jsxs(Text, { color: stage.status === "success" ? "green" : stage.status === "failure" ? "red" : "yellow", children: [
1901
- "\u21A9 reflect: ",
1902
- stage.status
1903
- ] });
1904
- case "sleep":
1905
- return /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1906
- "\u{1F4A4} sleep ",
1907
- Math.round(stage.ms / 1e3),
1908
- "s"
1909
- ] });
1910
- case "paused":
1911
- return /* @__PURE__ */ jsx(Text, { color: "yellow", children: "\u23F8 paused" });
1912
- case "stopped":
1913
- return /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u25A0 stopped" });
1914
- case "error":
1915
- return /* @__PURE__ */ jsxs(Text, { color: "red", children: [
1916
- "\u26A0 error: ",
1917
- stage.message
1918
- ] });
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);
1919
1830
  }
1831
+ return void 0;
1920
1832
  }
1921
- function ContextChip({ ctx }) {
1922
- const ratio = Math.max(0, Math.min(1, ctx.used / ctx.max));
1923
- const pct = Math.round(ratio * 100);
1924
- const color = ratio >= 0.85 ? "red" : ratio >= 0.65 ? "yellow" : "cyan";
1925
- return /* @__PURE__ */ jsxs(Text, { children: [
1926
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: "ctx " }),
1927
- /* @__PURE__ */ jsx(Text, { color, children: renderProgress(ratio, 10) }),
1928
- /* @__PURE__ */ jsxs(Text, { color, children: [
1929
- " ",
1930
- pct,
1931
- "%"
1932
- ] }),
1933
- /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1934
- " ",
1935
- "(",
1936
- fmtTok2(ctx.used),
1937
- "/",
1938
- fmtTok2(ctx.max),
1939
- ")"
1940
- ] })
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
2008
+ ] }),
2009
+ hint ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: hint }) : null
1941
2010
  ] });
1942
2011
  }
1943
- var FILLED = "\u2588";
1944
- var EMPTY = "\u2591";
1945
- function renderProgress(ratio, width) {
1946
- const clamped = Math.max(0, Math.min(1, ratio));
1947
- const filled = clamped === 0 ? 0 : Math.max(1, Math.round(clamped * width));
1948
- const capped = Math.min(width, filled);
1949
- 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`;
1950
2018
  }
1951
- function fmtTok2(n) {
1952
- if (n < 1e3) return String(n);
1953
- if (n < 1e6) return `${(n / 1e3).toFixed(n < 1e4 ? 1 : 0)}k`;
1954
- return `${(n / 1e6).toFixed(1)}M`;
2019
+ function fmtBytes3(n) {
2020
+ if (n < 1024) return `${n}B`;
2021
+ return `${(n / 1024).toFixed(1)}KB`;
1955
2022
  }
1956
- function fmtElapsed2(ms) {
1957
- const totalSec = Math.floor(ms / 1e3);
1958
- const h = Math.floor(totalSec / 3600);
1959
- const m = Math.floor(totalSec % 3600 / 60);
1960
- const s = totalSec % 60;
1961
- if (h > 0) {
1962
- 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
+ ] });
1963
2135
  }
1964
- 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
+ ] });
1965
2149
  }
1966
- function pad2(n) {
1967
- 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
+ ] });
1968
2171
  }
1969
2172
  var IGNORED_DIRS = /* @__PURE__ */ new Set([
1970
2173
  "node_modules",
@@ -2044,6 +2247,19 @@ async function searchFiles(root, query, limit = 8) {
2044
2247
  scored.sort((a, b) => a.score - b.score);
2045
2248
  return scored.slice(0, limit).map((x) => x.path);
2046
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
+ }
2047
2263
  async function readGitInfo(cwd) {
2048
2264
  const [branchRes, numstatRes, statusRes] = await Promise.all([
2049
2265
  runGit(cwd, ["branch", "--show-current"]),
@@ -2719,12 +2935,36 @@ function reducer(state, action) {
2719
2935
  }
2720
2936
  };
2721
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
+ }
2722
2955
  case "fleetCost": {
2723
- 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
+ };
2724
2961
  }
2725
2962
  case "setStreamFleet": {
2726
2963
  return { ...state, streamFleet: action.enabled };
2727
2964
  }
2965
+ case "toggleMonitor": {
2966
+ return { ...state, monitorOpen: !state.monitorOpen };
2967
+ }
2728
2968
  case "checkpointReceived": {
2729
2969
  const existing = state.checkpoints.find((c) => c.promptIndex === action.cp.promptIndex);
2730
2970
  if (existing) return state;
@@ -2756,6 +2996,9 @@ function reducer(state, action) {
2756
2996
  case "eternalStage": {
2757
2997
  return { ...state, eternalStage: action.stage };
2758
2998
  }
2999
+ case "goalSummary": {
3000
+ return { ...state, goalSummary: action.summary };
3001
+ }
2759
3002
  }
2760
3003
  }
2761
3004
  var PASTE_THRESHOLD_CHARS = 200;
@@ -2843,7 +3086,29 @@ function App({
2843
3086
  }, [statuslineHiddenItems]);
2844
3087
  useEffect(() => {
2845
3088
  setStatuslineHiddenItems(hiddenItems);
2846
- }, [setStatuslineHiddenItems]);
3089
+ }, [setStatuslineHiddenItems, hiddenItems]);
3090
+ const projectRoot = agent.ctx.projectRoot;
3091
+ useEffect(() => {
3092
+ if (!projectRoot) return;
3093
+ const goalPath = path2.join(projectRoot, ".wrongstack", "goal.json");
3094
+ fs2.readFile(goalPath, "utf8").then((raw) => {
3095
+ const goal = JSON.parse(raw);
3096
+ if (goal?.goal && typeof goal.iterations === "number") {
3097
+ const lastEntry = goal.journal?.[goal.journal.length - 1];
3098
+ dispatch({
3099
+ type: "goalSummary",
3100
+ summary: {
3101
+ goal: goal.goal,
3102
+ goalState: goal.goalState ?? "active",
3103
+ iterations: goal.iterations,
3104
+ lastTask: lastEntry?.task,
3105
+ lastStatus: lastEntry?.status
3106
+ }
3107
+ });
3108
+ }
3109
+ }).catch(() => {
3110
+ });
3111
+ }, [projectRoot]);
2847
3112
  const [state, dispatch] = useReducer(reducer, {
2848
3113
  entries: banner ? [
2849
3114
  {
@@ -2887,19 +3152,24 @@ function App({
2887
3152
  contextChipVersion: 0,
2888
3153
  fleet: {},
2889
3154
  fleetCost: 0,
3155
+ fleetTokens: { input: 0, output: 0 },
2890
3156
  streamFleet: true,
3157
+ monitorOpen: false,
2891
3158
  checkpoints: [],
2892
3159
  rewindOverlay: null,
2893
- eternalStage: null
3160
+ eternalStage: null,
3161
+ goalSummary: null
2894
3162
  });
2895
3163
  const builderRef = useRef(null);
2896
3164
  if (builderRef.current === null) {
2897
3165
  builderRef.current = new InputBuilder({ store: attachments });
2898
3166
  }
3167
+ const pasteAccumRef = useRef(null);
3168
+ const pasteFlushTimerRef = useRef(null);
2899
3169
  const activeCtrlRef = useRef(null);
3170
+ const exitRequestedRef = useRef(false);
2900
3171
  const inputGateRef = useRef(false);
2901
3172
  const lastEnterAtRef = useRef(0);
2902
- const projectRoot = agent.ctx.projectRoot;
2903
3173
  const projectName = React2.useMemo(() => {
2904
3174
  const base = path2.basename(projectRoot);
2905
3175
  return base && base !== path2.sep ? base : void 0;
@@ -3010,7 +3280,11 @@ function App({
3010
3280
  color: lbl.color,
3011
3281
  elapsedMs: Math.max(0, nowTick - e.startedAt),
3012
3282
  toolCalls: e.toolCalls,
3013
- 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
3014
3288
  };
3015
3289
  });
3016
3290
  }, [state.fleet, nowTick]);
@@ -3403,7 +3677,7 @@ function App({
3403
3677
  flushTimerRef.current = null;
3404
3678
  };
3405
3679
  const offDelta = events.on("provider.text_delta", (e) => {
3406
- const text = e.text.replace(/\x1b\[200~|\x1b\[201~/g, "");
3680
+ const text = e.text.replace(/\x1b?\[200~|\x1b?\[201~/g, "");
3407
3681
  streamingTextRef.current += text;
3408
3682
  pendingDeltaRef.current += text;
3409
3683
  if (!flushTimerRef.current) flushTimerRef.current = setTimeout(flush, FLUSH_MS);
@@ -3594,6 +3868,20 @@ function App({
3594
3868
  }
3595
3869
  });
3596
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
+ });
3597
3885
  const offIterationSummary = events.on("subagent.iteration_summary", (e) => {
3598
3886
  const lbl = labelFor(e.subagentId);
3599
3887
  const costStr = e.costUsd > 0 ? ` \xB7 ${e.costUsd.toFixed(3)}` : "";
@@ -3626,6 +3914,7 @@ function App({
3626
3914
  offStarted();
3627
3915
  offCompleted();
3628
3916
  offBudgetWarning();
3917
+ offBudgetExtended();
3629
3918
  offIterationSummary();
3630
3919
  offTool();
3631
3920
  };
@@ -3697,6 +3986,8 @@ function App({
3697
3986
  useEffect(() => {
3698
3987
  if (fleetStreamController) fleetStreamController.enabled = state.streamFleet;
3699
3988
  }, [state.streamFleet, fleetStreamController]);
3989
+ const lastEscAtRef = useRef(0);
3990
+ const ESC_DOUBLE_PRESS_MS = 1e3;
3700
3991
  useEffect(() => {
3701
3992
  const d = director;
3702
3993
  if (!d) return;
@@ -3737,7 +4028,7 @@ function App({
3737
4028
  });
3738
4029
  labelFor(s.id, meta?.name ?? s.name);
3739
4030
  }
3740
- 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 });
3741
4032
  const seen = new Set(Object.keys(status.subagents));
3742
4033
  const pending = /* @__PURE__ */ new Map();
3743
4034
  let flushTimer = null;
@@ -3847,7 +4138,7 @@ function App({
3847
4138
  break;
3848
4139
  }
3849
4140
  case "provider.response": {
3850
- 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 });
3851
4142
  break;
3852
4143
  }
3853
4144
  case "session.ended":
@@ -3891,7 +4182,7 @@ function App({
3891
4182
  iterations: payload.result.iterations,
3892
4183
  toolCalls: payload.result.toolCalls
3893
4184
  });
3894
- 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 });
3895
4186
  if (streamFlushTimer) {
3896
4187
  clearTimeout(streamFlushTimer);
3897
4188
  flushStreamBufs();
@@ -3911,14 +4202,16 @@ function App({
3911
4202
  const current = stateRef.current;
3912
4203
  if (current.interrupts >= 1) {
3913
4204
  getProcessRegistry().killAll({ force: true });
3914
- if (current.interrupts >= 2) {
3915
- process.exit(130);
3916
- }
3917
- try {
4205
+ if (exitRequestedRef.current) {
3918
4206
  process.exit(130);
3919
- } catch {
3920
4207
  }
4208
+ exitRequestedRef.current = true;
3921
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?.();
3922
4215
  return;
3923
4216
  }
3924
4217
  dispatch({ type: "interrupt" });
@@ -3970,6 +4263,37 @@ function App({
3970
4263
  });
3971
4264
  }
3972
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
+ }
3973
4297
  const killed = getProcessRegistry().killAll();
3974
4298
  const procTag = killed.length > 0 ? ` Killed ${killed.length} process${killed.length === 1 ? "" : "es"}.` : "";
3975
4299
  dispatch({
@@ -3982,11 +4306,52 @@ function App({
3982
4306
  return () => {
3983
4307
  process.off("SIGINT", onSigint);
3984
4308
  };
3985
- }, [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
+ };
3986
4323
  const handleKey = async (input, key) => {
3987
4324
  if (state.status === "aborting" && state.interrupts === 0) return;
3988
4325
  if (state.confirmQueue.length > 0) return;
3989
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
+ }
3990
4355
  const isEnter = key.return || input === "\r" || input === "\n";
3991
4356
  if (state.modelPicker.open) {
3992
4357
  if (key.escape) {
@@ -4165,6 +4530,14 @@ function App({
4165
4530
  });
4166
4531
  return;
4167
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
+ }
4168
4541
  if (isEnter) {
4169
4542
  const now = Date.now();
4170
4543
  if (now - lastEnterAtRef.current < 50) return;
@@ -4282,40 +4655,23 @@ function App({
4282
4655
  return;
4283
4656
  }
4284
4657
  if (!input || key.ctrl || key.meta) return;
4285
- let bracketedPaste = false;
4286
- let cleanInput = input;
4287
- if (input.includes("\x1B[200~") || input.includes("\x1B[201~")) {
4288
- cleanInput = input.replace(/\x1b\[200~/g, "").replace(/\x1b\[201~/g, "");
4289
- bracketedPaste = true;
4290
- }
4291
- if (bracketedPaste || cleanInput.length > PASTE_THRESHOLD_CHARS) {
4292
- const builder = builderRef.current;
4293
- if (!builder) return;
4294
- const ph = await builder.appendPaste(cleanInput);
4295
- if (ph) {
4296
- const lineCount = cleanInput.split("\n").length;
4297
- dispatch({ type: "addPlaceholder", ph: `${ph} (${lineCount} lines)` });
4298
- } else if (cleanInput.includes("\n")) {
4299
- const lineCount = cleanInput.split("\n").length;
4300
- dispatch({ type: "addPlaceholder", ph: `[pasted] (${lineCount} lines)` });
4301
- } else {
4302
- const next2 = buffer.slice(0, cursor) + cleanInput + buffer.slice(cursor);
4303
- setDraft(next2, cursor + cleanInput.length);
4304
- }
4658
+ if (input.length > PASTE_THRESHOLD_CHARS) {
4659
+ await commitPaste(input);
4305
4660
  return;
4306
4661
  }
4307
- if (cleanInput.includes("\n")) {
4308
- const normalized = cleanInput.replace(/\r?\n/g, " ");
4662
+ if (input.includes("\n")) {
4663
+ const normalized = input.replace(/\r?\n/g, " ");
4309
4664
  const next2 = buffer.slice(0, cursor) + normalized + buffer.slice(cursor);
4310
4665
  setDraft(next2, cursor + normalized.length);
4311
4666
  return;
4312
4667
  }
4313
- const next = buffer.slice(0, cursor) + cleanInput + buffer.slice(cursor);
4314
- setDraft(next, cursor + cleanInput.length);
4668
+ const next = buffer.slice(0, cursor) + input + buffer.slice(cursor);
4669
+ setDraft(next, cursor + input.length);
4315
4670
  };
4316
4671
  const runBlocks = async (blocks) => {
4317
4672
  const ctrl = new AbortController();
4318
4673
  activeCtrlRef.current = ctrl;
4674
+ dispatch({ type: "resetInterrupts" });
4319
4675
  dispatch({ type: "status", status: "running" });
4320
4676
  try {
4321
4677
  const startedAt = Date.now();
@@ -4711,6 +5067,7 @@ User message:
4711
5067
  StatusBar,
4712
5068
  {
4713
5069
  model: `${liveProvider}/${liveModel}`,
5070
+ version: appVersion,
4714
5071
  state: state.status,
4715
5072
  tokenCounter,
4716
5073
  hint: renderRunningTools(state.runningTools) || state.hint,
@@ -4728,10 +5085,19 @@ User message:
4728
5085
  subagentCount: Object.keys(state.fleet).length,
4729
5086
  processCount: getProcessRegistry().activeCount,
4730
5087
  hiddenItems,
4731
- eternalStage: state.eternalStage
5088
+ eternalStage: state.eternalStage,
5089
+ goalSummary: state.goalSummary
4732
5090
  }
4733
5091
  ),
4734
- 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
4735
5101
  ] });
4736
5102
  }
4737
5103
  function renderRunningTools(running) {
@@ -4879,7 +5245,8 @@ async function runTui(opts) {
4879
5245
  initialAsk: opts.initialAsk,
4880
5246
  getSDDContext: opts.getSDDContext,
4881
5247
  onSDDOutput: opts.onSDDOutput,
4882
- sessionsDir: opts.sessionsDir
5248
+ sessionsDir: opts.sessionsDir,
5249
+ projectRoot: opts.projectRoot
4883
5250
  }),
4884
5251
  { exitOnCtrlC: false }
4885
5252
  );