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