reasonix 0.7.0 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +695 -133
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +19 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -2663,6 +2663,11 @@ var CacheFirstLoop = class {
|
|
|
2663
2663
|
};
|
|
2664
2664
|
}
|
|
2665
2665
|
if (repairedCalls.length === 0) {
|
|
2666
|
+
const allSuppressed = report.stormsBroken > 0 && toolCalls.length > 0;
|
|
2667
|
+
if (allSuppressed) {
|
|
2668
|
+
yield* this.forceSummaryAfterIterLimit({ reason: "stuck" });
|
|
2669
|
+
return;
|
|
2670
|
+
}
|
|
2666
2671
|
this.autoCompactToolResultsOnTurnEnd();
|
|
2667
2672
|
yield { turn: this._turn, role: "done", content: assistantContent };
|
|
2668
2673
|
return;
|
|
@@ -2931,11 +2936,15 @@ function reasonPrefixFor(reason, iterCap) {
|
|
|
2931
2936
|
if (reason === "context-guard") {
|
|
2932
2937
|
return "[context budget running low \u2014 summarizing before the next call would overflow]";
|
|
2933
2938
|
}
|
|
2939
|
+
if (reason === "stuck") {
|
|
2940
|
+
return "[stuck on a repeated tool call \u2014 explaining what was tried and what's blocking progress]";
|
|
2941
|
+
}
|
|
2934
2942
|
return `[tool-call budget (${iterCap}) reached \u2014 forcing summary from what I found]`;
|
|
2935
2943
|
}
|
|
2936
2944
|
function errorLabelFor(reason, iterCap) {
|
|
2937
2945
|
if (reason === "aborted") return "aborted by user";
|
|
2938
2946
|
if (reason === "context-guard") return "context-guard triggered (prompt > 80% of window)";
|
|
2947
|
+
if (reason === "stuck") return "stuck (repeated tool call suppressed by storm-breaker)";
|
|
2939
2948
|
return `tool-call budget (${iterCap}) reached`;
|
|
2940
2949
|
}
|
|
2941
2950
|
function summarizeBranch(chosen, samples) {
|
|
@@ -5035,7 +5044,15 @@ async function runCommand(cmd, opts) {
|
|
|
5035
5044
|
shell: false,
|
|
5036
5045
|
// no shell-expansion — see header comment
|
|
5037
5046
|
windowsHide: true,
|
|
5038
|
-
|
|
5047
|
+
// PYTHONIOENCODING + PYTHONUTF8 force any spawned Python child
|
|
5048
|
+
// (run_command running `python script.py`, etc.) to emit UTF-8
|
|
5049
|
+
// on stdout/stderr. Without this, Chinese-Windows defaults
|
|
5050
|
+
// Python's stdout encoder to GBK and `print("…")` raises
|
|
5051
|
+
// UnicodeEncodeError on emoji / non-GBK chars — the model then
|
|
5052
|
+
// sees a Python traceback instead of the script's real output
|
|
5053
|
+
// and goes around in circles trying to fix the wrong problem.
|
|
5054
|
+
// Harmless on non-Python processes (env vars they don't read).
|
|
5055
|
+
env: { ...process.env, PYTHONIOENCODING: "utf-8", PYTHONUTF8: "1" }
|
|
5039
5056
|
};
|
|
5040
5057
|
const { bin, args, spawnOverrides } = prepareSpawn(argv);
|
|
5041
5058
|
const effectiveSpawnOpts = { ...spawnOpts, ...spawnOverrides };
|
|
@@ -5484,7 +5501,7 @@ function registerWebTools(registry, opts = {}) {
|
|
|
5484
5501
|
const maxFetchChars = opts.maxFetchChars ?? DEFAULT_FETCH_MAX_CHARS;
|
|
5485
5502
|
registry.register({
|
|
5486
5503
|
name: "web_search",
|
|
5487
|
-
description: "Search the public web. Returns ranked results with title, url, and snippet.
|
|
5504
|
+
description: "Search the public web. Returns ranked results with title, url, and snippet. Call this when the answer's correctness depends on current state \u2014 anything that changes over time (events, prices, releases, status of a thing in the real world). Composing such answers from training memory invents stale numbers; search first, then ground the answer in the results. For evergreen / definitional questions you don't need this.",
|
|
5488
5505
|
readOnly: true,
|
|
5489
5506
|
parameters: {
|
|
5490
5507
|
type: "object",
|
|
@@ -7099,7 +7116,7 @@ import { render } from "ink";
|
|
|
7099
7116
|
import React23, { useState as useState12 } from "react";
|
|
7100
7117
|
|
|
7101
7118
|
// src/cli/ui/App.tsx
|
|
7102
|
-
import { Box as Box19, Static, useApp, useInput as useInput5 } from "ink";
|
|
7119
|
+
import { Box as Box19, Static, useApp, useInput as useInput5, useStdout as useStdout5 } from "ink";
|
|
7103
7120
|
import React20, { useCallback as useCallback4, useEffect as useEffect5, useMemo as useMemo3, useRef as useRef5, useState as useState10 } from "react";
|
|
7104
7121
|
|
|
7105
7122
|
// src/code/pending-edits.ts
|
|
@@ -7402,7 +7419,7 @@ function AtMentionSuggestions({
|
|
|
7402
7419
|
}) {
|
|
7403
7420
|
if (matches === null) return null;
|
|
7404
7421
|
if (matches.length === 0) {
|
|
7405
|
-
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."));
|
|
7406
7423
|
}
|
|
7407
7424
|
const MAX = 8;
|
|
7408
7425
|
const total = matches.length;
|
|
@@ -7410,7 +7427,7 @@ function AtMentionSuggestions({
|
|
|
7410
7427
|
const shown = matches.slice(windowStart, windowStart + MAX);
|
|
7411
7428
|
const hiddenAbove = windowStart;
|
|
7412
7429
|
const hiddenBelow = total - windowStart - shown.length;
|
|
7413
|
-
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"));
|
|
7414
7431
|
}
|
|
7415
7432
|
function FileRow({ path, isSelected }) {
|
|
7416
7433
|
const marker = isSelected ? "\u25B8" : " ";
|
|
@@ -7705,7 +7722,7 @@ function EditConfirm({ block, onChoose }) {
|
|
|
7705
7722
|
const hiddenBelow = Math.max(0, allLines.length - effectiveScroll - budget);
|
|
7706
7723
|
const totalLines = allLines.length;
|
|
7707
7724
|
const showScrollHud = hiddenAbove + hiddenBelow > 0;
|
|
7708
|
-
return /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column", borderStyle: "round", borderColor: "
|
|
7725
|
+
return /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column", borderStyle: "round", borderColor: "yellow", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React4.createElement(Box4, null, /* @__PURE__ */ React4.createElement(Text4, { bold: true, color: "yellow" }, "\u25B8 model wants to edit a file")), /* @__PURE__ */ React4.createElement(Box4, null, /* @__PURE__ */ React4.createElement(Text4, { color: "yellow", dimColor: true }, "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")), /* @__PURE__ */ React4.createElement(Box4, { marginTop: 1 }, /* @__PURE__ */ React4.createElement(Text4, null, /* @__PURE__ */ React4.createElement(Text4, { color: isNew ? "green" : "yellow", bold: true }, `[${tag}] `), /* @__PURE__ */ React4.createElement(Text4, { color: "cyan" }, block.path), /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, ` (-${removed} +${added} lines)`), showScrollHud ? /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, ` \xB7 viewing ${effectiveScroll + 1}-${effectiveScroll + visibleLines.length}/${totalLines}`) : null)), hiddenAbove > 0 ? /* @__PURE__ */ React4.createElement(
|
|
7709
7726
|
Text4,
|
|
7710
7727
|
{
|
|
7711
7728
|
dimColor: true
|
|
@@ -7853,13 +7870,136 @@ function toSubscript(s) {
|
|
|
7853
7870
|
return out;
|
|
7854
7871
|
}
|
|
7855
7872
|
function stripMath(s) {
|
|
7856
|
-
return s.replace(
|
|
7873
|
+
return s.replace(/\$\$([\s\S]+?)\$\$/g, (_m, c) => `
|
|
7874
|
+
|
|
7875
|
+
${c.trim()}
|
|
7876
|
+
|
|
7877
|
+
`).replace(/(?<!\$)\$(?!\s)([^$\n]+?)(?<!\s)\$(?!\$)/g, "$1").replace(/\\\(\s*/g, "").replace(/\s*\\\)/g, "").replace(/\\\[\s*/g, "\n").replace(/\s*\\\]/g, "\n").replace(
|
|
7857
7878
|
/\\[dt]?frac\s*\{((?:[^{}]|\{[^{}]*\})+)\}\s*\{((?:[^{}]|\{[^{}]*\})+)\}/g,
|
|
7858
7879
|
(_m, num, den) => `(${num.trim()})/(${den.trim()})`
|
|
7859
7880
|
).replace(
|
|
7860
7881
|
/\\binom\s*\{([^{}]+)\}\s*\{([^{}]+)\}/g,
|
|
7861
7882
|
(_m, n, k) => `C(${n.trim()},${k.trim()})`
|
|
7862
|
-
).replace(/\\sqrt\s*\{([^{}]+)\}/g, (_m, g) => `\u221A(${g.trim()})`).replace(/\\boxed\s*\{([^{}]+)\}/g, (_m, g) => `\u3010${g.trim()}\u3011`).replace(/\\text\s*\{([^{}]+)\}/g, (_m, g) => g.trim()).replace(/\\overline\s*\{([^{}]+)\}/g, (_m, g) => `${g.trim()}\u0304`).replace(/\\hat\s*\{([^{}]+)\}/g, (_m, g) => `${g.trim()}\u0302`).replace(/\\vec\s*\{([^{}]+)\}/g, (_m, g) => `\u2192${g.trim()}`).replace(/\\cdot/g, "\xB7").replace(/\\times/g, "\xD7").replace(/\\div/g, "\xF7").replace(/\\pm/g, "\xB1").replace(/\\mp/g, "\u2213").replace(/\\leq/g, "\u2264").replace(/\\geq/g, "\u2265").replace(/\\neq/g, "\u2260").replace(/\\approx/g, "\u2248").replace(/\\in
|
|
7883
|
+
).replace(/\\sqrt\s*\{([^{}]+)\}/g, (_m, g) => `\u221A(${g.trim()})`).replace(/\\boxed\s*\{([^{}]+)\}/g, (_m, g) => `\u3010${g.trim()}\u3011`).replace(/\\text\s*\{([^{}]+)\}/g, (_m, g) => g.trim()).replace(/\\overline\s*\{([^{}]+)\}/g, (_m, g) => `${g.trim()}\u0304`).replace(/\\hat\s*\{([^{}]+)\}/g, (_m, g) => `${g.trim()}\u0302`).replace(/\\vec\s*\{([^{}]+)\}/g, (_m, g) => `\u2192${g.trim()}`).replace(/\\cdot/g, "\xB7").replace(/\\times/g, "\xD7").replace(/\\div/g, "\xF7").replace(/\\pm/g, "\xB1").replace(/\\mp/g, "\u2213").replace(/\\leq/g, "\u2264").replace(/\\geq/g, "\u2265").replace(/\\neq/g, "\u2260").replace(/\\approx/g, "\u2248").replace(/\\in(?![a-zA-Z])/g, "\u2208").replace(/\\notin(?![a-zA-Z])/g, "\u2209").replace(/\\infty/g, "\u221E").replace(/\\sum(?![a-zA-Z])/g, "\u03A3").replace(/\\prod(?![a-zA-Z])/g, "\u03A0").replace(/\\int(?![a-zA-Z])/g, "\u222B").replace(/\\alpha/g, "\u03B1").replace(/\\beta/g, "\u03B2").replace(/\\gamma/g, "\u03B3").replace(/\\delta/g, "\u03B4").replace(/\\theta/g, "\u03B8").replace(/\\lambda/g, "\u03BB").replace(/\\mu/g, "\u03BC").replace(/\\pi/g, "\u03C0").replace(/\\sigma/g, "\u03C3").replace(/\\phi/g, "\u03C6").replace(/\\omega/g, "\u03C9").replace(/\\implies(?![a-zA-Z])/g, "\u21D2").replace(/\\iff(?![a-zA-Z])/g, "\u21D4").replace(/\\to(?![a-zA-Z])/g, "\u2192").replace(/\\rightarrow/g, "\u2192").replace(/\\Rightarrow/g, "\u21D2").replace(/\\leftarrow/g, "\u2190").replace(/\\Leftarrow/g, "\u21D0").replace(/\\ldots/g, "\u2026").replace(/\\cdots/g, "\u22EF").replace(/\\quad/g, " ").replace(/\\qquad/g, " ").replace(/\\,/g, " ").replace(/\\;/g, " ").replace(/\\!/g, "").replace(/\\\\/g, "\n").replace(/\^([A-Za-z0-9+\-]+)\^/g, (m, g) => {
|
|
7884
|
+
for (const c of g) if (SUPERSCRIPT[c] === void 0) return m;
|
|
7885
|
+
return toSuperscript(g);
|
|
7886
|
+
}).replace(/(?<!~)~(?!~)([A-Za-z0-9+\-]+)~(?!~)/g, (m, g) => {
|
|
7887
|
+
for (const c of g) if (SUBSCRIPT[c] === void 0) return m;
|
|
7888
|
+
return toSubscript(g);
|
|
7889
|
+
}).replace(/\^\{([\w+-]+)\}/g, (_m, g) => toSuperscript(g)).replace(/\^([0-9+\-n])/g, (_m, g) => toSuperscript(g)).replace(/_\{([\w+-]+)\}/g, (_m, g) => toSubscript(g)).replace(/_([0-9+\-])/g, (_m, g) => toSubscript(g)).replace(/\\[a-zA-Z]+\s*\{([^{}]+)\}\s*\{([^{}]+)\}/g, "($1)/($2)").replace(/\\[a-zA-Z]+\s*\{([^{}]+)\}/g, "$1").replace(/\\[a-zA-Z]+/g, "").replace(/[ \t]{2,}/g, " ");
|
|
7890
|
+
}
|
|
7891
|
+
var EMOJI_MAP = {
|
|
7892
|
+
// faces
|
|
7893
|
+
smile: "\u{1F604}",
|
|
7894
|
+
smiley: "\u{1F603}",
|
|
7895
|
+
grin: "\u{1F601}",
|
|
7896
|
+
grinning: "\u{1F600}",
|
|
7897
|
+
joy: "\u{1F602}",
|
|
7898
|
+
laughing: "\u{1F606}",
|
|
7899
|
+
heart_eyes: "\u{1F60D}",
|
|
7900
|
+
blush: "\u{1F60A}",
|
|
7901
|
+
sunglasses: "\u{1F60E}",
|
|
7902
|
+
thinking: "\u{1F914}",
|
|
7903
|
+
neutral_face: "\u{1F610}",
|
|
7904
|
+
confused: "\u{1F615}",
|
|
7905
|
+
cry: "\u{1F622}",
|
|
7906
|
+
sob: "\u{1F62D}",
|
|
7907
|
+
rage: "\u{1F621}",
|
|
7908
|
+
angry: "\u{1F620}",
|
|
7909
|
+
scream: "\u{1F631}",
|
|
7910
|
+
wink: "\u{1F609}",
|
|
7911
|
+
kissing_heart: "\u{1F618}",
|
|
7912
|
+
// hearts
|
|
7913
|
+
heart: "\u2764\uFE0F",
|
|
7914
|
+
orange_heart: "\u{1F9E1}",
|
|
7915
|
+
yellow_heart: "\u{1F49B}",
|
|
7916
|
+
green_heart: "\u{1F49A}",
|
|
7917
|
+
blue_heart: "\u{1F499}",
|
|
7918
|
+
purple_heart: "\u{1F49C}",
|
|
7919
|
+
black_heart: "\u{1F5A4}",
|
|
7920
|
+
white_heart: "\u{1F90D}",
|
|
7921
|
+
broken_heart: "\u{1F494}",
|
|
7922
|
+
sparkling_heart: "\u{1F496}",
|
|
7923
|
+
two_hearts: "\u{1F495}",
|
|
7924
|
+
// gestures
|
|
7925
|
+
"+1": "\u{1F44D}",
|
|
7926
|
+
"-1": "\u{1F44E}",
|
|
7927
|
+
thumbsup: "\u{1F44D}",
|
|
7928
|
+
thumbsdown: "\u{1F44E}",
|
|
7929
|
+
wave: "\u{1F44B}",
|
|
7930
|
+
clap: "\u{1F44F}",
|
|
7931
|
+
muscle: "\u{1F4AA}",
|
|
7932
|
+
ok_hand: "\u{1F44C}",
|
|
7933
|
+
pray: "\u{1F64F}",
|
|
7934
|
+
fist: "\u270A",
|
|
7935
|
+
point_up: "\u261D\uFE0F",
|
|
7936
|
+
raised_hands: "\u{1F64C}",
|
|
7937
|
+
handshake: "\u{1F91D}",
|
|
7938
|
+
// symbols / signals
|
|
7939
|
+
rocket: "\u{1F680}",
|
|
7940
|
+
fire: "\u{1F525}",
|
|
7941
|
+
star: "\u2B50",
|
|
7942
|
+
star2: "\u{1F31F}",
|
|
7943
|
+
sparkles: "\u2728",
|
|
7944
|
+
boom: "\u{1F4A5}",
|
|
7945
|
+
zap: "\u26A1",
|
|
7946
|
+
tada: "\u{1F389}",
|
|
7947
|
+
bulb: "\u{1F4A1}",
|
|
7948
|
+
warning: "\u26A0\uFE0F",
|
|
7949
|
+
x: "\u274C",
|
|
7950
|
+
white_check_mark: "\u2705",
|
|
7951
|
+
heavy_check_mark: "\u2714\uFE0F",
|
|
7952
|
+
ballot_box_with_check: "\u2611\uFE0F",
|
|
7953
|
+
no_entry: "\u26D4",
|
|
7954
|
+
question: "\u2753",
|
|
7955
|
+
exclamation: "\u2757",
|
|
7956
|
+
bangbang: "\u203C\uFE0F",
|
|
7957
|
+
bell: "\u{1F514}",
|
|
7958
|
+
mute: "\u{1F515}",
|
|
7959
|
+
hundred: "\u{1F4AF}",
|
|
7960
|
+
"100": "\u{1F4AF}",
|
|
7961
|
+
eyes: "\u{1F440}",
|
|
7962
|
+
// tech / productivity
|
|
7963
|
+
computer: "\u{1F4BB}",
|
|
7964
|
+
iphone: "\u{1F4F1}",
|
|
7965
|
+
hammer: "\u{1F528}",
|
|
7966
|
+
wrench: "\u{1F527}",
|
|
7967
|
+
gear: "\u2699\uFE0F",
|
|
7968
|
+
package: "\u{1F4E6}",
|
|
7969
|
+
floppy_disk: "\u{1F4BE}",
|
|
7970
|
+
key: "\u{1F511}",
|
|
7971
|
+
lock: "\u{1F512}",
|
|
7972
|
+
unlock: "\u{1F513}",
|
|
7973
|
+
mag: "\u{1F50D}",
|
|
7974
|
+
memo: "\u{1F4DD}",
|
|
7975
|
+
pencil: "\u270F\uFE0F",
|
|
7976
|
+
bookmark: "\u{1F516}",
|
|
7977
|
+
// charts / time
|
|
7978
|
+
chart_with_upwards_trend: "\u{1F4C8}",
|
|
7979
|
+
chart_with_downwards_trend: "\u{1F4C9}",
|
|
7980
|
+
bar_chart: "\u{1F4CA}",
|
|
7981
|
+
hourglass: "\u23F3",
|
|
7982
|
+
calendar: "\u{1F4C5}",
|
|
7983
|
+
// misc
|
|
7984
|
+
robot: "\u{1F916}",
|
|
7985
|
+
ghost: "\u{1F47B}",
|
|
7986
|
+
bug: "\u{1F41B}",
|
|
7987
|
+
coffee: "\u2615",
|
|
7988
|
+
beer: "\u{1F37A}",
|
|
7989
|
+
sun: "\u2600\uFE0F",
|
|
7990
|
+
cloud: "\u2601\uFE0F",
|
|
7991
|
+
rainbow: "\u{1F308}",
|
|
7992
|
+
speech_balloon: "\u{1F4AC}",
|
|
7993
|
+
thought_balloon: "\u{1F4AD}",
|
|
7994
|
+
construction: "\u{1F6A7}"
|
|
7995
|
+
};
|
|
7996
|
+
function expandAutolinks(s) {
|
|
7997
|
+
return s.replace(/<((?:https?|ftp|mailto):[^\s<>]+)>/g, "[$1]($1)");
|
|
7998
|
+
}
|
|
7999
|
+
function expandEmoji(s) {
|
|
8000
|
+
return s.replace(/:([a-z0-9_+-]+):/gi, (m, name) => {
|
|
8001
|
+
return EMOJI_MAP[name.toLowerCase()] ?? m;
|
|
8002
|
+
});
|
|
7863
8003
|
}
|
|
7864
8004
|
function isExternalUrl(url) {
|
|
7865
8005
|
return /^[a-z][a-z0-9+.-]*:\/\//i.test(url) || url.startsWith("mailto:") || url.startsWith("//");
|
|
@@ -7914,18 +8054,25 @@ function validateCitation(url, projectRoot) {
|
|
|
7914
8054
|
}
|
|
7915
8055
|
return { ok: true };
|
|
7916
8056
|
}
|
|
8057
|
+
function shouldValidateAsCitation(url) {
|
|
8058
|
+
if (url.startsWith("#")) return false;
|
|
8059
|
+
if (url === "/" || url === "\\" || url === "") return false;
|
|
8060
|
+
if (!/[/\\.]/.test(url)) return false;
|
|
8061
|
+
return true;
|
|
8062
|
+
}
|
|
7917
8063
|
function collectCitations(text, projectRoot) {
|
|
7918
8064
|
const map = /* @__PURE__ */ new Map();
|
|
7919
8065
|
const re = /\[([^\]\n]+)\]\(([^)\n]+)\)/g;
|
|
7920
8066
|
for (const m of text.matchAll(re)) {
|
|
7921
8067
|
const url = m[2] ?? "";
|
|
7922
8068
|
if (!url || isExternalUrl(url)) continue;
|
|
8069
|
+
if (!shouldValidateAsCitation(url)) continue;
|
|
7923
8070
|
if (map.has(url)) continue;
|
|
7924
8071
|
map.set(url, validateCitation(url, projectRoot));
|
|
7925
8072
|
}
|
|
7926
8073
|
return map;
|
|
7927
8074
|
}
|
|
7928
|
-
var INLINE_RE = /(\[([^\]\n]+)\]\(([^)\n]+)\)
|
|
8075
|
+
var INLINE_RE = /(\[([^\]\n]+)\]\(([^)\n]+)\)|\*\*\*([^*\n]+?)\*\*\*|\*\*([^*\n]+?)\*\*|```([^\n]+?)```|`([^`\n]+?)`|~~([^~\n]+?)~~|(?<![*\w])\*([^*\n]+?)\*(?!\w)|\\([*_~`[\](){}#+\-.!\\]))/g;
|
|
7929
8076
|
function InlineMd({
|
|
7930
8077
|
text,
|
|
7931
8078
|
padTo,
|
|
@@ -7960,21 +8107,31 @@ function InlineMd({
|
|
|
7960
8107
|
}
|
|
7961
8108
|
} else if (m[4] !== void 0) {
|
|
7962
8109
|
parts.push(
|
|
7963
|
-
/* @__PURE__ */ React7.createElement(Text7, { key: `
|
|
8110
|
+
/* @__PURE__ */ React7.createElement(Text7, { key: `bi${idx++}`, bold: true, italic: true }, m[4])
|
|
7964
8111
|
);
|
|
7965
8112
|
} else if (m[5] !== void 0) {
|
|
7966
|
-
const stripped = m[5].replace(/^(\w+)\s+/, "");
|
|
7967
8113
|
parts.push(
|
|
7968
|
-
/* @__PURE__ */ React7.createElement(Text7, { key: `
|
|
8114
|
+
/* @__PURE__ */ React7.createElement(Text7, { key: `b${idx++}`, bold: true }, m[5])
|
|
7969
8115
|
);
|
|
7970
8116
|
} else if (m[6] !== void 0) {
|
|
8117
|
+
const stripped = m[6].replace(/^(\w+)\s+/, "");
|
|
7971
8118
|
parts.push(
|
|
7972
|
-
/* @__PURE__ */ React7.createElement(Text7, { key: `c${idx++}`, color: "yellow" },
|
|
8119
|
+
/* @__PURE__ */ React7.createElement(Text7, { key: `c${idx++}`, color: "yellow" }, stripped)
|
|
7973
8120
|
);
|
|
7974
8121
|
} else if (m[7] !== void 0) {
|
|
7975
8122
|
parts.push(
|
|
7976
|
-
/* @__PURE__ */ React7.createElement(Text7, { key: `
|
|
8123
|
+
/* @__PURE__ */ React7.createElement(Text7, { key: `c${idx++}`, color: "yellow" }, m[7])
|
|
8124
|
+
);
|
|
8125
|
+
} else if (m[8] !== void 0) {
|
|
8126
|
+
parts.push(
|
|
8127
|
+
/* @__PURE__ */ React7.createElement(Text7, { key: `s${idx++}`, strikethrough: true, dimColor: true }, m[8])
|
|
8128
|
+
);
|
|
8129
|
+
} else if (m[9] !== void 0) {
|
|
8130
|
+
parts.push(
|
|
8131
|
+
/* @__PURE__ */ React7.createElement(Text7, { key: `i${idx++}`, italic: true }, m[9])
|
|
7977
8132
|
);
|
|
8133
|
+
} else if (m[10] !== void 0) {
|
|
8134
|
+
parts.push(/* @__PURE__ */ React7.createElement(Text7, { key: `esc${idx++}` }, m[10]));
|
|
7978
8135
|
}
|
|
7979
8136
|
last = start + m[0].length;
|
|
7980
8137
|
}
|
|
@@ -7990,7 +8147,20 @@ function InlineMd({
|
|
|
7990
8147
|
return /* @__PURE__ */ React7.createElement(Text7, null, parts);
|
|
7991
8148
|
}
|
|
7992
8149
|
function stripInlineMarkup(s) {
|
|
7993
|
-
return s.replace(
|
|
8150
|
+
return s.replace(
|
|
8151
|
+
INLINE_RE,
|
|
8152
|
+
(match, _alt, linkText, _url, boldItalic, bold, code3, code1, strike, italic, escapeChar) => {
|
|
8153
|
+
if (linkText !== void 0) return linkText;
|
|
8154
|
+
if (boldItalic !== void 0) return boldItalic;
|
|
8155
|
+
if (bold !== void 0) return bold;
|
|
8156
|
+
if (code3 !== void 0) return code3.replace(/^(\w+)\s+/, "");
|
|
8157
|
+
if (code1 !== void 0) return code1;
|
|
8158
|
+
if (strike !== void 0) return strike;
|
|
8159
|
+
if (italic !== void 0) return italic;
|
|
8160
|
+
if (escapeChar !== void 0) return escapeChar;
|
|
8161
|
+
return match;
|
|
8162
|
+
}
|
|
8163
|
+
);
|
|
7994
8164
|
}
|
|
7995
8165
|
function visibleWidth(s) {
|
|
7996
8166
|
return displayWidth(stripInlineMarkup(s));
|
|
@@ -8005,10 +8175,16 @@ function parseBlocks(raw) {
|
|
|
8005
8175
|
let listBuf = null;
|
|
8006
8176
|
let codeFence = "";
|
|
8007
8177
|
const flushPara = () => {
|
|
8008
|
-
if (para.length)
|
|
8009
|
-
|
|
8010
|
-
|
|
8178
|
+
if (para.length === 0) return;
|
|
8179
|
+
let joined = "";
|
|
8180
|
+
for (let k = 0; k < para.length; k++) {
|
|
8181
|
+
joined += para[k].text;
|
|
8182
|
+
if (k < para.length - 1) {
|
|
8183
|
+
joined += para[k].hardBreak ? "\n" : " ";
|
|
8184
|
+
}
|
|
8011
8185
|
}
|
|
8186
|
+
out.push({ kind: "paragraph", text: joined });
|
|
8187
|
+
para = [];
|
|
8012
8188
|
};
|
|
8013
8189
|
const flushList = () => {
|
|
8014
8190
|
if (listBuf) {
|
|
@@ -8020,7 +8196,7 @@ function parseBlocks(raw) {
|
|
|
8020
8196
|
const rawLine = lines[i];
|
|
8021
8197
|
const line = rawLine.replace(/\s+$/g, "");
|
|
8022
8198
|
if (!inCode && /^<{7} SEARCH\s*$/.test(line)) {
|
|
8023
|
-
const filename = para.pop()?.trim();
|
|
8199
|
+
const filename = para.pop()?.text.trim();
|
|
8024
8200
|
if (filename) {
|
|
8025
8201
|
flushPara();
|
|
8026
8202
|
flushList();
|
|
@@ -8046,7 +8222,7 @@ function parseBlocks(raw) {
|
|
|
8046
8222
|
i = k;
|
|
8047
8223
|
continue;
|
|
8048
8224
|
}
|
|
8049
|
-
para.push(filename);
|
|
8225
|
+
para.push({ text: filename, hardBreak: false });
|
|
8050
8226
|
}
|
|
8051
8227
|
}
|
|
8052
8228
|
if (!inCode) {
|
|
@@ -8150,6 +8326,23 @@ function parseBlocks(raw) {
|
|
|
8150
8326
|
continue;
|
|
8151
8327
|
}
|
|
8152
8328
|
}
|
|
8329
|
+
const quoteMatch = line.match(/^\s*>\s?(.*)$/);
|
|
8330
|
+
if (quoteMatch) {
|
|
8331
|
+
flushPara();
|
|
8332
|
+
flushList();
|
|
8333
|
+
const innerLines = [quoteMatch[1] ?? ""];
|
|
8334
|
+
let j = i + 1;
|
|
8335
|
+
while (j < lines.length) {
|
|
8336
|
+
const nxt = lines[j].replace(/\s+$/g, "");
|
|
8337
|
+
const m = nxt.match(/^\s*>\s?(.*)$/);
|
|
8338
|
+
if (!m) break;
|
|
8339
|
+
innerLines.push(m[1] ?? "");
|
|
8340
|
+
j++;
|
|
8341
|
+
}
|
|
8342
|
+
out.push({ kind: "quote", children: parseBlocks(innerLines.join("\n")) });
|
|
8343
|
+
i = j - 1;
|
|
8344
|
+
continue;
|
|
8345
|
+
}
|
|
8153
8346
|
const bm = line.match(/^\s*[-*+]\s+(.+)$/);
|
|
8154
8347
|
if (bm) {
|
|
8155
8348
|
flushPara();
|
|
@@ -8157,7 +8350,7 @@ function parseBlocks(raw) {
|
|
|
8157
8350
|
flushList();
|
|
8158
8351
|
listBuf = { kind: "bullet", items: [], ordered: false, start: 1 };
|
|
8159
8352
|
}
|
|
8160
|
-
listBuf.items.push(bm[1]);
|
|
8353
|
+
listBuf.items.push(parseBulletItem(bm[1]));
|
|
8161
8354
|
continue;
|
|
8162
8355
|
}
|
|
8163
8356
|
const om = line.match(/^\s*(\d+)\.\s+(.+)$/);
|
|
@@ -8167,11 +8360,12 @@ function parseBlocks(raw) {
|
|
|
8167
8360
|
flushList();
|
|
8168
8361
|
listBuf = { kind: "bullet", items: [], ordered: true, start: Number(om[1]) };
|
|
8169
8362
|
}
|
|
8170
|
-
listBuf.items.push(om[2]);
|
|
8363
|
+
listBuf.items.push(parseBulletItem(om[2]));
|
|
8171
8364
|
continue;
|
|
8172
8365
|
}
|
|
8173
8366
|
flushList();
|
|
8174
|
-
|
|
8367
|
+
const hardBreak = / {2,}\r?$/.test(rawLine);
|
|
8368
|
+
para.push({ text: line, hardBreak });
|
|
8175
8369
|
}
|
|
8176
8370
|
if (inCode && codeBuf.length) {
|
|
8177
8371
|
out.push({ kind: "code", lang: codeLang, text: codeBuf.join("\n") });
|
|
@@ -8180,15 +8374,26 @@ function parseBlocks(raw) {
|
|
|
8180
8374
|
flushList();
|
|
8181
8375
|
return out;
|
|
8182
8376
|
}
|
|
8377
|
+
function parseBulletItem(raw) {
|
|
8378
|
+
const m = raw.match(/^\[([ xX])\]\s+(.*)$/);
|
|
8379
|
+
if (!m) return { text: raw };
|
|
8380
|
+
const done = m[1].toLowerCase() === "x";
|
|
8381
|
+
return { text: m[2] ?? "", task: done ? "done" : "todo" };
|
|
8382
|
+
}
|
|
8183
8383
|
function BlockView({ block, citations }) {
|
|
8184
8384
|
switch (block.kind) {
|
|
8185
8385
|
case "heading":
|
|
8186
8386
|
return /* @__PURE__ */ React7.createElement(Text7, { bold: true, color: "cyan" }, /* @__PURE__ */ React7.createElement(InlineMd, { text: block.text, citations }));
|
|
8187
8387
|
case "paragraph":
|
|
8188
|
-
return /* @__PURE__ */ React7.createElement(
|
|
8388
|
+
return /* @__PURE__ */ React7.createElement(ParagraphView, { text: block.text, citations });
|
|
8189
8389
|
case "bullet":
|
|
8190
|
-
return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column" }, block.items.map((item, i) => /* @__PURE__ */ React7.createElement(Box7, { key: `${i}-${item.slice(0, 24)}` }, /* @__PURE__ */ React7.createElement(Text7, { color: "cyan" }, block.
|
|
8390
|
+
return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column" }, block.items.map((item, i) => /* @__PURE__ */ React7.createElement(Box7, { key: `${i}-${item.text.slice(0, 24)}` }, /* @__PURE__ */ React7.createElement(Text7, { color: item.task === "done" ? "green" : "cyan" }, bulletPrefix(block, i, item)), item.task === "done" ? /* @__PURE__ */ React7.createElement(Text7, { strikethrough: true, dimColor: true }, /* @__PURE__ */ React7.createElement(InlineMd, { text: item.text, citations })) : /* @__PURE__ */ React7.createElement(InlineMd, { text: item.text, citations }))));
|
|
8391
|
+
case "quote":
|
|
8392
|
+
return /* @__PURE__ */ React7.createElement(BlockquoteView, { block, citations });
|
|
8191
8393
|
case "code":
|
|
8394
|
+
if (DIAGRAM_LANGS.has(block.lang.toLowerCase())) {
|
|
8395
|
+
return /* @__PURE__ */ React7.createElement(DiagramCodeBlock, { lang: block.lang, text: block.text });
|
|
8396
|
+
}
|
|
8192
8397
|
return /* @__PURE__ */ React7.createElement(Box7, { borderStyle: "single", borderColor: "gray", paddingX: 1 }, /* @__PURE__ */ React7.createElement(Text7, { color: "yellow" }, block.text));
|
|
8193
8398
|
case "edit-block":
|
|
8194
8399
|
return /* @__PURE__ */ React7.createElement(EditBlockRow, { block });
|
|
@@ -8198,6 +8403,42 @@ function BlockView({ block, citations }) {
|
|
|
8198
8403
|
return /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
8199
8404
|
}
|
|
8200
8405
|
}
|
|
8406
|
+
function ParagraphView({ text, citations }) {
|
|
8407
|
+
if (!text.includes("\n")) {
|
|
8408
|
+
return /* @__PURE__ */ React7.createElement(InlineMd, { text, citations });
|
|
8409
|
+
}
|
|
8410
|
+
const rows = text.split("\n");
|
|
8411
|
+
return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column" }, rows.map((row2, i) => (
|
|
8412
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: hard-break rows are source-ordered and never reorder
|
|
8413
|
+
/* @__PURE__ */ React7.createElement(InlineMd, { key: `ln-${i}`, text: row2, citations })
|
|
8414
|
+
)));
|
|
8415
|
+
}
|
|
8416
|
+
function bulletPrefix(block, i, item) {
|
|
8417
|
+
if (block.ordered) return ` ${block.start + i}. `;
|
|
8418
|
+
if (item.task === "done") return " \u2612 ";
|
|
8419
|
+
if (item.task === "todo") return " \u2610 ";
|
|
8420
|
+
return " \u2022 ";
|
|
8421
|
+
}
|
|
8422
|
+
function BlockquoteView({
|
|
8423
|
+
block,
|
|
8424
|
+
citations
|
|
8425
|
+
}) {
|
|
8426
|
+
return /* @__PURE__ */ React7.createElement(
|
|
8427
|
+
Box7,
|
|
8428
|
+
{
|
|
8429
|
+
borderStyle: "single",
|
|
8430
|
+
borderColor: "gray",
|
|
8431
|
+
borderDimColor: true,
|
|
8432
|
+
borderTop: false,
|
|
8433
|
+
borderRight: false,
|
|
8434
|
+
borderBottom: false,
|
|
8435
|
+
paddingLeft: 1,
|
|
8436
|
+
flexDirection: "column",
|
|
8437
|
+
gap: 1
|
|
8438
|
+
},
|
|
8439
|
+
block.children.map((child, i) => /* @__PURE__ */ React7.createElement(BlockView, { key: `q-${i}-${child.kind}`, block: child, citations }))
|
|
8440
|
+
);
|
|
8441
|
+
}
|
|
8201
8442
|
function splitTableRow(line) {
|
|
8202
8443
|
const SENTINEL = "\0";
|
|
8203
8444
|
const masked = line.replace(/\\\|/g, SENTINEL).replace(/│/g, "|");
|
|
@@ -8242,8 +8483,30 @@ function EditBlockRow({ block }) {
|
|
|
8242
8483
|
const replaceLines = block.replace.split("\n");
|
|
8243
8484
|
return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { bold: true, color: "cyan" }, block.filename), isNewFile ? /* @__PURE__ */ React7.createElement(Text7, { color: "green", bold: true }, " (new file)") : null), isNewFile ? null : /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", marginTop: 1 }, searchLines.map((line, i) => /* @__PURE__ */ React7.createElement(Text7, { key: `s-${i}-${line.length}`, color: "red" }, `- ${line}`))), /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", marginTop: isNewFile ? 1 : 0 }, replaceLines.map((line, i) => /* @__PURE__ */ React7.createElement(Text7, { key: `r-${i}-${line.length}`, color: "green" }, `+ ${line}`))));
|
|
8244
8485
|
}
|
|
8486
|
+
var DIAGRAM_LANGS = /* @__PURE__ */ new Set([
|
|
8487
|
+
"mermaid",
|
|
8488
|
+
"dot",
|
|
8489
|
+
"graphviz",
|
|
8490
|
+
"plantuml",
|
|
8491
|
+
"puml",
|
|
8492
|
+
"flowchart",
|
|
8493
|
+
"sequencediagram",
|
|
8494
|
+
"gantt",
|
|
8495
|
+
"erdiagram"
|
|
8496
|
+
]);
|
|
8497
|
+
var DIAGRAM_VIEWER_HINT = {
|
|
8498
|
+
mermaid: "\u2192 paste at https://mermaid.live to view",
|
|
8499
|
+
plantuml: "\u2192 paste at https://www.plantuml.com/plantuml to view",
|
|
8500
|
+
puml: "\u2192 paste at https://www.plantuml.com/plantuml to view",
|
|
8501
|
+
dot: "\u2192 paste at https://dreampuf.github.io/GraphvizOnline to view",
|
|
8502
|
+
graphviz: "\u2192 paste at https://dreampuf.github.io/GraphvizOnline to view"
|
|
8503
|
+
};
|
|
8504
|
+
function DiagramCodeBlock({ lang, text }) {
|
|
8505
|
+
const hint = DIAGRAM_VIEWER_HINT[lang.toLowerCase()] ?? "\u2192 render with the matching viewer to view";
|
|
8506
|
+
return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", borderStyle: "double", borderColor: "magenta", paddingX: 1 }, /* @__PURE__ */ React7.createElement(Text7, { bold: true, color: "magenta" }, `\u25C7 ${lang} diagram (source \u2014 terminal can't draw the graph)`), /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1 }, /* @__PURE__ */ React7.createElement(Text7, { color: "yellow" }, text)), /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1 }, /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, hint)));
|
|
8507
|
+
}
|
|
8245
8508
|
function Markdown({ text, projectRoot }) {
|
|
8246
|
-
const cleaned = stripMath(text);
|
|
8509
|
+
const cleaned = expandAutolinks(expandEmoji(stripMath(text)));
|
|
8247
8510
|
const root = projectRoot ?? process.cwd();
|
|
8248
8511
|
const citations = React7.useMemo(() => collectCitations(cleaned, root), [cleaned, root]);
|
|
8249
8512
|
const blocks = React7.useMemo(() => parseBlocks(cleaned), [cleaned]);
|
|
@@ -8343,7 +8606,8 @@ function summarizeStructured(content) {
|
|
|
8343
8606
|
}
|
|
8344
8607
|
}
|
|
8345
8608
|
function summarizeKnownTool(toolName, content) {
|
|
8346
|
-
|
|
8609
|
+
const hasSuffix = (s) => toolName === s || toolName.endsWith(`_${s}`);
|
|
8610
|
+
if (hasSuffix("read_file")) {
|
|
8347
8611
|
const lines = formatLineCount(content);
|
|
8348
8612
|
const bytes = formatBytes(content.length);
|
|
8349
8613
|
const head = clip(
|
|
@@ -8355,11 +8619,11 @@ function summarizeKnownTool(toolName, content) {
|
|
|
8355
8619
|
isError: false
|
|
8356
8620
|
};
|
|
8357
8621
|
}
|
|
8358
|
-
if (
|
|
8622
|
+
if (hasSuffix("list_directory") || hasSuffix("directory_tree")) {
|
|
8359
8623
|
const entries = content.split(/\r?\n/).filter((l) => l.trim()).length;
|
|
8360
8624
|
return { summary: `${entries} entr${entries === 1 ? "y" : "ies"}`, isError: false };
|
|
8361
8625
|
}
|
|
8362
|
-
if (
|
|
8626
|
+
if (hasSuffix("search_files") || hasSuffix("search_content")) {
|
|
8363
8627
|
const matches = content.split(/\r?\n/).filter((l) => l.trim()).length;
|
|
8364
8628
|
if (matches === 0) return { summary: "no matches", isError: false };
|
|
8365
8629
|
const first = firstNonEmptyLine(content);
|
|
@@ -8368,7 +8632,12 @@ function summarizeKnownTool(toolName, content) {
|
|
|
8368
8632
|
isError: false
|
|
8369
8633
|
};
|
|
8370
8634
|
}
|
|
8371
|
-
if (
|
|
8635
|
+
if (hasSuffix("write_file")) {
|
|
8636
|
+
const lines = formatLineCount(content);
|
|
8637
|
+
const bytes = formatBytes(content.length);
|
|
8638
|
+
return { summary: `wrote ${lines} \xB7 ${bytes}`, isError: false };
|
|
8639
|
+
}
|
|
8640
|
+
if (hasSuffix("run_command") || hasSuffix("run_background")) {
|
|
8372
8641
|
const exitMatch = content.match(/exit (?:code )?(-?\d+)/i);
|
|
8373
8642
|
const first = firstNonEmptyLine(content);
|
|
8374
8643
|
if (exitMatch) {
|
|
@@ -8420,12 +8689,16 @@ function RoleGlyph({
|
|
|
8420
8689
|
}) {
|
|
8421
8690
|
return /* @__PURE__ */ React9.createElement(Text8, { color, bold: true }, glyph);
|
|
8422
8691
|
}
|
|
8692
|
+
function indentContinuationLines(text) {
|
|
8693
|
+
if (!text.includes("\n")) return text;
|
|
8694
|
+
return text.split("\n").join("\n ");
|
|
8695
|
+
}
|
|
8423
8696
|
var EventRow = React9.memo(function EventRow2({
|
|
8424
8697
|
event,
|
|
8425
8698
|
projectRoot
|
|
8426
8699
|
}) {
|
|
8427
8700
|
if (event.role === "user") {
|
|
8428
|
-
return /* @__PURE__ */ React9.createElement(Box8, { marginTop: event.leadSeparator ? 1 : 0 }, /* @__PURE__ */ React9.createElement(RoleGlyph, { glyph: ROLE_GLYPH.user, color: "cyan" }), /* @__PURE__ */ React9.createElement(Text8, null, " ", event.text));
|
|
8701
|
+
return /* @__PURE__ */ React9.createElement(Box8, { marginTop: event.leadSeparator ? 1 : 0 }, /* @__PURE__ */ React9.createElement(RoleGlyph, { glyph: ROLE_GLYPH.user, color: "cyan" }), /* @__PURE__ */ React9.createElement(Text8, null, " ", indentContinuationLines(event.text)));
|
|
8429
8702
|
}
|
|
8430
8703
|
if (event.role === "assistant") {
|
|
8431
8704
|
if (event.streaming) return /* @__PURE__ */ React9.createElement(StreamingAssistant, { event });
|
|
@@ -8446,7 +8719,7 @@ var EventRow = React9.memo(function EventRow2({
|
|
|
8446
8719
|
return /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(RoleGlyph, { glyph, color }), /* @__PURE__ */ React9.createElement(Text8, { color, bold: true }, ` ${event.toolName ?? "?"}`), durationLabel ? /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, durationLabel) : null, /* @__PURE__ */ React9.createElement(Text8, { color, dimColor: true }, ` ${marker} `), /* @__PURE__ */ React9.createElement(Text8, { color: summary.isError ? "red" : void 0, dimColor: !summary.isError }, summary.summary), indexHint ? /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, indexHint) : null);
|
|
8447
8720
|
}
|
|
8448
8721
|
if (event.role === "error") {
|
|
8449
|
-
return /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(RoleGlyph, { glyph: ROLE_GLYPH.error, color: "red" }), /* @__PURE__ */ React9.createElement(Text8, { color: "red" }, " ", event.text));
|
|
8722
|
+
return /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(RoleGlyph, { glyph: ROLE_GLYPH.error, color: "red" }), /* @__PURE__ */ React9.createElement(Text8, { color: "red" }, " ", indentContinuationLines(event.text)));
|
|
8450
8723
|
}
|
|
8451
8724
|
if (event.role === "info") {
|
|
8452
8725
|
return /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, event.text));
|
|
@@ -8494,7 +8767,7 @@ var EventRow = React9.memo(function EventRow2({
|
|
|
8494
8767
|
)), r.summary ? /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, ` ${r.summary}`)) : null, /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, ` ${r.archiveBasename}`))), r.body ? /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React9.createElement(Markdown, { text: r.body, projectRoot })) : null, /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React9.createElement(PlanStepList, { steps: r.steps, statuses })), /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1 }, /* @__PURE__ */ React9.createElement(Text8, { dimColor: true }, r.total > 1 ? `(read-only \xB7 /replay ${r.index === 1 ? 2 : 1} for the ${r.index === 1 ? "next" : "newest"} archive)` : "(read-only \xB7 this is an archived plan)")));
|
|
8495
8768
|
}
|
|
8496
8769
|
if (event.role === "warning") {
|
|
8497
|
-
return /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(RoleGlyph, { glyph: ROLE_GLYPH.warning, color: "yellow" }), /* @__PURE__ */ React9.createElement(Text8, { color: "yellow" }, " ", event.text));
|
|
8770
|
+
return /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(RoleGlyph, { glyph: ROLE_GLYPH.warning, color: "yellow" }), /* @__PURE__ */ React9.createElement(Text8, { color: "yellow" }, " ", indentContinuationLines(event.text)));
|
|
8498
8771
|
}
|
|
8499
8772
|
return /* @__PURE__ */ React9.createElement(Box8, null, /* @__PURE__ */ React9.createElement(Text8, null, event.text));
|
|
8500
8773
|
});
|
|
@@ -8904,18 +9177,33 @@ function PlanReviseConfirmInner({
|
|
|
8904
9177
|
var PlanReviseConfirm = React14.memo(PlanReviseConfirmInner);
|
|
8905
9178
|
|
|
8906
9179
|
// src/cli/ui/PromptInput.tsx
|
|
8907
|
-
import { Box as Box14, Text as Text14, useInput as useInput4 } from "ink";
|
|
9180
|
+
import { Box as Box14, Text as Text14, useInput as useInput4, useStdout as useStdout3 } from "ink";
|
|
8908
9181
|
import React15, { useRef, useState as useState5 } from "react";
|
|
8909
9182
|
|
|
8910
9183
|
// src/cli/ui/multiline-keys.ts
|
|
8911
9184
|
var BACKSLASH_SUFFIX = /\\$/;
|
|
8912
9185
|
var NOOP = { next: null, cursor: null, submit: false };
|
|
8913
|
-
function
|
|
8914
|
-
if (key.
|
|
9186
|
+
function rewriteRawArrowEscape(key) {
|
|
9187
|
+
if (key.upArrow || key.downArrow || key.leftArrow || key.rightArrow) return key;
|
|
9188
|
+
if (key.input === "\x1B[A") return { ...key, upArrow: true, input: "" };
|
|
9189
|
+
if (key.input === "\x1B[B") return { ...key, downArrow: true, input: "" };
|
|
9190
|
+
if (key.input === "\x1B[C") return { ...key, rightArrow: true, input: "" };
|
|
9191
|
+
if (key.input === "\x1B[D") return { ...key, leftArrow: true, input: "" };
|
|
9192
|
+
return key;
|
|
9193
|
+
}
|
|
9194
|
+
function processMultilineKey(value, cursor, keyIn) {
|
|
9195
|
+
const key = rewriteRawArrowEscape(keyIn);
|
|
9196
|
+
if (key.tab || key.escape) {
|
|
8915
9197
|
return NOOP;
|
|
8916
9198
|
}
|
|
9199
|
+
if (key.pageUp) {
|
|
9200
|
+
return cursor === 0 ? NOOP : { next: null, cursor: 0, submit: false };
|
|
9201
|
+
}
|
|
9202
|
+
if (key.pageDown) {
|
|
9203
|
+
return cursor === value.length ? NOOP : { next: null, cursor: value.length, submit: false };
|
|
9204
|
+
}
|
|
8917
9205
|
if (value.length === 0 && (key.upArrow || key.downArrow)) {
|
|
8918
|
-
return NOOP;
|
|
9206
|
+
return { ...NOOP, historyHandoff: key.upArrow ? "prev" : "next" };
|
|
8919
9207
|
}
|
|
8920
9208
|
if (key.leftArrow) {
|
|
8921
9209
|
return { next: null, cursor: Math.max(0, cursor - 1), submit: false };
|
|
@@ -8925,11 +9213,13 @@ function processMultilineKey(value, cursor, key) {
|
|
|
8925
9213
|
}
|
|
8926
9214
|
if (key.upArrow) {
|
|
8927
9215
|
const moved = moveCursorUp(value, cursor);
|
|
8928
|
-
|
|
9216
|
+
if (moved === cursor) return { ...NOOP, historyHandoff: "prev" };
|
|
9217
|
+
return { next: null, cursor: moved, submit: false };
|
|
8929
9218
|
}
|
|
8930
9219
|
if (key.downArrow) {
|
|
8931
9220
|
const moved = moveCursorDown(value, cursor);
|
|
8932
|
-
|
|
9221
|
+
if (moved === cursor) return { ...NOOP, historyHandoff: "next" };
|
|
9222
|
+
return { next: null, cursor: moved, submit: false };
|
|
8933
9223
|
}
|
|
8934
9224
|
if (key.ctrl && key.input === "a") {
|
|
8935
9225
|
return { next: null, cursor: startOfLine(value, cursor), submit: false };
|
|
@@ -8937,6 +9227,29 @@ function processMultilineKey(value, cursor, key) {
|
|
|
8937
9227
|
if (key.ctrl && key.input === "e") {
|
|
8938
9228
|
return { next: null, cursor: endOfLine(value, cursor), submit: false };
|
|
8939
9229
|
}
|
|
9230
|
+
if (key.ctrl && key.input === "u") {
|
|
9231
|
+
return value.length === 0 ? NOOP : { next: "", cursor: 0, submit: false };
|
|
9232
|
+
}
|
|
9233
|
+
if (key.ctrl && key.input === "w") {
|
|
9234
|
+
if (cursor === 0) return NOOP;
|
|
9235
|
+
const wordStart = previousWordStart(value, cursor);
|
|
9236
|
+
return {
|
|
9237
|
+
next: value.slice(0, wordStart) + value.slice(cursor),
|
|
9238
|
+
cursor: wordStart,
|
|
9239
|
+
submit: false
|
|
9240
|
+
};
|
|
9241
|
+
}
|
|
9242
|
+
const stripped = key.input.replaceAll("\x1B[200~", "").replaceAll("\x1B[201~", "");
|
|
9243
|
+
const looksLikePaste = stripped.length > 1 && (stripped.includes("\n") || stripped.includes("\r"));
|
|
9244
|
+
if (looksLikePaste) {
|
|
9245
|
+
const normalized = stripped.replace(/\r\n?/g, "\n");
|
|
9246
|
+
return {
|
|
9247
|
+
next: null,
|
|
9248
|
+
cursor: null,
|
|
9249
|
+
submit: false,
|
|
9250
|
+
pasteRequest: { content: normalized }
|
|
9251
|
+
};
|
|
9252
|
+
}
|
|
8940
9253
|
if (key.input === "\n" || key.ctrl && key.input === "j") {
|
|
8941
9254
|
return insertAt(value, cursor, "\n");
|
|
8942
9255
|
}
|
|
@@ -8988,6 +9301,12 @@ function lineAndColumn(value, cursor) {
|
|
|
8988
9301
|
function startOfLine(value, cursor) {
|
|
8989
9302
|
return value.lastIndexOf("\n", cursor - 1) + 1;
|
|
8990
9303
|
}
|
|
9304
|
+
function previousWordStart(value, cursor) {
|
|
9305
|
+
let i = cursor;
|
|
9306
|
+
while (i > 0 && /\s/.test(value[i - 1] ?? "")) i--;
|
|
9307
|
+
while (i > 0 && !/\s/.test(value[i - 1] ?? "")) i--;
|
|
9308
|
+
return i;
|
|
9309
|
+
}
|
|
8991
9310
|
function endOfLine(value, cursor) {
|
|
8992
9311
|
const nl = value.indexOf("\n", cursor);
|
|
8993
9312
|
return nl === -1 ? value.length : nl;
|
|
@@ -9012,15 +9331,76 @@ function moveCursorDown(value, cursor) {
|
|
|
9012
9331
|
return nextStart + Math.min(col, nextLen);
|
|
9013
9332
|
}
|
|
9014
9333
|
|
|
9334
|
+
// src/cli/ui/paste-sentinels.ts
|
|
9335
|
+
var PASTE_SENTINEL_BASE = 57600;
|
|
9336
|
+
var PASTE_SENTINEL_RANGE = 256;
|
|
9337
|
+
var PASTE_SENTINEL_END = PASTE_SENTINEL_BASE + PASTE_SENTINEL_RANGE;
|
|
9338
|
+
function encodePasteSentinel(id) {
|
|
9339
|
+
if (id < 0 || id >= PASTE_SENTINEL_RANGE) {
|
|
9340
|
+
throw new Error(`paste sentinel id ${id} out of range [0, ${PASTE_SENTINEL_RANGE})`);
|
|
9341
|
+
}
|
|
9342
|
+
return String.fromCharCode(PASTE_SENTINEL_BASE + id);
|
|
9343
|
+
}
|
|
9344
|
+
function decodePasteSentinel(ch) {
|
|
9345
|
+
if (ch.length === 0) return null;
|
|
9346
|
+
const cp = ch.charCodeAt(0);
|
|
9347
|
+
if (cp < PASTE_SENTINEL_BASE || cp >= PASTE_SENTINEL_END) return null;
|
|
9348
|
+
return cp - PASTE_SENTINEL_BASE;
|
|
9349
|
+
}
|
|
9350
|
+
function makePasteEntry(id, content) {
|
|
9351
|
+
return {
|
|
9352
|
+
id,
|
|
9353
|
+
content,
|
|
9354
|
+
lineCount: content.split("\n").length,
|
|
9355
|
+
charCount: content.length
|
|
9356
|
+
};
|
|
9357
|
+
}
|
|
9358
|
+
function expandPasteSentinels(text, pastes) {
|
|
9359
|
+
let out = "";
|
|
9360
|
+
for (let i = 0; i < text.length; i++) {
|
|
9361
|
+
const ch = text[i];
|
|
9362
|
+
const id = decodePasteSentinel(ch);
|
|
9363
|
+
if (id === null) {
|
|
9364
|
+
out += ch;
|
|
9365
|
+
continue;
|
|
9366
|
+
}
|
|
9367
|
+
const entry = pastes.get(id);
|
|
9368
|
+
out += entry?.content ?? "";
|
|
9369
|
+
}
|
|
9370
|
+
return out;
|
|
9371
|
+
}
|
|
9372
|
+
function listPasteIdsInBuffer(text) {
|
|
9373
|
+
const ids = [];
|
|
9374
|
+
for (let i = 0; i < text.length; i++) {
|
|
9375
|
+
const id = decodePasteSentinel(text[i]);
|
|
9376
|
+
if (id !== null) ids.push(id);
|
|
9377
|
+
}
|
|
9378
|
+
return ids;
|
|
9379
|
+
}
|
|
9380
|
+
function formatBytesShort(n) {
|
|
9381
|
+
if (n < 1024) return `${n}B`;
|
|
9382
|
+
if (n < 1024 * 1024) return `${(n / 1024).toFixed(n < 1024 * 10 ? 1 : 0)}KB`;
|
|
9383
|
+
return `${(n / (1024 * 1024)).toFixed(1)}MB`;
|
|
9384
|
+
}
|
|
9385
|
+
|
|
9015
9386
|
// src/cli/ui/PromptInput.tsx
|
|
9387
|
+
var PASTE_START_MARKER = "\x1B[200~";
|
|
9388
|
+
var PASTE_END_MARKER = "\x1B[201~";
|
|
9389
|
+
var PASTE_MERGE_WINDOW_MS = 30;
|
|
9016
9390
|
function PromptInput({
|
|
9017
9391
|
value,
|
|
9018
9392
|
onChange,
|
|
9019
9393
|
onSubmit,
|
|
9020
9394
|
disabled,
|
|
9021
|
-
placeholder
|
|
9395
|
+
placeholder,
|
|
9396
|
+
onHistoryPrev,
|
|
9397
|
+
onHistoryNext
|
|
9022
9398
|
}) {
|
|
9023
9399
|
const [cursor, setCursor] = useState5(value.length);
|
|
9400
|
+
const pastesRef = useRef(/* @__PURE__ */ new Map());
|
|
9401
|
+
const nextPasteIdRef = useRef(0);
|
|
9402
|
+
const pasteAccumRef = useRef(null);
|
|
9403
|
+
const lastPasteRef = useRef(null);
|
|
9024
9404
|
const lastLocalValueRef = useRef(value);
|
|
9025
9405
|
if (value !== lastLocalValueRef.current) {
|
|
9026
9406
|
lastLocalValueRef.current = value;
|
|
@@ -9028,10 +9408,62 @@ function PromptInput({
|
|
|
9028
9408
|
setCursor(value.length);
|
|
9029
9409
|
}
|
|
9030
9410
|
}
|
|
9411
|
+
const cursorRef = useRef(cursor);
|
|
9412
|
+
cursorRef.current = cursor;
|
|
9031
9413
|
const tick = useTick();
|
|
9032
9414
|
const showCursor = disabled ? false : Math.floor(tick / 4) % 2 === 0;
|
|
9415
|
+
const registerPaste = (content) => {
|
|
9416
|
+
const v = lastLocalValueRef.current;
|
|
9417
|
+
const c = cursorRef.current;
|
|
9418
|
+
const now = Date.now();
|
|
9419
|
+
const last = lastPasteRef.current;
|
|
9420
|
+
const prevChar = c > 0 ? v[c - 1] : null;
|
|
9421
|
+
const prevId = prevChar ? decodePasteSentinel(prevChar) : null;
|
|
9422
|
+
const canMerge = last !== null && prevId === last.id && now - last.at < PASTE_MERGE_WINDOW_MS && pastesRef.current.has(last.id);
|
|
9423
|
+
if (canMerge && last) {
|
|
9424
|
+
const existing = pastesRef.current.get(last.id);
|
|
9425
|
+
if (existing) {
|
|
9426
|
+
const merged = existing.content + content;
|
|
9427
|
+
pastesRef.current.set(last.id, makePasteEntry(last.id, merged));
|
|
9428
|
+
lastPasteRef.current = { id: last.id, at: now };
|
|
9429
|
+
return;
|
|
9430
|
+
}
|
|
9431
|
+
}
|
|
9432
|
+
const id = nextPasteIdRef.current % PASTE_SENTINEL_RANGE;
|
|
9433
|
+
nextPasteIdRef.current = id + 1;
|
|
9434
|
+
pastesRef.current.set(id, makePasteEntry(id, content));
|
|
9435
|
+
const sentinel = encodePasteSentinel(id);
|
|
9436
|
+
const next = v.slice(0, c) + sentinel + v.slice(c);
|
|
9437
|
+
lastLocalValueRef.current = next;
|
|
9438
|
+
cursorRef.current = c + 1;
|
|
9439
|
+
onChange(next);
|
|
9440
|
+
setCursor(c + 1);
|
|
9441
|
+
lastPasteRef.current = { id, at: now };
|
|
9442
|
+
};
|
|
9033
9443
|
useInput4(
|
|
9034
9444
|
(input, key) => {
|
|
9445
|
+
if (pasteAccumRef.current !== null) {
|
|
9446
|
+
const endIdx = input.indexOf(PASTE_END_MARKER);
|
|
9447
|
+
if (endIdx === -1) {
|
|
9448
|
+
pasteAccumRef.current += input;
|
|
9449
|
+
return;
|
|
9450
|
+
}
|
|
9451
|
+
const content = pasteAccumRef.current + input.slice(0, endIdx);
|
|
9452
|
+
pasteAccumRef.current = null;
|
|
9453
|
+
registerPaste(content);
|
|
9454
|
+
return;
|
|
9455
|
+
}
|
|
9456
|
+
const startIdx = input.indexOf(PASTE_START_MARKER);
|
|
9457
|
+
if (startIdx !== -1) {
|
|
9458
|
+
const afterStart = input.slice(startIdx + PASTE_START_MARKER.length);
|
|
9459
|
+
const endIdx = afterStart.indexOf(PASTE_END_MARKER);
|
|
9460
|
+
if (endIdx !== -1) {
|
|
9461
|
+
registerPaste(afterStart.slice(0, endIdx));
|
|
9462
|
+
} else {
|
|
9463
|
+
pasteAccumRef.current = afterStart;
|
|
9464
|
+
}
|
|
9465
|
+
return;
|
|
9466
|
+
}
|
|
9035
9467
|
const ke = {
|
|
9036
9468
|
input,
|
|
9037
9469
|
return: key.return,
|
|
@@ -9050,6 +9482,10 @@ function PromptInput({
|
|
|
9050
9482
|
pageDown: key.pageDown
|
|
9051
9483
|
};
|
|
9052
9484
|
const action = processMultilineKey(value, cursor, ke);
|
|
9485
|
+
if (action.pasteRequest) {
|
|
9486
|
+
registerPaste(action.pasteRequest.content);
|
|
9487
|
+
return;
|
|
9488
|
+
}
|
|
9053
9489
|
if (action.next !== null) {
|
|
9054
9490
|
lastLocalValueRef.current = action.next;
|
|
9055
9491
|
onChange(action.next);
|
|
@@ -9057,45 +9493,134 @@ function PromptInput({
|
|
|
9057
9493
|
if (action.cursor !== null) {
|
|
9058
9494
|
setCursor(action.cursor);
|
|
9059
9495
|
}
|
|
9060
|
-
if (action.submit)
|
|
9496
|
+
if (action.submit) {
|
|
9497
|
+
const raw = action.submitValue ?? value;
|
|
9498
|
+
const expanded = expandPasteSentinels(raw, pastesRef.current);
|
|
9499
|
+
const reachable = new Set(listPasteIdsInBuffer(raw));
|
|
9500
|
+
for (const id of pastesRef.current.keys()) {
|
|
9501
|
+
if (!reachable.has(id)) pastesRef.current.delete(id);
|
|
9502
|
+
}
|
|
9503
|
+
onSubmit(expanded);
|
|
9504
|
+
}
|
|
9505
|
+
if (action.historyHandoff === "prev") onHistoryPrev?.();
|
|
9506
|
+
if (action.historyHandoff === "next") onHistoryNext?.();
|
|
9061
9507
|
},
|
|
9062
9508
|
{ isActive: !disabled }
|
|
9063
9509
|
);
|
|
9064
|
-
const
|
|
9510
|
+
const { stdout: stdout2 } = useStdout3();
|
|
9511
|
+
const cols = stdout2?.columns ?? 80;
|
|
9512
|
+
const narrow = cols <= 90;
|
|
9513
|
+
const promptPrefix = narrow ? "\u203A " : "you \u203A ";
|
|
9514
|
+
const continuationIndent = narrow ? " " : " ";
|
|
9515
|
+
const placeholderActive = narrow ? "type a message, or /command" : "type a message, or /command \xB7 [Shift+Enter] / [Ctrl+J] newline";
|
|
9516
|
+
const effectivePlaceholder = disabled ? placeholder ?? "\u2026waiting for response\u2026" : placeholder ?? placeholderActive;
|
|
9065
9517
|
const lines = value.length > 0 ? value.split("\n") : [""];
|
|
9066
9518
|
const borderColor = disabled ? "gray" : "cyan";
|
|
9067
9519
|
const { line: cursorLine, col: cursorCol } = lineAndColumn(value, cursor);
|
|
9068
|
-
|
|
9520
|
+
const renderItems = collapseLinesForDisplay(lines, cursorLine);
|
|
9521
|
+
const showHugeBufferHints = lines.length > 20;
|
|
9522
|
+
return /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement(Box14, { borderStyle: "round", borderColor, paddingX: 1, flexDirection: "column" }, renderItems.map((item, renderIdx) => {
|
|
9523
|
+
if (item.kind === "skip") {
|
|
9524
|
+
return (
|
|
9525
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: stable — skip markers are derived from a fixed-size window over `lines`
|
|
9526
|
+
/* @__PURE__ */ React15.createElement(Box14, { key: `skip-${renderIdx}` }, /* @__PURE__ */ React15.createElement(Text14, { dimColor: true }, continuationIndent), /* @__PURE__ */ React15.createElement(
|
|
9527
|
+
Text14,
|
|
9528
|
+
{
|
|
9529
|
+
dimColor: true
|
|
9530
|
+
},
|
|
9531
|
+
`[\u2026 ${item.linesHidden} line${item.linesHidden === 1 ? "" : "s"} hidden \u2014 full content kept, submitted on Enter \u2026]`
|
|
9532
|
+
))
|
|
9533
|
+
);
|
|
9534
|
+
}
|
|
9535
|
+
const line = item.line;
|
|
9536
|
+
const i = item.originalIndex;
|
|
9069
9537
|
const isFirst = i === 0;
|
|
9070
9538
|
const showPlaceholder = isFirst && value.length === 0;
|
|
9071
9539
|
const isCursorLine = i === cursorLine;
|
|
9072
|
-
return (
|
|
9073
|
-
|
|
9074
|
-
|
|
9075
|
-
|
|
9076
|
-
|
|
9077
|
-
|
|
9078
|
-
|
|
9079
|
-
|
|
9080
|
-
|
|
9081
|
-
|
|
9082
|
-
|
|
9540
|
+
return /* @__PURE__ */ React15.createElement(Box14, { key: `ln-${i}` }, isFirst ? /* @__PURE__ */ React15.createElement(Text14, { bold: true, color: borderColor }, promptPrefix) : /* @__PURE__ */ React15.createElement(Text14, { dimColor: true }, continuationIndent), showPlaceholder ? /* @__PURE__ */ React15.createElement(React15.Fragment, null, isCursorLine && !disabled ? /* @__PURE__ */ React15.createElement(Text14, { color: borderColor }, showCursor ? "\u258C" : " ") : null, /* @__PURE__ */ React15.createElement(Text14, { dimColor: true }, effectivePlaceholder)) : isCursorLine && !disabled ? /* @__PURE__ */ React15.createElement(
|
|
9541
|
+
LineWithCursor,
|
|
9542
|
+
{
|
|
9543
|
+
line,
|
|
9544
|
+
col: cursorCol,
|
|
9545
|
+
showCursor,
|
|
9546
|
+
borderColor,
|
|
9547
|
+
pastes: pastesRef.current
|
|
9548
|
+
}
|
|
9549
|
+
) : /* @__PURE__ */ React15.createElement(RenderLine, { line, pastes: pastesRef.current }));
|
|
9550
|
+
}), showHugeBufferHints && !disabled ? /* @__PURE__ */ React15.createElement(Box14, null, /* @__PURE__ */ React15.createElement(Text14, { dimColor: true }, continuationIndent), /* @__PURE__ */ React15.createElement(Text14, { dimColor: true }, `[${lines.length} lines \xB7 PageUp/PageDown jump to top/bottom \xB7 Ctrl+U clear \xB7 Ctrl+W del word]`)) : null), disabled ? /* @__PURE__ */ React15.createElement(Box14, { paddingX: 1 }, /* @__PURE__ */ React15.createElement(Text14, { dimColor: true }, "[Esc] to stop")) : null);
|
|
9551
|
+
}
|
|
9552
|
+
var COLLAPSE_THRESHOLD = 20;
|
|
9553
|
+
var COLLAPSE_HEAD_LINES = 3;
|
|
9554
|
+
var COLLAPSE_TAIL_LINES = 2;
|
|
9555
|
+
function collapseLinesForDisplay(lines, cursorLine) {
|
|
9556
|
+
if (lines.length <= COLLAPSE_THRESHOLD) {
|
|
9557
|
+
return lines.map((line, i) => ({ kind: "line", line, originalIndex: i }));
|
|
9558
|
+
}
|
|
9559
|
+
const keep = /* @__PURE__ */ new Set();
|
|
9560
|
+
for (let i = 0; i < COLLAPSE_HEAD_LINES && i < lines.length; i++) keep.add(i);
|
|
9561
|
+
for (let i = Math.max(0, lines.length - COLLAPSE_TAIL_LINES); i < lines.length; i++) keep.add(i);
|
|
9562
|
+
if (cursorLine >= 0 && cursorLine < lines.length) keep.add(cursorLine);
|
|
9563
|
+
const sorted = [...keep].sort((a, b) => a - b);
|
|
9564
|
+
const out = [];
|
|
9565
|
+
let prev = -1;
|
|
9566
|
+
for (const idx of sorted) {
|
|
9567
|
+
if (idx - prev > 1) {
|
|
9568
|
+
out.push({ kind: "skip", linesHidden: idx - prev - 1 });
|
|
9569
|
+
}
|
|
9570
|
+
out.push({ kind: "line", line: lines[idx] ?? "", originalIndex: idx });
|
|
9571
|
+
prev = idx;
|
|
9572
|
+
}
|
|
9573
|
+
return out;
|
|
9574
|
+
}
|
|
9575
|
+
function RenderLine({
|
|
9576
|
+
line,
|
|
9577
|
+
pastes,
|
|
9578
|
+
inverse
|
|
9579
|
+
}) {
|
|
9580
|
+
const segments = [];
|
|
9581
|
+
let buf = "";
|
|
9582
|
+
let segIdx = 0;
|
|
9583
|
+
const flushBuf = () => {
|
|
9584
|
+
if (buf.length === 0) return;
|
|
9585
|
+
segments.push(
|
|
9586
|
+
/* @__PURE__ */ React15.createElement(Text14, { key: `t-${segIdx++}`, inverse }, buf)
|
|
9083
9587
|
);
|
|
9084
|
-
|
|
9588
|
+
buf = "";
|
|
9589
|
+
};
|
|
9590
|
+
for (let i = 0; i < line.length; i++) {
|
|
9591
|
+
const ch = line[i];
|
|
9592
|
+
const id = decodePasteSentinel(ch);
|
|
9593
|
+
if (id === null) {
|
|
9594
|
+
buf += ch;
|
|
9595
|
+
continue;
|
|
9596
|
+
}
|
|
9597
|
+
flushBuf();
|
|
9598
|
+
const entry = pastes.get(id);
|
|
9599
|
+
const label = entry ? `[paste #${id + 1} \xB7 ${entry.lineCount}l \xB7 ${formatBytesShort(entry.charCount)}]` : `[paste #${id + 1} \xB7 (missing)]`;
|
|
9600
|
+
segments.push(
|
|
9601
|
+
/* @__PURE__ */ React15.createElement(Text14, { key: `p-${segIdx++}`, color: "magenta", bold: true, inverse }, label)
|
|
9602
|
+
);
|
|
9603
|
+
}
|
|
9604
|
+
flushBuf();
|
|
9605
|
+
if (segments.length === 0) {
|
|
9606
|
+
return /* @__PURE__ */ React15.createElement(Text14, null, " ");
|
|
9607
|
+
}
|
|
9608
|
+
return /* @__PURE__ */ React15.createElement(React15.Fragment, null, segments);
|
|
9085
9609
|
}
|
|
9086
9610
|
function LineWithCursor({
|
|
9087
9611
|
line,
|
|
9088
9612
|
col,
|
|
9089
9613
|
showCursor,
|
|
9090
|
-
borderColor
|
|
9614
|
+
borderColor,
|
|
9615
|
+
pastes
|
|
9091
9616
|
}) {
|
|
9092
9617
|
const before = line.slice(0, col);
|
|
9093
9618
|
const atCursor = line.slice(col, col + 1);
|
|
9094
9619
|
const after = line.slice(col + 1);
|
|
9095
9620
|
if (atCursor.length === 0) {
|
|
9096
|
-
return /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement(
|
|
9621
|
+
return /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement(RenderLine, { line: before, pastes }), /* @__PURE__ */ React15.createElement(Text14, { color: borderColor }, showCursor ? "\u258C" : " "));
|
|
9097
9622
|
}
|
|
9098
|
-
return /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement(
|
|
9623
|
+
return /* @__PURE__ */ React15.createElement(React15.Fragment, null, /* @__PURE__ */ React15.createElement(RenderLine, { line: before, pastes }), /* @__PURE__ */ React15.createElement(RenderLine, { line: atCursor, pastes, inverse: showCursor }), /* @__PURE__ */ React15.createElement(RenderLine, { line: after, pastes }));
|
|
9099
9624
|
}
|
|
9100
9625
|
|
|
9101
9626
|
// src/cli/ui/ShellConfirm.tsx
|
|
@@ -9103,7 +9628,7 @@ import { Box as Box15, Text as Text15 } from "ink";
|
|
|
9103
9628
|
import React16 from "react";
|
|
9104
9629
|
function ShellConfirm({ command, allowPrefix, kind, onChoose }) {
|
|
9105
9630
|
const isBackground = kind === "run_background";
|
|
9106
|
-
return /* @__PURE__ */ React16.createElement(Box15, { flexDirection: "column", borderStyle: "round", borderColor: "
|
|
9631
|
+
return /* @__PURE__ */ React16.createElement(Box15, { flexDirection: "column", borderStyle: "round", borderColor: "red", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React16.createElement(Box15, null, /* @__PURE__ */ React16.createElement(Text15, { bold: true, color: "red" }, isBackground ? "\u25B8 model wants to start a BACKGROUND process" : "\u25B8 model wants to run a shell command")), isBackground ? /* @__PURE__ */ React16.createElement(Box15, null, /* @__PURE__ */ React16.createElement(Text15, { dimColor: true }, " (long-running: dev server / watcher; keeps running after approval, /kill to stop)")) : null, /* @__PURE__ */ React16.createElement(Box15, null, /* @__PURE__ */ React16.createElement(Text15, { color: "red", dimColor: true }, "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")), /* @__PURE__ */ React16.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text15, null, /* @__PURE__ */ React16.createElement(Text15, { dimColor: true }, "$ "), /* @__PURE__ */ React16.createElement(Text15, { color: "cyan" }, command))), /* @__PURE__ */ React16.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React16.createElement(
|
|
9107
9632
|
SingleSelect,
|
|
9108
9633
|
{
|
|
9109
9634
|
initialValue: "run_once",
|
|
@@ -9170,11 +9695,11 @@ function SlashArgPicker({
|
|
|
9170
9695
|
partial
|
|
9171
9696
|
}) {
|
|
9172
9697
|
if (kind === "hint") {
|
|
9173
|
-
return /* @__PURE__ */ React17.createElement(Box16, { paddingX: 1 }, /* @__PURE__ */ React17.createElement(Text16, { dimColor: true }, " ", /* @__PURE__ */ React17.createElement(Text16, { bold: true }, "/", spec.cmd), spec.argsHint ? ` ${spec.argsHint}` : "", " \u2014 ", spec.summary));
|
|
9698
|
+
return /* @__PURE__ */ React17.createElement(Box16, { paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React17.createElement(Text16, { dimColor: true }, " ", /* @__PURE__ */ React17.createElement(Text16, { bold: true }, "/", spec.cmd), spec.argsHint ? ` ${spec.argsHint}` : "", " \u2014 ", spec.summary));
|
|
9174
9699
|
}
|
|
9175
9700
|
if (matches === null) return null;
|
|
9176
9701
|
if (matches.length === 0) {
|
|
9177
|
-
return /* @__PURE__ */ React17.createElement(Box16, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React17.createElement(Text16, { dimColor: true }, " ", /* @__PURE__ */ React17.createElement(Text16, { bold: true }, "/", spec.cmd), spec.argsHint ? ` ${spec.argsHint}` : "", " \u2014 ", spec.summary), /* @__PURE__ */ React17.createElement(Text16, { color: "yellow" }, ' no match for "', partial, '" \u2014 keep typing, or Backspace to edit'));
|
|
9702
|
+
return /* @__PURE__ */ React17.createElement(Box16, { flexDirection: "column", paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React17.createElement(Text16, { dimColor: true }, " ", /* @__PURE__ */ React17.createElement(Text16, { bold: true }, "/", spec.cmd), spec.argsHint ? ` ${spec.argsHint}` : "", " \u2014 ", spec.summary), /* @__PURE__ */ React17.createElement(Text16, { color: "yellow" }, ' no match for "', partial, '" \u2014 keep typing, or Backspace to edit'));
|
|
9178
9703
|
}
|
|
9179
9704
|
const MAX = 8;
|
|
9180
9705
|
const total = matches.length;
|
|
@@ -9182,7 +9707,7 @@ function SlashArgPicker({
|
|
|
9182
9707
|
const shown = matches.slice(windowStart, windowStart + MAX);
|
|
9183
9708
|
const hiddenAbove = windowStart;
|
|
9184
9709
|
const hiddenBelow = total - windowStart - shown.length;
|
|
9185
|
-
return /* @__PURE__ */ React17.createElement(Box16, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React17.createElement(Text16, { dimColor: true }, " ", /* @__PURE__ */ React17.createElement(Text16, { bold: true }, "/", spec.cmd), spec.argsHint ? ` ${spec.argsHint}` : "", " \u2014 ", spec.summary), hiddenAbove > 0 ? /* @__PURE__ */ React17.createElement(Text16, { dimColor: true }, " \u2191 ", hiddenAbove, " more above") : null, shown.map((value, i) => /* @__PURE__ */ React17.createElement(ArgRow, { key: value, value, isSelected: windowStart + i === selectedIndex })), hiddenBelow > 0 ? /* @__PURE__ */ React17.createElement(Text16, { dimColor: true }, " \u2193 ", hiddenBelow, " more below") : null, /* @__PURE__ */ React17.createElement(Text16, { dimColor: true }, " [\u2191\u2193] navigate \xB7 [Tab]/[Enter] pick"));
|
|
9710
|
+
return /* @__PURE__ */ React17.createElement(Box16, { flexDirection: "column", paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React17.createElement(Text16, { dimColor: true }, " ", /* @__PURE__ */ React17.createElement(Text16, { bold: true }, "/", spec.cmd), spec.argsHint ? ` ${spec.argsHint}` : "", " \u2014 ", spec.summary), hiddenAbove > 0 ? /* @__PURE__ */ React17.createElement(Text16, { dimColor: true }, " \u2191 ", hiddenAbove, " more above") : null, shown.map((value, i) => /* @__PURE__ */ React17.createElement(ArgRow, { key: value, value, isSelected: windowStart + i === selectedIndex })), hiddenBelow > 0 ? /* @__PURE__ */ React17.createElement(Text16, { dimColor: true }, " \u2193 ", hiddenBelow, " more below") : null, /* @__PURE__ */ React17.createElement(Text16, { dimColor: true }, " [\u2191\u2193] navigate \xB7 [Tab]/[Enter] pick"));
|
|
9186
9711
|
}
|
|
9187
9712
|
function ArgRow({ value, isSelected }) {
|
|
9188
9713
|
const marker = isSelected ? "\u25B8" : " ";
|
|
@@ -9201,7 +9726,7 @@ function SlashSuggestions({
|
|
|
9201
9726
|
}) {
|
|
9202
9727
|
if (matches === null) return null;
|
|
9203
9728
|
if (matches.length === 0) {
|
|
9204
|
-
return /* @__PURE__ */ React18.createElement(Box17, { paddingX: 1 }, /* @__PURE__ */ React18.createElement(Text17, { color: "yellow" }, "no slash command matches that prefix"), /* @__PURE__ */ React18.createElement(Text17, { dimColor: true }, " \u2014 Backspace to edit, or /help for the full list"));
|
|
9729
|
+
return /* @__PURE__ */ React18.createElement(Box17, { paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React18.createElement(Text17, { color: "yellow" }, "no slash command matches that prefix"), /* @__PURE__ */ React18.createElement(Text17, { dimColor: true }, " \u2014 Backspace to edit, or /help for the full list"));
|
|
9205
9730
|
}
|
|
9206
9731
|
const MAX = 8;
|
|
9207
9732
|
const total = matches.length;
|
|
@@ -9209,7 +9734,7 @@ function SlashSuggestions({
|
|
|
9209
9734
|
const shown = matches.slice(windowStart, windowStart + MAX);
|
|
9210
9735
|
const hiddenAbove = windowStart;
|
|
9211
9736
|
const hiddenBelow = total - windowStart - shown.length;
|
|
9212
|
-
return /* @__PURE__ */ React18.createElement(Box17, { flexDirection: "column", paddingX: 1 }, hiddenAbove > 0 ? /* @__PURE__ */ React18.createElement(Text17, { dimColor: true }, " \u2191 ", hiddenAbove, " more above") : null, shown.map((spec, i) => /* @__PURE__ */ React18.createElement(SuggestionRow, { key: spec.cmd, spec, isSelected: windowStart + i === selectedIndex })), hiddenBelow > 0 ? /* @__PURE__ */ React18.createElement(Text17, { dimColor: true }, " \u2193 ", hiddenBelow, " more below") : null, /* @__PURE__ */ React18.createElement(Text17, { dimColor: true }, " [\u2191\u2193] navigate \xB7 [Tab]/[Enter] pick"));
|
|
9737
|
+
return /* @__PURE__ */ React18.createElement(Box17, { flexDirection: "column", paddingX: 1, marginTop: 1 }, hiddenAbove > 0 ? /* @__PURE__ */ React18.createElement(Text17, { dimColor: true }, " \u2191 ", hiddenAbove, " more above") : null, shown.map((spec, i) => /* @__PURE__ */ React18.createElement(SuggestionRow, { key: spec.cmd, spec, isSelected: windowStart + i === selectedIndex })), hiddenBelow > 0 ? /* @__PURE__ */ React18.createElement(Text17, { dimColor: true }, " \u2193 ", hiddenBelow, " more below") : null, /* @__PURE__ */ React18.createElement(Text17, { dimColor: true }, " [\u2191\u2193] navigate \xB7 [Tab]/[Enter] pick"));
|
|
9213
9738
|
}
|
|
9214
9739
|
function SuggestionRow({ spec, isSelected }) {
|
|
9215
9740
|
const marker = isSelected ? "\u25B8" : " ";
|
|
@@ -9222,7 +9747,7 @@ function SuggestionRow({ spec, isSelected }) {
|
|
|
9222
9747
|
}
|
|
9223
9748
|
|
|
9224
9749
|
// src/cli/ui/StatsPanel.tsx
|
|
9225
|
-
import { Box as Box18, Text as Text18, useStdout as
|
|
9750
|
+
import { Box as Box18, Text as Text18, useStdout as useStdout4 } from "ink";
|
|
9226
9751
|
import React19 from "react";
|
|
9227
9752
|
var WORDMARK_STYLES = [
|
|
9228
9753
|
{ ch: "\u25C8", color: "#5eead4", isLogo: true },
|
|
@@ -9271,47 +9796,58 @@ function StatsPanel({
|
|
|
9271
9796
|
const branchOn = (branchBudget ?? 1) > 1;
|
|
9272
9797
|
const ctxMax = DEEPSEEK_CONTEXT_TOKENS[model2] ?? DEFAULT_CONTEXT_TOKENS;
|
|
9273
9798
|
const ctxRatio = summary.lastPromptTokens / ctxMax;
|
|
9274
|
-
const { stdout: stdout2 } =
|
|
9799
|
+
const { stdout: stdout2 } = useStdout4();
|
|
9275
9800
|
const columns = stdout2?.columns ?? 80;
|
|
9276
9801
|
const narrow = columns < NARROW_BREAKPOINT;
|
|
9277
9802
|
const coldStart = summary.turns <= COLD_START_TURNS;
|
|
9278
|
-
return
|
|
9279
|
-
|
|
9280
|
-
|
|
9281
|
-
|
|
9282
|
-
|
|
9283
|
-
|
|
9284
|
-
|
|
9285
|
-
|
|
9286
|
-
|
|
9287
|
-
|
|
9288
|
-
|
|
9289
|
-
|
|
9290
|
-
|
|
9291
|
-
|
|
9292
|
-
|
|
9293
|
-
|
|
9294
|
-
|
|
9295
|
-
|
|
9296
|
-
|
|
9297
|
-
|
|
9298
|
-
|
|
9299
|
-
|
|
9300
|
-
|
|
9301
|
-
|
|
9302
|
-
|
|
9303
|
-
|
|
9304
|
-
|
|
9305
|
-
|
|
9306
|
-
|
|
9307
|
-
|
|
9308
|
-
|
|
9309
|
-
|
|
9310
|
-
|
|
9311
|
-
|
|
9312
|
-
|
|
9313
|
-
|
|
9314
|
-
|
|
9803
|
+
return (
|
|
9804
|
+
// Explicit `width={columns}` pins the border frame to the exact
|
|
9805
|
+
// terminal width. Without this, Ink auto-flexes the Box to
|
|
9806
|
+
// container width, and on terminal resize the prior frame's
|
|
9807
|
+
// wrapped-overflow can leave tails in the scrollback (each
|
|
9808
|
+
// redraw stacks a slightly-wider-or-narrower frame). Fixing
|
|
9809
|
+
// width per-render doesn't eliminate the underlying Ink
|
|
9810
|
+
// limitation (eraseLines counts logical rows, not post-wrap
|
|
9811
|
+
// display rows) but makes each frame's dimensions exact so
|
|
9812
|
+
// there's no residual uncertainty in the erase.
|
|
9813
|
+
/* @__PURE__ */ React19.createElement(Box18, { borderStyle: "round", borderColor: "cyan", flexDirection: "column", paddingX: 1, width: columns }, /* @__PURE__ */ React19.createElement(
|
|
9814
|
+
Header,
|
|
9815
|
+
{
|
|
9816
|
+
model: model2,
|
|
9817
|
+
prefixHash,
|
|
9818
|
+
harvestOn,
|
|
9819
|
+
branchOn,
|
|
9820
|
+
branchBudget: branchBudget ?? 1,
|
|
9821
|
+
reasoningEffort,
|
|
9822
|
+
planMode,
|
|
9823
|
+
editMode,
|
|
9824
|
+
turns: summary.turns,
|
|
9825
|
+
updateAvailable,
|
|
9826
|
+
narrow,
|
|
9827
|
+
busy: busy ?? false,
|
|
9828
|
+
proArmed: proArmed ?? false,
|
|
9829
|
+
escalated: escalated ?? false
|
|
9830
|
+
}
|
|
9831
|
+
), narrow ? /* @__PURE__ */ React19.createElement(
|
|
9832
|
+
StackedMetrics,
|
|
9833
|
+
{
|
|
9834
|
+
summary,
|
|
9835
|
+
ctxRatio,
|
|
9836
|
+
ctxMax,
|
|
9837
|
+
balance,
|
|
9838
|
+
coldStart
|
|
9839
|
+
}
|
|
9840
|
+
) : /* @__PURE__ */ React19.createElement(
|
|
9841
|
+
InlineMetrics,
|
|
9842
|
+
{
|
|
9843
|
+
summary,
|
|
9844
|
+
ctxRatio,
|
|
9845
|
+
ctxMax,
|
|
9846
|
+
balance,
|
|
9847
|
+
coldStart
|
|
9848
|
+
}
|
|
9849
|
+
))
|
|
9850
|
+
);
|
|
9315
9851
|
}
|
|
9316
9852
|
function Header({
|
|
9317
9853
|
model: model2,
|
|
@@ -10117,7 +10653,7 @@ var handlers = {
|
|
|
10117
10653
|
var exit = () => ({ exit: true });
|
|
10118
10654
|
var clear = () => ({
|
|
10119
10655
|
clear: true,
|
|
10120
|
-
info: "\u25B8 cleared
|
|
10656
|
+
info: "\u25B8 terminal cleared (viewport + scrollback). Context (message log) is intact \u2014 next turn still sees everything. Use /new to start fresh, or /forget to delete the session entirely."
|
|
10121
10657
|
});
|
|
10122
10658
|
var resetLog = (_args, loop) => {
|
|
10123
10659
|
const { dropped } = loop.clearLog();
|
|
@@ -10133,8 +10669,11 @@ var keys = () => ({
|
|
|
10133
10669
|
" Enter submit the current prompt",
|
|
10134
10670
|
" Shift+Enter / Ctrl+J insert a newline (multi-line prompt)",
|
|
10135
10671
|
" \\<Enter> bash-style line continuation",
|
|
10136
|
-
" \u2190 \u2192 \u2191 \u2193 move cursor / recall history
|
|
10137
|
-
"
|
|
10672
|
+
" \u2190 \u2192 \u2191 \u2193 move cursor / recall history at buffer boundary",
|
|
10673
|
+
" PageUp / PageDown jump to top / bottom of the WHOLE buffer (handy after a big paste)",
|
|
10674
|
+
" Ctrl+A / Ctrl+E jump to start / end of the CURRENT line",
|
|
10675
|
+
" Ctrl+U clear the entire input buffer",
|
|
10676
|
+
" Ctrl+W delete the word before the cursor",
|
|
10138
10677
|
" Backspace delete left; Delete delete under cursor",
|
|
10139
10678
|
" Esc abort the in-flight turn",
|
|
10140
10679
|
" y / n accept / reject pending edits (code mode)",
|
|
@@ -10220,7 +10759,13 @@ var help = () => ({
|
|
|
10220
10759
|
"",
|
|
10221
10760
|
"Sessions (auto-enabled by default, named 'default'):",
|
|
10222
10761
|
" reasonix chat --session <name> use a different named session",
|
|
10223
|
-
" reasonix chat --no-session disable persistence for this run"
|
|
10762
|
+
" reasonix chat --no-session disable persistence for this run",
|
|
10763
|
+
"",
|
|
10764
|
+
"Known limitation:",
|
|
10765
|
+
" Resizing the terminal mid-session may stack ghost header frames in",
|
|
10766
|
+
" scrollback (Ink library's live-region clear doesn't account for line",
|
|
10767
|
+
" re-wrapping at the new width). Scroll-up history is unaffected; the",
|
|
10768
|
+
" artifact is purely visual and clears the next time you /clear."
|
|
10224
10769
|
].join("\n")
|
|
10225
10770
|
});
|
|
10226
10771
|
var setup = () => ({
|
|
@@ -11754,6 +12299,14 @@ function App({
|
|
|
11754
12299
|
const abortedThisTurn = useRef5(false);
|
|
11755
12300
|
const [ongoingTool, setOngoingTool] = useState10(null);
|
|
11756
12301
|
const [toolProgress, setToolProgress] = useState10(null);
|
|
12302
|
+
const { stdout: stdout2 } = useStdout5();
|
|
12303
|
+
useEffect5(() => {
|
|
12304
|
+
if (!stdout2 || !stdout2.isTTY) return;
|
|
12305
|
+
stdout2.write("\x1B[?2004h");
|
|
12306
|
+
return () => {
|
|
12307
|
+
stdout2.write("\x1B[?2004l");
|
|
12308
|
+
};
|
|
12309
|
+
}, [stdout2]);
|
|
11757
12310
|
const { activity: subagentActivity, sinkRef: subagentSinkRef } = useSubagent({
|
|
11758
12311
|
session,
|
|
11759
12312
|
setHistorical
|
|
@@ -12114,24 +12667,21 @@ function App({
|
|
|
12114
12667
|
return;
|
|
12115
12668
|
}
|
|
12116
12669
|
}
|
|
12117
|
-
if (input.length === 0) {
|
|
12118
|
-
const hist = promptHistory.current;
|
|
12119
|
-
if (key.upArrow) {
|
|
12120
|
-
if (hist.length === 0) return;
|
|
12121
|
-
const nextCursor = Math.min(historyCursor.current + 1, hist.length - 1);
|
|
12122
|
-
historyCursor.current = nextCursor;
|
|
12123
|
-
setInput(hist[hist.length - 1 - nextCursor] ?? "");
|
|
12124
|
-
return;
|
|
12125
|
-
}
|
|
12126
|
-
if (key.downArrow) {
|
|
12127
|
-
if (historyCursor.current < 0) return;
|
|
12128
|
-
const nextCursor = historyCursor.current - 1;
|
|
12129
|
-
historyCursor.current = nextCursor;
|
|
12130
|
-
setInput(nextCursor < 0 ? "" : hist[hist.length - 1 - nextCursor] ?? "");
|
|
12131
|
-
return;
|
|
12132
|
-
}
|
|
12133
|
-
}
|
|
12134
12670
|
});
|
|
12671
|
+
const recallPrev = useCallback4(() => {
|
|
12672
|
+
const hist = promptHistory.current;
|
|
12673
|
+
if (hist.length === 0) return;
|
|
12674
|
+
const nextCursor = Math.min(historyCursor.current + 1, hist.length - 1);
|
|
12675
|
+
historyCursor.current = nextCursor;
|
|
12676
|
+
setInput(hist[hist.length - 1 - nextCursor] ?? "");
|
|
12677
|
+
}, []);
|
|
12678
|
+
const recallNext = useCallback4(() => {
|
|
12679
|
+
if (historyCursor.current < 0) return;
|
|
12680
|
+
const hist = promptHistory.current;
|
|
12681
|
+
const nextCursor = historyCursor.current - 1;
|
|
12682
|
+
historyCursor.current = nextCursor;
|
|
12683
|
+
setInput(nextCursor < 0 ? "" : hist[hist.length - 1 - nextCursor] ?? "");
|
|
12684
|
+
}, []);
|
|
12135
12685
|
useEffect5(() => {
|
|
12136
12686
|
if (!tools || !codeMode) return;
|
|
12137
12687
|
tools.setToolInterceptor(async (name, args) => {
|
|
@@ -12216,7 +12766,7 @@ function App({
|
|
|
12216
12766
|
if (!codeMode) return "not in code mode";
|
|
12217
12767
|
const blocks = pendingEdits.current;
|
|
12218
12768
|
if (blocks.length === 0) {
|
|
12219
|
-
return "nothing pending \u2014 the
|
|
12769
|
+
return "nothing pending \u2014 the model hasn't proposed edits since the last /apply or /discard.";
|
|
12220
12770
|
}
|
|
12221
12771
|
const snaps = snapshotBeforeEdits(blocks, codeMode.rootDir);
|
|
12222
12772
|
const results = applyEditBlocks(blocks, codeMode.rootDir);
|
|
@@ -12392,6 +12942,7 @@ function App({
|
|
|
12392
12942
|
return;
|
|
12393
12943
|
}
|
|
12394
12944
|
if (result.clear && result.info) {
|
|
12945
|
+
stdout2?.write("\x1B[2J\x1B[3J\x1B[H");
|
|
12395
12946
|
setHistorical([
|
|
12396
12947
|
{
|
|
12397
12948
|
id: `sys-${Date.now()}`,
|
|
@@ -12407,6 +12958,7 @@ function App({
|
|
|
12407
12958
|
return;
|
|
12408
12959
|
}
|
|
12409
12960
|
if (result.clear) {
|
|
12961
|
+
stdout2?.write("\x1B[2J\x1B[3J\x1B[H");
|
|
12410
12962
|
setHistorical([]);
|
|
12411
12963
|
if (codeMode) {
|
|
12412
12964
|
pendingEdits.current = [];
|
|
@@ -12895,7 +13447,8 @@ function App({
|
|
|
12895
13447
|
refreshLatestVersion,
|
|
12896
13448
|
refreshModels,
|
|
12897
13449
|
proArmed,
|
|
12898
|
-
persistPlanState
|
|
13450
|
+
persistPlanState,
|
|
13451
|
+
stdout2
|
|
12899
13452
|
]
|
|
12900
13453
|
);
|
|
12901
13454
|
const handleShellConfirm = useCallback4(
|
|
@@ -13426,7 +13979,9 @@ Continue executing from the next pending step. Call mark_step_complete after eac
|
|
|
13426
13979
|
value: input,
|
|
13427
13980
|
onChange: setInput,
|
|
13428
13981
|
onSubmit: handleSubmit,
|
|
13429
|
-
disabled: busy
|
|
13982
|
+
disabled: busy,
|
|
13983
|
+
onHistoryPrev: recallPrev,
|
|
13984
|
+
onHistoryNext: recallNext
|
|
13430
13985
|
}
|
|
13431
13986
|
), /* @__PURE__ */ React20.createElement(SlashSuggestions, { matches: slashMatches, selectedIndex: slashSelected }), /* @__PURE__ */ React20.createElement(
|
|
13432
13987
|
AtMentionSuggestions,
|
|
@@ -13480,7 +14035,7 @@ function SessionPicker({
|
|
|
13480
14035
|
],
|
|
13481
14036
|
onSubmit: (v) => onChoose(v)
|
|
13482
14037
|
}
|
|
13483
|
-
), /* @__PURE__ */ React21.createElement(Box20, { marginTop: 1 }, /* @__PURE__ */ React21.createElement(Text19, { dimColor: true }, "\u2191\u2193
|
|
14038
|
+
), /* @__PURE__ */ React21.createElement(Box20, { marginTop: 1 }, /* @__PURE__ */ React21.createElement(Text19, { dimColor: true }, "[\u2191\u2193] navigate \xB7 [Enter] select")));
|
|
13484
14039
|
}
|
|
13485
14040
|
function relativeTime2(date) {
|
|
13486
14041
|
const ms = Date.now() - date.getTime();
|
|
@@ -13764,7 +14319,8 @@ function RecordView({ rec, compact: compact2 = false }) {
|
|
|
13764
14319
|
const toolArgsMax = compact2 ? 120 : 200;
|
|
13765
14320
|
const toolContentMax = compact2 ? 200 : 400;
|
|
13766
14321
|
if (rec.role === "user") {
|
|
13767
|
-
|
|
14322
|
+
const content = rec.content.includes("\n") ? rec.content.split("\n").join("\n ") : rec.content;
|
|
14323
|
+
return /* @__PURE__ */ React24.createElement(Box22, { marginTop: 1 }, /* @__PURE__ */ React24.createElement(Text21, { bold: true, color: "cyan" }, "you \u203A", " "), /* @__PURE__ */ React24.createElement(Text21, null, content));
|
|
13768
14324
|
}
|
|
13769
14325
|
if (rec.role === "assistant_final") {
|
|
13770
14326
|
return /* @__PURE__ */ React24.createElement(Box22, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React24.createElement(Box22, null, /* @__PURE__ */ React24.createElement(Text21, { bold: true, color: "green" }, "assistant"), rec.cost !== void 0 ? /* @__PURE__ */ React24.createElement(Text21, { dimColor: true }, " $", rec.cost.toFixed(6)) : null, rec.usage ? /* @__PURE__ */ React24.createElement(CacheBadge, { usage: rec.usage }) : null), rec.planState ? /* @__PURE__ */ React24.createElement(PlanStateBlock, { planState: rec.planState }) : null, rec.content ? /* @__PURE__ */ React24.createElement(Text21, null, rec.content) : /* @__PURE__ */ React24.createElement(Text21, { dimColor: true, italic: true }, "(tool-call response only)"));
|
|
@@ -14486,7 +15042,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
|
|
|
14486
15042
|
setStep("mcp");
|
|
14487
15043
|
}
|
|
14488
15044
|
}
|
|
14489
|
-
), /* @__PURE__ */ React29.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React29.createElement(Text24, { dimColor: true }, "\u2191
|
|
15045
|
+
), /* @__PURE__ */ React29.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React29.createElement(Text24, { dimColor: true }, "[\u2191\u2193] navigate \xB7 [Enter] confirm \xB7 [Esc] cancel")));
|
|
14490
15046
|
}
|
|
14491
15047
|
if (step === "mcp") {
|
|
14492
15048
|
return /* @__PURE__ */ React29.createElement(StepFrame, { title: "Which MCP servers should Reasonix wire up for you?", step: 2, total: 3 }, /* @__PURE__ */ React29.createElement(
|
|
@@ -14499,7 +15055,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
|
|
|
14499
15055
|
const needsArgs = selected.some((name) => CATALOG_BY_NAME.get(name)?.userArgs);
|
|
14500
15056
|
setStep(needsArgs ? "mcpArgs" : "review");
|
|
14501
15057
|
},
|
|
14502
|
-
footer: "\u2191
|
|
15058
|
+
footer: "[\u2191\u2193] navigate \xB7 [Space] toggle \xB7 [Enter] confirm \xB7 [Esc] cancel \xB7 empty = skip"
|
|
14503
15059
|
}
|
|
14504
15060
|
));
|
|
14505
15061
|
}
|
|
@@ -14541,7 +15097,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
|
|
|
14541
15097
|
), specs.map((spec, i) => (
|
|
14542
15098
|
// biome-ignore lint/suspicious/noArrayIndexKey: review-only render, order fixed
|
|
14543
15099
|
/* @__PURE__ */ React29.createElement(Box25, { key: i, paddingLeft: 14 }, /* @__PURE__ */ React29.createElement(Text24, { dimColor: true }, "\xB7 ", spec))
|
|
14544
|
-
)), /* @__PURE__ */ React29.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React29.createElement(Text24, null, "Saves to ", defaultConfigPath())), error ? /* @__PURE__ */ React29.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React29.createElement(Text24, { color: "red" }, error)) : null, /* @__PURE__ */ React29.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React29.createElement(Text24, { dimColor: true }, "
|
|
15100
|
+
)), /* @__PURE__ */ React29.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React29.createElement(Text24, null, "Saves to ", defaultConfigPath())), error ? /* @__PURE__ */ React29.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React29.createElement(Text24, { color: "red" }, error)) : null, /* @__PURE__ */ React29.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React29.createElement(Text24, { dimColor: true }, "[Enter] save \xB7 [Esc] cancel"))), /* @__PURE__ */ React29.createElement(
|
|
14545
15101
|
ReviewConfirm,
|
|
14546
15102
|
{
|
|
14547
15103
|
onConfirm: () => {
|
|
@@ -14567,7 +15123,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
|
|
|
14567
15123
|
}
|
|
14568
15124
|
));
|
|
14569
15125
|
}
|
|
14570
|
-
return /* @__PURE__ */ React29.createElement(Box25, { flexDirection: "column", borderStyle: "round", borderColor: "green", paddingX: 1 }, /* @__PURE__ */ React29.createElement(Text24, { bold: true, color: "green" }, "\u25B8 Saved."), /* @__PURE__ */ React29.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React29.createElement(Text24, null, "Run `reasonix` any time to start chatting \u2014 your settings are remembered.")), /* @__PURE__ */ React29.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React29.createElement(Text24, { dimColor: true }, "
|
|
15126
|
+
return /* @__PURE__ */ React29.createElement(Box25, { flexDirection: "column", borderStyle: "round", borderColor: "green", paddingX: 1 }, /* @__PURE__ */ React29.createElement(Text24, { bold: true, color: "green" }, "\u25B8 Saved."), /* @__PURE__ */ React29.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React29.createElement(Text24, null, "Run `reasonix` any time to start chatting \u2014 your settings are remembered.")), /* @__PURE__ */ React29.createElement(Box25, { marginTop: 1 }, /* @__PURE__ */ React29.createElement(Text24, { dimColor: true }, "[Enter] to exit")), /* @__PURE__ */ React29.createElement(ExitOnEnter, { onExit: exit2 }));
|
|
14571
15127
|
}
|
|
14572
15128
|
function ApiKeyStep({
|
|
14573
15129
|
onSubmit,
|
|
@@ -14846,6 +15402,12 @@ Every factual claim about a codebase must be backed by evidence. Reasonix VALIDA
|
|
|
14846
15402
|
|
|
14847
15403
|
Asserting absence without checking is how evaluative answers go wrong. Treat the urge to write "missing" as a red flag in your own reasoning.
|
|
14848
15404
|
|
|
15405
|
+
# Don't invent what changes \u2014 search instead
|
|
15406
|
+
|
|
15407
|
+
Your training data has a cutoff. When an answer's correctness depends on something that changes over time (the user is asking what's happening, not what's true) and a search tool is available, search first. Inventing currently-correct values from training memory is the most common way these answers go wrong, and the user usually can't tell until much later.
|
|
15408
|
+
|
|
15409
|
+
The signal isn't a topic list \u2014 it's: "if I'm wrong about this, is it because reality moved on?". If yes, ground the answer in fresh evidence; if no (definitions, mechanisms, well-established APIs), answer from memory.
|
|
15410
|
+
|
|
14849
15411
|
${ESCALATION_CONTRACT}`;
|
|
14850
15412
|
var program = new Command();
|
|
14851
15413
|
program.name("reasonix").description("DeepSeek-native agent framework \u2014 built for cache hits and cheap tokens.").version(VERSION);
|