reasonix 0.7.1 → 0.7.4

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/cli/index.js CHANGED
@@ -715,13 +715,13 @@ async function runHooks(opts) {
715
715
  const matching = opts.hooks.filter((h) => h.event === event && matchesTool(h, toolName));
716
716
  const outcomes = [];
717
717
  let blocked = false;
718
- const stdin2 = `${JSON.stringify(opts.payload)}
718
+ const stdin3 = `${JSON.stringify(opts.payload)}
719
719
  `;
720
720
  for (const hook of matching) {
721
721
  const start = Date.now();
722
722
  const timeoutMs = hook.timeout ?? DEFAULT_TIMEOUTS_MS[event];
723
723
  const cwd = hook.cwd ?? opts.payload.cwd;
724
- const raw = await spawner({ command: hook.command, cwd, stdin: stdin2, timeoutMs });
724
+ const raw = await spawner({ command: hook.command, cwd, stdin: stdin3, timeoutMs });
725
725
  const decision = decideOutcome(event, raw);
726
726
  outcomes.push({
727
727
  hook,
@@ -7113,11 +7113,11 @@ function formatLogSize(path = defaultUsageLogPath()) {
7113
7113
  // src/cli/commands/chat.tsx
7114
7114
  import { existsSync as existsSync12, statSync as statSync7 } from "fs";
7115
7115
  import { render } from "ink";
7116
- import React23, { useState as useState12 } from "react";
7116
+ import React25, { useState as useState12 } from "react";
7117
7117
 
7118
7118
  // src/cli/ui/App.tsx
7119
- import { Box as Box19, Static, useApp, useInput as useInput5, useStdout as useStdout4 } from "ink";
7120
- import React20, { useCallback as useCallback4, useEffect as useEffect5, useMemo as useMemo3, useRef as useRef5, useState as useState10 } from "react";
7119
+ import { Box as Box20, Static, useApp, useStdout as useStdout5 } from "ink";
7120
+ import React22, { useCallback as useCallback4, useEffect as useEffect6, useMemo as useMemo3, useRef as useRef6, useState as useState10 } from "react";
7121
7121
 
7122
7122
  // src/code/pending-edits.ts
7123
7123
  import { existsSync as existsSync9, mkdirSync as mkdirSync6, readFileSync as readFileSync11, unlinkSync as unlinkSync3, writeFileSync as writeFileSync5 } from "fs";
@@ -7419,7 +7419,7 @@ function AtMentionSuggestions({
7419
7419
  }) {
7420
7420
  if (matches === null) return null;
7421
7421
  if (matches.length === 0) {
7422
- return /* @__PURE__ */ React.createElement(Box, { paddingX: 1 }, /* @__PURE__ */ React.createElement(Text, { color: "yellow" }, 'no files match "@', query, '"'), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " ", "\u2014 keep typing, or Backspace to edit. Paths resolve from the code root."));
7422
+ return /* @__PURE__ */ React.createElement(Box, { paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { color: "yellow" }, 'no files match "@', query, '"'), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " ", "\u2014 keep typing, or Backspace to edit. Paths resolve from the code root."));
7423
7423
  }
7424
7424
  const MAX = 8;
7425
7425
  const total = matches.length;
@@ -7427,7 +7427,7 @@ function AtMentionSuggestions({
7427
7427
  const shown = matches.slice(windowStart, windowStart + MAX);
7428
7428
  const hiddenAbove = windowStart;
7429
7429
  const hiddenBelow = total - windowStart - shown.length;
7430
- return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", paddingX: 1 }, hiddenAbove > 0 ? /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " \u2191 ", hiddenAbove, " more above") : null, shown.map((path, i) => /* @__PURE__ */ React.createElement(FileRow, { key: path, path, isSelected: windowStart + i === selectedIndex })), hiddenBelow > 0 ? /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " \u2193 ", hiddenBelow, " more below") : null, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " [\u2191\u2193] navigate \xB7 [Tab]/[Enter] pick \xB7 file content inlined on send"));
7430
+ return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", paddingX: 1, marginTop: 1 }, hiddenAbove > 0 ? /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " \u2191 ", hiddenAbove, " more above") : null, shown.map((path, i) => /* @__PURE__ */ React.createElement(FileRow, { key: path, path, isSelected: windowStart + i === selectedIndex })), hiddenBelow > 0 ? /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " \u2193 ", hiddenBelow, " more below") : null, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " [\u2191\u2193] navigate \xB7 [Tab]/[Enter] pick \xB7 file content inlined on send"));
7431
7431
  }
7432
7432
  function FileRow({ path, isSelected }) {
7433
7433
  const marker = isSelected ? "\u25B8" : " ";
@@ -7442,11 +7442,348 @@ function FileRow({ path, isSelected }) {
7442
7442
 
7443
7443
  // src/cli/ui/ChoiceConfirm.tsx
7444
7444
  import { Box as Box3, Text as Text3 } from "ink";
7445
- import React3 from "react";
7445
+ import React4 from "react";
7446
+
7447
+ // src/cli/ui/Select.tsx
7448
+ import { Box as Box2, Text as Text2 } from "ink";
7449
+ import React3, { useState } from "react";
7450
+
7451
+ // src/cli/ui/keystroke-context.tsx
7452
+ import React2, { createContext, useContext, useEffect, useRef } from "react";
7453
+
7454
+ // src/cli/ui/stdin-reader.ts
7455
+ import { stdin } from "process";
7456
+ var ESC_TIMEOUT_MS = 250;
7457
+ var PASTE_END = "\x1B[201~";
7458
+ var PASTE_START_BARE = "[200~";
7459
+ var PASTE_END_BARE = "[201~";
7460
+ var CSI_TAIL_MAP = [
7461
+ { tail: "A", ev: { input: "", upArrow: true } },
7462
+ { tail: "B", ev: { input: "", downArrow: true } },
7463
+ { tail: "C", ev: { input: "", rightArrow: true } },
7464
+ { tail: "D", ev: { input: "", leftArrow: true } },
7465
+ { tail: "H", ev: { input: "", home: true } },
7466
+ { tail: "F", ev: { input: "", end: true } },
7467
+ { tail: "1~", ev: { input: "", home: true } },
7468
+ { tail: "4~", ev: { input: "", end: true } },
7469
+ { tail: "5~", ev: { input: "", pageUp: true } },
7470
+ { tail: "6~", ev: { input: "", pageDown: true } },
7471
+ { tail: "3~", ev: { input: "", delete: true } },
7472
+ { tail: "Z", ev: { input: "", shift: true, tab: true } },
7473
+ // modifyOtherKeys (xterm CSI > 4 ; 2 m) sequences for Enter with
7474
+ // modifiers. Only fired when App.tsx has enabled the mode at
7475
+ // startup; otherwise Shift+Enter stays indistinguishable from Enter.
7476
+ // Modifier encoding: 2=shift, 3=alt, 4=alt+shift, 5=ctrl,
7477
+ // 6=ctrl+shift, 7=ctrl+alt, 8=ctrl+alt+shift. Keycode 13 = Enter.
7478
+ { tail: "27;2;13~", ev: { input: "", return: true, shift: true } },
7479
+ { tail: "27;5;13~", ev: { input: "", return: true, ctrl: true } },
7480
+ { tail: "27;6;13~", ev: { input: "", return: true, ctrl: true, shift: true } },
7481
+ // Kitty keyboard protocol — same idea, different envelope:
7482
+ // `\x1b[<keycode>;<mod>u`. Some terminals (kitty, recent Windows
7483
+ // Terminal previews) prefer this shape. Harmless to map here too.
7484
+ { tail: "13;2u", ev: { input: "", return: true, shift: true } },
7485
+ { tail: "13;5u", ev: { input: "", return: true, ctrl: true } },
7486
+ { tail: "13;6u", ev: { input: "", return: true, ctrl: true, shift: true } }
7487
+ ];
7488
+ var SS3_MAP = {
7489
+ A: { input: "", upArrow: true },
7490
+ B: { input: "", downArrow: true },
7491
+ C: { input: "", rightArrow: true },
7492
+ D: { input: "", leftArrow: true },
7493
+ H: { input: "", home: true },
7494
+ F: { input: "", end: true }
7495
+ };
7496
+ function tryEscapelessCsi(chunk, i) {
7497
+ if (chunk[i] !== "[") return null;
7498
+ for (const entry of CSI_TAIL_MAP) {
7499
+ const candidate = `[${entry.tail}`;
7500
+ if (chunk.slice(i, i + candidate.length) === candidate) {
7501
+ return { advance: candidate.length, ev: entry.ev };
7502
+ }
7503
+ }
7504
+ return null;
7505
+ }
7506
+ function isCsiFinal(ch) {
7507
+ const code = ch.charCodeAt(0);
7508
+ return code >= 64 && code <= 126;
7509
+ }
7510
+ function lookupCsi(tail) {
7511
+ for (const entry of CSI_TAIL_MAP) {
7512
+ if (entry.tail === tail) return entry.ev;
7513
+ }
7514
+ return null;
7515
+ }
7516
+ var StdinReader = class {
7517
+ subscribers = /* @__PURE__ */ new Set();
7518
+ state = "idle";
7519
+ /** Buffer for partial sequences across chunks. */
7520
+ csiBuf = "";
7521
+ /** Buffer for paste content. */
7522
+ pasteBuf = "";
7523
+ escTimer = null;
7524
+ started = false;
7525
+ /** The actual `data` listener — kept as a field so `stop()` can detach it. */
7526
+ listener = null;
7527
+ start() {
7528
+ if (this.started) return;
7529
+ if (!stdin.isTTY) {
7530
+ return;
7531
+ }
7532
+ stdin.setRawMode(true);
7533
+ stdin.setEncoding("utf8");
7534
+ stdin.resume();
7535
+ this.listener = (chunk) => this.handleChunk(typeof chunk === "string" ? chunk : chunk.toString("utf8"));
7536
+ stdin.on("data", this.listener);
7537
+ this.started = true;
7538
+ }
7539
+ stop() {
7540
+ if (!this.started) return;
7541
+ if (this.listener) {
7542
+ stdin.off("data", this.listener);
7543
+ this.listener = null;
7544
+ }
7545
+ if (stdin.isTTY) {
7546
+ try {
7547
+ stdin.setRawMode(false);
7548
+ } catch {
7549
+ }
7550
+ }
7551
+ stdin.pause();
7552
+ this.cancelEscTimer();
7553
+ this.state = "idle";
7554
+ this.csiBuf = "";
7555
+ this.pasteBuf = "";
7556
+ this.started = false;
7557
+ }
7558
+ /**
7559
+ * Subscribe to parsed key events. Returns an unsubscribe function.
7560
+ * Multiple subscribers are supported — every event fans out to all
7561
+ * of them; the React Context layer above uses one subscriber and
7562
+ * dispatches further to its own consumer list.
7563
+ */
7564
+ subscribe(fn) {
7565
+ this.subscribers.add(fn);
7566
+ return () => {
7567
+ this.subscribers.delete(fn);
7568
+ };
7569
+ }
7570
+ /**
7571
+ * Inject a chunk of bytes as if it came from stdin. Used by tests
7572
+ * to drive the parser without a real TTY.
7573
+ */
7574
+ feed(chunk) {
7575
+ this.handleChunk(chunk);
7576
+ }
7577
+ dispatch(ev) {
7578
+ for (const sub of this.subscribers) sub(ev);
7579
+ }
7580
+ cancelEscTimer() {
7581
+ if (this.escTimer) {
7582
+ clearTimeout(this.escTimer);
7583
+ this.escTimer = null;
7584
+ }
7585
+ }
7586
+ scheduleEscTimer() {
7587
+ this.cancelEscTimer();
7588
+ this.escTimer = setTimeout(() => {
7589
+ if (this.state === "esc") {
7590
+ this.state = "idle";
7591
+ this.dispatch({ input: "", escape: true });
7592
+ }
7593
+ }, ESC_TIMEOUT_MS);
7594
+ }
7595
+ handleChunk(chunk) {
7596
+ this.cancelEscTimer();
7597
+ let i = 0;
7598
+ while (i < chunk.length) {
7599
+ if (this.state === "paste") {
7600
+ const endA = chunk.indexOf(PASTE_END, i);
7601
+ const endB = chunk.indexOf(PASTE_END_BARE, i);
7602
+ let endIdx = -1;
7603
+ let endLen = 0;
7604
+ if (endA !== -1 && (endB === -1 || endA <= endB)) {
7605
+ endIdx = endA;
7606
+ endLen = PASTE_END.length;
7607
+ } else if (endB !== -1) {
7608
+ endIdx = endB;
7609
+ endLen = PASTE_END_BARE.length;
7610
+ }
7611
+ if (endIdx === -1) {
7612
+ this.pasteBuf += chunk.slice(i);
7613
+ i = chunk.length;
7614
+ break;
7615
+ }
7616
+ this.pasteBuf += chunk.slice(i, endIdx);
7617
+ this.dispatch({ input: this.pasteBuf, paste: true });
7618
+ this.pasteBuf = "";
7619
+ this.state = "idle";
7620
+ i = endIdx + endLen;
7621
+ continue;
7622
+ }
7623
+ if (this.state === "csi") {
7624
+ const ch2 = chunk[i];
7625
+ this.csiBuf += ch2;
7626
+ if (isCsiFinal(ch2)) {
7627
+ this.dispatchCsi(this.csiBuf);
7628
+ this.csiBuf = "";
7629
+ if (this.state === "csi") this.state = "idle";
7630
+ }
7631
+ i++;
7632
+ continue;
7633
+ }
7634
+ if (this.state === "ss3") {
7635
+ const ev = SS3_MAP[chunk[i]];
7636
+ if (ev) this.dispatch(ev);
7637
+ this.state = "idle";
7638
+ i++;
7639
+ continue;
7640
+ }
7641
+ if (this.state === "esc") {
7642
+ const ch2 = chunk[i];
7643
+ if (ch2 === "[") {
7644
+ this.state = "csi";
7645
+ this.csiBuf = "";
7646
+ i++;
7647
+ continue;
7648
+ }
7649
+ if (ch2 === "O") {
7650
+ this.state = "ss3";
7651
+ i++;
7652
+ continue;
7653
+ }
7654
+ this.dispatch({ input: ch2, meta: true });
7655
+ this.state = "idle";
7656
+ i++;
7657
+ continue;
7658
+ }
7659
+ const ch = chunk[i];
7660
+ if (ch === "\x1B") {
7661
+ this.state = "esc";
7662
+ i++;
7663
+ continue;
7664
+ }
7665
+ if (chunk.slice(i, i + PASTE_START_BARE.length) === PASTE_START_BARE) {
7666
+ this.state = "paste";
7667
+ this.pasteBuf = "";
7668
+ i += PASTE_START_BARE.length;
7669
+ continue;
7670
+ }
7671
+ const escapeless = tryEscapelessCsi(chunk, i);
7672
+ if (escapeless) {
7673
+ this.dispatch(escapeless.ev);
7674
+ i += escapeless.advance;
7675
+ continue;
7676
+ }
7677
+ if (ch === "\r") {
7678
+ this.dispatch({ input: "", return: true });
7679
+ i++;
7680
+ continue;
7681
+ }
7682
+ if (ch === "\n") {
7683
+ this.dispatch({ input: "j", ctrl: true });
7684
+ i++;
7685
+ continue;
7686
+ }
7687
+ if (ch === " ") {
7688
+ this.dispatch({ input: "", tab: true });
7689
+ i++;
7690
+ continue;
7691
+ }
7692
+ if (ch === "\x7F" || ch === "\b") {
7693
+ this.dispatch({ input: "", backspace: true });
7694
+ i++;
7695
+ continue;
7696
+ }
7697
+ if (ch === "") {
7698
+ this.dispatch({ input: "c", ctrl: true });
7699
+ i++;
7700
+ continue;
7701
+ }
7702
+ const code = ch.charCodeAt(0);
7703
+ if (code >= 1 && code <= 26) {
7704
+ const letter = String.fromCharCode(96 + code);
7705
+ this.dispatch({ input: letter, ctrl: true });
7706
+ i++;
7707
+ continue;
7708
+ }
7709
+ let end = i + 1;
7710
+ while (end < chunk.length) {
7711
+ const c = chunk[end];
7712
+ if (c === "\x1B" || c === "\r" || c === "\n" || c === " ") break;
7713
+ if (c === "\x7F" || c === "\b" || c === "") break;
7714
+ const cc = c.charCodeAt(0);
7715
+ if (cc >= 1 && cc <= 26) break;
7716
+ if (c === "[" && tryEscapelessCsi(chunk, end)) break;
7717
+ if (chunk.slice(end, end + PASTE_START_BARE.length) === PASTE_START_BARE) break;
7718
+ end++;
7719
+ }
7720
+ this.dispatch({ input: chunk.slice(i, end) });
7721
+ i = end;
7722
+ }
7723
+ if (this.state === "esc") {
7724
+ this.scheduleEscTimer();
7725
+ }
7726
+ }
7727
+ dispatchCsi(seq) {
7728
+ if (seq === "200~") {
7729
+ this.state = "paste";
7730
+ this.pasteBuf = "";
7731
+ return;
7732
+ }
7733
+ if (seq === "201~") {
7734
+ return;
7735
+ }
7736
+ const ev = lookupCsi(seq);
7737
+ if (ev) this.dispatch(ev);
7738
+ }
7739
+ };
7740
+ var singleton = null;
7741
+ function getStdinReader() {
7742
+ if (!singleton) singleton = new StdinReader();
7743
+ return singleton;
7744
+ }
7745
+
7746
+ // src/cli/ui/keystroke-context.tsx
7747
+ var KeystrokeContext = createContext(null);
7748
+ function KeystrokeProvider({
7749
+ children,
7750
+ reader: providedReader
7751
+ }) {
7752
+ const handlersRef = useRef(/* @__PURE__ */ new Set());
7753
+ const busRef = useRef(null);
7754
+ if (busRef.current === null) {
7755
+ busRef.current = {
7756
+ subscribe(handler) {
7757
+ handlersRef.current.add(handler);
7758
+ return () => {
7759
+ handlersRef.current.delete(handler);
7760
+ };
7761
+ }
7762
+ };
7763
+ }
7764
+ useEffect(() => {
7765
+ const reader = providedReader ?? getStdinReader();
7766
+ reader.start();
7767
+ const unsubscribe = reader.subscribe((ev) => {
7768
+ for (const fn of [...handlersRef.current]) fn(ev);
7769
+ });
7770
+ return () => {
7771
+ unsubscribe();
7772
+ };
7773
+ }, [providedReader]);
7774
+ return /* @__PURE__ */ React2.createElement(KeystrokeContext.Provider, { value: busRef.current }, children);
7775
+ }
7776
+ function useKeystroke(handler, isActive = true) {
7777
+ const bus = useContext(KeystrokeContext);
7778
+ const handlerRef = useRef(handler);
7779
+ handlerRef.current = handler;
7780
+ useEffect(() => {
7781
+ if (!bus || !isActive) return void 0;
7782
+ return bus.subscribe((ev) => handlerRef.current(ev));
7783
+ }, [bus, isActive]);
7784
+ }
7446
7785
 
7447
7786
  // src/cli/ui/Select.tsx
7448
- import { Box as Box2, Text as Text2, useInput } from "ink";
7449
- import React2, { useState } from "react";
7450
7787
  function SingleSelect({
7451
7788
  items,
7452
7789
  initialValue,
@@ -7459,19 +7796,20 @@ function SingleSelect({
7459
7796
  items.findIndex((i) => i.value === initialValue && !i.disabled)
7460
7797
  );
7461
7798
  const [index, setIndex] = useState(initialIndex === -1 ? 0 : initialIndex);
7462
- useInput((_input, key) => {
7463
- if (key.upArrow) {
7799
+ useKeystroke((ev) => {
7800
+ if (ev.paste) return;
7801
+ if (ev.upArrow) {
7464
7802
  setIndex((i) => findNextEnabled(items, i, -1));
7465
- } else if (key.downArrow) {
7803
+ } else if (ev.downArrow) {
7466
7804
  setIndex((i) => findNextEnabled(items, i, 1));
7467
- } else if (key.return) {
7805
+ } else if (ev.return) {
7468
7806
  const chosen = items[index];
7469
7807
  if (chosen && !chosen.disabled) onSubmit(chosen.value);
7470
- } else if (key.escape && onCancel) {
7808
+ } else if (ev.escape && onCancel) {
7471
7809
  onCancel();
7472
7810
  }
7473
7811
  });
7474
- return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, items.map((item, i) => /* @__PURE__ */ React2.createElement(
7812
+ return /* @__PURE__ */ React3.createElement(Box2, { flexDirection: "column" }, items.map((item, i) => /* @__PURE__ */ React3.createElement(
7475
7813
  SelectRow,
7476
7814
  {
7477
7815
  key: item.value,
@@ -7479,7 +7817,7 @@ function SingleSelect({
7479
7817
  active: i === index,
7480
7818
  marker: i === index ? "\u25B8" : " "
7481
7819
  }
7482
- )), footer ? /* @__PURE__ */ React2.createElement(Box2, { marginTop: 1 }, /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, footer)) : null);
7820
+ )), footer ? /* @__PURE__ */ React3.createElement(Box2, { marginTop: 1 }, /* @__PURE__ */ React3.createElement(Text2, { dimColor: true }, footer)) : null);
7483
7821
  }
7484
7822
  function MultiSelect({
7485
7823
  items,
@@ -7493,12 +7831,13 @@ function MultiSelect({
7493
7831
  return first === -1 ? 0 : first;
7494
7832
  });
7495
7833
  const [selected, setSelected] = useState(new Set(initialSelected));
7496
- useInput((input, key) => {
7497
- if (key.upArrow) {
7834
+ useKeystroke((ev) => {
7835
+ if (ev.paste) return;
7836
+ if (ev.upArrow) {
7498
7837
  setIndex((i) => findNextEnabled(items, i, -1));
7499
- } else if (key.downArrow) {
7838
+ } else if (ev.downArrow) {
7500
7839
  setIndex((i) => findNextEnabled(items, i, 1));
7501
- } else if (input === " ") {
7840
+ } else if (ev.input === " ") {
7502
7841
  const item = items[index];
7503
7842
  if (!item || item.disabled) return;
7504
7843
  setSelected((prev) => {
@@ -7507,17 +7846,17 @@ function MultiSelect({
7507
7846
  else next.add(item.value);
7508
7847
  return next;
7509
7848
  });
7510
- } else if (key.return) {
7849
+ } else if (ev.return) {
7511
7850
  const ordered = items.filter((i) => selected.has(i.value)).map((i) => i.value);
7512
7851
  onSubmit(ordered);
7513
- } else if (key.escape && onCancel) {
7852
+ } else if (ev.escape && onCancel) {
7514
7853
  onCancel();
7515
7854
  }
7516
7855
  });
7517
- return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, items.map((item, i) => {
7856
+ return /* @__PURE__ */ React3.createElement(Box2, { flexDirection: "column" }, items.map((item, i) => {
7518
7857
  const checked = selected.has(item.value);
7519
7858
  const marker = checked ? "[x]" : "[ ]";
7520
- return /* @__PURE__ */ React2.createElement(
7859
+ return /* @__PURE__ */ React3.createElement(
7521
7860
  SelectRow,
7522
7861
  {
7523
7862
  key: item.value,
@@ -7526,7 +7865,7 @@ function MultiSelect({
7526
7865
  marker: `${i === index ? "\u25B8" : " "} ${marker}`
7527
7866
  }
7528
7867
  );
7529
- }), footer ? /* @__PURE__ */ React2.createElement(Box2, { marginTop: 1 }, /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, footer)) : null);
7868
+ }), footer ? /* @__PURE__ */ React3.createElement(Box2, { marginTop: 1 }, /* @__PURE__ */ React3.createElement(Text2, { dimColor: true }, footer)) : null);
7530
7869
  }
7531
7870
  function SelectRow({
7532
7871
  item,
@@ -7534,7 +7873,7 @@ function SelectRow({
7534
7873
  marker
7535
7874
  }) {
7536
7875
  const color = item.disabled ? "gray" : active ? "cyan" : void 0;
7537
- return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, /* @__PURE__ */ React2.createElement(Box2, null, /* @__PURE__ */ React2.createElement(Text2, { color }, marker, " ", item.label)), item.hint ? /* @__PURE__ */ React2.createElement(Box2, { paddingLeft: marker.length + 1 }, /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, item.hint)) : null);
7876
+ return /* @__PURE__ */ React3.createElement(Box2, { flexDirection: "column" }, /* @__PURE__ */ React3.createElement(Box2, null, /* @__PURE__ */ React3.createElement(Text2, { color }, marker, " ", item.label)), item.hint ? /* @__PURE__ */ React3.createElement(Box2, { paddingLeft: marker.length + 1 }, /* @__PURE__ */ React3.createElement(Text2, { dimColor: true }, item.hint)) : null);
7538
7877
  }
7539
7878
  function findNextEnabled(items, from, step) {
7540
7879
  if (items.length === 0) return 0;
@@ -7567,7 +7906,7 @@ function ChoiceConfirmInner({ question, options, allowCustom, onChoose }) {
7567
7906
  label: "Cancel \u2014 drop the question",
7568
7907
  hint: "Model stops and asks what you want instead."
7569
7908
  });
7570
- return /* @__PURE__ */ React3.createElement(Box3, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React3.createElement(Box3, null, /* @__PURE__ */ React3.createElement(Text3, { bold: true, color: "magenta" }, "\u{1F500} the model is asking you to pick")), /* @__PURE__ */ React3.createElement(Box3, { marginTop: 1 }, /* @__PURE__ */ React3.createElement(Text3, null, question)), /* @__PURE__ */ React3.createElement(Box3, { marginTop: 1 }, /* @__PURE__ */ React3.createElement(
7909
+ return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React4.createElement(Box3, null, /* @__PURE__ */ React4.createElement(Text3, { bold: true, color: "magenta" }, "\u{1F500} the model is asking you to pick")), /* @__PURE__ */ React4.createElement(Box3, { marginTop: 1 }, /* @__PURE__ */ React4.createElement(Text3, null, question)), /* @__PURE__ */ React4.createElement(Box3, { marginTop: 1 }, /* @__PURE__ */ React4.createElement(
7571
7910
  SingleSelect,
7572
7911
  {
7573
7912
  initialValue: options[0]?.id,
@@ -7582,11 +7921,11 @@ function ChoiceConfirmInner({ question, options, allowCustom, onChoose }) {
7582
7921
  }
7583
7922
  )));
7584
7923
  }
7585
- var ChoiceConfirm = React3.memo(ChoiceConfirmInner);
7924
+ var ChoiceConfirm = React4.memo(ChoiceConfirmInner);
7586
7925
 
7587
7926
  // src/cli/ui/EditConfirm.tsx
7588
- import { Box as Box4, Text as Text4, useInput as useInput2, useStdout } from "ink";
7589
- import React4, { useMemo, useState as useState2 } from "react";
7927
+ import { Box as Box4, Text as Text4, useStdout } from "ink";
7928
+ import React5, { useMemo, useState as useState2 } from "react";
7590
7929
 
7591
7930
  // src/code/diff-preview.ts
7592
7931
  function formatEditBlockDiff(block, opts = {}) {
@@ -7671,7 +8010,10 @@ function EditConfirm({ block, onChoose }) {
7671
8010
  const [scroll, setScroll] = useState2(0);
7672
8011
  const maxScroll = Math.max(0, allLines.length - budget);
7673
8012
  const effectiveScroll = Math.min(scroll, maxScroll);
7674
- useInput2((input, key) => {
8013
+ useKeystroke((ev) => {
8014
+ if (ev.paste) return;
8015
+ const input = ev.input;
8016
+ const key = ev;
7675
8017
  if (key.return || input === "y") {
7676
8018
  onChoose("apply");
7677
8019
  return;
@@ -7722,17 +8064,17 @@ function EditConfirm({ block, onChoose }) {
7722
8064
  const hiddenBelow = Math.max(0, allLines.length - effectiveScroll - budget);
7723
8065
  const totalLines = allLines.length;
7724
8066
  const showScrollHud = hiddenAbove + hiddenBelow > 0;
7725
- return /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React4.createElement(Box4, null, /* @__PURE__ */ React4.createElement(Text4, { bold: true, color: "cyan" }, "\u25B8 model wants to edit a file")), /* @__PURE__ */ React4.createElement(Box4, null, /* @__PURE__ */ React4.createElement(Text4, { color: "cyan", dimColor: true }, "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")), /* @__PURE__ */ React4.createElement(Box4, { marginTop: 1 }, /* @__PURE__ */ React4.createElement(Text4, null, /* @__PURE__ */ React4.createElement(Text4, { color: isNew ? "green" : "yellow", bold: true }, `[${tag}] `), /* @__PURE__ */ React4.createElement(Text4, { color: "cyan" }, block.path), /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, ` (-${removed} +${added} lines)`), showScrollHud ? /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, ` \xB7 viewing ${effectiveScroll + 1}-${effectiveScroll + visibleLines.length}/${totalLines}`) : null)), hiddenAbove > 0 ? /* @__PURE__ */ React4.createElement(
8067
+ return /* @__PURE__ */ React5.createElement(Box4, { flexDirection: "column", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React5.createElement(Box4, null, /* @__PURE__ */ React5.createElement(Text4, { bold: true, color: "yellow" }, "\u25B8 model wants to edit a file")), /* @__PURE__ */ React5.createElement(Box4, null, /* @__PURE__ */ React5.createElement(Text4, { color: "yellow", dimColor: true }, "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")), /* @__PURE__ */ React5.createElement(Box4, { marginTop: 1 }, /* @__PURE__ */ React5.createElement(Text4, null, /* @__PURE__ */ React5.createElement(Text4, { color: isNew ? "green" : "yellow", bold: true }, `[${tag}] `), /* @__PURE__ */ React5.createElement(Text4, { color: "cyan" }, block.path), /* @__PURE__ */ React5.createElement(Text4, { dimColor: true }, ` (-${removed} +${added} lines)`), showScrollHud ? /* @__PURE__ */ React5.createElement(Text4, { dimColor: true }, ` \xB7 viewing ${effectiveScroll + 1}-${effectiveScroll + visibleLines.length}/${totalLines}`) : null)), hiddenAbove > 0 ? /* @__PURE__ */ React5.createElement(
7726
8068
  Text4,
7727
8069
  {
7728
8070
  dimColor: true
7729
8071
  },
7730
8072
  ` \u2191 ${hiddenAbove} line${hiddenAbove === 1 ? "" : "s"} above (\u2191/k or PgUp)`
7731
- ) : null, /* @__PURE__ */ React4.createElement(Box4, { marginTop: hiddenAbove > 0 ? 0 : 1, flexDirection: "column" }, visibleLines.map((line, i) => {
8073
+ ) : null, /* @__PURE__ */ React5.createElement(Box4, { marginTop: hiddenAbove > 0 ? 0 : 1, flexDirection: "column" }, visibleLines.map((line, i) => {
7732
8074
  const trimmed = line.trimStart();
7733
8075
  const color = trimmed.startsWith("+") ? "green" : trimmed.startsWith("-") ? "red" : void 0;
7734
8076
  const dim = !color;
7735
- return /* @__PURE__ */ React4.createElement(
8077
+ return /* @__PURE__ */ React5.createElement(
7736
8078
  Text4,
7737
8079
  {
7738
8080
  key: `diff-${effectiveScroll}-${i}`,
@@ -7741,22 +8083,22 @@ function EditConfirm({ block, onChoose }) {
7741
8083
  },
7742
8084
  line
7743
8085
  );
7744
- })), hiddenBelow > 0 ? /* @__PURE__ */ React4.createElement(
8086
+ })), hiddenBelow > 0 ? /* @__PURE__ */ React5.createElement(
7745
8087
  Text4,
7746
8088
  {
7747
8089
  dimColor: true
7748
8090
  },
7749
8091
  ` \u2193 ${hiddenBelow} line${hiddenBelow === 1 ? "" : "s"} below (\u2193/j or Space/PgDn)`
7750
- ) : null, /* @__PURE__ */ React4.createElement(Box4, { marginTop: 1 }, /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, "[", /* @__PURE__ */ React4.createElement(Text4, { color: "cyan", bold: true }, "y"), "/Enter] apply \xB7 [", /* @__PURE__ */ React4.createElement(Text4, { color: "cyan", bold: true }, "n"), "] reject \xB7 [", /* @__PURE__ */ React4.createElement(Text4, { color: "cyan", bold: true }, "a"), "] apply rest \xB7 [", /* @__PURE__ */ React4.createElement(Text4, { color: "cyan", bold: true }, "A"), "] flip AUTO \xB7 [", /* @__PURE__ */ React4.createElement(Text4, { color: "cyan", bold: true }, "\u2191\u2193/Space"), "] scroll \xB7 [Esc] abort")));
8092
+ ) : null, /* @__PURE__ */ React5.createElement(Box4, { marginTop: 1 }, /* @__PURE__ */ React5.createElement(Text4, { dimColor: true }, "[", /* @__PURE__ */ React5.createElement(Text4, { color: "cyan", bold: true }, "y"), "/Enter] apply \xB7 [", /* @__PURE__ */ React5.createElement(Text4, { color: "cyan", bold: true }, "n"), "] reject \xB7 [", /* @__PURE__ */ React5.createElement(Text4, { color: "cyan", bold: true }, "a"), "] apply rest \xB7 [", /* @__PURE__ */ React5.createElement(Text4, { color: "cyan", bold: true }, "A"), "] flip AUTO \xB7 [", /* @__PURE__ */ React5.createElement(Text4, { color: "cyan", bold: true }, "\u2191\u2193/Space"), "] scroll \xB7 [Esc] abort")));
7751
8093
  }
7752
8094
 
7753
8095
  // src/cli/ui/EventLog.tsx
7754
8096
  import { Box as Box8, Text as Text8, useStdout as useStdout2 } from "ink";
7755
- import React9 from "react";
8097
+ import React10 from "react";
7756
8098
 
7757
8099
  // src/cli/ui/PlanStateBlock.tsx
7758
8100
  import { Box as Box5, Text as Text5 } from "ink";
7759
- import React5 from "react";
8101
+ import React6 from "react";
7760
8102
  function PlanStateBlock({ planState }) {
7761
8103
  const fields = [];
7762
8104
  if (planState.subgoals.length) fields.push(["subgoals", planState.subgoals, "cyan", false]);
@@ -7767,12 +8109,12 @@ function PlanStateBlock({ planState }) {
7767
8109
  if (planState.rejectedPaths.length)
7768
8110
  fields.push(["rejected", planState.rejectedPaths, "red", true]);
7769
8111
  if (fields.length === 0) return null;
7770
- return /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column", marginBottom: 1 }, fields.map(([label, items, color, dim]) => /* @__PURE__ */ React5.createElement(Text5, { key: label }, /* @__PURE__ */ React5.createElement(Text5, { color, bold: true, dimColor: dim }, "\u2039 ", label), /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, ` (${items.length})`), /* @__PURE__ */ React5.createElement(Text5, null, `: ${items.join(" \xB7 ")}`))));
8112
+ return /* @__PURE__ */ React6.createElement(Box5, { flexDirection: "column", marginBottom: 1 }, fields.map(([label, items, color, dim]) => /* @__PURE__ */ React6.createElement(Text5, { key: label }, /* @__PURE__ */ React6.createElement(Text5, { color, bold: true, dimColor: dim }, "\u2039 ", label), /* @__PURE__ */ React6.createElement(Text5, { dimColor: true }, ` (${items.length})`), /* @__PURE__ */ React6.createElement(Text5, null, `: ${items.join(" \xB7 ")}`))));
7771
8113
  }
7772
8114
 
7773
8115
  // src/cli/ui/PlanStepList.tsx
7774
8116
  import { Box as Box6, Text as Text6 } from "ink";
7775
- import React6 from "react";
8117
+ import React7 from "react";
7776
8118
  function riskDots(risk) {
7777
8119
  switch (risk) {
7778
8120
  case "high":
@@ -7811,25 +8153,25 @@ function PlanStepListInner({ steps, statuses, focusStepId }) {
7811
8153
  { length: steps.length },
7812
8154
  (_, i) => getStatus(steps[i].id, statuses)
7813
8155
  ).filter((s) => s === "done").length;
7814
- return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, `${steps.length} step${steps.length === 1 ? "" : "s"}`, doneCount > 0 ? ` \xB7 ${doneCount} done` : "", hasAnyRisk ? " \xB7 risk: " : ""), hasAnyRisk ? /* @__PURE__ */ React6.createElement(RiskLegend, null) : null), /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column", marginTop: 1 }, steps.map((step) => {
8156
+ return /* @__PURE__ */ React7.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React7.createElement(Box6, null, /* @__PURE__ */ React7.createElement(Text6, { dimColor: true }, `${steps.length} step${steps.length === 1 ? "" : "s"}`, doneCount > 0 ? ` \xB7 ${doneCount} done` : "", hasAnyRisk ? " \xB7 risk: " : ""), hasAnyRisk ? /* @__PURE__ */ React7.createElement(RiskLegend, null) : null), /* @__PURE__ */ React7.createElement(Box6, { flexDirection: "column", marginTop: 1 }, steps.map((step) => {
7815
8157
  const status2 = getStatus(step.id, statuses);
7816
8158
  const focus = focusStepId === step.id;
7817
8159
  const risk = riskDots(step.risk);
7818
8160
  const glyph = statusGlyph(status2);
7819
8161
  const titleDim = status2 === "done" || status2 === "skipped";
7820
- return /* @__PURE__ */ React6.createElement(Box6, { key: step.id }, /* @__PURE__ */ React6.createElement(Text6, { color: focus ? "cyan" : "gray", bold: focus }, focus ? "\u203A " : " "), /* @__PURE__ */ React6.createElement(Text6, { color: risk.color, bold: true }, risk.dots), /* @__PURE__ */ React6.createElement(Text6, { color: glyph.color, bold: true }, ` ${glyph.glyph} `), /* @__PURE__ */ React6.createElement(Text6, { dimColor: titleDim }, `${step.id} \xB7 ${step.title}`));
8162
+ return /* @__PURE__ */ React7.createElement(Box6, { key: step.id }, /* @__PURE__ */ React7.createElement(Text6, { color: focus ? "cyan" : "gray", bold: focus }, focus ? "\u203A " : " "), /* @__PURE__ */ React7.createElement(Text6, { color: risk.color, bold: true }, risk.dots), /* @__PURE__ */ React7.createElement(Text6, { color: glyph.color, bold: true }, ` ${glyph.glyph} `), /* @__PURE__ */ React7.createElement(Text6, { dimColor: titleDim }, `${step.id} \xB7 ${step.title}`));
7821
8163
  })));
7822
8164
  }
7823
8165
  function RiskLegend() {
7824
- return /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "green" }, "\u25CF"), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " low "), /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, "\u25CF\u25CF"), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " med "), /* @__PURE__ */ React6.createElement(Text6, { color: "red" }, "\u25CF\u25CF\u25CF"), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " high"));
8166
+ return /* @__PURE__ */ React7.createElement(Box6, null, /* @__PURE__ */ React7.createElement(Text6, { color: "green" }, "\u25CF"), /* @__PURE__ */ React7.createElement(Text6, { dimColor: true }, " low "), /* @__PURE__ */ React7.createElement(Text6, { color: "yellow" }, "\u25CF\u25CF"), /* @__PURE__ */ React7.createElement(Text6, { dimColor: true }, " med "), /* @__PURE__ */ React7.createElement(Text6, { color: "red" }, "\u25CF\u25CF\u25CF"), /* @__PURE__ */ React7.createElement(Text6, { dimColor: true }, " high"));
7825
8167
  }
7826
- var PlanStepList = React6.memo(PlanStepListInner);
8168
+ var PlanStepList = React7.memo(PlanStepListInner);
7827
8169
 
7828
8170
  // src/cli/ui/markdown.tsx
7829
8171
  import { readFileSync as readFileSync13, statSync as statSync6 } from "fs";
7830
8172
  import { isAbsolute as isAbsolute4, join as join11 } from "path";
7831
8173
  import { Box as Box7, Text as Text7 } from "ink";
7832
- import React7 from "react";
8174
+ import React8 from "react";
7833
8175
  var SUPERSCRIPT = {
7834
8176
  "0": "\u2070",
7835
8177
  "1": "\xB9",
@@ -8084,67 +8426,67 @@ function InlineMd({
8084
8426
  for (const m of text.matchAll(INLINE_RE)) {
8085
8427
  const start = m.index ?? 0;
8086
8428
  if (start > last) {
8087
- parts.push(/* @__PURE__ */ React7.createElement(Text7, { key: `t${idx++}` }, text.slice(last, start)));
8429
+ parts.push(/* @__PURE__ */ React8.createElement(Text7, { key: `t${idx++}` }, text.slice(last, start)));
8088
8430
  }
8089
8431
  if (m[2] !== void 0 && m[3] !== void 0) {
8090
8432
  const linkText = m[2];
8091
8433
  const url = m[3];
8092
8434
  if (isExternalUrl(url)) {
8093
8435
  parts.push(
8094
- /* @__PURE__ */ React7.createElement(Text7, { key: `l${idx++}`, color: "blue", underline: true }, linkText)
8436
+ /* @__PURE__ */ React8.createElement(Text7, { key: `l${idx++}`, color: "blue", underline: true }, linkText)
8095
8437
  );
8096
8438
  } else {
8097
8439
  const status2 = citations?.get(url);
8098
8440
  if (status2 && !status2.ok) {
8099
8441
  parts.push(
8100
- /* @__PURE__ */ React7.createElement(Text7, { key: `l${idx++}`, color: "red", strikethrough: true }, `${linkText} \u2717`)
8442
+ /* @__PURE__ */ React8.createElement(Text7, { key: `l${idx++}`, color: "red", strikethrough: true }, `${linkText} \u2717`)
8101
8443
  );
8102
8444
  } else {
8103
8445
  parts.push(
8104
- /* @__PURE__ */ React7.createElement(Text7, { key: `l${idx++}`, color: "cyan", underline: true }, linkText)
8446
+ /* @__PURE__ */ React8.createElement(Text7, { key: `l${idx++}`, color: "cyan", underline: true }, linkText)
8105
8447
  );
8106
8448
  }
8107
8449
  }
8108
8450
  } else if (m[4] !== void 0) {
8109
8451
  parts.push(
8110
- /* @__PURE__ */ React7.createElement(Text7, { key: `bi${idx++}`, bold: true, italic: true }, m[4])
8452
+ /* @__PURE__ */ React8.createElement(Text7, { key: `bi${idx++}`, bold: true, italic: true }, m[4])
8111
8453
  );
8112
8454
  } else if (m[5] !== void 0) {
8113
8455
  parts.push(
8114
- /* @__PURE__ */ React7.createElement(Text7, { key: `b${idx++}`, bold: true }, m[5])
8456
+ /* @__PURE__ */ React8.createElement(Text7, { key: `b${idx++}`, bold: true }, m[5])
8115
8457
  );
8116
8458
  } else if (m[6] !== void 0) {
8117
8459
  const stripped = m[6].replace(/^(\w+)\s+/, "");
8118
8460
  parts.push(
8119
- /* @__PURE__ */ React7.createElement(Text7, { key: `c${idx++}`, color: "yellow" }, stripped)
8461
+ /* @__PURE__ */ React8.createElement(Text7, { key: `c${idx++}`, color: "yellow" }, stripped)
8120
8462
  );
8121
8463
  } else if (m[7] !== void 0) {
8122
8464
  parts.push(
8123
- /* @__PURE__ */ React7.createElement(Text7, { key: `c${idx++}`, color: "yellow" }, m[7])
8465
+ /* @__PURE__ */ React8.createElement(Text7, { key: `c${idx++}`, color: "yellow" }, m[7])
8124
8466
  );
8125
8467
  } else if (m[8] !== void 0) {
8126
8468
  parts.push(
8127
- /* @__PURE__ */ React7.createElement(Text7, { key: `s${idx++}`, strikethrough: true, dimColor: true }, m[8])
8469
+ /* @__PURE__ */ React8.createElement(Text7, { key: `s${idx++}`, strikethrough: true, dimColor: true }, m[8])
8128
8470
  );
8129
8471
  } else if (m[9] !== void 0) {
8130
8472
  parts.push(
8131
- /* @__PURE__ */ React7.createElement(Text7, { key: `i${idx++}`, italic: true }, m[9])
8473
+ /* @__PURE__ */ React8.createElement(Text7, { key: `i${idx++}`, italic: true }, m[9])
8132
8474
  );
8133
8475
  } else if (m[10] !== void 0) {
8134
- parts.push(/* @__PURE__ */ React7.createElement(Text7, { key: `esc${idx++}` }, m[10]));
8476
+ parts.push(/* @__PURE__ */ React8.createElement(Text7, { key: `esc${idx++}` }, m[10]));
8135
8477
  }
8136
8478
  last = start + m[0].length;
8137
8479
  }
8138
8480
  if (last < text.length) {
8139
- parts.push(/* @__PURE__ */ React7.createElement(Text7, { key: `t${idx++}` }, text.slice(last)));
8481
+ parts.push(/* @__PURE__ */ React8.createElement(Text7, { key: `t${idx++}` }, text.slice(last)));
8140
8482
  }
8141
8483
  if (padTo !== void 0) {
8142
8484
  const seen = visibleWidth(text);
8143
8485
  if (seen < padTo) {
8144
- parts.push(/* @__PURE__ */ React7.createElement(Text7, { key: `pad${idx++}` }, " ".repeat(padTo - seen)));
8486
+ parts.push(/* @__PURE__ */ React8.createElement(Text7, { key: `pad${idx++}` }, " ".repeat(padTo - seen)));
8145
8487
  }
8146
8488
  }
8147
- return /* @__PURE__ */ React7.createElement(Text7, null, parts);
8489
+ return /* @__PURE__ */ React8.createElement(Text7, null, parts);
8148
8490
  }
8149
8491
  function stripInlineMarkup(s) {
8150
8492
  return s.replace(
@@ -8383,34 +8725,34 @@ function parseBulletItem(raw) {
8383
8725
  function BlockView({ block, citations }) {
8384
8726
  switch (block.kind) {
8385
8727
  case "heading":
8386
- return /* @__PURE__ */ React7.createElement(Text7, { bold: true, color: "cyan" }, /* @__PURE__ */ React7.createElement(InlineMd, { text: block.text, citations }));
8728
+ return /* @__PURE__ */ React8.createElement(Text7, { bold: true, color: "cyan" }, /* @__PURE__ */ React8.createElement(InlineMd, { text: block.text, citations }));
8387
8729
  case "paragraph":
8388
- return /* @__PURE__ */ React7.createElement(ParagraphView, { text: block.text, citations });
8730
+ return /* @__PURE__ */ React8.createElement(ParagraphView, { text: block.text, citations });
8389
8731
  case "bullet":
8390
- return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column" }, block.items.map((item, i) => /* @__PURE__ */ React7.createElement(Box7, { key: `${i}-${item.text.slice(0, 24)}` }, /* @__PURE__ */ React7.createElement(Text7, { color: item.task === "done" ? "green" : "cyan" }, bulletPrefix(block, i, item)), item.task === "done" ? /* @__PURE__ */ React7.createElement(Text7, { strikethrough: true, dimColor: true }, /* @__PURE__ */ React7.createElement(InlineMd, { text: item.text, citations })) : /* @__PURE__ */ React7.createElement(InlineMd, { text: item.text, citations }))));
8732
+ return /* @__PURE__ */ React8.createElement(Box7, { flexDirection: "column" }, block.items.map((item, i) => /* @__PURE__ */ React8.createElement(Box7, { key: `${i}-${item.text.slice(0, 24)}` }, /* @__PURE__ */ React8.createElement(Text7, { color: item.task === "done" ? "green" : "cyan" }, bulletPrefix(block, i, item)), item.task === "done" ? /* @__PURE__ */ React8.createElement(Text7, { strikethrough: true, dimColor: true }, /* @__PURE__ */ React8.createElement(InlineMd, { text: item.text, citations })) : /* @__PURE__ */ React8.createElement(InlineMd, { text: item.text, citations }))));
8391
8733
  case "quote":
8392
- return /* @__PURE__ */ React7.createElement(BlockquoteView, { block, citations });
8734
+ return /* @__PURE__ */ React8.createElement(BlockquoteView, { block, citations });
8393
8735
  case "code":
8394
8736
  if (DIAGRAM_LANGS.has(block.lang.toLowerCase())) {
8395
- return /* @__PURE__ */ React7.createElement(DiagramCodeBlock, { lang: block.lang, text: block.text });
8737
+ return /* @__PURE__ */ React8.createElement(DiagramCodeBlock, { lang: block.lang, text: block.text });
8396
8738
  }
8397
- return /* @__PURE__ */ React7.createElement(Box7, { borderStyle: "single", borderColor: "gray", paddingX: 1 }, /* @__PURE__ */ React7.createElement(Text7, { color: "yellow" }, block.text));
8739
+ return /* @__PURE__ */ React8.createElement(Box7, { borderStyle: "single", borderColor: "gray", paddingX: 1 }, /* @__PURE__ */ React8.createElement(Text7, { color: "yellow" }, block.text));
8398
8740
  case "edit-block":
8399
- return /* @__PURE__ */ React7.createElement(EditBlockRow, { block });
8741
+ return /* @__PURE__ */ React8.createElement(EditBlockRow, { block });
8400
8742
  case "table":
8401
- return /* @__PURE__ */ React7.createElement(TableBlockRow, { block, citations });
8743
+ return /* @__PURE__ */ React8.createElement(TableBlockRow, { block, citations });
8402
8744
  case "hr":
8403
- return /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
8745
+ return /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
8404
8746
  }
8405
8747
  }
8406
8748
  function ParagraphView({ text, citations }) {
8407
8749
  if (!text.includes("\n")) {
8408
- return /* @__PURE__ */ React7.createElement(InlineMd, { text, citations });
8750
+ return /* @__PURE__ */ React8.createElement(InlineMd, { text, citations });
8409
8751
  }
8410
8752
  const rows = text.split("\n");
8411
- return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column" }, rows.map((row2, i) => (
8753
+ return /* @__PURE__ */ React8.createElement(Box7, { flexDirection: "column" }, rows.map((row2, i) => (
8412
8754
  // biome-ignore lint/suspicious/noArrayIndexKey: hard-break rows are source-ordered and never reorder
8413
- /* @__PURE__ */ React7.createElement(InlineMd, { key: `ln-${i}`, text: row2, citations })
8755
+ /* @__PURE__ */ React8.createElement(InlineMd, { key: `ln-${i}`, text: row2, citations })
8414
8756
  )));
8415
8757
  }
8416
8758
  function bulletPrefix(block, i, item) {
@@ -8423,7 +8765,7 @@ function BlockquoteView({
8423
8765
  block,
8424
8766
  citations
8425
8767
  }) {
8426
- return /* @__PURE__ */ React7.createElement(
8768
+ return /* @__PURE__ */ React8.createElement(
8427
8769
  Box7,
8428
8770
  {
8429
8771
  borderStyle: "single",
@@ -8436,7 +8778,7 @@ function BlockquoteView({
8436
8778
  flexDirection: "column",
8437
8779
  gap: 1
8438
8780
  },
8439
- block.children.map((child, i) => /* @__PURE__ */ React7.createElement(BlockView, { key: `q-${i}-${child.kind}`, block: child, citations }))
8781
+ block.children.map((child, i) => /* @__PURE__ */ React8.createElement(BlockView, { key: `q-${i}-${child.kind}`, block: child, citations }))
8440
8782
  );
8441
8783
  }
8442
8784
  function splitTableRow(line) {
@@ -8454,14 +8796,14 @@ function TableBlockRow({ block, citations }) {
8454
8796
  widths.push(Math.min(40, Math.max(3, ...cellLengths)));
8455
8797
  }
8456
8798
  const separator = widths.map((w) => "\u2500".repeat(w)).join("\u2500\u253C\u2500");
8457
- return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column" }, /* @__PURE__ */ React7.createElement(Box7, null, block.header.map((cell, ci) => (
8799
+ return /* @__PURE__ */ React8.createElement(Box7, { flexDirection: "column" }, /* @__PURE__ */ React8.createElement(Box7, null, block.header.map((cell, ci) => (
8458
8800
  // biome-ignore lint/suspicious/noArrayIndexKey: table columns never reorder — derived from a static header array
8459
- /* @__PURE__ */ React7.createElement(Text7, { key: `h-${ci}`, bold: true, color: "cyan" }, /* @__PURE__ */ React7.createElement(InlineMd, { text: cell, padTo: widths[ci] ?? 3, citations }), ci < colCount - 1 ? " \u2502 " : "")
8460
- ))), /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, separator), block.rows.map((row2, ri) => (
8801
+ /* @__PURE__ */ React8.createElement(Text7, { key: `h-${ci}`, bold: true, color: "cyan" }, /* @__PURE__ */ React8.createElement(InlineMd, { text: cell, padTo: widths[ci] ?? 3, citations }), ci < colCount - 1 ? " \u2502 " : "")
8802
+ ))), /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, separator), block.rows.map((row2, ri) => (
8461
8803
  // biome-ignore lint/suspicious/noArrayIndexKey: table rows render in source order and don't reorder
8462
- /* @__PURE__ */ React7.createElement(Box7, { key: `r-${ri}` }, Array.from({ length: colCount }).map((_, ci) => (
8804
+ /* @__PURE__ */ React8.createElement(Box7, { key: `r-${ri}` }, Array.from({ length: colCount }).map((_, ci) => (
8463
8805
  // biome-ignore lint/suspicious/noArrayIndexKey: same — column axis is fixed by the table shape
8464
- /* @__PURE__ */ React7.createElement(Text7, { key: `c-${ri}-${ci}` }, /* @__PURE__ */ React7.createElement(InlineMd, { text: row2[ci] ?? "", padTo: widths[ci] ?? 3, citations }), ci < colCount - 1 ? " \u2502 " : "")
8806
+ /* @__PURE__ */ React8.createElement(Text7, { key: `c-${ri}-${ci}` }, /* @__PURE__ */ React8.createElement(InlineMd, { text: row2[ci] ?? "", padTo: widths[ci] ?? 3, citations }), ci < colCount - 1 ? " \u2502 " : "")
8465
8807
  )))
8466
8808
  )));
8467
8809
  }
@@ -8481,7 +8823,7 @@ function EditBlockRow({ block }) {
8481
8823
  const isNewFile = block.search.length === 0;
8482
8824
  const searchLines = block.search.split("\n");
8483
8825
  const replaceLines = block.replace.split("\n");
8484
- return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { bold: true, color: "cyan" }, block.filename), isNewFile ? /* @__PURE__ */ React7.createElement(Text7, { color: "green", bold: true }, " (new file)") : null), isNewFile ? null : /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", marginTop: 1 }, searchLines.map((line, i) => /* @__PURE__ */ React7.createElement(Text7, { key: `s-${i}-${line.length}`, color: "red" }, `- ${line}`))), /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", marginTop: isNewFile ? 1 : 0 }, replaceLines.map((line, i) => /* @__PURE__ */ React7.createElement(Text7, { key: `r-${i}-${line.length}`, color: "green" }, `+ ${line}`))));
8826
+ return /* @__PURE__ */ React8.createElement(Box7, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React8.createElement(Box7, null, /* @__PURE__ */ React8.createElement(Text7, { bold: true, color: "cyan" }, block.filename), isNewFile ? /* @__PURE__ */ React8.createElement(Text7, { color: "green", bold: true }, " (new file)") : null), isNewFile ? null : /* @__PURE__ */ React8.createElement(Box7, { flexDirection: "column", marginTop: 1 }, searchLines.map((line, i) => /* @__PURE__ */ React8.createElement(Text7, { key: `s-${i}-${line.length}`, color: "red" }, `- ${line}`))), /* @__PURE__ */ React8.createElement(Box7, { flexDirection: "column", marginTop: isNewFile ? 1 : 0 }, replaceLines.map((line, i) => /* @__PURE__ */ React8.createElement(Text7, { key: `r-${i}-${line.length}`, color: "green" }, `+ ${line}`))));
8485
8827
  }
8486
8828
  var DIAGRAM_LANGS = /* @__PURE__ */ new Set([
8487
8829
  "mermaid",
@@ -8503,41 +8845,41 @@ var DIAGRAM_VIEWER_HINT = {
8503
8845
  };
8504
8846
  function DiagramCodeBlock({ lang, text }) {
8505
8847
  const hint = DIAGRAM_VIEWER_HINT[lang.toLowerCase()] ?? "\u2192 render with the matching viewer to view";
8506
- return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", borderStyle: "double", borderColor: "magenta", paddingX: 1 }, /* @__PURE__ */ React7.createElement(Text7, { bold: true, color: "magenta" }, `\u25C7 ${lang} diagram (source \u2014 terminal can't draw the graph)`), /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1 }, /* @__PURE__ */ React7.createElement(Text7, { color: "yellow" }, text)), /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1 }, /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, hint)));
8848
+ return /* @__PURE__ */ React8.createElement(Box7, { flexDirection: "column", borderStyle: "double", borderColor: "magenta", paddingX: 1 }, /* @__PURE__ */ React8.createElement(Text7, { bold: true, color: "magenta" }, `\u25C7 ${lang} diagram (source \u2014 terminal can't draw the graph)`), /* @__PURE__ */ React8.createElement(Box7, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(Text7, { color: "yellow" }, text)), /* @__PURE__ */ React8.createElement(Box7, { marginTop: 1 }, /* @__PURE__ */ React8.createElement(Text7, { dimColor: true }, hint)));
8507
8849
  }
8508
8850
  function Markdown({ text, projectRoot }) {
8509
8851
  const cleaned = expandAutolinks(expandEmoji(stripMath(text)));
8510
8852
  const root = projectRoot ?? process.cwd();
8511
- const citations = React7.useMemo(() => collectCitations(cleaned, root), [cleaned, root]);
8512
- const blocks = React7.useMemo(() => parseBlocks(cleaned), [cleaned]);
8853
+ const citations = React8.useMemo(() => collectCitations(cleaned, root), [cleaned, root]);
8854
+ const blocks = React8.useMemo(() => parseBlocks(cleaned), [cleaned]);
8513
8855
  const broken = [];
8514
8856
  for (const [url, status2] of citations) {
8515
8857
  if (!status2.ok) broken.push({ url, reason: status2.reason });
8516
8858
  }
8517
- return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", gap: 1 }, blocks.map((b, i) => /* @__PURE__ */ React7.createElement(BlockView, { key: `${i}-${b.kind}`, block: b, citations })), broken.length > 0 ? /* @__PURE__ */ React7.createElement(BrokenCitationsBlock, { items: broken }) : null);
8859
+ return /* @__PURE__ */ React8.createElement(Box7, { flexDirection: "column", gap: 1 }, blocks.map((b, i) => /* @__PURE__ */ React8.createElement(BlockView, { key: `${i}-${b.kind}`, block: b, citations })), broken.length > 0 ? /* @__PURE__ */ React8.createElement(BrokenCitationsBlock, { items: broken }) : null);
8518
8860
  }
8519
8861
  function BrokenCitationsBlock({ items }) {
8520
- return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", borderStyle: "round", borderColor: "red", paddingX: 1 }, /* @__PURE__ */ React7.createElement(Text7, { color: "red", bold: true }, `\u26A0 ${items.length} broken citation${items.length > 1 ? "s" : ""} \u2014 the model referenced paths or lines that don't exist`), items.map((b, i) => (
8862
+ return /* @__PURE__ */ React8.createElement(Box7, { flexDirection: "column", borderStyle: "round", borderColor: "red", paddingX: 1 }, /* @__PURE__ */ React8.createElement(Text7, { color: "red", bold: true }, `\u26A0 ${items.length} broken citation${items.length > 1 ? "s" : ""} \u2014 the model referenced paths or lines that don't exist`), items.map((b, i) => (
8521
8863
  // biome-ignore lint/suspicious/noArrayIndexKey: list is derived from a Map iteration order, stable per render
8522
- /* @__PURE__ */ React7.createElement(Text7, { key: `bc-${i}`, color: "red" }, ` \u2717 ${b.url} \u2192 ${b.reason}`)
8864
+ /* @__PURE__ */ React8.createElement(Text7, { key: `bc-${i}`, color: "red" }, ` \u2717 ${b.url} \u2192 ${b.reason}`)
8523
8865
  )));
8524
8866
  }
8525
8867
 
8526
8868
  // src/cli/ui/ticker.tsx
8527
- import React8, { createContext, useContext, useEffect, useState as useState3 } from "react";
8869
+ import React9, { createContext as createContext2, useContext as useContext2, useEffect as useEffect2, useState as useState3 } from "react";
8528
8870
  var TICK_MS = 120;
8529
- var TickContext = createContext(0);
8871
+ var TickContext = createContext2(0);
8530
8872
  function TickerProvider({ children, disabled }) {
8531
8873
  const [tick, setTick] = useState3(0);
8532
- useEffect(() => {
8874
+ useEffect2(() => {
8533
8875
  if (disabled) return;
8534
8876
  const id = setInterval(() => setTick((t) => t + 1), TICK_MS);
8535
8877
  return () => clearInterval(id);
8536
8878
  }, [disabled]);
8537
- return /* @__PURE__ */ React8.createElement(TickContext.Provider, { value: tick }, children);
8879
+ return /* @__PURE__ */ React9.createElement(TickContext.Provider, { value: tick }, children);
8538
8880
  }
8539
8881
  function useTick() {
8540
- return useContext(TickContext);
8882
+ return useContext2(TickContext);
8541
8883
  }
8542
8884
  function useElapsedSeconds() {
8543
8885
  const [start] = useState3(() => Date.now());
@@ -8606,7 +8948,8 @@ function summarizeStructured(content) {
8606
8948
  }
8607
8949
  }
8608
8950
  function summarizeKnownTool(toolName, content) {
8609
- if (toolName === "read_file") {
8951
+ const hasSuffix = (s) => toolName === s || toolName.endsWith(`_${s}`);
8952
+ if (hasSuffix("read_file")) {
8610
8953
  const lines = formatLineCount(content);
8611
8954
  const bytes = formatBytes(content.length);
8612
8955
  const head = clip(
@@ -8618,11 +8961,11 @@ function summarizeKnownTool(toolName, content) {
8618
8961
  isError: false
8619
8962
  };
8620
8963
  }
8621
- if (toolName === "list_directory" || toolName === "directory_tree") {
8964
+ if (hasSuffix("list_directory") || hasSuffix("directory_tree")) {
8622
8965
  const entries = content.split(/\r?\n/).filter((l) => l.trim()).length;
8623
8966
  return { summary: `${entries} entr${entries === 1 ? "y" : "ies"}`, isError: false };
8624
8967
  }
8625
- if (toolName === "search_files" || toolName === "search_content") {
8968
+ if (hasSuffix("search_files") || hasSuffix("search_content")) {
8626
8969
  const matches = content.split(/\r?\n/).filter((l) => l.trim()).length;
8627
8970
  if (matches === 0) return { summary: "no matches", isError: false };
8628
8971
  const first = firstNonEmptyLine(content);
@@ -8631,7 +8974,12 @@ function summarizeKnownTool(toolName, content) {
8631
8974
  isError: false
8632
8975
  };
8633
8976
  }
8634
- if (toolName === "run_command" || toolName === "run_background") {
8977
+ if (hasSuffix("write_file")) {
8978
+ const lines = formatLineCount(content);
8979
+ const bytes = formatBytes(content.length);
8980
+ return { summary: `wrote ${lines} \xB7 ${bytes}`, isError: false };
8981
+ }
8982
+ if (hasSuffix("run_command") || hasSuffix("run_background")) {
8635
8983
  const exitMatch = content.match(/exit (?:code )?(-?\d+)/i);
8636
8984
  const first = firstNonEmptyLine(content);
8637
8985
  if (exitMatch) {
@@ -8681,24 +9029,28 @@ function RoleGlyph({
8681
9029
  glyph,
8682
9030
  color
8683
9031
  }) {
8684
- return /* @__PURE__ */ React9.createElement(Text8, { color, bold: true }, glyph);
9032
+ return /* @__PURE__ */ React10.createElement(Text8, { color, bold: true }, glyph);
9033
+ }
9034
+ function indentContinuationLines(text) {
9035
+ if (!text.includes("\n")) return text;
9036
+ return text.split("\n").join("\n ");
8685
9037
  }
8686
- var EventRow = React9.memo(function EventRow2({
9038
+ var EventRow = React10.memo(function EventRow2({
8687
9039
  event,
8688
9040
  projectRoot
8689
9041
  }) {
8690
9042
  if (event.role === "user") {
8691
- return /* @__PURE__ */ React9.createElement(Box8, { marginTop: event.leadSeparator ? 1 : 0 }, /* @__PURE__ */ React9.createElement(RoleGlyph, { glyph: ROLE_GLYPH.user, color: "cyan" }), /* @__PURE__ */ React9.createElement(Text8, null, " ", event.text));
9043
+ return /* @__PURE__ */ React10.createElement(Box8, { flexDirection: "column" }, event.leadSeparator ? /* @__PURE__ */ React10.createElement(TurnSeparator, null) : null, /* @__PURE__ */ React10.createElement(Box8, null, /* @__PURE__ */ React10.createElement(RoleGlyph, { glyph: ROLE_GLYPH.user, color: "cyan" }), /* @__PURE__ */ React10.createElement(Text8, null, " ", indentContinuationLines(event.text))));
8692
9044
  }
8693
9045
  if (event.role === "assistant") {
8694
- if (event.streaming) return /* @__PURE__ */ React9.createElement(StreamingAssistant, { event });
8695
- return /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(RoleGlyph, { glyph: ROLE_GLYPH.assistant, color: "green" }), event.stats ? /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, ` ${event.stats.model}`) : null), /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", paddingLeft: 2, marginTop: 1 }, event.branch ? /* @__PURE__ */ React9.createElement(BranchBlock, { branch: event.branch }) : null, event.reasoning ? /* @__PURE__ */ React9.createElement(ReasoningBlock, { reasoning: event.reasoning }) : null, !isPlanStateEmpty(event.planState) ? /* @__PURE__ */ React9.createElement(PlanStateBlock, { planState: event.planState }) : null, event.text ? /* @__PURE__ */ React9.createElement(Markdown, { text: event.text, projectRoot }) : /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, "(no content)"), event.stats ? /* @__PURE__ */ React9.createElement(StatsLine, { stats: event.stats }) : null, event.repair ? /* @__PURE__ */ React9.createElement(Text8, { color: "magenta" }, event.repair) : null));
9046
+ if (event.streaming) return /* @__PURE__ */ React10.createElement(StreamingAssistant, { event });
9047
+ return /* @__PURE__ */ React10.createElement(Box8, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React10.createElement(Box8, null, /* @__PURE__ */ React10.createElement(RoleGlyph, { glyph: ROLE_GLYPH.assistant, color: "green" }), event.stats ? /* @__PURE__ */ React10.createElement(Text8, { dimColor: true }, ` ${event.stats.model}`) : null), /* @__PURE__ */ React10.createElement(Box8, { flexDirection: "column", paddingLeft: 2, marginTop: 1 }, event.branch ? /* @__PURE__ */ React10.createElement(BranchBlock, { branch: event.branch }) : null, event.reasoning ? /* @__PURE__ */ React10.createElement(ReasoningBlock, { reasoning: event.reasoning }) : null, !isPlanStateEmpty(event.planState) ? /* @__PURE__ */ React10.createElement(PlanStateBlock, { planState: event.planState }) : null, event.text ? /* @__PURE__ */ React10.createElement(Markdown, { text: event.text, projectRoot }) : /* @__PURE__ */ React10.createElement(Text8, { dimColor: true }, "(no content)"), event.stats ? /* @__PURE__ */ React10.createElement(StatsLine, { stats: event.stats }) : null, event.repair ? /* @__PURE__ */ React10.createElement(Text8, { color: "magenta" }, event.repair) : null));
8696
9048
  }
8697
9049
  if (event.role === "tool") {
8698
9050
  const isExplicitError = event.text.startsWith("ERROR:");
8699
9051
  const isEditFile = (event.toolName === "edit_file" || event.toolName?.endsWith("_edit_file")) && !isExplicitError;
8700
9052
  if (isEditFile) {
8701
- return /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(RoleGlyph, { glyph: ROLE_GLYPH.toolOk, color: "yellow" }), /* @__PURE__ */ React9.createElement(Text8, { color: "yellow", bold: true }, ` ${event.toolName ?? "?"}`), /* @__PURE__ */ React9.createElement(Text8, { color: "yellow", dimColor: true }, " \u2192")), /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", paddingLeft: 2, marginTop: 1 }, /* @__PURE__ */ React9.createElement(EditFileDiff, { text: event.text })));
9053
+ return /* @__PURE__ */ React10.createElement(Box8, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React10.createElement(Box8, null, /* @__PURE__ */ React10.createElement(RoleGlyph, { glyph: ROLE_GLYPH.toolOk, color: "yellow" }), /* @__PURE__ */ React10.createElement(Text8, { color: "yellow", bold: true }, ` ${event.toolName ?? "?"}`), /* @__PURE__ */ React10.createElement(Text8, { color: "yellow", dimColor: true }, " \u2192")), /* @__PURE__ */ React10.createElement(Box8, { flexDirection: "column", paddingLeft: 2, marginTop: 1 }, /* @__PURE__ */ React10.createElement(EditFileDiff, { text: event.text })));
8702
9054
  }
8703
9055
  const summary = summarizeToolResult(event.toolName ?? "?", event.text);
8704
9056
  const color = summary.isError ? "red" : "yellow";
@@ -8706,22 +9058,22 @@ var EventRow = React9.memo(function EventRow2({
8706
9058
  const marker = summary.isError ? "\u2717" : "\u2192";
8707
9059
  const durationLabel = event.durationMs !== void 0 && event.durationMs >= 100 ? ` (${formatDuration(event.durationMs)})` : "";
8708
9060
  const indexHint = event.toolIndex !== void 0 ? ` /tool ${event.toolIndex}` : "";
8709
- return /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(RoleGlyph, { glyph, color }), /* @__PURE__ */ React9.createElement(Text8, { color, bold: true }, ` ${event.toolName ?? "?"}`), durationLabel ? /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, durationLabel) : null, /* @__PURE__ */ React9.createElement(Text8, { color, dimColor: true }, ` ${marker} `), /* @__PURE__ */ React9.createElement(Text8, { color: summary.isError ? "red" : void 0, dimColor: !summary.isError }, summary.summary), indexHint ? /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, indexHint) : null);
9061
+ return /* @__PURE__ */ React10.createElement(Box8, null, /* @__PURE__ */ React10.createElement(RoleGlyph, { glyph, color }), /* @__PURE__ */ React10.createElement(Text8, { color, bold: true }, ` ${event.toolName ?? "?"}`), durationLabel ? /* @__PURE__ */ React10.createElement(Text8, { dimColor: true }, durationLabel) : null, /* @__PURE__ */ React10.createElement(Text8, { color, dimColor: true }, ` ${marker} `), /* @__PURE__ */ React10.createElement(Text8, { color: summary.isError ? "red" : void 0, dimColor: !summary.isError }, summary.summary), indexHint ? /* @__PURE__ */ React10.createElement(Text8, { dimColor: true }, indexHint) : null);
8710
9062
  }
8711
9063
  if (event.role === "error") {
8712
- return /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(RoleGlyph, { glyph: ROLE_GLYPH.error, color: "red" }), /* @__PURE__ */ React9.createElement(Text8, { color: "red" }, " ", event.text));
9064
+ return /* @__PURE__ */ React10.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(RoleGlyph, { glyph: ROLE_GLYPH.error, color: "red" }), /* @__PURE__ */ React10.createElement(Text8, { color: "red" }, " ", indentContinuationLines(event.text)));
8713
9065
  }
8714
9066
  if (event.role === "info") {
8715
- return /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, event.text));
9067
+ return /* @__PURE__ */ React10.createElement(Box8, null, /* @__PURE__ */ React10.createElement(Text8, { dimColor: true }, event.text));
8716
9068
  }
8717
9069
  if (event.role === "plan") {
8718
- return /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(Text8, { bold: true, color: "cyan" }, "\u{1F4CB} plan proposed \u2014 pick a choice below")), /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React9.createElement(Markdown, { text: event.text, projectRoot })));
9070
+ return /* @__PURE__ */ React10.createElement(Box8, { flexDirection: "column", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React10.createElement(Box8, null, /* @__PURE__ */ React10.createElement(Text8, { bold: true, color: "cyan" }, "\u{1F4CB} plan proposed \u2014 pick a choice below")), /* @__PURE__ */ React10.createElement(Box8, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Markdown, { text: event.text, projectRoot })));
8719
9071
  }
8720
9072
  if (event.role === "step-progress") {
8721
9073
  const sp = event.stepProgress;
8722
9074
  const counter = sp && sp.total > 0 ? ` (${sp.completed}/${sp.total})` : "";
8723
9075
  const label = sp?.title ? `${sp.stepId} \xB7 ${sp.title}` : sp?.stepId ?? "";
8724
- return /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(Text8, { color: "green", bold: true }, "\u2713"), /* @__PURE__ */ React9.createElement(Text8, { color: "green" }, ` ${label}`), /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, counter)), event.text ? /* @__PURE__ */ React9.createElement(Box8, { paddingLeft: 2 }, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, event.text)) : null, sp?.notes ? /* @__PURE__ */ React9.createElement(Box8, { paddingLeft: 2 }, /* @__PURE__ */ React9.createElement(Text8, { color: "yellow", dimColor: true }, `note: ${sp.notes}`)) : null);
9076
+ return /* @__PURE__ */ React10.createElement(Box8, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React10.createElement(Box8, null, /* @__PURE__ */ React10.createElement(Text8, { color: "green", bold: true }, "\u2713"), /* @__PURE__ */ React10.createElement(Text8, { color: "green" }, ` ${label}`), /* @__PURE__ */ React10.createElement(Text8, { dimColor: true }, counter)), event.text ? /* @__PURE__ */ React10.createElement(Box8, { paddingLeft: 2 }, /* @__PURE__ */ React10.createElement(Text8, { dimColor: true }, event.text)) : null, sp?.notes ? /* @__PURE__ */ React10.createElement(Box8, { paddingLeft: 2 }, /* @__PURE__ */ React10.createElement(Text8, { color: "yellow", dimColor: true }, `note: ${sp.notes}`)) : null);
8725
9077
  }
8726
9078
  if (event.role === "plan-resumed") {
8727
9079
  const rp = event.resumedPlan;
@@ -8736,7 +9088,7 @@ var EventRow = React9.memo(function EventRow2({
8736
9088
  ])
8737
9089
  );
8738
9090
  const nextStep = rp.steps.find((s) => !completedSet.has(s.id));
8739
- return /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column" }, /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(Text8, { bold: true, color: "cyan" }, "\u25B8 resumed plan"), /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, ` ${done}/${total} done \xB7 last touched ${rp.relativeTime}`)), rp.summary ? /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(Text8, { color: "cyan" }, ` ${rp.summary}`)) : null), /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React9.createElement(PlanStepList, { steps: rp.steps, statuses, focusStepId: nextStep?.id })));
9091
+ return /* @__PURE__ */ React10.createElement(Box8, { flexDirection: "column", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React10.createElement(Box8, { flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Box8, null, /* @__PURE__ */ React10.createElement(Text8, { bold: true, color: "cyan" }, "\u25B8 resumed plan"), /* @__PURE__ */ React10.createElement(Text8, { dimColor: true }, ` ${done}/${total} done \xB7 last touched ${rp.relativeTime}`)), rp.summary ? /* @__PURE__ */ React10.createElement(Box8, null, /* @__PURE__ */ React10.createElement(Text8, { color: "cyan" }, ` ${rp.summary}`)) : null), /* @__PURE__ */ React10.createElement(Box8, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(PlanStepList, { steps: rp.steps, statuses, focusStepId: nextStep?.id })));
8740
9092
  }
8741
9093
  if (event.role === "plan-replay") {
8742
9094
  const r = event.replayPlan;
@@ -8748,31 +9100,40 @@ var EventRow = React9.memo(function EventRow2({
8748
9100
  r.steps.map((s) => [s.id, completedSet.has(s.id) ? "done" : "pending"])
8749
9101
  );
8750
9102
  const navHint = r.total > 1 ? ` \xB7 ${r.index}/${r.total}` : "";
8751
- return /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", borderStyle: "round", borderColor: "gray", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column" }, /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(Text8, { bold: true, dimColor: true }, "\u23EA replay"), /* @__PURE__ */ React9.createElement(
9103
+ return /* @__PURE__ */ React10.createElement(Box8, { flexDirection: "column", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React10.createElement(Box8, { flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Box8, null, /* @__PURE__ */ React10.createElement(Text8, { bold: true, dimColor: true }, "\u23EA replay"), /* @__PURE__ */ React10.createElement(
8752
9104
  Text8,
8753
9105
  {
8754
9106
  dimColor: true
8755
9107
  },
8756
9108
  ` completed ${r.relativeTime} \xB7 ${done}/${total} done${navHint}`
8757
- )), r.summary ? /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, ` ${r.summary}`)) : null, /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, ` ${r.archiveBasename}`))), r.body ? /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React9.createElement(Markdown, { text: r.body, projectRoot })) : null, /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React9.createElement(PlanStepList, { steps: r.steps, statuses })), /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, r.total > 1 ? `(read-only \xB7 /replay ${r.index === 1 ? 2 : 1} for the ${r.index === 1 ? "next" : "newest"} archive)` : "(read-only \xB7 this is an archived plan)")));
9109
+ )), r.summary ? /* @__PURE__ */ React10.createElement(Box8, null, /* @__PURE__ */ React10.createElement(Text8, { dimColor: true }, ` ${r.summary}`)) : null, /* @__PURE__ */ React10.createElement(Box8, null, /* @__PURE__ */ React10.createElement(Text8, { dimColor: true }, ` ${r.archiveBasename}`))), r.body ? /* @__PURE__ */ React10.createElement(Box8, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Markdown, { text: r.body, projectRoot })) : null, /* @__PURE__ */ React10.createElement(Box8, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(PlanStepList, { steps: r.steps, statuses })), /* @__PURE__ */ React10.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React10.createElement(Text8, { dimColor: true }, r.total > 1 ? `(read-only \xB7 /replay ${r.index === 1 ? 2 : 1} for the ${r.index === 1 ? "next" : "newest"} archive)` : "(read-only \xB7 this is an archived plan)")));
8758
9110
  }
8759
9111
  if (event.role === "warning") {
8760
- return /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(RoleGlyph, { glyph: ROLE_GLYPH.warning, color: "yellow" }), /* @__PURE__ */ React9.createElement(Text8, { color: "yellow" }, " ", event.text));
9112
+ return /* @__PURE__ */ React10.createElement(Box8, null, /* @__PURE__ */ React10.createElement(RoleGlyph, { glyph: ROLE_GLYPH.warning, color: "yellow" }), /* @__PURE__ */ React10.createElement(Text8, { color: "yellow" }, " ", indentContinuationLines(event.text)));
8761
9113
  }
8762
- return /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(Text8, null, event.text));
9114
+ return /* @__PURE__ */ React10.createElement(Box8, null, /* @__PURE__ */ React10.createElement(Text8, null, event.text));
8763
9115
  });
9116
+ function TurnSeparator() {
9117
+ const { stdout: stdout2 } = useStdout2();
9118
+ const cols = stdout2?.columns ?? 80;
9119
+ const width = Math.max(16, cols - 2);
9120
+ const half = Math.floor((width - 3) / 2);
9121
+ const left = "\u2500".repeat(half);
9122
+ const right = "\u2500".repeat(width - half - 3);
9123
+ return /* @__PURE__ */ React10.createElement(Box8, { marginTop: 1, marginBottom: 1 }, /* @__PURE__ */ React10.createElement(Text8, { dimColor: true }, left), /* @__PURE__ */ React10.createElement(Text8, { color: "cyan", bold: true }, " \u25C6 "), /* @__PURE__ */ React10.createElement(Text8, { dimColor: true }, right));
9124
+ }
8764
9125
  function EditFileDiff({ text }) {
8765
9126
  const lines = text.split(/\r?\n/);
8766
9127
  const [statusHeader, hunkHeader, ...body] = lines;
8767
- return /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column" }, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, ` ${statusHeader ?? ""}`), hunkHeader !== void 0 ? /* @__PURE__ */ React9.createElement(Text8, { color: "cyan", bold: true }, hunkHeader) : null, body.map((line, i) => {
9128
+ return /* @__PURE__ */ React10.createElement(Box8, { flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Text8, { dimColor: true }, ` ${statusHeader ?? ""}`), hunkHeader !== void 0 ? /* @__PURE__ */ React10.createElement(Text8, { color: "cyan", bold: true }, hunkHeader) : null, body.map((line, i) => {
8768
9129
  const key = `${i}-${line.slice(0, 32)}`;
8769
9130
  if (line.startsWith("- ")) {
8770
- return /* @__PURE__ */ React9.createElement(Text8, { key, color: "red" }, line);
9131
+ return /* @__PURE__ */ React10.createElement(Text8, { key, color: "red" }, line);
8771
9132
  }
8772
9133
  if (line.startsWith("+ ")) {
8773
- return /* @__PURE__ */ React9.createElement(Text8, { key, color: "green" }, line);
9134
+ return /* @__PURE__ */ React10.createElement(Text8, { key, color: "green" }, line);
8774
9135
  }
8775
- return /* @__PURE__ */ React9.createElement(Text8, { key, dimColor: true }, line);
9136
+ return /* @__PURE__ */ React10.createElement(Text8, { key, dimColor: true }, line);
8776
9137
  }));
8777
9138
  }
8778
9139
  function BranchBlock({ branch: branch2 }) {
@@ -8781,33 +9142,33 @@ function BranchBlock({ branch: branch2 }) {
8781
9142
  const t = (branch2.temperatures[i] ?? 0).toFixed(1);
8782
9143
  return `${marker} #${i} T=${t} u=${u}`;
8783
9144
  }).join(" ");
8784
- return /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(Text8, { color: "blue" }, "\u2387 branched ", /* @__PURE__ */ React9.createElement(Text8, { bold: true }, branch2.budget), ` samples \u2192 picked #${branch2.chosenIndex} `, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, per)));
9145
+ return /* @__PURE__ */ React10.createElement(Box8, null, /* @__PURE__ */ React10.createElement(Text8, { color: "blue" }, "\u2387 branched ", /* @__PURE__ */ React10.createElement(Text8, { bold: true }, branch2.budget), ` samples \u2192 picked #${branch2.chosenIndex} `, /* @__PURE__ */ React10.createElement(Text8, { dimColor: true }, per)));
8785
9146
  }
8786
9147
  function ReasoningBlock({ reasoning }) {
8787
9148
  const max = 260;
8788
9149
  const flat = reasoning.replace(/\s+/g, " ").trim();
8789
9150
  const preview = flat.length <= max ? flat : `\u2026 (+${flat.length - max} earlier chars) ${flat.slice(-max)}`;
8790
- return /* @__PURE__ */ React9.createElement(Box8, { marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, "\u258F "), /* @__PURE__ */ React9.createElement(Text8, { dimColor: true, italic: true }, "thinking ", preview));
9151
+ return /* @__PURE__ */ React10.createElement(Box8, { marginBottom: 1 }, /* @__PURE__ */ React10.createElement(Text8, { dimColor: true }, "\u258F "), /* @__PURE__ */ React10.createElement(Text8, { dimColor: true, italic: true }, "thinking ", preview));
8791
9152
  }
8792
9153
  function Elapsed() {
8793
9154
  const s = useElapsedSeconds();
8794
9155
  const mm = String(Math.floor(s / 60)).padStart(2, "0");
8795
9156
  const ss = String(s % 60).padStart(2, "0");
8796
- return /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, `${mm}:${ss}`);
9157
+ return /* @__PURE__ */ React10.createElement(Text8, { dimColor: true }, `${mm}:${ss}`);
8797
9158
  }
8798
9159
  function PulsingAssistantGlyph() {
8799
9160
  const tick = useTick();
8800
9161
  const on = Math.floor(tick / 4) % 2 === 0;
8801
- return /* @__PURE__ */ React9.createElement(Text8, { color: "green", bold: true }, on ? ROLE_GLYPH.assistant : ROLE_GLYPH.assistantPulse);
9162
+ return /* @__PURE__ */ React10.createElement(Text8, { color: "green", bold: true }, on ? ROLE_GLYPH.assistant : ROLE_GLYPH.assistantPulse);
8802
9163
  }
8803
9164
  function StreamingAssistant({ event }) {
8804
9165
  if (event.branchProgress) {
8805
9166
  const p = event.branchProgress;
8806
9167
  if (p.completed === 0) {
8807
- return /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(PulsingAssistantGlyph, null), /* @__PURE__ */ React9.createElement(Text8, { color: "blue" }, " \u2387 launching ", p.total, " parallel samples (R1 thinking in parallel)\u2026 "), /* @__PURE__ */ React9.createElement(Elapsed, null)), /* @__PURE__ */ React9.createElement(Text8, { color: "yellow" }, " ", "spread across T=0.0/0.5/1.0 \xB7 reasoner typically takes 30-90s \u2014 this is normal"));
9168
+ return /* @__PURE__ */ React10.createElement(Box8, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React10.createElement(Box8, null, /* @__PURE__ */ React10.createElement(PulsingAssistantGlyph, null), /* @__PURE__ */ React10.createElement(Text8, { color: "blue" }, " \u2387 launching ", p.total, " parallel samples (R1 thinking in parallel)\u2026 "), /* @__PURE__ */ React10.createElement(Elapsed, null)), /* @__PURE__ */ React10.createElement(Text8, { color: "yellow" }, " ", "spread across T=0.0/0.5/1.0 \xB7 reasoner typically takes 30-90s \u2014 this is normal"));
8808
9169
  }
8809
9170
  const pct2 = Math.round(p.completed / p.total * 100);
8810
- return /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(PulsingAssistantGlyph, null), /* @__PURE__ */ React9.createElement(Text8, { color: "blue" }, " \u2387 branching ", p.completed, "/", p.total, " (", pct2, "%) "), /* @__PURE__ */ React9.createElement(Elapsed, null)), /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, " latest #", p.latestIndex, " T=", p.latestTemperature.toFixed(1), " u=", p.latestUncertainties, p.completed < p.total ? " \xB7 waiting for other samples\u2026" : " \xB7 selecting winner\u2026"));
9171
+ return /* @__PURE__ */ React10.createElement(Box8, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React10.createElement(Box8, null, /* @__PURE__ */ React10.createElement(PulsingAssistantGlyph, null), /* @__PURE__ */ React10.createElement(Text8, { color: "blue" }, " \u2387 branching ", p.completed, "/", p.total, " (", pct2, "%) "), /* @__PURE__ */ React10.createElement(Elapsed, null)), /* @__PURE__ */ React10.createElement(Text8, { dimColor: true }, " latest #", p.latestIndex, " T=", p.latestTemperature.toFixed(1), " u=", p.latestUncertainties, p.completed < p.total ? " \xB7 waiting for other samples\u2026" : " \xB7 selecting winner\u2026"));
8811
9172
  }
8812
9173
  const tail = lastLine(event.text, 140);
8813
9174
  const reasoningTail = event.reasoning ? lastLine(event.reasoning, 120) : "";
@@ -8837,16 +9198,16 @@ function StreamingAssistant({ event }) {
8837
9198
  label = parts.join(" \xB7 ");
8838
9199
  labelColor = "green";
8839
9200
  }
8840
- return /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(PulsingAssistantGlyph, null), /* @__PURE__ */ React9.createElement(Text8, null, " "), /* @__PURE__ */ React9.createElement(Pulse, null), /* @__PURE__ */ React9.createElement(Text8, { color: labelColor }, ` ${label} `), /* @__PURE__ */ React9.createElement(Elapsed, null)), reasoningTail ? /* @__PURE__ */ React9.createElement(Text8, { dimColor: true, italic: true }, "\u21B3 thinking: ", reasoningTail) : null, tail ? /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, "\u25B8 ", tail) : preFirstByte ? (
9201
+ return /* @__PURE__ */ React10.createElement(Box8, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React10.createElement(Box8, null, /* @__PURE__ */ React10.createElement(PulsingAssistantGlyph, null), /* @__PURE__ */ React10.createElement(Text8, null, " "), /* @__PURE__ */ React10.createElement(Pulse, null), /* @__PURE__ */ React10.createElement(Text8, { color: labelColor }, ` ${label} `), /* @__PURE__ */ React10.createElement(Elapsed, null)), reasoningTail ? /* @__PURE__ */ React10.createElement(Text8, { dimColor: true, italic: true }, "\u21B3 thinking: ", reasoningTail) : null, tail ? /* @__PURE__ */ React10.createElement(Text8, { dimColor: true }, "\u25B8 ", tail) : preFirstByte ? (
8841
9202
  // Non-dim yellow: first-time users misread the dim version as
8842
9203
  // "app frozen". The reassurance has to be VISIBLE to do its job.
8843
- /* @__PURE__ */ React9.createElement(Text8, { color: "yellow", italic: true }, " waiting for first byte \u2014 this is normal, typically 5-60s depending on model + load")
8844
- ) : reasoningOnly ? /* @__PURE__ */ React9.createElement(Text8, { color: "yellow", italic: true }, " R1 is thinking before it speaks \u2014 body text arrives when reasoning finishes (typically 20-90s, this is normal)") : toolCallOnly ? /* @__PURE__ */ React9.createElement(Text8, { color: "magenta", italic: true }, " tool-call arguments streaming \u2014 the model is about to dispatch a tool") : event.reasoning ? /* @__PURE__ */ React9.createElement(Text8, { color: "yellow", italic: true }, " R1 still reasoning \u2014 body text or tool call arrives when thinking finishes") : null);
9204
+ /* @__PURE__ */ React10.createElement(Text8, { color: "yellow", italic: true }, " waiting for first byte \u2014 this is normal, typically 5-60s depending on model + load")
9205
+ ) : reasoningOnly ? /* @__PURE__ */ React10.createElement(Text8, { color: "yellow", italic: true }, " R1 is thinking before it speaks \u2014 body text arrives when reasoning finishes (typically 20-90s, this is normal)") : toolCallOnly ? /* @__PURE__ */ React10.createElement(Text8, { color: "magenta", italic: true }, " tool-call arguments streaming \u2014 the model is about to dispatch a tool") : event.reasoning ? /* @__PURE__ */ React10.createElement(Text8, { color: "yellow", italic: true }, " R1 still reasoning \u2014 body text or tool call arrives when thinking finishes") : null);
8845
9206
  }
8846
9207
  function Pulse() {
8847
9208
  const tick = useTick();
8848
9209
  const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
8849
- return /* @__PURE__ */ React9.createElement(Text8, { color: "cyan" }, frames[Math.floor(tick / 4) % frames.length]);
9210
+ return /* @__PURE__ */ React10.createElement(Text8, { color: "cyan" }, frames[Math.floor(tick / 4) % frames.length]);
8850
9211
  }
8851
9212
  function formatToolCallIndex(tb) {
8852
9213
  if (!tb || tb.index === void 0) return "";
@@ -8865,17 +9226,17 @@ function lastLine(s, maxChars) {
8865
9226
  }
8866
9227
  function StatsLine({ stats: stats2 }) {
8867
9228
  const hit = (stats2.cacheHitRatio * 100).toFixed(1);
8868
- return /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, "\u258F "), /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, "cache ", hit, "% \xB7 tokens ", stats2.usage.promptTokens, " \u2192 ", stats2.usage.completionTokens, " \xB7 $", stats2.cost.toFixed(6)));
9229
+ return /* @__PURE__ */ React10.createElement(Box8, null, /* @__PURE__ */ React10.createElement(Text8, { dimColor: true }, "\u258F "), /* @__PURE__ */ React10.createElement(Text8, { dimColor: true }, "cache ", hit, "% \xB7 tokens ", stats2.usage.promptTokens, " \u2192 ", stats2.usage.completionTokens, " \xB7 $", stats2.cost.toFixed(6)));
8869
9230
  }
8870
9231
 
8871
9232
  // src/cli/ui/LiveRows.tsx
8872
9233
  import { Box as Box9, Text as Text9 } from "ink";
8873
- import React10 from "react";
9234
+ import React11 from "react";
8874
9235
  var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
8875
9236
  function StatusRow({ text }) {
8876
9237
  const tick = useTick();
8877
9238
  const elapsed = useElapsedSeconds();
8878
- return /* @__PURE__ */ React10.createElement(Box9, { marginY: 1 }, /* @__PURE__ */ React10.createElement(Text9, { color: "magenta" }, SPINNER_FRAMES[tick % SPINNER_FRAMES.length]), /* @__PURE__ */ React10.createElement(Text9, { color: "magenta" }, ` ${text}`), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, ` ${elapsed}s`));
9239
+ return /* @__PURE__ */ React11.createElement(Box9, { marginY: 1 }, /* @__PURE__ */ React11.createElement(Text9, { color: "magenta" }, SPINNER_FRAMES[tick % SPINNER_FRAMES.length]), /* @__PURE__ */ React11.createElement(Text9, { color: "magenta" }, ` ${text}`), /* @__PURE__ */ React11.createElement(Text9, { dimColor: true }, ` ${elapsed}s`));
8879
9240
  }
8880
9241
  function ModeStatusBar({
8881
9242
  editMode,
@@ -8887,15 +9248,14 @@ function ModeStatusBar({
8887
9248
  }) {
8888
9249
  useTick();
8889
9250
  const running = jobs2?.runningCount() ?? 0;
8890
- const jobsTag = running > 0 ? /* @__PURE__ */ React10.createElement(Text9, { color: "yellow", bold: true }, ` \xB7 \u23F5 ${running} job${running === 1 ? "" : "s"}`) : null;
9251
+ const jobsTag = running > 0 ? /* @__PURE__ */ React11.createElement(Text9, { color: "yellow", bold: true }, ` \xB7 \u23F5 ${running} job${running === 1 ? "" : "s"}`) : null;
8891
9252
  if (planMode) {
8892
- return /* @__PURE__ */ React10.createElement(Box9, { paddingX: 1 }, /* @__PURE__ */ React10.createElement(Text9, { color: "red", bold: true, inverse: flash }, "\u25B8 PLAN"), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, " writes gated \u2014 submit_plan + approval required \xB7 /plan off to leave"), jobsTag);
9253
+ return /* @__PURE__ */ React11.createElement(Box9, { paddingX: 1 }, /* @__PURE__ */ React11.createElement(Text9, { color: "red", bold: true, inverse: flash }, "plan mode"), /* @__PURE__ */ React11.createElement(Text9, { dimColor: true }, " writes gated \xB7 /plan off to leave"), jobsTag);
8893
9254
  }
8894
- const label = editMode === "auto" ? "AUTO" : "review";
8895
- const color = editMode === "auto" ? "magenta" : "cyan";
8896
- const mid = editMode === "auto" ? undoArmed ? "edits apply immediately \xB7 u = undo last batch" : "edits apply immediately \xB7 5s undo window after each batch" : pendingCount > 0 ? `${pendingCount} queued \xB7 y = /apply \xB7 n = /discard` : "edits queue for review \xB7 y = /apply \xB7 n = /discard";
8897
- const flip = editMode === "auto" ? "Shift+Tab \u2192 review" : "Shift+Tab \u2192 AUTO";
8898
- return /* @__PURE__ */ React10.createElement(Box9, { paddingX: 1 }, /* @__PURE__ */ React10.createElement(Text9, { color, bold: true, inverse: flash }, `\u25B8 ${label}`), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, ` ${mid} \xB7 ${flip}`), jobsTag);
9255
+ const label = editMode === "auto" ? "auto" : "review";
9256
+ const labelColor = editMode === "auto" ? "magenta" : "cyan";
9257
+ const mid = editMode === "auto" ? "edits land now \xB7 u to undo" : pendingCount > 0 ? `${pendingCount} queued \xB7 y apply \xB7 n discard` : "edits queued \xB7 y apply \xB7 n discard";
9258
+ return /* @__PURE__ */ React11.createElement(Box9, { paddingX: 1 }, /* @__PURE__ */ React11.createElement(Text9, { color: labelColor, bold: true, inverse: flash }, label), /* @__PURE__ */ React11.createElement(Text9, { dimColor: true }, ` ${mid} \xB7 Shift+Tab to flip`), jobsTag);
8899
9259
  }
8900
9260
  function UndoBanner({
8901
9261
  banner
@@ -8905,14 +9265,14 @@ function UndoBanner({
8905
9265
  const remainingSec = Math.ceil(remainingMs / 1e3);
8906
9266
  const ok = banner.results.filter((r) => r.status === "applied" || r.status === "created").length;
8907
9267
  const total = banner.results.length;
8908
- return /* @__PURE__ */ React10.createElement(Box9, { marginY: 1, borderStyle: "round", borderColor: "magenta", paddingX: 1 }, /* @__PURE__ */ React10.createElement(Text9, { color: "magenta", bold: true }, "\u2713 auto-applied "), /* @__PURE__ */ React10.createElement(Text9, { color: "magenta" }, `${ok}/${total} edit${total === 1 ? "" : "s"}`), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, " \xB7 press "), /* @__PURE__ */ React10.createElement(Text9, { color: "magenta", bold: true }, "u"), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, " to undo ("), /* @__PURE__ */ React10.createElement(Text9, { color: remainingSec <= 1 ? "red" : "magenta" }, `${remainingSec}s`), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, ")"));
9268
+ return /* @__PURE__ */ React11.createElement(Box9, { marginY: 1, paddingX: 1 }, /* @__PURE__ */ React11.createElement(Text9, { color: "magenta", bold: true }, "\u2713 auto-applied "), /* @__PURE__ */ React11.createElement(Text9, { color: "magenta" }, `${ok}/${total} edit${total === 1 ? "" : "s"}`), /* @__PURE__ */ React11.createElement(Text9, { dimColor: true }, " \xB7 press "), /* @__PURE__ */ React11.createElement(Text9, { color: "magenta", bold: true }, "u"), /* @__PURE__ */ React11.createElement(Text9, { dimColor: true }, " to undo ("), /* @__PURE__ */ React11.createElement(Text9, { color: remainingSec <= 1 ? "red" : "magenta" }, `${remainingSec}s`), /* @__PURE__ */ React11.createElement(Text9, { dimColor: true }, ")"));
8909
9269
  }
8910
9270
  function SubagentRow({
8911
9271
  activity
8912
9272
  }) {
8913
9273
  const tick = useTick();
8914
9274
  const seconds = (activity.elapsedMs / 1e3).toFixed(1);
8915
- return /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2 }, /* @__PURE__ */ React10.createElement(Text9, { color: "magenta" }, SPINNER_FRAMES[tick % SPINNER_FRAMES.length]), /* @__PURE__ */ React10.createElement(Text9, { color: "magenta" }, ` \u232C subagent: ${activity.task}`), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, ` \xB7 iter ${activity.iter} \xB7 ${seconds}s`));
9275
+ return /* @__PURE__ */ React11.createElement(Box9, { paddingLeft: 2 }, /* @__PURE__ */ React11.createElement(Text9, { color: "magenta" }, SPINNER_FRAMES[tick % SPINNER_FRAMES.length]), /* @__PURE__ */ React11.createElement(Text9, { color: "magenta" }, ` \u232C subagent: ${activity.task}`), /* @__PURE__ */ React11.createElement(Text9, { dimColor: true }, ` \xB7 iter ${activity.iter} \xB7 ${seconds}s`));
8916
9276
  }
8917
9277
  function OngoingToolRow({
8918
9278
  tool: tool2,
@@ -8921,7 +9281,7 @@ function OngoingToolRow({
8921
9281
  const tick = useTick();
8922
9282
  const elapsed = useElapsedSeconds();
8923
9283
  const summary = summarizeToolArgs(tool2.name, tool2.args);
8924
- return /* @__PURE__ */ React10.createElement(Box9, { marginY: 1, flexDirection: "column" }, /* @__PURE__ */ React10.createElement(Box9, null, /* @__PURE__ */ React10.createElement(Text9, { color: "cyan" }, SPINNER_FRAMES[tick % SPINNER_FRAMES.length]), /* @__PURE__ */ React10.createElement(Text9, { color: "yellow" }, ` tool<${tool2.name}> running\u2026`), /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, ` ${elapsed}s`)), progress ? /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2 }, /* @__PURE__ */ React10.createElement(Text9, { color: "cyan" }, renderProgressLine(progress))) : null, summary ? /* @__PURE__ */ React10.createElement(Box9, { paddingLeft: 2 }, /* @__PURE__ */ React10.createElement(Text9, { dimColor: true }, summary)) : null);
9284
+ return /* @__PURE__ */ React11.createElement(Box9, { marginY: 1, flexDirection: "column" }, /* @__PURE__ */ React11.createElement(Box9, null, /* @__PURE__ */ React11.createElement(Text9, { color: "cyan" }, SPINNER_FRAMES[tick % SPINNER_FRAMES.length]), /* @__PURE__ */ React11.createElement(Text9, { color: "yellow" }, ` tool<${tool2.name}> running\u2026`), /* @__PURE__ */ React11.createElement(Text9, { dimColor: true }, ` ${elapsed}s`)), progress ? /* @__PURE__ */ React11.createElement(Box9, { paddingLeft: 2 }, /* @__PURE__ */ React11.createElement(Text9, { color: "cyan" }, renderProgressLine(progress))) : null, summary ? /* @__PURE__ */ React11.createElement(Box9, { paddingLeft: 2 }, /* @__PURE__ */ React11.createElement(Text9, { dimColor: true }, summary)) : null);
8925
9285
  }
8926
9286
  function renderProgressLine(p) {
8927
9287
  const msg = p.message ? ` ${p.message}` : "";
@@ -8978,7 +9338,7 @@ function summarizeToolArgs(name, args) {
8978
9338
 
8979
9339
  // src/cli/ui/PlanCheckpointConfirm.tsx
8980
9340
  import { Box as Box10, Text as Text10 } from "ink";
8981
- import React11 from "react";
9341
+ import React12 from "react";
8982
9342
  function PlanCheckpointConfirmInner({
8983
9343
  stepId,
8984
9344
  title,
@@ -8992,7 +9352,7 @@ function PlanCheckpointConfirmInner({
8992
9352
  const counter = total > 0 ? ` (${completed}/${total})` : "";
8993
9353
  const isLast = total > 0 && completed >= total;
8994
9354
  const statuses = buildStatusMap(steps, completedStepIds, stepId, isLast);
8995
- return /* @__PURE__ */ React11.createElement(Box10, { flexDirection: "column", borderStyle: "round", borderColor: "green", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React11.createElement(Box10, null, /* @__PURE__ */ React11.createElement(Text10, { bold: true, color: "green" }, "\u25B8 checkpoint \u2014 step done"), /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, ` ${label}${counter}`)), steps && steps.length > 0 ? /* @__PURE__ */ React11.createElement(Box10, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React11.createElement(PlanStepList, { steps, statuses, focusStepId: stepId })) : null, /* @__PURE__ */ React11.createElement(Box10, { marginTop: 1 }, /* @__PURE__ */ React11.createElement(
9355
+ return /* @__PURE__ */ React12.createElement(Box10, { flexDirection: "column", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React12.createElement(Box10, null, /* @__PURE__ */ React12.createElement(Text10, { bold: true, color: "green" }, "\u25B8 checkpoint \u2014 step done"), /* @__PURE__ */ React12.createElement(Text10, { dimColor: true }, ` ${label}${counter}`)), steps && steps.length > 0 ? /* @__PURE__ */ React12.createElement(Box10, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React12.createElement(PlanStepList, { steps, statuses, focusStepId: stepId })) : null, /* @__PURE__ */ React12.createElement(Box10, { marginTop: 1 }, /* @__PURE__ */ React12.createElement(
8996
9356
  SingleSelect,
8997
9357
  {
8998
9358
  initialValue: isLast ? "stop" : "continue",
@@ -9019,7 +9379,7 @@ function PlanCheckpointConfirmInner({
9019
9379
  }
9020
9380
  )));
9021
9381
  }
9022
- var PlanCheckpointConfirm = React11.memo(PlanCheckpointConfirmInner);
9382
+ var PlanCheckpointConfirm = React12.memo(PlanCheckpointConfirmInner);
9023
9383
  function buildStatusMap(steps, completedStepIds, currentStepId, isLast) {
9024
9384
  const map = /* @__PURE__ */ new Map();
9025
9385
  if (!steps) return map;
@@ -9037,10 +9397,10 @@ function buildStatusMap(steps, completedStepIds, currentStepId, isLast) {
9037
9397
 
9038
9398
  // src/cli/ui/PlanConfirm.tsx
9039
9399
  import { Box as Box11, Text as Text11 } from "ink";
9040
- import React12 from "react";
9400
+ import React13 from "react";
9041
9401
  function PlanConfirmInner({ plan: plan2, steps, summary, onChoose }) {
9042
9402
  const hasOpenQuestions = /^#{1,6}\s*(open[-\s]?questions?|risks?|unknowns?|assumptions?|unclear)/im.test(plan2) || /^#{1,6}\s*(待确认|开放问题|风险|未知|假设|不确定)/im.test(plan2);
9043
- return /* @__PURE__ */ React12.createElement(Box11, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React12.createElement(Box11, { flexDirection: "column" }, /* @__PURE__ */ React12.createElement(Box11, null, /* @__PURE__ */ React12.createElement(Text11, { bold: true, color: "cyan" }, "\u25B8 plan proposed (full text above) \u2014 approve / refine / cancel")), summary ? /* @__PURE__ */ React12.createElement(Box11, null, /* @__PURE__ */ React12.createElement(Text11, { color: "cyan" }, ` ${summary}`)) : null), hasOpenQuestions ? /* @__PURE__ */ React12.createElement(Box11, { marginTop: 1 }, /* @__PURE__ */ React12.createElement(Text11, { color: "yellow" }, "\u25B2 the plan flags open questions or risks \u2014 pick", " ", /* @__PURE__ */ React12.createElement(Text11, { bold: true }, "Refine / answer questions"), " to write concrete answers before the model moves on.")) : null, steps && steps.length > 0 ? /* @__PURE__ */ React12.createElement(Box11, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React12.createElement(PlanStepList, { steps })) : null, /* @__PURE__ */ React12.createElement(Box11, { marginTop: 1 }, /* @__PURE__ */ React12.createElement(
9403
+ return /* @__PURE__ */ React13.createElement(Box11, { flexDirection: "column", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React13.createElement(Box11, { flexDirection: "column" }, /* @__PURE__ */ React13.createElement(Box11, null, /* @__PURE__ */ React13.createElement(Text11, { bold: true, color: "cyan" }, "\u25B8 plan proposed (full text above) \u2014 approve / refine / cancel")), summary ? /* @__PURE__ */ React13.createElement(Box11, null, /* @__PURE__ */ React13.createElement(Text11, { color: "cyan" }, ` ${summary}`)) : null), hasOpenQuestions ? /* @__PURE__ */ React13.createElement(Box11, { marginTop: 1 }, /* @__PURE__ */ React13.createElement(Text11, { color: "yellow" }, "\u25B2 the plan flags open questions or risks \u2014 pick", " ", /* @__PURE__ */ React13.createElement(Text11, { bold: true }, "Refine / answer questions"), " to write concrete answers before the model moves on.")) : null, steps && steps.length > 0 ? /* @__PURE__ */ React13.createElement(Box11, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React13.createElement(PlanStepList, { steps })) : null, /* @__PURE__ */ React13.createElement(Box11, { marginTop: 1 }, /* @__PURE__ */ React13.createElement(
9044
9404
  SingleSelect,
9045
9405
  {
9046
9406
  initialValue: hasOpenQuestions ? "refine" : "approve",
@@ -9067,39 +9427,43 @@ function PlanConfirmInner({ plan: plan2, steps, summary, onChoose }) {
9067
9427
  }
9068
9428
  )));
9069
9429
  }
9070
- var PlanConfirm = React12.memo(PlanConfirmInner);
9430
+ var PlanConfirm = React13.memo(PlanConfirmInner);
9071
9431
 
9072
9432
  // src/cli/ui/PlanRefineInput.tsx
9073
- import { Box as Box12, Text as Text12, useInput as useInput3 } from "ink";
9074
- import React13, { useState as useState4 } from "react";
9433
+ import { Box as Box12, Text as Text12 } from "ink";
9434
+ import React14, { useState as useState4 } from "react";
9075
9435
  function PlanRefineInput({ mode: mode2, onSubmit, onCancel }) {
9076
9436
  const [value, setValue] = useState4("");
9077
- useInput3((input, key) => {
9078
- if (key.escape) {
9437
+ useKeystroke((ev) => {
9438
+ if (ev.paste) {
9439
+ setValue((v) => v + ev.input.replace(/\r?\n/g, " "));
9440
+ return;
9441
+ }
9442
+ if (ev.escape) {
9079
9443
  onCancel();
9080
9444
  return;
9081
9445
  }
9082
- if (key.return) {
9446
+ if (ev.return) {
9083
9447
  onSubmit(value.trim());
9084
9448
  return;
9085
9449
  }
9086
- if (key.backspace || key.delete) {
9450
+ if (ev.backspace || ev.delete) {
9087
9451
  setValue((v) => v.slice(0, -1));
9088
9452
  return;
9089
9453
  }
9090
- if (input && !key.ctrl && !key.meta) {
9091
- setValue((v) => v + input);
9454
+ if (ev.input && !ev.ctrl && !ev.meta) {
9455
+ setValue((v) => v + ev.input);
9092
9456
  }
9093
9457
  });
9094
9458
  const title = mode2 === "approve" ? "\u25B8 approving \u2014 any last instructions or answers to open questions?" : mode2 === "checkpoint-revise" ? "\u25B8 revising \u2014 what should change before the next step?" : mode2 === "choice-custom" ? "\u25B8 custom answer \u2014 type whatever fits" : "\u25B8 refining \u2014 what should the model change?";
9095
9459
  const hint = mode2 === "approve" ? "Answer questions the plan raised, add constraints, or just press Enter to approve as-is." : mode2 === "checkpoint-revise" ? "Scope change, skip steps, alternative approach \u2014 the model will adjust the remaining plan based on this." : mode2 === "choice-custom" ? "Free-form reply. The model reads it verbatim and proceeds \u2014 no need to match the listed options." : "Describe what's wrong or missing, or answer questions the plan raised.";
9096
9460
  const blankHint = mode2 === "approve" ? " (Enter with blank = approve without extra instructions.)" : mode2 === "checkpoint-revise" ? " (Enter with blank = continue with the current plan.)" : mode2 === "choice-custom" ? " (Enter with blank = ask the model what you actually want.)" : " (Enter with blank = ask the model to list concrete questions.)";
9097
- return /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React13.createElement(Box12, null, /* @__PURE__ */ React13.createElement(Text12, { bold: true, color: "yellow" }, title)), /* @__PURE__ */ React13.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, hint, " Enter to send \xB7 Esc to return to the picker.", value === "" ? blankHint : "")), /* @__PURE__ */ React13.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React13.createElement(Text12, null, /* @__PURE__ */ React13.createElement(Text12, { color: "yellow" }, "\u203A "), /* @__PURE__ */ React13.createElement(Text12, null, value || " "), /* @__PURE__ */ React13.createElement(Text12, { color: "yellow" }, "\u258D"))));
9461
+ return /* @__PURE__ */ React14.createElement(Box12, { flexDirection: "column", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React14.createElement(Box12, null, /* @__PURE__ */ React14.createElement(Text12, { bold: true, color: "yellow" }, title)), /* @__PURE__ */ React14.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React14.createElement(Text12, { dimColor: true }, hint, " Enter to send \xB7 Esc to return to the picker.", value === "" ? blankHint : "")), /* @__PURE__ */ React14.createElement(Box12, { marginTop: 1 }, /* @__PURE__ */ React14.createElement(Text12, null, /* @__PURE__ */ React14.createElement(Text12, { color: "yellow" }, "\u203A "), /* @__PURE__ */ React14.createElement(Text12, null, value || " "), /* @__PURE__ */ React14.createElement(Text12, { color: "yellow" }, "\u258D"))));
9098
9462
  }
9099
9463
 
9100
9464
  // src/cli/ui/PlanReviseConfirm.tsx
9101
9465
  import { Box as Box13, Text as Text13 } from "ink";
9102
- import React14 from "react";
9466
+ import React15 from "react";
9103
9467
  function computeDiff(oldSteps, newSteps) {
9104
9468
  const oldIds = new Set(oldSteps.map((s) => s.id));
9105
9469
  const newIds = new Set(newSteps.map((s) => s.id));
@@ -9135,14 +9499,14 @@ function PlanReviseConfirmInner({
9135
9499
  const removedCount = rows.filter((r) => r.kind === "removed").length;
9136
9500
  const addedCount = rows.filter((r) => r.kind === "added").length;
9137
9501
  const keptCount = rows.filter((r) => r.kind === "kept").length;
9138
- return /* @__PURE__ */ React14.createElement(Box13, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React14.createElement(Box13, null, /* @__PURE__ */ React14.createElement(Text13, { bold: true, color: "yellow" }, "\u270F plan revision proposed"), /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, ` \u2212${removedCount} +${addedCount} (${keptCount} kept)`)), /* @__PURE__ */ React14.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React14.createElement(Text13, null, reason)), summary ? /* @__PURE__ */ React14.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, `updated summary: ${summary}`)) : null, /* @__PURE__ */ React14.createElement(Box13, { marginTop: 1, flexDirection: "column" }, rows.map((row2) => {
9502
+ return /* @__PURE__ */ React15.createElement(Box13, { flexDirection: "column", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React15.createElement(Box13, null, /* @__PURE__ */ React15.createElement(Text13, { bold: true, color: "yellow" }, "\u270F plan revision proposed"), /* @__PURE__ */ React15.createElement(Text13, { dimColor: true }, ` \u2212${removedCount} +${addedCount} (${keptCount} kept)`)), /* @__PURE__ */ React15.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React15.createElement(Text13, null, reason)), summary ? /* @__PURE__ */ React15.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React15.createElement(Text13, { dimColor: true }, `updated summary: ${summary}`)) : null, /* @__PURE__ */ React15.createElement(Box13, { marginTop: 1, flexDirection: "column" }, rows.map((row2) => {
9139
9503
  const risk = riskDots2(row2.step.risk);
9140
9504
  const prefix = row2.kind === "removed" ? "\u2212" : row2.kind === "added" ? "+" : " ";
9141
9505
  const prefixColor = row2.kind === "removed" ? "red" : row2.kind === "added" ? "green" : "gray";
9142
9506
  const dim = row2.kind === "kept";
9143
9507
  const strike = row2.kind === "removed";
9144
- return /* @__PURE__ */ React14.createElement(Box13, { key: `${row2.kind}-${row2.step.id}` }, /* @__PURE__ */ React14.createElement(Text13, { color: prefixColor, bold: true }, `${prefix} `), /* @__PURE__ */ React14.createElement(Text13, { color: risk.color, bold: true, dimColor: dim }, risk.dots), /* @__PURE__ */ React14.createElement(Text13, { dimColor: dim, strikethrough: strike }, ` ${row2.step.id} \xB7 ${row2.step.title}`));
9145
- })), /* @__PURE__ */ React14.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React14.createElement(
9508
+ return /* @__PURE__ */ React15.createElement(Box13, { key: `${row2.kind}-${row2.step.id}` }, /* @__PURE__ */ React15.createElement(Text13, { color: prefixColor, bold: true }, `${prefix} `), /* @__PURE__ */ React15.createElement(Text13, { color: risk.color, bold: true, dimColor: dim }, risk.dots), /* @__PURE__ */ React15.createElement(Text13, { dimColor: dim, strikethrough: strike }, ` ${row2.step.id} \xB7 ${row2.step.title}`));
9509
+ })), /* @__PURE__ */ React15.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React15.createElement(
9146
9510
  SingleSelect,
9147
9511
  {
9148
9512
  initialValue: "accept",
@@ -9164,25 +9528,62 @@ function PlanReviseConfirmInner({
9164
9528
  }
9165
9529
  )));
9166
9530
  }
9167
- var PlanReviseConfirm = React14.memo(PlanReviseConfirmInner);
9531
+ var PlanReviseConfirm = React15.memo(PlanReviseConfirmInner);
9168
9532
 
9169
9533
  // src/cli/ui/PromptInput.tsx
9170
- import { Box as Box14, Text as Text14, useInput as useInput4 } from "ink";
9171
- import React15, { useRef, useState as useState5 } from "react";
9534
+ import { Box as Box14, Text as Text14, useStdout as useStdout3 } from "ink";
9535
+ import React16, { useRef as useRef2, useState as useState5 } from "react";
9536
+
9537
+ // src/cli/ui/key-normalize.ts
9538
+ var CSI_TAIL_TO_FLAGS = [
9539
+ // Arrow keys — the most common ConPTY victim.
9540
+ { tail: "[A", flags: { upArrow: true } },
9541
+ { tail: "[B", flags: { downArrow: true } },
9542
+ { tail: "[C", flags: { rightArrow: true } },
9543
+ { tail: "[D", flags: { leftArrow: true } },
9544
+ // Page navigation.
9545
+ { tail: "[5~", flags: { pageUp: true } },
9546
+ { tail: "[6~", flags: { pageDown: true } },
9547
+ // Forward-delete (the key labelled Delete on most keyboards).
9548
+ { tail: "[3~", flags: { delete: true } },
9549
+ // Shift+Tab — terminal sends `\x1b[Z` rather than tab-with-shift.
9550
+ { tail: "[Z", flags: { shift: true, tab: true } }
9551
+ ];
9552
+ function alreadyStructured(flags) {
9553
+ return Boolean(
9554
+ flags.upArrow || flags.downArrow || flags.leftArrow || flags.rightArrow || flags.pageUp || flags.pageDown || flags.delete || flags.tab && flags.shift
9555
+ );
9556
+ }
9557
+ function recoverCsiTail(input, existing = {}) {
9558
+ if (alreadyStructured(existing)) return null;
9559
+ for (const entry of CSI_TAIL_TO_FLAGS) {
9560
+ if (input === entry.tail || input === `\x1B${entry.tail}`) {
9561
+ return entry.flags;
9562
+ }
9563
+ }
9564
+ return null;
9565
+ }
9566
+ var STRIPPABLE_CSI_FRAGMENTS = [
9567
+ "\x1B[200~",
9568
+ "\x1B[201~",
9569
+ "[200~",
9570
+ "[201~",
9571
+ ...CSI_TAIL_TO_FLAGS.flatMap((e) => [`\x1B${e.tail}`, e.tail])
9572
+ ];
9573
+ function stripCsiFragments(input) {
9574
+ let out = input;
9575
+ for (const frag of STRIPPABLE_CSI_FRAGMENTS) {
9576
+ if (out.includes(frag)) out = out.replaceAll(frag, "");
9577
+ }
9578
+ return out;
9579
+ }
9172
9580
 
9173
9581
  // src/cli/ui/multiline-keys.ts
9174
9582
  var BACKSLASH_SUFFIX = /\\$/;
9175
9583
  var NOOP = { next: null, cursor: null, submit: false };
9176
- function rewriteRawArrowEscape(key) {
9177
- if (key.upArrow || key.downArrow || key.leftArrow || key.rightArrow) return key;
9178
- if (key.input === "\x1B[A") return { ...key, upArrow: true, input: "" };
9179
- if (key.input === "\x1B[B") return { ...key, downArrow: true, input: "" };
9180
- if (key.input === "\x1B[C") return { ...key, rightArrow: true, input: "" };
9181
- if (key.input === "\x1B[D") return { ...key, leftArrow: true, input: "" };
9182
- return key;
9183
- }
9184
9584
  function processMultilineKey(value, cursor, keyIn) {
9185
- const key = rewriteRawArrowEscape(keyIn);
9585
+ const recovered = recoverCsiTail(keyIn.input, keyIn);
9586
+ const key = recovered ? { ...keyIn, ...recovered, input: "" } : keyIn;
9186
9587
  if (key.tab || key.escape) {
9187
9588
  return NOOP;
9188
9589
  }
@@ -9229,7 +9630,7 @@ function processMultilineKey(value, cursor, keyIn) {
9229
9630
  submit: false
9230
9631
  };
9231
9632
  }
9232
- const stripped = key.input.replaceAll("\x1B[200~", "").replaceAll("\x1B[201~", "");
9633
+ const stripped = stripCsiFragments(key.input);
9233
9634
  const looksLikePaste = stripped.length > 1 && (stripped.includes("\n") || stripped.includes("\r"));
9234
9635
  if (looksLikePaste) {
9235
9636
  const normalized = stripped.replace(/\r\n?/g, "\n");
@@ -9373,10 +9774,159 @@ function formatBytesShort(n) {
9373
9774
  return `${(n / (1024 * 1024)).toFixed(1)}MB`;
9374
9775
  }
9375
9776
 
9777
+ // src/cli/ui/prompt-viewport.ts
9778
+ function charCells(ch) {
9779
+ if (ch.length === 0) return 0;
9780
+ const code = ch.charCodeAt(0);
9781
+ if (code < 32 || code === 127) return 0;
9782
+ if (code < 4352) return 1;
9783
+ if (code >= 4352 && code <= 4447) return 2;
9784
+ if (code >= 11904 && code <= 12350) return 2;
9785
+ if (code >= 12353 && code <= 13311) return 2;
9786
+ if (code >= 13312 && code <= 19903) return 2;
9787
+ if (code >= 19968 && code <= 40959) return 2;
9788
+ if (code >= 40960 && code <= 42191) return 2;
9789
+ if (code >= 44032 && code <= 55203) return 2;
9790
+ if (code >= 63744 && code <= 64255) return 2;
9791
+ if (code >= 65072 && code <= 65103) return 2;
9792
+ if (code >= 65280 && code <= 65376) return 2;
9793
+ if (code >= 65504 && code <= 65510) return 2;
9794
+ return 1;
9795
+ }
9796
+ function stringCells(s, pastes) {
9797
+ let n = 0;
9798
+ for (let i = 0; i < s.length; i++) {
9799
+ const ch = s[i];
9800
+ const id = decodePasteSentinel(ch);
9801
+ if (id !== null) {
9802
+ n += pasteSentinelCells(id, pastes);
9803
+ } else {
9804
+ n += charCells(ch);
9805
+ }
9806
+ }
9807
+ return n;
9808
+ }
9809
+ function pasteSentinelLabel(id, entry) {
9810
+ if (!entry) return `[paste #${id + 1} \xB7 (missing)]`;
9811
+ return `[paste #${id + 1} \xB7 ${entry.lineCount}l \xB7 ${formatBytesShort(entry.charCount)}]`;
9812
+ }
9813
+ function pasteSentinelCells(id, pastes) {
9814
+ const entry = pastes?.get(id);
9815
+ return pasteSentinelLabel(id, entry).length;
9816
+ }
9817
+ function buildViewport(line, cursorCol, visibleCells, pastes) {
9818
+ if (visibleCells <= 0) {
9819
+ return {
9820
+ segments: [],
9821
+ cursorCell: cursorCol === null ? null : 0,
9822
+ hiddenLeft: false,
9823
+ hiddenRight: line.length > 0
9824
+ };
9825
+ }
9826
+ const totalCells = stringCells(line, pastes);
9827
+ if (totalCells <= visibleCells) {
9828
+ const segments = textToSegments(line, pastes);
9829
+ let cursorCell = null;
9830
+ if (cursorCol !== null) {
9831
+ cursorCell = stringCells(line.slice(0, cursorCol), pastes);
9832
+ }
9833
+ return { segments, cursorCell, hiddenLeft: false, hiddenRight: false };
9834
+ }
9835
+ if (cursorCol === null) {
9836
+ return clipFromLeft(line, visibleCells, pastes);
9837
+ }
9838
+ return clipAroundCursor(line, cursorCol, visibleCells, pastes);
9839
+ }
9840
+ function clipFromLeft(line, visibleCells, pastes) {
9841
+ const budget = Math.max(1, visibleCells - 1);
9842
+ let used = 0;
9843
+ let end = 0;
9844
+ while (end < line.length) {
9845
+ const ch = line[end];
9846
+ const cw = charCellsAt(line, end, pastes);
9847
+ if (used + cw > budget) break;
9848
+ used += cw;
9849
+ end++;
9850
+ }
9851
+ const segments = textToSegments(line.slice(0, end), pastes);
9852
+ return { segments, cursorCell: null, hiddenLeft: false, hiddenRight: end < line.length };
9853
+ }
9854
+ function clipAroundCursor(line, cursorCol, visibleCells, pastes) {
9855
+ let budget = visibleCells;
9856
+ const reservedForMarkers = 2;
9857
+ budget = Math.max(1, budget - reservedForMarkers);
9858
+ const halfBudget = Math.floor(budget / 2);
9859
+ let start = cursorCol;
9860
+ let leftCells = 0;
9861
+ while (start > 0 && leftCells < halfBudget) {
9862
+ const cw = charCellsAt(line, start - 1, pastes);
9863
+ if (leftCells + cw > halfBudget) break;
9864
+ start--;
9865
+ leftCells += cw;
9866
+ }
9867
+ const rightBudget = budget - leftCells;
9868
+ let end = cursorCol;
9869
+ let rightCells = 0;
9870
+ const cursorChar = cursorCol < line.length ? charCellsAt(line, cursorCol, pastes) : 1;
9871
+ if (rightBudget >= cursorChar) {
9872
+ if (cursorCol < line.length) end = cursorCol + 1;
9873
+ rightCells = cursorChar;
9874
+ while (end < line.length && rightCells < rightBudget) {
9875
+ const cw = charCellsAt(line, end, pastes);
9876
+ if (rightCells + cw > rightBudget) break;
9877
+ rightCells += cw;
9878
+ end++;
9879
+ }
9880
+ }
9881
+ let extraLeftBudget = rightBudget - rightCells;
9882
+ while (start > 0 && extraLeftBudget > 0) {
9883
+ const cw = charCellsAt(line, start - 1, pastes);
9884
+ if (cw > extraLeftBudget) break;
9885
+ start--;
9886
+ leftCells += cw;
9887
+ extraLeftBudget -= cw;
9888
+ }
9889
+ const hiddenLeft = start > 0;
9890
+ const hiddenRight = end < line.length;
9891
+ const segments = textToSegments(line.slice(start, end), pastes);
9892
+ const cursorCell = stringCells(line.slice(start, cursorCol), pastes);
9893
+ return { segments, cursorCell, hiddenLeft, hiddenRight };
9894
+ }
9895
+ function charCellsAt(line, idx, pastes) {
9896
+ const ch = line[idx];
9897
+ const id = decodePasteSentinel(ch);
9898
+ if (id !== null) {
9899
+ const entry = pastes?.get(id);
9900
+ return pasteSentinelLabel(id, entry).length;
9901
+ }
9902
+ return charCells(ch);
9903
+ }
9904
+ function textToSegments(line, pastes) {
9905
+ const out = [];
9906
+ let buf = "";
9907
+ const flushBuf = () => {
9908
+ if (buf.length > 0) {
9909
+ out.push({ kind: "text", text: buf });
9910
+ buf = "";
9911
+ }
9912
+ };
9913
+ for (let i = 0; i < line.length; i++) {
9914
+ const ch = line[i];
9915
+ const id = decodePasteSentinel(ch);
9916
+ if (id !== null) {
9917
+ flushBuf();
9918
+ const label = pasteSentinelLabel(id, pastes?.get(id));
9919
+ out.push({ kind: "paste", id, label });
9920
+ } else {
9921
+ buf += ch;
9922
+ }
9923
+ }
9924
+ flushBuf();
9925
+ return out;
9926
+ }
9927
+
9376
9928
  // src/cli/ui/PromptInput.tsx
9377
- var PASTE_START_MARKER = "\x1B[200~";
9378
- var PASTE_END_MARKER = "\x1B[201~";
9379
- var PASTE_MERGE_WINDOW_MS = 30;
9929
+ var BAR = "\u258E ";
9380
9930
  function PromptInput({
9381
9931
  value,
9382
9932
  onChange,
@@ -9387,151 +9937,248 @@ function PromptInput({
9387
9937
  onHistoryNext
9388
9938
  }) {
9389
9939
  const [cursor, setCursor] = useState5(value.length);
9390
- const pastesRef = useRef(/* @__PURE__ */ new Map());
9391
- const nextPasteIdRef = useRef(0);
9392
- const pasteAccumRef = useRef(null);
9393
- const lastPasteRef = useRef(null);
9394
- const lastLocalValueRef = useRef(value);
9940
+ const pastesRef = useRef2(/* @__PURE__ */ new Map());
9941
+ const nextPasteIdRef = useRef2(0);
9942
+ const lastLocalValueRef = useRef2(value);
9395
9943
  if (value !== lastLocalValueRef.current) {
9396
9944
  lastLocalValueRef.current = value;
9397
- if (cursor !== value.length) {
9398
- setCursor(value.length);
9399
- }
9945
+ if (cursor !== value.length) setCursor(value.length);
9400
9946
  }
9401
- const cursorRef = useRef(cursor);
9402
- cursorRef.current = cursor;
9403
- const tick = useTick();
9404
- const showCursor = disabled ? false : Math.floor(tick / 4) % 2 === 0;
9405
9947
  const registerPaste = (content) => {
9406
9948
  const v = lastLocalValueRef.current;
9407
- const c = cursorRef.current;
9408
- const now = Date.now();
9409
- const last = lastPasteRef.current;
9410
- const prevChar = c > 0 ? v[c - 1] : null;
9411
- const prevId = prevChar ? decodePasteSentinel(prevChar) : null;
9412
- const canMerge = last !== null && prevId === last.id && now - last.at < PASTE_MERGE_WINDOW_MS && pastesRef.current.has(last.id);
9413
- if (canMerge && last) {
9414
- const existing = pastesRef.current.get(last.id);
9415
- if (existing) {
9416
- const merged = existing.content + content;
9417
- pastesRef.current.set(last.id, makePasteEntry(last.id, merged));
9418
- lastPasteRef.current = { id: last.id, at: now };
9419
- return;
9420
- }
9421
- }
9949
+ const c = cursor;
9422
9950
  const id = nextPasteIdRef.current % PASTE_SENTINEL_RANGE;
9423
9951
  nextPasteIdRef.current = id + 1;
9424
9952
  pastesRef.current.set(id, makePasteEntry(id, content));
9425
9953
  const sentinel = encodePasteSentinel(id);
9426
9954
  const next = v.slice(0, c) + sentinel + v.slice(c);
9427
9955
  lastLocalValueRef.current = next;
9428
- cursorRef.current = c + 1;
9429
9956
  onChange(next);
9430
9957
  setCursor(c + 1);
9431
- lastPasteRef.current = { id, at: now };
9432
9958
  };
9433
- useInput4(
9434
- (input, key) => {
9435
- if (pasteAccumRef.current !== null) {
9436
- const endIdx = input.indexOf(PASTE_END_MARKER);
9437
- if (endIdx === -1) {
9438
- pasteAccumRef.current += input;
9439
- return;
9440
- }
9441
- const content = pasteAccumRef.current + input.slice(0, endIdx);
9442
- pasteAccumRef.current = null;
9443
- registerPaste(content);
9444
- return;
9445
- }
9446
- const startIdx = input.indexOf(PASTE_START_MARKER);
9447
- if (startIdx !== -1) {
9448
- const afterStart = input.slice(startIdx + PASTE_START_MARKER.length);
9449
- const endIdx = afterStart.indexOf(PASTE_END_MARKER);
9450
- if (endIdx !== -1) {
9451
- registerPaste(afterStart.slice(0, endIdx));
9452
- } else {
9453
- pasteAccumRef.current = afterStart;
9454
- }
9455
- return;
9456
- }
9457
- const ke = {
9458
- input,
9459
- return: key.return,
9460
- shift: key.shift,
9461
- ctrl: key.ctrl,
9462
- meta: key.meta,
9463
- backspace: key.backspace,
9464
- delete: key.delete,
9465
- tab: key.tab,
9466
- upArrow: key.upArrow,
9467
- downArrow: key.downArrow,
9468
- leftArrow: key.leftArrow,
9469
- rightArrow: key.rightArrow,
9470
- escape: key.escape,
9471
- pageUp: key.pageUp,
9472
- pageDown: key.pageDown
9473
- };
9474
- const action = processMultilineKey(value, cursor, ke);
9475
- if (action.pasteRequest) {
9476
- registerPaste(action.pasteRequest.content);
9477
- return;
9478
- }
9479
- if (action.next !== null) {
9480
- lastLocalValueRef.current = action.next;
9481
- onChange(action.next);
9482
- }
9483
- if (action.cursor !== null) {
9484
- setCursor(action.cursor);
9485
- }
9486
- if (action.submit) {
9487
- const raw = action.submitValue ?? value;
9488
- const expanded = expandPasteSentinels(raw, pastesRef.current);
9489
- const reachable = new Set(listPasteIdsInBuffer(raw));
9490
- for (const id of pastesRef.current.keys()) {
9491
- if (!reachable.has(id)) pastesRef.current.delete(id);
9492
- }
9493
- onSubmit(expanded);
9959
+ useKeystroke((ev) => {
9960
+ if (disabled) return;
9961
+ if (ev.paste) {
9962
+ if (ev.input.length > 0) registerPaste(ev.input);
9963
+ return;
9964
+ }
9965
+ const key = {
9966
+ input: ev.input,
9967
+ return: ev.return,
9968
+ shift: ev.shift,
9969
+ ctrl: ev.ctrl,
9970
+ meta: ev.meta,
9971
+ backspace: ev.backspace,
9972
+ delete: ev.delete,
9973
+ tab: ev.tab,
9974
+ upArrow: ev.upArrow,
9975
+ downArrow: ev.downArrow,
9976
+ leftArrow: ev.leftArrow,
9977
+ rightArrow: ev.rightArrow,
9978
+ escape: ev.escape,
9979
+ pageUp: ev.pageUp,
9980
+ pageDown: ev.pageDown
9981
+ };
9982
+ const action = processMultilineKey(value, cursor, key);
9983
+ if (action.pasteRequest) {
9984
+ registerPaste(action.pasteRequest.content);
9985
+ return;
9986
+ }
9987
+ if (action.next !== null) {
9988
+ lastLocalValueRef.current = action.next;
9989
+ onChange(action.next);
9990
+ }
9991
+ if (action.cursor !== null) {
9992
+ setCursor(action.cursor);
9993
+ }
9994
+ if (action.submit) {
9995
+ const raw = action.submitValue ?? value;
9996
+ const expanded = expandPasteSentinels(raw, pastesRef.current);
9997
+ const reachable = new Set(listPasteIdsInBuffer(raw));
9998
+ for (const id of pastesRef.current.keys()) {
9999
+ if (!reachable.has(id)) pastesRef.current.delete(id);
9494
10000
  }
9495
- if (action.historyHandoff === "prev") onHistoryPrev?.();
9496
- if (action.historyHandoff === "next") onHistoryNext?.();
9497
- },
9498
- { isActive: !disabled }
9499
- );
9500
- const effectivePlaceholder = disabled ? placeholder ?? "\u2026waiting for response\u2026 \xB7 [Esc] to stop" : placeholder ?? "type a message, or /command \xB7 [Shift+Enter] / [Ctrl+J] newline";
10001
+ onSubmit(expanded);
10002
+ }
10003
+ if (action.historyHandoff === "prev") onHistoryPrev?.();
10004
+ if (action.historyHandoff === "next") onHistoryNext?.();
10005
+ }, !disabled);
10006
+ const { stdout: stdout2 } = useStdout3();
10007
+ const cols = stdout2?.columns ?? 80;
10008
+ const narrow = cols <= 90;
10009
+ const promptBody = narrow ? "\u203A " : "you \u203A ";
10010
+ const promptPrefix = BAR + promptBody;
10011
+ const continuationIndent = BAR + " ".repeat(promptBody.length);
10012
+ const prefixCells = promptPrefix.length;
10013
+ const visibleCells = Math.max(8, cols - prefixCells - 3);
10014
+ const placeholderActive = narrow ? "type a message, or /command" : "type a message, or /command \xB7 [Ctrl+J] newline (Shift+Enter where supported)";
10015
+ const effectivePlaceholder = disabled ? placeholder ?? "\u2026waiting for response\u2026" : placeholder ?? placeholderActive;
9501
10016
  const lines = value.length > 0 ? value.split("\n") : [""];
9502
- const borderColor = disabled ? "gray" : "cyan";
10017
+ const accentColor = disabled ? "gray" : "cyan";
9503
10018
  const { line: cursorLine, col: cursorCol } = lineAndColumn(value, cursor);
9504
10019
  const renderItems = collapseLinesForDisplay(lines, cursorLine);
9505
10020
  const showHugeBufferHints = lines.length > 20;
9506
- return /* @__PURE__ */ React15.createElement(Box14, { borderStyle: "round", borderColor, paddingX: 1, flexDirection: "column" }, renderItems.map((item, renderIdx) => {
10021
+ return /* @__PURE__ */ React16.createElement(Box14, { flexDirection: "column", paddingX: 1 }, renderItems.map((item, renderIdx) => {
9507
10022
  if (item.kind === "skip") {
9508
10023
  return (
9509
- // biome-ignore lint/suspicious/noArrayIndexKey: stable — skip markers are derived from a fixed-size window over `lines`
9510
- /* @__PURE__ */ React15.createElement(Box14, { key: `skip-${renderIdx}` }, /* @__PURE__ */ React15.createElement(Text14, { dimColor: true }, " "), /* @__PURE__ */ React15.createElement(
9511
- Text14,
9512
- {
9513
- dimColor: true
9514
- },
9515
- `[\u2026 ${item.linesHidden} line${item.linesHidden === 1 ? "" : "s"} hidden \u2014 full content kept, submitted on Enter \u2026]`
9516
- ))
10024
+ // biome-ignore lint/suspicious/noArrayIndexKey: stable — collapse markers derive from a fixed sliding window
10025
+ /* @__PURE__ */ React16.createElement(Box14, { key: `skip-${renderIdx}` }, /* @__PURE__ */ React16.createElement(Text14, { color: accentColor }, BAR), /* @__PURE__ */ React16.createElement(Text14, { dimColor: true }, continuationIndent.slice(BAR.length)), /* @__PURE__ */ React16.createElement(Text14, { dimColor: true }, `[\u2026 ${item.linesHidden} line${item.linesHidden === 1 ? "" : "s"} hidden \u2014 full content kept, submitted on Enter \u2026]`))
9517
10026
  );
9518
10027
  }
9519
- const line = item.line;
9520
10028
  const i = item.originalIndex;
10029
+ const line = item.line;
9521
10030
  const isFirst = i === 0;
9522
- const showPlaceholder = isFirst && value.length === 0;
9523
10031
  const isCursorLine = i === cursorLine;
9524
- return /* @__PURE__ */ React15.createElement(Box14, { key: `ln-${i}` }, isFirst ? /* @__PURE__ */ React15.createElement(Text14, { bold: true, color: borderColor }, "you \u203A", " ") : /* @__PURE__ */ React15.createElement(Text14, { dimColor: true }, " "), showPlaceholder ? /* @__PURE__ */ React15.createElement(React15.Fragment, null, isCursorLine && !disabled ? /* @__PURE__ */ React15.createElement(Text14, { color: borderColor }, showCursor ? "\u258C" : " ") : null, /* @__PURE__ */ React15.createElement(Text14, { dimColor: true }, effectivePlaceholder)) : isCursorLine && !disabled ? /* @__PURE__ */ React15.createElement(
9525
- LineWithCursor,
10032
+ const showPlaceholder = isFirst && value.length === 0;
10033
+ return /* @__PURE__ */ React16.createElement(
10034
+ PromptLine,
9526
10035
  {
10036
+ key: `ln-${i}`,
9527
10037
  line,
9528
- col: cursorCol,
9529
- showCursor,
9530
- borderColor,
9531
- pastes: pastesRef.current
10038
+ isFirst,
10039
+ isCursorLine: isCursorLine && !disabled,
10040
+ cursorCol: isCursorLine ? cursorCol : null,
10041
+ showPlaceholder,
10042
+ placeholderText: effectivePlaceholder,
10043
+ promptPrefix,
10044
+ continuationIndent,
10045
+ visibleCells,
10046
+ accentColor,
10047
+ pastes: pastesRef.current,
10048
+ disabled: disabled === true
9532
10049
  }
9533
- ) : /* @__PURE__ */ React15.createElement(RenderLine, { line, pastes: pastesRef.current }));
9534
- }), showHugeBufferHints && !disabled ? /* @__PURE__ */ React15.createElement(Box14, null, /* @__PURE__ */ React15.createElement(Text14, { dimColor: true }, " "), /* @__PURE__ */ React15.createElement(Text14, { dimColor: true }, `[${lines.length} lines \xB7 PageUp/PageDown jump to top/bottom \xB7 Ctrl+U clear \xB7 Ctrl+W del word]`)) : null);
10050
+ );
10051
+ }), showHugeBufferHints && !disabled ? /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Text14, { color: accentColor }, BAR), /* @__PURE__ */ React16.createElement(Text14, { dimColor: true }, continuationIndent.slice(BAR.length)), /* @__PURE__ */ React16.createElement(Text14, { dimColor: true }, `[${lines.length} lines \xB7 PageUp/PageDown jump to top/bottom \xB7 Ctrl+U clear \xB7 Ctrl+W del word]`)) : null, !disabled && !narrow && value.length > 0 && !value.includes("\n") ? /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Text14, { color: accentColor }, BAR), /* @__PURE__ */ React16.createElement(Text14, { dimColor: true }, continuationIndent.slice(BAR.length)), /* @__PURE__ */ React16.createElement(Text14, { dimColor: true }, "[Ctrl+J] newline \xB7 [Enter] submit \xB7 ends with \\ for line continuation")) : null, disabled ? /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Text14, { color: accentColor }, BAR), /* @__PURE__ */ React16.createElement(Text14, { dimColor: true }, continuationIndent.slice(BAR.length)), /* @__PURE__ */ React16.createElement(Text14, { dimColor: true }, "[Esc] to stop")) : null);
10052
+ }
10053
+ function PromptLine({
10054
+ line,
10055
+ isFirst,
10056
+ isCursorLine,
10057
+ cursorCol,
10058
+ showPlaceholder,
10059
+ placeholderText,
10060
+ promptPrefix,
10061
+ continuationIndent,
10062
+ visibleCells,
10063
+ accentColor,
10064
+ pastes,
10065
+ disabled
10066
+ }) {
10067
+ const barText = promptPrefix.slice(0, BAR.length);
10068
+ const bodyPrefix = promptPrefix.slice(BAR.length);
10069
+ const bodyContinuation = continuationIndent.slice(BAR.length);
10070
+ if (showPlaceholder) {
10071
+ return /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Text14, { color: accentColor }, barText), /* @__PURE__ */ React16.createElement(Text14, { bold: true, color: accentColor }, bodyPrefix), !disabled ? /* @__PURE__ */ React16.createElement(Text14, { color: accentColor }, "\u258C") : null, /* @__PURE__ */ React16.createElement(Text14, { dimColor: true }, placeholderText));
10072
+ }
10073
+ const viewport = buildViewport(line, isCursorLine ? cursorCol : null, visibleCells, pastes);
10074
+ return /* @__PURE__ */ React16.createElement(Box14, null, /* @__PURE__ */ React16.createElement(Text14, { color: accentColor }, barText), isFirst ? /* @__PURE__ */ React16.createElement(Text14, { bold: true, color: accentColor }, bodyPrefix) : /* @__PURE__ */ React16.createElement(Text14, { dimColor: true }, bodyContinuation), viewport.hiddenLeft ? /* @__PURE__ */ React16.createElement(Text14, { color: "gray", dimColor: true }, "\u2039") : null, /* @__PURE__ */ React16.createElement(
10075
+ ViewportContent,
10076
+ {
10077
+ segments: viewport.segments,
10078
+ cursorCell: isCursorLine ? viewport.cursorCell : null,
10079
+ accentColor
10080
+ }
10081
+ ), viewport.hiddenRight ? /* @__PURE__ */ React16.createElement(Text14, { color: "gray", dimColor: true }, "\u203A") : null);
10082
+ }
10083
+ function ViewportContent({
10084
+ segments,
10085
+ cursorCell,
10086
+ accentColor
10087
+ }) {
10088
+ if (cursorCell === null) {
10089
+ return /* @__PURE__ */ React16.createElement(React16.Fragment, null, segments.map((seg, i) => renderSegment(seg, i, false)));
10090
+ }
10091
+ const out = [];
10092
+ let cells = 0;
10093
+ let placed = false;
10094
+ for (let i = 0; i < segments.length; i++) {
10095
+ const seg = segments[i];
10096
+ const segCells = segmentCells(seg);
10097
+ if (placed) {
10098
+ out.push(renderSegment(seg, i, false));
10099
+ continue;
10100
+ }
10101
+ if (cursorCell >= cells + segCells) {
10102
+ out.push(renderSegment(seg, i, false));
10103
+ cells += segCells;
10104
+ continue;
10105
+ }
10106
+ if (seg.kind === "paste") {
10107
+ out.push(
10108
+ /* @__PURE__ */ React16.createElement(Text14, { key: `p-${i}-cursor`, color: "magenta", bold: true, inverse: true }, seg.label)
10109
+ );
10110
+ placed = true;
10111
+ cells += segCells;
10112
+ continue;
10113
+ }
10114
+ const offsetIntoSeg = cursorCell - cells;
10115
+ const split = splitTextByCells(seg.text, offsetIntoSeg);
10116
+ if (split.before.length > 0) {
10117
+ out.push(/* @__PURE__ */ React16.createElement(Text14, { key: `t-${i}-b` }, split.before));
10118
+ }
10119
+ if (split.atCursor.length > 0) {
10120
+ out.push(
10121
+ /* @__PURE__ */ React16.createElement(Text14, { key: `t-${i}-c`, inverse: true, color: accentColor }, split.atCursor)
10122
+ );
10123
+ } else {
10124
+ out.push(
10125
+ /* @__PURE__ */ React16.createElement(Text14, { key: `t-${i}-c-eol`, color: accentColor }, "\u258C")
10126
+ );
10127
+ }
10128
+ if (split.after.length > 0) {
10129
+ out.push(/* @__PURE__ */ React16.createElement(Text14, { key: `t-${i}-a` }, split.after));
10130
+ }
10131
+ placed = true;
10132
+ cells += segCells;
10133
+ }
10134
+ if (!placed) {
10135
+ out.push(
10136
+ /* @__PURE__ */ React16.createElement(Text14, { key: "cursor-eol", color: accentColor }, "\u258C")
10137
+ );
10138
+ }
10139
+ return /* @__PURE__ */ React16.createElement(React16.Fragment, null, out);
10140
+ }
10141
+ function segmentCells(seg) {
10142
+ if (seg.kind === "paste") return seg.label.length;
10143
+ return stringCells(seg.text);
10144
+ }
10145
+ function splitTextByCells(text, cellOffset) {
10146
+ let cells = 0;
10147
+ for (let i = 0; i < text.length; i++) {
10148
+ const ch = text[i];
10149
+ const cw = charCellsForText(ch);
10150
+ if (cells === cellOffset) {
10151
+ return { before: text.slice(0, i), atCursor: ch, after: text.slice(i + 1) };
10152
+ }
10153
+ if (cells + cw > cellOffset) {
10154
+ return { before: text.slice(0, i), atCursor: ch, after: text.slice(i + 1) };
10155
+ }
10156
+ cells += cw;
10157
+ }
10158
+ return { before: text, atCursor: "", after: "" };
10159
+ }
10160
+ function charCellsForText(ch) {
10161
+ const code = ch.charCodeAt(0);
10162
+ if (code < 32 || code === 127) return 0;
10163
+ if (code < 4352) return 1;
10164
+ if (code >= 4352 && code <= 4447) return 2;
10165
+ if (code >= 11904 && code <= 12350) return 2;
10166
+ if (code >= 12353 && code <= 13311) return 2;
10167
+ if (code >= 13312 && code <= 19903) return 2;
10168
+ if (code >= 19968 && code <= 40959) return 2;
10169
+ if (code >= 40960 && code <= 42191) return 2;
10170
+ if (code >= 44032 && code <= 55203) return 2;
10171
+ if (code >= 63744 && code <= 64255) return 2;
10172
+ if (code >= 65072 && code <= 65103) return 2;
10173
+ if (code >= 65280 && code <= 65376) return 2;
10174
+ if (code >= 65504 && code <= 65510) return 2;
10175
+ return 1;
10176
+ }
10177
+ function renderSegment(seg, key, _inverse) {
10178
+ if (seg.kind === "text") {
10179
+ return /* @__PURE__ */ React16.createElement(Text14, { key: `s-${key}` }, seg.text);
10180
+ }
10181
+ return /* @__PURE__ */ React16.createElement(Text14, { key: `s-${key}`, color: "magenta", bold: true }, seg.label);
9535
10182
  }
9536
10183
  var COLLAPSE_THRESHOLD = 20;
9537
10184
  var COLLAPSE_HEAD_LINES = 3;
@@ -9556,63 +10203,13 @@ function collapseLinesForDisplay(lines, cursorLine) {
9556
10203
  }
9557
10204
  return out;
9558
10205
  }
9559
- function RenderLine({
9560
- line,
9561
- pastes,
9562
- inverse
9563
- }) {
9564
- const segments = [];
9565
- let buf = "";
9566
- let segIdx = 0;
9567
- const flushBuf = () => {
9568
- if (buf.length === 0) return;
9569
- segments.push(
9570
- /* @__PURE__ */ React15.createElement(Text14, { key: `t-${segIdx++}`, inverse }, buf)
9571
- );
9572
- buf = "";
9573
- };
9574
- for (let i = 0; i < line.length; i++) {
9575
- const ch = line[i];
9576
- const id = decodePasteSentinel(ch);
9577
- if (id === null) {
9578
- buf += ch;
9579
- continue;
9580
- }
9581
- flushBuf();
9582
- const entry = pastes.get(id);
9583
- const label = entry ? `[paste #${id + 1} \xB7 ${entry.lineCount}l \xB7 ${formatBytesShort(entry.charCount)}]` : `[paste #${id + 1} \xB7 (missing)]`;
9584
- segments.push(
9585
- /* @__PURE__ */ React15.createElement(Text14, { key: `p-${segIdx++}`, color: "magenta", bold: true, inverse }, label)
9586
- );
9587
- }
9588
- flushBuf();
9589
- if (segments.length === 0) {
9590
- return /* @__PURE__ */ React15.createElement(Text14, null, " ");
9591
- }
9592
- return /* @__PURE__ */ React15.createElement(React15.Fragment, null, segments);
9593
- }
9594
- function LineWithCursor({
9595
- line,
9596
- col,
9597
- showCursor,
9598
- borderColor,
9599
- pastes
9600
- }) {
9601
- const before = line.slice(0, col);
9602
- const atCursor = line.slice(col, col + 1);
9603
- const after = line.slice(col + 1);
9604
- if (atCursor.length === 0) {
9605
- return /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement(RenderLine, { line: before, pastes }), /* @__PURE__ */ React15.createElement(Text14, { color: borderColor }, showCursor ? "\u258C" : " "));
9606
- }
9607
- return /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement(RenderLine, { line: before, pastes }), /* @__PURE__ */ React15.createElement(RenderLine, { line: atCursor, pastes, inverse: showCursor }), /* @__PURE__ */ React15.createElement(RenderLine, { line: after, pastes }));
9608
- }
9609
10206
 
9610
10207
  // src/cli/ui/ShellConfirm.tsx
9611
10208
  import { Box as Box15, Text as Text15 } from "ink";
9612
- import React16 from "react";
10209
+ import React17 from "react";
9613
10210
  function ShellConfirm({ command, allowPrefix, kind, onChoose }) {
9614
10211
  const isBackground = kind === "run_background";
9615
- return /* @__PURE__ */ React16.createElement(Box15, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React16.createElement(Box15, null, /* @__PURE__ */ React16.createElement(Text15, { bold: true, color: "yellow" }, isBackground ? "\u25B8 model wants to start a BACKGROUND process" : "\u25B8 model wants to run a shell command")), isBackground ? /* @__PURE__ */ React16.createElement(Box15, null, /* @__PURE__ */ React16.createElement(Text15, { dimColor: true }, " (long-running: dev server / watcher; keeps running after approval, /kill to stop)")) : null, /* @__PURE__ */ React16.createElement(Box15, null, /* @__PURE__ */ React16.createElement(Text15, { color: "yellow", dimColor: true }, "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")), /* @__PURE__ */ React16.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text15, null, /* @__PURE__ */ React16.createElement(Text15, { dimColor: true }, "$ "), /* @__PURE__ */ React16.createElement(Text15, { color: "cyan" }, command))), /* @__PURE__ */ React16.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React16.createElement(
10212
+ return /* @__PURE__ */ React17.createElement(Box15, { flexDirection: "column", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React17.createElement(Box15, null, /* @__PURE__ */ React17.createElement(Text15, { bold: true, color: "red" }, isBackground ? "\u25B8 model wants to start a BACKGROUND process" : "\u25B8 model wants to run a shell command")), isBackground ? /* @__PURE__ */ React17.createElement(Box15, null, /* @__PURE__ */ React17.createElement(Text15, { dimColor: true }, " (long-running: dev server / watcher; keeps running after approval, /kill to stop)")) : null, /* @__PURE__ */ React17.createElement(Box15, null, /* @__PURE__ */ React17.createElement(Text15, { color: "red", dimColor: true }, "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")), /* @__PURE__ */ React17.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React17.createElement(Text15, null, /* @__PURE__ */ React17.createElement(Text15, { dimColor: true }, "$ "), /* @__PURE__ */ React17.createElement(Text15, { color: "cyan" }, command))), /* @__PURE__ */ React17.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React17.createElement(
9616
10213
  SingleSelect,
9617
10214
  {
9618
10215
  initialValue: "run_once",
@@ -9670,7 +10267,7 @@ function derivePrefix(command) {
9670
10267
 
9671
10268
  // src/cli/ui/SlashArgPicker.tsx
9672
10269
  import { Box as Box16, Text as Text16 } from "ink";
9673
- import React17 from "react";
10270
+ import React18 from "react";
9674
10271
  function SlashArgPicker({
9675
10272
  matches,
9676
10273
  selectedIndex,
@@ -9679,11 +10276,11 @@ function SlashArgPicker({
9679
10276
  partial
9680
10277
  }) {
9681
10278
  if (kind === "hint") {
9682
- return /* @__PURE__ */ React17.createElement(Box16, { paddingX: 1 }, /* @__PURE__ */ React17.createElement(Text16, { dimColor: true }, " ", /* @__PURE__ */ React17.createElement(Text16, { bold: true }, "/", spec.cmd), spec.argsHint ? ` ${spec.argsHint}` : "", " \u2014 ", spec.summary));
10279
+ return /* @__PURE__ */ React18.createElement(Box16, { paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React18.createElement(Text16, { dimColor: true }, " ", /* @__PURE__ */ React18.createElement(Text16, { bold: true }, "/", spec.cmd), spec.argsHint ? ` ${spec.argsHint}` : "", " \u2014 ", spec.summary));
9683
10280
  }
9684
10281
  if (matches === null) return null;
9685
10282
  if (matches.length === 0) {
9686
- return /* @__PURE__ */ React17.createElement(Box16, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React17.createElement(Text16, { dimColor: true }, " ", /* @__PURE__ */ React17.createElement(Text16, { bold: true }, "/", spec.cmd), spec.argsHint ? ` ${spec.argsHint}` : "", " \u2014 ", spec.summary), /* @__PURE__ */ React17.createElement(Text16, { color: "yellow" }, ' no match for "', partial, '" \u2014 keep typing, or Backspace to edit'));
10283
+ return /* @__PURE__ */ React18.createElement(Box16, { flexDirection: "column", paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React18.createElement(Text16, { dimColor: true }, " ", /* @__PURE__ */ React18.createElement(Text16, { bold: true }, "/", spec.cmd), spec.argsHint ? ` ${spec.argsHint}` : "", " \u2014 ", spec.summary), /* @__PURE__ */ React18.createElement(Text16, { color: "yellow" }, ' no match for "', partial, '" \u2014 keep typing, or Backspace to edit'));
9687
10284
  }
9688
10285
  const MAX = 8;
9689
10286
  const total = matches.length;
@@ -9691,26 +10288,26 @@ function SlashArgPicker({
9691
10288
  const shown = matches.slice(windowStart, windowStart + MAX);
9692
10289
  const hiddenAbove = windowStart;
9693
10290
  const hiddenBelow = total - windowStart - shown.length;
9694
- return /* @__PURE__ */ React17.createElement(Box16, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React17.createElement(Text16, { dimColor: true }, " ", /* @__PURE__ */ React17.createElement(Text16, { bold: true }, "/", spec.cmd), spec.argsHint ? ` ${spec.argsHint}` : "", " \u2014 ", spec.summary), hiddenAbove > 0 ? /* @__PURE__ */ React17.createElement(Text16, { dimColor: true }, " \u2191 ", hiddenAbove, " more above") : null, shown.map((value, i) => /* @__PURE__ */ React17.createElement(ArgRow, { key: value, value, isSelected: windowStart + i === selectedIndex })), hiddenBelow > 0 ? /* @__PURE__ */ React17.createElement(Text16, { dimColor: true }, " \u2193 ", hiddenBelow, " more below") : null, /* @__PURE__ */ React17.createElement(Text16, { dimColor: true }, " [\u2191\u2193] navigate \xB7 [Tab]/[Enter] pick"));
10291
+ return /* @__PURE__ */ React18.createElement(Box16, { flexDirection: "column", paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React18.createElement(Text16, { dimColor: true }, " ", /* @__PURE__ */ React18.createElement(Text16, { bold: true }, "/", spec.cmd), spec.argsHint ? ` ${spec.argsHint}` : "", " \u2014 ", spec.summary), hiddenAbove > 0 ? /* @__PURE__ */ React18.createElement(Text16, { dimColor: true }, " \u2191 ", hiddenAbove, " more above") : null, shown.map((value, i) => /* @__PURE__ */ React18.createElement(ArgRow, { key: value, value, isSelected: windowStart + i === selectedIndex })), hiddenBelow > 0 ? /* @__PURE__ */ React18.createElement(Text16, { dimColor: true }, " \u2193 ", hiddenBelow, " more below") : null, /* @__PURE__ */ React18.createElement(Text16, { dimColor: true }, " [\u2191\u2193] navigate \xB7 [Tab]/[Enter] pick"));
9695
10292
  }
9696
10293
  function ArgRow({ value, isSelected }) {
9697
10294
  const marker = isSelected ? "\u25B8" : " ";
9698
10295
  if (isSelected) {
9699
- return /* @__PURE__ */ React17.createElement(Box16, null, /* @__PURE__ */ React17.createElement(Text16, { bold: true, color: "cyan" }, marker, " ", value));
10296
+ return /* @__PURE__ */ React18.createElement(Box16, null, /* @__PURE__ */ React18.createElement(Text16, { bold: true, color: "cyan" }, marker, " ", value));
9700
10297
  }
9701
- return /* @__PURE__ */ React17.createElement(Box16, null, /* @__PURE__ */ React17.createElement(Text16, { dimColor: true }, marker, " ", value));
10298
+ return /* @__PURE__ */ React18.createElement(Box16, null, /* @__PURE__ */ React18.createElement(Text16, { dimColor: true }, marker, " ", value));
9702
10299
  }
9703
10300
 
9704
10301
  // src/cli/ui/SlashSuggestions.tsx
9705
10302
  import { Box as Box17, Text as Text17 } from "ink";
9706
- import React18 from "react";
10303
+ import React19 from "react";
9707
10304
  function SlashSuggestions({
9708
10305
  matches,
9709
10306
  selectedIndex
9710
10307
  }) {
9711
10308
  if (matches === null) return null;
9712
10309
  if (matches.length === 0) {
9713
- return /* @__PURE__ */ React18.createElement(Box17, { paddingX: 1 }, /* @__PURE__ */ React18.createElement(Text17, { color: "yellow" }, "no slash command matches that prefix"), /* @__PURE__ */ React18.createElement(Text17, { dimColor: true }, " \u2014 Backspace to edit, or /help for the full list"));
10310
+ return /* @__PURE__ */ React19.createElement(Box17, { paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React19.createElement(Text17, { color: "yellow" }, "no slash command matches that prefix"), /* @__PURE__ */ React19.createElement(Text17, { dimColor: true }, " \u2014 Backspace to edit, or /help for the full list"));
9714
10311
  }
9715
10312
  const MAX = 8;
9716
10313
  const total = matches.length;
@@ -9718,21 +10315,21 @@ function SlashSuggestions({
9718
10315
  const shown = matches.slice(windowStart, windowStart + MAX);
9719
10316
  const hiddenAbove = windowStart;
9720
10317
  const hiddenBelow = total - windowStart - shown.length;
9721
- return /* @__PURE__ */ React18.createElement(Box17, { flexDirection: "column", paddingX: 1 }, hiddenAbove > 0 ? /* @__PURE__ */ React18.createElement(Text17, { dimColor: true }, " \u2191 ", hiddenAbove, " more above") : null, shown.map((spec, i) => /* @__PURE__ */ React18.createElement(SuggestionRow, { key: spec.cmd, spec, isSelected: windowStart + i === selectedIndex })), hiddenBelow > 0 ? /* @__PURE__ */ React18.createElement(Text17, { dimColor: true }, " \u2193 ", hiddenBelow, " more below") : null, /* @__PURE__ */ React18.createElement(Text17, { dimColor: true }, " [\u2191\u2193] navigate \xB7 [Tab]/[Enter] pick"));
10318
+ return /* @__PURE__ */ React19.createElement(Box17, { flexDirection: "column", paddingX: 1, marginTop: 1 }, hiddenAbove > 0 ? /* @__PURE__ */ React19.createElement(Text17, { dimColor: true }, " \u2191 ", hiddenAbove, " more above") : null, shown.map((spec, i) => /* @__PURE__ */ React19.createElement(SuggestionRow, { key: spec.cmd, spec, isSelected: windowStart + i === selectedIndex })), hiddenBelow > 0 ? /* @__PURE__ */ React19.createElement(Text17, { dimColor: true }, " \u2193 ", hiddenBelow, " more below") : null, /* @__PURE__ */ React19.createElement(Text17, { dimColor: true }, " [\u2191\u2193] navigate \xB7 [Tab]/[Enter] pick"));
9722
10319
  }
9723
10320
  function SuggestionRow({ spec, isSelected }) {
9724
10321
  const marker = isSelected ? "\u25B8" : " ";
9725
10322
  const name = `/${spec.cmd}`;
9726
10323
  const argsSuffix = spec.argsHint ? ` ${spec.argsHint}` : "";
9727
10324
  if (isSelected) {
9728
- return /* @__PURE__ */ React18.createElement(Box17, null, /* @__PURE__ */ React18.createElement(Text17, { bold: true, color: "cyan" }, marker, " ", name.padEnd(12), argsSuffix.padEnd(16)), /* @__PURE__ */ React18.createElement(Text17, { color: "cyan" }, " ", spec.summary));
10325
+ return /* @__PURE__ */ React19.createElement(Box17, null, /* @__PURE__ */ React19.createElement(Text17, { bold: true, color: "cyan" }, marker, " ", name.padEnd(12), argsSuffix.padEnd(16)), /* @__PURE__ */ React19.createElement(Text17, { color: "cyan" }, " ", spec.summary));
9729
10326
  }
9730
- return /* @__PURE__ */ React18.createElement(Box17, null, /* @__PURE__ */ React18.createElement(Text17, { dimColor: true }, marker, " ", name.padEnd(12), argsSuffix.padEnd(16), " ", spec.summary));
10327
+ return /* @__PURE__ */ React19.createElement(Box17, null, /* @__PURE__ */ React19.createElement(Text17, { dimColor: true }, marker, " ", name.padEnd(12), argsSuffix.padEnd(16), " ", spec.summary));
9731
10328
  }
9732
10329
 
9733
10330
  // src/cli/ui/StatsPanel.tsx
9734
- import { Box as Box18, Text as Text18, useStdout as useStdout3 } from "ink";
9735
- import React19 from "react";
10331
+ import { Box as Box18, Text as Text18, useStdout as useStdout4 } from "ink";
10332
+ import React20 from "react";
9736
10333
  var WORDMARK_STYLES = [
9737
10334
  { ch: "\u25C8", color: "#5eead4", isLogo: true },
9738
10335
  // teal — brand mark
@@ -9758,7 +10355,7 @@ function Wordmark({ busy }) {
9758
10355
  const tick = useTick();
9759
10356
  const period = busy ? 5 : 12;
9760
10357
  const bright = Math.floor(tick / period) % 2 === 0;
9761
- return /* @__PURE__ */ React19.createElement(Text18, null, WORDMARK_STYLES.map((c) => /* @__PURE__ */ React19.createElement(Text18, { key: `${c.ch}-${c.color}`, color: c.color, bold: c.isLogo ? bright : true }, c.ch)));
10358
+ return /* @__PURE__ */ React20.createElement(Text18, null, WORDMARK_STYLES.map((c) => /* @__PURE__ */ React20.createElement(Text18, { key: `${c.ch}-${c.color}`, color: c.color, bold: c.isLogo ? bright : true }, c.ch)));
9762
10359
  }
9763
10360
  var NARROW_BREAKPOINT = 120;
9764
10361
  var COLD_START_TURNS = 3;
@@ -9780,21 +10377,20 @@ function StatsPanel({
9780
10377
  const branchOn = (branchBudget ?? 1) > 1;
9781
10378
  const ctxMax = DEEPSEEK_CONTEXT_TOKENS[model2] ?? DEFAULT_CONTEXT_TOKENS;
9782
10379
  const ctxRatio = summary.lastPromptTokens / ctxMax;
9783
- const { stdout: stdout2 } = useStdout3();
10380
+ const { stdout: stdout2 } = useStdout4();
9784
10381
  const columns = stdout2?.columns ?? 80;
9785
10382
  const narrow = columns < NARROW_BREAKPOINT;
9786
10383
  const coldStart = summary.turns <= COLD_START_TURNS;
10384
+ const ruleWidth = Math.max(20, columns - 2);
9787
10385
  return (
9788
- // Explicit `width={columns}` pins the border frame to the exact
9789
- // terminal width. Without this, Ink auto-flexes the Box to
9790
- // container width, and on terminal resize the prior frame's
9791
- // wrapped-overflow can leave tails in the scrollback (each
9792
- // redraw stacks a slightly-wider-or-narrower frame). Fixing
9793
- // width per-render doesn't eliminate the underlying Ink
9794
- // limitation (eraseLines counts logical rows, not post-wrap
9795
- // display rows) but makes each frame's dimensions exact so
9796
- // there's no residual uncertainty in the erase.
9797
- /* @__PURE__ */ React19.createElement(Box18, { borderStyle: "round", borderColor: "cyan", flexDirection: "column", paddingX: 1, width: columns }, /* @__PURE__ */ React19.createElement(
10386
+ // Borderless layout: no `borderStyle`, no rounded box, no `width={cols}`
10387
+ // pinning. Bordered Boxes were the most visible amplifier of Ink's
10388
+ // eraseLines miscount on Windows terminals every miscounted render
10389
+ // pushed a top-border frame into scrollback. Without a border there
10390
+ // is nothing visually obvious to duplicate; visual structure comes
10391
+ // from the gradient wordmark + colored pills + a top/bottom margin
10392
+ // + a thin dim rule that closes the panel cleanly under the metrics.
10393
+ /* @__PURE__ */ React20.createElement(Box18, { flexDirection: "column", paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React20.createElement(
9798
10394
  Header,
9799
10395
  {
9800
10396
  model: model2,
@@ -9812,7 +10408,7 @@ function StatsPanel({
9812
10408
  proArmed: proArmed ?? false,
9813
10409
  escalated: escalated ?? false
9814
10410
  }
9815
- ), narrow ? /* @__PURE__ */ React19.createElement(
10411
+ ), narrow ? /* @__PURE__ */ React20.createElement(
9816
10412
  StackedMetrics,
9817
10413
  {
9818
10414
  summary,
@@ -9821,7 +10417,7 @@ function StatsPanel({
9821
10417
  balance,
9822
10418
  coldStart
9823
10419
  }
9824
- ) : /* @__PURE__ */ React19.createElement(
10420
+ ) : /* @__PURE__ */ React20.createElement(
9825
10421
  InlineMetrics,
9826
10422
  {
9827
10423
  summary,
@@ -9830,7 +10426,7 @@ function StatsPanel({
9830
10426
  balance,
9831
10427
  coldStart
9832
10428
  }
9833
- ))
10429
+ ), /* @__PURE__ */ React20.createElement(Box18, { marginTop: 1 }, /* @__PURE__ */ React20.createElement(Text18, { dimColor: true }, "\u2500".repeat(ruleWidth))))
9834
10430
  );
9835
10431
  }
9836
10432
  function Header({
@@ -9849,7 +10445,12 @@ function Header({
9849
10445
  proArmed,
9850
10446
  escalated
9851
10447
  }) {
9852
- return /* @__PURE__ */ React19.createElement(Box18, { justifyContent: "space-between" }, /* @__PURE__ */ React19.createElement(Box18, null, /* @__PURE__ */ React19.createElement(Wordmark, { busy }), /* @__PURE__ */ React19.createElement(Text18, { dimColor: true }, ` v${VERSION}`), /* @__PURE__ */ React19.createElement(Text18, { dimColor: true }, " \xB7 "), /* @__PURE__ */ React19.createElement(Text18, { color: "yellow" }, model2), narrow ? null : /* @__PURE__ */ React19.createElement(React19.Fragment, null, /* @__PURE__ */ React19.createElement(Text18, { dimColor: true }, " \xB7 "), /* @__PURE__ */ React19.createElement(Text18, { dimColor: true }, prefixHash)), harvestOn ? /* @__PURE__ */ React19.createElement(Text18, { color: "magenta" }, " \xB7 harvest") : null, branchOn ? /* @__PURE__ */ React19.createElement(Text18, { color: "blue" }, " \xB7 branch", branchBudget) : null, reasoningEffort === "max" ? /* @__PURE__ */ React19.createElement(Text18, { color: "green" }, " \xB7 max") : null, reasoningEffort === "high" ? /* @__PURE__ */ React19.createElement(Text18, { color: "yellow" }, " \xB7 high") : null, planMode ? /* @__PURE__ */ React19.createElement(Text18, { color: "red", bold: true }, " \xB7 PLAN") : null, editMode ? /* @__PURE__ */ React19.createElement(Text18, { color: editMode === "auto" ? "magenta" : "cyan", bold: true }, editMode === "auto" ? " \xB7 AUTO" : " \xB7 review") : null, escalated ? /* @__PURE__ */ React19.createElement(Text18, { color: "red", bold: true }, " \xB7 \u21E7 pro escalated") : proArmed ? /* @__PURE__ */ React19.createElement(Text18, { color: "yellow", bold: true }, " \xB7 \u21E7 pro armed") : null), /* @__PURE__ */ React19.createElement(Text18, null, updateAvailable ? /* @__PURE__ */ React19.createElement(Text18, { color: "yellow", bold: true }, `update: ${updateAvailable} \xB7 `) : null, /* @__PURE__ */ React19.createElement(Text18, { dimColor: true }, narrow ? `turn ${turns}` : `turn ${turns} \xB7 /help`)));
10448
+ const modePill = planMode ? { label: "PLAN", bg: "red" } : editMode === "auto" ? { label: "AUTO", bg: "magenta" } : editMode === "review" ? { label: "REVIEW", bg: "cyan" } : null;
10449
+ const proPill = escalated ? { label: "\u21E7 PRO", bg: "red" } : proArmed ? { label: "\u21E7 PRO", bg: "yellow" } : null;
10450
+ return /* @__PURE__ */ React20.createElement(Box18, { justifyContent: "space-between" }, /* @__PURE__ */ React20.createElement(Box18, null, /* @__PURE__ */ React20.createElement(Wordmark, { busy }), /* @__PURE__ */ React20.createElement(Text18, { dimColor: true }, ` ${VERSION}`), /* @__PURE__ */ React20.createElement(Text18, { dimColor: true }, " "), /* @__PURE__ */ React20.createElement(Text18, { color: "yellow", bold: true }, model2.replace(/^deepseek-/, "")), modePill ? /* @__PURE__ */ React20.createElement(React20.Fragment, null, /* @__PURE__ */ React20.createElement(Text18, null, " "), /* @__PURE__ */ React20.createElement(Pill, { label: modePill.label, bg: modePill.bg })) : null, proPill ? /* @__PURE__ */ React20.createElement(React20.Fragment, null, /* @__PURE__ */ React20.createElement(Text18, null, " "), /* @__PURE__ */ React20.createElement(Pill, { label: proPill.label, bg: proPill.bg })) : null, harvestOn ? /* @__PURE__ */ React20.createElement(Text18, { dimColor: true }, /* @__PURE__ */ React20.createElement(Text18, null, " "), /* @__PURE__ */ React20.createElement(Text18, { color: "magenta" }, "harvest")) : null, branchOn ? /* @__PURE__ */ React20.createElement(Text18, { dimColor: true }, /* @__PURE__ */ React20.createElement(Text18, null, " "), /* @__PURE__ */ React20.createElement(Text18, { color: "blue" }, `branch\xD7${branchBudget}`)) : null, reasoningEffort === "max" ? /* @__PURE__ */ React20.createElement(React20.Fragment, null, /* @__PURE__ */ React20.createElement(Text18, null, " "), /* @__PURE__ */ React20.createElement(Text18, { color: "green", dimColor: true }, "max")) : null, reasoningEffort === "high" ? /* @__PURE__ */ React20.createElement(React20.Fragment, null, /* @__PURE__ */ React20.createElement(Text18, null, " "), /* @__PURE__ */ React20.createElement(Text18, { color: "yellow", dimColor: true }, "high")) : null), /* @__PURE__ */ React20.createElement(Text18, null, updateAvailable ? /* @__PURE__ */ React20.createElement(Text18, { color: "yellow", bold: true }, `\u2191 ${updateAvailable} `) : null, /* @__PURE__ */ React20.createElement(Text18, { dimColor: true }, narrow ? `t${turns}` : `turn ${turns} \xB7 /help`)));
10451
+ }
10452
+ function Pill({ label, bg }) {
10453
+ return /* @__PURE__ */ React20.createElement(Text18, { backgroundColor: bg, color: "white", bold: true }, ` ${label} `);
9853
10454
  }
9854
10455
  function InlineMetrics({
9855
10456
  summary,
@@ -9858,7 +10459,7 @@ function InlineMetrics({
9858
10459
  balance,
9859
10460
  coldStart
9860
10461
  }) {
9861
- return /* @__PURE__ */ React19.createElement(Box18, { marginTop: 1, gap: 3 }, /* @__PURE__ */ React19.createElement(ContextCell, { ratio: ctxRatio, promptTokens: summary.lastPromptTokens, ctxMax }), /* @__PURE__ */ React19.createElement(CacheCell, { hitRatio: summary.cacheHitRatio, coldStart, turns: summary.turns }), /* @__PURE__ */ React19.createElement(CostCell, { summary, coldStart }), balance ? /* @__PURE__ */ React19.createElement(BalanceCell, { balance }) : null);
10462
+ return /* @__PURE__ */ React20.createElement(Box18, { marginTop: 1, gap: 3 }, /* @__PURE__ */ React20.createElement(ContextCell, { ratio: ctxRatio, promptTokens: summary.lastPromptTokens, ctxMax }), /* @__PURE__ */ React20.createElement(CacheCell, { hitRatio: summary.cacheHitRatio, coldStart, turns: summary.turns }), /* @__PURE__ */ React20.createElement(CostCell, { summary, coldStart }), balance ? /* @__PURE__ */ React20.createElement(BalanceCell, { balance }) : null);
9862
10463
  }
9863
10464
  function StackedMetrics({
9864
10465
  summary,
@@ -9867,7 +10468,7 @@ function StackedMetrics({
9867
10468
  balance,
9868
10469
  coldStart
9869
10470
  }) {
9870
- return /* @__PURE__ */ React19.createElement(Box18, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React19.createElement(
10471
+ return /* @__PURE__ */ React20.createElement(Box18, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React20.createElement(
9871
10472
  ContextCell,
9872
10473
  {
9873
10474
  ratio: ctxRatio,
@@ -9875,7 +10476,7 @@ function StackedMetrics({
9875
10476
  ctxMax,
9876
10477
  showBar: true
9877
10478
  }
9878
- ), balance ? /* @__PURE__ */ React19.createElement(BalanceCell, { balance }) : null, /* @__PURE__ */ React19.createElement(CacheCell, { hitRatio: summary.cacheHitRatio, coldStart, turns: summary.turns }), /* @__PURE__ */ React19.createElement(CostCell, { summary, coldStart }));
10479
+ ), balance ? /* @__PURE__ */ React20.createElement(BalanceCell, { balance }) : null, /* @__PURE__ */ React20.createElement(CacheCell, { hitRatio: summary.cacheHitRatio, coldStart, turns: summary.turns }), /* @__PURE__ */ React20.createElement(CostCell, { summary, coldStart }));
9879
10480
  }
9880
10481
  function ContextCell({
9881
10482
  ratio,
@@ -9884,11 +10485,11 @@ function ContextCell({
9884
10485
  showBar
9885
10486
  }) {
9886
10487
  if (promptTokens === 0) {
9887
- return /* @__PURE__ */ React19.createElement(Text18, null, /* @__PURE__ */ React19.createElement(Text18, { dimColor: true }, "ctx "), /* @__PURE__ */ React19.createElement(Text18, { dimColor: true }, "\u2014 (no turns yet)"));
10488
+ return /* @__PURE__ */ React20.createElement(Text18, null, /* @__PURE__ */ React20.createElement(Text18, { dimColor: true }, "ctx "), /* @__PURE__ */ React20.createElement(Text18, { dimColor: true }, "\u2014 (no turns yet)"));
9888
10489
  }
9889
10490
  const color = ratio >= 0.8 ? "red" : ratio >= 0.6 ? "yellow" : "green";
9890
10491
  const pct2 = Math.round(ratio * 100);
9891
- return /* @__PURE__ */ React19.createElement(Text18, null, /* @__PURE__ */ React19.createElement(Text18, { dimColor: true }, "ctx "), showBar ? /* @__PURE__ */ React19.createElement(Bar, { ratio, color }) : null, showBar ? /* @__PURE__ */ React19.createElement(Text18, null, " ") : null, /* @__PURE__ */ React19.createElement(Text18, { color, bold: true }, formatTokens(promptTokens), "/", formatTokens(ctxMax)), /* @__PURE__ */ React19.createElement(Text18, { dimColor: true }, " (", pct2, "%)"), ratio >= 0.8 ? /* @__PURE__ */ React19.createElement(Text18, { color: "red", bold: true }, " \xB7 /compact") : null);
10492
+ return /* @__PURE__ */ React20.createElement(Text18, null, /* @__PURE__ */ React20.createElement(Text18, { dimColor: true }, "ctx "), showBar ? /* @__PURE__ */ React20.createElement(Bar, { ratio, color }) : null, showBar ? /* @__PURE__ */ React20.createElement(Text18, null, " ") : null, /* @__PURE__ */ React20.createElement(Text18, { color, bold: true }, formatTokens(promptTokens), "/", formatTokens(ctxMax)), /* @__PURE__ */ React20.createElement(Text18, { dimColor: true }, " (", pct2, "%)"), ratio >= 0.8 ? /* @__PURE__ */ React20.createElement(Text18, { color: "red", bold: true }, " \xB7 /compact") : null);
9892
10493
  }
9893
10494
  function CacheCell({
9894
10495
  hitRatio,
@@ -9897,13 +10498,13 @@ function CacheCell({
9897
10498
  }) {
9898
10499
  const pct2 = (hitRatio * 100).toFixed(1);
9899
10500
  if (turns === 0) {
9900
- return /* @__PURE__ */ React19.createElement(Text18, null, /* @__PURE__ */ React19.createElement(Text18, { dimColor: true }, "cache "), /* @__PURE__ */ React19.createElement(Text18, { dimColor: true }, "\u2014"));
10501
+ return /* @__PURE__ */ React20.createElement(Text18, null, /* @__PURE__ */ React20.createElement(Text18, { dimColor: true }, "cache "), /* @__PURE__ */ React20.createElement(Text18, { dimColor: true }, "\u2014"));
9901
10502
  }
9902
10503
  if (coldStart) {
9903
- return /* @__PURE__ */ React19.createElement(Text18, null, /* @__PURE__ */ React19.createElement(Text18, { dimColor: true }, "cache "), /* @__PURE__ */ React19.createElement(Text18, { dimColor: true }, pct2, "% "), /* @__PURE__ */ React19.createElement(Text18, { dimColor: true, italic: true }, "(cold start)"));
10504
+ return /* @__PURE__ */ React20.createElement(Text18, null, /* @__PURE__ */ React20.createElement(Text18, { dimColor: true }, "cache "), /* @__PURE__ */ React20.createElement(Text18, { dimColor: true }, pct2, "% "), /* @__PURE__ */ React20.createElement(Text18, { dimColor: true, italic: true }, "(cold start)"));
9904
10505
  }
9905
10506
  const color = hitRatio >= 0.7 ? "green" : hitRatio >= 0.4 ? "yellow" : "red";
9906
- return /* @__PURE__ */ React19.createElement(Text18, null, /* @__PURE__ */ React19.createElement(Text18, { dimColor: true }, "cache "), /* @__PURE__ */ React19.createElement(Text18, { color, bold: true }, pct2, "%"));
10507
+ return /* @__PURE__ */ React20.createElement(Text18, null, /* @__PURE__ */ React20.createElement(Text18, { dimColor: true }, "cache "), /* @__PURE__ */ React20.createElement(Text18, { color, bold: true }, pct2, "%"));
9907
10508
  }
9908
10509
  function turnCostColor(cost) {
9909
10510
  if (cost <= 0) return void 0;
@@ -9922,21 +10523,21 @@ function CostCell({
9922
10523
  coldStart
9923
10524
  }) {
9924
10525
  if (summary.turns === 0) {
9925
- return /* @__PURE__ */ React19.createElement(Text18, null, /* @__PURE__ */ React19.createElement(Text18, { dimColor: true }, "cost "), /* @__PURE__ */ React19.createElement(Text18, { dimColor: true }, "\u2014"));
10526
+ return /* @__PURE__ */ React20.createElement(Text18, null, /* @__PURE__ */ React20.createElement(Text18, { dimColor: true }, "cost "), /* @__PURE__ */ React20.createElement(Text18, { dimColor: true }, "\u2014"));
9926
10527
  }
9927
10528
  const turnColor = coldStart ? void 0 : turnCostColor(summary.lastTurnCostUsd);
9928
10529
  const sessionColor = coldStart ? void 0 : sessionCostColor(summary.totalCostUsd);
9929
- return /* @__PURE__ */ React19.createElement(Text18, null, /* @__PURE__ */ React19.createElement(Text18, { dimColor: true }, "turn "), /* @__PURE__ */ React19.createElement(Text18, { color: turnColor, bold: !coldStart, dimColor: coldStart }, "$", summary.lastTurnCostUsd.toFixed(4)), /* @__PURE__ */ React19.createElement(Text18, { dimColor: true }, " \xB7 session "), /* @__PURE__ */ React19.createElement(Text18, { color: sessionColor, bold: !coldStart, dimColor: coldStart }, "$", summary.totalCostUsd.toFixed(4)));
10530
+ return /* @__PURE__ */ React20.createElement(Text18, null, /* @__PURE__ */ React20.createElement(Text18, { dimColor: true }, "turn "), /* @__PURE__ */ React20.createElement(Text18, { color: turnColor, bold: !coldStart, dimColor: coldStart }, "$", summary.lastTurnCostUsd.toFixed(4)), /* @__PURE__ */ React20.createElement(Text18, { dimColor: true }, " \xB7 session "), /* @__PURE__ */ React20.createElement(Text18, { color: sessionColor, bold: !coldStart, dimColor: coldStart }, "$", summary.totalCostUsd.toFixed(4)));
9930
10531
  }
9931
10532
  function BalanceCell({ balance }) {
9932
10533
  const color = balance.total < 1 ? "red" : balance.total < 5 ? "yellow" : "green";
9933
- return /* @__PURE__ */ React19.createElement(Text18, null, /* @__PURE__ */ React19.createElement(Text18, { dimColor: true }, "balance "), /* @__PURE__ */ React19.createElement(Text18, { color, bold: true }, balance.currency === "USD" ? "$" : "", balance.total.toFixed(2), balance.currency !== "USD" ? ` ${balance.currency}` : ""));
10534
+ return /* @__PURE__ */ React20.createElement(Text18, null, /* @__PURE__ */ React20.createElement(Text18, { dimColor: true }, "balance "), /* @__PURE__ */ React20.createElement(Text18, { color, bold: true }, balance.currency === "USD" ? "$" : "", balance.total.toFixed(2), balance.currency !== "USD" ? ` ${balance.currency}` : ""));
9934
10535
  }
9935
10536
  function Bar({ ratio, color }) {
9936
10537
  const cells = 10;
9937
10538
  const filled = Math.max(0, Math.min(cells, Math.round(ratio * cells)));
9938
10539
  const bar = "\u2588".repeat(filled) + "\u2591".repeat(cells - filled);
9939
- return /* @__PURE__ */ React19.createElement(Text18, { color }, bar);
10540
+ return /* @__PURE__ */ React20.createElement(Text18, { color }, bar);
9940
10541
  }
9941
10542
  function formatTokens(n) {
9942
10543
  if (n < 1024) return String(n);
@@ -9944,6 +10545,16 @@ function formatTokens(n) {
9944
10545
  return k >= 100 ? `${k.toFixed(0)}K` : `${k.toFixed(1)}K`;
9945
10546
  }
9946
10547
 
10548
+ // src/cli/ui/WelcomeBanner.tsx
10549
+ import { Box as Box19, Text as Text19 } from "ink";
10550
+ import React21 from "react";
10551
+ function WelcomeBanner({ inCodeMode }) {
10552
+ return /* @__PURE__ */ React21.createElement(Box19, { flexDirection: "column", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React21.createElement(Box19, null, /* @__PURE__ */ React21.createElement(Text19, { bold: true, color: "cyan" }, "Hi"), /* @__PURE__ */ React21.createElement(Text19, null, " \u2014 type a message to start, or try:")), /* @__PURE__ */ React21.createElement(Box19, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React21.createElement(Hint, { cmd: "/help", desc: "every command + keyboard shortcut" }), /* @__PURE__ */ React21.createElement(Hint, { cmd: "/skill", desc: "invoke a stored playbook" }), inCodeMode ? /* @__PURE__ */ React21.createElement(React21.Fragment, null, /* @__PURE__ */ React21.createElement(Hint, { cmd: "@path", desc: "inline a file in your message" }), /* @__PURE__ */ React21.createElement(Hint, { cmd: "!cmd", desc: "run a shell command, output goes to context" })) : null, /* @__PURE__ */ React21.createElement(Hint, { cmd: "/exit", desc: "quit" })));
10553
+ }
10554
+ function Hint({ cmd, desc }) {
10555
+ return /* @__PURE__ */ React21.createElement(Box19, null, /* @__PURE__ */ React21.createElement(Text19, { dimColor: true }, " "), /* @__PURE__ */ React21.createElement(Text19, { bold: true, color: "magenta" }, cmd.padEnd(8)), /* @__PURE__ */ React21.createElement(Text19, { dimColor: true }, ` ${desc}`));
10556
+ }
10557
+
9947
10558
  // src/cli/ui/bang.ts
9948
10559
  function detectBangCommand(text) {
9949
10560
  if (!text.startsWith("!")) return null;
@@ -11784,7 +12395,7 @@ function handleSlash(cmd, args, loop, ctx = {}) {
11784
12395
  }
11785
12396
 
11786
12397
  // src/cli/ui/useCompletionPickers.ts
11787
- import { useCallback, useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState6 } from "react";
12398
+ import { useCallback, useEffect as useEffect3, useMemo as useMemo2, useRef as useRef3, useState as useState6 } from "react";
11788
12399
  function useCompletionPickers({
11789
12400
  input,
11790
12401
  setInput,
@@ -11797,7 +12408,7 @@ function useCompletionPickers({
11797
12408
  if (!input.startsWith("/") || input.includes(" ")) return null;
11798
12409
  return suggestSlashCommands(input.slice(1), !!codeMode);
11799
12410
  }, [input, codeMode]);
11800
- useEffect2(() => {
12411
+ useEffect3(() => {
11801
12412
  setSlashSelected((prev) => {
11802
12413
  if (!slashMatches || slashMatches.length === 0) return 0;
11803
12414
  if (prev >= slashMatches.length) return slashMatches.length - 1;
@@ -11813,7 +12424,7 @@ function useCompletionPickers({
11813
12424
  return [];
11814
12425
  }
11815
12426
  }, [codeMode?.rootDir]);
11816
- const recentFilesRef = useRef2([]);
12427
+ const recentFilesRef = useRef3([]);
11817
12428
  const recordRecentFile = useCallback((p) => {
11818
12429
  const list = recentFilesRef.current;
11819
12430
  const i = list.indexOf(p);
@@ -11833,7 +12444,7 @@ function useCompletionPickers({
11833
12444
  recentlyUsed: recentFilesRef.current
11834
12445
  });
11835
12446
  }, [atPicker, atFiles]);
11836
- useEffect2(() => {
12447
+ useEffect3(() => {
11837
12448
  setAtSelected((prev) => {
11838
12449
  if (!atMatches || atMatches.length === 0) return 0;
11839
12450
  if (prev >= atMatches.length) return atMatches.length - 1;
@@ -11894,7 +12505,7 @@ function useCompletionPickers({
11894
12505
  }
11895
12506
  return null;
11896
12507
  }, [slashArgContext, models2, mcpServers]);
11897
- useEffect2(() => {
12508
+ useEffect3(() => {
11898
12509
  setSlashArgSelected((prev) => {
11899
12510
  if (!slashArgMatches || slashArgMatches.length === 0) return 0;
11900
12511
  if (prev >= slashArgMatches.length) return slashArgMatches.length - 1;
@@ -11928,13 +12539,13 @@ function useCompletionPickers({
11928
12539
  }
11929
12540
 
11930
12541
  // src/cli/ui/useEditHistory.ts
11931
- import { useCallback as useCallback2, useRef as useRef3, useState as useState7 } from "react";
12542
+ import { useCallback as useCallback2, useRef as useRef4, useState as useState7 } from "react";
11932
12543
  function useEditHistory(codeMode) {
11933
- const editHistory = useRef3([]);
11934
- const nextHistoryId = useRef3(1);
11935
- const currentTurnEntry = useRef3(null);
12544
+ const editHistory = useRef4([]);
12545
+ const nextHistoryId = useRef4(1);
12546
+ const currentTurnEntry = useRef4(null);
11936
12547
  const [undoBanner, setUndoBanner] = useState7(null);
11937
- const undoTimeoutRef = useRef3(null);
12548
+ const undoTimeoutRef = useRef4(null);
11938
12549
  const recordEdit = useCallback2(
11939
12550
  (source, blocks, results, snaps) => {
11940
12551
  if (snaps.length === 0) return;
@@ -12130,12 +12741,12 @@ function useEditHistory(codeMode) {
12130
12741
  }
12131
12742
 
12132
12743
  // src/cli/ui/useSessionInfo.ts
12133
- import { useCallback as useCallback3, useEffect as useEffect3, useState as useState8 } from "react";
12744
+ import { useCallback as useCallback3, useEffect as useEffect4, useState as useState8 } from "react";
12134
12745
  function useSessionInfo(loop) {
12135
12746
  const [balance, setBalance] = useState8(null);
12136
12747
  const [models2, setModels] = useState8(null);
12137
12748
  const [latestVersion, setLatestVersion] = useState8(null);
12138
- useEffect3(() => {
12749
+ useEffect4(() => {
12139
12750
  let cancelled = false;
12140
12751
  void (async () => {
12141
12752
  const bal = await loop.client.getBalance().catch(() => null);
@@ -12147,7 +12758,7 @@ function useSessionInfo(loop) {
12147
12758
  cancelled = true;
12148
12759
  };
12149
12760
  }, [loop]);
12150
- useEffect3(() => {
12761
+ useEffect4(() => {
12151
12762
  let cancelled = false;
12152
12763
  void (async () => {
12153
12764
  const list = await loop.client.listModels().catch(() => null);
@@ -12158,7 +12769,7 @@ function useSessionInfo(loop) {
12158
12769
  cancelled = true;
12159
12770
  };
12160
12771
  }, [loop]);
12161
- useEffect3(() => {
12772
+ useEffect4(() => {
12162
12773
  let cancelled = false;
12163
12774
  void (async () => {
12164
12775
  const latest = await getLatestVersion();
@@ -12203,11 +12814,11 @@ function useSessionInfo(loop) {
12203
12814
  }
12204
12815
 
12205
12816
  // src/cli/ui/useSubagent.ts
12206
- import { useEffect as useEffect4, useRef as useRef4, useState as useState9 } from "react";
12817
+ import { useEffect as useEffect5, useRef as useRef5, useState as useState9 } from "react";
12207
12818
  function useSubagent({ session, setHistorical }) {
12208
12819
  const [activity, setActivity] = useState9(null);
12209
- const sinkRef = useRef4({ current: null });
12210
- useEffect4(() => {
12820
+ const sinkRef = useRef5({ current: null });
12821
+ useEffect5(() => {
12211
12822
  sinkRef.current.current = (ev) => {
12212
12823
  if (ev.kind === "start") {
12213
12824
  setActivity({
@@ -12280,15 +12891,17 @@ function App({
12280
12891
  const [streaming, setStreaming] = useState10(null);
12281
12892
  const [input, setInput] = useState10("");
12282
12893
  const [busy, setBusy] = useState10(false);
12283
- const abortedThisTurn = useRef5(false);
12894
+ const abortedThisTurn = useRef6(false);
12284
12895
  const [ongoingTool, setOngoingTool] = useState10(null);
12285
12896
  const [toolProgress, setToolProgress] = useState10(null);
12286
- const { stdout: stdout2 } = useStdout4();
12287
- useEffect5(() => {
12897
+ const { stdout: stdout2 } = useStdout5();
12898
+ useEffect6(() => {
12288
12899
  if (!stdout2 || !stdout2.isTTY) return;
12289
12900
  stdout2.write("\x1B[?2004h");
12901
+ stdout2.write("\x1B[>4;2m");
12290
12902
  return () => {
12291
12903
  stdout2.write("\x1B[?2004l");
12904
+ stdout2.write("\x1B[>4m");
12292
12905
  };
12293
12906
  }, [stdout2]);
12294
12907
  const { activity: subagentActivity, sinkRef: subagentSinkRef } = useSubagent({
@@ -12310,24 +12923,24 @@ function App({
12310
12923
  sealCurrentEntry,
12311
12924
  hasUndoable
12312
12925
  } = useEditHistory(codeMode);
12313
- const pendingEdits = useRef5([]);
12926
+ const pendingEdits = useRef6([]);
12314
12927
  const [pendingCount, setPendingCount] = useState10(0);
12315
12928
  const syncPendingCount = useCallback4(() => {
12316
12929
  setPendingCount(pendingEdits.current.length);
12317
12930
  }, []);
12318
12931
  const [editMode, setEditMode] = useState10(() => codeMode ? loadEditMode() : "review");
12319
- const editModeRef = useRef5(editMode);
12320
- useEffect5(() => {
12932
+ const editModeRef = useRef6(editMode);
12933
+ useEffect6(() => {
12321
12934
  editModeRef.current = editMode;
12322
12935
  if (codeMode) saveEditMode(editMode);
12323
12936
  }, [editMode, codeMode]);
12324
12937
  const [pendingEditReview, setPendingEditReview] = useState10(null);
12325
- const editReviewResolveRef = useRef5(null);
12326
- const turnEditPolicyRef = useRef5("ask");
12938
+ const editReviewResolveRef = useRef6(null);
12939
+ const turnEditPolicyRef = useRef6("ask");
12327
12940
  const [modeFlash, setModeFlash] = useState10(false);
12328
- const modeFlashTimeoutRef = useRef5(null);
12329
- const prevEditModeRef = useRef5(editMode);
12330
- useEffect5(() => {
12941
+ const modeFlashTimeoutRef = useRef6(null);
12942
+ const prevEditModeRef = useRef6(editMode);
12943
+ useEffect6(() => {
12331
12944
  if (prevEditModeRef.current === editMode) return;
12332
12945
  prevEditModeRef.current = editMode;
12333
12946
  setModeFlash(true);
@@ -12349,15 +12962,15 @@ function App({
12349
12962
  const [proArmed, setProArmed] = useState10(false);
12350
12963
  const [turnOnPro, setTurnOnPro] = useState10(false);
12351
12964
  const [queuedSubmit, setQueuedSubmit] = useState10(null);
12352
- const promptHistory = useRef5([]);
12353
- const historyCursor = useRef5(-1);
12354
- const assistantIterCounter = useRef5(0);
12355
- const toolHistoryRef = useRef5([]);
12356
- const planStepsRef = useRef5(null);
12357
- const completedStepIdsRef = useRef5(/* @__PURE__ */ new Set());
12358
- const planBodyRef = useRef5(null);
12359
- const planSummaryRef = useRef5(null);
12360
- const toolStartedAtRef = useRef5(null);
12965
+ const promptHistory = useRef6([]);
12966
+ const historyCursor = useRef6(-1);
12967
+ const assistantIterCounter = useRef6(0);
12968
+ const toolHistoryRef = useRef6([]);
12969
+ const planStepsRef = useRef6(null);
12970
+ const completedStepIdsRef = useRef6(/* @__PURE__ */ new Set());
12971
+ const planBodyRef = useRef6(null);
12972
+ const planSummaryRef = useRef6(null);
12973
+ const toolStartedAtRef = useRef6(null);
12361
12974
  const persistPlanState = useCallback4(() => {
12362
12975
  if (!session) return;
12363
12976
  const steps = planStepsRef.current;
@@ -12381,7 +12994,7 @@ function App({
12381
12994
  lastPromptTokens: 0,
12382
12995
  lastTurnCostUsd: 0
12383
12996
  });
12384
- const transcriptRef = useRef5(null);
12997
+ const transcriptRef = useRef6(null);
12385
12998
  if (transcript && !transcriptRef.current) {
12386
12999
  transcriptRef.current = openTranscriptFile(transcript, {
12387
13000
  version: 1,
@@ -12390,12 +13003,12 @@ function App({
12390
13003
  startedAt: (/* @__PURE__ */ new Date()).toISOString()
12391
13004
  });
12392
13005
  }
12393
- useEffect5(() => {
13006
+ useEffect6(() => {
12394
13007
  return () => {
12395
13008
  transcriptRef.current?.end();
12396
13009
  };
12397
13010
  }, []);
12398
- const loopRef = useRef5(null);
13011
+ const loopRef = useRef6(null);
12399
13012
  const loop = useMemo3(() => {
12400
13013
  if (loopRef.current) return loopRef.current;
12401
13014
  const client = new DeepSeekClient();
@@ -12444,7 +13057,7 @@ function App({
12444
13057
  loopRef.current = l;
12445
13058
  return l;
12446
13059
  }, [model2, system, harvest3, branch2, session, tools, codeMode]);
12447
- useEffect5(() => {
13060
+ useEffect6(() => {
12448
13061
  loop.hooks = hookList;
12449
13062
  }, [loop, hookList]);
12450
13063
  const {
@@ -12472,7 +13085,7 @@ function App({
12472
13085
  setSlashArgSelected,
12473
13086
  pickSlashArg
12474
13087
  } = useCompletionPickers({ input, setInput, codeMode, models: models2, mcpServers });
12475
- useEffect5(() => {
13088
+ useEffect6(() => {
12476
13089
  if (!progressSink) return;
12477
13090
  progressSink.current = (info) => {
12478
13091
  setToolProgress({
@@ -12485,8 +13098,8 @@ function App({
12485
13098
  if (progressSink.current) progressSink.current = null;
12486
13099
  };
12487
13100
  }, [progressSink]);
12488
- const sessionBannerShown = useRef5(false);
12489
- useEffect5(() => {
13101
+ const sessionBannerShown = useRef6(false);
13102
+ useEffect6(() => {
12490
13103
  if (sessionBannerShown.current) return;
12491
13104
  sessionBannerShown.current = true;
12492
13105
  if (!session) {
@@ -12568,7 +13181,26 @@ function App({
12568
13181
  markEditModeHintShown();
12569
13182
  }
12570
13183
  }, [session, loop, codeMode, syncPendingCount]);
12571
- useInput5((chKey, key) => {
13184
+ const quitProcess = useCallback4(() => {
13185
+ transcriptRef.current?.end();
13186
+ process.exit(0);
13187
+ }, []);
13188
+ useEffect6(() => {
13189
+ process.on("SIGINT", quitProcess);
13190
+ return () => {
13191
+ process.off("SIGINT", quitProcess);
13192
+ };
13193
+ }, [quitProcess]);
13194
+ useKeystroke((ev) => {
13195
+ const chKey = ev.input;
13196
+ const key = ev;
13197
+ if (ev.paste) {
13198
+ return;
13199
+ }
13200
+ if (key.ctrl && key.input === "c") {
13201
+ quitProcess();
13202
+ return;
13203
+ }
12572
13204
  if (key.escape && busy) {
12573
13205
  if (abortedThisTurn.current) return;
12574
13206
  abortedThisTurn.current = true;
@@ -12666,7 +13298,7 @@ function App({
12666
13298
  historyCursor.current = nextCursor;
12667
13299
  setInput(nextCursor < 0 ? "" : hist[hist.length - 1 - nextCursor] ?? "");
12668
13300
  }, []);
12669
- useEffect5(() => {
13301
+ useEffect6(() => {
12670
13302
  if (!tools || !codeMode) return;
12671
13303
  tools.setToolInterceptor(async (name, args) => {
12672
13304
  if (name !== "edit_file" && name !== "write_file") return null;
@@ -12750,7 +13382,7 @@ function App({
12750
13382
  if (!codeMode) return "not in code mode";
12751
13383
  const blocks = pendingEdits.current;
12752
13384
  if (blocks.length === 0) {
12753
- return "nothing pending \u2014 the assistant hasn't proposed edits since the last /apply or /discard.";
13385
+ return "nothing pending \u2014 the model hasn't proposed edits since the last /apply or /discard.";
12754
13386
  }
12755
13387
  const snaps = snapshotBeforeEdits(blocks, codeMode.rootDir);
12756
13388
  const results = applyEditBlocks(blocks, codeMode.rootDir);
@@ -13532,7 +14164,7 @@ ${body}`;
13532
14164
  },
13533
14165
  [pendingShell, codeMode, handleSubmit, busy, loop]
13534
14166
  );
13535
- useEffect5(() => {
14167
+ useEffect6(() => {
13536
14168
  if (!busy && queuedSubmit !== null) {
13537
14169
  const text = queuedSubmit;
13538
14170
  setQueuedSubmit(null);
@@ -13576,8 +14208,8 @@ ${body}`;
13576
14208
  },
13577
14209
  [pendingPlan, togglePlanMode, busy, loop, handleSubmit, persistPlanState]
13578
14210
  );
13579
- const handlePlanConfirmRef = useRef5(handlePlanConfirm);
13580
- useEffect5(() => {
14211
+ const handlePlanConfirmRef = useRef6(handlePlanConfirm);
14212
+ useEffect6(() => {
13581
14213
  handlePlanConfirmRef.current = handlePlanConfirm;
13582
14214
  }, [handlePlanConfirm]);
13583
14215
  const stableHandlePlanConfirm = useCallback4(
@@ -13668,8 +14300,8 @@ Stay in plan mode \u2014 address the feedback (explore more if needed), then sub
13668
14300
  },
13669
14301
  [pendingCheckpoint, busy, loop, handleSubmit]
13670
14302
  );
13671
- const handleCheckpointConfirmRef = useRef5(handleCheckpointConfirm);
13672
- useEffect5(() => {
14303
+ const handleCheckpointConfirmRef = useRef6(handleCheckpointConfirm);
14304
+ useEffect6(() => {
13673
14305
  handleCheckpointConfirmRef.current = handleCheckpointConfirm;
13674
14306
  }, [handleCheckpointConfirm]);
13675
14307
  const stableHandleCheckpointConfirm = useCallback4(
@@ -13746,8 +14378,8 @@ If the feedback only tweaks how you execute (extra constraints, style preference
13746
14378
  },
13747
14379
  [pendingChoice, busy, loop, handleSubmit]
13748
14380
  );
13749
- const handleChoiceConfirmRef = useRef5(handleChoiceConfirm);
13750
- useEffect5(() => {
14381
+ const handleChoiceConfirmRef = useRef6(handleChoiceConfirm);
14382
+ useEffect6(() => {
13751
14383
  handleChoiceConfirmRef.current = handleChoiceConfirm;
13752
14384
  }, [handleChoiceConfirm]);
13753
14385
  const stableHandleChoiceConfirm = useCallback4(
@@ -13837,20 +14469,20 @@ Continue executing from the next pending step. Call mark_step_complete after eac
13837
14469
  },
13838
14470
  [pendingRevision, busy, loop, handleSubmit, persistPlanState]
13839
14471
  );
13840
- const handleReviseConfirmRef = useRef5(handleReviseConfirm);
13841
- useEffect5(() => {
14472
+ const handleReviseConfirmRef = useRef6(handleReviseConfirm);
14473
+ useEffect6(() => {
13842
14474
  handleReviseConfirmRef.current = handleReviseConfirm;
13843
14475
  }, [handleReviseConfirm]);
13844
14476
  const stableHandleReviseConfirm = useCallback4(
13845
14477
  async (choice) => handleReviseConfirmRef.current(choice),
13846
14478
  []
13847
14479
  );
13848
- return /* @__PURE__ */ React20.createElement(
14480
+ return /* @__PURE__ */ React22.createElement(React22.Fragment, null, /* @__PURE__ */ React22.createElement(
13849
14481
  TickerProvider,
13850
14482
  {
13851
14483
  disabled: PLAIN_UI || !!pendingPlan || !!pendingShell || !!pendingEditReview || !!pendingCheckpoint || !!stagedCheckpointRevise || !!pendingChoice || !!stagedChoiceCustom || !!pendingRevision
13852
14484
  },
13853
- /* @__PURE__ */ React20.createElement(Box19, { flexDirection: "column" }, /* @__PURE__ */ React20.createElement(
14485
+ /* @__PURE__ */ React22.createElement(Box20, { flexDirection: "column" }, /* @__PURE__ */ React22.createElement(
13854
14486
  StatsPanel,
13855
14487
  {
13856
14488
  summary,
@@ -13867,28 +14499,28 @@ Continue executing from the next pending step. Call mark_step_complete after eac
13867
14499
  proArmed,
13868
14500
  escalated: turnOnPro
13869
14501
  }
13870
- ), /* @__PURE__ */ React20.createElement(Static, { items: historical }, (item) => /* @__PURE__ */ React20.createElement(EventRow, { key: item.id, event: item, projectRoot: hookCwd })), !PLAIN_UI && !pendingShell && !pendingPlan && !stagedInput && !pendingEditReview && !pendingCheckpoint && !stagedCheckpointRevise && streaming ? /* @__PURE__ */ React20.createElement(Box19, { marginY: 1 }, /* @__PURE__ */ React20.createElement(EventRow, { event: streaming, projectRoot: hookCwd })) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !stagedInput && !pendingEditReview && !pendingCheckpoint && !stagedCheckpointRevise && ongoingTool ? /* @__PURE__ */ React20.createElement(OngoingToolRow, { tool: ongoingTool, progress: toolProgress }) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !stagedInput && !pendingEditReview && !pendingCheckpoint && !stagedCheckpointRevise && subagentActivity ? /* @__PURE__ */ React20.createElement(SubagentRow, { activity: subagentActivity }) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !stagedInput && !pendingEditReview && !pendingCheckpoint && !stagedCheckpointRevise && !ongoingTool && statusLine ? /* @__PURE__ */ React20.createElement(StatusRow, { text: statusLine }) : null, !PLAIN_UI && undoBanner && !pendingShell && !pendingPlan && !stagedInput && !pendingEditReview && !pendingCheckpoint && !stagedCheckpointRevise && !pendingChoice && !stagedChoiceCustom && !pendingRevision ? /* @__PURE__ */ React20.createElement(UndoBanner, { banner: undoBanner }) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !stagedInput && !pendingEditReview && !pendingCheckpoint && !stagedCheckpointRevise && busy && !streaming && !ongoingTool && !statusLine ? /* @__PURE__ */ React20.createElement(StatusRow, { text: "processing\u2026" }) : null, stagedInput ? /* @__PURE__ */ React20.createElement(
14502
+ ), /* @__PURE__ */ React22.createElement(Static, { items: historical }, (item) => /* @__PURE__ */ React22.createElement(EventRow, { key: item.id, event: item, projectRoot: hookCwd })), !historical.some((e) => e.role === "user" || e.role === "assistant") && !busy && !streaming ? /* @__PURE__ */ React22.createElement(WelcomeBanner, { inCodeMode: !!codeMode }) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !stagedInput && !pendingEditReview && !pendingCheckpoint && !stagedCheckpointRevise && streaming ? /* @__PURE__ */ React22.createElement(Box20, { marginY: 1 }, /* @__PURE__ */ React22.createElement(EventRow, { event: streaming, projectRoot: hookCwd })) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !stagedInput && !pendingEditReview && !pendingCheckpoint && !stagedCheckpointRevise && ongoingTool ? /* @__PURE__ */ React22.createElement(OngoingToolRow, { tool: ongoingTool, progress: toolProgress }) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !stagedInput && !pendingEditReview && !pendingCheckpoint && !stagedCheckpointRevise && subagentActivity ? /* @__PURE__ */ React22.createElement(SubagentRow, { activity: subagentActivity }) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !stagedInput && !pendingEditReview && !pendingCheckpoint && !stagedCheckpointRevise && !ongoingTool && statusLine ? /* @__PURE__ */ React22.createElement(StatusRow, { text: statusLine }) : null, !PLAIN_UI && undoBanner && !pendingShell && !pendingPlan && !stagedInput && !pendingEditReview && !pendingCheckpoint && !stagedCheckpointRevise && !pendingChoice && !stagedChoiceCustom && !pendingRevision ? /* @__PURE__ */ React22.createElement(UndoBanner, { banner: undoBanner }) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !stagedInput && !pendingEditReview && !pendingCheckpoint && !stagedCheckpointRevise && busy && !streaming && !ongoingTool && !statusLine ? /* @__PURE__ */ React22.createElement(StatusRow, { text: "processing\u2026" }) : null, stagedInput ? /* @__PURE__ */ React22.createElement(
13871
14503
  PlanRefineInput,
13872
14504
  {
13873
14505
  mode: stagedInput.mode,
13874
14506
  onSubmit: handleStagedInputSubmit,
13875
14507
  onCancel: handleStagedInputCancel
13876
14508
  }
13877
- ) : stagedCheckpointRevise ? /* @__PURE__ */ React20.createElement(
14509
+ ) : stagedCheckpointRevise ? /* @__PURE__ */ React22.createElement(
13878
14510
  PlanRefineInput,
13879
14511
  {
13880
14512
  mode: "checkpoint-revise",
13881
14513
  onSubmit: handleCheckpointReviseSubmit,
13882
14514
  onCancel: handleCheckpointReviseCancel
13883
14515
  }
13884
- ) : stagedChoiceCustom ? /* @__PURE__ */ React20.createElement(
14516
+ ) : stagedChoiceCustom ? /* @__PURE__ */ React22.createElement(
13885
14517
  PlanRefineInput,
13886
14518
  {
13887
14519
  mode: "choice-custom",
13888
14520
  onSubmit: handleChoiceCustomSubmit,
13889
14521
  onCancel: handleChoiceCustomCancel
13890
14522
  }
13891
- ) : pendingChoice ? /* @__PURE__ */ React20.createElement(
14523
+ ) : pendingChoice ? /* @__PURE__ */ React22.createElement(
13892
14524
  ChoiceConfirm,
13893
14525
  {
13894
14526
  question: pendingChoice.question,
@@ -13896,7 +14528,7 @@ Continue executing from the next pending step. Call mark_step_complete after eac
13896
14528
  allowCustom: pendingChoice.allowCustom,
13897
14529
  onChoose: stableHandleChoiceConfirm
13898
14530
  }
13899
- ) : pendingRevision ? /* @__PURE__ */ React20.createElement(
14531
+ ) : pendingRevision ? /* @__PURE__ */ React22.createElement(
13900
14532
  PlanReviseConfirm,
13901
14533
  {
13902
14534
  reason: pendingRevision.reason,
@@ -13907,7 +14539,7 @@ Continue executing from the next pending step. Call mark_step_complete after eac
13907
14539
  summary: pendingRevision.summary,
13908
14540
  onChoose: stableHandleReviseConfirm
13909
14541
  }
13910
- ) : pendingCheckpoint ? /* @__PURE__ */ React20.createElement(
14542
+ ) : pendingCheckpoint ? /* @__PURE__ */ React22.createElement(
13911
14543
  PlanCheckpointConfirm,
13912
14544
  {
13913
14545
  stepId: pendingCheckpoint.stepId,
@@ -13918,7 +14550,7 @@ Continue executing from the next pending step. Call mark_step_complete after eac
13918
14550
  completedStepIds: completedStepIdsRef.current,
13919
14551
  onChoose: stableHandleCheckpointConfirm
13920
14552
  }
13921
- ) : pendingPlan ? /* @__PURE__ */ React20.createElement(
14553
+ ) : pendingPlan ? /* @__PURE__ */ React22.createElement(
13922
14554
  PlanConfirm,
13923
14555
  {
13924
14556
  plan: pendingPlan,
@@ -13927,7 +14559,7 @@ Continue executing from the next pending step. Call mark_step_complete after eac
13927
14559
  onChoose: stableHandlePlanConfirm,
13928
14560
  projectRoot: hookCwd
13929
14561
  }
13930
- ) : pendingShell ? /* @__PURE__ */ React20.createElement(
14562
+ ) : pendingShell ? /* @__PURE__ */ React22.createElement(
13931
14563
  ShellConfirm,
13932
14564
  {
13933
14565
  command: pendingShell.command,
@@ -13935,7 +14567,7 @@ Continue executing from the next pending step. Call mark_step_complete after eac
13935
14567
  kind: pendingShell.kind,
13936
14568
  onChoose: handleShellConfirm
13937
14569
  }
13938
- ) : pendingEditReview ? /* @__PURE__ */ React20.createElement(
14570
+ ) : pendingEditReview ? /* @__PURE__ */ React22.createElement(
13939
14571
  EditConfirm,
13940
14572
  {
13941
14573
  block: pendingEditReview,
@@ -13947,7 +14579,7 @@ Continue executing from the next pending step. Call mark_step_complete after eac
13947
14579
  }
13948
14580
  }
13949
14581
  }
13950
- ) : /* @__PURE__ */ React20.createElement(React20.Fragment, null, codeMode ? /* @__PURE__ */ React20.createElement(
14582
+ ) : /* @__PURE__ */ React22.createElement(React22.Fragment, null, codeMode ? /* @__PURE__ */ React22.createElement(
13951
14583
  ModeStatusBar,
13952
14584
  {
13953
14585
  editMode,
@@ -13957,7 +14589,7 @@ Continue executing from the next pending step. Call mark_step_complete after eac
13957
14589
  undoArmed: !!undoBanner || hasUndoable(),
13958
14590
  jobs: codeMode.jobs
13959
14591
  }
13960
- ) : null, /* @__PURE__ */ React20.createElement(
14592
+ ) : null, /* @__PURE__ */ React22.createElement(
13961
14593
  PromptInput,
13962
14594
  {
13963
14595
  value: input,
@@ -13967,14 +14599,14 @@ Continue executing from the next pending step. Call mark_step_complete after eac
13967
14599
  onHistoryPrev: recallPrev,
13968
14600
  onHistoryNext: recallNext
13969
14601
  }
13970
- ), /* @__PURE__ */ React20.createElement(SlashSuggestions, { matches: slashMatches, selectedIndex: slashSelected }), /* @__PURE__ */ React20.createElement(
14602
+ ), /* @__PURE__ */ React22.createElement(SlashSuggestions, { matches: slashMatches, selectedIndex: slashSelected }), /* @__PURE__ */ React22.createElement(
13971
14603
  AtMentionSuggestions,
13972
14604
  {
13973
14605
  matches: atMatches,
13974
14606
  selectedIndex: atSelected,
13975
14607
  query: atPicker?.query ?? ""
13976
14608
  }
13977
- ), slashArgContext ? /* @__PURE__ */ React20.createElement(
14609
+ ), slashArgContext ? /* @__PURE__ */ React22.createElement(
13978
14610
  SlashArgPicker,
13979
14611
  {
13980
14612
  matches: slashArgMatches,
@@ -13984,19 +14616,19 @@ Continue executing from the next pending step. Call mark_step_complete after eac
13984
14616
  partial: slashArgContext.partial
13985
14617
  }
13986
14618
  ) : null))
13987
- );
14619
+ ));
13988
14620
  }
13989
14621
 
13990
14622
  // src/cli/ui/SessionPicker.tsx
13991
- import { Box as Box20, Text as Text19 } from "ink";
13992
- import React21 from "react";
14623
+ import { Box as Box21, Text as Text20 } from "ink";
14624
+ import React23 from "react";
13993
14625
  function SessionPicker({
13994
14626
  sessionName,
13995
14627
  messageCount,
13996
14628
  lastActive,
13997
14629
  onChoose
13998
14630
  }) {
13999
- return /* @__PURE__ */ React21.createElement(Box20, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React21.createElement(Box20, { marginBottom: 1 }, /* @__PURE__ */ React21.createElement(Text19, { bold: true, color: "cyan" }, `Session "${sessionName}" has ${messageCount} prior message${messageCount === 1 ? "" : "s"}`), /* @__PURE__ */ React21.createElement(Text19, { dimColor: true }, ` \xB7 last active ${relativeTime2(lastActive)}`)), /* @__PURE__ */ React21.createElement(
14631
+ return /* @__PURE__ */ React23.createElement(Box21, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React23.createElement(Box21, { marginBottom: 1 }, /* @__PURE__ */ React23.createElement(Text20, { bold: true, color: "cyan" }, `Session "${sessionName}" has ${messageCount} prior message${messageCount === 1 ? "" : "s"}`), /* @__PURE__ */ React23.createElement(Text20, { dimColor: true }, ` \xB7 last active ${relativeTime2(lastActive)}`)), /* @__PURE__ */ React23.createElement(
14000
14632
  SingleSelect,
14001
14633
  {
14002
14634
  initialValue: "new",
@@ -14019,7 +14651,7 @@ function SessionPicker({
14019
14651
  ],
14020
14652
  onSubmit: (v) => onChoose(v)
14021
14653
  }
14022
- ), /* @__PURE__ */ React21.createElement(Box20, { marginTop: 1 }, /* @__PURE__ */ React21.createElement(Text19, { dimColor: true }, "\u2191\u2193 to move \xB7 Enter to pick")));
14654
+ ), /* @__PURE__ */ React23.createElement(Box21, { marginTop: 1 }, /* @__PURE__ */ React23.createElement(Text20, { dimColor: true }, "[\u2191\u2193] navigate \xB7 [Enter] select")));
14023
14655
  }
14024
14656
  function relativeTime2(date) {
14025
14657
  const ms = Date.now() - date.getTime();
@@ -14035,9 +14667,9 @@ function relativeTime2(date) {
14035
14667
  }
14036
14668
 
14037
14669
  // src/cli/ui/Setup.tsx
14038
- import { Box as Box21, Text as Text20, useApp as useApp2 } from "ink";
14670
+ import { Box as Box22, Text as Text21, useApp as useApp2 } from "ink";
14039
14671
  import TextInput from "ink-text-input";
14040
- import React22, { useState as useState11 } from "react";
14672
+ import React24, { useState as useState11 } from "react";
14041
14673
  function Setup({ onReady }) {
14042
14674
  const [value, setValue] = useState11("");
14043
14675
  const [error, setError] = useState11(null);
@@ -14061,7 +14693,7 @@ function Setup({ onReady }) {
14061
14693
  }
14062
14694
  onReady(trimmed);
14063
14695
  };
14064
- return /* @__PURE__ */ React22.createElement(Box21, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React22.createElement(Text20, { bold: true, color: "cyan" }, "Welcome to Reasonix."), /* @__PURE__ */ React22.createElement(Box21, { marginTop: 1 }, /* @__PURE__ */ React22.createElement(Text20, null, "Paste your DeepSeek API key to get started.")), /* @__PURE__ */ React22.createElement(Text20, { dimColor: true }, "Get one (free credit on signup): https://platform.deepseek.com/api_keys"), /* @__PURE__ */ React22.createElement(Text20, { dimColor: true }, "Saved locally to ", defaultConfigPath()), /* @__PURE__ */ React22.createElement(Box21, { marginTop: 1 }, /* @__PURE__ */ React22.createElement(Text20, { bold: true, color: "cyan" }, "key \u203A "), /* @__PURE__ */ React22.createElement(
14696
+ return /* @__PURE__ */ React24.createElement(Box22, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React24.createElement(Text21, { bold: true, color: "cyan" }, "Welcome to Reasonix."), /* @__PURE__ */ React24.createElement(Box22, { marginTop: 1 }, /* @__PURE__ */ React24.createElement(Text21, null, "Paste your DeepSeek API key to get started.")), /* @__PURE__ */ React24.createElement(Text21, { dimColor: true }, "Get one (free credit on signup): https://platform.deepseek.com/api_keys"), /* @__PURE__ */ React24.createElement(Text21, { dimColor: true }, "Saved locally to ", defaultConfigPath()), /* @__PURE__ */ React24.createElement(Box22, { marginTop: 1 }, /* @__PURE__ */ React24.createElement(Text21, { bold: true, color: "cyan" }, "key \u203A "), /* @__PURE__ */ React24.createElement(
14065
14697
  TextInput,
14066
14698
  {
14067
14699
  value,
@@ -14070,7 +14702,7 @@ function Setup({ onReady }) {
14070
14702
  mask: "\u2022",
14071
14703
  placeholder: "sk-..."
14072
14704
  }
14073
- )), error ? /* @__PURE__ */ React22.createElement(Box21, { marginTop: 1 }, /* @__PURE__ */ React22.createElement(Text20, { color: "red" }, error)) : value ? /* @__PURE__ */ React22.createElement(Box21, { marginTop: 1 }, /* @__PURE__ */ React22.createElement(Text20, { dimColor: true }, "preview: ", redactKey(value))) : null, /* @__PURE__ */ React22.createElement(Box21, { marginTop: 1 }, /* @__PURE__ */ React22.createElement(Text20, { dimColor: true }, "(Type /exit to abort.)")));
14705
+ )), error ? /* @__PURE__ */ React24.createElement(Box22, { marginTop: 1 }, /* @__PURE__ */ React24.createElement(Text21, { color: "red" }, error)) : value ? /* @__PURE__ */ React24.createElement(Box22, { marginTop: 1 }, /* @__PURE__ */ React24.createElement(Text21, { dimColor: true }, "preview: ", redactKey(value))) : null, /* @__PURE__ */ React24.createElement(Box22, { marginTop: 1 }, /* @__PURE__ */ React24.createElement(Text21, { dimColor: true }, "(Type /exit to abort.)")));
14074
14706
  }
14075
14707
 
14076
14708
  // src/cli/commands/chat.tsx
@@ -14086,7 +14718,7 @@ function Root({
14086
14718
  const [key, setKey] = useState12(initialKey);
14087
14719
  const [pending, setPending] = useState12(sessionPreview);
14088
14720
  if (!key) {
14089
- return /* @__PURE__ */ React23.createElement(
14721
+ return /* @__PURE__ */ React25.createElement(
14090
14722
  Setup,
14091
14723
  {
14092
14724
  onReady: (k) => {
@@ -14098,7 +14730,7 @@ function Root({
14098
14730
  }
14099
14731
  process.env.DEEPSEEK_API_KEY = key;
14100
14732
  if (pending && appProps.session) {
14101
- return /* @__PURE__ */ React23.createElement(
14733
+ return /* @__PURE__ */ React25.createElement(KeystrokeProvider, null, /* @__PURE__ */ React25.createElement(
14102
14734
  SessionPicker,
14103
14735
  {
14104
14736
  sessionName: appProps.session,
@@ -14111,9 +14743,9 @@ function Root({
14111
14743
  setPending(void 0);
14112
14744
  }
14113
14745
  }
14114
- );
14746
+ ));
14115
14747
  }
14116
- return /* @__PURE__ */ React23.createElement(
14748
+ return /* @__PURE__ */ React25.createElement(KeystrokeProvider, null, /* @__PURE__ */ React25.createElement(
14117
14749
  App,
14118
14750
  {
14119
14751
  model: appProps.model,
@@ -14128,7 +14760,7 @@ function Root({
14128
14760
  progressSink,
14129
14761
  codeMode: appProps.codeMode
14130
14762
  }
14131
- );
14763
+ ));
14132
14764
  }
14133
14765
  async function chatCommand(opts) {
14134
14766
  loadDotenv();
@@ -14218,7 +14850,7 @@ async function chatCommand(opts) {
14218
14850
  rewriteSession(opts.session, []);
14219
14851
  }
14220
14852
  const { waitUntilExit } = render(
14221
- /* @__PURE__ */ React23.createElement(
14853
+ /* @__PURE__ */ React25.createElement(
14222
14854
  Root,
14223
14855
  {
14224
14856
  initialKey,
@@ -14267,12 +14899,9 @@ async function codeCommand(opts = {}) {
14267
14899
  `\u25B8 reasonix code: rooted at ${rootDir}, session "${session ?? "(ephemeral)"}" \xB7 ${tools.size} native tool(s)
14268
14900
  `
14269
14901
  );
14270
- const sigShutdown = () => {
14902
+ process.once("exit", () => {
14271
14903
  void jobs2.shutdown();
14272
- };
14273
- process.once("SIGINT", sigShutdown);
14274
- process.once("SIGTERM", sigShutdown);
14275
- process.once("exit", sigShutdown);
14904
+ });
14276
14905
  await chatCommand({
14277
14906
  model: opts.model ?? "deepseek-v4-flash",
14278
14907
  harvest: opts.harvest ?? false,
@@ -14290,34 +14919,35 @@ async function codeCommand(opts = {}) {
14290
14919
  import { writeFileSync as writeFileSync7 } from "fs";
14291
14920
  import { basename as basename3 } from "path";
14292
14921
  import { render as render2 } from "ink";
14293
- import React26 from "react";
14922
+ import React28 from "react";
14294
14923
 
14295
14924
  // src/cli/ui/DiffApp.tsx
14296
- import { Box as Box23, Static as Static2, Text as Text22, useApp as useApp3, useInput as useInput6 } from "ink";
14297
- import React25, { useState as useState13 } from "react";
14925
+ import { Box as Box24, Static as Static2, Text as Text23, useApp as useApp3, useInput } from "ink";
14926
+ import React27, { useState as useState13 } from "react";
14298
14927
 
14299
14928
  // src/cli/ui/RecordView.tsx
14300
- import { Box as Box22, Text as Text21 } from "ink";
14301
- import React24 from "react";
14929
+ import { Box as Box23, Text as Text22 } from "ink";
14930
+ import React26 from "react";
14302
14931
  function RecordView({ rec, compact: compact2 = false }) {
14303
14932
  const toolArgsMax = compact2 ? 120 : 200;
14304
14933
  const toolContentMax = compact2 ? 200 : 400;
14305
14934
  if (rec.role === "user") {
14306
- return /* @__PURE__ */ React24.createElement(Box22, { marginTop: 1 }, /* @__PURE__ */ React24.createElement(Text21, { bold: true, color: "cyan" }, "you \u203A", " "), /* @__PURE__ */ React24.createElement(Text21, null, rec.content));
14935
+ const content = rec.content.includes("\n") ? rec.content.split("\n").join("\n ") : rec.content;
14936
+ return /* @__PURE__ */ React26.createElement(Box23, { marginTop: 1 }, /* @__PURE__ */ React26.createElement(Text22, { bold: true, color: "cyan" }, "you \u203A", " "), /* @__PURE__ */ React26.createElement(Text22, null, content));
14307
14937
  }
14308
14938
  if (rec.role === "assistant_final") {
14309
- return /* @__PURE__ */ React24.createElement(Box22, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React24.createElement(Box22, null, /* @__PURE__ */ React24.createElement(Text21, { bold: true, color: "green" }, "assistant"), rec.cost !== void 0 ? /* @__PURE__ */ React24.createElement(Text21, { dimColor: true }, " $", rec.cost.toFixed(6)) : null, rec.usage ? /* @__PURE__ */ React24.createElement(CacheBadge, { usage: rec.usage }) : null), rec.planState ? /* @__PURE__ */ React24.createElement(PlanStateBlock, { planState: rec.planState }) : null, rec.content ? /* @__PURE__ */ React24.createElement(Text21, null, rec.content) : /* @__PURE__ */ React24.createElement(Text21, { dimColor: true, italic: true }, "(tool-call response only)"));
14939
+ return /* @__PURE__ */ React26.createElement(Box23, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React26.createElement(Box23, null, /* @__PURE__ */ React26.createElement(Text22, { bold: true, color: "green" }, "assistant"), rec.cost !== void 0 ? /* @__PURE__ */ React26.createElement(Text22, { dimColor: true }, " $", rec.cost.toFixed(6)) : null, rec.usage ? /* @__PURE__ */ React26.createElement(CacheBadge, { usage: rec.usage }) : null), rec.planState ? /* @__PURE__ */ React26.createElement(PlanStateBlock, { planState: rec.planState }) : null, rec.content ? /* @__PURE__ */ React26.createElement(Text22, null, rec.content) : /* @__PURE__ */ React26.createElement(Text22, { dimColor: true, italic: true }, "(tool-call response only)"));
14310
14940
  }
14311
14941
  if (rec.role === "tool") {
14312
- return /* @__PURE__ */ React24.createElement(Box22, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React24.createElement(Text21, { color: "yellow" }, "tool<", rec.tool ?? "?", ">"), rec.args ? /* @__PURE__ */ React24.createElement(Text21, { dimColor: true }, " args: ", truncate2(rec.args, toolArgsMax)) : null, /* @__PURE__ */ React24.createElement(Text21, { dimColor: true }, " \u2192 ", truncate2(rec.content, toolContentMax)));
14942
+ return /* @__PURE__ */ React26.createElement(Box23, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React26.createElement(Text22, { color: "yellow" }, "tool<", rec.tool ?? "?", ">"), rec.args ? /* @__PURE__ */ React26.createElement(Text22, { dimColor: true }, " args: ", truncate2(rec.args, toolArgsMax)) : null, /* @__PURE__ */ React26.createElement(Text22, { dimColor: true }, " \u2192 ", truncate2(rec.content, toolContentMax)));
14313
14943
  }
14314
14944
  if (rec.role === "error") {
14315
- return /* @__PURE__ */ React24.createElement(Box22, { marginTop: 1 }, /* @__PURE__ */ React24.createElement(Text21, { color: "red", bold: true }, "error", " "), /* @__PURE__ */ React24.createElement(Text21, { color: "red" }, rec.error ?? rec.content));
14945
+ return /* @__PURE__ */ React26.createElement(Box23, { marginTop: 1 }, /* @__PURE__ */ React26.createElement(Text22, { color: "red", bold: true }, "error", " "), /* @__PURE__ */ React26.createElement(Text22, { color: "red" }, rec.error ?? rec.content));
14316
14946
  }
14317
14947
  if (rec.role === "done" || rec.role === "assistant_delta") {
14318
14948
  return null;
14319
14949
  }
14320
- return /* @__PURE__ */ React24.createElement(Box22, null, /* @__PURE__ */ React24.createElement(Text21, { dimColor: true }, "[", rec.role, "] ", rec.content));
14950
+ return /* @__PURE__ */ React26.createElement(Box23, null, /* @__PURE__ */ React26.createElement(Text22, { dimColor: true }, "[", rec.role, "] ", rec.content));
14321
14951
  }
14322
14952
  function CacheBadge({ usage }) {
14323
14953
  const hit = usage.prompt_cache_hit_tokens ?? 0;
@@ -14326,7 +14956,7 @@ function CacheBadge({ usage }) {
14326
14956
  if (total === 0) return null;
14327
14957
  const pct2 = hit / total * 100;
14328
14958
  const color = pct2 >= 70 ? "green" : pct2 >= 40 ? "yellow" : "red";
14329
- return /* @__PURE__ */ React24.createElement(Text21, null, /* @__PURE__ */ React24.createElement(Text21, { dimColor: true }, " \xB7 cache "), /* @__PURE__ */ React24.createElement(Text21, { color }, pct2.toFixed(1), "%"));
14959
+ return /* @__PURE__ */ React26.createElement(Text22, null, /* @__PURE__ */ React26.createElement(Text22, { dimColor: true }, " \xB7 cache "), /* @__PURE__ */ React26.createElement(Text22, { color }, pct2.toFixed(1), "%"));
14330
14960
  }
14331
14961
  function truncate2(s, max) {
14332
14962
  return s.length <= max ? s : `${s.slice(0, max)}\u2026 (+${s.length - max} chars)`;
@@ -14338,7 +14968,7 @@ function DiffApp({ report }) {
14338
14968
  const maxIdx = Math.max(0, report.pairs.length - 1);
14339
14969
  const initialIdx = report.firstDivergenceTurn ? report.pairs.findIndex((p) => p.turn === report.firstDivergenceTurn) : 0;
14340
14970
  const [idx, setIdx] = useState13(Math.max(0, initialIdx));
14341
- useInput6((input, key) => {
14971
+ useInput((input, key) => {
14342
14972
  if (input === "q" || key.ctrl && input === "c") {
14343
14973
  exit2();
14344
14974
  return;
@@ -14360,7 +14990,7 @@ function DiffApp({ report }) {
14360
14990
  }
14361
14991
  });
14362
14992
  const pair = report.pairs[idx];
14363
- return /* @__PURE__ */ React25.createElement(Box23, { flexDirection: "column" }, /* @__PURE__ */ React25.createElement(DiffHeader, { report }), /* @__PURE__ */ React25.createElement(Box23, { marginTop: 1, paddingX: 1, justifyContent: "space-between" }, /* @__PURE__ */ React25.createElement(Text22, { color: "cyan", bold: true }, "turn ", pair?.turn ?? "?", " (", idx + 1, " / ", report.pairs.length, ")"), /* @__PURE__ */ React25.createElement(Text22, null, pair ? /* @__PURE__ */ React25.createElement(KindBadge, { kind: pair.kind }) : null)), /* @__PURE__ */ React25.createElement(Box23, { flexDirection: "row", marginTop: 1 }, /* @__PURE__ */ React25.createElement(Pane, { label: report.a.label, headerColor: "blue", records: paneRecords(pair, "a") }), /* @__PURE__ */ React25.createElement(Pane, { label: report.b.label, headerColor: "magenta", records: paneRecords(pair, "b") })), pair?.divergenceNote ? /* @__PURE__ */ React25.createElement(Box23, { marginTop: 1, paddingX: 1 }, /* @__PURE__ */ React25.createElement(Text22, { color: "yellow" }, "\u2605 "), /* @__PURE__ */ React25.createElement(Text22, null, pair.divergenceNote)) : null, /* @__PURE__ */ React25.createElement(Box23, { marginTop: 1, paddingX: 1, borderStyle: "single", borderColor: "gray" }, /* @__PURE__ */ React25.createElement(Text22, { dimColor: true }, /* @__PURE__ */ React25.createElement(Text22, { bold: true }, "j"), "/", /* @__PURE__ */ React25.createElement(Text22, { bold: true }, "\u2193"), " next \xB7 ", /* @__PURE__ */ React25.createElement(Text22, { bold: true }, "k"), "/", /* @__PURE__ */ React25.createElement(Text22, { bold: true }, "\u2191"), " ", "prev \xB7 ", /* @__PURE__ */ React25.createElement(Text22, { bold: true }, "n"), " next-diverge \xB7 ", /* @__PURE__ */ React25.createElement(Text22, { bold: true }, "N"), "/", /* @__PURE__ */ React25.createElement(Text22, { bold: true }, "p"), " ", "prev-diverge \xB7 ", /* @__PURE__ */ React25.createElement(Text22, { bold: true }, "g"), "/", /* @__PURE__ */ React25.createElement(Text22, { bold: true }, "G"), " first/last \xB7 ", /* @__PURE__ */ React25.createElement(Text22, { bold: true }, "q"), " ", "quit")));
14993
+ return /* @__PURE__ */ React27.createElement(Box24, { flexDirection: "column" }, /* @__PURE__ */ React27.createElement(DiffHeader, { report }), /* @__PURE__ */ React27.createElement(Box24, { marginTop: 1, paddingX: 1, justifyContent: "space-between" }, /* @__PURE__ */ React27.createElement(Text23, { color: "cyan", bold: true }, "turn ", pair?.turn ?? "?", " (", idx + 1, " / ", report.pairs.length, ")"), /* @__PURE__ */ React27.createElement(Text23, null, pair ? /* @__PURE__ */ React27.createElement(KindBadge, { kind: pair.kind }) : null)), /* @__PURE__ */ React27.createElement(Box24, { flexDirection: "row", marginTop: 1 }, /* @__PURE__ */ React27.createElement(Pane, { label: report.a.label, headerColor: "blue", records: paneRecords(pair, "a") }), /* @__PURE__ */ React27.createElement(Pane, { label: report.b.label, headerColor: "magenta", records: paneRecords(pair, "b") })), pair?.divergenceNote ? /* @__PURE__ */ React27.createElement(Box24, { marginTop: 1, paddingX: 1 }, /* @__PURE__ */ React27.createElement(Text23, { color: "yellow" }, "\u2605 "), /* @__PURE__ */ React27.createElement(Text23, null, pair.divergenceNote)) : null, /* @__PURE__ */ React27.createElement(Box24, { marginTop: 1, paddingX: 1, borderStyle: "single", borderColor: "gray" }, /* @__PURE__ */ React27.createElement(Text23, { dimColor: true }, /* @__PURE__ */ React27.createElement(Text23, { bold: true }, "j"), "/", /* @__PURE__ */ React27.createElement(Text23, { bold: true }, "\u2193"), " next \xB7 ", /* @__PURE__ */ React27.createElement(Text23, { bold: true }, "k"), "/", /* @__PURE__ */ React27.createElement(Text23, { bold: true }, "\u2191"), " ", "prev \xB7 ", /* @__PURE__ */ React27.createElement(Text23, { bold: true }, "n"), " next-diverge \xB7 ", /* @__PURE__ */ React27.createElement(Text23, { bold: true }, "N"), "/", /* @__PURE__ */ React27.createElement(Text23, { bold: true }, "p"), " ", "prev-diverge \xB7 ", /* @__PURE__ */ React27.createElement(Text23, { bold: true }, "g"), "/", /* @__PURE__ */ React27.createElement(Text23, { bold: true }, "G"), " first/last \xB7 ", /* @__PURE__ */ React27.createElement(Text23, { bold: true }, "q"), " ", "quit")));
14364
14994
  }
14365
14995
  function DiffHeader({ report }) {
14366
14996
  const a = report.a;
@@ -14378,15 +15008,15 @@ function DiffHeader({ report }) {
14378
15008
  } else if (a.stats.prefixHashes[0] && a.stats.prefixHashes[0] === b.stats.prefixHashes[0]) {
14379
15009
  prefixLine = `shared prefix hash ${a.stats.prefixHashes[0].slice(0, 12)}\u2026 \u2014 cache delta attributable to log stability, not prompt change.`;
14380
15010
  }
14381
- return /* @__PURE__ */ React25.createElement(Box23, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React25.createElement(Box23, { justifyContent: "space-between" }, /* @__PURE__ */ React25.createElement(Text22, null, /* @__PURE__ */ React25.createElement(Text22, { color: "cyan", bold: true }, "reasonix diff"), /* @__PURE__ */ React25.createElement(Text22, { dimColor: true }, " \xB7 A="), /* @__PURE__ */ React25.createElement(Text22, { color: "blue" }, a.label), /* @__PURE__ */ React25.createElement(Text22, { dimColor: true }, " vs B="), /* @__PURE__ */ React25.createElement(Text22, { color: "magenta" }, b.label)), /* @__PURE__ */ React25.createElement(Text22, { dimColor: true }, report.pairs.length, " turns aligned")), /* @__PURE__ */ React25.createElement(Box23, { marginTop: 1, gap: 3 }, /* @__PURE__ */ React25.createElement(Text22, null, /* @__PURE__ */ React25.createElement(Text22, { dimColor: true }, "cache "), /* @__PURE__ */ React25.createElement(Text22, null, (a.stats.cacheHitRatio * 100).toFixed(1), "%"), /* @__PURE__ */ React25.createElement(Text22, { dimColor: true }, " \u2192 "), /* @__PURE__ */ React25.createElement(Text22, null, (b.stats.cacheHitRatio * 100).toFixed(1), "%"), /* @__PURE__ */ React25.createElement(Text22, { color: cacheDelta >= 0 ? "green" : "red", bold: true }, " ", cacheDelta >= 0 ? "+" : "", (cacheDelta * 100).toFixed(1), "pp")), /* @__PURE__ */ React25.createElement(Text22, null, /* @__PURE__ */ React25.createElement(Text22, { dimColor: true }, "cost "), /* @__PURE__ */ React25.createElement(Text22, null, "$", a.stats.totalCostUsd.toFixed(6)), /* @__PURE__ */ React25.createElement(Text22, { dimColor: true }, " \u2192 "), /* @__PURE__ */ React25.createElement(Text22, null, "$", b.stats.totalCostUsd.toFixed(6)), /* @__PURE__ */ React25.createElement(Text22, { color: costDelta2 <= 0 ? "green" : "red", bold: true }, " ", costDelta2 >= 0 ? "+" : "", costDelta2.toFixed(1), "%")), /* @__PURE__ */ React25.createElement(Text22, null, /* @__PURE__ */ React25.createElement(Text22, { dimColor: true }, "model calls "), /* @__PURE__ */ React25.createElement(Text22, null, a.stats.turns, " \u2192 ", b.stats.turns))), prefixLine ? /* @__PURE__ */ React25.createElement(Box23, { marginTop: 1 }, /* @__PURE__ */ React25.createElement(Text22, { dimColor: true, italic: true }, prefixLine)) : null);
15011
+ return /* @__PURE__ */ React27.createElement(Box24, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React27.createElement(Box24, { justifyContent: "space-between" }, /* @__PURE__ */ React27.createElement(Text23, null, /* @__PURE__ */ React27.createElement(Text23, { color: "cyan", bold: true }, "reasonix diff"), /* @__PURE__ */ React27.createElement(Text23, { dimColor: true }, " \xB7 A="), /* @__PURE__ */ React27.createElement(Text23, { color: "blue" }, a.label), /* @__PURE__ */ React27.createElement(Text23, { dimColor: true }, " vs B="), /* @__PURE__ */ React27.createElement(Text23, { color: "magenta" }, b.label)), /* @__PURE__ */ React27.createElement(Text23, { dimColor: true }, report.pairs.length, " turns aligned")), /* @__PURE__ */ React27.createElement(Box24, { marginTop: 1, gap: 3 }, /* @__PURE__ */ React27.createElement(Text23, null, /* @__PURE__ */ React27.createElement(Text23, { dimColor: true }, "cache "), /* @__PURE__ */ React27.createElement(Text23, null, (a.stats.cacheHitRatio * 100).toFixed(1), "%"), /* @__PURE__ */ React27.createElement(Text23, { dimColor: true }, " \u2192 "), /* @__PURE__ */ React27.createElement(Text23, null, (b.stats.cacheHitRatio * 100).toFixed(1), "%"), /* @__PURE__ */ React27.createElement(Text23, { color: cacheDelta >= 0 ? "green" : "red", bold: true }, " ", cacheDelta >= 0 ? "+" : "", (cacheDelta * 100).toFixed(1), "pp")), /* @__PURE__ */ React27.createElement(Text23, null, /* @__PURE__ */ React27.createElement(Text23, { dimColor: true }, "cost "), /* @__PURE__ */ React27.createElement(Text23, null, "$", a.stats.totalCostUsd.toFixed(6)), /* @__PURE__ */ React27.createElement(Text23, { dimColor: true }, " \u2192 "), /* @__PURE__ */ React27.createElement(Text23, null, "$", b.stats.totalCostUsd.toFixed(6)), /* @__PURE__ */ React27.createElement(Text23, { color: costDelta2 <= 0 ? "green" : "red", bold: true }, " ", costDelta2 >= 0 ? "+" : "", costDelta2.toFixed(1), "%")), /* @__PURE__ */ React27.createElement(Text23, null, /* @__PURE__ */ React27.createElement(Text23, { dimColor: true }, "model calls "), /* @__PURE__ */ React27.createElement(Text23, null, a.stats.turns, " \u2192 ", b.stats.turns))), prefixLine ? /* @__PURE__ */ React27.createElement(Box24, { marginTop: 1 }, /* @__PURE__ */ React27.createElement(Text23, { dimColor: true, italic: true }, prefixLine)) : null);
14382
15012
  }
14383
15013
  function Pane({
14384
15014
  label,
14385
15015
  headerColor,
14386
15016
  records
14387
15017
  }) {
14388
- return /* @__PURE__ */ React25.createElement(
14389
- Box23,
15018
+ return /* @__PURE__ */ React27.createElement(
15019
+ Box24,
14390
15020
  {
14391
15021
  flexDirection: "column",
14392
15022
  flexGrow: 1,
@@ -14394,21 +15024,21 @@ function Pane({
14394
15024
  borderStyle: "single",
14395
15025
  borderColor: headerColor
14396
15026
  },
14397
- /* @__PURE__ */ React25.createElement(Text22, { color: headerColor, bold: true }, label),
14398
- records.length === 0 ? /* @__PURE__ */ React25.createElement(Box23, { marginTop: 1 }, /* @__PURE__ */ React25.createElement(Text22, { dimColor: true, italic: true }, "(no records on this side for this turn)")) : /* @__PURE__ */ React25.createElement(Static2, { items: records.map((rec, i) => ({ key: `${label}-${i}`, rec })) }, ({ key, rec }) => /* @__PURE__ */ React25.createElement(RecordView, { key, rec, compact: true }))
15027
+ /* @__PURE__ */ React27.createElement(Text23, { color: headerColor, bold: true }, label),
15028
+ records.length === 0 ? /* @__PURE__ */ React27.createElement(Box24, { marginTop: 1 }, /* @__PURE__ */ React27.createElement(Text23, { dimColor: true, italic: true }, "(no records on this side for this turn)")) : /* @__PURE__ */ React27.createElement(Static2, { items: records.map((rec, i) => ({ key: `${label}-${i}`, rec })) }, ({ key, rec }) => /* @__PURE__ */ React27.createElement(RecordView, { key, rec, compact: true }))
14399
15029
  );
14400
15030
  }
14401
15031
  function KindBadge({ kind }) {
14402
15032
  if (kind === "match") {
14403
- return /* @__PURE__ */ React25.createElement(Text22, { color: "green" }, "\u2713 match");
15033
+ return /* @__PURE__ */ React27.createElement(Text23, { color: "green" }, "\u2713 match");
14404
15034
  }
14405
15035
  if (kind === "diverge") {
14406
- return /* @__PURE__ */ React25.createElement(Text22, { color: "yellow" }, "\u2605 diverge");
15036
+ return /* @__PURE__ */ React27.createElement(Text23, { color: "yellow" }, "\u2605 diverge");
14407
15037
  }
14408
15038
  if (kind === "only_in_a") {
14409
- return /* @__PURE__ */ React25.createElement(Text22, { color: "blue" }, "\u2190 only in A");
15039
+ return /* @__PURE__ */ React27.createElement(Text23, { color: "blue" }, "\u2190 only in A");
14410
15040
  }
14411
- return /* @__PURE__ */ React25.createElement(Text22, { color: "magenta" }, "\u2192 only in B");
15041
+ return /* @__PURE__ */ React27.createElement(Text23, { color: "magenta" }, "\u2192 only in B");
14412
15042
  }
14413
15043
  function paneRecords(pair, side) {
14414
15044
  if (!pair) return [];
@@ -14439,7 +15069,7 @@ markdown report written to ${opts.mdPath}`);
14439
15069
  return;
14440
15070
  }
14441
15071
  if (wantTui) {
14442
- const { waitUntilExit } = render2(React26.createElement(DiffApp, { report }), {
15072
+ const { waitUntilExit } = render2(React28.createElement(DiffApp, { report }), {
14443
15073
  exitOnCtrlC: true,
14444
15074
  patchConsole: false
14445
15075
  });
@@ -14580,16 +15210,16 @@ function pad2(s, width) {
14580
15210
 
14581
15211
  // src/cli/commands/replay.ts
14582
15212
  import { render as render3 } from "ink";
14583
- import React28 from "react";
15213
+ import React30 from "react";
14584
15214
 
14585
15215
  // src/cli/ui/ReplayApp.tsx
14586
- import { Box as Box24, Static as Static3, Text as Text23, useApp as useApp4, useInput as useInput7 } from "ink";
14587
- import React27, { useMemo as useMemo4, useState as useState14 } from "react";
15216
+ import { Box as Box25, Static as Static3, Text as Text24, useApp as useApp4, useInput as useInput2 } from "ink";
15217
+ import React29, { useMemo as useMemo4, useState as useState14 } from "react";
14588
15218
  function ReplayApp({ meta, pages }) {
14589
15219
  const { exit: exit2 } = useApp4();
14590
15220
  const maxIdx = Math.max(0, pages.length - 1);
14591
15221
  const [idx, setIdx] = useState14(maxIdx);
14592
- useInput7((input, key) => {
15222
+ useInput2((input, key) => {
14593
15223
  if (input === "q" || key.ctrl && input === "c") {
14594
15224
  exit2();
14595
15225
  return;
@@ -14624,14 +15254,14 @@ function ReplayApp({ meta, pages }) {
14624
15254
  const prefixHash = cumStats.prefixHashes.length === 1 ? cumStats.prefixHashes[0].slice(0, 16) : cumStats.prefixHashes.length === 0 ? "(untracked)" : `(churned \xD7${cumStats.prefixHashes.length})`;
14625
15255
  const currentPage = pages[idx];
14626
15256
  const progressLabel = pages.length === 0 ? "empty transcript" : `turn ${idx + 1} / ${pages.length}`;
14627
- return /* @__PURE__ */ React27.createElement(Box24, { flexDirection: "column" }, /* @__PURE__ */ React27.createElement(
15257
+ return /* @__PURE__ */ React29.createElement(Box25, { flexDirection: "column" }, /* @__PURE__ */ React29.createElement(
14628
15258
  StatsPanel,
14629
15259
  {
14630
15260
  summary,
14631
15261
  model: cumStats.models[0] ?? meta?.model ?? "?",
14632
15262
  prefixHash
14633
15263
  }
14634
- ), /* @__PURE__ */ React27.createElement(Box24, { flexDirection: "column", marginTop: 1, paddingX: 1 }, /* @__PURE__ */ React27.createElement(Box24, { justifyContent: "space-between" }, /* @__PURE__ */ React27.createElement(Text23, { color: "cyan", bold: true }, progressLabel), meta ? /* @__PURE__ */ React27.createElement(Text23, { dimColor: true }, meta.source, meta.task ? ` \xB7 ${meta.task}` : "", meta.mode ? ` \xB7 ${meta.mode}` : "") : null), currentPage ? /* @__PURE__ */ React27.createElement(Static3, { items: currentPage.records.map((rec, i) => ({ key: `${idx}-${i}`, rec })) }, ({ key, rec }) => /* @__PURE__ */ React27.createElement(RecordView, { key, rec })) : /* @__PURE__ */ React27.createElement(Text23, { dimColor: true, italic: true }, "no records")), /* @__PURE__ */ React27.createElement(Box24, { marginTop: 1, paddingX: 1, borderStyle: "single", borderColor: "gray" }, /* @__PURE__ */ React27.createElement(Text23, { dimColor: true }, /* @__PURE__ */ React27.createElement(Text23, { bold: true }, "j"), "/", /* @__PURE__ */ React27.createElement(Text23, { bold: true }, "\u2193"), "/", /* @__PURE__ */ React27.createElement(Text23, { bold: true }, "space"), " next \xB7 ", /* @__PURE__ */ React27.createElement(Text23, { bold: true }, "k"), "/", /* @__PURE__ */ React27.createElement(Text23, { bold: true }, "\u2191"), " prev \xB7 ", /* @__PURE__ */ React27.createElement(Text23, { bold: true }, "g"), " first \xB7 ", /* @__PURE__ */ React27.createElement(Text23, { bold: true }, "G"), " last \xB7", " ", /* @__PURE__ */ React27.createElement(Text23, { bold: true }, "q"), " quit")));
15264
+ ), /* @__PURE__ */ React29.createElement(Box25, { flexDirection: "column", marginTop: 1, paddingX: 1 }, /* @__PURE__ */ React29.createElement(Box25, { justifyContent: "space-between" }, /* @__PURE__ */ React29.createElement(Text24, { color: "cyan", bold: true }, progressLabel), meta ? /* @__PURE__ */ React29.createElement(Text24, { dimColor: true }, meta.source, meta.task ? ` \xB7 ${meta.task}` : "", meta.mode ? ` \xB7 ${meta.mode}` : "") : null), currentPage ? /* @__PURE__ */ React29.createElement(Static3, { items: currentPage.records.map((rec, i) => ({ key: `${idx}-${i}`, rec })) }, ({ key, rec }) => /* @__PURE__ */ React29.createElement(RecordView, { key, rec })) : /* @__PURE__ */ React29.createElement(Text24, { dimColor: true, italic: true }, "no records")), /* @__PURE__ */ React29.createElement(Box25, { marginTop: 1, paddingX: 1, borderStyle: "single", borderColor: "gray" }, /* @__PURE__ */ React29.createElement(Text24, { dimColor: true }, /* @__PURE__ */ React29.createElement(Text24, { bold: true }, "j"), "/", /* @__PURE__ */ React29.createElement(Text24, { bold: true }, "\u2193"), "/", /* @__PURE__ */ React29.createElement(Text24, { bold: true }, "space"), " next \xB7 ", /* @__PURE__ */ React29.createElement(Text24, { bold: true }, "k"), "/", /* @__PURE__ */ React29.createElement(Text24, { bold: true }, "\u2191"), " prev \xB7 ", /* @__PURE__ */ React29.createElement(Text24, { bold: true }, "g"), " first \xB7 ", /* @__PURE__ */ React29.createElement(Text24, { bold: true }, "G"), " last \xB7", " ", /* @__PURE__ */ React29.createElement(Text24, { bold: true }, "q"), " quit")));
14635
15265
  }
14636
15266
 
14637
15267
  // src/cli/commands/replay.ts
@@ -14643,7 +15273,7 @@ async function replayCommand(opts) {
14643
15273
  }
14644
15274
  const { parsed } = replayFromFile(opts.path);
14645
15275
  const pages = groupRecordsByTurn(parsed.records);
14646
- const { waitUntilExit } = render3(React28.createElement(ReplayApp, { meta: parsed.meta, pages }), {
15276
+ const { waitUntilExit } = render3(React30.createElement(ReplayApp, { meta: parsed.meta, pages }), {
14647
15277
  exitOnCtrlC: true,
14648
15278
  patchConsole: false
14649
15279
  });
@@ -14737,12 +15367,12 @@ function oneLine2(s, max = 200) {
14737
15367
  }
14738
15368
 
14739
15369
  // src/cli/commands/run.ts
14740
- import { stdin, stdout } from "process";
15370
+ import { stdin as stdin2, stdout } from "process";
14741
15371
  import { createInterface } from "readline/promises";
14742
15372
  async function ensureApiKey() {
14743
15373
  const existing = loadApiKey();
14744
15374
  if (existing) return existing;
14745
- if (!stdin.isTTY) {
15375
+ if (!stdin2.isTTY) {
14746
15376
  process.stderr.write(
14747
15377
  "DEEPSEEK_API_KEY is not set and stdin is not a TTY (cannot prompt).\nSet the env var, or run `reasonix chat` once interactively to save a key.\n"
14748
15378
  );
@@ -14751,7 +15381,7 @@ async function ensureApiKey() {
14751
15381
  process.stdout.write(
14752
15382
  "DeepSeek API key not configured.\nGet one at https://platform.deepseek.com/api_keys\n"
14753
15383
  );
14754
- const rl = createInterface({ input: stdin, output: stdout });
15384
+ const rl = createInterface({ input: stdin2, output: stdout });
14755
15385
  try {
14756
15386
  while (true) {
14757
15387
  const answer = (await rl.question("API key \u203A ")).trim();
@@ -14948,12 +15578,12 @@ function truncate3(s, max) {
14948
15578
 
14949
15579
  // src/cli/commands/setup.tsx
14950
15580
  import { render as render4 } from "ink";
14951
- import React30 from "react";
15581
+ import React32 from "react";
14952
15582
 
14953
15583
  // src/cli/ui/Wizard.tsx
14954
- import { Box as Box25, Text as Text24, useApp as useApp5, useInput as useInput8 } from "ink";
15584
+ import { Box as Box26, Text as Text25, useApp as useApp5, useInput as useInput3 } from "ink";
14955
15585
  import TextInput2 from "ink-text-input";
14956
- import React29, { useState as useState15 } from "react";
15586
+ import React31, { useState as useState15 } from "react";
14957
15587
 
14958
15588
  // src/cli/ui/presets.ts
14959
15589
  var PRESETS = {
@@ -14997,11 +15627,11 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
14997
15627
  catalogArgs: {}
14998
15628
  });
14999
15629
  const [error, setError] = useState15(null);
15000
- useInput8((_input, key) => {
15630
+ useInput3((_input, key) => {
15001
15631
  if (key.escape && step !== "saved" && onCancel) onCancel();
15002
15632
  });
15003
15633
  if (step === "apiKey") {
15004
- return /* @__PURE__ */ React29.createElement(
15634
+ return /* @__PURE__ */ React31.createElement(
15005
15635
  ApiKeyStep,
15006
15636
  {
15007
15637
  onSubmit: (key) => {
@@ -15015,7 +15645,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
15015
15645
  );
15016
15646
  }
15017
15647
  if (step === "preset") {
15018
- return /* @__PURE__ */ React29.createElement(StepFrame, { title: "Pick a preset", step: 1, total: 3 }, /* @__PURE__ */ React29.createElement(
15648
+ return /* @__PURE__ */ React31.createElement(StepFrame, { title: "Pick a preset", step: 1, total: 3 }, /* @__PURE__ */ React31.createElement(
15019
15649
  SingleSelect,
15020
15650
  {
15021
15651
  items: presetItems(),
@@ -15025,10 +15655,10 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
15025
15655
  setStep("mcp");
15026
15656
  }
15027
15657
  }
15028
- ), /* @__PURE__ */ React29.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React29.createElement(Text24, { dimColor: true }, "\u2191/\u2193 move \xB7 enter confirm \xB7 esc cancel")));
15658
+ ), /* @__PURE__ */ React31.createElement(Box26, { marginTop: 1 }, /* @__PURE__ */ React31.createElement(Text25, { dimColor: true }, "[\u2191\u2193] navigate \xB7 [Enter] confirm \xB7 [Esc] cancel")));
15029
15659
  }
15030
15660
  if (step === "mcp") {
15031
- return /* @__PURE__ */ React29.createElement(StepFrame, { title: "Which MCP servers should Reasonix wire up for you?", step: 2, total: 3 }, /* @__PURE__ */ React29.createElement(
15661
+ return /* @__PURE__ */ React31.createElement(StepFrame, { title: "Which MCP servers should Reasonix wire up for you?", step: 2, total: 3 }, /* @__PURE__ */ React31.createElement(
15032
15662
  MultiSelect,
15033
15663
  {
15034
15664
  items: mcpItems(),
@@ -15038,7 +15668,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
15038
15668
  const needsArgs = selected.some((name) => CATALOG_BY_NAME.get(name)?.userArgs);
15039
15669
  setStep(needsArgs ? "mcpArgs" : "review");
15040
15670
  },
15041
- footer: "\u2191/\u2193 move \xB7 space toggle \xB7 enter confirm \xB7 esc cancel \xB7 leave empty to skip"
15671
+ footer: "[\u2191\u2193] navigate \xB7 [Space] toggle \xB7 [Enter] confirm \xB7 [Esc] cancel \xB7 empty = skip"
15042
15672
  }
15043
15673
  ));
15044
15674
  }
@@ -15053,7 +15683,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
15053
15683
  }
15054
15684
  const currentName = pending[0];
15055
15685
  const entry = CATALOG_BY_NAME.get(currentName);
15056
- return /* @__PURE__ */ React29.createElement(
15686
+ return /* @__PURE__ */ React31.createElement(
15057
15687
  McpArgsStep,
15058
15688
  {
15059
15689
  entry,
@@ -15071,7 +15701,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
15071
15701
  }
15072
15702
  if (step === "review") {
15073
15703
  const specs = data.selectedCatalog.map((name) => buildSpec(name, data.catalogArgs));
15074
- return /* @__PURE__ */ React29.createElement(StepFrame, { title: "Ready to save", step: 3, total: 3 }, /* @__PURE__ */ React29.createElement(Box25, { flexDirection: "column" }, /* @__PURE__ */ React29.createElement(SummaryLine, { label: "API key", value: redactKey(data.apiKey) }), /* @__PURE__ */ React29.createElement(SummaryLine, { label: "Preset", value: data.preset }), /* @__PURE__ */ React29.createElement(
15704
+ return /* @__PURE__ */ React31.createElement(StepFrame, { title: "Ready to save", step: 3, total: 3 }, /* @__PURE__ */ React31.createElement(Box26, { flexDirection: "column" }, /* @__PURE__ */ React31.createElement(SummaryLine, { label: "API key", value: redactKey(data.apiKey) }), /* @__PURE__ */ React31.createElement(SummaryLine, { label: "Preset", value: data.preset }), /* @__PURE__ */ React31.createElement(
15075
15705
  SummaryLine,
15076
15706
  {
15077
15707
  label: "MCP",
@@ -15079,8 +15709,8 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
15079
15709
  }
15080
15710
  ), specs.map((spec, i) => (
15081
15711
  // biome-ignore lint/suspicious/noArrayIndexKey: review-only render, order fixed
15082
- /* @__PURE__ */ React29.createElement(Box25, { key: i, paddingLeft: 14 }, /* @__PURE__ */ React29.createElement(Text24, { dimColor: true }, "\xB7 ", spec))
15083
- )), /* @__PURE__ */ React29.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React29.createElement(Text24, null, "Saves to ", defaultConfigPath())), error ? /* @__PURE__ */ React29.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React29.createElement(Text24, { color: "red" }, error)) : null, /* @__PURE__ */ React29.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React29.createElement(Text24, { dimColor: true }, "enter save \xB7 esc cancel"))), /* @__PURE__ */ React29.createElement(
15712
+ /* @__PURE__ */ React31.createElement(Box26, { key: i, paddingLeft: 14 }, /* @__PURE__ */ React31.createElement(Text25, { dimColor: true }, "\xB7 ", spec))
15713
+ )), /* @__PURE__ */ React31.createElement(Box26, { marginTop: 1 }, /* @__PURE__ */ React31.createElement(Text25, null, "Saves to ", defaultConfigPath())), error ? /* @__PURE__ */ React31.createElement(Box26, { marginTop: 1 }, /* @__PURE__ */ React31.createElement(Text25, { color: "red" }, error)) : null, /* @__PURE__ */ React31.createElement(Box26, { marginTop: 1 }, /* @__PURE__ */ React31.createElement(Text25, { dimColor: true }, "[Enter] save \xB7 [Esc] cancel"))), /* @__PURE__ */ React31.createElement(
15084
15714
  ReviewConfirm,
15085
15715
  {
15086
15716
  onConfirm: () => {
@@ -15106,7 +15736,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
15106
15736
  }
15107
15737
  ));
15108
15738
  }
15109
- return /* @__PURE__ */ React29.createElement(Box25, { flexDirection: "column", borderStyle: "round", borderColor: "green", paddingX: 1 }, /* @__PURE__ */ React29.createElement(Text24, { bold: true, color: "green" }, "\u25B8 Saved."), /* @__PURE__ */ React29.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React29.createElement(Text24, null, "Run `reasonix` any time to start chatting \u2014 your settings are remembered.")), /* @__PURE__ */ React29.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React29.createElement(Text24, { dimColor: true }, "Press enter to exit.")), /* @__PURE__ */ React29.createElement(ExitOnEnter, { onExit: exit2 }));
15739
+ return /* @__PURE__ */ React31.createElement(Box26, { flexDirection: "column", borderStyle: "round", borderColor: "green", paddingX: 1 }, /* @__PURE__ */ React31.createElement(Text25, { bold: true, color: "green" }, "\u25B8 Saved."), /* @__PURE__ */ React31.createElement(Box26, { marginTop: 1 }, /* @__PURE__ */ React31.createElement(Text25, null, "Run `reasonix` any time to start chatting \u2014 your settings are remembered.")), /* @__PURE__ */ React31.createElement(Box26, { marginTop: 1 }, /* @__PURE__ */ React31.createElement(Text25, { dimColor: true }, "[Enter] to exit")), /* @__PURE__ */ React31.createElement(ExitOnEnter, { onExit: exit2 }));
15110
15740
  }
15111
15741
  function ApiKeyStep({
15112
15742
  onSubmit,
@@ -15114,7 +15744,7 @@ function ApiKeyStep({
15114
15744
  onError
15115
15745
  }) {
15116
15746
  const [value, setValue] = useState15("");
15117
- return /* @__PURE__ */ React29.createElement(Box25, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React29.createElement(Text24, { bold: true, color: "cyan" }, "Welcome to Reasonix."), /* @__PURE__ */ React29.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React29.createElement(Text24, null, "Paste your DeepSeek API key to get started.")), /* @__PURE__ */ React29.createElement(Text24, { dimColor: true }, "Get one (free credit on signup): https://platform.deepseek.com/api_keys"), /* @__PURE__ */ React29.createElement(Text24, { dimColor: true }, "Saved locally to ", defaultConfigPath()), /* @__PURE__ */ React29.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React29.createElement(Text24, { bold: true, color: "cyan" }, "key \u203A "), /* @__PURE__ */ React29.createElement(
15747
+ return /* @__PURE__ */ React31.createElement(Box26, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React31.createElement(Text25, { bold: true, color: "cyan" }, "Welcome to Reasonix."), /* @__PURE__ */ React31.createElement(Box26, { marginTop: 1 }, /* @__PURE__ */ React31.createElement(Text25, null, "Paste your DeepSeek API key to get started.")), /* @__PURE__ */ React31.createElement(Text25, { dimColor: true }, "Get one (free credit on signup): https://platform.deepseek.com/api_keys"), /* @__PURE__ */ React31.createElement(Text25, { dimColor: true }, "Saved locally to ", defaultConfigPath()), /* @__PURE__ */ React31.createElement(Box26, { marginTop: 1 }, /* @__PURE__ */ React31.createElement(Text25, { bold: true, color: "cyan" }, "key \u203A "), /* @__PURE__ */ React31.createElement(
15118
15748
  TextInput2,
15119
15749
  {
15120
15750
  value,
@@ -15131,7 +15761,7 @@ function ApiKeyStep({
15131
15761
  mask: "\u2022",
15132
15762
  placeholder: "sk-..."
15133
15763
  }
15134
- )), error ? /* @__PURE__ */ React29.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React29.createElement(Text24, { color: "red" }, error)) : value ? /* @__PURE__ */ React29.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React29.createElement(Text24, { dimColor: true }, "preview: ", redactKey(value))) : null);
15764
+ )), error ? /* @__PURE__ */ React31.createElement(Box26, { marginTop: 1 }, /* @__PURE__ */ React31.createElement(Text25, { color: "red" }, error)) : value ? /* @__PURE__ */ React31.createElement(Box26, { marginTop: 1 }, /* @__PURE__ */ React31.createElement(Text25, { dimColor: true }, "preview: ", redactKey(value))) : null);
15135
15765
  }
15136
15766
  function McpArgsStep({
15137
15767
  entry,
@@ -15140,7 +15770,7 @@ function McpArgsStep({
15140
15770
  onError
15141
15771
  }) {
15142
15772
  const [value, setValue] = useState15("");
15143
- return /* @__PURE__ */ React29.createElement(StepFrame, { title: `Configure ${entry.name}`, step: 2, total: 3 }, /* @__PURE__ */ React29.createElement(Box25, { flexDirection: "column" }, /* @__PURE__ */ React29.createElement(Text24, null, entry.summary), entry.note ? /* @__PURE__ */ React29.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React29.createElement(Text24, { dimColor: true }, entry.note)) : null, /* @__PURE__ */ React29.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React29.createElement(Text24, null, "Required parameter: "), /* @__PURE__ */ React29.createElement(Text24, { bold: true }, entry.userArgs)), /* @__PURE__ */ React29.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React29.createElement(Text24, { bold: true, color: "cyan" }, entry.userArgs, " \u203A "), /* @__PURE__ */ React29.createElement(
15773
+ return /* @__PURE__ */ React31.createElement(StepFrame, { title: `Configure ${entry.name}`, step: 2, total: 3 }, /* @__PURE__ */ React31.createElement(Box26, { flexDirection: "column" }, /* @__PURE__ */ React31.createElement(Text25, null, entry.summary), entry.note ? /* @__PURE__ */ React31.createElement(Box26, { marginTop: 1 }, /* @__PURE__ */ React31.createElement(Text25, { dimColor: true }, entry.note)) : null, /* @__PURE__ */ React31.createElement(Box26, { marginTop: 1 }, /* @__PURE__ */ React31.createElement(Text25, null, "Required parameter: "), /* @__PURE__ */ React31.createElement(Text25, { bold: true }, entry.userArgs)), /* @__PURE__ */ React31.createElement(Box26, { marginTop: 1 }, /* @__PURE__ */ React31.createElement(Text25, { bold: true, color: "cyan" }, entry.userArgs, " \u203A "), /* @__PURE__ */ React31.createElement(
15144
15774
  TextInput2,
15145
15775
  {
15146
15776
  value,
@@ -15156,16 +15786,16 @@ function McpArgsStep({
15156
15786
  },
15157
15787
  placeholder: placeholderFor(entry)
15158
15788
  }
15159
- )), error ? /* @__PURE__ */ React29.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React29.createElement(Text24, { color: "red" }, error)) : null));
15789
+ )), error ? /* @__PURE__ */ React31.createElement(Box26, { marginTop: 1 }, /* @__PURE__ */ React31.createElement(Text25, { color: "red" }, error)) : null));
15160
15790
  }
15161
15791
  function ReviewConfirm({ onConfirm }) {
15162
- useInput8((_i, key) => {
15792
+ useInput3((_i, key) => {
15163
15793
  if (key.return) onConfirm();
15164
15794
  });
15165
15795
  return null;
15166
15796
  }
15167
15797
  function ExitOnEnter({ onExit }) {
15168
- useInput8((_i, key) => {
15798
+ useInput3((_i, key) => {
15169
15799
  if (key.return) onExit();
15170
15800
  });
15171
15801
  return null;
@@ -15176,10 +15806,10 @@ function StepFrame({
15176
15806
  total,
15177
15807
  children
15178
15808
  }) {
15179
- return /* @__PURE__ */ React29.createElement(Box25, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React29.createElement(Box25, null, /* @__PURE__ */ React29.createElement(Text24, { dimColor: true }, "Step ", step, "/", total, " \xB7", " "), /* @__PURE__ */ React29.createElement(Text24, { bold: true, color: "cyan" }, title)), /* @__PURE__ */ React29.createElement(Box25, { marginTop: 1, flexDirection: "column" }, children));
15809
+ return /* @__PURE__ */ React31.createElement(Box26, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React31.createElement(Box26, null, /* @__PURE__ */ React31.createElement(Text25, { dimColor: true }, "Step ", step, "/", total, " \xB7", " "), /* @__PURE__ */ React31.createElement(Text25, { bold: true, color: "cyan" }, title)), /* @__PURE__ */ React31.createElement(Box26, { marginTop: 1, flexDirection: "column" }, children));
15180
15810
  }
15181
15811
  function SummaryLine({ label, value }) {
15182
- return /* @__PURE__ */ React29.createElement(Box25, null, /* @__PURE__ */ React29.createElement(Text24, null, label.padEnd(12)), /* @__PURE__ */ React29.createElement(Text24, { bold: true }, value));
15812
+ return /* @__PURE__ */ React31.createElement(Box26, null, /* @__PURE__ */ React31.createElement(Text25, null, label.padEnd(12)), /* @__PURE__ */ React31.createElement(Text25, { bold: true }, value));
15183
15813
  }
15184
15814
  function presetItems() {
15185
15815
  return ["fast", "smart", "max"].map((name) => ({
@@ -15235,7 +15865,7 @@ async function setupCommand(_opts = {}) {
15235
15865
  const existingKey = loadApiKey();
15236
15866
  const existing = readConfig();
15237
15867
  const { waitUntilExit, unmount } = render4(
15238
- /* @__PURE__ */ React30.createElement(
15868
+ /* @__PURE__ */ React32.createElement(
15239
15869
  Wizard,
15240
15870
  {
15241
15871
  existingApiKey: existingKey,