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 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
- env: process.env
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. Use this when the question needs information more current than your training data, when you're unsure of a factual detail, or when the user asks about a specific webpage/library/release you haven't seen.",
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: "cyan", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React4.createElement(Box4, null, /* @__PURE__ */ React4.createElement(Text4, { bold: true, color: "cyan" }, "\u25B8 model wants to edit a file")), /* @__PURE__ */ React4.createElement(Box4, null, /* @__PURE__ */ React4.createElement(Text4, { color: "cyan", dimColor: true }, "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")), /* @__PURE__ */ React4.createElement(Box4, { marginTop: 1 }, /* @__PURE__ */ React4.createElement(Text4, null, /* @__PURE__ */ React4.createElement(Text4, { color: isNew ? "green" : "yellow", bold: true }, `[${tag}] `), /* @__PURE__ */ React4.createElement(Text4, { color: "cyan" }, block.path), /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, ` (-${removed} +${added} lines)`), showScrollHud ? /* @__PURE__ */ React4.createElement(Text4, { dimColor: true }, ` \xB7 viewing ${effectiveScroll + 1}-${effectiveScroll + visibleLines.length}/${totalLines}`) : null)), hiddenAbove > 0 ? /* @__PURE__ */ React4.createElement(
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(/\\\(\s*/g, "").replace(/\s*\\\)/g, "").replace(/\\\[\s*/g, "\n").replace(/\s*\\\]/g, "\n").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\b/g, "\u2208").replace(/\\notin\b/g, "\u2209").replace(/\\infty/g, "\u221E").replace(/\\sum\b/g, "\u03A3").replace(/\\prod\b/g, "\u03A0").replace(/\\int\b/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\b/g, "\u21D2").replace(/\\iff\b/g, "\u21D4").replace(/\\to\b/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(/\^\{([\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, " ");
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]+)\)|\*\*([^*\n]+?)\*\*|```([^\n]+?)```|`([^`\n]+?)`|(?<![*\w])\*([^*\n]+?)\*(?!\w))/g;
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: `b${idx++}`, bold: true }, m[4])
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: `c${idx++}`, color: "yellow" }, stripped)
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" }, m[6])
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: `i${idx++}`, italic: true }, m[7])
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(/\[([^\]\n]+)\]\(([^)\n]+)\)/g, "$1").replace(/\*\*([^*\n]+?)\*\*/g, "$1").replace(/```([^\n]+?)```/g, (_m, c) => c.replace(/^(\w+)\s+/, "")).replace(/`([^`\n]+?)`/g, "$1").replace(/(?<![*\w])\*([^*\n]+?)\*(?!\w)/g, "$1");
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
- out.push({ kind: "paragraph", text: para.join(" ") });
8010
- para = [];
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
- para.push(line);
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(InlineMd, { text: block.text, citations });
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.ordered ? ` ${block.start + i}. ` : " \u2022 "), /* @__PURE__ */ React7.createElement(InlineMd, { text: item, citations }))));
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
- if (toolName === "read_file") {
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 (toolName === "list_directory" || toolName === "directory_tree") {
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 (toolName === "search_files" || toolName === "search_content") {
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 (toolName === "run_command" || toolName === "run_background") {
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 processMultilineKey(value, cursor, key) {
8914
- if (key.tab || key.escape || key.pageUp || key.pageDown) {
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
- return moved === cursor ? NOOP : { next: null, cursor: moved, submit: false };
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
- return moved === cursor ? NOOP : { next: null, cursor: moved, submit: false };
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) onSubmit(action.submitValue ?? value);
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 effectivePlaceholder = disabled ? placeholder ?? "\u2026waiting for response\u2026 \xB7 [Esc] to stop" : placeholder ?? "type a message, or /command \xB7 [Shift+Enter] / [Ctrl+J] newline";
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
- return /* @__PURE__ */ React15.createElement(Box14, { borderStyle: "round", borderColor, paddingX: 1, flexDirection: "column" }, lines.map((line, i) => {
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
- // biome-ignore lint/suspicious/noArrayIndexKey: stable by construction — lines are derived from `value.split("\n")` and never reordered
9074
- /* @__PURE__ */ React15.createElement(Box14, { key: i }, isFirst ? /* @__PURE__ */ React15.createElement(Text14, { bold: true, color: borderColor }, "you \u203A", " ") : /* @__PURE__ */ React15.createElement(Text14, { dimColor: true }, " "), showPlaceholder ? /* @__PURE__ */ React15.createElement(React15.Fragment, null, isCursorLine && !disabled ? /* @__PURE__ */ React15.createElement(Text14, { color: borderColor }, showCursor ? "\u258C" : " ") : null, /* @__PURE__ */ React15.createElement(Text14, { dimColor: true }, effectivePlaceholder)) : isCursorLine && !disabled ? /* @__PURE__ */ React15.createElement(
9075
- LineWithCursor,
9076
- {
9077
- line,
9078
- col: cursorCol,
9079
- showCursor,
9080
- borderColor
9081
- }
9082
- ) : /* @__PURE__ */ React15.createElement(Text14, null, line))
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(Text14, null, before), /* @__PURE__ */ React15.createElement(Text14, { color: borderColor }, showCursor ? "\u258C" : " "));
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(Text14, null, before), /* @__PURE__ */ React15.createElement(Text14, { inverse: showCursor }, atCursor), /* @__PURE__ */ React15.createElement(Text14, null, after));
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: "yellow", paddingX: 1, marginY: 1 }, /* @__PURE__ */ React16.createElement(Box15, null, /* @__PURE__ */ React16.createElement(Text15, { bold: true, color: "yellow" }, isBackground ? "\u25B8 model wants to start a BACKGROUND process" : "\u25B8 model wants to run a shell command")), isBackground ? /* @__PURE__ */ React16.createElement(Box15, null, /* @__PURE__ */ React16.createElement(Text15, { dimColor: true }, " (long-running: dev server / watcher; keeps running after approval, /kill to stop)")) : null, /* @__PURE__ */ React16.createElement(Box15, null, /* @__PURE__ */ React16.createElement(Text15, { color: "yellow", dimColor: true }, "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")), /* @__PURE__ */ React16.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text15, null, /* @__PURE__ */ React16.createElement(Text15, { dimColor: true }, "$ "), /* @__PURE__ */ React16.createElement(Text15, { color: "cyan" }, command))), /* @__PURE__ */ React16.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React16.createElement(
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 useStdout3 } from "ink";
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 } = useStdout3();
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 /* @__PURE__ */ React19.createElement(Box18, { borderStyle: "round", borderColor: "cyan", flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React19.createElement(
9279
- Header,
9280
- {
9281
- model: model2,
9282
- prefixHash,
9283
- harvestOn,
9284
- branchOn,
9285
- branchBudget: branchBudget ?? 1,
9286
- reasoningEffort,
9287
- planMode,
9288
- editMode,
9289
- turns: summary.turns,
9290
- updateAvailable,
9291
- narrow,
9292
- busy: busy ?? false,
9293
- proArmed: proArmed ?? false,
9294
- escalated: escalated ?? false
9295
- }
9296
- ), narrow ? /* @__PURE__ */ React19.createElement(
9297
- StackedMetrics,
9298
- {
9299
- summary,
9300
- ctxRatio,
9301
- ctxMax,
9302
- balance,
9303
- coldStart
9304
- }
9305
- ) : /* @__PURE__ */ React19.createElement(
9306
- InlineMetrics,
9307
- {
9308
- summary,
9309
- ctxRatio,
9310
- ctxMax,
9311
- balance,
9312
- coldStart
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 visible scrollback only. Context (message log) is intact \u2014 next turn still sees everything. Use /new to start fresh, or /forget to delete the session entirely."
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 when buffer empty",
10137
- " Ctrl+A / Ctrl+E jump to start / end of the current line",
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 assistant hasn't proposed edits since the last /apply or /discard.";
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 to move \xB7 Enter to pick")));
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
- return /* @__PURE__ */ React24.createElement(Box22, { marginTop: 1 }, /* @__PURE__ */ React24.createElement(Text21, { bold: true, color: "cyan" }, "you \u203A", " "), /* @__PURE__ */ React24.createElement(Text21, null, rec.content));
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/\u2193 move \xB7 enter confirm \xB7 esc cancel")));
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/\u2193 move \xB7 space toggle \xB7 enter confirm \xB7 esc cancel \xB7 leave empty to skip"
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 }, "enter save \xB7 esc cancel"))), /* @__PURE__ */ React29.createElement(
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 }, "Press enter to exit.")), /* @__PURE__ */ React29.createElement(ExitOnEnter, { onExit: exit2 }));
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);