reasonix 0.7.2 → 0.7.5

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 React26, { 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 useStdout5 } 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 Box21, Static, useApp, useStdout as useStdout8 } from "ink";
7120
+ import React23, { 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";
@@ -7430,23 +7430,376 @@ function AtMentionSuggestions({
7430
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
- const marker = isSelected ? "\u25B8" : " ";
7434
7433
  const slash = path.lastIndexOf("/");
7435
7434
  const dir = slash >= 0 ? `${path.slice(0, slash)}/` : "";
7436
7435
  const base = slash >= 0 ? path.slice(slash + 1) : path;
7437
7436
  if (isSelected) {
7438
- return /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { bold: true, color: "cyan" }, marker, " ", base), /* @__PURE__ */ React.createElement(Text, { color: "cyan" }, dir ? ` ${dir}` : ""));
7437
+ return /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { backgroundColor: "#67e8f9", color: "black", bold: true }, ` \u25B8 ${base}${dir ? ` ${dir}` : ""} `));
7439
7438
  }
7440
- return /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, marker, " ", base, dir ? ` ${dir}` : ""));
7439
+ return /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { color: "#94a3b8" }, ` ${base}`), dir ? /* @__PURE__ */ React.createElement(Text, { dimColor: true }, ` ${dir}`) : null);
7441
7440
  }
7442
7441
 
7443
7442
  // src/cli/ui/ChoiceConfirm.tsx
7443
+ import { Box as Box4 } from "ink";
7444
+ import React5 from "react";
7445
+
7446
+ // src/cli/ui/ModalCard.tsx
7447
+ import { Box as Box2, Text as Text2, useStdout } from "ink";
7448
+ import React2 from "react";
7449
+ function ModalCard({
7450
+ accent,
7451
+ title,
7452
+ subtitle,
7453
+ icon,
7454
+ children
7455
+ }) {
7456
+ const { stdout: stdout2 } = useStdout();
7457
+ const cols = stdout2?.columns ?? 80;
7458
+ const ruleWidth = Math.min(80, Math.max(28, cols - 4));
7459
+ const titleText = icon ? ` ${icon} ${title} ` : ` ${title} `;
7460
+ return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React2.createElement(Box2, null, /* @__PURE__ */ React2.createElement(Text2, { color: accent }, "\u2594".repeat(ruleWidth))), /* @__PURE__ */ React2.createElement(Box2, { marginTop: 1 }, /* @__PURE__ */ React2.createElement(Text2, { backgroundColor: accent, color: "black", bold: true }, titleText), subtitle ? /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, ` ${subtitle}`) : null), /* @__PURE__ */ React2.createElement(Box2, { marginTop: 1, flexDirection: "column" }, children), /* @__PURE__ */ React2.createElement(Box2, { marginTop: 1 }, /* @__PURE__ */ React2.createElement(Text2, { color: accent, dimColor: true }, "\u2581".repeat(ruleWidth))));
7461
+ }
7462
+
7463
+ // src/cli/ui/Select.tsx
7444
7464
  import { Box as Box3, Text as Text3 } from "ink";
7445
- import React3 from "react";
7465
+ import React4, { useState } from "react";
7466
+
7467
+ // src/cli/ui/keystroke-context.tsx
7468
+ import React3, { createContext, useContext, useEffect, useRef } from "react";
7469
+
7470
+ // src/cli/ui/stdin-reader.ts
7471
+ import { stdin } from "process";
7472
+ var ESC_TIMEOUT_MS = 250;
7473
+ var PASTE_END = "\x1B[201~";
7474
+ var PASTE_START_BARE = "[200~";
7475
+ var PASTE_END_BARE = "[201~";
7476
+ var CSI_TAIL_MAP = [
7477
+ { tail: "A", ev: { input: "", upArrow: true } },
7478
+ { tail: "B", ev: { input: "", downArrow: true } },
7479
+ { tail: "C", ev: { input: "", rightArrow: true } },
7480
+ { tail: "D", ev: { input: "", leftArrow: true } },
7481
+ { tail: "H", ev: { input: "", home: true } },
7482
+ { tail: "F", ev: { input: "", end: true } },
7483
+ { tail: "1~", ev: { input: "", home: true } },
7484
+ { tail: "4~", ev: { input: "", end: true } },
7485
+ { tail: "5~", ev: { input: "", pageUp: true } },
7486
+ { tail: "6~", ev: { input: "", pageDown: true } },
7487
+ { tail: "3~", ev: { input: "", delete: true } },
7488
+ { tail: "Z", ev: { input: "", shift: true, tab: true } },
7489
+ // modifyOtherKeys (xterm CSI > 4 ; 2 m) sequences for Enter with
7490
+ // modifiers. Only fired when App.tsx has enabled the mode at
7491
+ // startup; otherwise Shift+Enter stays indistinguishable from Enter.
7492
+ // Modifier encoding: 2=shift, 3=alt, 4=alt+shift, 5=ctrl,
7493
+ // 6=ctrl+shift, 7=ctrl+alt, 8=ctrl+alt+shift. Keycode 13 = Enter.
7494
+ { tail: "27;2;13~", ev: { input: "", return: true, shift: true } },
7495
+ { tail: "27;5;13~", ev: { input: "", return: true, ctrl: true } },
7496
+ { tail: "27;6;13~", ev: { input: "", return: true, ctrl: true, shift: true } },
7497
+ // Kitty keyboard protocol — same idea, different envelope:
7498
+ // `\x1b[<keycode>;<mod>u`. Some terminals (kitty, recent Windows
7499
+ // Terminal previews) prefer this shape. Harmless to map here too.
7500
+ { tail: "13;2u", ev: { input: "", return: true, shift: true } },
7501
+ { tail: "13;5u", ev: { input: "", return: true, ctrl: true } },
7502
+ { tail: "13;6u", ev: { input: "", return: true, ctrl: true, shift: true } }
7503
+ ];
7504
+ var SS3_MAP = {
7505
+ A: { input: "", upArrow: true },
7506
+ B: { input: "", downArrow: true },
7507
+ C: { input: "", rightArrow: true },
7508
+ D: { input: "", leftArrow: true },
7509
+ H: { input: "", home: true },
7510
+ F: { input: "", end: true }
7511
+ };
7512
+ function tryEscapelessCsi(chunk, i) {
7513
+ if (chunk[i] !== "[") return null;
7514
+ for (const entry of CSI_TAIL_MAP) {
7515
+ const candidate = `[${entry.tail}`;
7516
+ if (chunk.slice(i, i + candidate.length) === candidate) {
7517
+ return { advance: candidate.length, ev: entry.ev };
7518
+ }
7519
+ }
7520
+ return null;
7521
+ }
7522
+ function isCsiFinal(ch) {
7523
+ const code = ch.charCodeAt(0);
7524
+ return code >= 64 && code <= 126;
7525
+ }
7526
+ function lookupCsi(tail) {
7527
+ for (const entry of CSI_TAIL_MAP) {
7528
+ if (entry.tail === tail) return entry.ev;
7529
+ }
7530
+ return null;
7531
+ }
7532
+ var StdinReader = class {
7533
+ subscribers = /* @__PURE__ */ new Set();
7534
+ state = "idle";
7535
+ /** Buffer for partial sequences across chunks. */
7536
+ csiBuf = "";
7537
+ /** Buffer for paste content. */
7538
+ pasteBuf = "";
7539
+ escTimer = null;
7540
+ started = false;
7541
+ /** The actual `data` listener — kept as a field so `stop()` can detach it. */
7542
+ listener = null;
7543
+ start() {
7544
+ if (this.started) return;
7545
+ if (!stdin.isTTY) {
7546
+ return;
7547
+ }
7548
+ stdin.setRawMode(true);
7549
+ stdin.setEncoding("utf8");
7550
+ stdin.resume();
7551
+ this.listener = (chunk) => this.handleChunk(typeof chunk === "string" ? chunk : chunk.toString("utf8"));
7552
+ stdin.on("data", this.listener);
7553
+ this.started = true;
7554
+ }
7555
+ stop() {
7556
+ if (!this.started) return;
7557
+ if (this.listener) {
7558
+ stdin.off("data", this.listener);
7559
+ this.listener = null;
7560
+ }
7561
+ if (stdin.isTTY) {
7562
+ try {
7563
+ stdin.setRawMode(false);
7564
+ } catch {
7565
+ }
7566
+ }
7567
+ stdin.pause();
7568
+ this.cancelEscTimer();
7569
+ this.state = "idle";
7570
+ this.csiBuf = "";
7571
+ this.pasteBuf = "";
7572
+ this.started = false;
7573
+ }
7574
+ /**
7575
+ * Subscribe to parsed key events. Returns an unsubscribe function.
7576
+ * Multiple subscribers are supported — every event fans out to all
7577
+ * of them; the React Context layer above uses one subscriber and
7578
+ * dispatches further to its own consumer list.
7579
+ */
7580
+ subscribe(fn) {
7581
+ this.subscribers.add(fn);
7582
+ return () => {
7583
+ this.subscribers.delete(fn);
7584
+ };
7585
+ }
7586
+ /**
7587
+ * Inject a chunk of bytes as if it came from stdin. Used by tests
7588
+ * to drive the parser without a real TTY.
7589
+ */
7590
+ feed(chunk) {
7591
+ this.handleChunk(chunk);
7592
+ }
7593
+ dispatch(ev) {
7594
+ for (const sub of this.subscribers) sub(ev);
7595
+ }
7596
+ cancelEscTimer() {
7597
+ if (this.escTimer) {
7598
+ clearTimeout(this.escTimer);
7599
+ this.escTimer = null;
7600
+ }
7601
+ }
7602
+ scheduleEscTimer() {
7603
+ this.cancelEscTimer();
7604
+ this.escTimer = setTimeout(() => {
7605
+ if (this.state === "esc") {
7606
+ this.state = "idle";
7607
+ this.dispatch({ input: "", escape: true });
7608
+ }
7609
+ }, ESC_TIMEOUT_MS);
7610
+ }
7611
+ handleChunk(chunk) {
7612
+ this.cancelEscTimer();
7613
+ let i = 0;
7614
+ while (i < chunk.length) {
7615
+ if (this.state === "paste") {
7616
+ const endA = chunk.indexOf(PASTE_END, i);
7617
+ const endB = chunk.indexOf(PASTE_END_BARE, i);
7618
+ let endIdx = -1;
7619
+ let endLen = 0;
7620
+ if (endA !== -1 && (endB === -1 || endA <= endB)) {
7621
+ endIdx = endA;
7622
+ endLen = PASTE_END.length;
7623
+ } else if (endB !== -1) {
7624
+ endIdx = endB;
7625
+ endLen = PASTE_END_BARE.length;
7626
+ }
7627
+ if (endIdx === -1) {
7628
+ this.pasteBuf += chunk.slice(i);
7629
+ i = chunk.length;
7630
+ break;
7631
+ }
7632
+ this.pasteBuf += chunk.slice(i, endIdx);
7633
+ this.dispatch({ input: this.pasteBuf, paste: true });
7634
+ this.pasteBuf = "";
7635
+ this.state = "idle";
7636
+ i = endIdx + endLen;
7637
+ continue;
7638
+ }
7639
+ if (this.state === "csi") {
7640
+ const ch2 = chunk[i];
7641
+ this.csiBuf += ch2;
7642
+ if (isCsiFinal(ch2)) {
7643
+ this.dispatchCsi(this.csiBuf);
7644
+ this.csiBuf = "";
7645
+ if (this.state === "csi") this.state = "idle";
7646
+ }
7647
+ i++;
7648
+ continue;
7649
+ }
7650
+ if (this.state === "ss3") {
7651
+ const ev = SS3_MAP[chunk[i]];
7652
+ if (ev) this.dispatch(ev);
7653
+ this.state = "idle";
7654
+ i++;
7655
+ continue;
7656
+ }
7657
+ if (this.state === "esc") {
7658
+ const ch2 = chunk[i];
7659
+ if (ch2 === "[") {
7660
+ this.state = "csi";
7661
+ this.csiBuf = "";
7662
+ i++;
7663
+ continue;
7664
+ }
7665
+ if (ch2 === "O") {
7666
+ this.state = "ss3";
7667
+ i++;
7668
+ continue;
7669
+ }
7670
+ this.dispatch({ input: ch2, meta: true });
7671
+ this.state = "idle";
7672
+ i++;
7673
+ continue;
7674
+ }
7675
+ const ch = chunk[i];
7676
+ if (ch === "\x1B") {
7677
+ this.state = "esc";
7678
+ i++;
7679
+ continue;
7680
+ }
7681
+ if (chunk.slice(i, i + PASTE_START_BARE.length) === PASTE_START_BARE) {
7682
+ this.state = "paste";
7683
+ this.pasteBuf = "";
7684
+ i += PASTE_START_BARE.length;
7685
+ continue;
7686
+ }
7687
+ const escapeless = tryEscapelessCsi(chunk, i);
7688
+ if (escapeless) {
7689
+ this.dispatch(escapeless.ev);
7690
+ i += escapeless.advance;
7691
+ continue;
7692
+ }
7693
+ if (ch === "\r") {
7694
+ this.dispatch({ input: "", return: true });
7695
+ i++;
7696
+ continue;
7697
+ }
7698
+ if (ch === "\n") {
7699
+ this.dispatch({ input: "j", ctrl: true });
7700
+ i++;
7701
+ continue;
7702
+ }
7703
+ if (ch === " ") {
7704
+ this.dispatch({ input: "", tab: true });
7705
+ i++;
7706
+ continue;
7707
+ }
7708
+ if (ch === "\x7F" || ch === "\b") {
7709
+ this.dispatch({ input: "", backspace: true });
7710
+ i++;
7711
+ continue;
7712
+ }
7713
+ if (ch === "") {
7714
+ this.dispatch({ input: "c", ctrl: true });
7715
+ i++;
7716
+ continue;
7717
+ }
7718
+ const code = ch.charCodeAt(0);
7719
+ if (code >= 1 && code <= 26) {
7720
+ const letter = String.fromCharCode(96 + code);
7721
+ this.dispatch({ input: letter, ctrl: true });
7722
+ i++;
7723
+ continue;
7724
+ }
7725
+ let end = i + 1;
7726
+ while (end < chunk.length) {
7727
+ const c = chunk[end];
7728
+ if (c === "\x1B" || c === "\r" || c === "\n" || c === " ") break;
7729
+ if (c === "\x7F" || c === "\b" || c === "") break;
7730
+ const cc = c.charCodeAt(0);
7731
+ if (cc >= 1 && cc <= 26) break;
7732
+ if (c === "[" && tryEscapelessCsi(chunk, end)) break;
7733
+ if (chunk.slice(end, end + PASTE_START_BARE.length) === PASTE_START_BARE) break;
7734
+ end++;
7735
+ }
7736
+ this.dispatch({ input: chunk.slice(i, end) });
7737
+ i = end;
7738
+ }
7739
+ if (this.state === "esc") {
7740
+ this.scheduleEscTimer();
7741
+ }
7742
+ }
7743
+ dispatchCsi(seq) {
7744
+ if (seq === "200~") {
7745
+ this.state = "paste";
7746
+ this.pasteBuf = "";
7747
+ return;
7748
+ }
7749
+ if (seq === "201~") {
7750
+ return;
7751
+ }
7752
+ const ev = lookupCsi(seq);
7753
+ if (ev) this.dispatch(ev);
7754
+ }
7755
+ };
7756
+ var singleton = null;
7757
+ function getStdinReader() {
7758
+ if (!singleton) singleton = new StdinReader();
7759
+ return singleton;
7760
+ }
7761
+
7762
+ // src/cli/ui/keystroke-context.tsx
7763
+ var KeystrokeContext = createContext(null);
7764
+ function KeystrokeProvider({
7765
+ children,
7766
+ reader: providedReader
7767
+ }) {
7768
+ const handlersRef = useRef(/* @__PURE__ */ new Set());
7769
+ const busRef = useRef(null);
7770
+ if (busRef.current === null) {
7771
+ busRef.current = {
7772
+ subscribe(handler) {
7773
+ handlersRef.current.add(handler);
7774
+ return () => {
7775
+ handlersRef.current.delete(handler);
7776
+ };
7777
+ }
7778
+ };
7779
+ }
7780
+ useEffect(() => {
7781
+ const reader = providedReader ?? getStdinReader();
7782
+ reader.start();
7783
+ const unsubscribe = reader.subscribe((ev) => {
7784
+ for (const fn of [...handlersRef.current]) fn(ev);
7785
+ });
7786
+ return () => {
7787
+ unsubscribe();
7788
+ };
7789
+ }, [providedReader]);
7790
+ return /* @__PURE__ */ React3.createElement(KeystrokeContext.Provider, { value: busRef.current }, children);
7791
+ }
7792
+ function useKeystroke(handler, isActive = true) {
7793
+ const bus = useContext(KeystrokeContext);
7794
+ const handlerRef = useRef(handler);
7795
+ handlerRef.current = handler;
7796
+ useEffect(() => {
7797
+ if (!bus || !isActive) return void 0;
7798
+ return bus.subscribe((ev) => handlerRef.current(ev));
7799
+ }, [bus, isActive]);
7800
+ }
7446
7801
 
7447
7802
  // src/cli/ui/Select.tsx
7448
- import { Box as Box2, Text as Text2, useInput } from "ink";
7449
- import React2, { useState } from "react";
7450
7803
  function SingleSelect({
7451
7804
  items,
7452
7805
  initialValue,
@@ -7459,19 +7812,20 @@ function SingleSelect({
7459
7812
  items.findIndex((i) => i.value === initialValue && !i.disabled)
7460
7813
  );
7461
7814
  const [index, setIndex] = useState(initialIndex === -1 ? 0 : initialIndex);
7462
- useInput((_input, key) => {
7463
- if (key.upArrow) {
7815
+ useKeystroke((ev) => {
7816
+ if (ev.paste) return;
7817
+ if (ev.upArrow) {
7464
7818
  setIndex((i) => findNextEnabled(items, i, -1));
7465
- } else if (key.downArrow) {
7819
+ } else if (ev.downArrow) {
7466
7820
  setIndex((i) => findNextEnabled(items, i, 1));
7467
- } else if (key.return) {
7821
+ } else if (ev.return) {
7468
7822
  const chosen = items[index];
7469
7823
  if (chosen && !chosen.disabled) onSubmit(chosen.value);
7470
- } else if (key.escape && onCancel) {
7824
+ } else if (ev.escape && onCancel) {
7471
7825
  onCancel();
7472
7826
  }
7473
7827
  });
7474
- return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, items.map((item, i) => /* @__PURE__ */ React2.createElement(
7828
+ return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column" }, items.map((item, i) => /* @__PURE__ */ React4.createElement(
7475
7829
  SelectRow,
7476
7830
  {
7477
7831
  key: item.value,
@@ -7479,7 +7833,7 @@ function SingleSelect({
7479
7833
  active: i === index,
7480
7834
  marker: i === index ? "\u25B8" : " "
7481
7835
  }
7482
- )), footer ? /* @__PURE__ */ React2.createElement(Box2, { marginTop: 1 }, /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, footer)) : null);
7836
+ )), footer ? /* @__PURE__ */ React4.createElement(Box3, { marginTop: 1 }, /* @__PURE__ */ React4.createElement(Text3, { dimColor: true }, footer)) : null);
7483
7837
  }
7484
7838
  function MultiSelect({
7485
7839
  items,
@@ -7493,12 +7847,13 @@ function MultiSelect({
7493
7847
  return first === -1 ? 0 : first;
7494
7848
  });
7495
7849
  const [selected, setSelected] = useState(new Set(initialSelected));
7496
- useInput((input, key) => {
7497
- if (key.upArrow) {
7850
+ useKeystroke((ev) => {
7851
+ if (ev.paste) return;
7852
+ if (ev.upArrow) {
7498
7853
  setIndex((i) => findNextEnabled(items, i, -1));
7499
- } else if (key.downArrow) {
7854
+ } else if (ev.downArrow) {
7500
7855
  setIndex((i) => findNextEnabled(items, i, 1));
7501
- } else if (input === " ") {
7856
+ } else if (ev.input === " ") {
7502
7857
  const item = items[index];
7503
7858
  if (!item || item.disabled) return;
7504
7859
  setSelected((prev) => {
@@ -7507,17 +7862,17 @@ function MultiSelect({
7507
7862
  else next.add(item.value);
7508
7863
  return next;
7509
7864
  });
7510
- } else if (key.return) {
7865
+ } else if (ev.return) {
7511
7866
  const ordered = items.filter((i) => selected.has(i.value)).map((i) => i.value);
7512
7867
  onSubmit(ordered);
7513
- } else if (key.escape && onCancel) {
7868
+ } else if (ev.escape && onCancel) {
7514
7869
  onCancel();
7515
7870
  }
7516
7871
  });
7517
- return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, items.map((item, i) => {
7872
+ return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column" }, items.map((item, i) => {
7518
7873
  const checked = selected.has(item.value);
7519
7874
  const marker = checked ? "[x]" : "[ ]";
7520
- return /* @__PURE__ */ React2.createElement(
7875
+ return /* @__PURE__ */ React4.createElement(
7521
7876
  SelectRow,
7522
7877
  {
7523
7878
  key: item.value,
@@ -7526,7 +7881,7 @@ function MultiSelect({
7526
7881
  marker: `${i === index ? "\u25B8" : " "} ${marker}`
7527
7882
  }
7528
7883
  );
7529
- }), footer ? /* @__PURE__ */ React2.createElement(Box2, { marginTop: 1 }, /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, footer)) : null);
7884
+ }), footer ? /* @__PURE__ */ React4.createElement(Box3, { marginTop: 1 }, /* @__PURE__ */ React4.createElement(Text3, { dimColor: true }, footer)) : null);
7530
7885
  }
7531
7886
  function SelectRow({
7532
7887
  item,
@@ -7534,7 +7889,7 @@ function SelectRow({
7534
7889
  marker
7535
7890
  }) {
7536
7891
  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);
7892
+ return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column" }, /* @__PURE__ */ React4.createElement(Box3, null, /* @__PURE__ */ React4.createElement(Text3, { color }, marker, " ", item.label)), item.hint ? /* @__PURE__ */ React4.createElement(Box3, { paddingLeft: marker.length + 1 }, /* @__PURE__ */ React4.createElement(Text3, { dimColor: true }, item.hint)) : null);
7538
7893
  }
7539
7894
  function findNextEnabled(items, from, step) {
7540
7895
  if (items.length === 0) return 0;
@@ -7567,7 +7922,7 @@ function ChoiceConfirmInner({ question, options, allowCustom, onChoose }) {
7567
7922
  label: "Cancel \u2014 drop the question",
7568
7923
  hint: "Model stops and asks what you want instead."
7569
7924
  });
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(
7925
+ return /* @__PURE__ */ React5.createElement(ModalCard, { accent: "#f0abfc", icon: "\u{1F500}", title: "model wants you to pick", subtitle: question }, /* @__PURE__ */ React5.createElement(Box4, null, /* @__PURE__ */ React5.createElement(
7571
7926
  SingleSelect,
7572
7927
  {
7573
7928
  initialValue: options[0]?.id,
@@ -7582,11 +7937,11 @@ function ChoiceConfirmInner({ question, options, allowCustom, onChoose }) {
7582
7937
  }
7583
7938
  )));
7584
7939
  }
7585
- var ChoiceConfirm = React3.memo(ChoiceConfirmInner);
7940
+ var ChoiceConfirm = React5.memo(ChoiceConfirmInner);
7586
7941
 
7587
7942
  // 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";
7943
+ import { Box as Box5, Text as Text4, useStdout as useStdout2 } from "ink";
7944
+ import React6, { useMemo, useState as useState2 } from "react";
7590
7945
 
7591
7946
  // src/code/diff-preview.ts
7592
7947
  function formatEditBlockDiff(block, opts = {}) {
@@ -7661,7 +8016,7 @@ function capLines(lines, maxLines, indent) {
7661
8016
  var MODAL_OVERHEAD_ROWS = 18;
7662
8017
  var MIN_DIFF_ROWS = 8;
7663
8018
  function EditConfirm({ block, onChoose }) {
7664
- const { stdout: stdout2 } = useStdout();
8019
+ const { stdout: stdout2 } = useStdout2();
7665
8020
  const rows = stdout2?.rows ?? 40;
7666
8021
  const budget = Math.max(MIN_DIFF_ROWS, rows - MODAL_OVERHEAD_ROWS);
7667
8022
  const allLines = useMemo(
@@ -7671,7 +8026,10 @@ function EditConfirm({ block, onChoose }) {
7671
8026
  const [scroll, setScroll] = useState2(0);
7672
8027
  const maxScroll = Math.max(0, allLines.length - budget);
7673
8028
  const effectiveScroll = Math.min(scroll, maxScroll);
7674
- useInput2((input, key) => {
8029
+ useKeystroke((ev) => {
8030
+ if (ev.paste) return;
8031
+ const input = ev.input;
8032
+ const key = ev;
7675
8033
  if (key.return || input === "y") {
7676
8034
  onChoose("apply");
7677
8035
  return;
@@ -7722,57 +8080,75 @@ function EditConfirm({ block, onChoose }) {
7722
8080
  const hiddenBelow = Math.max(0, allLines.length - effectiveScroll - budget);
7723
8081
  const totalLines = allLines.length;
7724
8082
  const showScrollHud = hiddenAbove + hiddenBelow > 0;
7725
- return /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React4.createElement(Box4, null, /* @__PURE__ */ React4.createElement(Text4, { bold: true, color: "yellow" }, "\u25B8 model wants to edit a file")), /* @__PURE__ */ React4.createElement(Box4, null, /* @__PURE__ */ React4.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__ */ 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(
7726
- Text4,
8083
+ const subtitleParts = [`-${removed} +${added} lines`];
8084
+ if (showScrollHud) {
8085
+ subtitleParts.push(
8086
+ `viewing ${effectiveScroll + 1}-${effectiveScroll + visibleLines.length}/${totalLines}`
8087
+ );
8088
+ }
8089
+ return /* @__PURE__ */ React6.createElement(
8090
+ ModalCard,
7727
8091
  {
7728
- dimColor: true
8092
+ accent: isNew ? "#86efac" : "#fcd34d",
8093
+ icon: isNew ? "\u271A" : "\u270E",
8094
+ title: `${tag} ${block.path}`,
8095
+ subtitle: subtitleParts.join(" \xB7 ")
7729
8096
  },
7730
- ` \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) => {
7732
- const trimmed = line.trimStart();
7733
- const color = trimmed.startsWith("+") ? "green" : trimmed.startsWith("-") ? "red" : void 0;
7734
- const dim = !color;
7735
- return /* @__PURE__ */ React4.createElement(
8097
+ hiddenAbove > 0 ? /* @__PURE__ */ React6.createElement(
7736
8098
  Text4,
7737
8099
  {
7738
- key: `diff-${effectiveScroll}-${i}`,
7739
- color,
7740
- dimColor: dim
8100
+ dimColor: true
7741
8101
  },
7742
- line
7743
- );
7744
- })), hiddenBelow > 0 ? /* @__PURE__ */ React4.createElement(
7745
- Text4,
7746
- {
7747
- dimColor: true
7748
- },
7749
- ` \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")));
8102
+ ` \u2191 ${hiddenAbove} line${hiddenAbove === 1 ? "" : "s"} above (\u2191/k or PgUp)`
8103
+ ) : null,
8104
+ /* @__PURE__ */ React6.createElement(Box5, { flexDirection: "column" }, visibleLines.map((line, i) => {
8105
+ const trimmed = line.trimStart();
8106
+ const color = trimmed.startsWith("+") ? "#4ade80" : trimmed.startsWith("-") ? "#f87171" : void 0;
8107
+ const dim = !color;
8108
+ return /* @__PURE__ */ React6.createElement(
8109
+ Text4,
8110
+ {
8111
+ key: `diff-${effectiveScroll}-${i}`,
8112
+ color,
8113
+ dimColor: dim
8114
+ },
8115
+ line
8116
+ );
8117
+ })),
8118
+ hiddenBelow > 0 ? /* @__PURE__ */ React6.createElement(
8119
+ Text4,
8120
+ {
8121
+ dimColor: true
8122
+ },
8123
+ ` \u2193 ${hiddenBelow} line${hiddenBelow === 1 ? "" : "s"} below (\u2193/j or Space/PgDn)`
8124
+ ) : null,
8125
+ /* @__PURE__ */ React6.createElement(Box5, { marginTop: 1 }, /* @__PURE__ */ React6.createElement(Text4, { dimColor: true }, "[", /* @__PURE__ */ React6.createElement(Text4, { color: "#67e8f9", bold: true }, "y"), "/Enter] apply \xB7 [", /* @__PURE__ */ React6.createElement(Text4, { color: "#67e8f9", bold: true }, "n"), "] reject \xB7 [", /* @__PURE__ */ React6.createElement(Text4, { color: "#67e8f9", bold: true }, "a"), "] apply rest \xB7 [", /* @__PURE__ */ React6.createElement(Text4, { color: "#67e8f9", bold: true }, "A"), "] flip AUTO \xB7 [", /* @__PURE__ */ React6.createElement(Text4, { color: "#67e8f9", bold: true }, "\u2191\u2193/Space"), "] scroll \xB7 [Esc] abort"))
8126
+ );
7751
8127
  }
7752
8128
 
7753
8129
  // src/cli/ui/EventLog.tsx
7754
- import { Box as Box8, Text as Text8, useStdout as useStdout2 } from "ink";
7755
- import React9 from "react";
8130
+ import { Box as Box9, Text as Text8, useStdout as useStdout3 } from "ink";
8131
+ import React11 from "react";
7756
8132
 
7757
8133
  // src/cli/ui/PlanStateBlock.tsx
7758
- import { Box as Box5, Text as Text5 } from "ink";
7759
- import React5 from "react";
8134
+ import { Box as Box6, Text as Text5 } from "ink";
8135
+ import React7 from "react";
7760
8136
  function PlanStateBlock({ planState }) {
7761
8137
  const fields = [];
7762
- if (planState.subgoals.length) fields.push(["subgoals", planState.subgoals, "cyan", false]);
8138
+ if (planState.subgoals.length) fields.push(["subgoals", planState.subgoals, "#67e8f9", false]);
7763
8139
  if (planState.hypotheses.length)
7764
- fields.push(["hypotheses", planState.hypotheses, "green", false]);
8140
+ fields.push(["hypotheses", planState.hypotheses, "#86efac", false]);
7765
8141
  if (planState.uncertainties.length)
7766
- fields.push(["uncertainties", planState.uncertainties, "yellow", false]);
8142
+ fields.push(["uncertainties", planState.uncertainties, "#fcd34d", false]);
7767
8143
  if (planState.rejectedPaths.length)
7768
- fields.push(["rejected", planState.rejectedPaths, "red", true]);
8144
+ fields.push(["rejected", planState.rejectedPaths, "#94a3b8", true]);
7769
8145
  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 ")}`))));
8146
+ return /* @__PURE__ */ React7.createElement(Box6, { flexDirection: "column", marginBottom: 1 }, fields.map(([label, items, color, dim]) => /* @__PURE__ */ React7.createElement(Box6, { key: label }, /* @__PURE__ */ React7.createElement(Text5, { backgroundColor: color, color: "black", bold: true, dimColor: dim }, ` ${label} ${items.length} `), /* @__PURE__ */ React7.createElement(Text5, null, " "), /* @__PURE__ */ React7.createElement(Text5, { dimColor: dim }, items.join(" \xB7 ")))));
7771
8147
  }
7772
8148
 
7773
8149
  // src/cli/ui/PlanStepList.tsx
7774
- import { Box as Box6, Text as Text6 } from "ink";
7775
- import React6 from "react";
8150
+ import { Box as Box7, Text as Text6 } from "ink";
8151
+ import React8 from "react";
7776
8152
  function riskDots(risk) {
7777
8153
  switch (risk) {
7778
8154
  case "high":
@@ -7785,18 +8161,6 @@ function riskDots(risk) {
7785
8161
  return { dots: " ", color: "gray" };
7786
8162
  }
7787
8163
  }
7788
- function statusGlyph(status2) {
7789
- switch (status2) {
7790
- case "done":
7791
- return { glyph: "\u2713", color: "green" };
7792
- case "running":
7793
- return { glyph: "\u25B6", color: "cyan" };
7794
- case "skipped":
7795
- return { glyph: "\u2014", color: "gray" };
7796
- default:
7797
- return { glyph: " ", color: "yellow" };
7798
- }
7799
- }
7800
8164
  function getStatus(stepId, statuses) {
7801
8165
  if (!statuses) return "pending";
7802
8166
  if (statuses instanceof Map) {
@@ -7811,25 +8175,37 @@ function PlanStepListInner({ steps, statuses, focusStepId }) {
7811
8175
  { length: steps.length },
7812
8176
  (_, i) => getStatus(steps[i].id, statuses)
7813
8177
  ).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) => {
8178
+ const pct2 = Math.round(doneCount / steps.length * 100);
8179
+ return /* @__PURE__ */ React8.createElement(Box7, { flexDirection: "column" }, /* @__PURE__ */ React8.createElement(Box7, null, /* @__PURE__ */ React8.createElement(Text6, { dimColor: true }, `${steps.length} step${steps.length === 1 ? "" : "s"}`, doneCount > 0 ? ` \xB7 ${doneCount}/${steps.length} done (${pct2}%)` : "", hasAnyRisk ? " \xB7 risk: " : ""), hasAnyRisk ? /* @__PURE__ */ React8.createElement(RiskLegend, null) : null), /* @__PURE__ */ React8.createElement(Box7, { flexDirection: "column", marginTop: 1 }, steps.map((step) => {
7815
8180
  const status2 = getStatus(step.id, statuses);
7816
8181
  const focus = focusStepId === step.id;
7817
8182
  const risk = riskDots(step.risk);
7818
- const glyph = statusGlyph(status2);
7819
8183
  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}`));
8184
+ return /* @__PURE__ */ React8.createElement(Box7, { key: step.id }, /* @__PURE__ */ React8.createElement(Text6, { color: focus ? "#67e8f9" : "gray", bold: focus }, focus ? "\u25B8 " : " "), /* @__PURE__ */ React8.createElement(Text6, { color: risk.color, bold: true }, risk.dots), /* @__PURE__ */ React8.createElement(Text6, null, " "), /* @__PURE__ */ React8.createElement(StatusBadge, { status: status2 }), /* @__PURE__ */ React8.createElement(Text6, null, " "), /* @__PURE__ */ React8.createElement(Text6, { dimColor: titleDim, bold: focus }, `${step.id} \xB7 ${step.title}`));
7821
8185
  })));
7822
8186
  }
8187
+ function StatusBadge({ status: status2 }) {
8188
+ switch (status2) {
8189
+ case "done":
8190
+ return /* @__PURE__ */ React8.createElement(Text6, { backgroundColor: "#4ade80", color: "black", bold: true }, " \u2713 DONE ");
8191
+ case "running":
8192
+ return /* @__PURE__ */ React8.createElement(Text6, { backgroundColor: "#67e8f9", color: "black", bold: true }, " \u25B6 RUN ");
8193
+ case "skipped":
8194
+ return /* @__PURE__ */ React8.createElement(Text6, { backgroundColor: "#94a3b8", color: "black", bold: true }, " \u2014 SKIP ");
8195
+ default:
8196
+ return /* @__PURE__ */ React8.createElement(Text6, { color: "#94a3b8", dimColor: true }, " \u2610 PEND ");
8197
+ }
8198
+ }
7823
8199
  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"));
8200
+ return /* @__PURE__ */ React8.createElement(Box7, null, /* @__PURE__ */ React8.createElement(Text6, { color: "green" }, "\u25CF"), /* @__PURE__ */ React8.createElement(Text6, { dimColor: true }, " low "), /* @__PURE__ */ React8.createElement(Text6, { color: "yellow" }, "\u25CF\u25CF"), /* @__PURE__ */ React8.createElement(Text6, { dimColor: true }, " med "), /* @__PURE__ */ React8.createElement(Text6, { color: "red" }, "\u25CF\u25CF\u25CF"), /* @__PURE__ */ React8.createElement(Text6, { dimColor: true }, " high"));
7825
8201
  }
7826
- var PlanStepList = React6.memo(PlanStepListInner);
8202
+ var PlanStepList = React8.memo(PlanStepListInner);
7827
8203
 
7828
8204
  // src/cli/ui/markdown.tsx
7829
8205
  import { readFileSync as readFileSync13, statSync as statSync6 } from "fs";
7830
8206
  import { isAbsolute as isAbsolute4, join as join11 } from "path";
7831
- import { Box as Box7, Text as Text7 } from "ink";
7832
- import React7 from "react";
8207
+ import { Box as Box8, Text as Text7 } from "ink";
8208
+ import React9 from "react";
7833
8209
  var SUPERSCRIPT = {
7834
8210
  "0": "\u2070",
7835
8211
  "1": "\xB9",
@@ -8084,67 +8460,67 @@ function InlineMd({
8084
8460
  for (const m of text.matchAll(INLINE_RE)) {
8085
8461
  const start = m.index ?? 0;
8086
8462
  if (start > last) {
8087
- parts.push(/* @__PURE__ */ React7.createElement(Text7, { key: `t${idx++}` }, text.slice(last, start)));
8463
+ parts.push(/* @__PURE__ */ React9.createElement(Text7, { key: `t${idx++}` }, text.slice(last, start)));
8088
8464
  }
8089
8465
  if (m[2] !== void 0 && m[3] !== void 0) {
8090
8466
  const linkText = m[2];
8091
8467
  const url = m[3];
8092
8468
  if (isExternalUrl(url)) {
8093
8469
  parts.push(
8094
- /* @__PURE__ */ React7.createElement(Text7, { key: `l${idx++}`, color: "blue", underline: true }, linkText)
8470
+ /* @__PURE__ */ React9.createElement(Text7, { key: `l${idx++}`, color: "blue", underline: true }, linkText)
8095
8471
  );
8096
8472
  } else {
8097
8473
  const status2 = citations?.get(url);
8098
8474
  if (status2 && !status2.ok) {
8099
8475
  parts.push(
8100
- /* @__PURE__ */ React7.createElement(Text7, { key: `l${idx++}`, color: "red", strikethrough: true }, `${linkText} \u2717`)
8476
+ /* @__PURE__ */ React9.createElement(Text7, { key: `l${idx++}`, color: "red", strikethrough: true }, `${linkText} \u2717`)
8101
8477
  );
8102
8478
  } else {
8103
8479
  parts.push(
8104
- /* @__PURE__ */ React7.createElement(Text7, { key: `l${idx++}`, color: "cyan", underline: true }, linkText)
8480
+ /* @__PURE__ */ React9.createElement(Text7, { key: `l${idx++}`, color: "cyan", underline: true }, linkText)
8105
8481
  );
8106
8482
  }
8107
8483
  }
8108
8484
  } else if (m[4] !== void 0) {
8109
8485
  parts.push(
8110
- /* @__PURE__ */ React7.createElement(Text7, { key: `bi${idx++}`, bold: true, italic: true }, m[4])
8486
+ /* @__PURE__ */ React9.createElement(Text7, { key: `bi${idx++}`, bold: true, italic: true }, m[4])
8111
8487
  );
8112
8488
  } else if (m[5] !== void 0) {
8113
8489
  parts.push(
8114
- /* @__PURE__ */ React7.createElement(Text7, { key: `b${idx++}`, bold: true }, m[5])
8490
+ /* @__PURE__ */ React9.createElement(Text7, { key: `b${idx++}`, bold: true }, m[5])
8115
8491
  );
8116
8492
  } else if (m[6] !== void 0) {
8117
8493
  const stripped = m[6].replace(/^(\w+)\s+/, "");
8118
8494
  parts.push(
8119
- /* @__PURE__ */ React7.createElement(Text7, { key: `c${idx++}`, color: "yellow" }, stripped)
8495
+ /* @__PURE__ */ React9.createElement(Text7, { key: `c${idx++}`, color: "yellow" }, stripped)
8120
8496
  );
8121
8497
  } else if (m[7] !== void 0) {
8122
8498
  parts.push(
8123
- /* @__PURE__ */ React7.createElement(Text7, { key: `c${idx++}`, color: "yellow" }, m[7])
8499
+ /* @__PURE__ */ React9.createElement(Text7, { key: `c${idx++}`, color: "yellow" }, m[7])
8124
8500
  );
8125
8501
  } else if (m[8] !== void 0) {
8126
8502
  parts.push(
8127
- /* @__PURE__ */ React7.createElement(Text7, { key: `s${idx++}`, strikethrough: true, dimColor: true }, m[8])
8503
+ /* @__PURE__ */ React9.createElement(Text7, { key: `s${idx++}`, strikethrough: true, dimColor: true }, m[8])
8128
8504
  );
8129
8505
  } else if (m[9] !== void 0) {
8130
8506
  parts.push(
8131
- /* @__PURE__ */ React7.createElement(Text7, { key: `i${idx++}`, italic: true }, m[9])
8507
+ /* @__PURE__ */ React9.createElement(Text7, { key: `i${idx++}`, italic: true }, m[9])
8132
8508
  );
8133
8509
  } else if (m[10] !== void 0) {
8134
- parts.push(/* @__PURE__ */ React7.createElement(Text7, { key: `esc${idx++}` }, m[10]));
8510
+ parts.push(/* @__PURE__ */ React9.createElement(Text7, { key: `esc${idx++}` }, m[10]));
8135
8511
  }
8136
8512
  last = start + m[0].length;
8137
8513
  }
8138
8514
  if (last < text.length) {
8139
- parts.push(/* @__PURE__ */ React7.createElement(Text7, { key: `t${idx++}` }, text.slice(last)));
8515
+ parts.push(/* @__PURE__ */ React9.createElement(Text7, { key: `t${idx++}` }, text.slice(last)));
8140
8516
  }
8141
8517
  if (padTo !== void 0) {
8142
8518
  const seen = visibleWidth(text);
8143
8519
  if (seen < padTo) {
8144
- parts.push(/* @__PURE__ */ React7.createElement(Text7, { key: `pad${idx++}` }, " ".repeat(padTo - seen)));
8520
+ parts.push(/* @__PURE__ */ React9.createElement(Text7, { key: `pad${idx++}` }, " ".repeat(padTo - seen)));
8145
8521
  }
8146
8522
  }
8147
- return /* @__PURE__ */ React7.createElement(Text7, null, parts);
8523
+ return /* @__PURE__ */ React9.createElement(Text7, null, parts);
8148
8524
  }
8149
8525
  function stripInlineMarkup(s) {
8150
8526
  return s.replace(
@@ -8383,34 +8759,34 @@ function parseBulletItem(raw) {
8383
8759
  function BlockView({ block, citations }) {
8384
8760
  switch (block.kind) {
8385
8761
  case "heading":
8386
- return /* @__PURE__ */ React7.createElement(Text7, { bold: true, color: "cyan" }, /* @__PURE__ */ React7.createElement(InlineMd, { text: block.text, citations }));
8762
+ return /* @__PURE__ */ React9.createElement(HeadingView, { level: block.level, text: block.text, citations });
8387
8763
  case "paragraph":
8388
- return /* @__PURE__ */ React7.createElement(ParagraphView, { text: block.text, citations });
8764
+ return /* @__PURE__ */ React9.createElement(ParagraphView, { text: block.text, citations });
8389
8765
  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 }))));
8766
+ return /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column" }, block.items.map((item, i) => /* @__PURE__ */ React9.createElement(Box8, { key: `${i}-${item.text.slice(0, 24)}` }, /* @__PURE__ */ React9.createElement(Text7, { color: item.task === "done" ? "green" : "cyan" }, bulletPrefix(block, i, item)), item.task === "done" ? /* @__PURE__ */ React9.createElement(Text7, { strikethrough: true, dimColor: true }, /* @__PURE__ */ React9.createElement(InlineMd, { text: item.text, citations })) : /* @__PURE__ */ React9.createElement(InlineMd, { text: item.text, citations }))));
8391
8767
  case "quote":
8392
- return /* @__PURE__ */ React7.createElement(BlockquoteView, { block, citations });
8768
+ return /* @__PURE__ */ React9.createElement(BlockquoteView, { block, citations });
8393
8769
  case "code":
8394
8770
  if (DIAGRAM_LANGS.has(block.lang.toLowerCase())) {
8395
- return /* @__PURE__ */ React7.createElement(DiagramCodeBlock, { lang: block.lang, text: block.text });
8771
+ return /* @__PURE__ */ React9.createElement(DiagramCodeBlock, { lang: block.lang, text: block.text });
8396
8772
  }
8397
- return /* @__PURE__ */ React7.createElement(Box7, { borderStyle: "single", borderColor: "gray", paddingX: 1 }, /* @__PURE__ */ React7.createElement(Text7, { color: "yellow" }, block.text));
8773
+ return /* @__PURE__ */ React9.createElement(CodeBlockView, { lang: block.lang, text: block.text });
8398
8774
  case "edit-block":
8399
- return /* @__PURE__ */ React7.createElement(EditBlockRow, { block });
8775
+ return /* @__PURE__ */ React9.createElement(EditBlockRow, { block });
8400
8776
  case "table":
8401
- return /* @__PURE__ */ React7.createElement(TableBlockRow, { block, citations });
8777
+ return /* @__PURE__ */ React9.createElement(TableBlockRow, { block, citations });
8402
8778
  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");
8779
+ return /* @__PURE__ */ React9.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
8780
  }
8405
8781
  }
8406
8782
  function ParagraphView({ text, citations }) {
8407
8783
  if (!text.includes("\n")) {
8408
- return /* @__PURE__ */ React7.createElement(InlineMd, { text, citations });
8784
+ return /* @__PURE__ */ React9.createElement(InlineMd, { text, citations });
8409
8785
  }
8410
8786
  const rows = text.split("\n");
8411
- return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column" }, rows.map((row2, i) => (
8787
+ return /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column" }, rows.map((row2, i) => (
8412
8788
  // 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 })
8789
+ /* @__PURE__ */ React9.createElement(InlineMd, { key: `ln-${i}`, text: row2, citations })
8414
8790
  )));
8415
8791
  }
8416
8792
  function bulletPrefix(block, i, item) {
@@ -8423,12 +8799,11 @@ function BlockquoteView({
8423
8799
  block,
8424
8800
  citations
8425
8801
  }) {
8426
- return /* @__PURE__ */ React7.createElement(
8427
- Box7,
8802
+ return /* @__PURE__ */ React9.createElement(
8803
+ Box8,
8428
8804
  {
8429
8805
  borderStyle: "single",
8430
- borderColor: "gray",
8431
- borderDimColor: true,
8806
+ borderColor: "#c4b5fd",
8432
8807
  borderTop: false,
8433
8808
  borderRight: false,
8434
8809
  borderBottom: false,
@@ -8436,7 +8811,7 @@ function BlockquoteView({
8436
8811
  flexDirection: "column",
8437
8812
  gap: 1
8438
8813
  },
8439
- block.children.map((child, i) => /* @__PURE__ */ React7.createElement(BlockView, { key: `q-${i}-${child.kind}`, block: child, citations }))
8814
+ block.children.map((child, i) => /* @__PURE__ */ React9.createElement(BlockView, { key: `q-${i}-${child.kind}`, block: child, citations }))
8440
8815
  );
8441
8816
  }
8442
8817
  function splitTableRow(line) {
@@ -8454,14 +8829,14 @@ function TableBlockRow({ block, citations }) {
8454
8829
  widths.push(Math.min(40, Math.max(3, ...cellLengths)));
8455
8830
  }
8456
8831
  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) => (
8832
+ return /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column" }, /* @__PURE__ */ React9.createElement(Box8, null, block.header.map((cell, ci) => (
8458
8833
  // 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) => (
8834
+ /* @__PURE__ */ React9.createElement(Text7, { key: `h-${ci}`, bold: true, color: "cyan" }, /* @__PURE__ */ React9.createElement(InlineMd, { text: cell, padTo: widths[ci] ?? 3, citations }), ci < colCount - 1 ? " \u2502 " : "")
8835
+ ))), /* @__PURE__ */ React9.createElement(Text7, { dimColor: true }, separator), block.rows.map((row2, ri) => (
8461
8836
  // 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) => (
8837
+ /* @__PURE__ */ React9.createElement(Box8, { key: `r-${ri}` }, Array.from({ length: colCount }).map((_, ci) => (
8463
8838
  // 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 " : "")
8839
+ /* @__PURE__ */ React9.createElement(Text7, { key: `c-${ri}-${ci}` }, /* @__PURE__ */ React9.createElement(InlineMd, { text: row2[ci] ?? "", padTo: widths[ci] ?? 3, citations }), ci < colCount - 1 ? " \u2502 " : "")
8465
8840
  )))
8466
8841
  )));
8467
8842
  }
@@ -8481,7 +8856,7 @@ function EditBlockRow({ block }) {
8481
8856
  const isNewFile = block.search.length === 0;
8482
8857
  const searchLines = block.search.split("\n");
8483
8858
  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}`))));
8859
+ return /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(Text7, { bold: true, color: "cyan" }, block.filename), isNewFile ? /* @__PURE__ */ React9.createElement(Text7, { color: "green", bold: true }, " (new file)") : null), isNewFile ? null : /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", marginTop: 1 }, searchLines.map((line, i) => /* @__PURE__ */ React9.createElement(Text7, { key: `s-${i}-${line.length}`, color: "red" }, `- ${line}`))), /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", marginTop: isNewFile ? 1 : 0 }, replaceLines.map((line, i) => /* @__PURE__ */ React9.createElement(Text7, { key: `r-${i}-${line.length}`, color: "green" }, `+ ${line}`))));
8485
8860
  }
8486
8861
  var DIAGRAM_LANGS = /* @__PURE__ */ new Set([
8487
8862
  "mermaid",
@@ -8501,43 +8876,137 @@ var DIAGRAM_VIEWER_HINT = {
8501
8876
  dot: "\u2192 paste at https://dreampuf.github.io/GraphvizOnline to view",
8502
8877
  graphviz: "\u2192 paste at https://dreampuf.github.io/GraphvizOnline to view"
8503
8878
  };
8879
+ function HeadingView({
8880
+ level,
8881
+ text,
8882
+ citations
8883
+ }) {
8884
+ if (level === 1) {
8885
+ return /* @__PURE__ */ React9.createElement(Box8, { marginY: 1 }, /* @__PURE__ */ React9.createElement(Text7, { backgroundColor: "#67e8f9", color: "black", bold: true }, ` ${text} `));
8886
+ }
8887
+ if (level === 2) {
8888
+ return /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(Text7, { backgroundColor: "#c4b5fd", color: "black", bold: true }, ` ${text} `));
8889
+ }
8890
+ if (level === 3) {
8891
+ return /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(Text7, { backgroundColor: "#f0abfc", color: "black", bold: true }, ` ${text} `));
8892
+ }
8893
+ return /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(Text7, { bold: true, color: "#f0abfc" }, "\u25B8", " "), /* @__PURE__ */ React9.createElement(Text7, { bold: true }, /* @__PURE__ */ React9.createElement(InlineMd, { text, citations })));
8894
+ }
8895
+ function CodeBlockView({ lang, text }) {
8896
+ const langLabel = lang.trim();
8897
+ return /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", borderStyle: "round", borderColor: "#7dd3fc", paddingX: 1 }, langLabel ? /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(Text7, { backgroundColor: "#7dd3fc", color: "black", bold: true }, ` ${langLabel} `)) : null, /* @__PURE__ */ React9.createElement(Text7, { color: "#fde68a" }, text));
8898
+ }
8504
8899
  function DiagramCodeBlock({ lang, text }) {
8505
8900
  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)));
8901
+ return /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", borderStyle: "double", borderColor: "magenta", paddingX: 1 }, /* @__PURE__ */ React9.createElement(Text7, { bold: true, color: "magenta" }, `\u25C7 ${lang} diagram (source \u2014 terminal can't draw the graph)`), /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(Text7, { color: "yellow" }, text)), /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(Text7, { dimColor: true }, hint)));
8507
8902
  }
8508
8903
  function Markdown({ text, projectRoot }) {
8509
8904
  const cleaned = expandAutolinks(expandEmoji(stripMath(text)));
8510
8905
  const root = projectRoot ?? process.cwd();
8511
- const citations = React7.useMemo(() => collectCitations(cleaned, root), [cleaned, root]);
8512
- const blocks = React7.useMemo(() => parseBlocks(cleaned), [cleaned]);
8906
+ const citations = React9.useMemo(() => collectCitations(cleaned, root), [cleaned, root]);
8907
+ const blocks = React9.useMemo(() => parseBlocks(cleaned), [cleaned]);
8513
8908
  const broken = [];
8514
8909
  for (const [url, status2] of citations) {
8515
8910
  if (!status2.ok) broken.push({ url, reason: status2.reason });
8516
8911
  }
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);
8912
+ return /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", gap: 1 }, blocks.map((b, i) => /* @__PURE__ */ React9.createElement(BlockView, { key: `${i}-${b.kind}`, block: b, citations })), broken.length > 0 ? /* @__PURE__ */ React9.createElement(BrokenCitationsBlock, { items: broken }) : null);
8518
8913
  }
8519
8914
  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) => (
8915
+ return /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", borderStyle: "round", borderColor: "red", paddingX: 1 }, /* @__PURE__ */ React9.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
8916
  // 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}`)
8917
+ /* @__PURE__ */ React9.createElement(Text7, { key: `bc-${i}`, color: "red" }, ` \u2717 ${b.url} \u2192 ${b.reason}`)
8523
8918
  )));
8524
8919
  }
8525
8920
 
8921
+ // src/cli/ui/theme.ts
8922
+ var GRADIENT = [
8923
+ "#5eead4",
8924
+ // teal
8925
+ "#67e8f9",
8926
+ // cyan
8927
+ "#7dd3fc",
8928
+ // sky
8929
+ "#93c5fd",
8930
+ // blue
8931
+ "#a5b4fc",
8932
+ // indigo
8933
+ "#c4b5fd",
8934
+ // violet
8935
+ "#d8b4fe",
8936
+ // purple
8937
+ "#f0abfc"
8938
+ // fuchsia
8939
+ ];
8940
+ var COLOR = {
8941
+ primary: "#67e8f9",
8942
+ // cyan-300
8943
+ accent: "#c4b5fd",
8944
+ // violet-300
8945
+ brand: "#5eead4",
8946
+ // teal-300
8947
+ user: "#67e8f9",
8948
+ // user message glyph + bar
8949
+ assistant: "#86efac",
8950
+ // green-300, assistant glyph + bar
8951
+ tool: "#fcd34d",
8952
+ // amber-300, tool ok pill bg
8953
+ toolErr: "#fda4af",
8954
+ // rose-300, tool err pill bg
8955
+ info: "#94a3b8",
8956
+ // slate-400, info / dim
8957
+ warn: "#fbbf24",
8958
+ // amber-400
8959
+ err: "#f87171",
8960
+ // red-400
8961
+ ok: "#4ade80"
8962
+ // green-400
8963
+ };
8964
+ var GLYPH = {
8965
+ brand: "\u25C8",
8966
+ user: "\u25C7",
8967
+ assistant: "\u25C6",
8968
+ toolOk: "\u25A3",
8969
+ toolErr: "\u25A5",
8970
+ warn: "\u25B2",
8971
+ err: "\u2726",
8972
+ arrow: "\u203A",
8973
+ bullet: "\xB7",
8974
+ bar: "\u258E",
8975
+ thinBar: "\u258F",
8976
+ block: "\u2588",
8977
+ shade1: "\u2591",
8978
+ shade2: "\u2592",
8979
+ shade3: "\u2593"
8980
+ };
8981
+ function gradientCells(width, glyph = GLYPH.block) {
8982
+ const cells = [];
8983
+ if (width <= 0) return cells;
8984
+ const last = GRADIENT.length - 1;
8985
+ for (let i = 0; i < width; i++) {
8986
+ const t = width === 1 ? 0 : i * last / (width - 1);
8987
+ const lo = Math.floor(t);
8988
+ const hi = Math.min(last, lo + 1);
8989
+ const color = t - lo < 0.5 ? GRADIENT[lo] : GRADIENT[hi];
8990
+ cells.push({ ch: glyph, color });
8991
+ }
8992
+ return cells;
8993
+ }
8994
+
8526
8995
  // src/cli/ui/ticker.tsx
8527
- import React8, { createContext, useContext, useEffect, useState as useState3 } from "react";
8996
+ import React10, { createContext as createContext2, useContext as useContext2, useEffect as useEffect2, useState as useState3 } from "react";
8528
8997
  var TICK_MS = 120;
8529
- var TickContext = createContext(0);
8998
+ var TickContext = createContext2(0);
8530
8999
  function TickerProvider({ children, disabled }) {
8531
9000
  const [tick, setTick] = useState3(0);
8532
- useEffect(() => {
9001
+ useEffect2(() => {
8533
9002
  if (disabled) return;
8534
9003
  const id = setInterval(() => setTick((t) => t + 1), TICK_MS);
8535
9004
  return () => clearInterval(id);
8536
9005
  }, [disabled]);
8537
- return /* @__PURE__ */ React8.createElement(TickContext.Provider, { value: tick }, children);
9006
+ return /* @__PURE__ */ React10.createElement(TickContext.Provider, { value: tick }, children);
8538
9007
  }
8539
9008
  function useTick() {
8540
- return useContext(TickContext);
9009
+ return useContext2(TickContext);
8541
9010
  }
8542
9011
  function useElapsedSeconds() {
8543
9012
  const [start] = useState3(() => Date.now());
@@ -8687,51 +9156,105 @@ function RoleGlyph({
8687
9156
  glyph,
8688
9157
  color
8689
9158
  }) {
8690
- return /* @__PURE__ */ React9.createElement(Text8, { color, bold: true }, glyph);
9159
+ return /* @__PURE__ */ React11.createElement(Text8, { color, bold: true }, glyph);
9160
+ }
9161
+ function ToolPill({ label, status: status2 }) {
9162
+ const bg = status2 === "err" ? "red" : "yellow";
9163
+ const symbol = status2 === "err" ? "\u2717" : "\u2713";
9164
+ return /* @__PURE__ */ React11.createElement(Text8, { backgroundColor: bg, color: "black", bold: true }, ` ${symbol} ${label} `);
8691
9165
  }
8692
9166
  function indentContinuationLines(text) {
8693
9167
  if (!text.includes("\n")) return text;
8694
9168
  return text.split("\n").join("\n ");
8695
9169
  }
8696
- var EventRow = React9.memo(function EventRow2({
9170
+ var EventRow = React11.memo(function EventRow2({
8697
9171
  event,
8698
9172
  projectRoot
8699
9173
  }) {
8700
9174
  if (event.role === "user") {
8701
- 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, " ", indentContinuationLines(event.text)));
9175
+ return /* @__PURE__ */ React11.createElement(Box9, { flexDirection: "column" }, event.leadSeparator ? /* @__PURE__ */ React11.createElement(TurnSeparator, null) : null, /* @__PURE__ */ React11.createElement(Box9, null, /* @__PURE__ */ React11.createElement(RoleGlyph, { glyph: ROLE_GLYPH.user, color: "cyan" }), /* @__PURE__ */ React11.createElement(Text8, null, " "), /* @__PURE__ */ React11.createElement(
9176
+ Box9,
9177
+ {
9178
+ flexDirection: "column",
9179
+ borderStyle: "single",
9180
+ borderTop: false,
9181
+ borderRight: false,
9182
+ borderBottom: false,
9183
+ borderColor: COLOR.user,
9184
+ paddingLeft: 1
9185
+ },
9186
+ /* @__PURE__ */ React11.createElement(Text8, null, indentContinuationLines(event.text))
9187
+ )));
8702
9188
  }
8703
9189
  if (event.role === "assistant") {
8704
- if (event.streaming) return /* @__PURE__ */ React9.createElement(StreamingAssistant, { event });
8705
- 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));
9190
+ if (event.streaming) return /* @__PURE__ */ React11.createElement(StreamingAssistant, { event });
9191
+ return /* @__PURE__ */ React11.createElement(Box9, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React11.createElement(Box9, null, /* @__PURE__ */ React11.createElement(RoleGlyph, { glyph: ROLE_GLYPH.assistant, color: "green" }), event.stats ? /* @__PURE__ */ React11.createElement(React11.Fragment, null, /* @__PURE__ */ React11.createElement(Text8, null, " "), /* @__PURE__ */ React11.createElement(Text8, { backgroundColor: COLOR.assistant, color: "black", bold: true }, ` ${event.stats.model.replace(/^deepseek-/, "")} `)) : null), /* @__PURE__ */ React11.createElement(
9192
+ Box9,
9193
+ {
9194
+ flexDirection: "column",
9195
+ marginTop: 1,
9196
+ borderStyle: "single",
9197
+ borderTop: false,
9198
+ borderRight: false,
9199
+ borderBottom: false,
9200
+ borderColor: COLOR.assistant,
9201
+ paddingLeft: 1
9202
+ },
9203
+ event.branch ? /* @__PURE__ */ React11.createElement(BranchBlock, { branch: event.branch }) : null,
9204
+ event.reasoning ? /* @__PURE__ */ React11.createElement(ReasoningBlock, { reasoning: event.reasoning }) : null,
9205
+ !isPlanStateEmpty(event.planState) ? /* @__PURE__ */ React11.createElement(PlanStateBlock, { planState: event.planState }) : null,
9206
+ event.text ? /* @__PURE__ */ React11.createElement(Markdown, { text: event.text, projectRoot }) : /* @__PURE__ */ React11.createElement(Text8, { dimColor: true }, "(no content)"),
9207
+ event.stats ? /* @__PURE__ */ React11.createElement(StatsLine, { stats: event.stats }) : null,
9208
+ event.repair ? /* @__PURE__ */ React11.createElement(Text8, { color: COLOR.accent }, event.repair) : null
9209
+ ));
8706
9210
  }
8707
9211
  if (event.role === "tool") {
8708
9212
  const isExplicitError = event.text.startsWith("ERROR:");
8709
9213
  const isEditFile = (event.toolName === "edit_file" || event.toolName?.endsWith("_edit_file")) && !isExplicitError;
8710
9214
  if (isEditFile) {
8711
- 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 })));
9215
+ return /* @__PURE__ */ React11.createElement(Box9, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React11.createElement(Box9, null, /* @__PURE__ */ React11.createElement(ToolPill, { label: event.toolName ?? "?", status: "ok" }), /* @__PURE__ */ React11.createElement(Text8, { dimColor: true }, " diff:")), /* @__PURE__ */ React11.createElement(
9216
+ Box9,
9217
+ {
9218
+ flexDirection: "column",
9219
+ marginTop: 1,
9220
+ borderStyle: "single",
9221
+ borderTop: false,
9222
+ borderRight: false,
9223
+ borderBottom: false,
9224
+ borderColor: COLOR.tool,
9225
+ paddingLeft: 1
9226
+ },
9227
+ /* @__PURE__ */ React11.createElement(EditFileDiff, { text: event.text })
9228
+ ));
8712
9229
  }
8713
9230
  const summary = summarizeToolResult(event.toolName ?? "?", event.text);
8714
- const color = summary.isError ? "red" : "yellow";
8715
- const glyph = summary.isError ? ROLE_GLYPH.toolErr : ROLE_GLYPH.toolOk;
8716
- const marker = summary.isError ? "\u2717" : "\u2192";
8717
- const durationLabel = event.durationMs !== void 0 && event.durationMs >= 100 ? ` (${formatDuration(event.durationMs)})` : "";
9231
+ const status2 = summary.isError ? "err" : "ok";
9232
+ const durationLabel = event.durationMs !== void 0 && event.durationMs >= 100 ? formatDuration(event.durationMs) : "";
8718
9233
  const indexHint = event.toolIndex !== void 0 ? ` /tool ${event.toolIndex}` : "";
8719
- 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);
9234
+ return /* @__PURE__ */ React11.createElement(Box9, null, /* @__PURE__ */ React11.createElement(ToolPill, { label: event.toolName ?? "?", status: status2 }), durationLabel ? /* @__PURE__ */ React11.createElement(Text8, { dimColor: true }, ` ${durationLabel}`) : null, /* @__PURE__ */ React11.createElement(Text8, { dimColor: true }, " "), /* @__PURE__ */ React11.createElement(Text8, { color: status2 === "err" ? "red" : void 0, dimColor: status2 === "ok" }, summary.summary), indexHint ? /* @__PURE__ */ React11.createElement(Text8, { dimColor: true }, indexHint) : null);
8720
9235
  }
8721
9236
  if (event.role === "error") {
8722
- return /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(RoleGlyph, { glyph: ROLE_GLYPH.error, color: "red" }), /* @__PURE__ */ React9.createElement(Text8, { color: "red" }, " ", indentContinuationLines(event.text)));
9237
+ return /* @__PURE__ */ React11.createElement(Box9, { marginTop: 1 }, /* @__PURE__ */ React11.createElement(Text8, { backgroundColor: "#f87171", color: "black", bold: true }, " \u2726 ERROR "), /* @__PURE__ */ React11.createElement(Text8, null, " "), /* @__PURE__ */ React11.createElement(Text8, { color: "#f87171" }, indentContinuationLines(event.text)));
8723
9238
  }
8724
9239
  if (event.role === "info") {
8725
- return /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, event.text));
9240
+ const m = event.text.match(/^([▸▶▲⚠✓✗✖↻ⓘ])\s*(.*)$/s);
9241
+ const lead = m?.[1] ?? "\u25B8";
9242
+ const body = m?.[2] ?? event.text;
9243
+ let leadColor = COLOR.info;
9244
+ if (lead === "\u25B2" || lead === "\u26A0") leadColor = COLOR.warn;
9245
+ else if (lead === "\u2713") leadColor = COLOR.ok;
9246
+ else if (lead === "\u2717" || lead === "\u2716") leadColor = COLOR.err;
9247
+ else if (lead === "\u21BB") leadColor = COLOR.primary;
9248
+ return /* @__PURE__ */ React11.createElement(Box9, null, /* @__PURE__ */ React11.createElement(Text8, { color: leadColor, bold: true }, lead), /* @__PURE__ */ React11.createElement(Text8, null, " "), /* @__PURE__ */ React11.createElement(Text8, { dimColor: true }, body));
8726
9249
  }
8727
9250
  if (event.role === "plan") {
8728
- 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 })));
9251
+ return /* @__PURE__ */ React11.createElement(Box9, { flexDirection: "column", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React11.createElement(Box9, null, /* @__PURE__ */ React11.createElement(Text8, { bold: true, color: "cyan" }, "\u{1F4CB} plan proposed \u2014 pick a choice below")), /* @__PURE__ */ React11.createElement(Box9, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React11.createElement(Markdown, { text: event.text, projectRoot })));
8729
9252
  }
8730
9253
  if (event.role === "step-progress") {
8731
9254
  const sp = event.stepProgress;
8732
- const counter = sp && sp.total > 0 ? ` (${sp.completed}/${sp.total})` : "";
9255
+ const counter = sp && sp.total > 0 ? `${sp.completed}/${sp.total}` : "";
8733
9256
  const label = sp?.title ? `${sp.stepId} \xB7 ${sp.title}` : sp?.stepId ?? "";
8734
- 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);
9257
+ return /* @__PURE__ */ React11.createElement(Box9, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React11.createElement(Box9, null, /* @__PURE__ */ React11.createElement(Text8, { backgroundColor: "#4ade80", color: "black", bold: true }, " \u2713 STEP "), counter ? /* @__PURE__ */ React11.createElement(React11.Fragment, null, /* @__PURE__ */ React11.createElement(Text8, null, " "), /* @__PURE__ */ React11.createElement(Text8, { color: "#4ade80", bold: true }, counter)) : null, /* @__PURE__ */ React11.createElement(Text8, null, " "), /* @__PURE__ */ React11.createElement(Text8, { color: "#86efac" }, label)), event.text ? /* @__PURE__ */ React11.createElement(Box9, { paddingLeft: 2 }, /* @__PURE__ */ React11.createElement(Text8, { dimColor: true }, event.text)) : null, sp?.notes ? /* @__PURE__ */ React11.createElement(Box9, { paddingLeft: 2 }, /* @__PURE__ */ React11.createElement(Text8, { color: "#fbbf24", dimColor: true }, `note: ${sp.notes}`)) : null);
8735
9258
  }
8736
9259
  if (event.role === "plan-resumed") {
8737
9260
  const rp = event.resumedPlan;
@@ -8746,7 +9269,7 @@ var EventRow = React9.memo(function EventRow2({
8746
9269
  ])
8747
9270
  );
8748
9271
  const nextStep = rp.steps.find((s) => !completedSet.has(s.id));
8749
- 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 })));
9272
+ return /* @__PURE__ */ React11.createElement(Box9, { flexDirection: "column", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React11.createElement(Box9, null, /* @__PURE__ */ React11.createElement(Text8, { backgroundColor: "#67e8f9", color: "black", bold: true }, " \u21BB RESUMED PLAN "), /* @__PURE__ */ React11.createElement(Text8, null, " "), /* @__PURE__ */ React11.createElement(Text8, { color: "#67e8f9", bold: true }, `${done}/${total}`), /* @__PURE__ */ React11.createElement(Text8, { dimColor: true }, ` done \xB7 last touched ${rp.relativeTime}`)), rp.summary ? /* @__PURE__ */ React11.createElement(Box9, { marginTop: 1 }, /* @__PURE__ */ React11.createElement(Text8, { color: "#67e8f9" }, rp.summary)) : null, /* @__PURE__ */ React11.createElement(Box9, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React11.createElement(PlanStepList, { steps: rp.steps, statuses, focusStepId: nextStep?.id })));
8750
9273
  }
8751
9274
  if (event.role === "plan-replay") {
8752
9275
  const r = event.replayPlan;
@@ -8757,67 +9280,79 @@ var EventRow = React9.memo(function EventRow2({
8757
9280
  const statuses = new Map(
8758
9281
  r.steps.map((s) => [s.id, completedSet.has(s.id) ? "done" : "pending"])
8759
9282
  );
8760
- const navHint = r.total > 1 ? ` \xB7 ${r.index}/${r.total}` : "";
8761
- 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(
8762
- Text8,
8763
- {
8764
- dimColor: true
8765
- },
8766
- ` completed ${r.relativeTime} \xB7 ${done}/${total} done${navHint}`
8767
- )), 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)")));
9283
+ const navHint = r.total > 1 ? ` \xB7 ${r.index}/${r.total}` : "";
9284
+ return /* @__PURE__ */ React11.createElement(Box9, { flexDirection: "column", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React11.createElement(Box9, null, /* @__PURE__ */ React11.createElement(Text8, { backgroundColor: "#94a3b8", color: "black", bold: true }, " \u23EA REPLAY "), /* @__PURE__ */ React11.createElement(Text8, null, " "), /* @__PURE__ */ React11.createElement(Text8, { color: "#94a3b8", bold: true }, `${done}/${total}`), /* @__PURE__ */ React11.createElement(Text8, { dimColor: true }, ` done \xB7 ${r.relativeTime}${navHint}`)), r.summary ? /* @__PURE__ */ React11.createElement(Box9, { marginTop: 1 }, /* @__PURE__ */ React11.createElement(Text8, { color: "#94a3b8" }, r.summary)) : null, /* @__PURE__ */ React11.createElement(Box9, null, /* @__PURE__ */ React11.createElement(Text8, { dimColor: true }, r.archiveBasename)), r.body ? /* @__PURE__ */ React11.createElement(Box9, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React11.createElement(Markdown, { text: r.body, projectRoot })) : null, /* @__PURE__ */ React11.createElement(Box9, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React11.createElement(PlanStepList, { steps: r.steps, statuses })), /* @__PURE__ */ React11.createElement(Box9, { marginTop: 1 }, /* @__PURE__ */ React11.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)")));
8768
9285
  }
8769
9286
  if (event.role === "warning") {
8770
- return /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(RoleGlyph, { glyph: ROLE_GLYPH.warning, color: "yellow" }), /* @__PURE__ */ React9.createElement(Text8, { color: "yellow" }, " ", indentContinuationLines(event.text)));
9287
+ return /* @__PURE__ */ React11.createElement(Box9, null, /* @__PURE__ */ React11.createElement(Text8, { backgroundColor: "#fbbf24", color: "black", bold: true }, " \u25B2 WARN "), /* @__PURE__ */ React11.createElement(Text8, null, " "), /* @__PURE__ */ React11.createElement(Text8, { color: "#fbbf24" }, indentContinuationLines(event.text)));
8771
9288
  }
8772
- return /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(Text8, null, event.text));
9289
+ return /* @__PURE__ */ React11.createElement(Box9, null, /* @__PURE__ */ React11.createElement(Text8, null, event.text));
8773
9290
  });
9291
+ function TurnSeparator() {
9292
+ const { stdout: stdout2 } = useStdout3();
9293
+ const cols = stdout2?.columns ?? 80;
9294
+ const width = Math.max(16, cols - 2);
9295
+ const sideWidth = Math.max(2, Math.floor((width - 5) / 2));
9296
+ const leftCells = gradientCells(sideWidth, "\u2500");
9297
+ const rightCells = gradientCells(sideWidth, "\u2500");
9298
+ return /* @__PURE__ */ React11.createElement(Box9, { marginTop: 1, marginBottom: 1 }, leftCells.map((c, i) => (
9299
+ // biome-ignore lint/suspicious/noArrayIndexKey: fixed-width gradient row
9300
+ /* @__PURE__ */ React11.createElement(Text8, { key: `tsep-l-${i}`, color: c.color }, c.ch)
9301
+ )), /* @__PURE__ */ React11.createElement(Text8, { color: COLOR.brand, bold: true }, " \u25C6 "), rightCells.map((c, i) => (
9302
+ // biome-ignore lint/suspicious/noArrayIndexKey: fixed-width gradient row
9303
+ /* @__PURE__ */ React11.createElement(Text8, { key: `tsep-r-${i}`, color: c.color }, c.ch)
9304
+ )));
9305
+ }
8774
9306
  function EditFileDiff({ text }) {
8775
9307
  const lines = text.split(/\r?\n/);
8776
9308
  const [statusHeader, hunkHeader, ...body] = lines;
8777
- 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) => {
9309
+ return /* @__PURE__ */ React11.createElement(Box9, { flexDirection: "column" }, /* @__PURE__ */ React11.createElement(Text8, { dimColor: true }, ` ${statusHeader ?? ""}`), hunkHeader !== void 0 ? /* @__PURE__ */ React11.createElement(Box9, { marginTop: 1 }, /* @__PURE__ */ React11.createElement(Text8, { backgroundColor: "#c4b5fd", color: "black", bold: true }, ` ${hunkHeader.trim()} `)) : null, body.map((line, i) => {
8778
9310
  const key = `${i}-${line.slice(0, 32)}`;
8779
- if (line.startsWith("- ")) {
8780
- return /* @__PURE__ */ React9.createElement(Text8, { key, color: "red" }, line);
9311
+ const stripped = line.replace(/^ {2}/, "");
9312
+ if (stripped.startsWith("- ")) {
9313
+ return /* @__PURE__ */ React11.createElement(Box9, { key }, /* @__PURE__ */ React11.createElement(Text8, { color: "#f87171", bold: true }, "\u2212 "), /* @__PURE__ */ React11.createElement(Text8, { color: "#fca5a5" }, stripped.slice(2)));
8781
9314
  }
8782
- if (line.startsWith("+ ")) {
8783
- return /* @__PURE__ */ React9.createElement(Text8, { key, color: "green" }, line);
9315
+ if (stripped.startsWith("+ ")) {
9316
+ return /* @__PURE__ */ React11.createElement(Box9, { key }, /* @__PURE__ */ React11.createElement(Text8, { color: "#4ade80", bold: true }, "+ "), /* @__PURE__ */ React11.createElement(Text8, { color: "#86efac" }, stripped.slice(2)));
8784
9317
  }
8785
- return /* @__PURE__ */ React9.createElement(Text8, { key, dimColor: true }, line);
9318
+ return /* @__PURE__ */ React11.createElement(Box9, { key }, /* @__PURE__ */ React11.createElement(Text8, { dimColor: true }, " "), /* @__PURE__ */ React11.createElement(Text8, { dimColor: true }, stripped));
8786
9319
  }));
8787
9320
  }
8788
9321
  function BranchBlock({ branch: branch2 }) {
8789
- const per = branch2.uncertainties.map((u, i) => {
8790
- const marker = i === branch2.chosenIndex ? "\u25B8" : " ";
9322
+ return /* @__PURE__ */ React11.createElement(Box9, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React11.createElement(Box9, null, /* @__PURE__ */ React11.createElement(Text8, { backgroundColor: "#93c5fd", color: "black", bold: true }, ` \u2387 BRANCH \xD7${branch2.budget} `), /* @__PURE__ */ React11.createElement(Text8, null, " "), /* @__PURE__ */ React11.createElement(Text8, { color: "#93c5fd" }, "picked "), /* @__PURE__ */ React11.createElement(Text8, { color: "#93c5fd", bold: true }, "#", branch2.chosenIndex)), /* @__PURE__ */ React11.createElement(Box9, { paddingLeft: 2, marginTop: 1 }, branch2.uncertainties.map((u, i) => {
9323
+ const chosen = i === branch2.chosenIndex;
8791
9324
  const t = (branch2.temperatures[i] ?? 0).toFixed(1);
8792
- return `${marker} #${i} T=${t} u=${u}`;
8793
- }).join(" ");
8794
- 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)));
9325
+ return (
9326
+ // biome-ignore lint/suspicious/noArrayIndexKey: branch index is positional and stable
9327
+ /* @__PURE__ */ React11.createElement(Text8, { key: `b-${i}` }, /* @__PURE__ */ React11.createElement(Text8, { color: chosen ? "#93c5fd" : "#475569", bold: chosen }, chosen ? "\u25B8 " : " "), /* @__PURE__ */ React11.createElement(Text8, { color: chosen ? "#93c5fd" : "#94a3b8", bold: chosen }, `#${i}`), /* @__PURE__ */ React11.createElement(Text8, { dimColor: true }, ` T=${t} u=${u} `))
9328
+ );
9329
+ })));
8795
9330
  }
8796
9331
  function ReasoningBlock({ reasoning }) {
8797
9332
  const max = 260;
8798
9333
  const flat = reasoning.replace(/\s+/g, " ").trim();
8799
9334
  const preview = flat.length <= max ? flat : `\u2026 (+${flat.length - max} earlier chars) ${flat.slice(-max)}`;
8800
- return /* @__PURE__ */ React9.createElement(Box8, { marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, "\u258F "), /* @__PURE__ */ React9.createElement(Text8, { dimColor: true, italic: true }, "thinking ", preview));
9335
+ return /* @__PURE__ */ React11.createElement(Box9, { marginBottom: 1 }, /* @__PURE__ */ React11.createElement(Text8, { backgroundColor: COLOR.accent, color: "black", bold: true }, " \u22EF thinking "), /* @__PURE__ */ React11.createElement(Text8, null, " "), /* @__PURE__ */ React11.createElement(Text8, { color: COLOR.accent, italic: true, dimColor: true }, preview));
8801
9336
  }
8802
9337
  function Elapsed() {
8803
9338
  const s = useElapsedSeconds();
8804
9339
  const mm = String(Math.floor(s / 60)).padStart(2, "0");
8805
9340
  const ss = String(s % 60).padStart(2, "0");
8806
- return /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, `${mm}:${ss}`);
9341
+ return /* @__PURE__ */ React11.createElement(Text8, { dimColor: true }, `${mm}:${ss}`);
8807
9342
  }
8808
9343
  function PulsingAssistantGlyph() {
8809
9344
  const tick = useTick();
8810
9345
  const on = Math.floor(tick / 4) % 2 === 0;
8811
- return /* @__PURE__ */ React9.createElement(Text8, { color: "green", bold: true }, on ? ROLE_GLYPH.assistant : ROLE_GLYPH.assistantPulse);
9346
+ return /* @__PURE__ */ React11.createElement(Text8, { color: "green", bold: true }, on ? ROLE_GLYPH.assistant : ROLE_GLYPH.assistantPulse);
8812
9347
  }
8813
9348
  function StreamingAssistant({ event }) {
8814
9349
  if (event.branchProgress) {
8815
9350
  const p = event.branchProgress;
8816
9351
  if (p.completed === 0) {
8817
- 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"));
9352
+ return /* @__PURE__ */ React11.createElement(Box9, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React11.createElement(Box9, null, /* @__PURE__ */ React11.createElement(PulsingAssistantGlyph, null), /* @__PURE__ */ React11.createElement(Text8, { color: "blue" }, " \u2387 launching ", p.total, " parallel samples (R1 thinking in parallel)\u2026 "), /* @__PURE__ */ React11.createElement(Elapsed, null)), /* @__PURE__ */ React11.createElement(Text8, { color: "yellow" }, " ", "spread across T=0.0/0.5/1.0 \xB7 reasoner typically takes 30-90s \u2014 this is normal"));
8818
9353
  }
8819
9354
  const pct2 = Math.round(p.completed / p.total * 100);
8820
- 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"));
9355
+ return /* @__PURE__ */ React11.createElement(Box9, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React11.createElement(Box9, null, /* @__PURE__ */ React11.createElement(PulsingAssistantGlyph, null), /* @__PURE__ */ React11.createElement(Text8, { color: "blue" }, " \u2387 branching ", p.completed, "/", p.total, " (", pct2, "%) "), /* @__PURE__ */ React11.createElement(Elapsed, null)), /* @__PURE__ */ React11.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"));
8821
9356
  }
8822
9357
  const tail = lastLine(event.text, 140);
8823
9358
  const reasoningTail = event.reasoning ? lastLine(event.reasoning, 120) : "";
@@ -8825,38 +9360,43 @@ function StreamingAssistant({ event }) {
8825
9360
  const preFirstByte = !event.text && !event.reasoning && !toolCallBuild;
8826
9361
  const reasoningOnly = !event.text && !!event.reasoning && !toolCallBuild;
8827
9362
  const toolCallOnly = !event.text && !event.reasoning && !!toolCallBuild;
9363
+ let pillBg;
9364
+ let pillText;
8828
9365
  let label;
8829
- let labelColor;
8830
9366
  if (preFirstByte) {
9367
+ pillBg = "#fbbf24";
9368
+ pillText = "WAITING";
8831
9369
  label = "request sent \xB7 waiting for server";
8832
- labelColor = "yellow";
8833
9370
  } else if (reasoningOnly) {
8834
- label = `R1 reasoning \xB7 ${event.reasoning?.length ?? 0} chars of thought`;
8835
- labelColor = "cyan";
9371
+ pillBg = "#c4b5fd";
9372
+ pillText = "THINKING";
9373
+ label = `${event.reasoning?.length ?? 0} chars of thought`;
8836
9374
  } else if (toolCallOnly) {
8837
- label = `assembling tool call${formatToolCallIndex(toolCallBuild)} <${toolCallBuild.name}> \xB7 ${toolCallBuild.chars} chars of arguments${formatReadyTail(toolCallBuild)}`;
8838
- labelColor = "magenta";
9375
+ pillBg = "#f0abfc";
9376
+ pillText = "DISPATCH";
9377
+ label = `assembling${formatToolCallIndex(toolCallBuild)} <${toolCallBuild.name}> \xB7 ${toolCallBuild.chars} chars${formatReadyTail(toolCallBuild)}`;
8839
9378
  } else {
8840
- const parts = [`writing response \xB7 ${event.text.length} chars`];
8841
- if (event.reasoning) parts.push(`after ${event.reasoning.length} chars of reasoning`);
9379
+ pillBg = "#86efac";
9380
+ pillText = "WRITING";
9381
+ const parts = [`${event.text.length} chars`];
9382
+ if (event.reasoning) parts.push(`after ${event.reasoning.length} reasoning`);
8842
9383
  if (toolCallBuild) {
8843
9384
  parts.push(
8844
- `building tool call${formatToolCallIndex(toolCallBuild)} <${toolCallBuild.name}> \xB7 ${toolCallBuild.chars} chars${formatReadyTail(toolCallBuild)}`
9385
+ `tool${formatToolCallIndex(toolCallBuild)} <${toolCallBuild.name}> ${toolCallBuild.chars}c${formatReadyTail(toolCallBuild)}`
8845
9386
  );
8846
9387
  }
8847
9388
  label = parts.join(" \xB7 ");
8848
- labelColor = "green";
8849
9389
  }
8850
- 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 ? (
8851
- // Non-dim yellow: first-time users misread the dim version as
9390
+ return /* @__PURE__ */ React11.createElement(Box9, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React11.createElement(Box9, null, /* @__PURE__ */ React11.createElement(PulsingAssistantGlyph, null), /* @__PURE__ */ React11.createElement(Text8, null, " "), /* @__PURE__ */ React11.createElement(Pulse, null), /* @__PURE__ */ React11.createElement(Text8, null, " "), /* @__PURE__ */ React11.createElement(Text8, { backgroundColor: pillBg, color: "black", bold: true }, ` ${pillText} `), /* @__PURE__ */ React11.createElement(Text8, { dimColor: true }, ` ${label} `), /* @__PURE__ */ React11.createElement(Elapsed, null)), reasoningTail ? /* @__PURE__ */ React11.createElement(Box9, { paddingLeft: 3 }, /* @__PURE__ */ React11.createElement(Text8, { color: "#c4b5fd", italic: true, dimColor: true }, "\u21B3 ", reasoningTail)) : null, tail ? /* @__PURE__ */ React11.createElement(Box9, { paddingLeft: 3 }, /* @__PURE__ */ React11.createElement(Text8, { dimColor: true }, "\u25B8 ", tail)) : preFirstByte ? (
9391
+ // Non-dim amber: first-time users misread the dim version as
8852
9392
  // "app frozen". The reassurance has to be VISIBLE to do its job.
8853
- /* @__PURE__ */ React9.createElement(Text8, { color: "yellow", italic: true }, " waiting for first byte \u2014 this is normal, typically 5-60s depending on model + load")
8854
- ) : 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);
9393
+ /* @__PURE__ */ React11.createElement(Box9, { paddingLeft: 3 }, /* @__PURE__ */ React11.createElement(Text8, { color: "#fbbf24", italic: true }, "waiting for first byte \u2014 typical 5\u201360s depending on model + load"))
9394
+ ) : reasoningOnly ? /* @__PURE__ */ React11.createElement(Box9, { paddingLeft: 3 }, /* @__PURE__ */ React11.createElement(Text8, { color: "#c4b5fd", italic: true }, "R1 thinks before it speaks \u2014 body text arrives when reasoning finishes (20\u201390s)")) : toolCallOnly ? /* @__PURE__ */ React11.createElement(Box9, { paddingLeft: 3 }, /* @__PURE__ */ React11.createElement(Text8, { color: "#f0abfc", italic: true }, "tool-call arguments streaming \u2014 about to dispatch")) : event.reasoning ? /* @__PURE__ */ React11.createElement(Box9, { paddingLeft: 3 }, /* @__PURE__ */ React11.createElement(Text8, { color: "#fbbf24", italic: true }, "R1 still reasoning \u2014 body text or tool call arrives when thinking finishes")) : null);
8855
9395
  }
8856
9396
  function Pulse() {
8857
9397
  const tick = useTick();
8858
9398
  const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
8859
- return /* @__PURE__ */ React9.createElement(Text8, { color: "cyan" }, frames[Math.floor(tick / 4) % frames.length]);
9399
+ return /* @__PURE__ */ React11.createElement(Text8, { color: "cyan" }, frames[Math.floor(tick / 4) % frames.length]);
8860
9400
  }
8861
9401
  function formatToolCallIndex(tb) {
8862
9402
  if (!tb || tb.index === void 0) return "";
@@ -8875,17 +9415,18 @@ function lastLine(s, maxChars) {
8875
9415
  }
8876
9416
  function StatsLine({ stats: stats2 }) {
8877
9417
  const hit = (stats2.cacheHitRatio * 100).toFixed(1);
8878
- 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)));
9418
+ const hitColor = stats2.cacheHitRatio >= 0.7 ? "#4ade80" : stats2.cacheHitRatio >= 0.4 ? "#fcd34d" : "#f87171";
9419
+ return /* @__PURE__ */ React11.createElement(Box9, { marginTop: 1 }, /* @__PURE__ */ React11.createElement(Text8, { color: hitColor, bold: true }, `\u232C ${hit}%`), /* @__PURE__ */ React11.createElement(Text8, { dimColor: true }, " \xB7 "), /* @__PURE__ */ React11.createElement(Text8, { color: "#94a3b8" }, "in ", /* @__PURE__ */ React11.createElement(Text8, { color: "#67e8f9", bold: true }, stats2.usage.promptTokens), " \u2192 out ", /* @__PURE__ */ React11.createElement(Text8, { color: "#c4b5fd", bold: true }, stats2.usage.completionTokens)), /* @__PURE__ */ React11.createElement(Text8, { dimColor: true }, " \xB7 "), /* @__PURE__ */ React11.createElement(Text8, { color: "#86efac", bold: true }, `$${stats2.cost.toFixed(6)}`));
8879
9420
  }
8880
9421
 
8881
9422
  // src/cli/ui/LiveRows.tsx
8882
- import { Box as Box9, Text as Text9 } from "ink";
8883
- import React10 from "react";
9423
+ import { Box as Box10, Text as Text9, useStdout as useStdout4 } from "ink";
9424
+ import React12 from "react";
8884
9425
  var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
8885
9426
  function StatusRow({ text }) {
8886
9427
  const tick = useTick();
8887
9428
  const elapsed = useElapsedSeconds();
8888
- 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`));
9429
+ return /* @__PURE__ */ React12.createElement(Box10, { marginY: 1, paddingX: 1 }, /* @__PURE__ */ React12.createElement(Text9, { color: "#c4b5fd", bold: true }, SPINNER_FRAMES[tick % SPINNER_FRAMES.length]), /* @__PURE__ */ React12.createElement(Text9, null, " "), /* @__PURE__ */ React12.createElement(Text9, { color: "#c4b5fd" }, text), /* @__PURE__ */ React12.createElement(Text9, { dimColor: true }, ` \xB7 ${elapsed}s`));
8889
9430
  }
8890
9431
  function ModeStatusBar({
8891
9432
  editMode,
@@ -8897,15 +9438,28 @@ function ModeStatusBar({
8897
9438
  }) {
8898
9439
  useTick();
8899
9440
  const running = jobs2?.runningCount() ?? 0;
8900
- const jobsTag = running > 0 ? /* @__PURE__ */ React10.createElement(Text9, { color: "yellow", bold: true }, ` \xB7 \u23F5 ${running} job${running === 1 ? "" : "s"}`) : null;
9441
+ const jobsTag = running > 0 ? /* @__PURE__ */ React12.createElement(Text9, { color: "yellow", bold: true }, ` \xB7 \u23F5 ${running} job${running === 1 ? "" : "s"}`) : null;
8901
9442
  if (planMode) {
8902
- 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);
9443
+ return /* @__PURE__ */ React12.createElement(ModeBarFrame, null, /* @__PURE__ */ React12.createElement(ModePill, { label: "PLAN MODE", bg: "red", flash }), /* @__PURE__ */ React12.createElement(Text9, { dimColor: true }, " writes gated \xB7 /plan off to leave"), jobsTag);
8903
9444
  }
8904
- const label = editMode === "auto" ? "AUTO" : "review";
8905
- const color = editMode === "auto" ? "magenta" : "cyan";
8906
- 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";
8907
- const flip = editMode === "auto" ? "Shift+Tab \u2192 review" : "Shift+Tab \u2192 AUTO";
8908
- 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);
9445
+ const isAuto = editMode === "auto";
9446
+ const label = isAuto ? "AUTO" : "REVIEW";
9447
+ const bg = isAuto ? "magenta" : "cyan";
9448
+ const mid = isAuto ? "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";
9449
+ return /* @__PURE__ */ React12.createElement(ModeBarFrame, null, /* @__PURE__ */ React12.createElement(ModePill, { label, bg, flash }), /* @__PURE__ */ React12.createElement(Text9, { dimColor: true }, ` ${mid} \xB7 Shift+Tab to flip`), jobsTag);
9450
+ }
9451
+ function ModeBarFrame({ children }) {
9452
+ const { stdout: stdout2 } = useStdout4();
9453
+ const cols = stdout2?.columns ?? 80;
9454
+ const ruleWidth = Math.max(20, cols - 2);
9455
+ return /* @__PURE__ */ React12.createElement(Box10, { flexDirection: "column" }, /* @__PURE__ */ React12.createElement(Box10, { paddingX: 1 }, /* @__PURE__ */ React12.createElement(Text9, { color: "#475569", dimColor: true }, "\u254C".repeat(ruleWidth))), /* @__PURE__ */ React12.createElement(Box10, { paddingX: 1 }, children));
9456
+ }
9457
+ function ModePill({
9458
+ label,
9459
+ bg,
9460
+ flash
9461
+ }) {
9462
+ return /* @__PURE__ */ React12.createElement(Text9, { backgroundColor: bg, color: "white", bold: true, inverse: flash }, ` ${label} `);
8909
9463
  }
8910
9464
  function UndoBanner({
8911
9465
  banner
@@ -8915,14 +9469,15 @@ function UndoBanner({
8915
9469
  const remainingSec = Math.ceil(remainingMs / 1e3);
8916
9470
  const ok = banner.results.filter((r) => r.status === "applied" || r.status === "created").length;
8917
9471
  const total = banner.results.length;
8918
- 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 }, ")"));
9472
+ const urgent = remainingSec <= 1;
9473
+ return /* @__PURE__ */ React12.createElement(Box10, { marginY: 1, paddingX: 1 }, /* @__PURE__ */ React12.createElement(Text9, { backgroundColor: "#c4b5fd", color: "black", bold: true }, ` \u2713 AUTO-APPLIED ${ok}/${total} `), /* @__PURE__ */ React12.createElement(Text9, { dimColor: true }, " press "), /* @__PURE__ */ React12.createElement(Text9, { backgroundColor: "#67e8f9", color: "black", bold: true }, " u "), /* @__PURE__ */ React12.createElement(Text9, { dimColor: true }, " to undo "), /* @__PURE__ */ React12.createElement(Text9, { color: urgent ? "#f87171" : "#c4b5fd", bold: urgent }, `${remainingSec}s`));
8919
9474
  }
8920
9475
  function SubagentRow({
8921
9476
  activity
8922
9477
  }) {
8923
9478
  const tick = useTick();
8924
9479
  const seconds = (activity.elapsedMs / 1e3).toFixed(1);
8925
- 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`));
9480
+ return /* @__PURE__ */ React12.createElement(Box10, { paddingLeft: 3 }, /* @__PURE__ */ React12.createElement(Text9, { color: "#c4b5fd", bold: true }, SPINNER_FRAMES[tick % SPINNER_FRAMES.length]), /* @__PURE__ */ React12.createElement(Text9, null, " "), /* @__PURE__ */ React12.createElement(Text9, { backgroundColor: "#c4b5fd", color: "black", bold: true }, " \u232C subagent "), /* @__PURE__ */ React12.createElement(Text9, { color: "#c4b5fd" }, ` ${activity.task}`), /* @__PURE__ */ React12.createElement(Text9, { dimColor: true }, ` iter ${activity.iter} \xB7 ${seconds}s`));
8926
9481
  }
8927
9482
  function OngoingToolRow({
8928
9483
  tool: tool2,
@@ -8931,7 +9486,7 @@ function OngoingToolRow({
8931
9486
  const tick = useTick();
8932
9487
  const elapsed = useElapsedSeconds();
8933
9488
  const summary = summarizeToolArgs(tool2.name, tool2.args);
8934
- 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);
9489
+ return /* @__PURE__ */ React12.createElement(Box10, { marginY: 1, flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React12.createElement(Box10, null, /* @__PURE__ */ React12.createElement(Text9, { color: "#fcd34d", bold: true }, SPINNER_FRAMES[tick % SPINNER_FRAMES.length]), /* @__PURE__ */ React12.createElement(Text9, null, " "), /* @__PURE__ */ React12.createElement(Text9, { backgroundColor: "#fcd34d", color: "black", bold: true }, ` \u23F5 ${tool2.name} `), /* @__PURE__ */ React12.createElement(Text9, { color: "#fcd34d" }, " running"), /* @__PURE__ */ React12.createElement(Text9, { dimColor: true }, ` \xB7 ${elapsed}s`)), progress ? /* @__PURE__ */ React12.createElement(Box10, { paddingLeft: 3 }, /* @__PURE__ */ React12.createElement(Text9, { color: "#67e8f9" }, renderProgressLine(progress))) : null, summary ? /* @__PURE__ */ React12.createElement(Box10, { paddingLeft: 3 }, /* @__PURE__ */ React12.createElement(Text9, { dimColor: true }, summary)) : null);
8935
9490
  }
8936
9491
  function renderProgressLine(p) {
8937
9492
  const msg = p.message ? ` ${p.message}` : "";
@@ -8987,8 +9542,8 @@ function summarizeToolArgs(name, args) {
8987
9542
  }
8988
9543
 
8989
9544
  // src/cli/ui/PlanCheckpointConfirm.tsx
8990
- import { Box as Box10, Text as Text10 } from "ink";
8991
- import React11 from "react";
9545
+ import { Box as Box11 } from "ink";
9546
+ import React13 from "react";
8992
9547
  function PlanCheckpointConfirmInner({
8993
9548
  stepId,
8994
9549
  title,
@@ -8999,10 +9554,11 @@ function PlanCheckpointConfirmInner({
8999
9554
  onChoose
9000
9555
  }) {
9001
9556
  const label = title ? `${stepId} \xB7 ${title}` : stepId;
9002
- const counter = total > 0 ? ` (${completed}/${total})` : "";
9557
+ const counter = total > 0 ? `${completed}/${total}` : "";
9003
9558
  const isLast = total > 0 && completed >= total;
9004
9559
  const statuses = buildStatusMap(steps, completedStepIds, stepId, isLast);
9005
- 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(
9560
+ const subtitle = counter ? `${counter} \xB7 ${label}` : label;
9561
+ return /* @__PURE__ */ React13.createElement(ModalCard, { accent: "#86efac", icon: "\u2713", title: "checkpoint \u2014 step done", subtitle }, steps && steps.length > 0 ? /* @__PURE__ */ React13.createElement(Box11, { marginBottom: 1, flexDirection: "column" }, /* @__PURE__ */ React13.createElement(PlanStepList, { steps, statuses, focusStepId: stepId })) : null, /* @__PURE__ */ React13.createElement(
9006
9562
  SingleSelect,
9007
9563
  {
9008
9564
  initialValue: isLast ? "stop" : "continue",
@@ -9027,9 +9583,9 @@ function PlanCheckpointConfirmInner({
9027
9583
  onCancel: () => onChoose("stop"),
9028
9584
  footer: "[\u2191\u2193] navigate \xB7 [Enter] select \xB7 [Esc] stop"
9029
9585
  }
9030
- )));
9586
+ ));
9031
9587
  }
9032
- var PlanCheckpointConfirm = React11.memo(PlanCheckpointConfirmInner);
9588
+ var PlanCheckpointConfirm = React13.memo(PlanCheckpointConfirmInner);
9033
9589
  function buildStatusMap(steps, completedStepIds, currentStepId, isLast) {
9034
9590
  const map = /* @__PURE__ */ new Map();
9035
9591
  if (!steps) return map;
@@ -9046,70 +9602,103 @@ function buildStatusMap(steps, completedStepIds, currentStepId, isLast) {
9046
9602
  }
9047
9603
 
9048
9604
  // src/cli/ui/PlanConfirm.tsx
9049
- import { Box as Box11, Text as Text11 } from "ink";
9050
- import React12 from "react";
9605
+ import { Box as Box12, Text as Text10 } from "ink";
9606
+ import React14 from "react";
9051
9607
  function PlanConfirmInner({ plan: plan2, steps, summary, onChoose }) {
9052
9608
  const hasOpenQuestions = /^#{1,6}\s*(open[-\s]?questions?|risks?|unknowns?|assumptions?|unclear)/im.test(plan2) || /^#{1,6}\s*(待确认|开放问题|风险|未知|假设|不确定)/im.test(plan2);
9053
- 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(
9054
- SingleSelect,
9609
+ return /* @__PURE__ */ React14.createElement(
9610
+ ModalCard,
9055
9611
  {
9056
- initialValue: hasOpenQuestions ? "refine" : "approve",
9057
- items: [
9058
- {
9059
- value: "approve",
9060
- label: "Approve and implement",
9061
- hint: "Exit plan mode. The model starts executing. You'll get a text input to add any last instructions (or just press Enter to skip)."
9062
- },
9063
- {
9064
- value: "refine",
9065
- label: "Refine / answer questions",
9066
- hint: "Stay in plan mode. Write answers, modifications, or critiques; the model revises and re-submits."
9067
- },
9068
- {
9069
- value: "cancel",
9070
- label: "Cancel",
9071
- hint: "Exit plan mode. Drop the plan; the model won't implement it."
9072
- }
9073
- ],
9074
- onSubmit: (v) => onChoose(v),
9075
- onCancel: () => onChoose("cancel"),
9076
- footer: "[\u2191\u2193] navigate \xB7 [Enter] select \xB7 [Esc] cancel"
9077
- }
9078
- )));
9612
+ accent: "#67e8f9",
9613
+ icon: "\u{1F4CB}",
9614
+ title: "plan proposed",
9615
+ subtitle: summary ?? "approve / refine / cancel"
9616
+ },
9617
+ hasOpenQuestions ? /* @__PURE__ */ React14.createElement(Box12, { marginBottom: 1 }, /* @__PURE__ */ React14.createElement(Text10, { color: "#fbbf24" }, "\u25B2 the plan flags open questions or risks \u2014 pick", " ", /* @__PURE__ */ React14.createElement(Text10, { bold: true }, "Refine / answer questions"), " to write concrete answers before the model moves on.")) : null,
9618
+ steps && steps.length > 0 ? /* @__PURE__ */ React14.createElement(Box12, { marginBottom: 1, flexDirection: "column" }, /* @__PURE__ */ React14.createElement(PlanStepList, { steps })) : null,
9619
+ /* @__PURE__ */ React14.createElement(
9620
+ SingleSelect,
9621
+ {
9622
+ initialValue: hasOpenQuestions ? "refine" : "approve",
9623
+ items: [
9624
+ {
9625
+ value: "approve",
9626
+ label: "Approve and implement",
9627
+ hint: "Exit plan mode. The model starts executing. You'll get a text input to add any last instructions (or just press Enter to skip)."
9628
+ },
9629
+ {
9630
+ value: "refine",
9631
+ label: "Refine / answer questions",
9632
+ hint: "Stay in plan mode. Write answers, modifications, or critiques; the model revises and re-submits."
9633
+ },
9634
+ {
9635
+ value: "cancel",
9636
+ label: "Cancel",
9637
+ hint: "Exit plan mode. Drop the plan; the model won't implement it."
9638
+ }
9639
+ ],
9640
+ onSubmit: (v) => onChoose(v),
9641
+ onCancel: () => onChoose("cancel"),
9642
+ footer: "[\u2191\u2193] navigate \xB7 [Enter] select \xB7 [Esc] cancel"
9643
+ }
9644
+ )
9645
+ );
9079
9646
  }
9080
- var PlanConfirm = React12.memo(PlanConfirmInner);
9647
+ var PlanConfirm = React14.memo(PlanConfirmInner);
9081
9648
 
9082
9649
  // src/cli/ui/PlanRefineInput.tsx
9083
- import { Box as Box12, Text as Text12, useInput as useInput3 } from "ink";
9084
- import React13, { useState as useState4 } from "react";
9650
+ import { Box as Box13, Text as Text11 } from "ink";
9651
+ import React15, { useState as useState4 } from "react";
9085
9652
  function PlanRefineInput({ mode: mode2, onSubmit, onCancel }) {
9086
9653
  const [value, setValue] = useState4("");
9087
- useInput3((input, key) => {
9088
- if (key.escape) {
9654
+ useKeystroke((ev) => {
9655
+ if (ev.paste) {
9656
+ setValue((v) => v + ev.input.replace(/\r?\n/g, " "));
9657
+ return;
9658
+ }
9659
+ if (ev.escape) {
9089
9660
  onCancel();
9090
9661
  return;
9091
9662
  }
9092
- if (key.return) {
9663
+ if (ev.return) {
9093
9664
  onSubmit(value.trim());
9094
9665
  return;
9095
9666
  }
9096
- if (key.backspace || key.delete) {
9667
+ if (ev.backspace || ev.delete) {
9097
9668
  setValue((v) => v.slice(0, -1));
9098
9669
  return;
9099
9670
  }
9100
- if (input && !key.ctrl && !key.meta) {
9101
- setValue((v) => v + input);
9671
+ if (ev.input && !ev.ctrl && !ev.meta) {
9672
+ setValue((v) => v + ev.input);
9102
9673
  }
9103
9674
  });
9104
- 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?";
9105
- 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.";
9675
+ const tick = useTick();
9676
+ const cursorOn = Math.floor(tick / 4) % 2 === 0;
9677
+ const meta = mode2 === "approve" ? {
9678
+ title: "approving \u2014 any last instructions?",
9679
+ icon: "\u{1F4CB}",
9680
+ accent: "#67e8f9"
9681
+ } : mode2 === "checkpoint-revise" ? {
9682
+ title: "revising \u2014 what should change before the next step?",
9683
+ icon: "\u270F",
9684
+ accent: "#fbbf24"
9685
+ } : mode2 === "choice-custom" ? {
9686
+ title: "custom answer \u2014 type whatever fits",
9687
+ icon: "\u{1F500}",
9688
+ accent: "#f0abfc"
9689
+ } : {
9690
+ title: "refining \u2014 what should the model change?",
9691
+ icon: "\u270F",
9692
+ accent: "#fbbf24"
9693
+ };
9694
+ 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 adjusts the remaining plan." : 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.";
9106
9695
  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.)";
9107
- 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"))));
9696
+ return /* @__PURE__ */ React15.createElement(ModalCard, { accent: meta.accent, icon: meta.icon, title: meta.title }, /* @__PURE__ */ React15.createElement(Box13, { marginBottom: 1 }, /* @__PURE__ */ React15.createElement(Text11, { dimColor: true }, hint, " Enter to send \xB7 Esc to return to the picker.", value === "" ? blankHint : "")), /* @__PURE__ */ React15.createElement(Box13, null, /* @__PURE__ */ React15.createElement(Text11, { color: meta.accent, bold: true }, "\u203A "), /* @__PURE__ */ React15.createElement(Text11, null, value), /* @__PURE__ */ React15.createElement(Text11, { color: meta.accent, bold: true }, cursorOn ? "\u258D" : " ")));
9108
9697
  }
9109
9698
 
9110
9699
  // src/cli/ui/PlanReviseConfirm.tsx
9111
- import { Box as Box13, Text as Text13 } from "ink";
9112
- import React14 from "react";
9700
+ import { Box as Box14, Text as Text12 } from "ink";
9701
+ import React16 from "react";
9113
9702
  function computeDiff(oldSteps, newSteps) {
9114
9703
  const oldIds = new Set(oldSteps.map((s) => s.id));
9115
9704
  const newIds = new Set(newSteps.map((s) => s.id));
@@ -9125,13 +9714,13 @@ function computeDiff(oldSteps, newSteps) {
9125
9714
  function riskDots2(risk) {
9126
9715
  switch (risk) {
9127
9716
  case "high":
9128
- return { dots: "\u25CF\u25CF\u25CF", color: "red" };
9717
+ return { dots: "\u25CF\u25CF\u25CF", color: "#f87171" };
9129
9718
  case "med":
9130
- return { dots: "\u25CF\u25CF ", color: "yellow" };
9719
+ return { dots: "\u25CF\u25CF ", color: "#fbbf24" };
9131
9720
  case "low":
9132
- return { dots: "\u25CF ", color: "green" };
9721
+ return { dots: "\u25CF ", color: "#4ade80" };
9133
9722
  default:
9134
- return { dots: " ", color: "gray" };
9723
+ return { dots: " ", color: "#94a3b8" };
9135
9724
  }
9136
9725
  }
9137
9726
  function PlanReviseConfirmInner({
@@ -9145,54 +9734,103 @@ function PlanReviseConfirmInner({
9145
9734
  const removedCount = rows.filter((r) => r.kind === "removed").length;
9146
9735
  const addedCount = rows.filter((r) => r.kind === "added").length;
9147
9736
  const keptCount = rows.filter((r) => r.kind === "kept").length;
9148
- 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) => {
9149
- const risk = riskDots2(row2.step.risk);
9150
- const prefix = row2.kind === "removed" ? "\u2212" : row2.kind === "added" ? "+" : " ";
9151
- const prefixColor = row2.kind === "removed" ? "red" : row2.kind === "added" ? "green" : "gray";
9152
- const dim = row2.kind === "kept";
9153
- const strike = row2.kind === "removed";
9154
- 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}`));
9155
- })), /* @__PURE__ */ React14.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React14.createElement(
9156
- SingleSelect,
9737
+ return /* @__PURE__ */ React16.createElement(
9738
+ ModalCard,
9157
9739
  {
9158
- initialValue: "accept",
9159
- items: [
9160
- {
9161
- value: "accept",
9162
- label: "Accept revision \u2014 apply the new step list",
9163
- hint: "Replaces the remaining plan with the proposed steps. Done steps are untouched."
9164
- },
9165
- {
9166
- value: "reject",
9167
- label: "Reject \u2014 keep the original plan",
9168
- hint: "Drops the proposal. Model continues with the original remaining steps."
9169
- }
9170
- ],
9171
- onSubmit: (v) => onChoose(v),
9172
- onCancel: () => onChoose("reject"),
9173
- footer: "[\u2191\u2193] navigate \xB7 [Enter] select \xB7 [Esc] reject"
9174
- }
9175
- )));
9740
+ accent: "#fbbf24",
9741
+ icon: "\u270F",
9742
+ title: "plan revision proposed",
9743
+ subtitle: `\u2212${removedCount} +${addedCount} \xB7 ${keptCount} kept`
9744
+ },
9745
+ /* @__PURE__ */ React16.createElement(Box14, { marginBottom: 1 }, /* @__PURE__ */ React16.createElement(Text12, null, reason)),
9746
+ summary ? /* @__PURE__ */ React16.createElement(Box14, { marginBottom: 1 }, /* @__PURE__ */ React16.createElement(Text12, { dimColor: true }, `updated summary: ${summary}`)) : null,
9747
+ /* @__PURE__ */ React16.createElement(Box14, { marginBottom: 1, flexDirection: "column" }, rows.map((row2) => {
9748
+ const risk = riskDots2(row2.step.risk);
9749
+ const prefix = row2.kind === "removed" ? "\u2212" : row2.kind === "added" ? "+" : " ";
9750
+ const prefixColor = row2.kind === "removed" ? "#f87171" : row2.kind === "added" ? "#4ade80" : "#94a3b8";
9751
+ const dim = row2.kind === "kept";
9752
+ const strike = row2.kind === "removed";
9753
+ return /* @__PURE__ */ React16.createElement(Box14, { key: `${row2.kind}-${row2.step.id}` }, /* @__PURE__ */ React16.createElement(Text12, { color: prefixColor, bold: true }, `${prefix} `), /* @__PURE__ */ React16.createElement(Text12, { color: risk.color, bold: true, dimColor: dim }, risk.dots), /* @__PURE__ */ React16.createElement(Text12, { dimColor: dim, strikethrough: strike }, ` ${row2.step.id} \xB7 ${row2.step.title}`));
9754
+ })),
9755
+ /* @__PURE__ */ React16.createElement(
9756
+ SingleSelect,
9757
+ {
9758
+ initialValue: "accept",
9759
+ items: [
9760
+ {
9761
+ value: "accept",
9762
+ label: "Accept revision \u2014 apply the new step list",
9763
+ hint: "Replaces the remaining plan with the proposed steps. Done steps are untouched."
9764
+ },
9765
+ {
9766
+ value: "reject",
9767
+ label: "Reject \u2014 keep the original plan",
9768
+ hint: "Drops the proposal. Model continues with the original remaining steps."
9769
+ }
9770
+ ],
9771
+ onSubmit: (v) => onChoose(v),
9772
+ onCancel: () => onChoose("reject"),
9773
+ footer: "[\u2191\u2193] navigate \xB7 [Enter] select \xB7 [Esc] reject"
9774
+ }
9775
+ )
9776
+ );
9176
9777
  }
9177
- var PlanReviseConfirm = React14.memo(PlanReviseConfirmInner);
9778
+ var PlanReviseConfirm = React16.memo(PlanReviseConfirmInner);
9178
9779
 
9179
9780
  // src/cli/ui/PromptInput.tsx
9180
- import { Box as Box14, Text as Text14, useInput as useInput4, useStdout as useStdout3 } from "ink";
9181
- import React15, { useRef, useState as useState5 } from "react";
9781
+ import { Box as Box15, Text as Text13, useStdout as useStdout5 } from "ink";
9782
+ import React17, { useRef as useRef2, useState as useState5 } from "react";
9783
+
9784
+ // src/cli/ui/key-normalize.ts
9785
+ var CSI_TAIL_TO_FLAGS = [
9786
+ // Arrow keys — the most common ConPTY victim.
9787
+ { tail: "[A", flags: { upArrow: true } },
9788
+ { tail: "[B", flags: { downArrow: true } },
9789
+ { tail: "[C", flags: { rightArrow: true } },
9790
+ { tail: "[D", flags: { leftArrow: true } },
9791
+ // Page navigation.
9792
+ { tail: "[5~", flags: { pageUp: true } },
9793
+ { tail: "[6~", flags: { pageDown: true } },
9794
+ // Forward-delete (the key labelled Delete on most keyboards).
9795
+ { tail: "[3~", flags: { delete: true } },
9796
+ // Shift+Tab — terminal sends `\x1b[Z` rather than tab-with-shift.
9797
+ { tail: "[Z", flags: { shift: true, tab: true } }
9798
+ ];
9799
+ function alreadyStructured(flags) {
9800
+ return Boolean(
9801
+ flags.upArrow || flags.downArrow || flags.leftArrow || flags.rightArrow || flags.pageUp || flags.pageDown || flags.delete || flags.tab && flags.shift
9802
+ );
9803
+ }
9804
+ function recoverCsiTail(input, existing = {}) {
9805
+ if (alreadyStructured(existing)) return null;
9806
+ for (const entry of CSI_TAIL_TO_FLAGS) {
9807
+ if (input === entry.tail || input === `\x1B${entry.tail}`) {
9808
+ return entry.flags;
9809
+ }
9810
+ }
9811
+ return null;
9812
+ }
9813
+ var STRIPPABLE_CSI_FRAGMENTS = [
9814
+ "\x1B[200~",
9815
+ "\x1B[201~",
9816
+ "[200~",
9817
+ "[201~",
9818
+ ...CSI_TAIL_TO_FLAGS.flatMap((e) => [`\x1B${e.tail}`, e.tail])
9819
+ ];
9820
+ function stripCsiFragments(input) {
9821
+ let out = input;
9822
+ for (const frag of STRIPPABLE_CSI_FRAGMENTS) {
9823
+ if (out.includes(frag)) out = out.replaceAll(frag, "");
9824
+ }
9825
+ return out;
9826
+ }
9182
9827
 
9183
9828
  // src/cli/ui/multiline-keys.ts
9184
9829
  var BACKSLASH_SUFFIX = /\\$/;
9185
9830
  var NOOP = { next: null, cursor: null, submit: false };
9186
- function rewriteRawArrowEscape(key) {
9187
- if (key.upArrow || key.downArrow || key.leftArrow || key.rightArrow) return key;
9188
- if (key.input === "\x1B[A") return { ...key, upArrow: true, input: "" };
9189
- if (key.input === "\x1B[B") return { ...key, downArrow: true, input: "" };
9190
- if (key.input === "\x1B[C") return { ...key, rightArrow: true, input: "" };
9191
- if (key.input === "\x1B[D") return { ...key, leftArrow: true, input: "" };
9192
- return key;
9193
- }
9194
9831
  function processMultilineKey(value, cursor, keyIn) {
9195
- const key = rewriteRawArrowEscape(keyIn);
9832
+ const recovered = recoverCsiTail(keyIn.input, keyIn);
9833
+ const key = recovered ? { ...keyIn, ...recovered, input: "" } : keyIn;
9196
9834
  if (key.tab || key.escape) {
9197
9835
  return NOOP;
9198
9836
  }
@@ -9239,7 +9877,7 @@ function processMultilineKey(value, cursor, keyIn) {
9239
9877
  submit: false
9240
9878
  };
9241
9879
  }
9242
- const stripped = key.input.replaceAll("\x1B[200~", "").replaceAll("\x1B[201~", "");
9880
+ const stripped = stripCsiFragments(key.input);
9243
9881
  const looksLikePaste = stripped.length > 1 && (stripped.includes("\n") || stripped.includes("\r"));
9244
9882
  if (looksLikePaste) {
9245
9883
  const normalized = stripped.replace(/\r\n?/g, "\n");
@@ -9383,10 +10021,159 @@ function formatBytesShort(n) {
9383
10021
  return `${(n / (1024 * 1024)).toFixed(1)}MB`;
9384
10022
  }
9385
10023
 
10024
+ // src/cli/ui/prompt-viewport.ts
10025
+ function charCells(ch) {
10026
+ if (ch.length === 0) return 0;
10027
+ const code = ch.charCodeAt(0);
10028
+ if (code < 32 || code === 127) return 0;
10029
+ if (code < 4352) return 1;
10030
+ if (code >= 4352 && code <= 4447) return 2;
10031
+ if (code >= 11904 && code <= 12350) return 2;
10032
+ if (code >= 12353 && code <= 13311) return 2;
10033
+ if (code >= 13312 && code <= 19903) return 2;
10034
+ if (code >= 19968 && code <= 40959) return 2;
10035
+ if (code >= 40960 && code <= 42191) return 2;
10036
+ if (code >= 44032 && code <= 55203) return 2;
10037
+ if (code >= 63744 && code <= 64255) return 2;
10038
+ if (code >= 65072 && code <= 65103) return 2;
10039
+ if (code >= 65280 && code <= 65376) return 2;
10040
+ if (code >= 65504 && code <= 65510) return 2;
10041
+ return 1;
10042
+ }
10043
+ function stringCells(s, pastes) {
10044
+ let n = 0;
10045
+ for (let i = 0; i < s.length; i++) {
10046
+ const ch = s[i];
10047
+ const id = decodePasteSentinel(ch);
10048
+ if (id !== null) {
10049
+ n += pasteSentinelCells(id, pastes);
10050
+ } else {
10051
+ n += charCells(ch);
10052
+ }
10053
+ }
10054
+ return n;
10055
+ }
10056
+ function pasteSentinelLabel(id, entry) {
10057
+ if (!entry) return `[paste #${id + 1} \xB7 (missing)]`;
10058
+ return `[paste #${id + 1} \xB7 ${entry.lineCount}l \xB7 ${formatBytesShort(entry.charCount)}]`;
10059
+ }
10060
+ function pasteSentinelCells(id, pastes) {
10061
+ const entry = pastes?.get(id);
10062
+ return pasteSentinelLabel(id, entry).length;
10063
+ }
10064
+ function buildViewport(line, cursorCol, visibleCells, pastes) {
10065
+ if (visibleCells <= 0) {
10066
+ return {
10067
+ segments: [],
10068
+ cursorCell: cursorCol === null ? null : 0,
10069
+ hiddenLeft: false,
10070
+ hiddenRight: line.length > 0
10071
+ };
10072
+ }
10073
+ const totalCells = stringCells(line, pastes);
10074
+ if (totalCells <= visibleCells) {
10075
+ const segments = textToSegments(line, pastes);
10076
+ let cursorCell = null;
10077
+ if (cursorCol !== null) {
10078
+ cursorCell = stringCells(line.slice(0, cursorCol), pastes);
10079
+ }
10080
+ return { segments, cursorCell, hiddenLeft: false, hiddenRight: false };
10081
+ }
10082
+ if (cursorCol === null) {
10083
+ return clipFromLeft(line, visibleCells, pastes);
10084
+ }
10085
+ return clipAroundCursor(line, cursorCol, visibleCells, pastes);
10086
+ }
10087
+ function clipFromLeft(line, visibleCells, pastes) {
10088
+ const budget = Math.max(1, visibleCells - 1);
10089
+ let used = 0;
10090
+ let end = 0;
10091
+ while (end < line.length) {
10092
+ const ch = line[end];
10093
+ const cw = charCellsAt(line, end, pastes);
10094
+ if (used + cw > budget) break;
10095
+ used += cw;
10096
+ end++;
10097
+ }
10098
+ const segments = textToSegments(line.slice(0, end), pastes);
10099
+ return { segments, cursorCell: null, hiddenLeft: false, hiddenRight: end < line.length };
10100
+ }
10101
+ function clipAroundCursor(line, cursorCol, visibleCells, pastes) {
10102
+ let budget = visibleCells;
10103
+ const reservedForMarkers = 2;
10104
+ budget = Math.max(1, budget - reservedForMarkers);
10105
+ const halfBudget = Math.floor(budget / 2);
10106
+ let start = cursorCol;
10107
+ let leftCells = 0;
10108
+ while (start > 0 && leftCells < halfBudget) {
10109
+ const cw = charCellsAt(line, start - 1, pastes);
10110
+ if (leftCells + cw > halfBudget) break;
10111
+ start--;
10112
+ leftCells += cw;
10113
+ }
10114
+ const rightBudget = budget - leftCells;
10115
+ let end = cursorCol;
10116
+ let rightCells = 0;
10117
+ const cursorChar = cursorCol < line.length ? charCellsAt(line, cursorCol, pastes) : 1;
10118
+ if (rightBudget >= cursorChar) {
10119
+ if (cursorCol < line.length) end = cursorCol + 1;
10120
+ rightCells = cursorChar;
10121
+ while (end < line.length && rightCells < rightBudget) {
10122
+ const cw = charCellsAt(line, end, pastes);
10123
+ if (rightCells + cw > rightBudget) break;
10124
+ rightCells += cw;
10125
+ end++;
10126
+ }
10127
+ }
10128
+ let extraLeftBudget = rightBudget - rightCells;
10129
+ while (start > 0 && extraLeftBudget > 0) {
10130
+ const cw = charCellsAt(line, start - 1, pastes);
10131
+ if (cw > extraLeftBudget) break;
10132
+ start--;
10133
+ leftCells += cw;
10134
+ extraLeftBudget -= cw;
10135
+ }
10136
+ const hiddenLeft = start > 0;
10137
+ const hiddenRight = end < line.length;
10138
+ const segments = textToSegments(line.slice(start, end), pastes);
10139
+ const cursorCell = stringCells(line.slice(start, cursorCol), pastes);
10140
+ return { segments, cursorCell, hiddenLeft, hiddenRight };
10141
+ }
10142
+ function charCellsAt(line, idx, pastes) {
10143
+ const ch = line[idx];
10144
+ const id = decodePasteSentinel(ch);
10145
+ if (id !== null) {
10146
+ const entry = pastes?.get(id);
10147
+ return pasteSentinelLabel(id, entry).length;
10148
+ }
10149
+ return charCells(ch);
10150
+ }
10151
+ function textToSegments(line, pastes) {
10152
+ const out = [];
10153
+ let buf = "";
10154
+ const flushBuf = () => {
10155
+ if (buf.length > 0) {
10156
+ out.push({ kind: "text", text: buf });
10157
+ buf = "";
10158
+ }
10159
+ };
10160
+ for (let i = 0; i < line.length; i++) {
10161
+ const ch = line[i];
10162
+ const id = decodePasteSentinel(ch);
10163
+ if (id !== null) {
10164
+ flushBuf();
10165
+ const label = pasteSentinelLabel(id, pastes?.get(id));
10166
+ out.push({ kind: "paste", id, label });
10167
+ } else {
10168
+ buf += ch;
10169
+ }
10170
+ }
10171
+ flushBuf();
10172
+ return out;
10173
+ }
10174
+
9386
10175
  // src/cli/ui/PromptInput.tsx
9387
- var PASTE_START_MARKER = "\x1B[200~";
9388
- var PASTE_END_MARKER = "\x1B[201~";
9389
- var PASTE_MERGE_WINDOW_MS = 30;
10176
+ var BAR = "\u258E ";
9390
10177
  function PromptInput({
9391
10178
  value,
9392
10179
  onChange,
@@ -9397,157 +10184,259 @@ function PromptInput({
9397
10184
  onHistoryNext
9398
10185
  }) {
9399
10186
  const [cursor, setCursor] = useState5(value.length);
9400
- const pastesRef = useRef(/* @__PURE__ */ new Map());
9401
- const nextPasteIdRef = useRef(0);
9402
- const pasteAccumRef = useRef(null);
9403
- const lastPasteRef = useRef(null);
9404
- const lastLocalValueRef = useRef(value);
10187
+ const pastesRef = useRef2(/* @__PURE__ */ new Map());
10188
+ const nextPasteIdRef = useRef2(0);
10189
+ const lastLocalValueRef = useRef2(value);
9405
10190
  if (value !== lastLocalValueRef.current) {
9406
10191
  lastLocalValueRef.current = value;
9407
- if (cursor !== value.length) {
9408
- setCursor(value.length);
9409
- }
10192
+ if (cursor !== value.length) setCursor(value.length);
9410
10193
  }
9411
- const cursorRef = useRef(cursor);
9412
- cursorRef.current = cursor;
9413
- const tick = useTick();
9414
- const showCursor = disabled ? false : Math.floor(tick / 4) % 2 === 0;
9415
10194
  const registerPaste = (content) => {
9416
10195
  const v = lastLocalValueRef.current;
9417
- const c = cursorRef.current;
9418
- const now = Date.now();
9419
- const last = lastPasteRef.current;
9420
- const prevChar = c > 0 ? v[c - 1] : null;
9421
- const prevId = prevChar ? decodePasteSentinel(prevChar) : null;
9422
- const canMerge = last !== null && prevId === last.id && now - last.at < PASTE_MERGE_WINDOW_MS && pastesRef.current.has(last.id);
9423
- if (canMerge && last) {
9424
- const existing = pastesRef.current.get(last.id);
9425
- if (existing) {
9426
- const merged = existing.content + content;
9427
- pastesRef.current.set(last.id, makePasteEntry(last.id, merged));
9428
- lastPasteRef.current = { id: last.id, at: now };
9429
- return;
9430
- }
9431
- }
10196
+ const c = cursor;
9432
10197
  const id = nextPasteIdRef.current % PASTE_SENTINEL_RANGE;
9433
10198
  nextPasteIdRef.current = id + 1;
9434
10199
  pastesRef.current.set(id, makePasteEntry(id, content));
9435
10200
  const sentinel = encodePasteSentinel(id);
9436
10201
  const next = v.slice(0, c) + sentinel + v.slice(c);
9437
10202
  lastLocalValueRef.current = next;
9438
- cursorRef.current = c + 1;
9439
10203
  onChange(next);
9440
10204
  setCursor(c + 1);
9441
- lastPasteRef.current = { id, at: now };
9442
10205
  };
9443
- useInput4(
9444
- (input, key) => {
9445
- if (pasteAccumRef.current !== null) {
9446
- const endIdx = input.indexOf(PASTE_END_MARKER);
9447
- if (endIdx === -1) {
9448
- pasteAccumRef.current += input;
9449
- return;
9450
- }
9451
- const content = pasteAccumRef.current + input.slice(0, endIdx);
9452
- pasteAccumRef.current = null;
9453
- registerPaste(content);
9454
- return;
9455
- }
9456
- const startIdx = input.indexOf(PASTE_START_MARKER);
9457
- if (startIdx !== -1) {
9458
- const afterStart = input.slice(startIdx + PASTE_START_MARKER.length);
9459
- const endIdx = afterStart.indexOf(PASTE_END_MARKER);
9460
- if (endIdx !== -1) {
9461
- registerPaste(afterStart.slice(0, endIdx));
9462
- } else {
9463
- pasteAccumRef.current = afterStart;
9464
- }
9465
- return;
9466
- }
9467
- const ke = {
9468
- input,
9469
- return: key.return,
9470
- shift: key.shift,
9471
- ctrl: key.ctrl,
9472
- meta: key.meta,
9473
- backspace: key.backspace,
9474
- delete: key.delete,
9475
- tab: key.tab,
9476
- upArrow: key.upArrow,
9477
- downArrow: key.downArrow,
9478
- leftArrow: key.leftArrow,
9479
- rightArrow: key.rightArrow,
9480
- escape: key.escape,
9481
- pageUp: key.pageUp,
9482
- pageDown: key.pageDown
9483
- };
9484
- const action = processMultilineKey(value, cursor, ke);
9485
- if (action.pasteRequest) {
9486
- registerPaste(action.pasteRequest.content);
9487
- return;
9488
- }
9489
- if (action.next !== null) {
9490
- lastLocalValueRef.current = action.next;
9491
- onChange(action.next);
9492
- }
9493
- if (action.cursor !== null) {
9494
- setCursor(action.cursor);
9495
- }
9496
- if (action.submit) {
9497
- const raw = action.submitValue ?? value;
9498
- const expanded = expandPasteSentinels(raw, pastesRef.current);
9499
- const reachable = new Set(listPasteIdsInBuffer(raw));
9500
- for (const id of pastesRef.current.keys()) {
9501
- if (!reachable.has(id)) pastesRef.current.delete(id);
9502
- }
9503
- onSubmit(expanded);
10206
+ useKeystroke((ev) => {
10207
+ if (disabled) return;
10208
+ if (ev.paste) {
10209
+ if (ev.input.length > 0) registerPaste(ev.input);
10210
+ return;
10211
+ }
10212
+ const key = {
10213
+ input: ev.input,
10214
+ return: ev.return,
10215
+ shift: ev.shift,
10216
+ ctrl: ev.ctrl,
10217
+ meta: ev.meta,
10218
+ backspace: ev.backspace,
10219
+ delete: ev.delete,
10220
+ tab: ev.tab,
10221
+ upArrow: ev.upArrow,
10222
+ downArrow: ev.downArrow,
10223
+ leftArrow: ev.leftArrow,
10224
+ rightArrow: ev.rightArrow,
10225
+ escape: ev.escape,
10226
+ pageUp: ev.pageUp,
10227
+ pageDown: ev.pageDown
10228
+ };
10229
+ const action = processMultilineKey(value, cursor, key);
10230
+ if (action.pasteRequest) {
10231
+ registerPaste(action.pasteRequest.content);
10232
+ return;
10233
+ }
10234
+ if (action.next !== null) {
10235
+ lastLocalValueRef.current = action.next;
10236
+ onChange(action.next);
10237
+ }
10238
+ if (action.cursor !== null) {
10239
+ setCursor(action.cursor);
10240
+ }
10241
+ if (action.submit) {
10242
+ const raw = action.submitValue ?? value;
10243
+ const expanded = expandPasteSentinels(raw, pastesRef.current);
10244
+ const reachable = new Set(listPasteIdsInBuffer(raw));
10245
+ for (const id of pastesRef.current.keys()) {
10246
+ if (!reachable.has(id)) pastesRef.current.delete(id);
9504
10247
  }
9505
- if (action.historyHandoff === "prev") onHistoryPrev?.();
9506
- if (action.historyHandoff === "next") onHistoryNext?.();
9507
- },
9508
- { isActive: !disabled }
9509
- );
9510
- const { stdout: stdout2 } = useStdout3();
10248
+ onSubmit(expanded);
10249
+ }
10250
+ if (action.historyHandoff === "prev") onHistoryPrev?.();
10251
+ if (action.historyHandoff === "next") onHistoryNext?.();
10252
+ }, !disabled);
10253
+ const { stdout: stdout2 } = useStdout5();
9511
10254
  const cols = stdout2?.columns ?? 80;
9512
10255
  const narrow = cols <= 90;
9513
- const promptPrefix = narrow ? "\u203A " : "you \u203A ";
9514
- const continuationIndent = narrow ? " " : " ";
9515
- const placeholderActive = narrow ? "type a message, or /command" : "type a message, or /command \xB7 [Shift+Enter] / [Ctrl+J] newline";
10256
+ const promptBody = narrow ? "\u203A " : "you \u203A ";
10257
+ const promptPrefix = BAR + promptBody;
10258
+ const continuationIndent = BAR + " ".repeat(promptBody.length);
10259
+ const prefixCells = promptPrefix.length;
10260
+ const visibleCells = Math.max(8, cols - prefixCells - 3);
10261
+ const placeholderActive = narrow ? "type a message, or /command" : "type a message, or /command \xB7 [Ctrl+J] newline (Shift+Enter where supported)";
9516
10262
  const effectivePlaceholder = disabled ? placeholder ?? "\u2026waiting for response\u2026" : placeholder ?? placeholderActive;
9517
10263
  const lines = value.length > 0 ? value.split("\n") : [""];
9518
- const borderColor = disabled ? "gray" : "cyan";
10264
+ const accentColor = disabled ? "gray" : "cyan";
10265
+ const animate = !disabled && cols >= 100;
10266
+ const tick = useTick();
10267
+ const barOffset = animate ? Math.floor(tick / 6) : 0;
10268
+ const barColorAt = (rowIdx) => disabled ? "gray" : GRADIENT[((rowIdx + barOffset) % GRADIENT.length + GRADIENT.length) % GRADIENT.length];
10269
+ const cursorVisible = animate ? Math.floor(tick / 4) % 2 === 0 : true;
9519
10270
  const { line: cursorLine, col: cursorCol } = lineAndColumn(value, cursor);
9520
10271
  const renderItems = collapseLinesForDisplay(lines, cursorLine);
9521
10272
  const showHugeBufferHints = lines.length > 20;
9522
- return /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement(Box14, { borderStyle: "round", borderColor, paddingX: 1, flexDirection: "column" }, renderItems.map((item, renderIdx) => {
10273
+ return /* @__PURE__ */ React17.createElement(Box15, { flexDirection: "column", paddingX: 1 }, renderItems.map((item, renderIdx) => {
9523
10274
  if (item.kind === "skip") {
9524
10275
  return (
9525
- // biome-ignore lint/suspicious/noArrayIndexKey: stable — skip markers are derived from a fixed-size window over `lines`
9526
- /* @__PURE__ */ React15.createElement(Box14, { key: `skip-${renderIdx}` }, /* @__PURE__ */ React15.createElement(Text14, { dimColor: true }, continuationIndent), /* @__PURE__ */ React15.createElement(
9527
- Text14,
9528
- {
9529
- dimColor: true
9530
- },
9531
- `[\u2026 ${item.linesHidden} line${item.linesHidden === 1 ? "" : "s"} hidden \u2014 full content kept, submitted on Enter \u2026]`
9532
- ))
10276
+ // biome-ignore lint/suspicious/noArrayIndexKey: stable — collapse markers derive from a fixed sliding window
10277
+ /* @__PURE__ */ React17.createElement(Box15, { key: `skip-${renderIdx}` }, /* @__PURE__ */ React17.createElement(Text13, { color: barColorAt(renderIdx) }, BAR), /* @__PURE__ */ React17.createElement(Text13, { dimColor: true }, continuationIndent.slice(BAR.length)), /* @__PURE__ */ React17.createElement(Text13, { dimColor: true }, `[\u2026 ${item.linesHidden} line${item.linesHidden === 1 ? "" : "s"} hidden \u2014 full content kept, submitted on Enter \u2026]`))
9533
10278
  );
9534
10279
  }
9535
- const line = item.line;
9536
10280
  const i = item.originalIndex;
10281
+ const line = item.line;
9537
10282
  const isFirst = i === 0;
9538
- const showPlaceholder = isFirst && value.length === 0;
9539
10283
  const isCursorLine = i === cursorLine;
9540
- return /* @__PURE__ */ React15.createElement(Box14, { key: `ln-${i}` }, isFirst ? /* @__PURE__ */ React15.createElement(Text14, { bold: true, color: borderColor }, promptPrefix) : /* @__PURE__ */ React15.createElement(Text14, { dimColor: true }, continuationIndent), 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(
9541
- LineWithCursor,
10284
+ const showPlaceholder = isFirst && value.length === 0;
10285
+ return /* @__PURE__ */ React17.createElement(
10286
+ PromptLine,
9542
10287
  {
10288
+ key: `ln-${i}`,
9543
10289
  line,
9544
- col: cursorCol,
9545
- showCursor,
9546
- borderColor,
9547
- pastes: pastesRef.current
10290
+ isFirst,
10291
+ isCursorLine: isCursorLine && !disabled,
10292
+ cursorCol: isCursorLine ? cursorCol : null,
10293
+ cursorVisible,
10294
+ showPlaceholder,
10295
+ placeholderText: effectivePlaceholder,
10296
+ promptPrefix,
10297
+ continuationIndent,
10298
+ visibleCells,
10299
+ accentColor,
10300
+ barColor: barColorAt(i),
10301
+ pastes: pastesRef.current,
10302
+ disabled: disabled === true
9548
10303
  }
9549
- ) : /* @__PURE__ */ React15.createElement(RenderLine, { line, pastes: pastesRef.current }));
9550
- }), showHugeBufferHints && !disabled ? /* @__PURE__ */ React15.createElement(Box14, null, /* @__PURE__ */ React15.createElement(Text14, { dimColor: true }, continuationIndent), /* @__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), disabled ? /* @__PURE__ */ React15.createElement(Box14, { paddingX: 1 }, /* @__PURE__ */ React15.createElement(Text14, { dimColor: true }, "[Esc] to stop")) : null);
10304
+ );
10305
+ }), showHugeBufferHints && !disabled ? /* @__PURE__ */ React17.createElement(Box15, null, /* @__PURE__ */ React17.createElement(Text13, { color: barColorAt(0) }, BAR), /* @__PURE__ */ React17.createElement(Text13, { dimColor: true }, continuationIndent.slice(BAR.length)), /* @__PURE__ */ React17.createElement(Text13, { 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__ */ React17.createElement(Box15, null, /* @__PURE__ */ React17.createElement(Text13, { color: barColorAt(0) }, BAR), /* @__PURE__ */ React17.createElement(Text13, { dimColor: true }, continuationIndent.slice(BAR.length)), /* @__PURE__ */ React17.createElement(Text13, { dimColor: true }, "[Ctrl+J] newline \xB7 [Enter] submit \xB7 ends with \\ for line continuation")) : null, disabled ? /* @__PURE__ */ React17.createElement(Box15, null, /* @__PURE__ */ React17.createElement(Text13, { color: barColorAt(0) }, BAR), /* @__PURE__ */ React17.createElement(Text13, { dimColor: true }, continuationIndent.slice(BAR.length)), /* @__PURE__ */ React17.createElement(Text13, { dimColor: true }, "[Esc] to stop")) : null);
10306
+ }
10307
+ function PromptLine({
10308
+ line,
10309
+ isFirst,
10310
+ isCursorLine,
10311
+ cursorCol,
10312
+ cursorVisible,
10313
+ showPlaceholder,
10314
+ placeholderText,
10315
+ promptPrefix,
10316
+ continuationIndent,
10317
+ visibleCells,
10318
+ accentColor,
10319
+ barColor,
10320
+ pastes,
10321
+ disabled
10322
+ }) {
10323
+ const barText = promptPrefix.slice(0, BAR.length);
10324
+ const bodyPrefix = promptPrefix.slice(BAR.length);
10325
+ const bodyContinuation = continuationIndent.slice(BAR.length);
10326
+ if (showPlaceholder) {
10327
+ return /* @__PURE__ */ React17.createElement(Box15, null, /* @__PURE__ */ React17.createElement(Text13, { color: barColor }, barText), /* @__PURE__ */ React17.createElement(Text13, { bold: true, color: accentColor }, bodyPrefix), !disabled ? /* @__PURE__ */ React17.createElement(Text13, { color: accentColor }, cursorVisible ? "\u258C" : " ") : null, /* @__PURE__ */ React17.createElement(Text13, { dimColor: true }, placeholderText));
10328
+ }
10329
+ const viewport = buildViewport(line, isCursorLine ? cursorCol : null, visibleCells, pastes);
10330
+ return /* @__PURE__ */ React17.createElement(Box15, null, /* @__PURE__ */ React17.createElement(Text13, { color: barColor }, barText), isFirst ? /* @__PURE__ */ React17.createElement(Text13, { bold: true, color: accentColor }, bodyPrefix) : /* @__PURE__ */ React17.createElement(Text13, { dimColor: true }, bodyContinuation), viewport.hiddenLeft ? /* @__PURE__ */ React17.createElement(Text13, { color: "gray", dimColor: true }, "\u2039") : null, /* @__PURE__ */ React17.createElement(
10331
+ ViewportContent,
10332
+ {
10333
+ segments: viewport.segments,
10334
+ cursorCell: isCursorLine ? viewport.cursorCell : null,
10335
+ accentColor,
10336
+ cursorVisible
10337
+ }
10338
+ ), viewport.hiddenRight ? /* @__PURE__ */ React17.createElement(Text13, { color: "gray", dimColor: true }, "\u203A") : null);
10339
+ }
10340
+ function ViewportContent({
10341
+ segments,
10342
+ cursorCell,
10343
+ accentColor,
10344
+ cursorVisible
10345
+ }) {
10346
+ if (cursorCell === null) {
10347
+ return /* @__PURE__ */ React17.createElement(React17.Fragment, null, segments.map((seg, i) => renderSegment(seg, i, false)));
10348
+ }
10349
+ const out = [];
10350
+ let cells = 0;
10351
+ let placed = false;
10352
+ for (let i = 0; i < segments.length; i++) {
10353
+ const seg = segments[i];
10354
+ const segCells = segmentCells(seg);
10355
+ if (placed) {
10356
+ out.push(renderSegment(seg, i, false));
10357
+ continue;
10358
+ }
10359
+ if (cursorCell >= cells + segCells) {
10360
+ out.push(renderSegment(seg, i, false));
10361
+ cells += segCells;
10362
+ continue;
10363
+ }
10364
+ if (seg.kind === "paste") {
10365
+ out.push(
10366
+ /* @__PURE__ */ React17.createElement(Text13, { key: `p-${i}-cursor`, color: "magenta", bold: true, inverse: cursorVisible }, seg.label)
10367
+ );
10368
+ placed = true;
10369
+ cells += segCells;
10370
+ continue;
10371
+ }
10372
+ const offsetIntoSeg = cursorCell - cells;
10373
+ const split = splitTextByCells(seg.text, offsetIntoSeg);
10374
+ if (split.before.length > 0) {
10375
+ out.push(/* @__PURE__ */ React17.createElement(Text13, { key: `t-${i}-b` }, split.before));
10376
+ }
10377
+ if (split.atCursor.length > 0) {
10378
+ out.push(
10379
+ /* @__PURE__ */ React17.createElement(Text13, { key: `t-${i}-c`, inverse: cursorVisible, color: accentColor }, split.atCursor)
10380
+ );
10381
+ } else {
10382
+ out.push(
10383
+ /* @__PURE__ */ React17.createElement(Text13, { key: `t-${i}-c-eol`, color: accentColor }, cursorVisible ? "\u258C" : " ")
10384
+ );
10385
+ }
10386
+ if (split.after.length > 0) {
10387
+ out.push(/* @__PURE__ */ React17.createElement(Text13, { key: `t-${i}-a` }, split.after));
10388
+ }
10389
+ placed = true;
10390
+ cells += segCells;
10391
+ }
10392
+ if (!placed) {
10393
+ out.push(
10394
+ /* @__PURE__ */ React17.createElement(Text13, { key: "cursor-eol", color: accentColor }, cursorVisible ? "\u258C" : " ")
10395
+ );
10396
+ }
10397
+ return /* @__PURE__ */ React17.createElement(React17.Fragment, null, out);
10398
+ }
10399
+ function segmentCells(seg) {
10400
+ if (seg.kind === "paste") return seg.label.length;
10401
+ return stringCells(seg.text);
10402
+ }
10403
+ function splitTextByCells(text, cellOffset) {
10404
+ let cells = 0;
10405
+ for (let i = 0; i < text.length; i++) {
10406
+ const ch = text[i];
10407
+ const cw = charCellsForText(ch);
10408
+ if (cells === cellOffset) {
10409
+ return { before: text.slice(0, i), atCursor: ch, after: text.slice(i + 1) };
10410
+ }
10411
+ if (cells + cw > cellOffset) {
10412
+ return { before: text.slice(0, i), atCursor: ch, after: text.slice(i + 1) };
10413
+ }
10414
+ cells += cw;
10415
+ }
10416
+ return { before: text, atCursor: "", after: "" };
10417
+ }
10418
+ function charCellsForText(ch) {
10419
+ const code = ch.charCodeAt(0);
10420
+ if (code < 32 || code === 127) return 0;
10421
+ if (code < 4352) return 1;
10422
+ if (code >= 4352 && code <= 4447) return 2;
10423
+ if (code >= 11904 && code <= 12350) return 2;
10424
+ if (code >= 12353 && code <= 13311) return 2;
10425
+ if (code >= 13312 && code <= 19903) return 2;
10426
+ if (code >= 19968 && code <= 40959) return 2;
10427
+ if (code >= 40960 && code <= 42191) return 2;
10428
+ if (code >= 44032 && code <= 55203) return 2;
10429
+ if (code >= 63744 && code <= 64255) return 2;
10430
+ if (code >= 65072 && code <= 65103) return 2;
10431
+ if (code >= 65280 && code <= 65376) return 2;
10432
+ if (code >= 65504 && code <= 65510) return 2;
10433
+ return 1;
10434
+ }
10435
+ function renderSegment(seg, key, _inverse) {
10436
+ if (seg.kind === "text") {
10437
+ return /* @__PURE__ */ React17.createElement(Text13, { key: `s-${key}` }, seg.text);
10438
+ }
10439
+ return /* @__PURE__ */ React17.createElement(Text13, { key: `s-${key}`, backgroundColor: "#f0abfc", color: "black", bold: true }, seg.label);
9551
10440
  }
9552
10441
  var COLLAPSE_THRESHOLD = 20;
9553
10442
  var COLLAPSE_HEAD_LINES = 3;
@@ -9572,88 +10461,49 @@ function collapseLinesForDisplay(lines, cursorLine) {
9572
10461
  }
9573
10462
  return out;
9574
10463
  }
9575
- function RenderLine({
9576
- line,
9577
- pastes,
9578
- inverse
9579
- }) {
9580
- const segments = [];
9581
- let buf = "";
9582
- let segIdx = 0;
9583
- const flushBuf = () => {
9584
- if (buf.length === 0) return;
9585
- segments.push(
9586
- /* @__PURE__ */ React15.createElement(Text14, { key: `t-${segIdx++}`, inverse }, buf)
9587
- );
9588
- buf = "";
9589
- };
9590
- for (let i = 0; i < line.length; i++) {
9591
- const ch = line[i];
9592
- const id = decodePasteSentinel(ch);
9593
- if (id === null) {
9594
- buf += ch;
9595
- continue;
9596
- }
9597
- flushBuf();
9598
- const entry = pastes.get(id);
9599
- const label = entry ? `[paste #${id + 1} \xB7 ${entry.lineCount}l \xB7 ${formatBytesShort(entry.charCount)}]` : `[paste #${id + 1} \xB7 (missing)]`;
9600
- segments.push(
9601
- /* @__PURE__ */ React15.createElement(Text14, { key: `p-${segIdx++}`, color: "magenta", bold: true, inverse }, label)
9602
- );
9603
- }
9604
- flushBuf();
9605
- if (segments.length === 0) {
9606
- return /* @__PURE__ */ React15.createElement(Text14, null, " ");
9607
- }
9608
- return /* @__PURE__ */ React15.createElement(React15.Fragment, null, segments);
9609
- }
9610
- function LineWithCursor({
9611
- line,
9612
- col,
9613
- showCursor,
9614
- borderColor,
9615
- pastes
9616
- }) {
9617
- const before = line.slice(0, col);
9618
- const atCursor = line.slice(col, col + 1);
9619
- const after = line.slice(col + 1);
9620
- if (atCursor.length === 0) {
9621
- return /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement(RenderLine, { line: before, pastes }), /* @__PURE__ */ React15.createElement(Text14, { color: borderColor }, showCursor ? "\u258C" : " "));
9622
- }
9623
- 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 }));
9624
- }
9625
10464
 
9626
10465
  // src/cli/ui/ShellConfirm.tsx
9627
- import { Box as Box15, Text as Text15 } from "ink";
9628
- import React16 from "react";
10466
+ import { Box as Box16, Text as Text14 } from "ink";
10467
+ import React18 from "react";
9629
10468
  function ShellConfirm({ command, allowPrefix, kind, onChoose }) {
9630
10469
  const isBackground = kind === "run_background";
9631
- return /* @__PURE__ */ React16.createElement(Box15, { flexDirection: "column", borderStyle: "round", borderColor: "red", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React16.createElement(Box15, null, /* @__PURE__ */ React16.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__ */ 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: "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__ */ 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(
9632
- SingleSelect,
10470
+ const subtitle = isBackground ? "long-running process \u2014 keeps running after approval, /kill to stop" : "model wants to run a shell command";
10471
+ return /* @__PURE__ */ React18.createElement(
10472
+ ModalCard,
9633
10473
  {
9634
- initialValue: "run_once",
9635
- items: [
9636
- {
9637
- value: "run_once",
9638
- label: "Run once",
9639
- hint: "Execute this command, don't remember it."
9640
- },
9641
- {
9642
- value: "always_allow",
9643
- label: `Always allow "${allowPrefix}" in this project`,
9644
- hint: "Save the prefix to ~/.reasonix/config.json; future matches auto-run."
9645
- },
9646
- {
9647
- value: "deny",
9648
- label: "Deny",
9649
- hint: "Tell the model the user refused; it will continue without this command."
9650
- }
9651
- ],
9652
- onSubmit: (v) => onChoose(v),
9653
- onCancel: () => onChoose("deny"),
9654
- footer: "[\u2191\u2193] navigate \xB7 [Enter] select \xB7 [Esc] deny"
9655
- }
9656
- )));
10474
+ accent: "#f87171",
10475
+ icon: isBackground ? "\u23F1" : "\u26A1",
10476
+ title: isBackground ? "background process" : "shell command",
10477
+ subtitle
10478
+ },
10479
+ /* @__PURE__ */ React18.createElement(Box16, { marginBottom: 1 }, /* @__PURE__ */ React18.createElement(Text14, { dimColor: true }, "$ "), /* @__PURE__ */ React18.createElement(Text14, { color: "#67e8f9", bold: true }, command)),
10480
+ /* @__PURE__ */ React18.createElement(
10481
+ SingleSelect,
10482
+ {
10483
+ initialValue: "run_once",
10484
+ items: [
10485
+ {
10486
+ value: "run_once",
10487
+ label: "Run once",
10488
+ hint: "Execute this command, don't remember it."
10489
+ },
10490
+ {
10491
+ value: "always_allow",
10492
+ label: `Always allow "${allowPrefix}" in this project`,
10493
+ hint: "Save the prefix to ~/.reasonix/config.json; future matches auto-run."
10494
+ },
10495
+ {
10496
+ value: "deny",
10497
+ label: "Deny",
10498
+ hint: "Tell the model the user refused; it will continue without this command."
10499
+ }
10500
+ ],
10501
+ onSubmit: (v) => onChoose(v),
10502
+ onCancel: () => onChoose("deny"),
10503
+ footer: "[\u2191\u2193] navigate \xB7 [Enter] select \xB7 [Esc] deny"
10504
+ }
10505
+ )
10506
+ );
9657
10507
  }
9658
10508
  function derivePrefix(command) {
9659
10509
  const tokens = command.trim().split(/\s+/).filter(Boolean);
@@ -9685,8 +10535,8 @@ function derivePrefix(command) {
9685
10535
  }
9686
10536
 
9687
10537
  // src/cli/ui/SlashArgPicker.tsx
9688
- import { Box as Box16, Text as Text16 } from "ink";
9689
- import React17 from "react";
10538
+ import { Box as Box17, Text as Text15 } from "ink";
10539
+ import React19 from "react";
9690
10540
  function SlashArgPicker({
9691
10541
  matches,
9692
10542
  selectedIndex,
@@ -9695,11 +10545,11 @@ function SlashArgPicker({
9695
10545
  partial
9696
10546
  }) {
9697
10547
  if (kind === "hint") {
9698
- return /* @__PURE__ */ React17.createElement(Box16, { paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React17.createElement(Text16, { dimColor: true }, " ", /* @__PURE__ */ React17.createElement(Text16, { bold: true }, "/", spec.cmd), spec.argsHint ? ` ${spec.argsHint}` : "", " \u2014 ", spec.summary));
10548
+ return /* @__PURE__ */ React19.createElement(Box17, { paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React19.createElement(Text15, { dimColor: true }, " ", /* @__PURE__ */ React19.createElement(Text15, { bold: true }, "/", spec.cmd), spec.argsHint ? ` ${spec.argsHint}` : "", " \u2014 ", spec.summary));
9699
10549
  }
9700
10550
  if (matches === null) return null;
9701
10551
  if (matches.length === 0) {
9702
- return /* @__PURE__ */ React17.createElement(Box16, { flexDirection: "column", paddingX: 1, marginTop: 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'));
10552
+ return /* @__PURE__ */ React19.createElement(Box17, { flexDirection: "column", paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React19.createElement(Text15, { dimColor: true }, " ", /* @__PURE__ */ React19.createElement(Text15, { bold: true }, "/", spec.cmd), spec.argsHint ? ` ${spec.argsHint}` : "", " \u2014 ", spec.summary), /* @__PURE__ */ React19.createElement(Text15, { color: "yellow" }, ' no match for "', partial, '" \u2014 keep typing, or Backspace to edit'));
9703
10553
  }
9704
10554
  const MAX = 8;
9705
10555
  const total = matches.length;
@@ -9707,26 +10557,26 @@ function SlashArgPicker({
9707
10557
  const shown = matches.slice(windowStart, windowStart + MAX);
9708
10558
  const hiddenAbove = windowStart;
9709
10559
  const hiddenBelow = total - windowStart - shown.length;
9710
- return /* @__PURE__ */ React17.createElement(Box16, { flexDirection: "column", paddingX: 1, marginTop: 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"));
10560
+ return /* @__PURE__ */ React19.createElement(Box17, { flexDirection: "column", paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React19.createElement(Text15, { dimColor: true }, " ", /* @__PURE__ */ React19.createElement(Text15, { bold: true }, "/", spec.cmd), spec.argsHint ? ` ${spec.argsHint}` : "", " \u2014 ", spec.summary), hiddenAbove > 0 ? /* @__PURE__ */ React19.createElement(Text15, { dimColor: true }, " \u2191 ", hiddenAbove, " more above") : null, shown.map((value, i) => /* @__PURE__ */ React19.createElement(ArgRow, { key: value, value, isSelected: windowStart + i === selectedIndex })), hiddenBelow > 0 ? /* @__PURE__ */ React19.createElement(Text15, { dimColor: true }, " \u2193 ", hiddenBelow, " more below") : null, /* @__PURE__ */ React19.createElement(Text15, { dimColor: true }, " [\u2191\u2193] navigate \xB7 [Tab]/[Enter] pick"));
9711
10561
  }
9712
10562
  function ArgRow({ value, isSelected }) {
9713
10563
  const marker = isSelected ? "\u25B8" : " ";
9714
10564
  if (isSelected) {
9715
- return /* @__PURE__ */ React17.createElement(Box16, null, /* @__PURE__ */ React17.createElement(Text16, { bold: true, color: "cyan" }, marker, " ", value));
10565
+ return /* @__PURE__ */ React19.createElement(Box17, null, /* @__PURE__ */ React19.createElement(Text15, { bold: true, color: "cyan" }, marker, " ", value));
9716
10566
  }
9717
- return /* @__PURE__ */ React17.createElement(Box16, null, /* @__PURE__ */ React17.createElement(Text16, { dimColor: true }, marker, " ", value));
10567
+ return /* @__PURE__ */ React19.createElement(Box17, null, /* @__PURE__ */ React19.createElement(Text15, { dimColor: true }, marker, " ", value));
9718
10568
  }
9719
10569
 
9720
10570
  // src/cli/ui/SlashSuggestions.tsx
9721
- import { Box as Box17, Text as Text17 } from "ink";
9722
- import React18 from "react";
10571
+ import { Box as Box18, Text as Text16 } from "ink";
10572
+ import React20 from "react";
9723
10573
  function SlashSuggestions({
9724
10574
  matches,
9725
10575
  selectedIndex
9726
10576
  }) {
9727
10577
  if (matches === null) return null;
9728
10578
  if (matches.length === 0) {
9729
- return /* @__PURE__ */ React18.createElement(Box17, { paddingX: 1, marginTop: 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"));
10579
+ return /* @__PURE__ */ React20.createElement(Box18, { paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React20.createElement(Text16, { color: "yellow" }, "no slash command matches that prefix"), /* @__PURE__ */ React20.createElement(Text16, { dimColor: true }, " \u2014 Backspace to edit, or /help for the full list"));
9730
10580
  }
9731
10581
  const MAX = 8;
9732
10582
  const total = matches.length;
@@ -9734,47 +10584,29 @@ function SlashSuggestions({
9734
10584
  const shown = matches.slice(windowStart, windowStart + MAX);
9735
10585
  const hiddenAbove = windowStart;
9736
10586
  const hiddenBelow = total - windowStart - shown.length;
9737
- return /* @__PURE__ */ React18.createElement(Box17, { flexDirection: "column", paddingX: 1, marginTop: 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"));
10587
+ return /* @__PURE__ */ React20.createElement(Box18, { flexDirection: "column", paddingX: 1, marginTop: 1 }, hiddenAbove > 0 ? /* @__PURE__ */ React20.createElement(Text16, { dimColor: true }, " \u2191 ", hiddenAbove, " more above") : null, shown.map((spec, i) => /* @__PURE__ */ React20.createElement(SuggestionRow, { key: spec.cmd, spec, isSelected: windowStart + i === selectedIndex })), hiddenBelow > 0 ? /* @__PURE__ */ React20.createElement(Text16, { dimColor: true }, " \u2193 ", hiddenBelow, " more below") : null, /* @__PURE__ */ React20.createElement(Text16, { dimColor: true }, " [\u2191\u2193] navigate \xB7 [Tab]/[Enter] pick"));
9738
10588
  }
9739
10589
  function SuggestionRow({ spec, isSelected }) {
9740
- const marker = isSelected ? "\u25B8" : " ";
9741
10590
  const name = `/${spec.cmd}`;
9742
10591
  const argsSuffix = spec.argsHint ? ` ${spec.argsHint}` : "";
9743
10592
  if (isSelected) {
9744
- 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));
10593
+ return /* @__PURE__ */ React20.createElement(Box18, null, /* @__PURE__ */ React20.createElement(Text16, { backgroundColor: "#67e8f9", color: "black", bold: true }, ` \u25B8 ${name.padEnd(12)}${argsSuffix.padEnd(16)} ${spec.summary} `));
9745
10594
  }
9746
- return /* @__PURE__ */ React18.createElement(Box17, null, /* @__PURE__ */ React18.createElement(Text17, { dimColor: true }, marker, " ", name.padEnd(12), argsSuffix.padEnd(16), " ", spec.summary));
10595
+ return /* @__PURE__ */ React20.createElement(Box18, null, /* @__PURE__ */ React20.createElement(Text16, { color: "#94a3b8" }, ` ${name.padEnd(12)}${argsSuffix.padEnd(16)} ${spec.summary}`));
9747
10596
  }
9748
10597
 
9749
10598
  // src/cli/ui/StatsPanel.tsx
9750
- import { Box as Box18, Text as Text18, useStdout as useStdout4 } from "ink";
9751
- import React19 from "react";
9752
- var WORDMARK_STYLES = [
9753
- { ch: "\u25C8", color: "#5eead4", isLogo: true },
9754
- // teal — brand mark
9755
- { ch: " ", color: "#5eead4", isLogo: false },
9756
- { ch: "R", color: "#67e8f9", isLogo: false },
9757
- // cyan
9758
- { ch: "E", color: "#7dd3fc", isLogo: false },
9759
- // sky
9760
- { ch: "A", color: "#93c5fd", isLogo: false },
9761
- // blue
9762
- { ch: "S", color: "#a5b4fc", isLogo: false },
9763
- // indigo
9764
- { ch: "O", color: "#c4b5fd", isLogo: false },
9765
- // violet
9766
- { ch: "N", color: "#d8b4fe", isLogo: false },
9767
- // purple
9768
- { ch: "I", color: "#f0abfc", isLogo: false },
9769
- // fuchsia
9770
- { ch: "X", color: "#f0abfc", isLogo: false }
9771
- // fuchsia
9772
- ];
9773
- function Wordmark({ busy }) {
10599
+ import { Box as Box19, Text as Text17, useStdout as useStdout6 } from "ink";
10600
+ import React21 from "react";
10601
+ var WORDMARK_LETTERS = ["R", "E", "A", "S", "O", "N", "I", "X"];
10602
+ function Wordmark({ busy, animate }) {
9774
10603
  const tick = useTick();
9775
- const period = busy ? 5 : 12;
9776
- const bright = Math.floor(tick / period) % 2 === 0;
9777
- 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)));
10604
+ const pulsePeriod = busy ? 5 : 12;
10605
+ const bright = animate ? Math.floor(tick / pulsePeriod) % 2 === 0 : true;
10606
+ const rotateEvery = busy ? 2 : 4;
10607
+ const offset = animate ? Math.floor(tick / rotateEvery) : 0;
10608
+ const colorAt = (i) => GRADIENT[((i + offset) % GRADIENT.length + GRADIENT.length) % GRADIENT.length];
10609
+ return /* @__PURE__ */ React21.createElement(Text17, null, /* @__PURE__ */ React21.createElement(Text17, { color: colorAt(0), bold: bright }, "\u25C8"), /* @__PURE__ */ React21.createElement(Text17, null, " "), WORDMARK_LETTERS.map((letter, i) => /* @__PURE__ */ React21.createElement(Text17, { key: letter, color: colorAt(i + 1), bold: true }, letter)));
9778
10610
  }
9779
10611
  var NARROW_BREAKPOINT = 120;
9780
10612
  var COLD_START_TURNS = 3;
@@ -9796,21 +10628,20 @@ function StatsPanel({
9796
10628
  const branchOn = (branchBudget ?? 1) > 1;
9797
10629
  const ctxMax = DEEPSEEK_CONTEXT_TOKENS[model2] ?? DEFAULT_CONTEXT_TOKENS;
9798
10630
  const ctxRatio = summary.lastPromptTokens / ctxMax;
9799
- const { stdout: stdout2 } = useStdout4();
10631
+ const { stdout: stdout2 } = useStdout6();
9800
10632
  const columns = stdout2?.columns ?? 80;
9801
10633
  const narrow = columns < NARROW_BREAKPOINT;
9802
10634
  const coldStart = summary.turns <= COLD_START_TURNS;
10635
+ const ruleWidth = Math.max(20, columns - 2);
10636
+ const animate = columns >= 100;
9803
10637
  return (
9804
- // Explicit `width={columns}` pins the border frame to the exact
9805
- // terminal width. Without this, Ink auto-flexes the Box to
9806
- // container width, and on terminal resize the prior frame's
9807
- // wrapped-overflow can leave tails in the scrollback (each
9808
- // redraw stacks a slightly-wider-or-narrower frame). Fixing
9809
- // width per-render doesn't eliminate the underlying Ink
9810
- // limitation (eraseLines counts logical rows, not post-wrap
9811
- // display rows) but makes each frame's dimensions exact so
9812
- // there's no residual uncertainty in the erase.
9813
- /* @__PURE__ */ React19.createElement(Box18, { borderStyle: "round", borderColor: "cyan", flexDirection: "column", paddingX: 1, width: columns }, /* @__PURE__ */ React19.createElement(
10638
+ // Borderless layout: no `borderStyle`, no rounded box. Bordered
10639
+ // Boxes were the most visible amplifier of Ink's eraseLines
10640
+ // miscount on Windows terminals. Visual weight here comes from
10641
+ // truecolor gradient rules at the top and bottom (rendered as
10642
+ // pure Text so they never trigger the eraseLines bug), the
10643
+ // animated wordmark + pill row, and a soft inner padding.
10644
+ /* @__PURE__ */ React21.createElement(Box19, { flexDirection: "column", paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React21.createElement(GradientRule, { width: ruleWidth, animate }), /* @__PURE__ */ React21.createElement(Box19, { marginTop: 1 }, /* @__PURE__ */ React21.createElement(
9814
10645
  Header,
9815
10646
  {
9816
10647
  model: model2,
@@ -9826,9 +10657,10 @@ function StatsPanel({
9826
10657
  narrow,
9827
10658
  busy: busy ?? false,
9828
10659
  proArmed: proArmed ?? false,
9829
- escalated: escalated ?? false
10660
+ escalated: escalated ?? false,
10661
+ animate
9830
10662
  }
9831
- ), narrow ? /* @__PURE__ */ React19.createElement(
10663
+ )), narrow ? /* @__PURE__ */ React21.createElement(
9832
10664
  StackedMetrics,
9833
10665
  {
9834
10666
  summary,
@@ -9837,7 +10669,7 @@ function StatsPanel({
9837
10669
  balance,
9838
10670
  coldStart
9839
10671
  }
9840
- ) : /* @__PURE__ */ React19.createElement(
10672
+ ) : /* @__PURE__ */ React21.createElement(
9841
10673
  InlineMetrics,
9842
10674
  {
9843
10675
  summary,
@@ -9846,9 +10678,28 @@ function StatsPanel({
9846
10678
  balance,
9847
10679
  coldStart
9848
10680
  }
9849
- ))
10681
+ ), /* @__PURE__ */ React21.createElement(Box19, { marginTop: 1 }, /* @__PURE__ */ React21.createElement(GradientRule, { width: ruleWidth, thin: true, animate })))
9850
10682
  );
9851
10683
  }
10684
+ function GradientRule({
10685
+ width,
10686
+ thin,
10687
+ animate
10688
+ }) {
10689
+ const tick = useTick();
10690
+ const offset = animate ? Math.floor(tick / 6) : 0;
10691
+ const ch = thin ? "\u2581" : "\u2584";
10692
+ const len = GRADIENT.length;
10693
+ return /* @__PURE__ */ React21.createElement(Box19, null, Array.from({ length: width }, (_, i) => {
10694
+ const t = width === 1 ? 0 : i * (len - 1) / (width - 1);
10695
+ const idx = (Math.round(t) + offset) % len;
10696
+ const color = GRADIENT[(idx % len + len) % len];
10697
+ return (
10698
+ // biome-ignore lint/suspicious/noArrayIndexKey: fixed-width gradient cells never reorder
10699
+ /* @__PURE__ */ React21.createElement(Text17, { key: `grule-${i}`, color }, ch)
10700
+ );
10701
+ }));
10702
+ }
9852
10703
  function Header({
9853
10704
  model: model2,
9854
10705
  prefixHash,
@@ -9863,9 +10714,16 @@ function Header({
9863
10714
  narrow,
9864
10715
  busy,
9865
10716
  proArmed,
9866
- escalated
10717
+ escalated,
10718
+ animate
9867
10719
  }) {
9868
- 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`)));
10720
+ const modePill = planMode ? { label: "PLAN", bg: "red" } : editMode === "auto" ? { label: "AUTO", bg: "magenta" } : editMode === "review" ? { label: "REVIEW", bg: "cyan" } : null;
10721
+ const proPill = escalated ? { label: "\u21E7 PRO", bg: "red" } : proArmed ? { label: "\u21E7 PRO", bg: "yellow" } : null;
10722
+ const showSecondary = animate && !narrow;
10723
+ return /* @__PURE__ */ React21.createElement(Box19, { justifyContent: "space-between" }, /* @__PURE__ */ React21.createElement(Box19, null, /* @__PURE__ */ React21.createElement(Wordmark, { busy, animate }), /* @__PURE__ */ React21.createElement(Text17, { dimColor: true }, ` ${VERSION}`), /* @__PURE__ */ React21.createElement(Text17, { dimColor: true }, " "), /* @__PURE__ */ React21.createElement(Text17, { color: "yellow", bold: true }, model2.replace(/^deepseek-/, "")), modePill ? /* @__PURE__ */ React21.createElement(React21.Fragment, null, /* @__PURE__ */ React21.createElement(Text17, null, " "), /* @__PURE__ */ React21.createElement(Pill, { label: modePill.label, bg: modePill.bg })) : null, proPill ? /* @__PURE__ */ React21.createElement(React21.Fragment, null, /* @__PURE__ */ React21.createElement(Text17, null, " "), /* @__PURE__ */ React21.createElement(Pill, { label: proPill.label, bg: proPill.bg })) : null, showSecondary && harvestOn ? /* @__PURE__ */ React21.createElement(Text17, { dimColor: true }, /* @__PURE__ */ React21.createElement(Text17, null, " "), /* @__PURE__ */ React21.createElement(Text17, { color: "magenta" }, "harvest")) : null, showSecondary && branchOn ? /* @__PURE__ */ React21.createElement(Text17, { dimColor: true }, /* @__PURE__ */ React21.createElement(Text17, null, " "), /* @__PURE__ */ React21.createElement(Text17, { color: "blue" }, `branch\xD7${branchBudget}`)) : null, showSecondary && reasoningEffort === "max" ? /* @__PURE__ */ React21.createElement(React21.Fragment, null, /* @__PURE__ */ React21.createElement(Text17, null, " "), /* @__PURE__ */ React21.createElement(Text17, { color: "green", dimColor: true }, "max")) : null, showSecondary && reasoningEffort === "high" ? /* @__PURE__ */ React21.createElement(React21.Fragment, null, /* @__PURE__ */ React21.createElement(Text17, null, " "), /* @__PURE__ */ React21.createElement(Text17, { color: "yellow", dimColor: true }, "high")) : null), /* @__PURE__ */ React21.createElement(Text17, null, updateAvailable ? /* @__PURE__ */ React21.createElement(Text17, { color: "yellow", bold: true }, `\u2191 ${updateAvailable} `) : null, /* @__PURE__ */ React21.createElement(Text17, { dimColor: true }, narrow ? `t${turns}` : `turn ${turns} \xB7 /help`)));
10724
+ }
10725
+ function Pill({ label, bg }) {
10726
+ return /* @__PURE__ */ React21.createElement(Text17, { backgroundColor: bg, color: "white", bold: true }, ` ${label} `);
9869
10727
  }
9870
10728
  function InlineMetrics({
9871
10729
  summary,
@@ -9874,7 +10732,7 @@ function InlineMetrics({
9874
10732
  balance,
9875
10733
  coldStart
9876
10734
  }) {
9877
- 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);
10735
+ return /* @__PURE__ */ React21.createElement(Box19, { marginTop: 1, gap: 3 }, /* @__PURE__ */ React21.createElement(ContextCell, { ratio: ctxRatio, promptTokens: summary.lastPromptTokens, ctxMax }), /* @__PURE__ */ React21.createElement(CacheCell, { hitRatio: summary.cacheHitRatio, coldStart, turns: summary.turns }), /* @__PURE__ */ React21.createElement(CostCell, { summary, coldStart }), balance ? /* @__PURE__ */ React21.createElement(BalanceCell, { balance }) : null);
9878
10736
  }
9879
10737
  function StackedMetrics({
9880
10738
  summary,
@@ -9883,7 +10741,7 @@ function StackedMetrics({
9883
10741
  balance,
9884
10742
  coldStart
9885
10743
  }) {
9886
- return /* @__PURE__ */ React19.createElement(Box18, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React19.createElement(
10744
+ return /* @__PURE__ */ React21.createElement(Box19, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React21.createElement(
9887
10745
  ContextCell,
9888
10746
  {
9889
10747
  ratio: ctxRatio,
@@ -9891,7 +10749,7 @@ function StackedMetrics({
9891
10749
  ctxMax,
9892
10750
  showBar: true
9893
10751
  }
9894
- ), balance ? /* @__PURE__ */ React19.createElement(BalanceCell, { balance }) : null, /* @__PURE__ */ React19.createElement(CacheCell, { hitRatio: summary.cacheHitRatio, coldStart, turns: summary.turns }), /* @__PURE__ */ React19.createElement(CostCell, { summary, coldStart }));
10752
+ ), balance ? /* @__PURE__ */ React21.createElement(BalanceCell, { balance }) : null, /* @__PURE__ */ React21.createElement(CacheCell, { hitRatio: summary.cacheHitRatio, coldStart, turns: summary.turns }), /* @__PURE__ */ React21.createElement(CostCell, { summary, coldStart }));
9895
10753
  }
9896
10754
  function ContextCell({
9897
10755
  ratio,
@@ -9900,11 +10758,11 @@ function ContextCell({
9900
10758
  showBar
9901
10759
  }) {
9902
10760
  if (promptTokens === 0) {
9903
- return /* @__PURE__ */ React19.createElement(Text18, null, /* @__PURE__ */ React19.createElement(Text18, { dimColor: true }, "ctx "), /* @__PURE__ */ React19.createElement(Text18, { dimColor: true }, "\u2014 (no turns yet)"));
10761
+ return /* @__PURE__ */ React21.createElement(Text17, null, /* @__PURE__ */ React21.createElement(Text17, { color: COLOR.info, dimColor: true }, "\u25A3 ctx "), /* @__PURE__ */ React21.createElement(Text17, { dimColor: true }, "\u2014 (no turns yet)"));
9904
10762
  }
9905
- const color = ratio >= 0.8 ? "red" : ratio >= 0.6 ? "yellow" : "green";
10763
+ const color = ratio >= 0.8 ? COLOR.err : ratio >= 0.6 ? COLOR.warn : COLOR.ok;
9906
10764
  const pct2 = Math.round(ratio * 100);
9907
- 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);
10765
+ return /* @__PURE__ */ React21.createElement(Text17, null, /* @__PURE__ */ React21.createElement(Text17, { color: COLOR.info }, "\u25A3 ctx "), /* @__PURE__ */ React21.createElement(Bar, { ratio, color, cells: showBar ? 14 : 10 }), /* @__PURE__ */ React21.createElement(Text17, null, " "), /* @__PURE__ */ React21.createElement(Text17, { color, bold: true }, formatTokens(promptTokens), "/", formatTokens(ctxMax)), /* @__PURE__ */ React21.createElement(Text17, { dimColor: true }, " (", pct2, "%)"), ratio >= 0.8 ? /* @__PURE__ */ React21.createElement(Text17, { color: COLOR.err, bold: true }, " \xB7 /compact") : null);
9908
10766
  }
9909
10767
  function CacheCell({
9910
10768
  hitRatio,
@@ -9913,46 +10771,48 @@ function CacheCell({
9913
10771
  }) {
9914
10772
  const pct2 = (hitRatio * 100).toFixed(1);
9915
10773
  if (turns === 0) {
9916
- return /* @__PURE__ */ React19.createElement(Text18, null, /* @__PURE__ */ React19.createElement(Text18, { dimColor: true }, "cache "), /* @__PURE__ */ React19.createElement(Text18, { dimColor: true }, "\u2014"));
10774
+ return /* @__PURE__ */ React21.createElement(Text17, null, /* @__PURE__ */ React21.createElement(Text17, { color: COLOR.info, dimColor: true }, "\u232C cache "), /* @__PURE__ */ React21.createElement(Text17, { dimColor: true }, "\u2014"));
9917
10775
  }
9918
10776
  if (coldStart) {
9919
- 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)"));
10777
+ return /* @__PURE__ */ React21.createElement(Text17, null, /* @__PURE__ */ React21.createElement(Text17, { color: COLOR.info, dimColor: true }, "\u232C cache "), /* @__PURE__ */ React21.createElement(Text17, { dimColor: true }, pct2, "% "), /* @__PURE__ */ React21.createElement(Text17, { dimColor: true, italic: true }, "(cold start)"));
9920
10778
  }
9921
- const color = hitRatio >= 0.7 ? "green" : hitRatio >= 0.4 ? "yellow" : "red";
9922
- return /* @__PURE__ */ React19.createElement(Text18, null, /* @__PURE__ */ React19.createElement(Text18, { dimColor: true }, "cache "), /* @__PURE__ */ React19.createElement(Text18, { color, bold: true }, pct2, "%"));
10779
+ const color = hitRatio >= 0.7 ? COLOR.ok : hitRatio >= 0.4 ? COLOR.warn : COLOR.err;
10780
+ return /* @__PURE__ */ React21.createElement(Text17, null, /* @__PURE__ */ React21.createElement(Text17, { color: COLOR.info }, "\u232C cache "), /* @__PURE__ */ React21.createElement(Text17, { color, bold: true }, pct2, "%"));
9923
10781
  }
9924
10782
  function turnCostColor(cost) {
9925
10783
  if (cost <= 0) return void 0;
9926
- if (cost >= 0.2) return "red";
9927
- if (cost >= 0.05) return "yellow";
9928
- return "green";
10784
+ if (cost >= 0.2) return COLOR.err;
10785
+ if (cost >= 0.05) return COLOR.warn;
10786
+ return COLOR.ok;
9929
10787
  }
9930
10788
  function sessionCostColor(cost) {
9931
10789
  if (cost <= 0) return void 0;
9932
- if (cost >= 5) return "red";
9933
- if (cost >= 0.5) return "yellow";
9934
- return "green";
10790
+ if (cost >= 5) return COLOR.err;
10791
+ if (cost >= 0.5) return COLOR.warn;
10792
+ return COLOR.ok;
9935
10793
  }
9936
10794
  function CostCell({
9937
10795
  summary,
9938
10796
  coldStart
9939
10797
  }) {
9940
10798
  if (summary.turns === 0) {
9941
- return /* @__PURE__ */ React19.createElement(Text18, null, /* @__PURE__ */ React19.createElement(Text18, { dimColor: true }, "cost "), /* @__PURE__ */ React19.createElement(Text18, { dimColor: true }, "\u2014"));
10799
+ return /* @__PURE__ */ React21.createElement(Text17, null, /* @__PURE__ */ React21.createElement(Text17, { color: COLOR.info, dimColor: true }, "\u25F4 cost "), /* @__PURE__ */ React21.createElement(Text17, { dimColor: true }, "\u2014"));
9942
10800
  }
9943
10801
  const turnColor = coldStart ? void 0 : turnCostColor(summary.lastTurnCostUsd);
9944
10802
  const sessionColor = coldStart ? void 0 : sessionCostColor(summary.totalCostUsd);
9945
- 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)));
10803
+ return /* @__PURE__ */ React21.createElement(Text17, null, /* @__PURE__ */ React21.createElement(Text17, { color: COLOR.info }, "\u25F4 turn "), /* @__PURE__ */ React21.createElement(Text17, { color: turnColor, bold: !coldStart, dimColor: coldStart }, "$", summary.lastTurnCostUsd.toFixed(4)), /* @__PURE__ */ React21.createElement(Text17, { dimColor: true }, " \xB7 session "), /* @__PURE__ */ React21.createElement(Text17, { color: sessionColor, bold: !coldStart, dimColor: coldStart }, "$", summary.totalCostUsd.toFixed(4)));
9946
10804
  }
9947
10805
  function BalanceCell({ balance }) {
9948
- const color = balance.total < 1 ? "red" : balance.total < 5 ? "yellow" : "green";
9949
- 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}` : ""));
10806
+ const color = balance.total < 1 ? COLOR.err : balance.total < 5 ? COLOR.warn : COLOR.ok;
10807
+ return /* @__PURE__ */ React21.createElement(Text17, null, /* @__PURE__ */ React21.createElement(Text17, { color: COLOR.info }, "\u25D0 balance "), /* @__PURE__ */ React21.createElement(Text17, { color, bold: true }, balance.currency === "USD" ? "$" : "", balance.total.toFixed(2), balance.currency !== "USD" ? ` ${balance.currency}` : ""));
9950
10808
  }
9951
- function Bar({ ratio, color }) {
9952
- const cells = 10;
10809
+ function Bar({
10810
+ ratio,
10811
+ color,
10812
+ cells = 14
10813
+ }) {
9953
10814
  const filled = Math.max(0, Math.min(cells, Math.round(ratio * cells)));
9954
- const bar = "\u2588".repeat(filled) + "\u2591".repeat(cells - filled);
9955
- return /* @__PURE__ */ React19.createElement(Text18, { color }, bar);
10815
+ return /* @__PURE__ */ React21.createElement(Text17, null, /* @__PURE__ */ React21.createElement(Text17, { color }, "\u25B0".repeat(filled)), /* @__PURE__ */ React21.createElement(Text17, { dimColor: true }, "\u25B1".repeat(cells - filled)));
9956
10816
  }
9957
10817
  function formatTokens(n) {
9958
10818
  if (n < 1024) return String(n);
@@ -9960,6 +10820,29 @@ function formatTokens(n) {
9960
10820
  return k >= 100 ? `${k.toFixed(0)}K` : `${k.toFixed(1)}K`;
9961
10821
  }
9962
10822
 
10823
+ // src/cli/ui/WelcomeBanner.tsx
10824
+ import { Box as Box20, Text as Text18, useStdout as useStdout7 } from "ink";
10825
+ import React22 from "react";
10826
+ function WelcomeBanner({ inCodeMode }) {
10827
+ const { stdout: stdout2 } = useStdout7();
10828
+ const cols = stdout2?.columns ?? 80;
10829
+ const ruleWidth = Math.min(60, Math.max(28, cols - 4));
10830
+ return /* @__PURE__ */ React22.createElement(Box20, { flexDirection: "column", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React22.createElement(GradientRule2, { width: ruleWidth }), /* @__PURE__ */ React22.createElement(BarRow, null, /* @__PURE__ */ React22.createElement(Text18, { bold: true, color: COLOR.brand }, "\u25C8 welcome"), /* @__PURE__ */ React22.createElement(Text18, { dimColor: true }, " \xB7 type a message to start")), /* @__PURE__ */ React22.createElement(BarRow, null), /* @__PURE__ */ React22.createElement(BarRow, null, /* @__PURE__ */ React22.createElement(Text18, { bold: true, color: COLOR.primary }, "quick start")), /* @__PURE__ */ React22.createElement(Hint, { cmd: "/help", desc: "every command + keyboard shortcut" }), /* @__PURE__ */ React22.createElement(Hint, { cmd: "/skill", desc: "invoke a stored playbook" }), inCodeMode ? /* @__PURE__ */ React22.createElement(React22.Fragment, null, /* @__PURE__ */ React22.createElement(Hint, { cmd: "@path", desc: "inline a file in your message" }), /* @__PURE__ */ React22.createElement(Hint, { cmd: "!cmd", desc: "run a shell command, output goes to context" })) : null, /* @__PURE__ */ React22.createElement(Hint, { cmd: "/exit", desc: "quit (Ctrl+C also works)" }), /* @__PURE__ */ React22.createElement(BarRow, null), /* @__PURE__ */ React22.createElement(BarRow, null, /* @__PURE__ */ React22.createElement(Text18, { dimColor: true, italic: true }, "tip:"), /* @__PURE__ */ React22.createElement(Text18, { dimColor: true }, " Ctrl+J inserts a newline \xB7 trailing \\ also continues")), /* @__PURE__ */ React22.createElement(Box20, { marginTop: 1 }, /* @__PURE__ */ React22.createElement(GradientRule2, { width: ruleWidth, thin: true })));
10831
+ }
10832
+ function GradientRule2({ width, thin }) {
10833
+ const cells = gradientCells(width, thin ? "\u2581" : "\u2584");
10834
+ return /* @__PURE__ */ React22.createElement(Box20, null, cells.map((c, i) => (
10835
+ // biome-ignore lint/suspicious/noArrayIndexKey: fixed-width gradient row, never reordered
10836
+ /* @__PURE__ */ React22.createElement(Text18, { key: `wrule-${i}`, color: c.color }, c.ch)
10837
+ )));
10838
+ }
10839
+ function BarRow({ children }) {
10840
+ return /* @__PURE__ */ React22.createElement(Box20, null, /* @__PURE__ */ React22.createElement(Text18, { color: COLOR.brand, bold: true }, "\u258E"), /* @__PURE__ */ React22.createElement(Text18, null, " "), children);
10841
+ }
10842
+ function Hint({ cmd, desc }) {
10843
+ return /* @__PURE__ */ React22.createElement(BarRow, null, /* @__PURE__ */ React22.createElement(Text18, { bold: true, color: COLOR.accent }, cmd.padEnd(8)), /* @__PURE__ */ React22.createElement(Text18, { dimColor: true }, ` ${desc}`));
10844
+ }
10845
+
9963
10846
  // src/cli/ui/bang.ts
9964
10847
  function detectBangCommand(text) {
9965
10848
  if (!text.startsWith("!")) return null;
@@ -11800,7 +12683,7 @@ function handleSlash(cmd, args, loop, ctx = {}) {
11800
12683
  }
11801
12684
 
11802
12685
  // src/cli/ui/useCompletionPickers.ts
11803
- import { useCallback, useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState6 } from "react";
12686
+ import { useCallback, useEffect as useEffect3, useMemo as useMemo2, useRef as useRef3, useState as useState6 } from "react";
11804
12687
  function useCompletionPickers({
11805
12688
  input,
11806
12689
  setInput,
@@ -11813,7 +12696,7 @@ function useCompletionPickers({
11813
12696
  if (!input.startsWith("/") || input.includes(" ")) return null;
11814
12697
  return suggestSlashCommands(input.slice(1), !!codeMode);
11815
12698
  }, [input, codeMode]);
11816
- useEffect2(() => {
12699
+ useEffect3(() => {
11817
12700
  setSlashSelected((prev) => {
11818
12701
  if (!slashMatches || slashMatches.length === 0) return 0;
11819
12702
  if (prev >= slashMatches.length) return slashMatches.length - 1;
@@ -11829,7 +12712,7 @@ function useCompletionPickers({
11829
12712
  return [];
11830
12713
  }
11831
12714
  }, [codeMode?.rootDir]);
11832
- const recentFilesRef = useRef2([]);
12715
+ const recentFilesRef = useRef3([]);
11833
12716
  const recordRecentFile = useCallback((p) => {
11834
12717
  const list = recentFilesRef.current;
11835
12718
  const i = list.indexOf(p);
@@ -11849,7 +12732,7 @@ function useCompletionPickers({
11849
12732
  recentlyUsed: recentFilesRef.current
11850
12733
  });
11851
12734
  }, [atPicker, atFiles]);
11852
- useEffect2(() => {
12735
+ useEffect3(() => {
11853
12736
  setAtSelected((prev) => {
11854
12737
  if (!atMatches || atMatches.length === 0) return 0;
11855
12738
  if (prev >= atMatches.length) return atMatches.length - 1;
@@ -11910,7 +12793,7 @@ function useCompletionPickers({
11910
12793
  }
11911
12794
  return null;
11912
12795
  }, [slashArgContext, models2, mcpServers]);
11913
- useEffect2(() => {
12796
+ useEffect3(() => {
11914
12797
  setSlashArgSelected((prev) => {
11915
12798
  if (!slashArgMatches || slashArgMatches.length === 0) return 0;
11916
12799
  if (prev >= slashArgMatches.length) return slashArgMatches.length - 1;
@@ -11944,13 +12827,13 @@ function useCompletionPickers({
11944
12827
  }
11945
12828
 
11946
12829
  // src/cli/ui/useEditHistory.ts
11947
- import { useCallback as useCallback2, useRef as useRef3, useState as useState7 } from "react";
12830
+ import { useCallback as useCallback2, useRef as useRef4, useState as useState7 } from "react";
11948
12831
  function useEditHistory(codeMode) {
11949
- const editHistory = useRef3([]);
11950
- const nextHistoryId = useRef3(1);
11951
- const currentTurnEntry = useRef3(null);
12832
+ const editHistory = useRef4([]);
12833
+ const nextHistoryId = useRef4(1);
12834
+ const currentTurnEntry = useRef4(null);
11952
12835
  const [undoBanner, setUndoBanner] = useState7(null);
11953
- const undoTimeoutRef = useRef3(null);
12836
+ const undoTimeoutRef = useRef4(null);
11954
12837
  const recordEdit = useCallback2(
11955
12838
  (source, blocks, results, snaps) => {
11956
12839
  if (snaps.length === 0) return;
@@ -12146,12 +13029,12 @@ function useEditHistory(codeMode) {
12146
13029
  }
12147
13030
 
12148
13031
  // src/cli/ui/useSessionInfo.ts
12149
- import { useCallback as useCallback3, useEffect as useEffect3, useState as useState8 } from "react";
13032
+ import { useCallback as useCallback3, useEffect as useEffect4, useState as useState8 } from "react";
12150
13033
  function useSessionInfo(loop) {
12151
13034
  const [balance, setBalance] = useState8(null);
12152
13035
  const [models2, setModels] = useState8(null);
12153
13036
  const [latestVersion, setLatestVersion] = useState8(null);
12154
- useEffect3(() => {
13037
+ useEffect4(() => {
12155
13038
  let cancelled = false;
12156
13039
  void (async () => {
12157
13040
  const bal = await loop.client.getBalance().catch(() => null);
@@ -12163,7 +13046,7 @@ function useSessionInfo(loop) {
12163
13046
  cancelled = true;
12164
13047
  };
12165
13048
  }, [loop]);
12166
- useEffect3(() => {
13049
+ useEffect4(() => {
12167
13050
  let cancelled = false;
12168
13051
  void (async () => {
12169
13052
  const list = await loop.client.listModels().catch(() => null);
@@ -12174,7 +13057,7 @@ function useSessionInfo(loop) {
12174
13057
  cancelled = true;
12175
13058
  };
12176
13059
  }, [loop]);
12177
- useEffect3(() => {
13060
+ useEffect4(() => {
12178
13061
  let cancelled = false;
12179
13062
  void (async () => {
12180
13063
  const latest = await getLatestVersion();
@@ -12219,11 +13102,11 @@ function useSessionInfo(loop) {
12219
13102
  }
12220
13103
 
12221
13104
  // src/cli/ui/useSubagent.ts
12222
- import { useEffect as useEffect4, useRef as useRef4, useState as useState9 } from "react";
13105
+ import { useEffect as useEffect5, useRef as useRef5, useState as useState9 } from "react";
12223
13106
  function useSubagent({ session, setHistorical }) {
12224
13107
  const [activity, setActivity] = useState9(null);
12225
- const sinkRef = useRef4({ current: null });
12226
- useEffect4(() => {
13108
+ const sinkRef = useRef5({ current: null });
13109
+ useEffect5(() => {
12227
13110
  sinkRef.current.current = (ev) => {
12228
13111
  if (ev.kind === "start") {
12229
13112
  setActivity({
@@ -12296,15 +13179,17 @@ function App({
12296
13179
  const [streaming, setStreaming] = useState10(null);
12297
13180
  const [input, setInput] = useState10("");
12298
13181
  const [busy, setBusy] = useState10(false);
12299
- const abortedThisTurn = useRef5(false);
13182
+ const abortedThisTurn = useRef6(false);
12300
13183
  const [ongoingTool, setOngoingTool] = useState10(null);
12301
13184
  const [toolProgress, setToolProgress] = useState10(null);
12302
- const { stdout: stdout2 } = useStdout5();
12303
- useEffect5(() => {
13185
+ const { stdout: stdout2 } = useStdout8();
13186
+ useEffect6(() => {
12304
13187
  if (!stdout2 || !stdout2.isTTY) return;
12305
13188
  stdout2.write("\x1B[?2004h");
13189
+ stdout2.write("\x1B[>4;2m");
12306
13190
  return () => {
12307
13191
  stdout2.write("\x1B[?2004l");
13192
+ stdout2.write("\x1B[>4m");
12308
13193
  };
12309
13194
  }, [stdout2]);
12310
13195
  const { activity: subagentActivity, sinkRef: subagentSinkRef } = useSubagent({
@@ -12326,24 +13211,24 @@ function App({
12326
13211
  sealCurrentEntry,
12327
13212
  hasUndoable
12328
13213
  } = useEditHistory(codeMode);
12329
- const pendingEdits = useRef5([]);
13214
+ const pendingEdits = useRef6([]);
12330
13215
  const [pendingCount, setPendingCount] = useState10(0);
12331
13216
  const syncPendingCount = useCallback4(() => {
12332
13217
  setPendingCount(pendingEdits.current.length);
12333
13218
  }, []);
12334
13219
  const [editMode, setEditMode] = useState10(() => codeMode ? loadEditMode() : "review");
12335
- const editModeRef = useRef5(editMode);
12336
- useEffect5(() => {
13220
+ const editModeRef = useRef6(editMode);
13221
+ useEffect6(() => {
12337
13222
  editModeRef.current = editMode;
12338
13223
  if (codeMode) saveEditMode(editMode);
12339
13224
  }, [editMode, codeMode]);
12340
13225
  const [pendingEditReview, setPendingEditReview] = useState10(null);
12341
- const editReviewResolveRef = useRef5(null);
12342
- const turnEditPolicyRef = useRef5("ask");
13226
+ const editReviewResolveRef = useRef6(null);
13227
+ const turnEditPolicyRef = useRef6("ask");
12343
13228
  const [modeFlash, setModeFlash] = useState10(false);
12344
- const modeFlashTimeoutRef = useRef5(null);
12345
- const prevEditModeRef = useRef5(editMode);
12346
- useEffect5(() => {
13229
+ const modeFlashTimeoutRef = useRef6(null);
13230
+ const prevEditModeRef = useRef6(editMode);
13231
+ useEffect6(() => {
12347
13232
  if (prevEditModeRef.current === editMode) return;
12348
13233
  prevEditModeRef.current = editMode;
12349
13234
  setModeFlash(true);
@@ -12365,15 +13250,15 @@ function App({
12365
13250
  const [proArmed, setProArmed] = useState10(false);
12366
13251
  const [turnOnPro, setTurnOnPro] = useState10(false);
12367
13252
  const [queuedSubmit, setQueuedSubmit] = useState10(null);
12368
- const promptHistory = useRef5([]);
12369
- const historyCursor = useRef5(-1);
12370
- const assistantIterCounter = useRef5(0);
12371
- const toolHistoryRef = useRef5([]);
12372
- const planStepsRef = useRef5(null);
12373
- const completedStepIdsRef = useRef5(/* @__PURE__ */ new Set());
12374
- const planBodyRef = useRef5(null);
12375
- const planSummaryRef = useRef5(null);
12376
- const toolStartedAtRef = useRef5(null);
13253
+ const promptHistory = useRef6([]);
13254
+ const historyCursor = useRef6(-1);
13255
+ const assistantIterCounter = useRef6(0);
13256
+ const toolHistoryRef = useRef6([]);
13257
+ const planStepsRef = useRef6(null);
13258
+ const completedStepIdsRef = useRef6(/* @__PURE__ */ new Set());
13259
+ const planBodyRef = useRef6(null);
13260
+ const planSummaryRef = useRef6(null);
13261
+ const toolStartedAtRef = useRef6(null);
12377
13262
  const persistPlanState = useCallback4(() => {
12378
13263
  if (!session) return;
12379
13264
  const steps = planStepsRef.current;
@@ -12397,7 +13282,7 @@ function App({
12397
13282
  lastPromptTokens: 0,
12398
13283
  lastTurnCostUsd: 0
12399
13284
  });
12400
- const transcriptRef = useRef5(null);
13285
+ const transcriptRef = useRef6(null);
12401
13286
  if (transcript && !transcriptRef.current) {
12402
13287
  transcriptRef.current = openTranscriptFile(transcript, {
12403
13288
  version: 1,
@@ -12406,12 +13291,12 @@ function App({
12406
13291
  startedAt: (/* @__PURE__ */ new Date()).toISOString()
12407
13292
  });
12408
13293
  }
12409
- useEffect5(() => {
13294
+ useEffect6(() => {
12410
13295
  return () => {
12411
13296
  transcriptRef.current?.end();
12412
13297
  };
12413
13298
  }, []);
12414
- const loopRef = useRef5(null);
13299
+ const loopRef = useRef6(null);
12415
13300
  const loop = useMemo3(() => {
12416
13301
  if (loopRef.current) return loopRef.current;
12417
13302
  const client = new DeepSeekClient();
@@ -12460,7 +13345,7 @@ function App({
12460
13345
  loopRef.current = l;
12461
13346
  return l;
12462
13347
  }, [model2, system, harvest3, branch2, session, tools, codeMode]);
12463
- useEffect5(() => {
13348
+ useEffect6(() => {
12464
13349
  loop.hooks = hookList;
12465
13350
  }, [loop, hookList]);
12466
13351
  const {
@@ -12488,7 +13373,7 @@ function App({
12488
13373
  setSlashArgSelected,
12489
13374
  pickSlashArg
12490
13375
  } = useCompletionPickers({ input, setInput, codeMode, models: models2, mcpServers });
12491
- useEffect5(() => {
13376
+ useEffect6(() => {
12492
13377
  if (!progressSink) return;
12493
13378
  progressSink.current = (info) => {
12494
13379
  setToolProgress({
@@ -12501,8 +13386,8 @@ function App({
12501
13386
  if (progressSink.current) progressSink.current = null;
12502
13387
  };
12503
13388
  }, [progressSink]);
12504
- const sessionBannerShown = useRef5(false);
12505
- useEffect5(() => {
13389
+ const sessionBannerShown = useRef6(false);
13390
+ useEffect6(() => {
12506
13391
  if (sessionBannerShown.current) return;
12507
13392
  sessionBannerShown.current = true;
12508
13393
  if (!session) {
@@ -12584,7 +13469,26 @@ function App({
12584
13469
  markEditModeHintShown();
12585
13470
  }
12586
13471
  }, [session, loop, codeMode, syncPendingCount]);
12587
- useInput5((chKey, key) => {
13472
+ const quitProcess = useCallback4(() => {
13473
+ transcriptRef.current?.end();
13474
+ process.exit(0);
13475
+ }, []);
13476
+ useEffect6(() => {
13477
+ process.on("SIGINT", quitProcess);
13478
+ return () => {
13479
+ process.off("SIGINT", quitProcess);
13480
+ };
13481
+ }, [quitProcess]);
13482
+ useKeystroke((ev) => {
13483
+ const chKey = ev.input;
13484
+ const key = ev;
13485
+ if (ev.paste) {
13486
+ return;
13487
+ }
13488
+ if (key.ctrl && key.input === "c") {
13489
+ quitProcess();
13490
+ return;
13491
+ }
12588
13492
  if (key.escape && busy) {
12589
13493
  if (abortedThisTurn.current) return;
12590
13494
  abortedThisTurn.current = true;
@@ -12682,7 +13586,7 @@ function App({
12682
13586
  historyCursor.current = nextCursor;
12683
13587
  setInput(nextCursor < 0 ? "" : hist[hist.length - 1 - nextCursor] ?? "");
12684
13588
  }, []);
12685
- useEffect5(() => {
13589
+ useEffect6(() => {
12686
13590
  if (!tools || !codeMode) return;
12687
13591
  tools.setToolInterceptor(async (name, args) => {
12688
13592
  if (name !== "edit_file" && name !== "write_file") return null;
@@ -13548,7 +14452,7 @@ ${body}`;
13548
14452
  },
13549
14453
  [pendingShell, codeMode, handleSubmit, busy, loop]
13550
14454
  );
13551
- useEffect5(() => {
14455
+ useEffect6(() => {
13552
14456
  if (!busy && queuedSubmit !== null) {
13553
14457
  const text = queuedSubmit;
13554
14458
  setQueuedSubmit(null);
@@ -13592,8 +14496,8 @@ ${body}`;
13592
14496
  },
13593
14497
  [pendingPlan, togglePlanMode, busy, loop, handleSubmit, persistPlanState]
13594
14498
  );
13595
- const handlePlanConfirmRef = useRef5(handlePlanConfirm);
13596
- useEffect5(() => {
14499
+ const handlePlanConfirmRef = useRef6(handlePlanConfirm);
14500
+ useEffect6(() => {
13597
14501
  handlePlanConfirmRef.current = handlePlanConfirm;
13598
14502
  }, [handlePlanConfirm]);
13599
14503
  const stableHandlePlanConfirm = useCallback4(
@@ -13684,8 +14588,8 @@ Stay in plan mode \u2014 address the feedback (explore more if needed), then sub
13684
14588
  },
13685
14589
  [pendingCheckpoint, busy, loop, handleSubmit]
13686
14590
  );
13687
- const handleCheckpointConfirmRef = useRef5(handleCheckpointConfirm);
13688
- useEffect5(() => {
14591
+ const handleCheckpointConfirmRef = useRef6(handleCheckpointConfirm);
14592
+ useEffect6(() => {
13689
14593
  handleCheckpointConfirmRef.current = handleCheckpointConfirm;
13690
14594
  }, [handleCheckpointConfirm]);
13691
14595
  const stableHandleCheckpointConfirm = useCallback4(
@@ -13762,8 +14666,8 @@ If the feedback only tweaks how you execute (extra constraints, style preference
13762
14666
  },
13763
14667
  [pendingChoice, busy, loop, handleSubmit]
13764
14668
  );
13765
- const handleChoiceConfirmRef = useRef5(handleChoiceConfirm);
13766
- useEffect5(() => {
14669
+ const handleChoiceConfirmRef = useRef6(handleChoiceConfirm);
14670
+ useEffect6(() => {
13767
14671
  handleChoiceConfirmRef.current = handleChoiceConfirm;
13768
14672
  }, [handleChoiceConfirm]);
13769
14673
  const stableHandleChoiceConfirm = useCallback4(
@@ -13853,20 +14757,20 @@ Continue executing from the next pending step. Call mark_step_complete after eac
13853
14757
  },
13854
14758
  [pendingRevision, busy, loop, handleSubmit, persistPlanState]
13855
14759
  );
13856
- const handleReviseConfirmRef = useRef5(handleReviseConfirm);
13857
- useEffect5(() => {
14760
+ const handleReviseConfirmRef = useRef6(handleReviseConfirm);
14761
+ useEffect6(() => {
13858
14762
  handleReviseConfirmRef.current = handleReviseConfirm;
13859
14763
  }, [handleReviseConfirm]);
13860
14764
  const stableHandleReviseConfirm = useCallback4(
13861
14765
  async (choice) => handleReviseConfirmRef.current(choice),
13862
14766
  []
13863
14767
  );
13864
- return /* @__PURE__ */ React20.createElement(
14768
+ return /* @__PURE__ */ React23.createElement(React23.Fragment, null, /* @__PURE__ */ React23.createElement(
13865
14769
  TickerProvider,
13866
14770
  {
13867
14771
  disabled: PLAIN_UI || !!pendingPlan || !!pendingShell || !!pendingEditReview || !!pendingCheckpoint || !!stagedCheckpointRevise || !!pendingChoice || !!stagedChoiceCustom || !!pendingRevision
13868
14772
  },
13869
- /* @__PURE__ */ React20.createElement(Box19, { flexDirection: "column" }, /* @__PURE__ */ React20.createElement(
14773
+ /* @__PURE__ */ React23.createElement(Box21, { flexDirection: "column" }, /* @__PURE__ */ React23.createElement(
13870
14774
  StatsPanel,
13871
14775
  {
13872
14776
  summary,
@@ -13883,28 +14787,28 @@ Continue executing from the next pending step. Call mark_step_complete after eac
13883
14787
  proArmed,
13884
14788
  escalated: turnOnPro
13885
14789
  }
13886
- ), /* @__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(
14790
+ ), /* @__PURE__ */ React23.createElement(Static, { items: historical }, (item) => /* @__PURE__ */ React23.createElement(EventRow, { key: item.id, event: item, projectRoot: hookCwd })), !historical.some((e) => e.role === "user" || e.role === "assistant") && !busy && !streaming ? /* @__PURE__ */ React23.createElement(WelcomeBanner, { inCodeMode: !!codeMode }) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !stagedInput && !pendingEditReview && !pendingCheckpoint && !stagedCheckpointRevise && streaming ? /* @__PURE__ */ React23.createElement(Box21, { marginY: 1 }, /* @__PURE__ */ React23.createElement(EventRow, { event: streaming, projectRoot: hookCwd })) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !stagedInput && !pendingEditReview && !pendingCheckpoint && !stagedCheckpointRevise && ongoingTool ? /* @__PURE__ */ React23.createElement(OngoingToolRow, { tool: ongoingTool, progress: toolProgress }) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !stagedInput && !pendingEditReview && !pendingCheckpoint && !stagedCheckpointRevise && subagentActivity ? /* @__PURE__ */ React23.createElement(SubagentRow, { activity: subagentActivity }) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !stagedInput && !pendingEditReview && !pendingCheckpoint && !stagedCheckpointRevise && !ongoingTool && statusLine ? /* @__PURE__ */ React23.createElement(StatusRow, { text: statusLine }) : null, !PLAIN_UI && undoBanner && !pendingShell && !pendingPlan && !stagedInput && !pendingEditReview && !pendingCheckpoint && !stagedCheckpointRevise && !pendingChoice && !stagedChoiceCustom && !pendingRevision ? /* @__PURE__ */ React23.createElement(UndoBanner, { banner: undoBanner }) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !stagedInput && !pendingEditReview && !pendingCheckpoint && !stagedCheckpointRevise && busy && !streaming && !ongoingTool && !statusLine ? /* @__PURE__ */ React23.createElement(StatusRow, { text: "processing\u2026" }) : null, stagedInput ? /* @__PURE__ */ React23.createElement(
13887
14791
  PlanRefineInput,
13888
14792
  {
13889
14793
  mode: stagedInput.mode,
13890
14794
  onSubmit: handleStagedInputSubmit,
13891
14795
  onCancel: handleStagedInputCancel
13892
14796
  }
13893
- ) : stagedCheckpointRevise ? /* @__PURE__ */ React20.createElement(
14797
+ ) : stagedCheckpointRevise ? /* @__PURE__ */ React23.createElement(
13894
14798
  PlanRefineInput,
13895
14799
  {
13896
14800
  mode: "checkpoint-revise",
13897
14801
  onSubmit: handleCheckpointReviseSubmit,
13898
14802
  onCancel: handleCheckpointReviseCancel
13899
14803
  }
13900
- ) : stagedChoiceCustom ? /* @__PURE__ */ React20.createElement(
14804
+ ) : stagedChoiceCustom ? /* @__PURE__ */ React23.createElement(
13901
14805
  PlanRefineInput,
13902
14806
  {
13903
14807
  mode: "choice-custom",
13904
14808
  onSubmit: handleChoiceCustomSubmit,
13905
14809
  onCancel: handleChoiceCustomCancel
13906
14810
  }
13907
- ) : pendingChoice ? /* @__PURE__ */ React20.createElement(
14811
+ ) : pendingChoice ? /* @__PURE__ */ React23.createElement(
13908
14812
  ChoiceConfirm,
13909
14813
  {
13910
14814
  question: pendingChoice.question,
@@ -13912,7 +14816,7 @@ Continue executing from the next pending step. Call mark_step_complete after eac
13912
14816
  allowCustom: pendingChoice.allowCustom,
13913
14817
  onChoose: stableHandleChoiceConfirm
13914
14818
  }
13915
- ) : pendingRevision ? /* @__PURE__ */ React20.createElement(
14819
+ ) : pendingRevision ? /* @__PURE__ */ React23.createElement(
13916
14820
  PlanReviseConfirm,
13917
14821
  {
13918
14822
  reason: pendingRevision.reason,
@@ -13923,7 +14827,7 @@ Continue executing from the next pending step. Call mark_step_complete after eac
13923
14827
  summary: pendingRevision.summary,
13924
14828
  onChoose: stableHandleReviseConfirm
13925
14829
  }
13926
- ) : pendingCheckpoint ? /* @__PURE__ */ React20.createElement(
14830
+ ) : pendingCheckpoint ? /* @__PURE__ */ React23.createElement(
13927
14831
  PlanCheckpointConfirm,
13928
14832
  {
13929
14833
  stepId: pendingCheckpoint.stepId,
@@ -13934,7 +14838,7 @@ Continue executing from the next pending step. Call mark_step_complete after eac
13934
14838
  completedStepIds: completedStepIdsRef.current,
13935
14839
  onChoose: stableHandleCheckpointConfirm
13936
14840
  }
13937
- ) : pendingPlan ? /* @__PURE__ */ React20.createElement(
14841
+ ) : pendingPlan ? /* @__PURE__ */ React23.createElement(
13938
14842
  PlanConfirm,
13939
14843
  {
13940
14844
  plan: pendingPlan,
@@ -13943,7 +14847,7 @@ Continue executing from the next pending step. Call mark_step_complete after eac
13943
14847
  onChoose: stableHandlePlanConfirm,
13944
14848
  projectRoot: hookCwd
13945
14849
  }
13946
- ) : pendingShell ? /* @__PURE__ */ React20.createElement(
14850
+ ) : pendingShell ? /* @__PURE__ */ React23.createElement(
13947
14851
  ShellConfirm,
13948
14852
  {
13949
14853
  command: pendingShell.command,
@@ -13951,7 +14855,7 @@ Continue executing from the next pending step. Call mark_step_complete after eac
13951
14855
  kind: pendingShell.kind,
13952
14856
  onChoose: handleShellConfirm
13953
14857
  }
13954
- ) : pendingEditReview ? /* @__PURE__ */ React20.createElement(
14858
+ ) : pendingEditReview ? /* @__PURE__ */ React23.createElement(
13955
14859
  EditConfirm,
13956
14860
  {
13957
14861
  block: pendingEditReview,
@@ -13963,7 +14867,7 @@ Continue executing from the next pending step. Call mark_step_complete after eac
13963
14867
  }
13964
14868
  }
13965
14869
  }
13966
- ) : /* @__PURE__ */ React20.createElement(React20.Fragment, null, codeMode ? /* @__PURE__ */ React20.createElement(
14870
+ ) : /* @__PURE__ */ React23.createElement(React23.Fragment, null, codeMode ? /* @__PURE__ */ React23.createElement(
13967
14871
  ModeStatusBar,
13968
14872
  {
13969
14873
  editMode,
@@ -13973,7 +14877,7 @@ Continue executing from the next pending step. Call mark_step_complete after eac
13973
14877
  undoArmed: !!undoBanner || hasUndoable(),
13974
14878
  jobs: codeMode.jobs
13975
14879
  }
13976
- ) : null, /* @__PURE__ */ React20.createElement(
14880
+ ) : null, /* @__PURE__ */ React23.createElement(
13977
14881
  PromptInput,
13978
14882
  {
13979
14883
  value: input,
@@ -13983,14 +14887,14 @@ Continue executing from the next pending step. Call mark_step_complete after eac
13983
14887
  onHistoryPrev: recallPrev,
13984
14888
  onHistoryNext: recallNext
13985
14889
  }
13986
- ), /* @__PURE__ */ React20.createElement(SlashSuggestions, { matches: slashMatches, selectedIndex: slashSelected }), /* @__PURE__ */ React20.createElement(
14890
+ ), /* @__PURE__ */ React23.createElement(SlashSuggestions, { matches: slashMatches, selectedIndex: slashSelected }), /* @__PURE__ */ React23.createElement(
13987
14891
  AtMentionSuggestions,
13988
14892
  {
13989
14893
  matches: atMatches,
13990
14894
  selectedIndex: atSelected,
13991
14895
  query: atPicker?.query ?? ""
13992
14896
  }
13993
- ), slashArgContext ? /* @__PURE__ */ React20.createElement(
14897
+ ), slashArgContext ? /* @__PURE__ */ React23.createElement(
13994
14898
  SlashArgPicker,
13995
14899
  {
13996
14900
  matches: slashArgMatches,
@@ -14000,19 +14904,19 @@ Continue executing from the next pending step. Call mark_step_complete after eac
14000
14904
  partial: slashArgContext.partial
14001
14905
  }
14002
14906
  ) : null))
14003
- );
14907
+ ));
14004
14908
  }
14005
14909
 
14006
14910
  // src/cli/ui/SessionPicker.tsx
14007
- import { Box as Box20, Text as Text19 } from "ink";
14008
- import React21 from "react";
14911
+ import { Box as Box22, Text as Text19 } from "ink";
14912
+ import React24 from "react";
14009
14913
  function SessionPicker({
14010
14914
  sessionName,
14011
14915
  messageCount,
14012
14916
  lastActive,
14013
14917
  onChoose
14014
14918
  }) {
14015
- 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(
14919
+ return /* @__PURE__ */ React24.createElement(Box22, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React24.createElement(Box22, { marginBottom: 1 }, /* @__PURE__ */ React24.createElement(Text19, { bold: true, color: "cyan" }, `Session "${sessionName}" has ${messageCount} prior message${messageCount === 1 ? "" : "s"}`), /* @__PURE__ */ React24.createElement(Text19, { dimColor: true }, ` \xB7 last active ${relativeTime2(lastActive)}`)), /* @__PURE__ */ React24.createElement(
14016
14920
  SingleSelect,
14017
14921
  {
14018
14922
  initialValue: "new",
@@ -14035,7 +14939,7 @@ function SessionPicker({
14035
14939
  ],
14036
14940
  onSubmit: (v) => onChoose(v)
14037
14941
  }
14038
- ), /* @__PURE__ */ React21.createElement(Box20, { marginTop: 1 }, /* @__PURE__ */ React21.createElement(Text19, { dimColor: true }, "[\u2191\u2193] navigate \xB7 [Enter] select")));
14942
+ ), /* @__PURE__ */ React24.createElement(Box22, { marginTop: 1 }, /* @__PURE__ */ React24.createElement(Text19, { dimColor: true }, "[\u2191\u2193] navigate \xB7 [Enter] select")));
14039
14943
  }
14040
14944
  function relativeTime2(date) {
14041
14945
  const ms = Date.now() - date.getTime();
@@ -14051,9 +14955,9 @@ function relativeTime2(date) {
14051
14955
  }
14052
14956
 
14053
14957
  // src/cli/ui/Setup.tsx
14054
- import { Box as Box21, Text as Text20, useApp as useApp2 } from "ink";
14958
+ import { Box as Box23, Text as Text20, useApp as useApp2 } from "ink";
14055
14959
  import TextInput from "ink-text-input";
14056
- import React22, { useState as useState11 } from "react";
14960
+ import React25, { useState as useState11 } from "react";
14057
14961
  function Setup({ onReady }) {
14058
14962
  const [value, setValue] = useState11("");
14059
14963
  const [error, setError] = useState11(null);
@@ -14077,7 +14981,7 @@ function Setup({ onReady }) {
14077
14981
  }
14078
14982
  onReady(trimmed);
14079
14983
  };
14080
- 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(
14984
+ return /* @__PURE__ */ React25.createElement(Box23, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React25.createElement(Text20, { bold: true, color: "cyan" }, "Welcome to Reasonix."), /* @__PURE__ */ React25.createElement(Box23, { marginTop: 1 }, /* @__PURE__ */ React25.createElement(Text20, null, "Paste your DeepSeek API key to get started.")), /* @__PURE__ */ React25.createElement(Text20, { dimColor: true }, "Get one (free credit on signup): https://platform.deepseek.com/api_keys"), /* @__PURE__ */ React25.createElement(Text20, { dimColor: true }, "Saved locally to ", defaultConfigPath()), /* @__PURE__ */ React25.createElement(Box23, { marginTop: 1 }, /* @__PURE__ */ React25.createElement(Text20, { bold: true, color: "cyan" }, "key \u203A "), /* @__PURE__ */ React25.createElement(
14081
14985
  TextInput,
14082
14986
  {
14083
14987
  value,
@@ -14086,7 +14990,7 @@ function Setup({ onReady }) {
14086
14990
  mask: "\u2022",
14087
14991
  placeholder: "sk-..."
14088
14992
  }
14089
- )), 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.)")));
14993
+ )), error ? /* @__PURE__ */ React25.createElement(Box23, { marginTop: 1 }, /* @__PURE__ */ React25.createElement(Text20, { color: "red" }, error)) : value ? /* @__PURE__ */ React25.createElement(Box23, { marginTop: 1 }, /* @__PURE__ */ React25.createElement(Text20, { dimColor: true }, "preview: ", redactKey(value))) : null, /* @__PURE__ */ React25.createElement(Box23, { marginTop: 1 }, /* @__PURE__ */ React25.createElement(Text20, { dimColor: true }, "(Type /exit to abort.)")));
14090
14994
  }
14091
14995
 
14092
14996
  // src/cli/commands/chat.tsx
@@ -14102,7 +15006,7 @@ function Root({
14102
15006
  const [key, setKey] = useState12(initialKey);
14103
15007
  const [pending, setPending] = useState12(sessionPreview);
14104
15008
  if (!key) {
14105
- return /* @__PURE__ */ React23.createElement(
15009
+ return /* @__PURE__ */ React26.createElement(
14106
15010
  Setup,
14107
15011
  {
14108
15012
  onReady: (k) => {
@@ -14114,7 +15018,7 @@ function Root({
14114
15018
  }
14115
15019
  process.env.DEEPSEEK_API_KEY = key;
14116
15020
  if (pending && appProps.session) {
14117
- return /* @__PURE__ */ React23.createElement(
15021
+ return /* @__PURE__ */ React26.createElement(KeystrokeProvider, null, /* @__PURE__ */ React26.createElement(
14118
15022
  SessionPicker,
14119
15023
  {
14120
15024
  sessionName: appProps.session,
@@ -14127,9 +15031,9 @@ function Root({
14127
15031
  setPending(void 0);
14128
15032
  }
14129
15033
  }
14130
- );
15034
+ ));
14131
15035
  }
14132
- return /* @__PURE__ */ React23.createElement(
15036
+ return /* @__PURE__ */ React26.createElement(KeystrokeProvider, null, /* @__PURE__ */ React26.createElement(
14133
15037
  App,
14134
15038
  {
14135
15039
  model: appProps.model,
@@ -14144,7 +15048,7 @@ function Root({
14144
15048
  progressSink,
14145
15049
  codeMode: appProps.codeMode
14146
15050
  }
14147
- );
15051
+ ));
14148
15052
  }
14149
15053
  async function chatCommand(opts) {
14150
15054
  loadDotenv();
@@ -14233,8 +15137,11 @@ async function chatCommand(opts) {
14233
15137
  } else if (opts.session && opts.forceNew) {
14234
15138
  rewriteSession(opts.session, []);
14235
15139
  }
15140
+ if (process.stdout.isTTY) {
15141
+ process.stdout.write("\x1B[2J\x1B[3J\x1B[H");
15142
+ }
14236
15143
  const { waitUntilExit } = render(
14237
- /* @__PURE__ */ React23.createElement(
15144
+ /* @__PURE__ */ React26.createElement(
14238
15145
  Root,
14239
15146
  {
14240
15147
  initialKey,
@@ -14283,12 +15190,9 @@ async function codeCommand(opts = {}) {
14283
15190
  `\u25B8 reasonix code: rooted at ${rootDir}, session "${session ?? "(ephemeral)"}" \xB7 ${tools.size} native tool(s)
14284
15191
  `
14285
15192
  );
14286
- const sigShutdown = () => {
15193
+ process.once("exit", () => {
14287
15194
  void jobs2.shutdown();
14288
- };
14289
- process.once("SIGINT", sigShutdown);
14290
- process.once("SIGTERM", sigShutdown);
14291
- process.once("exit", sigShutdown);
15195
+ });
14292
15196
  await chatCommand({
14293
15197
  model: opts.model ?? "deepseek-v4-flash",
14294
15198
  harvest: opts.harvest ?? false,
@@ -14306,35 +15210,35 @@ async function codeCommand(opts = {}) {
14306
15210
  import { writeFileSync as writeFileSync7 } from "fs";
14307
15211
  import { basename as basename3 } from "path";
14308
15212
  import { render as render2 } from "ink";
14309
- import React26 from "react";
15213
+ import React29 from "react";
14310
15214
 
14311
15215
  // src/cli/ui/DiffApp.tsx
14312
- import { Box as Box23, Static as Static2, Text as Text22, useApp as useApp3, useInput as useInput6 } from "ink";
14313
- import React25, { useState as useState13 } from "react";
15216
+ import { Box as Box25, Static as Static2, Text as Text22, useApp as useApp3, useInput } from "ink";
15217
+ import React28, { useState as useState13 } from "react";
14314
15218
 
14315
15219
  // src/cli/ui/RecordView.tsx
14316
- import { Box as Box22, Text as Text21 } from "ink";
14317
- import React24 from "react";
15220
+ import { Box as Box24, Text as Text21 } from "ink";
15221
+ import React27 from "react";
14318
15222
  function RecordView({ rec, compact: compact2 = false }) {
14319
15223
  const toolArgsMax = compact2 ? 120 : 200;
14320
15224
  const toolContentMax = compact2 ? 200 : 400;
14321
15225
  if (rec.role === "user") {
14322
15226
  const content = rec.content.includes("\n") ? rec.content.split("\n").join("\n ") : rec.content;
14323
- return /* @__PURE__ */ React24.createElement(Box22, { marginTop: 1 }, /* @__PURE__ */ React24.createElement(Text21, { bold: true, color: "cyan" }, "you \u203A", " "), /* @__PURE__ */ React24.createElement(Text21, null, content));
15227
+ return /* @__PURE__ */ React27.createElement(Box24, { marginTop: 1 }, /* @__PURE__ */ React27.createElement(Text21, { bold: true, color: "cyan" }, "you \u203A", " "), /* @__PURE__ */ React27.createElement(Text21, null, content));
14324
15228
  }
14325
15229
  if (rec.role === "assistant_final") {
14326
- 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)"));
15230
+ return /* @__PURE__ */ React27.createElement(Box24, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React27.createElement(Box24, null, /* @__PURE__ */ React27.createElement(Text21, { bold: true, color: "green" }, "assistant"), rec.cost !== void 0 ? /* @__PURE__ */ React27.createElement(Text21, { dimColor: true }, " $", rec.cost.toFixed(6)) : null, rec.usage ? /* @__PURE__ */ React27.createElement(CacheBadge, { usage: rec.usage }) : null), rec.planState ? /* @__PURE__ */ React27.createElement(PlanStateBlock, { planState: rec.planState }) : null, rec.content ? /* @__PURE__ */ React27.createElement(Text21, null, rec.content) : /* @__PURE__ */ React27.createElement(Text21, { dimColor: true, italic: true }, "(tool-call response only)"));
14327
15231
  }
14328
15232
  if (rec.role === "tool") {
14329
- 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)));
15233
+ return /* @__PURE__ */ React27.createElement(Box24, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React27.createElement(Text21, { color: "yellow" }, "tool<", rec.tool ?? "?", ">"), rec.args ? /* @__PURE__ */ React27.createElement(Text21, { dimColor: true }, " args: ", truncate2(rec.args, toolArgsMax)) : null, /* @__PURE__ */ React27.createElement(Text21, { dimColor: true }, " \u2192 ", truncate2(rec.content, toolContentMax)));
14330
15234
  }
14331
15235
  if (rec.role === "error") {
14332
- 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));
15236
+ return /* @__PURE__ */ React27.createElement(Box24, { marginTop: 1 }, /* @__PURE__ */ React27.createElement(Text21, { color: "red", bold: true }, "error", " "), /* @__PURE__ */ React27.createElement(Text21, { color: "red" }, rec.error ?? rec.content));
14333
15237
  }
14334
15238
  if (rec.role === "done" || rec.role === "assistant_delta") {
14335
15239
  return null;
14336
15240
  }
14337
- return /* @__PURE__ */ React24.createElement(Box22, null, /* @__PURE__ */ React24.createElement(Text21, { dimColor: true }, "[", rec.role, "] ", rec.content));
15241
+ return /* @__PURE__ */ React27.createElement(Box24, null, /* @__PURE__ */ React27.createElement(Text21, { dimColor: true }, "[", rec.role, "] ", rec.content));
14338
15242
  }
14339
15243
  function CacheBadge({ usage }) {
14340
15244
  const hit = usage.prompt_cache_hit_tokens ?? 0;
@@ -14343,7 +15247,7 @@ function CacheBadge({ usage }) {
14343
15247
  if (total === 0) return null;
14344
15248
  const pct2 = hit / total * 100;
14345
15249
  const color = pct2 >= 70 ? "green" : pct2 >= 40 ? "yellow" : "red";
14346
- return /* @__PURE__ */ React24.createElement(Text21, null, /* @__PURE__ */ React24.createElement(Text21, { dimColor: true }, " \xB7 cache "), /* @__PURE__ */ React24.createElement(Text21, { color }, pct2.toFixed(1), "%"));
15250
+ return /* @__PURE__ */ React27.createElement(Text21, null, /* @__PURE__ */ React27.createElement(Text21, { dimColor: true }, " \xB7 cache "), /* @__PURE__ */ React27.createElement(Text21, { color }, pct2.toFixed(1), "%"));
14347
15251
  }
14348
15252
  function truncate2(s, max) {
14349
15253
  return s.length <= max ? s : `${s.slice(0, max)}\u2026 (+${s.length - max} chars)`;
@@ -14355,7 +15259,7 @@ function DiffApp({ report }) {
14355
15259
  const maxIdx = Math.max(0, report.pairs.length - 1);
14356
15260
  const initialIdx = report.firstDivergenceTurn ? report.pairs.findIndex((p) => p.turn === report.firstDivergenceTurn) : 0;
14357
15261
  const [idx, setIdx] = useState13(Math.max(0, initialIdx));
14358
- useInput6((input, key) => {
15262
+ useInput((input, key) => {
14359
15263
  if (input === "q" || key.ctrl && input === "c") {
14360
15264
  exit2();
14361
15265
  return;
@@ -14377,7 +15281,7 @@ function DiffApp({ report }) {
14377
15281
  }
14378
15282
  });
14379
15283
  const pair = report.pairs[idx];
14380
- 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")));
15284
+ return /* @__PURE__ */ React28.createElement(Box25, { flexDirection: "column" }, /* @__PURE__ */ React28.createElement(DiffHeader, { report }), /* @__PURE__ */ React28.createElement(Box25, { marginTop: 1, paddingX: 1, justifyContent: "space-between" }, /* @__PURE__ */ React28.createElement(Text22, { color: "cyan", bold: true }, "turn ", pair?.turn ?? "?", " (", idx + 1, " / ", report.pairs.length, ")"), /* @__PURE__ */ React28.createElement(Text22, null, pair ? /* @__PURE__ */ React28.createElement(KindBadge, { kind: pair.kind }) : null)), /* @__PURE__ */ React28.createElement(Box25, { flexDirection: "row", marginTop: 1 }, /* @__PURE__ */ React28.createElement(Pane, { label: report.a.label, headerColor: "blue", records: paneRecords(pair, "a") }), /* @__PURE__ */ React28.createElement(Pane, { label: report.b.label, headerColor: "magenta", records: paneRecords(pair, "b") })), pair?.divergenceNote ? /* @__PURE__ */ React28.createElement(Box25, { marginTop: 1, paddingX: 1 }, /* @__PURE__ */ React28.createElement(Text22, { color: "yellow" }, "\u2605 "), /* @__PURE__ */ React28.createElement(Text22, null, pair.divergenceNote)) : null, /* @__PURE__ */ React28.createElement(Box25, { marginTop: 1, paddingX: 1, borderStyle: "single", borderColor: "gray" }, /* @__PURE__ */ React28.createElement(Text22, { dimColor: true }, /* @__PURE__ */ React28.createElement(Text22, { bold: true }, "j"), "/", /* @__PURE__ */ React28.createElement(Text22, { bold: true }, "\u2193"), " next \xB7 ", /* @__PURE__ */ React28.createElement(Text22, { bold: true }, "k"), "/", /* @__PURE__ */ React28.createElement(Text22, { bold: true }, "\u2191"), " ", "prev \xB7 ", /* @__PURE__ */ React28.createElement(Text22, { bold: true }, "n"), " next-diverge \xB7 ", /* @__PURE__ */ React28.createElement(Text22, { bold: true }, "N"), "/", /* @__PURE__ */ React28.createElement(Text22, { bold: true }, "p"), " ", "prev-diverge \xB7 ", /* @__PURE__ */ React28.createElement(Text22, { bold: true }, "g"), "/", /* @__PURE__ */ React28.createElement(Text22, { bold: true }, "G"), " first/last \xB7 ", /* @__PURE__ */ React28.createElement(Text22, { bold: true }, "q"), " ", "quit")));
14381
15285
  }
14382
15286
  function DiffHeader({ report }) {
14383
15287
  const a = report.a;
@@ -14395,15 +15299,15 @@ function DiffHeader({ report }) {
14395
15299
  } else if (a.stats.prefixHashes[0] && a.stats.prefixHashes[0] === b.stats.prefixHashes[0]) {
14396
15300
  prefixLine = `shared prefix hash ${a.stats.prefixHashes[0].slice(0, 12)}\u2026 \u2014 cache delta attributable to log stability, not prompt change.`;
14397
15301
  }
14398
- 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);
15302
+ return /* @__PURE__ */ React28.createElement(Box25, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React28.createElement(Box25, { justifyContent: "space-between" }, /* @__PURE__ */ React28.createElement(Text22, null, /* @__PURE__ */ React28.createElement(Text22, { color: "cyan", bold: true }, "reasonix diff"), /* @__PURE__ */ React28.createElement(Text22, { dimColor: true }, " \xB7 A="), /* @__PURE__ */ React28.createElement(Text22, { color: "blue" }, a.label), /* @__PURE__ */ React28.createElement(Text22, { dimColor: true }, " vs B="), /* @__PURE__ */ React28.createElement(Text22, { color: "magenta" }, b.label)), /* @__PURE__ */ React28.createElement(Text22, { dimColor: true }, report.pairs.length, " turns aligned")), /* @__PURE__ */ React28.createElement(Box25, { marginTop: 1, gap: 3 }, /* @__PURE__ */ React28.createElement(Text22, null, /* @__PURE__ */ React28.createElement(Text22, { dimColor: true }, "cache "), /* @__PURE__ */ React28.createElement(Text22, null, (a.stats.cacheHitRatio * 100).toFixed(1), "%"), /* @__PURE__ */ React28.createElement(Text22, { dimColor: true }, " \u2192 "), /* @__PURE__ */ React28.createElement(Text22, null, (b.stats.cacheHitRatio * 100).toFixed(1), "%"), /* @__PURE__ */ React28.createElement(Text22, { color: cacheDelta >= 0 ? "green" : "red", bold: true }, " ", cacheDelta >= 0 ? "+" : "", (cacheDelta * 100).toFixed(1), "pp")), /* @__PURE__ */ React28.createElement(Text22, null, /* @__PURE__ */ React28.createElement(Text22, { dimColor: true }, "cost "), /* @__PURE__ */ React28.createElement(Text22, null, "$", a.stats.totalCostUsd.toFixed(6)), /* @__PURE__ */ React28.createElement(Text22, { dimColor: true }, " \u2192 "), /* @__PURE__ */ React28.createElement(Text22, null, "$", b.stats.totalCostUsd.toFixed(6)), /* @__PURE__ */ React28.createElement(Text22, { color: costDelta2 <= 0 ? "green" : "red", bold: true }, " ", costDelta2 >= 0 ? "+" : "", costDelta2.toFixed(1), "%")), /* @__PURE__ */ React28.createElement(Text22, null, /* @__PURE__ */ React28.createElement(Text22, { dimColor: true }, "model calls "), /* @__PURE__ */ React28.createElement(Text22, null, a.stats.turns, " \u2192 ", b.stats.turns))), prefixLine ? /* @__PURE__ */ React28.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React28.createElement(Text22, { dimColor: true, italic: true }, prefixLine)) : null);
14399
15303
  }
14400
15304
  function Pane({
14401
15305
  label,
14402
15306
  headerColor,
14403
15307
  records
14404
15308
  }) {
14405
- return /* @__PURE__ */ React25.createElement(
14406
- Box23,
15309
+ return /* @__PURE__ */ React28.createElement(
15310
+ Box25,
14407
15311
  {
14408
15312
  flexDirection: "column",
14409
15313
  flexGrow: 1,
@@ -14411,21 +15315,21 @@ function Pane({
14411
15315
  borderStyle: "single",
14412
15316
  borderColor: headerColor
14413
15317
  },
14414
- /* @__PURE__ */ React25.createElement(Text22, { color: headerColor, bold: true }, label),
14415
- 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 }))
15318
+ /* @__PURE__ */ React28.createElement(Text22, { color: headerColor, bold: true }, label),
15319
+ records.length === 0 ? /* @__PURE__ */ React28.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React28.createElement(Text22, { dimColor: true, italic: true }, "(no records on this side for this turn)")) : /* @__PURE__ */ React28.createElement(Static2, { items: records.map((rec, i) => ({ key: `${label}-${i}`, rec })) }, ({ key, rec }) => /* @__PURE__ */ React28.createElement(RecordView, { key, rec, compact: true }))
14416
15320
  );
14417
15321
  }
14418
15322
  function KindBadge({ kind }) {
14419
15323
  if (kind === "match") {
14420
- return /* @__PURE__ */ React25.createElement(Text22, { color: "green" }, "\u2713 match");
15324
+ return /* @__PURE__ */ React28.createElement(Text22, { color: "green" }, "\u2713 match");
14421
15325
  }
14422
15326
  if (kind === "diverge") {
14423
- return /* @__PURE__ */ React25.createElement(Text22, { color: "yellow" }, "\u2605 diverge");
15327
+ return /* @__PURE__ */ React28.createElement(Text22, { color: "yellow" }, "\u2605 diverge");
14424
15328
  }
14425
15329
  if (kind === "only_in_a") {
14426
- return /* @__PURE__ */ React25.createElement(Text22, { color: "blue" }, "\u2190 only in A");
15330
+ return /* @__PURE__ */ React28.createElement(Text22, { color: "blue" }, "\u2190 only in A");
14427
15331
  }
14428
- return /* @__PURE__ */ React25.createElement(Text22, { color: "magenta" }, "\u2192 only in B");
15332
+ return /* @__PURE__ */ React28.createElement(Text22, { color: "magenta" }, "\u2192 only in B");
14429
15333
  }
14430
15334
  function paneRecords(pair, side) {
14431
15335
  if (!pair) return [];
@@ -14456,7 +15360,7 @@ markdown report written to ${opts.mdPath}`);
14456
15360
  return;
14457
15361
  }
14458
15362
  if (wantTui) {
14459
- const { waitUntilExit } = render2(React26.createElement(DiffApp, { report }), {
15363
+ const { waitUntilExit } = render2(React29.createElement(DiffApp, { report }), {
14460
15364
  exitOnCtrlC: true,
14461
15365
  patchConsole: false
14462
15366
  });
@@ -14597,16 +15501,16 @@ function pad2(s, width) {
14597
15501
 
14598
15502
  // src/cli/commands/replay.ts
14599
15503
  import { render as render3 } from "ink";
14600
- import React28 from "react";
15504
+ import React31 from "react";
14601
15505
 
14602
15506
  // src/cli/ui/ReplayApp.tsx
14603
- import { Box as Box24, Static as Static3, Text as Text23, useApp as useApp4, useInput as useInput7 } from "ink";
14604
- import React27, { useMemo as useMemo4, useState as useState14 } from "react";
15507
+ import { Box as Box26, Static as Static3, Text as Text23, useApp as useApp4, useInput as useInput2 } from "ink";
15508
+ import React30, { useMemo as useMemo4, useState as useState14 } from "react";
14605
15509
  function ReplayApp({ meta, pages }) {
14606
15510
  const { exit: exit2 } = useApp4();
14607
15511
  const maxIdx = Math.max(0, pages.length - 1);
14608
15512
  const [idx, setIdx] = useState14(maxIdx);
14609
- useInput7((input, key) => {
15513
+ useInput2((input, key) => {
14610
15514
  if (input === "q" || key.ctrl && input === "c") {
14611
15515
  exit2();
14612
15516
  return;
@@ -14641,14 +15545,14 @@ function ReplayApp({ meta, pages }) {
14641
15545
  const prefixHash = cumStats.prefixHashes.length === 1 ? cumStats.prefixHashes[0].slice(0, 16) : cumStats.prefixHashes.length === 0 ? "(untracked)" : `(churned \xD7${cumStats.prefixHashes.length})`;
14642
15546
  const currentPage = pages[idx];
14643
15547
  const progressLabel = pages.length === 0 ? "empty transcript" : `turn ${idx + 1} / ${pages.length}`;
14644
- return /* @__PURE__ */ React27.createElement(Box24, { flexDirection: "column" }, /* @__PURE__ */ React27.createElement(
15548
+ return /* @__PURE__ */ React30.createElement(Box26, { flexDirection: "column" }, /* @__PURE__ */ React30.createElement(
14645
15549
  StatsPanel,
14646
15550
  {
14647
15551
  summary,
14648
15552
  model: cumStats.models[0] ?? meta?.model ?? "?",
14649
15553
  prefixHash
14650
15554
  }
14651
- ), /* @__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")));
15555
+ ), /* @__PURE__ */ React30.createElement(Box26, { flexDirection: "column", marginTop: 1, paddingX: 1 }, /* @__PURE__ */ React30.createElement(Box26, { justifyContent: "space-between" }, /* @__PURE__ */ React30.createElement(Text23, { color: "cyan", bold: true }, progressLabel), meta ? /* @__PURE__ */ React30.createElement(Text23, { dimColor: true }, meta.source, meta.task ? ` \xB7 ${meta.task}` : "", meta.mode ? ` \xB7 ${meta.mode}` : "") : null), currentPage ? /* @__PURE__ */ React30.createElement(Static3, { items: currentPage.records.map((rec, i) => ({ key: `${idx}-${i}`, rec })) }, ({ key, rec }) => /* @__PURE__ */ React30.createElement(RecordView, { key, rec })) : /* @__PURE__ */ React30.createElement(Text23, { dimColor: true, italic: true }, "no records")), /* @__PURE__ */ React30.createElement(Box26, { marginTop: 1, paddingX: 1, borderStyle: "single", borderColor: "gray" }, /* @__PURE__ */ React30.createElement(Text23, { dimColor: true }, /* @__PURE__ */ React30.createElement(Text23, { bold: true }, "j"), "/", /* @__PURE__ */ React30.createElement(Text23, { bold: true }, "\u2193"), "/", /* @__PURE__ */ React30.createElement(Text23, { bold: true }, "space"), " next \xB7 ", /* @__PURE__ */ React30.createElement(Text23, { bold: true }, "k"), "/", /* @__PURE__ */ React30.createElement(Text23, { bold: true }, "\u2191"), " prev \xB7 ", /* @__PURE__ */ React30.createElement(Text23, { bold: true }, "g"), " first \xB7 ", /* @__PURE__ */ React30.createElement(Text23, { bold: true }, "G"), " last \xB7", " ", /* @__PURE__ */ React30.createElement(Text23, { bold: true }, "q"), " quit")));
14652
15556
  }
14653
15557
 
14654
15558
  // src/cli/commands/replay.ts
@@ -14660,7 +15564,7 @@ async function replayCommand(opts) {
14660
15564
  }
14661
15565
  const { parsed } = replayFromFile(opts.path);
14662
15566
  const pages = groupRecordsByTurn(parsed.records);
14663
- const { waitUntilExit } = render3(React28.createElement(ReplayApp, { meta: parsed.meta, pages }), {
15567
+ const { waitUntilExit } = render3(React31.createElement(ReplayApp, { meta: parsed.meta, pages }), {
14664
15568
  exitOnCtrlC: true,
14665
15569
  patchConsole: false
14666
15570
  });
@@ -14754,12 +15658,12 @@ function oneLine2(s, max = 200) {
14754
15658
  }
14755
15659
 
14756
15660
  // src/cli/commands/run.ts
14757
- import { stdin, stdout } from "process";
15661
+ import { stdin as stdin2, stdout } from "process";
14758
15662
  import { createInterface } from "readline/promises";
14759
15663
  async function ensureApiKey() {
14760
15664
  const existing = loadApiKey();
14761
15665
  if (existing) return existing;
14762
- if (!stdin.isTTY) {
15666
+ if (!stdin2.isTTY) {
14763
15667
  process.stderr.write(
14764
15668
  "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"
14765
15669
  );
@@ -14768,7 +15672,7 @@ async function ensureApiKey() {
14768
15672
  process.stdout.write(
14769
15673
  "DeepSeek API key not configured.\nGet one at https://platform.deepseek.com/api_keys\n"
14770
15674
  );
14771
- const rl = createInterface({ input: stdin, output: stdout });
15675
+ const rl = createInterface({ input: stdin2, output: stdout });
14772
15676
  try {
14773
15677
  while (true) {
14774
15678
  const answer = (await rl.question("API key \u203A ")).trim();
@@ -14965,12 +15869,12 @@ function truncate3(s, max) {
14965
15869
 
14966
15870
  // src/cli/commands/setup.tsx
14967
15871
  import { render as render4 } from "ink";
14968
- import React30 from "react";
15872
+ import React33 from "react";
14969
15873
 
14970
15874
  // src/cli/ui/Wizard.tsx
14971
- import { Box as Box25, Text as Text24, useApp as useApp5, useInput as useInput8 } from "ink";
15875
+ import { Box as Box27, Text as Text24, useApp as useApp5, useInput as useInput3 } from "ink";
14972
15876
  import TextInput2 from "ink-text-input";
14973
- import React29, { useState as useState15 } from "react";
15877
+ import React32, { useState as useState15 } from "react";
14974
15878
 
14975
15879
  // src/cli/ui/presets.ts
14976
15880
  var PRESETS = {
@@ -15014,11 +15918,11 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
15014
15918
  catalogArgs: {}
15015
15919
  });
15016
15920
  const [error, setError] = useState15(null);
15017
- useInput8((_input, key) => {
15921
+ useInput3((_input, key) => {
15018
15922
  if (key.escape && step !== "saved" && onCancel) onCancel();
15019
15923
  });
15020
15924
  if (step === "apiKey") {
15021
- return /* @__PURE__ */ React29.createElement(
15925
+ return /* @__PURE__ */ React32.createElement(
15022
15926
  ApiKeyStep,
15023
15927
  {
15024
15928
  onSubmit: (key) => {
@@ -15032,7 +15936,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
15032
15936
  );
15033
15937
  }
15034
15938
  if (step === "preset") {
15035
- return /* @__PURE__ */ React29.createElement(StepFrame, { title: "Pick a preset", step: 1, total: 3 }, /* @__PURE__ */ React29.createElement(
15939
+ return /* @__PURE__ */ React32.createElement(StepFrame, { title: "Pick a preset", step: 1, total: 3 }, /* @__PURE__ */ React32.createElement(
15036
15940
  SingleSelect,
15037
15941
  {
15038
15942
  items: presetItems(),
@@ -15042,10 +15946,10 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
15042
15946
  setStep("mcp");
15043
15947
  }
15044
15948
  }
15045
- ), /* @__PURE__ */ React29.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React29.createElement(Text24, { dimColor: true }, "[\u2191\u2193] navigate \xB7 [Enter] confirm \xB7 [Esc] cancel")));
15949
+ ), /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text24, { dimColor: true }, "[\u2191\u2193] navigate \xB7 [Enter] confirm \xB7 [Esc] cancel")));
15046
15950
  }
15047
15951
  if (step === "mcp") {
15048
- return /* @__PURE__ */ React29.createElement(StepFrame, { title: "Which MCP servers should Reasonix wire up for you?", step: 2, total: 3 }, /* @__PURE__ */ React29.createElement(
15952
+ return /* @__PURE__ */ React32.createElement(StepFrame, { title: "Which MCP servers should Reasonix wire up for you?", step: 2, total: 3 }, /* @__PURE__ */ React32.createElement(
15049
15953
  MultiSelect,
15050
15954
  {
15051
15955
  items: mcpItems(),
@@ -15070,7 +15974,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
15070
15974
  }
15071
15975
  const currentName = pending[0];
15072
15976
  const entry = CATALOG_BY_NAME.get(currentName);
15073
- return /* @__PURE__ */ React29.createElement(
15977
+ return /* @__PURE__ */ React32.createElement(
15074
15978
  McpArgsStep,
15075
15979
  {
15076
15980
  entry,
@@ -15088,7 +15992,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
15088
15992
  }
15089
15993
  if (step === "review") {
15090
15994
  const specs = data.selectedCatalog.map((name) => buildSpec(name, data.catalogArgs));
15091
- 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(
15995
+ return /* @__PURE__ */ React32.createElement(StepFrame, { title: "Ready to save", step: 3, total: 3 }, /* @__PURE__ */ React32.createElement(Box27, { flexDirection: "column" }, /* @__PURE__ */ React32.createElement(SummaryLine, { label: "API key", value: redactKey(data.apiKey) }), /* @__PURE__ */ React32.createElement(SummaryLine, { label: "Preset", value: data.preset }), /* @__PURE__ */ React32.createElement(
15092
15996
  SummaryLine,
15093
15997
  {
15094
15998
  label: "MCP",
@@ -15096,8 +16000,8 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
15096
16000
  }
15097
16001
  ), specs.map((spec, i) => (
15098
16002
  // biome-ignore lint/suspicious/noArrayIndexKey: review-only render, order fixed
15099
- /* @__PURE__ */ React29.createElement(Box25, { key: i, paddingLeft: 14 }, /* @__PURE__ */ React29.createElement(Text24, { dimColor: true }, "\xB7 ", spec))
15100
- )), /* @__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(
16003
+ /* @__PURE__ */ React32.createElement(Box27, { key: i, paddingLeft: 14 }, /* @__PURE__ */ React32.createElement(Text24, { dimColor: true }, "\xB7 ", spec))
16004
+ )), /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text24, null, "Saves to ", defaultConfigPath())), error ? /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text24, { color: "red" }, error)) : null, /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text24, { dimColor: true }, "[Enter] save \xB7 [Esc] cancel"))), /* @__PURE__ */ React32.createElement(
15101
16005
  ReviewConfirm,
15102
16006
  {
15103
16007
  onConfirm: () => {
@@ -15123,7 +16027,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
15123
16027
  }
15124
16028
  ));
15125
16029
  }
15126
- 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 }, "[Enter] to exit")), /* @__PURE__ */ React29.createElement(ExitOnEnter, { onExit: exit2 }));
16030
+ return /* @__PURE__ */ React32.createElement(Box27, { flexDirection: "column", borderStyle: "round", borderColor: "green", paddingX: 1 }, /* @__PURE__ */ React32.createElement(Text24, { bold: true, color: "green" }, "\u25B8 Saved."), /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text24, null, "Run `reasonix` any time to start chatting \u2014 your settings are remembered.")), /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text24, { dimColor: true }, "[Enter] to exit")), /* @__PURE__ */ React32.createElement(ExitOnEnter, { onExit: exit2 }));
15127
16031
  }
15128
16032
  function ApiKeyStep({
15129
16033
  onSubmit,
@@ -15131,7 +16035,7 @@ function ApiKeyStep({
15131
16035
  onError
15132
16036
  }) {
15133
16037
  const [value, setValue] = useState15("");
15134
- 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(
16038
+ return /* @__PURE__ */ React32.createElement(Box27, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React32.createElement(Text24, { bold: true, color: "cyan" }, "Welcome to Reasonix."), /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text24, null, "Paste your DeepSeek API key to get started.")), /* @__PURE__ */ React32.createElement(Text24, { dimColor: true }, "Get one (free credit on signup): https://platform.deepseek.com/api_keys"), /* @__PURE__ */ React32.createElement(Text24, { dimColor: true }, "Saved locally to ", defaultConfigPath()), /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text24, { bold: true, color: "cyan" }, "key \u203A "), /* @__PURE__ */ React32.createElement(
15135
16039
  TextInput2,
15136
16040
  {
15137
16041
  value,
@@ -15148,7 +16052,7 @@ function ApiKeyStep({
15148
16052
  mask: "\u2022",
15149
16053
  placeholder: "sk-..."
15150
16054
  }
15151
- )), 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);
16055
+ )), error ? /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text24, { color: "red" }, error)) : value ? /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text24, { dimColor: true }, "preview: ", redactKey(value))) : null);
15152
16056
  }
15153
16057
  function McpArgsStep({
15154
16058
  entry,
@@ -15157,7 +16061,7 @@ function McpArgsStep({
15157
16061
  onError
15158
16062
  }) {
15159
16063
  const [value, setValue] = useState15("");
15160
- 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(
16064
+ return /* @__PURE__ */ React32.createElement(StepFrame, { title: `Configure ${entry.name}`, step: 2, total: 3 }, /* @__PURE__ */ React32.createElement(Box27, { flexDirection: "column" }, /* @__PURE__ */ React32.createElement(Text24, null, entry.summary), entry.note ? /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text24, { dimColor: true }, entry.note)) : null, /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text24, null, "Required parameter: "), /* @__PURE__ */ React32.createElement(Text24, { bold: true }, entry.userArgs)), /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text24, { bold: true, color: "cyan" }, entry.userArgs, " \u203A "), /* @__PURE__ */ React32.createElement(
15161
16065
  TextInput2,
15162
16066
  {
15163
16067
  value,
@@ -15173,16 +16077,16 @@ function McpArgsStep({
15173
16077
  },
15174
16078
  placeholder: placeholderFor(entry)
15175
16079
  }
15176
- )), error ? /* @__PURE__ */ React29.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React29.createElement(Text24, { color: "red" }, error)) : null));
16080
+ )), error ? /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1 }, /* @__PURE__ */ React32.createElement(Text24, { color: "red" }, error)) : null));
15177
16081
  }
15178
16082
  function ReviewConfirm({ onConfirm }) {
15179
- useInput8((_i, key) => {
16083
+ useInput3((_i, key) => {
15180
16084
  if (key.return) onConfirm();
15181
16085
  });
15182
16086
  return null;
15183
16087
  }
15184
16088
  function ExitOnEnter({ onExit }) {
15185
- useInput8((_i, key) => {
16089
+ useInput3((_i, key) => {
15186
16090
  if (key.return) onExit();
15187
16091
  });
15188
16092
  return null;
@@ -15193,10 +16097,10 @@ function StepFrame({
15193
16097
  total,
15194
16098
  children
15195
16099
  }) {
15196
- 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));
16100
+ return /* @__PURE__ */ React32.createElement(Box27, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React32.createElement(Box27, null, /* @__PURE__ */ React32.createElement(Text24, { dimColor: true }, "Step ", step, "/", total, " \xB7", " "), /* @__PURE__ */ React32.createElement(Text24, { bold: true, color: "cyan" }, title)), /* @__PURE__ */ React32.createElement(Box27, { marginTop: 1, flexDirection: "column" }, children));
15197
16101
  }
15198
16102
  function SummaryLine({ label, value }) {
15199
- return /* @__PURE__ */ React29.createElement(Box25, null, /* @__PURE__ */ React29.createElement(Text24, null, label.padEnd(12)), /* @__PURE__ */ React29.createElement(Text24, { bold: true }, value));
16103
+ return /* @__PURE__ */ React32.createElement(Box27, null, /* @__PURE__ */ React32.createElement(Text24, null, label.padEnd(12)), /* @__PURE__ */ React32.createElement(Text24, { bold: true }, value));
15200
16104
  }
15201
16105
  function presetItems() {
15202
16106
  return ["fast", "smart", "max"].map((name) => ({
@@ -15252,7 +16156,7 @@ async function setupCommand(_opts = {}) {
15252
16156
  const existingKey = loadApiKey();
15253
16157
  const existing = readConfig();
15254
16158
  const { waitUntilExit, unmount } = render4(
15255
- /* @__PURE__ */ React30.createElement(
16159
+ /* @__PURE__ */ React33.createElement(
15256
16160
  Wizard,
15257
16161
  {
15258
16162
  existingApiKey: existingKey,