reasonix 0.5.8 → 0.5.13

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
@@ -2589,7 +2589,7 @@ var DEFAULT_PICKER_IGNORE_DIRS = [
2589
2589
  "venv",
2590
2590
  "__pycache__"
2591
2591
  ];
2592
- function listFilesSync(root, opts = {}) {
2592
+ function listFilesWithStatsSync(root, opts = {}) {
2593
2593
  const maxResults = Math.max(1, opts.maxResults ?? 500);
2594
2594
  const ignore = new Set(opts.ignoreDirs ?? DEFAULT_PICKER_IGNORE_DIRS);
2595
2595
  const rootAbs = resolve(root);
@@ -2610,7 +2610,12 @@ function listFilesSync(root, opts = {}) {
2610
2610
  if (ent.name.startsWith(".") || ignore.has(ent.name)) continue;
2611
2611
  walk2(join5(dirAbs, ent.name), relPath);
2612
2612
  } else if (ent.isFile()) {
2613
- out.push(relPath);
2613
+ let mtimeMs = 0;
2614
+ try {
2615
+ mtimeMs = statSync2(join5(dirAbs, ent.name)).mtimeMs;
2616
+ } catch {
2617
+ }
2618
+ out.push({ path: relPath, mtimeMs });
2614
2619
  }
2615
2620
  }
2616
2621
  };
@@ -2625,12 +2630,31 @@ function detectAtPicker(input) {
2625
2630
  const atOffset = input.length - query.length - 1;
2626
2631
  return { query, atOffset };
2627
2632
  }
2628
- function rankPickerCandidates(files, query, limit = 40) {
2629
- if (!query) return files.slice(0, limit);
2633
+ function rankPickerCandidates(files, query, limitOrOpts) {
2634
+ const opts = typeof limitOrOpts === "number" ? { limit: limitOrOpts } : limitOrOpts ?? {};
2635
+ const limit = opts.limit ?? 40;
2636
+ const recent = new Set(opts.recentlyUsed ?? []);
2637
+ const entries = files.map(
2638
+ (f) => typeof f === "string" ? { path: f, mtimeMs: 0 } : f
2639
+ );
2640
+ if (!query) {
2641
+ const anyMtime = entries.some((e) => e.mtimeMs > 0);
2642
+ if (!anyMtime && recent.size === 0) {
2643
+ return entries.slice(0, limit).map((e) => e.path);
2644
+ }
2645
+ const sorted = [...entries].sort((a, b) => {
2646
+ const aRecent = recent.has(a.path) ? 1 : 0;
2647
+ const bRecent = recent.has(b.path) ? 1 : 0;
2648
+ if (aRecent !== bRecent) return bRecent - aRecent;
2649
+ if (a.mtimeMs !== b.mtimeMs) return b.mtimeMs - a.mtimeMs;
2650
+ return a.path.localeCompare(b.path);
2651
+ });
2652
+ return sorted.slice(0, limit).map((e) => e.path);
2653
+ }
2630
2654
  const needle = query.toLowerCase();
2631
2655
  const scored = [];
2632
- for (const f of files) {
2633
- const lower = f.toLowerCase();
2656
+ for (const e of entries) {
2657
+ const lower = e.path.toLowerCase();
2634
2658
  const hit = lower.indexOf(needle);
2635
2659
  if (hit < 0) continue;
2636
2660
  const slash = lower.lastIndexOf("/");
@@ -2638,9 +2662,18 @@ function rankPickerCandidates(files, query, limit = 40) {
2638
2662
  let score = 2;
2639
2663
  if (base.startsWith(needle)) score = 0;
2640
2664
  else if (lower.startsWith(needle)) score = 1;
2641
- scored.push({ path: f, score: score * 1e4 + hit });
2665
+ scored.push({
2666
+ path: e.path,
2667
+ score: score * 1e4 + hit,
2668
+ mtimeMs: e.mtimeMs,
2669
+ recent: recent.has(e.path)
2670
+ });
2642
2671
  }
2643
- scored.sort((a, b) => a.score - b.score);
2672
+ scored.sort((a, b) => {
2673
+ if (a.score !== b.score) return a.score - b.score;
2674
+ if (a.recent !== b.recent) return a.recent ? -1 : 1;
2675
+ return b.mtimeMs - a.mtimeMs;
2676
+ });
2644
2677
  return scored.slice(0, limit).map((s) => s.path);
2645
2678
  }
2646
2679
  var AT_MENTION_PATTERN = /(?<=^|\s)@([a-zA-Z0-9_./\\-]+)/g;
@@ -5606,11 +5639,80 @@ function formatLogSize(path = defaultUsageLogPath()) {
5606
5639
  // src/cli/commands/chat.tsx
5607
5640
  import { existsSync as existsSync10, statSync as statSync6 } from "fs";
5608
5641
  import { render } from "ink";
5609
- import React16, { useState as useState7 } from "react";
5642
+ import React17, { useState as useState7 } from "react";
5610
5643
 
5611
5644
  // src/cli/ui/App.tsx
5612
- import { Box as Box12, Static, Text as Text12, useApp, useInput as useInput4 } from "ink";
5613
- import React13, { useCallback, useEffect as useEffect2, useMemo, useRef as useRef2, useState as useState5 } from "react";
5645
+ import { Box as Box13, Static, Text as Text13, useApp, useInput as useInput4 } from "ink";
5646
+ import React14, { useCallback, useEffect as useEffect2, useMemo, useRef as useRef2, useState as useState5 } from "react";
5647
+
5648
+ // src/code/diff-preview.ts
5649
+ function formatEditBlockDiff(block, opts = {}) {
5650
+ const contextLines = Math.max(0, opts.contextLines ?? 2);
5651
+ const maxLines = Math.max(4, opts.maxLines ?? 20);
5652
+ const indent = opts.indent ?? " ";
5653
+ const search = block.search === "" ? [] : block.search.split("\n");
5654
+ const replace = block.replace.split("\n");
5655
+ if (search.length === 0) {
5656
+ return renderAllPlus(replace, indent, maxLines);
5657
+ }
5658
+ let leading = 0;
5659
+ while (leading < search.length && leading < replace.length && search[leading] === replace[leading]) {
5660
+ leading++;
5661
+ }
5662
+ let trailing = 0;
5663
+ while (trailing < search.length - leading && trailing < replace.length - leading && search[search.length - 1 - trailing] === replace[replace.length - 1 - trailing]) {
5664
+ trailing++;
5665
+ }
5666
+ const searchMiddle = search.slice(leading, search.length - trailing);
5667
+ const replaceMiddle = replace.slice(leading, replace.length - trailing);
5668
+ const leadShown = search.slice(Math.max(0, leading - contextLines), leading);
5669
+ const leadHidden = leading - leadShown.length;
5670
+ const trailShown = search.slice(
5671
+ search.length - trailing,
5672
+ search.length - trailing + contextLines
5673
+ );
5674
+ const trailHidden = trailing - trailShown.length;
5675
+ const out = [];
5676
+ if (leadHidden > 0) {
5677
+ out.push(`${indent} \u2026 ${leadHidden} unchanged line${leadHidden === 1 ? "" : "s"} above`);
5678
+ }
5679
+ for (const l of leadShown) out.push(`${indent} ${l}`);
5680
+ for (const l of searchMiddle) out.push(`${indent}- ${l}`);
5681
+ for (const l of replaceMiddle) out.push(`${indent}+ ${l}`);
5682
+ for (const l of trailShown) out.push(`${indent} ${l}`);
5683
+ if (trailHidden > 0) {
5684
+ out.push(`${indent} \u2026 ${trailHidden} unchanged line${trailHidden === 1 ? "" : "s"} below`);
5685
+ }
5686
+ return capLines(out, maxLines, indent);
5687
+ }
5688
+ function formatAllBlockDiffs(blocks, opts = {}) {
5689
+ const out = [];
5690
+ for (let i = 0; i < blocks.length; i++) {
5691
+ const b = blocks[i];
5692
+ const removed = b.search === "" ? 0 : countLines2(b.search);
5693
+ const added = countLines2(b.replace);
5694
+ const tag = b.search === "" ? "NEW " : " ";
5695
+ if (i > 0) out.push("");
5696
+ out.push(` ${tag}${b.path} (-${removed} +${added} lines)`);
5697
+ out.push(...formatEditBlockDiff(b, opts));
5698
+ }
5699
+ return out;
5700
+ }
5701
+ function countLines2(s) {
5702
+ if (s.length === 0) return 0;
5703
+ return (s.match(/\n/g)?.length ?? 0) + 1;
5704
+ }
5705
+ function renderAllPlus(lines, indent, maxLines) {
5706
+ const out = lines.map((l) => `${indent}+ ${l}`);
5707
+ return capLines(out, maxLines, indent);
5708
+ }
5709
+ function capLines(lines, maxLines, indent) {
5710
+ if (lines.length <= maxLines) return lines;
5711
+ const head = lines.slice(0, maxLines - 1);
5712
+ const hidden = lines.length - head.length;
5713
+ head.push(`${indent}\u2026 (${hidden} more diff lines \u2014 full content applies on /apply)`);
5714
+ return head;
5715
+ }
5614
5716
 
5615
5717
  // src/tools/skills.ts
5616
5718
  function registerSkillTools(registry, opts = {}) {
@@ -6792,16 +6894,49 @@ function derivePrefix(command) {
6792
6894
  return TWO_TOKEN_WRAPPERS.has(first) ? `${first} ${tokens[1]}` : first;
6793
6895
  }
6794
6896
 
6795
- // src/cli/ui/SlashSuggestions.tsx
6897
+ // src/cli/ui/SlashArgPicker.tsx
6796
6898
  import { Box as Box10, Text as Text10 } from "ink";
6797
6899
  import React11 from "react";
6900
+ function SlashArgPicker({
6901
+ matches,
6902
+ selectedIndex,
6903
+ spec,
6904
+ kind,
6905
+ partial
6906
+ }) {
6907
+ if (kind === "hint") {
6908
+ return /* @__PURE__ */ React11.createElement(Box10, { paddingX: 1 }, /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, " ", /* @__PURE__ */ React11.createElement(Text10, { bold: true }, "/", spec.cmd), spec.argsHint ? ` ${spec.argsHint}` : "", " \u2014 ", spec.summary));
6909
+ }
6910
+ if (matches === null) return null;
6911
+ if (matches.length === 0) {
6912
+ return /* @__PURE__ */ React11.createElement(Box10, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, " ", /* @__PURE__ */ React11.createElement(Text10, { bold: true }, "/", spec.cmd), spec.argsHint ? ` ${spec.argsHint}` : "", " \u2014 ", spec.summary), /* @__PURE__ */ React11.createElement(Text10, { color: "yellow" }, ' no match for "', partial, '" \u2014 keep typing, or Backspace to edit'));
6913
+ }
6914
+ const MAX = 8;
6915
+ const total = matches.length;
6916
+ const windowStart = total <= MAX ? 0 : Math.max(0, Math.min(selectedIndex - Math.floor(MAX / 2), total - MAX));
6917
+ const shown = matches.slice(windowStart, windowStart + MAX);
6918
+ const hiddenAbove = windowStart;
6919
+ const hiddenBelow = total - windowStart - shown.length;
6920
+ return /* @__PURE__ */ React11.createElement(Box10, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, " ", /* @__PURE__ */ React11.createElement(Text10, { bold: true }, "/", spec.cmd), spec.argsHint ? ` ${spec.argsHint}` : "", " \u2014 ", spec.summary), hiddenAbove > 0 ? /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, " \u2191 ", hiddenAbove, " more above") : null, shown.map((value, i) => /* @__PURE__ */ React11.createElement(ArgRow, { key: value, value, isSelected: windowStart + i === selectedIndex })), hiddenBelow > 0 ? /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, " \u2193 ", hiddenBelow, " more below") : null, /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, " [\u2191\u2193] navigate \xB7 [Tab]/[Enter] pick"));
6921
+ }
6922
+ function ArgRow({ value, isSelected }) {
6923
+ const marker = isSelected ? "\u25B8" : " ";
6924
+ if (isSelected) {
6925
+ return /* @__PURE__ */ React11.createElement(Box10, null, /* @__PURE__ */ React11.createElement(Text10, { bold: true, color: "cyan" }, marker, " ", value));
6926
+ }
6927
+ return /* @__PURE__ */ React11.createElement(Box10, null, /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, marker, " ", value));
6928
+ }
6929
+
6930
+ // src/cli/ui/SlashSuggestions.tsx
6931
+ import { Box as Box11, Text as Text11 } from "ink";
6932
+ import React12 from "react";
6798
6933
  function SlashSuggestions({
6799
6934
  matches,
6800
6935
  selectedIndex
6801
6936
  }) {
6802
6937
  if (matches === null) return null;
6803
6938
  if (matches.length === 0) {
6804
- return /* @__PURE__ */ React11.createElement(Box10, { paddingX: 1 }, /* @__PURE__ */ React11.createElement(Text10, { color: "yellow" }, "no slash command matches that prefix"), /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, " \u2014 Backspace to edit, or /help for the full list"));
6939
+ return /* @__PURE__ */ React12.createElement(Box11, { paddingX: 1 }, /* @__PURE__ */ React12.createElement(Text11, { color: "yellow" }, "no slash command matches that prefix"), /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, " \u2014 Backspace to edit, or /help for the full list"));
6805
6940
  }
6806
6941
  const MAX = 8;
6807
6942
  const total = matches.length;
@@ -6809,21 +6944,21 @@ function SlashSuggestions({
6809
6944
  const shown = matches.slice(windowStart, windowStart + MAX);
6810
6945
  const hiddenAbove = windowStart;
6811
6946
  const hiddenBelow = total - windowStart - shown.length;
6812
- return /* @__PURE__ */ React11.createElement(Box10, { flexDirection: "column", paddingX: 1 }, hiddenAbove > 0 ? /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, " \u2191 ", hiddenAbove, " more above") : null, shown.map((spec, i) => /* @__PURE__ */ React11.createElement(SuggestionRow, { key: spec.cmd, spec, isSelected: windowStart + i === selectedIndex })), hiddenBelow > 0 ? /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, " \u2193 ", hiddenBelow, " more below") : null, /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, " [\u2191\u2193] navigate \xB7 [Tab]/[Enter] pick"));
6947
+ return /* @__PURE__ */ React12.createElement(Box11, { flexDirection: "column", paddingX: 1 }, hiddenAbove > 0 ? /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, " \u2191 ", hiddenAbove, " more above") : null, shown.map((spec, i) => /* @__PURE__ */ React12.createElement(SuggestionRow, { key: spec.cmd, spec, isSelected: windowStart + i === selectedIndex })), hiddenBelow > 0 ? /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, " \u2193 ", hiddenBelow, " more below") : null, /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, " [\u2191\u2193] navigate \xB7 [Tab]/[Enter] pick"));
6813
6948
  }
6814
6949
  function SuggestionRow({ spec, isSelected }) {
6815
6950
  const marker = isSelected ? "\u25B8" : " ";
6816
6951
  const name = `/${spec.cmd}`;
6817
6952
  const argsSuffix = spec.argsHint ? ` ${spec.argsHint}` : "";
6818
6953
  if (isSelected) {
6819
- return /* @__PURE__ */ React11.createElement(Box10, null, /* @__PURE__ */ React11.createElement(Text10, { bold: true, color: "cyan" }, marker, " ", name.padEnd(12), argsSuffix.padEnd(16)), /* @__PURE__ */ React11.createElement(Text10, { color: "cyan" }, " ", spec.summary));
6954
+ return /* @__PURE__ */ React12.createElement(Box11, null, /* @__PURE__ */ React12.createElement(Text11, { bold: true, color: "cyan" }, marker, " ", name.padEnd(12), argsSuffix.padEnd(16)), /* @__PURE__ */ React12.createElement(Text11, { color: "cyan" }, " ", spec.summary));
6820
6955
  }
6821
- return /* @__PURE__ */ React11.createElement(Box10, null, /* @__PURE__ */ React11.createElement(Text10, { dimColor: true }, marker, " ", name.padEnd(12), argsSuffix.padEnd(16), " ", spec.summary));
6956
+ return /* @__PURE__ */ React12.createElement(Box11, null, /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, marker, " ", name.padEnd(12), argsSuffix.padEnd(16), " ", spec.summary));
6822
6957
  }
6823
6958
 
6824
6959
  // src/cli/ui/StatsPanel.tsx
6825
- import { Box as Box11, Text as Text11, useStdout as useStdout2 } from "ink";
6826
- import React12 from "react";
6960
+ import { Box as Box12, Text as Text12, useStdout as useStdout2 } from "ink";
6961
+ import React13 from "react";
6827
6962
  var WORDMARK_STYLES = [
6828
6963
  { ch: "\u25C8", color: "#5eead4", isLogo: true },
6829
6964
  // teal — brand mark
@@ -6849,7 +6984,7 @@ function Wordmark({ busy }) {
6849
6984
  const tick = useTick();
6850
6985
  const period = busy ? 5 : 12;
6851
6986
  const bright = Math.floor(tick / period) % 2 === 0;
6852
- return /* @__PURE__ */ React12.createElement(Text11, null, WORDMARK_STYLES.map((c) => /* @__PURE__ */ React12.createElement(Text11, { key: `${c.ch}-${c.color}`, color: c.color, bold: c.isLogo ? bright : true }, c.ch)));
6987
+ return /* @__PURE__ */ React13.createElement(Text12, null, WORDMARK_STYLES.map((c) => /* @__PURE__ */ React13.createElement(Text12, { key: `${c.ch}-${c.color}`, color: c.color, bold: c.isLogo ? bright : true }, c.ch)));
6853
6988
  }
6854
6989
  var NARROW_BREAKPOINT = 120;
6855
6990
  var COLD_START_TURNS = 3;
@@ -6871,7 +7006,7 @@ function StatsPanel({
6871
7006
  const columns = stdout2?.columns ?? 80;
6872
7007
  const narrow = columns < NARROW_BREAKPOINT;
6873
7008
  const coldStart = summary.turns <= COLD_START_TURNS;
6874
- return /* @__PURE__ */ React12.createElement(Box11, { borderStyle: "round", borderColor: "cyan", flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React12.createElement(
7009
+ return /* @__PURE__ */ React13.createElement(Box12, { borderStyle: "round", borderColor: "cyan", flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React13.createElement(
6875
7010
  Header,
6876
7011
  {
6877
7012
  model,
@@ -6885,7 +7020,7 @@ function StatsPanel({
6885
7020
  narrow,
6886
7021
  busy: busy ?? false
6887
7022
  }
6888
- ), narrow ? /* @__PURE__ */ React12.createElement(
7023
+ ), narrow ? /* @__PURE__ */ React13.createElement(
6889
7024
  StackedMetrics,
6890
7025
  {
6891
7026
  summary,
@@ -6894,7 +7029,7 @@ function StatsPanel({
6894
7029
  balance,
6895
7030
  coldStart
6896
7031
  }
6897
- ) : /* @__PURE__ */ React12.createElement(
7032
+ ) : /* @__PURE__ */ React13.createElement(
6898
7033
  InlineMetrics,
6899
7034
  {
6900
7035
  summary,
@@ -6917,7 +7052,7 @@ function Header({
6917
7052
  narrow,
6918
7053
  busy
6919
7054
  }) {
6920
- return /* @__PURE__ */ React12.createElement(Box11, { justifyContent: "space-between" }, /* @__PURE__ */ React12.createElement(Box11, null, /* @__PURE__ */ React12.createElement(Wordmark, { busy }), /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, ` v${VERSION}`), /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, " \xB7 "), /* @__PURE__ */ React12.createElement(Text11, { color: "yellow" }, model), narrow ? null : /* @__PURE__ */ React12.createElement(React12.Fragment, null, /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, " \xB7 "), /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, prefixHash)), harvestOn ? /* @__PURE__ */ React12.createElement(Text11, { color: "magenta" }, " \xB7 harvest") : null, branchOn ? /* @__PURE__ */ React12.createElement(Text11, { color: "blue" }, " \xB7 branch", branchBudget) : null, planMode ? /* @__PURE__ */ React12.createElement(Text11, { color: "red", bold: true }, " \xB7 PLAN") : null), /* @__PURE__ */ React12.createElement(Text11, null, updateAvailable ? /* @__PURE__ */ React12.createElement(Text11, { color: "yellow", bold: true }, `update: ${updateAvailable} \xB7 `) : null, /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, narrow ? `turn ${turns}` : `turn ${turns} \xB7 /help`)));
7055
+ return /* @__PURE__ */ React13.createElement(Box12, { justifyContent: "space-between" }, /* @__PURE__ */ React13.createElement(Box12, null, /* @__PURE__ */ React13.createElement(Wordmark, { busy }), /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, ` v${VERSION}`), /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, " \xB7 "), /* @__PURE__ */ React13.createElement(Text12, { color: "yellow" }, model), narrow ? null : /* @__PURE__ */ React13.createElement(React13.Fragment, null, /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, " \xB7 "), /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, prefixHash)), harvestOn ? /* @__PURE__ */ React13.createElement(Text12, { color: "magenta" }, " \xB7 harvest") : null, branchOn ? /* @__PURE__ */ React13.createElement(Text12, { color: "blue" }, " \xB7 branch", branchBudget) : null, planMode ? /* @__PURE__ */ React13.createElement(Text12, { color: "red", bold: true }, " \xB7 PLAN") : null), /* @__PURE__ */ React13.createElement(Text12, null, updateAvailable ? /* @__PURE__ */ React13.createElement(Text12, { color: "yellow", bold: true }, `update: ${updateAvailable} \xB7 `) : null, /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, narrow ? `turn ${turns}` : `turn ${turns} \xB7 /help`)));
6921
7056
  }
6922
7057
  function InlineMetrics({
6923
7058
  summary,
@@ -6926,7 +7061,7 @@ function InlineMetrics({
6926
7061
  balance,
6927
7062
  coldStart
6928
7063
  }) {
6929
- return /* @__PURE__ */ React12.createElement(Box11, { marginTop: 1, gap: 3 }, /* @__PURE__ */ React12.createElement(ContextCell, { ratio: ctxRatio, promptTokens: summary.lastPromptTokens, ctxMax }), /* @__PURE__ */ React12.createElement(CacheCell, { hitRatio: summary.cacheHitRatio, coldStart, turns: summary.turns }), /* @__PURE__ */ React12.createElement(CostCell, { summary, coldStart }), balance ? /* @__PURE__ */ React12.createElement(BalanceCell, { balance }) : null);
7064
+ return /* @__PURE__ */ React13.createElement(Box12, { marginTop: 1, gap: 3 }, /* @__PURE__ */ React13.createElement(ContextCell, { ratio: ctxRatio, promptTokens: summary.lastPromptTokens, ctxMax }), /* @__PURE__ */ React13.createElement(CacheCell, { hitRatio: summary.cacheHitRatio, coldStart, turns: summary.turns }), /* @__PURE__ */ React13.createElement(CostCell, { summary, coldStart }), balance ? /* @__PURE__ */ React13.createElement(BalanceCell, { balance }) : null);
6930
7065
  }
6931
7066
  function StackedMetrics({
6932
7067
  summary,
@@ -6935,7 +7070,7 @@ function StackedMetrics({
6935
7070
  balance,
6936
7071
  coldStart
6937
7072
  }) {
6938
- return /* @__PURE__ */ React12.createElement(Box11, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React12.createElement(
7073
+ return /* @__PURE__ */ React13.createElement(Box12, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React13.createElement(
6939
7074
  ContextCell,
6940
7075
  {
6941
7076
  ratio: ctxRatio,
@@ -6943,7 +7078,7 @@ function StackedMetrics({
6943
7078
  ctxMax,
6944
7079
  showBar: true
6945
7080
  }
6946
- ), balance ? /* @__PURE__ */ React12.createElement(BalanceCell, { balance }) : null, /* @__PURE__ */ React12.createElement(CacheCell, { hitRatio: summary.cacheHitRatio, coldStart, turns: summary.turns }), /* @__PURE__ */ React12.createElement(CostCell, { summary, coldStart }));
7081
+ ), balance ? /* @__PURE__ */ React13.createElement(BalanceCell, { balance }) : null, /* @__PURE__ */ React13.createElement(CacheCell, { hitRatio: summary.cacheHitRatio, coldStart, turns: summary.turns }), /* @__PURE__ */ React13.createElement(CostCell, { summary, coldStart }));
6947
7082
  }
6948
7083
  function ContextCell({
6949
7084
  ratio,
@@ -6952,11 +7087,11 @@ function ContextCell({
6952
7087
  showBar
6953
7088
  }) {
6954
7089
  if (promptTokens === 0) {
6955
- return /* @__PURE__ */ React12.createElement(Text11, null, /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "ctx "), /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "\u2014 (no turns yet)"));
7090
+ return /* @__PURE__ */ React13.createElement(Text12, null, /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, "ctx "), /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, "\u2014 (no turns yet)"));
6956
7091
  }
6957
7092
  const color = ratio >= 0.8 ? "red" : ratio >= 0.6 ? "yellow" : "green";
6958
7093
  const pct2 = Math.round(ratio * 100);
6959
- return /* @__PURE__ */ React12.createElement(Text11, null, /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "ctx "), showBar ? /* @__PURE__ */ React12.createElement(Bar, { ratio, color }) : null, showBar ? /* @__PURE__ */ React12.createElement(Text11, null, " ") : null, /* @__PURE__ */ React12.createElement(Text11, { color, bold: true }, formatTokens(promptTokens), "/", formatTokens(ctxMax)), /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, " (", pct2, "%)"), ratio >= 0.8 ? /* @__PURE__ */ React12.createElement(Text11, { color: "red", bold: true }, " \xB7 /compact") : null);
7094
+ return /* @__PURE__ */ React13.createElement(Text12, null, /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, "ctx "), showBar ? /* @__PURE__ */ React13.createElement(Bar, { ratio, color }) : null, showBar ? /* @__PURE__ */ React13.createElement(Text12, null, " ") : null, /* @__PURE__ */ React13.createElement(Text12, { color, bold: true }, formatTokens(promptTokens), "/", formatTokens(ctxMax)), /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, " (", pct2, "%)"), ratio >= 0.8 ? /* @__PURE__ */ React13.createElement(Text12, { color: "red", bold: true }, " \xB7 /compact") : null);
6960
7095
  }
6961
7096
  function CacheCell({
6962
7097
  hitRatio,
@@ -6965,33 +7100,33 @@ function CacheCell({
6965
7100
  }) {
6966
7101
  const pct2 = (hitRatio * 100).toFixed(1);
6967
7102
  if (turns === 0) {
6968
- return /* @__PURE__ */ React12.createElement(Text11, null, /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "cache "), /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "\u2014"));
7103
+ return /* @__PURE__ */ React13.createElement(Text12, null, /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, "cache "), /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, "\u2014"));
6969
7104
  }
6970
7105
  if (coldStart) {
6971
- return /* @__PURE__ */ React12.createElement(Text11, null, /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "cache "), /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, pct2, "% "), /* @__PURE__ */ React12.createElement(Text11, { dimColor: true, italic: true }, "(cold start)"));
7106
+ return /* @__PURE__ */ React13.createElement(Text12, null, /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, "cache "), /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, pct2, "% "), /* @__PURE__ */ React13.createElement(Text12, { dimColor: true, italic: true }, "(cold start)"));
6972
7107
  }
6973
7108
  const color = hitRatio >= 0.7 ? "green" : hitRatio >= 0.4 ? "yellow" : "red";
6974
- return /* @__PURE__ */ React12.createElement(Text11, null, /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "cache "), /* @__PURE__ */ React12.createElement(Text11, { color, bold: true }, pct2, "%"));
7109
+ return /* @__PURE__ */ React13.createElement(Text12, null, /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, "cache "), /* @__PURE__ */ React13.createElement(Text12, { color, bold: true }, pct2, "%"));
6975
7110
  }
6976
7111
  function CostCell({
6977
7112
  summary,
6978
7113
  coldStart
6979
7114
  }) {
6980
7115
  if (summary.turns === 0) {
6981
- return /* @__PURE__ */ React12.createElement(Text11, null, /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "cost "), /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "\u2014"));
7116
+ return /* @__PURE__ */ React13.createElement(Text12, null, /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, "cost "), /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, "\u2014"));
6982
7117
  }
6983
7118
  const primaryColor = coldStart ? void 0 : "green";
6984
- return /* @__PURE__ */ React12.createElement(Text11, null, /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "cost "), /* @__PURE__ */ React12.createElement(Text11, { color: primaryColor, bold: !coldStart, dimColor: coldStart }, "$", summary.totalCostUsd.toFixed(6)), /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, " (in ", "$", summary.totalInputCostUsd.toFixed(6), " \xB7 out ", "$", summary.totalOutputCostUsd.toFixed(6), ")"));
7119
+ return /* @__PURE__ */ React13.createElement(Text12, null, /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, "cost "), /* @__PURE__ */ React13.createElement(Text12, { color: primaryColor, bold: !coldStart, dimColor: coldStart }, "$", summary.totalCostUsd.toFixed(6)), /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, " (in ", "$", summary.totalInputCostUsd.toFixed(6), " \xB7 out ", "$", summary.totalOutputCostUsd.toFixed(6), ")"));
6985
7120
  }
6986
7121
  function BalanceCell({ balance }) {
6987
7122
  const color = balance.total < 1 ? "red" : balance.total < 5 ? "yellow" : "green";
6988
- return /* @__PURE__ */ React12.createElement(Text11, null, /* @__PURE__ */ React12.createElement(Text11, { dimColor: true }, "balance "), /* @__PURE__ */ React12.createElement(Text11, { color, bold: true }, balance.currency === "USD" ? "$" : "", balance.total.toFixed(2), balance.currency !== "USD" ? ` ${balance.currency}` : ""));
7123
+ return /* @__PURE__ */ React13.createElement(Text12, null, /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, "balance "), /* @__PURE__ */ React13.createElement(Text12, { color, bold: true }, balance.currency === "USD" ? "$" : "", balance.total.toFixed(2), balance.currency !== "USD" ? ` ${balance.currency}` : ""));
6989
7124
  }
6990
7125
  function Bar({ ratio, color }) {
6991
7126
  const cells = 10;
6992
7127
  const filled = Math.max(0, Math.min(cells, Math.round(ratio * cells)));
6993
7128
  const bar = "\u2588".repeat(filled) + "\u2591".repeat(cells - filled);
6994
- return /* @__PURE__ */ React12.createElement(Text11, { color }, bar);
7129
+ return /* @__PURE__ */ React13.createElement(Text12, { color }, bar);
6995
7130
  }
6996
7131
  function formatTokens(n) {
6997
7132
  if (n < 1024) return String(n);
@@ -7011,6 +7146,38 @@ function formatBangUserMessage(cmd, output) {
7011
7146
  ${output}`;
7012
7147
  }
7013
7148
 
7149
+ // src/cli/ui/paste-collapse.ts
7150
+ var DEFAULT_PASTE_LINE_THRESHOLD = 40;
7151
+ var DEFAULT_PASTE_CHAR_THRESHOLD = 2e3;
7152
+ var DEFAULT_PASTE_HEAD_LINES = 10;
7153
+ function formatLongPaste(input, opts = {}) {
7154
+ const lineCap = opts.lineThreshold ?? DEFAULT_PASTE_LINE_THRESHOLD;
7155
+ const charCap = opts.charThreshold ?? DEFAULT_PASTE_CHAR_THRESHOLD;
7156
+ const headN = Math.max(1, opts.headLines ?? DEFAULT_PASTE_HEAD_LINES);
7157
+ const originalChars = input.length;
7158
+ const lines = input.split("\n");
7159
+ const originalLines = lines.length;
7160
+ if (originalChars <= charCap && originalLines <= lineCap) {
7161
+ return { displayText: input, collapsed: false, originalChars, originalLines };
7162
+ }
7163
+ const header2 = `\u25B8 pasted ${formatBytes(originalChars)} (${originalLines} lines) \u2014 first ${Math.min(headN, originalLines)} shown, full text sent to model`;
7164
+ const head = lines.slice(0, headN).join("\n");
7165
+ const remaining = originalLines - headN;
7166
+ const footer = remaining > 0 ? `\u2026 (${remaining} more line${remaining === 1 ? "" : "s"})` : "";
7167
+ const displayText = footer ? `${header2}
7168
+ ${head}
7169
+ ${footer}` : `${header2}
7170
+ ${head}`;
7171
+ return { displayText, collapsed: true, originalChars, originalLines };
7172
+ }
7173
+ function formatBytes(n) {
7174
+ if (n < 1024) return `${n} B`;
7175
+ const kb = n / 1024;
7176
+ if (kb < 1024) return `${kb.toFixed(kb >= 10 ? 0 : 1)} KB`;
7177
+ const mb = kb / 1024;
7178
+ return `${mb.toFixed(mb >= 10 ? 0 : 1)} MB`;
7179
+ }
7180
+
7014
7181
  // src/cli/ui/slash.ts
7015
7182
  import { spawnSync } from "child_process";
7016
7183
 
@@ -7127,12 +7294,28 @@ var SLASH_COMMANDS = [
7127
7294
  {
7128
7295
  cmd: "preset",
7129
7296
  argsHint: "<fast|smart|max>",
7130
- summary: "one-tap model + harvest + branch bundle"
7297
+ summary: "one-tap model + harvest + branch bundle",
7298
+ argCompleter: ["fast", "smart", "max"]
7299
+ },
7300
+ {
7301
+ cmd: "model",
7302
+ argsHint: "<id>",
7303
+ summary: "switch DeepSeek model id",
7304
+ argCompleter: "models"
7131
7305
  },
7132
- { cmd: "model", argsHint: "<id>", summary: "switch DeepSeek model id" },
7133
7306
  { cmd: "models", summary: "list available models fetched from DeepSeek /models" },
7134
- { cmd: "harvest", argsHint: "[on|off]", summary: "toggle Pillar-2 plan-state extraction" },
7135
- { cmd: "branch", argsHint: "<N|off>", summary: "run N parallel samples per turn (N>=2)" },
7307
+ {
7308
+ cmd: "harvest",
7309
+ argsHint: "[on|off]",
7310
+ summary: "toggle Pillar-2 plan-state extraction",
7311
+ argCompleter: ["on", "off"]
7312
+ },
7313
+ {
7314
+ cmd: "branch",
7315
+ argsHint: "<N|off>",
7316
+ summary: "run N parallel samples per turn (N>=2)",
7317
+ argCompleter: ["off", "2", "3", "4", "5"]
7318
+ },
7136
7319
  { cmd: "mcp", summary: "list MCP servers + tools attached to this session" },
7137
7320
  { cmd: "tool", argsHint: "[N]", summary: "dump full output of the Nth tool call (1=latest)" },
7138
7321
  {
@@ -7169,6 +7352,7 @@ var SLASH_COMMANDS = [
7169
7352
  argsHint: "[tokens]",
7170
7353
  summary: "shrink oversized tool results in the log (cap in tokens, default 4000)"
7171
7354
  },
7355
+ { cmd: "keys", summary: "show all keyboard shortcuts and prompt prefixes" },
7172
7356
  { cmd: "sessions", summary: "list saved sessions (current marked with \u25B8)" },
7173
7357
  { cmd: "forget", summary: "delete the current session from disk" },
7174
7358
  { cmd: "setup", summary: "reminds you to exit and run `reasonix setup`" },
@@ -7189,7 +7373,8 @@ var SLASH_COMMANDS = [
7189
7373
  cmd: "plan",
7190
7374
  argsHint: "[on|off]",
7191
7375
  summary: "toggle read-only plan mode (writes bounced until submit_plan + approval)",
7192
- contextual: "code"
7376
+ contextual: "code",
7377
+ argCompleter: ["on", "off"]
7193
7378
  },
7194
7379
  {
7195
7380
  cmd: "apply-plan",
@@ -7204,6 +7389,27 @@ function suggestSlashCommands(prefix, codeMode = false) {
7204
7389
  return c.cmd.startsWith(p);
7205
7390
  });
7206
7391
  }
7392
+ function detectSlashArgContext(input, codeMode = false) {
7393
+ const m = /^\/(\S+) ([\s\S]*)$/.exec(input);
7394
+ if (!m) return null;
7395
+ const cmdName = m[1].toLowerCase();
7396
+ const tail = m[2] ?? "";
7397
+ const spec = SLASH_COMMANDS.find(
7398
+ (s) => s.cmd === cmdName && (s.contextual !== "code" || codeMode)
7399
+ );
7400
+ if (!spec) return null;
7401
+ const hasInternalSpace = /\s/.test(tail);
7402
+ const partialOffset = input.length - tail.length;
7403
+ if (hasInternalSpace) {
7404
+ return { spec, partial: tail, partialOffset, kind: "hint" };
7405
+ }
7406
+ return {
7407
+ spec,
7408
+ partial: tail,
7409
+ partialOffset,
7410
+ kind: spec.argCompleter ? "picker" : "hint"
7411
+ };
7412
+ }
7207
7413
  function parseSlash(text) {
7208
7414
  if (!text.startsWith("/")) return null;
7209
7415
  const parts = text.slice(1).trim().split(/\s+/);
@@ -7229,12 +7435,42 @@ function handleSlash(cmd, args, loop, ctx = {}) {
7229
7435
  info: `\u25B8 new conversation \u2014 dropped ${dropped} message(s) from context. Same session, fresh slate.`
7230
7436
  };
7231
7437
  }
7438
+ case "keys":
7439
+ return {
7440
+ info: [
7441
+ "Keyboard & prompt shortcuts:",
7442
+ "",
7443
+ " Enter submit the current prompt",
7444
+ " Shift+Enter / Ctrl+J insert a newline (multi-line prompt)",
7445
+ " \\<Enter> bash-style line continuation",
7446
+ " \u2190 \u2192 \u2191 \u2193 move cursor / recall history when buffer empty",
7447
+ " Ctrl+A / Ctrl+E jump to start / end of the current line",
7448
+ " Backspace delete left; Delete delete under cursor",
7449
+ " Esc abort the in-flight turn",
7450
+ " y / n accept / reject pending edits (code mode)",
7451
+ "",
7452
+ "Prompt prefixes:",
7453
+ " /<name> slash command; Tab/Enter picks from the suggestion list",
7454
+ " @<path> inline a file under [Referenced files] (code mode).",
7455
+ " Trailing `@\u2026` opens a file picker; \u2191/\u2193 navigate, Tab/Enter pick.",
7456
+ " !<cmd> run <cmd> as shell in the sandbox root; output goes into context",
7457
+ " so the model sees it next turn. No allowlist gate.",
7458
+ "",
7459
+ "Pickers (slash + @-mention):",
7460
+ " \u2191 / \u2193 navigate the suggestion list",
7461
+ " Tab insert the highlighted item without submitting",
7462
+ " Enter insert and (slash) run it, (@) keep editing",
7463
+ "",
7464
+ "Useful slashes: /help \xB7 /context \xB7 /stats \xB7 /compact \xB7 /new \xB7 /exit"
7465
+ ].join("\n")
7466
+ };
7232
7467
  case "help":
7233
7468
  case "?":
7234
7469
  return {
7235
7470
  info: [
7236
7471
  "Commands:",
7237
7472
  " /help this message",
7473
+ " /keys keyboard shortcuts + prompt prefixes (!, @, /)",
7238
7474
  " /status show current settings",
7239
7475
  " /preset <fast|smart|max> one-tap presets \u2014 see below",
7240
7476
  " /model <id> deepseek-chat or deepseek-reasoner",
@@ -8178,11 +8414,19 @@ function App({
8178
8414
  const atFiles = useMemo(() => {
8179
8415
  if (!codeMode?.rootDir) return [];
8180
8416
  try {
8181
- return listFilesSync(codeMode.rootDir, { maxResults: 500 });
8417
+ return listFilesWithStatsSync(codeMode.rootDir, { maxResults: 500 });
8182
8418
  } catch {
8183
8419
  return [];
8184
8420
  }
8185
8421
  }, [codeMode?.rootDir]);
8422
+ const recentFilesRef = useRef2([]);
8423
+ const recordRecentFile = useCallback((p) => {
8424
+ const list = recentFilesRef.current;
8425
+ const i = list.indexOf(p);
8426
+ if (i >= 0) list.splice(i, 1);
8427
+ list.unshift(p);
8428
+ if (list.length > 20) list.length = 20;
8429
+ }, []);
8186
8430
  const atPicker = useMemo(() => {
8187
8431
  if (!codeMode?.rootDir) return null;
8188
8432
  if (slashMatches !== null) return null;
@@ -8190,7 +8434,10 @@ function App({
8190
8434
  }, [codeMode?.rootDir, input, slashMatches]);
8191
8435
  const atMatches = useMemo(() => {
8192
8436
  if (!atPicker) return null;
8193
- return rankPickerCandidates(atFiles, atPicker.query, 40);
8437
+ return rankPickerCandidates(atFiles, atPicker.query, {
8438
+ limit: 40,
8439
+ recentlyUsed: recentFilesRef.current
8440
+ });
8194
8441
  }, [atPicker, atFiles]);
8195
8442
  useEffect2(() => {
8196
8443
  setAtSelected((prev) => {
@@ -8207,6 +8454,45 @@ function App({
8207
8454
  },
8208
8455
  [atPicker, input]
8209
8456
  );
8457
+ const [slashArgSelected, setSlashArgSelected] = useState5(0);
8458
+ const slashArgContext = useMemo(() => {
8459
+ if (!input.startsWith("/")) return null;
8460
+ if (slashMatches !== null) return null;
8461
+ return detectSlashArgContext(input, !!codeMode);
8462
+ }, [input, slashMatches, codeMode]);
8463
+ const slashArgMatches = useMemo(() => {
8464
+ if (!slashArgContext || slashArgContext.kind !== "picker") return null;
8465
+ const completer = slashArgContext.spec.argCompleter;
8466
+ const partial = slashArgContext.partial;
8467
+ const needle = partial.toLowerCase();
8468
+ if (Array.isArray(completer)) {
8469
+ if (partial && completer.some((v) => v.toLowerCase() === needle)) return null;
8470
+ if (!partial) return completer.slice();
8471
+ return completer.filter((v) => v.toLowerCase().startsWith(needle));
8472
+ }
8473
+ if (completer === "models") {
8474
+ const all = models ?? [];
8475
+ if (partial && all.some((m) => m.toLowerCase() === needle)) return null;
8476
+ if (!partial) return all.slice(0, 40);
8477
+ return all.filter((m) => m.toLowerCase().includes(needle)).slice(0, 40);
8478
+ }
8479
+ return null;
8480
+ }, [slashArgContext, models]);
8481
+ useEffect2(() => {
8482
+ setSlashArgSelected((prev) => {
8483
+ if (!slashArgMatches || slashArgMatches.length === 0) return 0;
8484
+ if (prev >= slashArgMatches.length) return slashArgMatches.length - 1;
8485
+ return prev;
8486
+ });
8487
+ }, [slashArgMatches]);
8488
+ const pickSlashArg = useCallback(
8489
+ (chosen) => {
8490
+ if (!slashArgContext) return;
8491
+ const before = input.slice(0, slashArgContext.partialOffset);
8492
+ setInput(`${before}${chosen}`);
8493
+ },
8494
+ [slashArgContext, input]
8495
+ );
8210
8496
  const loopRef = useRef2(null);
8211
8497
  const subagentSinkRef = useRef2({ current: null });
8212
8498
  const loop = useMemo(() => {
@@ -8391,6 +8677,21 @@ function App({
8391
8677
  return;
8392
8678
  }
8393
8679
  }
8680
+ if (slashArgMatches && slashArgMatches.length > 0) {
8681
+ if (key.upArrow) {
8682
+ setSlashArgSelected((i) => Math.max(0, i - 1));
8683
+ return;
8684
+ }
8685
+ if (key.downArrow) {
8686
+ setSlashArgSelected((i) => Math.min(slashArgMatches.length - 1, i + 1));
8687
+ return;
8688
+ }
8689
+ if (key.tab) {
8690
+ const sel = slashArgMatches[slashArgSelected] ?? slashArgMatches[0];
8691
+ if (sel) pickSlashArg(sel);
8692
+ return;
8693
+ }
8694
+ }
8394
8695
  if (slashMatches && slashMatches.length > 0) {
8395
8696
  if (key.upArrow) {
8396
8697
  setSlashSelected((i) => Math.max(0, i - 1));
@@ -8483,6 +8784,13 @@ function App({
8483
8784
  return;
8484
8785
  }
8485
8786
  }
8787
+ if (slashArgMatches && slashArgMatches.length > 0 && slashArgContext) {
8788
+ const sel = slashArgMatches[slashArgSelected] ?? slashArgMatches[0];
8789
+ if (sel) {
8790
+ pickSlashArg(sel);
8791
+ return;
8792
+ }
8793
+ }
8486
8794
  if (text.startsWith("/") && !text.includes(" ")) {
8487
8795
  const typed = text.slice(1).toLowerCase();
8488
8796
  const matches = suggestSlashCommands(typed, !!codeMode);
@@ -8632,13 +8940,19 @@ function App({
8632
8940
  if (promptReport.blocked) return;
8633
8941
  }
8634
8942
  promptHistory.current.push(text);
8943
+ const pasteDisplay = formatLongPaste(text);
8635
8944
  setHistorical((prev) => [
8636
8945
  ...prev,
8637
8946
  // `leadSeparator`: thin rule above this user turn when history
8638
8947
  // isn't empty — visual pacing for multi-turn sessions. First
8639
8948
  // user message leaves it off so the UI doesn't open with a
8640
8949
  // dangling divider.
8641
- { id: `u-${Date.now()}`, role: "user", text, leadSeparator: prev.length > 0 }
8950
+ {
8951
+ id: `u-${Date.now()}`,
8952
+ role: "user",
8953
+ text: pasteDisplay.displayText,
8954
+ leadSeparator: prev.length > 0
8955
+ }
8642
8956
  ]);
8643
8957
  const assistantId = `a-${Date.now()}`;
8644
8958
  const streamRef = { id: assistantId, text: "", reasoning: "" };
@@ -8775,6 +9089,19 @@ function App({
8775
9089
  } else if (ev.role === "tool_start") {
8776
9090
  setOngoingTool({ name: ev.toolName ?? "?", args: ev.toolArgs });
8777
9091
  setToolProgress(null);
9092
+ if (codeMode && ev.toolArgs) {
9093
+ try {
9094
+ const parsed = JSON.parse(ev.toolArgs);
9095
+ for (const k of ["path", "file_path", "file"]) {
9096
+ const v = parsed[k];
9097
+ if (typeof v === "string" && v.trim()) {
9098
+ recordRecentFile(v.trim());
9099
+ break;
9100
+ }
9101
+ }
9102
+ } catch {
9103
+ }
9104
+ }
8778
9105
  } else if (ev.role === "tool") {
8779
9106
  flush();
8780
9107
  setOngoingTool(null);
@@ -8884,6 +9211,11 @@ function App({
8884
9211
  atPicker,
8885
9212
  atSelected,
8886
9213
  pickAtMention,
9214
+ recordRecentFile,
9215
+ slashArgMatches,
9216
+ slashArgContext,
9217
+ slashArgSelected,
9218
+ pickSlashArg,
8887
9219
  togglePlanMode,
8888
9220
  writeTranscript
8889
9221
  ]
@@ -9034,7 +9366,7 @@ Stay in plan mode \u2014 address the feedback (explore more if needed), then sub
9034
9366
  if (stagedInput?.plan) setPendingPlan(stagedInput.plan);
9035
9367
  setStagedInput(null);
9036
9368
  }, [stagedInput]);
9037
- return /* @__PURE__ */ React13.createElement(TickerProvider, { disabled: PLAIN_UI }, /* @__PURE__ */ React13.createElement(Box12, { flexDirection: "column" }, /* @__PURE__ */ React13.createElement(
9369
+ return /* @__PURE__ */ React14.createElement(TickerProvider, { disabled: PLAIN_UI }, /* @__PURE__ */ React14.createElement(Box13, { flexDirection: "column" }, /* @__PURE__ */ React14.createElement(
9038
9370
  StatsPanel,
9039
9371
  {
9040
9372
  summary,
@@ -9047,21 +9379,21 @@ Stay in plan mode \u2014 address the feedback (explore more if needed), then sub
9047
9379
  busy,
9048
9380
  updateAvailable
9049
9381
  }
9050
- ), /* @__PURE__ */ React13.createElement(Static, { items: historical }, (item) => /* @__PURE__ */ React13.createElement(EventRow, { key: item.id, event: item, projectRoot: hookCwd })), !PLAIN_UI && !pendingShell && !pendingPlan && !stagedInput && streaming ? /* @__PURE__ */ React13.createElement(Box12, { marginY: 1 }, /* @__PURE__ */ React13.createElement(EventRow, { event: streaming, projectRoot: hookCwd })) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !stagedInput && ongoingTool ? /* @__PURE__ */ React13.createElement(OngoingToolRow, { tool: ongoingTool, progress: toolProgress }) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !stagedInput && subagentActivity ? /* @__PURE__ */ React13.createElement(SubagentRow, { activity: subagentActivity }) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !stagedInput && !ongoingTool && statusLine ? /* @__PURE__ */ React13.createElement(StatusRow, { text: statusLine }) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !stagedInput && busy && !streaming && !ongoingTool && !statusLine ? /* @__PURE__ */ React13.createElement(StatusRow, { text: "processing\u2026" }) : null, stagedInput ? /* @__PURE__ */ React13.createElement(
9382
+ ), /* @__PURE__ */ React14.createElement(Static, { items: historical }, (item) => /* @__PURE__ */ React14.createElement(EventRow, { key: item.id, event: item, projectRoot: hookCwd })), !PLAIN_UI && !pendingShell && !pendingPlan && !stagedInput && streaming ? /* @__PURE__ */ React14.createElement(Box13, { marginY: 1 }, /* @__PURE__ */ React14.createElement(EventRow, { event: streaming, projectRoot: hookCwd })) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !stagedInput && ongoingTool ? /* @__PURE__ */ React14.createElement(OngoingToolRow, { tool: ongoingTool, progress: toolProgress }) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !stagedInput && subagentActivity ? /* @__PURE__ */ React14.createElement(SubagentRow, { activity: subagentActivity }) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !stagedInput && !ongoingTool && statusLine ? /* @__PURE__ */ React14.createElement(StatusRow, { text: statusLine }) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !stagedInput && busy && !streaming && !ongoingTool && !statusLine ? /* @__PURE__ */ React14.createElement(StatusRow, { text: "processing\u2026" }) : null, stagedInput ? /* @__PURE__ */ React14.createElement(
9051
9383
  PlanRefineInput,
9052
9384
  {
9053
9385
  mode: stagedInput.mode,
9054
9386
  onSubmit: handleStagedInputSubmit,
9055
9387
  onCancel: handleStagedInputCancel
9056
9388
  }
9057
- ) : pendingPlan ? /* @__PURE__ */ React13.createElement(PlanConfirm, { plan: pendingPlan, onChoose: handlePlanConfirm, projectRoot: hookCwd }) : pendingShell ? /* @__PURE__ */ React13.createElement(
9389
+ ) : pendingPlan ? /* @__PURE__ */ React14.createElement(PlanConfirm, { plan: pendingPlan, onChoose: handlePlanConfirm, projectRoot: hookCwd }) : pendingShell ? /* @__PURE__ */ React14.createElement(
9058
9390
  ShellConfirm,
9059
9391
  {
9060
9392
  command: pendingShell,
9061
9393
  allowPrefix: derivePrefix(pendingShell),
9062
9394
  onChoose: handleShellConfirm
9063
9395
  }
9064
- ) : /* @__PURE__ */ React13.createElement(React13.Fragment, null, /* @__PURE__ */ React13.createElement(
9396
+ ) : /* @__PURE__ */ React14.createElement(React14.Fragment, null, /* @__PURE__ */ React14.createElement(
9065
9397
  PromptInput,
9066
9398
  {
9067
9399
  value: input,
@@ -9069,27 +9401,36 @@ Stay in plan mode \u2014 address the feedback (explore more if needed), then sub
9069
9401
  onSubmit: handleSubmit,
9070
9402
  disabled: busy
9071
9403
  }
9072
- ), /* @__PURE__ */ React13.createElement(SlashSuggestions, { matches: slashMatches, selectedIndex: slashSelected }), /* @__PURE__ */ React13.createElement(
9404
+ ), /* @__PURE__ */ React14.createElement(SlashSuggestions, { matches: slashMatches, selectedIndex: slashSelected }), /* @__PURE__ */ React14.createElement(
9073
9405
  AtMentionSuggestions,
9074
9406
  {
9075
9407
  matches: atMatches,
9076
9408
  selectedIndex: atSelected,
9077
9409
  query: atPicker?.query ?? ""
9078
9410
  }
9079
- ))));
9411
+ ), slashArgContext ? /* @__PURE__ */ React14.createElement(
9412
+ SlashArgPicker,
9413
+ {
9414
+ matches: slashArgMatches,
9415
+ selectedIndex: slashArgSelected,
9416
+ spec: slashArgContext.spec,
9417
+ kind: slashArgContext.kind,
9418
+ partial: slashArgContext.partial
9419
+ }
9420
+ ) : null)));
9080
9421
  }
9081
9422
  var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
9082
9423
  function StatusRow({ text }) {
9083
9424
  const tick = useTick();
9084
9425
  const elapsed = useElapsedSeconds();
9085
- return /* @__PURE__ */ React13.createElement(Box12, { marginY: 1 }, /* @__PURE__ */ React13.createElement(Text12, { color: "magenta" }, SPINNER_FRAMES[tick % SPINNER_FRAMES.length]), /* @__PURE__ */ React13.createElement(Text12, { color: "magenta" }, ` ${text}`), /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, ` ${elapsed}s`));
9426
+ return /* @__PURE__ */ React14.createElement(Box13, { marginY: 1 }, /* @__PURE__ */ React14.createElement(Text13, { color: "magenta" }, SPINNER_FRAMES[tick % SPINNER_FRAMES.length]), /* @__PURE__ */ React14.createElement(Text13, { color: "magenta" }, ` ${text}`), /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, ` ${elapsed}s`));
9086
9427
  }
9087
9428
  function SubagentRow({
9088
9429
  activity
9089
9430
  }) {
9090
9431
  const tick = useTick();
9091
9432
  const seconds = (activity.elapsedMs / 1e3).toFixed(1);
9092
- return /* @__PURE__ */ React13.createElement(Box12, { paddingLeft: 2 }, /* @__PURE__ */ React13.createElement(Text12, { color: "magenta" }, SPINNER_FRAMES[tick % SPINNER_FRAMES.length]), /* @__PURE__ */ React13.createElement(Text12, { color: "magenta" }, ` \u232C subagent: ${activity.task}`), /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, ` \xB7 iter ${activity.iter} \xB7 ${seconds}s`));
9433
+ return /* @__PURE__ */ React14.createElement(Box13, { paddingLeft: 2 }, /* @__PURE__ */ React14.createElement(Text13, { color: "magenta" }, SPINNER_FRAMES[tick % SPINNER_FRAMES.length]), /* @__PURE__ */ React14.createElement(Text13, { color: "magenta" }, ` \u232C subagent: ${activity.task}`), /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, ` \xB7 iter ${activity.iter} \xB7 ${seconds}s`));
9093
9434
  }
9094
9435
  function OngoingToolRow({
9095
9436
  tool,
@@ -9098,7 +9439,7 @@ function OngoingToolRow({
9098
9439
  const tick = useTick();
9099
9440
  const elapsed = useElapsedSeconds();
9100
9441
  const summary = summarizeToolArgs(tool.name, tool.args);
9101
- return /* @__PURE__ */ React13.createElement(Box12, { marginY: 1, flexDirection: "column" }, /* @__PURE__ */ React13.createElement(Box12, null, /* @__PURE__ */ React13.createElement(Text12, { color: "cyan" }, SPINNER_FRAMES[tick % SPINNER_FRAMES.length]), /* @__PURE__ */ React13.createElement(Text12, { color: "yellow" }, ` tool<${tool.name}> running\u2026`), /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, ` ${elapsed}s`)), progress ? /* @__PURE__ */ React13.createElement(Box12, { paddingLeft: 2 }, /* @__PURE__ */ React13.createElement(Text12, { color: "cyan" }, renderProgressLine(progress))) : null, summary ? /* @__PURE__ */ React13.createElement(Box12, { paddingLeft: 2 }, /* @__PURE__ */ React13.createElement(Text12, { dimColor: true }, summary)) : null);
9442
+ return /* @__PURE__ */ React14.createElement(Box13, { marginY: 1, flexDirection: "column" }, /* @__PURE__ */ React14.createElement(Box13, null, /* @__PURE__ */ React14.createElement(Text13, { color: "cyan" }, SPINNER_FRAMES[tick % SPINNER_FRAMES.length]), /* @__PURE__ */ React14.createElement(Text13, { color: "yellow" }, ` tool<${tool.name}> running\u2026`), /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, ` ${elapsed}s`)), progress ? /* @__PURE__ */ React14.createElement(Box13, { paddingLeft: 2 }, /* @__PURE__ */ React14.createElement(Text13, { color: "cyan" }, renderProgressLine(progress))) : null, summary ? /* @__PURE__ */ React14.createElement(Box13, { paddingLeft: 2 }, /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, summary)) : null);
9102
9443
  }
9103
9444
  function renderProgressLine(p) {
9104
9445
  const msg = p.message ? ` ${p.message}` : "";
@@ -9164,18 +9505,9 @@ function formatEditResults(results) {
9164
9505
  return [header2, ...lines].join("\n");
9165
9506
  }
9166
9507
  function formatPendingPreview(blocks) {
9167
- const lines = blocks.map((b) => {
9168
- const removed = b.search === "" ? 0 : countLines2(b.search);
9169
- const added = countLines2(b.replace);
9170
- const tag = b.search === "" ? "NEW " : " ";
9171
- return ` ${tag}${b.path} (-${removed} +${added} lines)`;
9172
- });
9173
9508
  const header2 = `\u25B8 ${blocks.length} pending edit block(s) \u2014 /apply (or y) to commit \xB7 /discard (or n) to drop`;
9174
- return [header2, ...lines].join("\n");
9175
- }
9176
- function countLines2(s) {
9177
- if (s.length === 0) return 0;
9178
- return (s.match(/\n/g)?.length ?? 0) + 1;
9509
+ const diffLines = formatAllBlockDiffs(blocks);
9510
+ return [header2, ...diffLines].join("\n");
9179
9511
  }
9180
9512
  function formatUndoResults(results) {
9181
9513
  const lines = results.map((r) => {
@@ -9194,15 +9526,15 @@ function describeRepair(repair) {
9194
9526
  }
9195
9527
 
9196
9528
  // src/cli/ui/SessionPicker.tsx
9197
- import { Box as Box13, Text as Text13 } from "ink";
9198
- import React14 from "react";
9529
+ import { Box as Box14, Text as Text14 } from "ink";
9530
+ import React15 from "react";
9199
9531
  function SessionPicker({
9200
9532
  sessionName,
9201
9533
  messageCount,
9202
9534
  lastActive,
9203
9535
  onChoose
9204
9536
  }) {
9205
- return /* @__PURE__ */ React14.createElement(Box13, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React14.createElement(Box13, { marginBottom: 1 }, /* @__PURE__ */ React14.createElement(Text13, { bold: true, color: "cyan" }, `Session "${sessionName}" has ${messageCount} prior message${messageCount === 1 ? "" : "s"}`), /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, ` \xB7 last active ${relativeTime(lastActive)}`)), /* @__PURE__ */ React14.createElement(
9537
+ return /* @__PURE__ */ React15.createElement(Box14, { flexDirection: "column", marginY: 1 }, /* @__PURE__ */ React15.createElement(Box14, { marginBottom: 1 }, /* @__PURE__ */ React15.createElement(Text14, { bold: true, color: "cyan" }, `Session "${sessionName}" has ${messageCount} prior message${messageCount === 1 ? "" : "s"}`), /* @__PURE__ */ React15.createElement(Text14, { dimColor: true }, ` \xB7 last active ${relativeTime(lastActive)}`)), /* @__PURE__ */ React15.createElement(
9206
9538
  SingleSelect,
9207
9539
  {
9208
9540
  initialValue: "new",
@@ -9225,7 +9557,7 @@ function SessionPicker({
9225
9557
  ],
9226
9558
  onSubmit: (v) => onChoose(v)
9227
9559
  }
9228
- ), /* @__PURE__ */ React14.createElement(Box13, { marginTop: 1 }, /* @__PURE__ */ React14.createElement(Text13, { dimColor: true }, "\u2191\u2193 to move \xB7 Enter to pick")));
9560
+ ), /* @__PURE__ */ React15.createElement(Box14, { marginTop: 1 }, /* @__PURE__ */ React15.createElement(Text14, { dimColor: true }, "\u2191\u2193 to move \xB7 Enter to pick")));
9229
9561
  }
9230
9562
  function relativeTime(date) {
9231
9563
  const ms = Date.now() - date.getTime();
@@ -9241,9 +9573,9 @@ function relativeTime(date) {
9241
9573
  }
9242
9574
 
9243
9575
  // src/cli/ui/Setup.tsx
9244
- import { Box as Box14, Text as Text14, useApp as useApp2 } from "ink";
9576
+ import { Box as Box15, Text as Text15, useApp as useApp2 } from "ink";
9245
9577
  import TextInput from "ink-text-input";
9246
- import React15, { useState as useState6 } from "react";
9578
+ import React16, { useState as useState6 } from "react";
9247
9579
  function Setup({ onReady }) {
9248
9580
  const [value, setValue] = useState6("");
9249
9581
  const [error, setError] = useState6(null);
@@ -9267,7 +9599,7 @@ function Setup({ onReady }) {
9267
9599
  }
9268
9600
  onReady(trimmed);
9269
9601
  };
9270
- return /* @__PURE__ */ React15.createElement(Box14, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React15.createElement(Text14, { bold: true, color: "cyan" }, "Welcome to Reasonix."), /* @__PURE__ */ React15.createElement(Box14, { marginTop: 1 }, /* @__PURE__ */ React15.createElement(Text14, null, "Paste your DeepSeek API key to get started.")), /* @__PURE__ */ React15.createElement(Text14, { dimColor: true }, "Get one (free credit on signup): https://platform.deepseek.com/api_keys"), /* @__PURE__ */ React15.createElement(Text14, { dimColor: true }, "Saved locally to ", defaultConfigPath()), /* @__PURE__ */ React15.createElement(Box14, { marginTop: 1 }, /* @__PURE__ */ React15.createElement(Text14, { bold: true, color: "cyan" }, "key \u203A "), /* @__PURE__ */ React15.createElement(
9602
+ return /* @__PURE__ */ React16.createElement(Box15, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React16.createElement(Text15, { bold: true, color: "cyan" }, "Welcome to Reasonix."), /* @__PURE__ */ React16.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text15, null, "Paste your DeepSeek API key to get started.")), /* @__PURE__ */ React16.createElement(Text15, { dimColor: true }, "Get one (free credit on signup): https://platform.deepseek.com/api_keys"), /* @__PURE__ */ React16.createElement(Text15, { dimColor: true }, "Saved locally to ", defaultConfigPath()), /* @__PURE__ */ React16.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text15, { bold: true, color: "cyan" }, "key \u203A "), /* @__PURE__ */ React16.createElement(
9271
9603
  TextInput,
9272
9604
  {
9273
9605
  value,
@@ -9276,7 +9608,7 @@ function Setup({ onReady }) {
9276
9608
  mask: "\u2022",
9277
9609
  placeholder: "sk-..."
9278
9610
  }
9279
- )), error ? /* @__PURE__ */ React15.createElement(Box14, { marginTop: 1 }, /* @__PURE__ */ React15.createElement(Text14, { color: "red" }, error)) : value ? /* @__PURE__ */ React15.createElement(Box14, { marginTop: 1 }, /* @__PURE__ */ React15.createElement(Text14, { dimColor: true }, "preview: ", redactKey(value))) : null, /* @__PURE__ */ React15.createElement(Box14, { marginTop: 1 }, /* @__PURE__ */ React15.createElement(Text14, { dimColor: true }, "(Type /exit to abort.)")));
9611
+ )), error ? /* @__PURE__ */ React16.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text15, { color: "red" }, error)) : value ? /* @__PURE__ */ React16.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text15, { dimColor: true }, "preview: ", redactKey(value))) : null, /* @__PURE__ */ React16.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React16.createElement(Text15, { dimColor: true }, "(Type /exit to abort.)")));
9280
9612
  }
9281
9613
 
9282
9614
  // src/cli/commands/chat.tsx
@@ -9292,7 +9624,7 @@ function Root({
9292
9624
  const [key, setKey] = useState7(initialKey);
9293
9625
  const [pending, setPending] = useState7(sessionPreview);
9294
9626
  if (!key) {
9295
- return /* @__PURE__ */ React16.createElement(
9627
+ return /* @__PURE__ */ React17.createElement(
9296
9628
  Setup,
9297
9629
  {
9298
9630
  onReady: (k) => {
@@ -9304,7 +9636,7 @@ function Root({
9304
9636
  }
9305
9637
  process.env.DEEPSEEK_API_KEY = key;
9306
9638
  if (pending && appProps.session) {
9307
- return /* @__PURE__ */ React16.createElement(
9639
+ return /* @__PURE__ */ React17.createElement(
9308
9640
  SessionPicker,
9309
9641
  {
9310
9642
  sessionName: appProps.session,
@@ -9319,7 +9651,7 @@ function Root({
9319
9651
  }
9320
9652
  );
9321
9653
  }
9322
- return /* @__PURE__ */ React16.createElement(
9654
+ return /* @__PURE__ */ React17.createElement(
9323
9655
  App,
9324
9656
  {
9325
9657
  model: appProps.model,
@@ -9422,7 +9754,7 @@ async function chatCommand(opts) {
9422
9754
  rewriteSession(opts.session, []);
9423
9755
  }
9424
9756
  const { waitUntilExit } = render(
9425
- /* @__PURE__ */ React16.createElement(
9757
+ /* @__PURE__ */ React17.createElement(
9426
9758
  Root,
9427
9759
  {
9428
9760
  initialKey,
@@ -9485,34 +9817,34 @@ async function codeCommand(opts = {}) {
9485
9817
  import { writeFileSync as writeFileSync5 } from "fs";
9486
9818
  import { basename as basename2 } from "path";
9487
9819
  import { render as render2 } from "ink";
9488
- import React19 from "react";
9820
+ import React20 from "react";
9489
9821
 
9490
9822
  // src/cli/ui/DiffApp.tsx
9491
- import { Box as Box16, Static as Static2, Text as Text16, useApp as useApp3, useInput as useInput5 } from "ink";
9492
- import React18, { useState as useState8 } from "react";
9823
+ import { Box as Box17, Static as Static2, Text as Text17, useApp as useApp3, useInput as useInput5 } from "ink";
9824
+ import React19, { useState as useState8 } from "react";
9493
9825
 
9494
9826
  // src/cli/ui/RecordView.tsx
9495
- import { Box as Box15, Text as Text15 } from "ink";
9496
- import React17 from "react";
9827
+ import { Box as Box16, Text as Text16 } from "ink";
9828
+ import React18 from "react";
9497
9829
  function RecordView({ rec, compact = false }) {
9498
9830
  const toolArgsMax = compact ? 120 : 200;
9499
9831
  const toolContentMax = compact ? 200 : 400;
9500
9832
  if (rec.role === "user") {
9501
- return /* @__PURE__ */ React17.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React17.createElement(Text15, { bold: true, color: "cyan" }, "you \u203A", " "), /* @__PURE__ */ React17.createElement(Text15, null, rec.content));
9833
+ return /* @__PURE__ */ React18.createElement(Box16, { marginTop: 1 }, /* @__PURE__ */ React18.createElement(Text16, { bold: true, color: "cyan" }, "you \u203A", " "), /* @__PURE__ */ React18.createElement(Text16, null, rec.content));
9502
9834
  }
9503
9835
  if (rec.role === "assistant_final") {
9504
- return /* @__PURE__ */ React17.createElement(Box15, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React17.createElement(Box15, null, /* @__PURE__ */ React17.createElement(Text15, { bold: true, color: "green" }, "assistant"), rec.cost !== void 0 ? /* @__PURE__ */ React17.createElement(Text15, { dimColor: true }, " $", rec.cost.toFixed(6)) : null, rec.usage ? /* @__PURE__ */ React17.createElement(CacheBadge, { usage: rec.usage }) : null), rec.planState ? /* @__PURE__ */ React17.createElement(PlanStateBlock, { planState: rec.planState }) : null, rec.content ? /* @__PURE__ */ React17.createElement(Text15, null, rec.content) : /* @__PURE__ */ React17.createElement(Text15, { dimColor: true, italic: true }, "(tool-call response only)"));
9836
+ return /* @__PURE__ */ React18.createElement(Box16, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React18.createElement(Box16, null, /* @__PURE__ */ React18.createElement(Text16, { bold: true, color: "green" }, "assistant"), rec.cost !== void 0 ? /* @__PURE__ */ React18.createElement(Text16, { dimColor: true }, " $", rec.cost.toFixed(6)) : null, rec.usage ? /* @__PURE__ */ React18.createElement(CacheBadge, { usage: rec.usage }) : null), rec.planState ? /* @__PURE__ */ React18.createElement(PlanStateBlock, { planState: rec.planState }) : null, rec.content ? /* @__PURE__ */ React18.createElement(Text16, null, rec.content) : /* @__PURE__ */ React18.createElement(Text16, { dimColor: true, italic: true }, "(tool-call response only)"));
9505
9837
  }
9506
9838
  if (rec.role === "tool") {
9507
- return /* @__PURE__ */ React17.createElement(Box15, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React17.createElement(Text15, { color: "yellow" }, "tool<", rec.tool ?? "?", ">"), rec.args ? /* @__PURE__ */ React17.createElement(Text15, { dimColor: true }, " args: ", truncate3(rec.args, toolArgsMax)) : null, /* @__PURE__ */ React17.createElement(Text15, { dimColor: true }, " \u2192 ", truncate3(rec.content, toolContentMax)));
9839
+ return /* @__PURE__ */ React18.createElement(Box16, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React18.createElement(Text16, { color: "yellow" }, "tool<", rec.tool ?? "?", ">"), rec.args ? /* @__PURE__ */ React18.createElement(Text16, { dimColor: true }, " args: ", truncate3(rec.args, toolArgsMax)) : null, /* @__PURE__ */ React18.createElement(Text16, { dimColor: true }, " \u2192 ", truncate3(rec.content, toolContentMax)));
9508
9840
  }
9509
9841
  if (rec.role === "error") {
9510
- return /* @__PURE__ */ React17.createElement(Box15, { marginTop: 1 }, /* @__PURE__ */ React17.createElement(Text15, { color: "red", bold: true }, "error", " "), /* @__PURE__ */ React17.createElement(Text15, { color: "red" }, rec.error ?? rec.content));
9842
+ return /* @__PURE__ */ React18.createElement(Box16, { marginTop: 1 }, /* @__PURE__ */ React18.createElement(Text16, { color: "red", bold: true }, "error", " "), /* @__PURE__ */ React18.createElement(Text16, { color: "red" }, rec.error ?? rec.content));
9511
9843
  }
9512
9844
  if (rec.role === "done" || rec.role === "assistant_delta") {
9513
9845
  return null;
9514
9846
  }
9515
- return /* @__PURE__ */ React17.createElement(Box15, null, /* @__PURE__ */ React17.createElement(Text15, { dimColor: true }, "[", rec.role, "] ", rec.content));
9847
+ return /* @__PURE__ */ React18.createElement(Box16, null, /* @__PURE__ */ React18.createElement(Text16, { dimColor: true }, "[", rec.role, "] ", rec.content));
9516
9848
  }
9517
9849
  function CacheBadge({ usage }) {
9518
9850
  const hit = usage.prompt_cache_hit_tokens ?? 0;
@@ -9521,7 +9853,7 @@ function CacheBadge({ usage }) {
9521
9853
  if (total === 0) return null;
9522
9854
  const pct2 = hit / total * 100;
9523
9855
  const color = pct2 >= 70 ? "green" : pct2 >= 40 ? "yellow" : "red";
9524
- return /* @__PURE__ */ React17.createElement(Text15, null, /* @__PURE__ */ React17.createElement(Text15, { dimColor: true }, " \xB7 cache "), /* @__PURE__ */ React17.createElement(Text15, { color }, pct2.toFixed(1), "%"));
9856
+ return /* @__PURE__ */ React18.createElement(Text16, null, /* @__PURE__ */ React18.createElement(Text16, { dimColor: true }, " \xB7 cache "), /* @__PURE__ */ React18.createElement(Text16, { color }, pct2.toFixed(1), "%"));
9525
9857
  }
9526
9858
  function truncate3(s, max) {
9527
9859
  return s.length <= max ? s : `${s.slice(0, max)}\u2026 (+${s.length - max} chars)`;
@@ -9555,7 +9887,7 @@ function DiffApp({ report }) {
9555
9887
  }
9556
9888
  });
9557
9889
  const pair = report.pairs[idx];
9558
- return /* @__PURE__ */ React18.createElement(Box16, { flexDirection: "column" }, /* @__PURE__ */ React18.createElement(DiffHeader, { report }), /* @__PURE__ */ React18.createElement(Box16, { marginTop: 1, paddingX: 1, justifyContent: "space-between" }, /* @__PURE__ */ React18.createElement(Text16, { color: "cyan", bold: true }, "turn ", pair?.turn ?? "?", " (", idx + 1, " / ", report.pairs.length, ")"), /* @__PURE__ */ React18.createElement(Text16, null, pair ? /* @__PURE__ */ React18.createElement(KindBadge, { kind: pair.kind }) : null)), /* @__PURE__ */ React18.createElement(Box16, { flexDirection: "row", marginTop: 1 }, /* @__PURE__ */ React18.createElement(Pane, { label: report.a.label, headerColor: "blue", records: paneRecords(pair, "a") }), /* @__PURE__ */ React18.createElement(Pane, { label: report.b.label, headerColor: "magenta", records: paneRecords(pair, "b") })), pair?.divergenceNote ? /* @__PURE__ */ React18.createElement(Box16, { marginTop: 1, paddingX: 1 }, /* @__PURE__ */ React18.createElement(Text16, { color: "yellow" }, "\u2605 "), /* @__PURE__ */ React18.createElement(Text16, null, pair.divergenceNote)) : null, /* @__PURE__ */ React18.createElement(Box16, { marginTop: 1, paddingX: 1, borderStyle: "single", borderColor: "gray" }, /* @__PURE__ */ React18.createElement(Text16, { dimColor: true }, /* @__PURE__ */ React18.createElement(Text16, { bold: true }, "j"), "/", /* @__PURE__ */ React18.createElement(Text16, { bold: true }, "\u2193"), " next \xB7 ", /* @__PURE__ */ React18.createElement(Text16, { bold: true }, "k"), "/", /* @__PURE__ */ React18.createElement(Text16, { bold: true }, "\u2191"), " ", "prev \xB7 ", /* @__PURE__ */ React18.createElement(Text16, { bold: true }, "n"), " next-diverge \xB7 ", /* @__PURE__ */ React18.createElement(Text16, { bold: true }, "N"), "/", /* @__PURE__ */ React18.createElement(Text16, { bold: true }, "p"), " ", "prev-diverge \xB7 ", /* @__PURE__ */ React18.createElement(Text16, { bold: true }, "g"), "/", /* @__PURE__ */ React18.createElement(Text16, { bold: true }, "G"), " first/last \xB7 ", /* @__PURE__ */ React18.createElement(Text16, { bold: true }, "q"), " ", "quit")));
9890
+ return /* @__PURE__ */ React19.createElement(Box17, { flexDirection: "column" }, /* @__PURE__ */ React19.createElement(DiffHeader, { report }), /* @__PURE__ */ React19.createElement(Box17, { marginTop: 1, paddingX: 1, justifyContent: "space-between" }, /* @__PURE__ */ React19.createElement(Text17, { color: "cyan", bold: true }, "turn ", pair?.turn ?? "?", " (", idx + 1, " / ", report.pairs.length, ")"), /* @__PURE__ */ React19.createElement(Text17, null, pair ? /* @__PURE__ */ React19.createElement(KindBadge, { kind: pair.kind }) : null)), /* @__PURE__ */ React19.createElement(Box17, { flexDirection: "row", marginTop: 1 }, /* @__PURE__ */ React19.createElement(Pane, { label: report.a.label, headerColor: "blue", records: paneRecords(pair, "a") }), /* @__PURE__ */ React19.createElement(Pane, { label: report.b.label, headerColor: "magenta", records: paneRecords(pair, "b") })), pair?.divergenceNote ? /* @__PURE__ */ React19.createElement(Box17, { marginTop: 1, paddingX: 1 }, /* @__PURE__ */ React19.createElement(Text17, { color: "yellow" }, "\u2605 "), /* @__PURE__ */ React19.createElement(Text17, null, pair.divergenceNote)) : null, /* @__PURE__ */ React19.createElement(Box17, { marginTop: 1, paddingX: 1, borderStyle: "single", borderColor: "gray" }, /* @__PURE__ */ React19.createElement(Text17, { dimColor: true }, /* @__PURE__ */ React19.createElement(Text17, { bold: true }, "j"), "/", /* @__PURE__ */ React19.createElement(Text17, { bold: true }, "\u2193"), " next \xB7 ", /* @__PURE__ */ React19.createElement(Text17, { bold: true }, "k"), "/", /* @__PURE__ */ React19.createElement(Text17, { bold: true }, "\u2191"), " ", "prev \xB7 ", /* @__PURE__ */ React19.createElement(Text17, { bold: true }, "n"), " next-diverge \xB7 ", /* @__PURE__ */ React19.createElement(Text17, { bold: true }, "N"), "/", /* @__PURE__ */ React19.createElement(Text17, { bold: true }, "p"), " ", "prev-diverge \xB7 ", /* @__PURE__ */ React19.createElement(Text17, { bold: true }, "g"), "/", /* @__PURE__ */ React19.createElement(Text17, { bold: true }, "G"), " first/last \xB7 ", /* @__PURE__ */ React19.createElement(Text17, { bold: true }, "q"), " ", "quit")));
9559
9891
  }
9560
9892
  function DiffHeader({ report }) {
9561
9893
  const a = report.a;
@@ -9573,15 +9905,15 @@ function DiffHeader({ report }) {
9573
9905
  } else if (a.stats.prefixHashes[0] && a.stats.prefixHashes[0] === b.stats.prefixHashes[0]) {
9574
9906
  prefixLine = `shared prefix hash ${a.stats.prefixHashes[0].slice(0, 12)}\u2026 \u2014 cache delta attributable to log stability, not prompt change.`;
9575
9907
  }
9576
- return /* @__PURE__ */ React18.createElement(Box16, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React18.createElement(Box16, { justifyContent: "space-between" }, /* @__PURE__ */ React18.createElement(Text16, null, /* @__PURE__ */ React18.createElement(Text16, { color: "cyan", bold: true }, "reasonix diff"), /* @__PURE__ */ React18.createElement(Text16, { dimColor: true }, " \xB7 A="), /* @__PURE__ */ React18.createElement(Text16, { color: "blue" }, a.label), /* @__PURE__ */ React18.createElement(Text16, { dimColor: true }, " vs B="), /* @__PURE__ */ React18.createElement(Text16, { color: "magenta" }, b.label)), /* @__PURE__ */ React18.createElement(Text16, { dimColor: true }, report.pairs.length, " turns aligned")), /* @__PURE__ */ React18.createElement(Box16, { marginTop: 1, gap: 3 }, /* @__PURE__ */ React18.createElement(Text16, null, /* @__PURE__ */ React18.createElement(Text16, { dimColor: true }, "cache "), /* @__PURE__ */ React18.createElement(Text16, null, (a.stats.cacheHitRatio * 100).toFixed(1), "%"), /* @__PURE__ */ React18.createElement(Text16, { dimColor: true }, " \u2192 "), /* @__PURE__ */ React18.createElement(Text16, null, (b.stats.cacheHitRatio * 100).toFixed(1), "%"), /* @__PURE__ */ React18.createElement(Text16, { color: cacheDelta >= 0 ? "green" : "red", bold: true }, " ", cacheDelta >= 0 ? "+" : "", (cacheDelta * 100).toFixed(1), "pp")), /* @__PURE__ */ React18.createElement(Text16, null, /* @__PURE__ */ React18.createElement(Text16, { dimColor: true }, "cost "), /* @__PURE__ */ React18.createElement(Text16, null, "$", a.stats.totalCostUsd.toFixed(6)), /* @__PURE__ */ React18.createElement(Text16, { dimColor: true }, " \u2192 "), /* @__PURE__ */ React18.createElement(Text16, null, "$", b.stats.totalCostUsd.toFixed(6)), /* @__PURE__ */ React18.createElement(Text16, { color: costDelta2 <= 0 ? "green" : "red", bold: true }, " ", costDelta2 >= 0 ? "+" : "", costDelta2.toFixed(1), "%")), /* @__PURE__ */ React18.createElement(Text16, null, /* @__PURE__ */ React18.createElement(Text16, { dimColor: true }, "model calls "), /* @__PURE__ */ React18.createElement(Text16, null, a.stats.turns, " \u2192 ", b.stats.turns))), prefixLine ? /* @__PURE__ */ React18.createElement(Box16, { marginTop: 1 }, /* @__PURE__ */ React18.createElement(Text16, { dimColor: true, italic: true }, prefixLine)) : null);
9908
+ return /* @__PURE__ */ React19.createElement(Box17, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React19.createElement(Box17, { justifyContent: "space-between" }, /* @__PURE__ */ React19.createElement(Text17, null, /* @__PURE__ */ React19.createElement(Text17, { color: "cyan", bold: true }, "reasonix diff"), /* @__PURE__ */ React19.createElement(Text17, { dimColor: true }, " \xB7 A="), /* @__PURE__ */ React19.createElement(Text17, { color: "blue" }, a.label), /* @__PURE__ */ React19.createElement(Text17, { dimColor: true }, " vs B="), /* @__PURE__ */ React19.createElement(Text17, { color: "magenta" }, b.label)), /* @__PURE__ */ React19.createElement(Text17, { dimColor: true }, report.pairs.length, " turns aligned")), /* @__PURE__ */ React19.createElement(Box17, { marginTop: 1, gap: 3 }, /* @__PURE__ */ React19.createElement(Text17, null, /* @__PURE__ */ React19.createElement(Text17, { dimColor: true }, "cache "), /* @__PURE__ */ React19.createElement(Text17, null, (a.stats.cacheHitRatio * 100).toFixed(1), "%"), /* @__PURE__ */ React19.createElement(Text17, { dimColor: true }, " \u2192 "), /* @__PURE__ */ React19.createElement(Text17, null, (b.stats.cacheHitRatio * 100).toFixed(1), "%"), /* @__PURE__ */ React19.createElement(Text17, { color: cacheDelta >= 0 ? "green" : "red", bold: true }, " ", cacheDelta >= 0 ? "+" : "", (cacheDelta * 100).toFixed(1), "pp")), /* @__PURE__ */ React19.createElement(Text17, null, /* @__PURE__ */ React19.createElement(Text17, { dimColor: true }, "cost "), /* @__PURE__ */ React19.createElement(Text17, null, "$", a.stats.totalCostUsd.toFixed(6)), /* @__PURE__ */ React19.createElement(Text17, { dimColor: true }, " \u2192 "), /* @__PURE__ */ React19.createElement(Text17, null, "$", b.stats.totalCostUsd.toFixed(6)), /* @__PURE__ */ React19.createElement(Text17, { color: costDelta2 <= 0 ? "green" : "red", bold: true }, " ", costDelta2 >= 0 ? "+" : "", costDelta2.toFixed(1), "%")), /* @__PURE__ */ React19.createElement(Text17, null, /* @__PURE__ */ React19.createElement(Text17, { dimColor: true }, "model calls "), /* @__PURE__ */ React19.createElement(Text17, null, a.stats.turns, " \u2192 ", b.stats.turns))), prefixLine ? /* @__PURE__ */ React19.createElement(Box17, { marginTop: 1 }, /* @__PURE__ */ React19.createElement(Text17, { dimColor: true, italic: true }, prefixLine)) : null);
9577
9909
  }
9578
9910
  function Pane({
9579
9911
  label,
9580
9912
  headerColor,
9581
9913
  records
9582
9914
  }) {
9583
- return /* @__PURE__ */ React18.createElement(
9584
- Box16,
9915
+ return /* @__PURE__ */ React19.createElement(
9916
+ Box17,
9585
9917
  {
9586
9918
  flexDirection: "column",
9587
9919
  flexGrow: 1,
@@ -9589,21 +9921,21 @@ function Pane({
9589
9921
  borderStyle: "single",
9590
9922
  borderColor: headerColor
9591
9923
  },
9592
- /* @__PURE__ */ React18.createElement(Text16, { color: headerColor, bold: true }, label),
9593
- records.length === 0 ? /* @__PURE__ */ React18.createElement(Box16, { marginTop: 1 }, /* @__PURE__ */ React18.createElement(Text16, { dimColor: true, italic: true }, "(no records on this side for this turn)")) : /* @__PURE__ */ React18.createElement(Static2, { items: records.map((rec, i) => ({ key: `${label}-${i}`, rec })) }, ({ key, rec }) => /* @__PURE__ */ React18.createElement(RecordView, { key, rec, compact: true }))
9924
+ /* @__PURE__ */ React19.createElement(Text17, { color: headerColor, bold: true }, label),
9925
+ records.length === 0 ? /* @__PURE__ */ React19.createElement(Box17, { marginTop: 1 }, /* @__PURE__ */ React19.createElement(Text17, { dimColor: true, italic: true }, "(no records on this side for this turn)")) : /* @__PURE__ */ React19.createElement(Static2, { items: records.map((rec, i) => ({ key: `${label}-${i}`, rec })) }, ({ key, rec }) => /* @__PURE__ */ React19.createElement(RecordView, { key, rec, compact: true }))
9594
9926
  );
9595
9927
  }
9596
9928
  function KindBadge({ kind }) {
9597
9929
  if (kind === "match") {
9598
- return /* @__PURE__ */ React18.createElement(Text16, { color: "green" }, "\u2713 match");
9930
+ return /* @__PURE__ */ React19.createElement(Text17, { color: "green" }, "\u2713 match");
9599
9931
  }
9600
9932
  if (kind === "diverge") {
9601
- return /* @__PURE__ */ React18.createElement(Text16, { color: "yellow" }, "\u2605 diverge");
9933
+ return /* @__PURE__ */ React19.createElement(Text17, { color: "yellow" }, "\u2605 diverge");
9602
9934
  }
9603
9935
  if (kind === "only_in_a") {
9604
- return /* @__PURE__ */ React18.createElement(Text16, { color: "blue" }, "\u2190 only in A");
9936
+ return /* @__PURE__ */ React19.createElement(Text17, { color: "blue" }, "\u2190 only in A");
9605
9937
  }
9606
- return /* @__PURE__ */ React18.createElement(Text16, { color: "magenta" }, "\u2192 only in B");
9938
+ return /* @__PURE__ */ React19.createElement(Text17, { color: "magenta" }, "\u2192 only in B");
9607
9939
  }
9608
9940
  function paneRecords(pair, side) {
9609
9941
  if (!pair) return [];
@@ -9634,7 +9966,7 @@ markdown report written to ${opts.mdPath}`);
9634
9966
  return;
9635
9967
  }
9636
9968
  if (wantTui) {
9637
- const { waitUntilExit } = render2(React19.createElement(DiffApp, { report }), {
9969
+ const { waitUntilExit } = render2(React20.createElement(DiffApp, { report }), {
9638
9970
  exitOnCtrlC: true,
9639
9971
  patchConsole: false
9640
9972
  });
@@ -9775,11 +10107,11 @@ function pad2(s, width) {
9775
10107
 
9776
10108
  // src/cli/commands/replay.ts
9777
10109
  import { render as render3 } from "ink";
9778
- import React21 from "react";
10110
+ import React22 from "react";
9779
10111
 
9780
10112
  // src/cli/ui/ReplayApp.tsx
9781
- import { Box as Box17, Static as Static3, Text as Text17, useApp as useApp4, useInput as useInput6 } from "ink";
9782
- import React20, { useMemo as useMemo2, useState as useState9 } from "react";
10113
+ import { Box as Box18, Static as Static3, Text as Text18, useApp as useApp4, useInput as useInput6 } from "ink";
10114
+ import React21, { useMemo as useMemo2, useState as useState9 } from "react";
9783
10115
  function ReplayApp({ meta, pages }) {
9784
10116
  const { exit } = useApp4();
9785
10117
  const maxIdx = Math.max(0, pages.length - 1);
@@ -9818,14 +10150,14 @@ function ReplayApp({ meta, pages }) {
9818
10150
  const prefixHash = cumStats.prefixHashes.length === 1 ? cumStats.prefixHashes[0].slice(0, 16) : cumStats.prefixHashes.length === 0 ? "(untracked)" : `(churned \xD7${cumStats.prefixHashes.length})`;
9819
10151
  const currentPage = pages[idx];
9820
10152
  const progressLabel = pages.length === 0 ? "empty transcript" : `turn ${idx + 1} / ${pages.length}`;
9821
- return /* @__PURE__ */ React20.createElement(Box17, { flexDirection: "column" }, /* @__PURE__ */ React20.createElement(
10153
+ return /* @__PURE__ */ React21.createElement(Box18, { flexDirection: "column" }, /* @__PURE__ */ React21.createElement(
9822
10154
  StatsPanel,
9823
10155
  {
9824
10156
  summary,
9825
10157
  model: cumStats.models[0] ?? meta?.model ?? "?",
9826
10158
  prefixHash
9827
10159
  }
9828
- ), /* @__PURE__ */ React20.createElement(Box17, { flexDirection: "column", marginTop: 1, paddingX: 1 }, /* @__PURE__ */ React20.createElement(Box17, { justifyContent: "space-between" }, /* @__PURE__ */ React20.createElement(Text17, { color: "cyan", bold: true }, progressLabel), meta ? /* @__PURE__ */ React20.createElement(Text17, { dimColor: true }, meta.source, meta.task ? ` \xB7 ${meta.task}` : "", meta.mode ? ` \xB7 ${meta.mode}` : "") : null), currentPage ? /* @__PURE__ */ React20.createElement(Static3, { items: currentPage.records.map((rec, i) => ({ key: `${idx}-${i}`, rec })) }, ({ key, rec }) => /* @__PURE__ */ React20.createElement(RecordView, { key, rec })) : /* @__PURE__ */ React20.createElement(Text17, { dimColor: true, italic: true }, "no records")), /* @__PURE__ */ React20.createElement(Box17, { marginTop: 1, paddingX: 1, borderStyle: "single", borderColor: "gray" }, /* @__PURE__ */ React20.createElement(Text17, { dimColor: true }, /* @__PURE__ */ React20.createElement(Text17, { bold: true }, "j"), "/", /* @__PURE__ */ React20.createElement(Text17, { bold: true }, "\u2193"), "/", /* @__PURE__ */ React20.createElement(Text17, { bold: true }, "space"), " next \xB7 ", /* @__PURE__ */ React20.createElement(Text17, { bold: true }, "k"), "/", /* @__PURE__ */ React20.createElement(Text17, { bold: true }, "\u2191"), " prev \xB7 ", /* @__PURE__ */ React20.createElement(Text17, { bold: true }, "g"), " first \xB7 ", /* @__PURE__ */ React20.createElement(Text17, { bold: true }, "G"), " last \xB7", " ", /* @__PURE__ */ React20.createElement(Text17, { bold: true }, "q"), " quit")));
10160
+ ), /* @__PURE__ */ React21.createElement(Box18, { flexDirection: "column", marginTop: 1, paddingX: 1 }, /* @__PURE__ */ React21.createElement(Box18, { justifyContent: "space-between" }, /* @__PURE__ */ React21.createElement(Text18, { color: "cyan", bold: true }, progressLabel), meta ? /* @__PURE__ */ React21.createElement(Text18, { dimColor: true }, meta.source, meta.task ? ` \xB7 ${meta.task}` : "", meta.mode ? ` \xB7 ${meta.mode}` : "") : null), currentPage ? /* @__PURE__ */ React21.createElement(Static3, { items: currentPage.records.map((rec, i) => ({ key: `${idx}-${i}`, rec })) }, ({ key, rec }) => /* @__PURE__ */ React21.createElement(RecordView, { key, rec })) : /* @__PURE__ */ React21.createElement(Text18, { dimColor: true, italic: true }, "no records")), /* @__PURE__ */ React21.createElement(Box18, { marginTop: 1, paddingX: 1, borderStyle: "single", borderColor: "gray" }, /* @__PURE__ */ React21.createElement(Text18, { dimColor: true }, /* @__PURE__ */ React21.createElement(Text18, { bold: true }, "j"), "/", /* @__PURE__ */ React21.createElement(Text18, { bold: true }, "\u2193"), "/", /* @__PURE__ */ React21.createElement(Text18, { bold: true }, "space"), " next \xB7 ", /* @__PURE__ */ React21.createElement(Text18, { bold: true }, "k"), "/", /* @__PURE__ */ React21.createElement(Text18, { bold: true }, "\u2191"), " prev \xB7 ", /* @__PURE__ */ React21.createElement(Text18, { bold: true }, "g"), " first \xB7 ", /* @__PURE__ */ React21.createElement(Text18, { bold: true }, "G"), " last \xB7", " ", /* @__PURE__ */ React21.createElement(Text18, { bold: true }, "q"), " quit")));
9829
10161
  }
9830
10162
 
9831
10163
  // src/cli/commands/replay.ts
@@ -9837,7 +10169,7 @@ async function replayCommand(opts) {
9837
10169
  }
9838
10170
  const { parsed } = replayFromFile(opts.path);
9839
10171
  const pages = groupRecordsByTurn(parsed.records);
9840
- const { waitUntilExit } = render3(React21.createElement(ReplayApp, { meta: parsed.meta, pages }), {
10172
+ const { waitUntilExit } = render3(React22.createElement(ReplayApp, { meta: parsed.meta, pages }), {
9841
10173
  exitOnCtrlC: true,
9842
10174
  patchConsole: false
9843
10175
  });
@@ -10142,12 +10474,12 @@ function truncate4(s, max) {
10142
10474
 
10143
10475
  // src/cli/commands/setup.tsx
10144
10476
  import { render as render4 } from "ink";
10145
- import React23 from "react";
10477
+ import React24 from "react";
10146
10478
 
10147
10479
  // src/cli/ui/Wizard.tsx
10148
- import { Box as Box18, Text as Text18, useApp as useApp5, useInput as useInput7 } from "ink";
10480
+ import { Box as Box19, Text as Text19, useApp as useApp5, useInput as useInput7 } from "ink";
10149
10481
  import TextInput2 from "ink-text-input";
10150
- import React22, { useState as useState10 } from "react";
10482
+ import React23, { useState as useState10 } from "react";
10151
10483
 
10152
10484
  // src/cli/ui/presets.ts
10153
10485
  var PRESETS = {
@@ -10186,7 +10518,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
10186
10518
  if (key.escape && step !== "saved" && onCancel) onCancel();
10187
10519
  });
10188
10520
  if (step === "apiKey") {
10189
- return /* @__PURE__ */ React22.createElement(
10521
+ return /* @__PURE__ */ React23.createElement(
10190
10522
  ApiKeyStep,
10191
10523
  {
10192
10524
  onSubmit: (key) => {
@@ -10200,7 +10532,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
10200
10532
  );
10201
10533
  }
10202
10534
  if (step === "preset") {
10203
- return /* @__PURE__ */ React22.createElement(StepFrame, { title: "Pick a preset", step: 1, total: 3 }, /* @__PURE__ */ React22.createElement(
10535
+ return /* @__PURE__ */ React23.createElement(StepFrame, { title: "Pick a preset", step: 1, total: 3 }, /* @__PURE__ */ React23.createElement(
10204
10536
  SingleSelect,
10205
10537
  {
10206
10538
  items: presetItems(),
@@ -10210,10 +10542,10 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
10210
10542
  setStep("mcp");
10211
10543
  }
10212
10544
  }
10213
- ), /* @__PURE__ */ React22.createElement(Box18, { marginTop: 1 }, /* @__PURE__ */ React22.createElement(Text18, { dimColor: true }, "\u2191/\u2193 move \xB7 enter confirm \xB7 esc cancel")));
10545
+ ), /* @__PURE__ */ React23.createElement(Box19, { marginTop: 1 }, /* @__PURE__ */ React23.createElement(Text19, { dimColor: true }, "\u2191/\u2193 move \xB7 enter confirm \xB7 esc cancel")));
10214
10546
  }
10215
10547
  if (step === "mcp") {
10216
- return /* @__PURE__ */ React22.createElement(StepFrame, { title: "Which MCP servers should Reasonix wire up for you?", step: 2, total: 3 }, /* @__PURE__ */ React22.createElement(
10548
+ return /* @__PURE__ */ React23.createElement(StepFrame, { title: "Which MCP servers should Reasonix wire up for you?", step: 2, total: 3 }, /* @__PURE__ */ React23.createElement(
10217
10549
  MultiSelect,
10218
10550
  {
10219
10551
  items: mcpItems(),
@@ -10238,7 +10570,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
10238
10570
  }
10239
10571
  const currentName = pending[0];
10240
10572
  const entry = CATALOG_BY_NAME.get(currentName);
10241
- return /* @__PURE__ */ React22.createElement(
10573
+ return /* @__PURE__ */ React23.createElement(
10242
10574
  McpArgsStep,
10243
10575
  {
10244
10576
  entry,
@@ -10256,7 +10588,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
10256
10588
  }
10257
10589
  if (step === "review") {
10258
10590
  const specs = data.selectedCatalog.map((name) => buildSpec(name, data.catalogArgs));
10259
- return /* @__PURE__ */ React22.createElement(StepFrame, { title: "Ready to save", step: 3, total: 3 }, /* @__PURE__ */ React22.createElement(Box18, { flexDirection: "column" }, /* @__PURE__ */ React22.createElement(SummaryLine, { label: "API key", value: redactKey(data.apiKey) }), /* @__PURE__ */ React22.createElement(SummaryLine, { label: "Preset", value: data.preset }), /* @__PURE__ */ React22.createElement(
10591
+ return /* @__PURE__ */ React23.createElement(StepFrame, { title: "Ready to save", step: 3, total: 3 }, /* @__PURE__ */ React23.createElement(Box19, { flexDirection: "column" }, /* @__PURE__ */ React23.createElement(SummaryLine, { label: "API key", value: redactKey(data.apiKey) }), /* @__PURE__ */ React23.createElement(SummaryLine, { label: "Preset", value: data.preset }), /* @__PURE__ */ React23.createElement(
10260
10592
  SummaryLine,
10261
10593
  {
10262
10594
  label: "MCP",
@@ -10264,8 +10596,8 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
10264
10596
  }
10265
10597
  ), specs.map((spec, i) => (
10266
10598
  // biome-ignore lint/suspicious/noArrayIndexKey: review-only render, order fixed
10267
- /* @__PURE__ */ React22.createElement(Box18, { key: i, paddingLeft: 14 }, /* @__PURE__ */ React22.createElement(Text18, { dimColor: true }, "\xB7 ", spec))
10268
- )), /* @__PURE__ */ React22.createElement(Box18, { marginTop: 1 }, /* @__PURE__ */ React22.createElement(Text18, null, "Saves to ", defaultConfigPath())), error ? /* @__PURE__ */ React22.createElement(Box18, { marginTop: 1 }, /* @__PURE__ */ React22.createElement(Text18, { color: "red" }, error)) : null, /* @__PURE__ */ React22.createElement(Box18, { marginTop: 1 }, /* @__PURE__ */ React22.createElement(Text18, { dimColor: true }, "enter save \xB7 esc cancel"))), /* @__PURE__ */ React22.createElement(
10599
+ /* @__PURE__ */ React23.createElement(Box19, { key: i, paddingLeft: 14 }, /* @__PURE__ */ React23.createElement(Text19, { dimColor: true }, "\xB7 ", spec))
10600
+ )), /* @__PURE__ */ React23.createElement(Box19, { marginTop: 1 }, /* @__PURE__ */ React23.createElement(Text19, null, "Saves to ", defaultConfigPath())), error ? /* @__PURE__ */ React23.createElement(Box19, { marginTop: 1 }, /* @__PURE__ */ React23.createElement(Text19, { color: "red" }, error)) : null, /* @__PURE__ */ React23.createElement(Box19, { marginTop: 1 }, /* @__PURE__ */ React23.createElement(Text19, { dimColor: true }, "enter save \xB7 esc cancel"))), /* @__PURE__ */ React23.createElement(
10269
10601
  ReviewConfirm,
10270
10602
  {
10271
10603
  onConfirm: () => {
@@ -10291,7 +10623,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
10291
10623
  }
10292
10624
  ));
10293
10625
  }
10294
- return /* @__PURE__ */ React22.createElement(Box18, { flexDirection: "column", borderStyle: "round", borderColor: "green", paddingX: 1 }, /* @__PURE__ */ React22.createElement(Text18, { bold: true, color: "green" }, "\u25B8 Saved."), /* @__PURE__ */ React22.createElement(Box18, { marginTop: 1 }, /* @__PURE__ */ React22.createElement(Text18, null, "Run `reasonix` any time to start chatting \u2014 your settings are remembered.")), /* @__PURE__ */ React22.createElement(Box18, { marginTop: 1 }, /* @__PURE__ */ React22.createElement(Text18, { dimColor: true }, "Press enter to exit.")), /* @__PURE__ */ React22.createElement(ExitOnEnter, { onExit: exit }));
10626
+ return /* @__PURE__ */ React23.createElement(Box19, { flexDirection: "column", borderStyle: "round", borderColor: "green", paddingX: 1 }, /* @__PURE__ */ React23.createElement(Text19, { bold: true, color: "green" }, "\u25B8 Saved."), /* @__PURE__ */ React23.createElement(Box19, { marginTop: 1 }, /* @__PURE__ */ React23.createElement(Text19, null, "Run `reasonix` any time to start chatting \u2014 your settings are remembered.")), /* @__PURE__ */ React23.createElement(Box19, { marginTop: 1 }, /* @__PURE__ */ React23.createElement(Text19, { dimColor: true }, "Press enter to exit.")), /* @__PURE__ */ React23.createElement(ExitOnEnter, { onExit: exit }));
10295
10627
  }
10296
10628
  function ApiKeyStep({
10297
10629
  onSubmit,
@@ -10299,7 +10631,7 @@ function ApiKeyStep({
10299
10631
  onError
10300
10632
  }) {
10301
10633
  const [value, setValue] = useState10("");
10302
- return /* @__PURE__ */ React22.createElement(Box18, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React22.createElement(Text18, { bold: true, color: "cyan" }, "Welcome to Reasonix."), /* @__PURE__ */ React22.createElement(Box18, { marginTop: 1 }, /* @__PURE__ */ React22.createElement(Text18, null, "Paste your DeepSeek API key to get started.")), /* @__PURE__ */ React22.createElement(Text18, { dimColor: true }, "Get one (free credit on signup): https://platform.deepseek.com/api_keys"), /* @__PURE__ */ React22.createElement(Text18, { dimColor: true }, "Saved locally to ", defaultConfigPath()), /* @__PURE__ */ React22.createElement(Box18, { marginTop: 1 }, /* @__PURE__ */ React22.createElement(Text18, { bold: true, color: "cyan" }, "key \u203A "), /* @__PURE__ */ React22.createElement(
10634
+ return /* @__PURE__ */ React23.createElement(Box19, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React23.createElement(Text19, { bold: true, color: "cyan" }, "Welcome to Reasonix."), /* @__PURE__ */ React23.createElement(Box19, { marginTop: 1 }, /* @__PURE__ */ React23.createElement(Text19, null, "Paste your DeepSeek API key to get started.")), /* @__PURE__ */ React23.createElement(Text19, { dimColor: true }, "Get one (free credit on signup): https://platform.deepseek.com/api_keys"), /* @__PURE__ */ React23.createElement(Text19, { dimColor: true }, "Saved locally to ", defaultConfigPath()), /* @__PURE__ */ React23.createElement(Box19, { marginTop: 1 }, /* @__PURE__ */ React23.createElement(Text19, { bold: true, color: "cyan" }, "key \u203A "), /* @__PURE__ */ React23.createElement(
10303
10635
  TextInput2,
10304
10636
  {
10305
10637
  value,
@@ -10316,7 +10648,7 @@ function ApiKeyStep({
10316
10648
  mask: "\u2022",
10317
10649
  placeholder: "sk-..."
10318
10650
  }
10319
- )), error ? /* @__PURE__ */ React22.createElement(Box18, { marginTop: 1 }, /* @__PURE__ */ React22.createElement(Text18, { color: "red" }, error)) : value ? /* @__PURE__ */ React22.createElement(Box18, { marginTop: 1 }, /* @__PURE__ */ React22.createElement(Text18, { dimColor: true }, "preview: ", redactKey(value))) : null);
10651
+ )), error ? /* @__PURE__ */ React23.createElement(Box19, { marginTop: 1 }, /* @__PURE__ */ React23.createElement(Text19, { color: "red" }, error)) : value ? /* @__PURE__ */ React23.createElement(Box19, { marginTop: 1 }, /* @__PURE__ */ React23.createElement(Text19, { dimColor: true }, "preview: ", redactKey(value))) : null);
10320
10652
  }
10321
10653
  function McpArgsStep({
10322
10654
  entry,
@@ -10325,7 +10657,7 @@ function McpArgsStep({
10325
10657
  onError
10326
10658
  }) {
10327
10659
  const [value, setValue] = useState10("");
10328
- return /* @__PURE__ */ React22.createElement(StepFrame, { title: `Configure ${entry.name}`, step: 2, total: 3 }, /* @__PURE__ */ React22.createElement(Box18, { flexDirection: "column" }, /* @__PURE__ */ React22.createElement(Text18, null, entry.summary), entry.note ? /* @__PURE__ */ React22.createElement(Box18, { marginTop: 1 }, /* @__PURE__ */ React22.createElement(Text18, { dimColor: true }, entry.note)) : null, /* @__PURE__ */ React22.createElement(Box18, { marginTop: 1 }, /* @__PURE__ */ React22.createElement(Text18, null, "Required parameter: "), /* @__PURE__ */ React22.createElement(Text18, { bold: true }, entry.userArgs)), /* @__PURE__ */ React22.createElement(Box18, { marginTop: 1 }, /* @__PURE__ */ React22.createElement(Text18, { bold: true, color: "cyan" }, entry.userArgs, " \u203A "), /* @__PURE__ */ React22.createElement(
10660
+ return /* @__PURE__ */ React23.createElement(StepFrame, { title: `Configure ${entry.name}`, step: 2, total: 3 }, /* @__PURE__ */ React23.createElement(Box19, { flexDirection: "column" }, /* @__PURE__ */ React23.createElement(Text19, null, entry.summary), entry.note ? /* @__PURE__ */ React23.createElement(Box19, { marginTop: 1 }, /* @__PURE__ */ React23.createElement(Text19, { dimColor: true }, entry.note)) : null, /* @__PURE__ */ React23.createElement(Box19, { marginTop: 1 }, /* @__PURE__ */ React23.createElement(Text19, null, "Required parameter: "), /* @__PURE__ */ React23.createElement(Text19, { bold: true }, entry.userArgs)), /* @__PURE__ */ React23.createElement(Box19, { marginTop: 1 }, /* @__PURE__ */ React23.createElement(Text19, { bold: true, color: "cyan" }, entry.userArgs, " \u203A "), /* @__PURE__ */ React23.createElement(
10329
10661
  TextInput2,
10330
10662
  {
10331
10663
  value,
@@ -10341,7 +10673,7 @@ function McpArgsStep({
10341
10673
  },
10342
10674
  placeholder: placeholderFor(entry)
10343
10675
  }
10344
- )), error ? /* @__PURE__ */ React22.createElement(Box18, { marginTop: 1 }, /* @__PURE__ */ React22.createElement(Text18, { color: "red" }, error)) : null));
10676
+ )), error ? /* @__PURE__ */ React23.createElement(Box19, { marginTop: 1 }, /* @__PURE__ */ React23.createElement(Text19, { color: "red" }, error)) : null));
10345
10677
  }
10346
10678
  function ReviewConfirm({ onConfirm }) {
10347
10679
  useInput7((_i, key) => {
@@ -10361,10 +10693,10 @@ function StepFrame({
10361
10693
  total,
10362
10694
  children
10363
10695
  }) {
10364
- return /* @__PURE__ */ React22.createElement(Box18, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React22.createElement(Box18, null, /* @__PURE__ */ React22.createElement(Text18, { dimColor: true }, "Step ", step, "/", total, " \xB7", " "), /* @__PURE__ */ React22.createElement(Text18, { bold: true, color: "cyan" }, title)), /* @__PURE__ */ React22.createElement(Box18, { marginTop: 1, flexDirection: "column" }, children));
10696
+ return /* @__PURE__ */ React23.createElement(Box19, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1 }, /* @__PURE__ */ React23.createElement(Box19, null, /* @__PURE__ */ React23.createElement(Text19, { dimColor: true }, "Step ", step, "/", total, " \xB7", " "), /* @__PURE__ */ React23.createElement(Text19, { bold: true, color: "cyan" }, title)), /* @__PURE__ */ React23.createElement(Box19, { marginTop: 1, flexDirection: "column" }, children));
10365
10697
  }
10366
10698
  function SummaryLine({ label, value }) {
10367
- return /* @__PURE__ */ React22.createElement(Box18, null, /* @__PURE__ */ React22.createElement(Text18, null, label.padEnd(12)), /* @__PURE__ */ React22.createElement(Text18, { bold: true }, value));
10699
+ return /* @__PURE__ */ React23.createElement(Box19, null, /* @__PURE__ */ React23.createElement(Text19, null, label.padEnd(12)), /* @__PURE__ */ React23.createElement(Text19, { bold: true }, value));
10368
10700
  }
10369
10701
  function presetItems() {
10370
10702
  return ["fast", "smart", "max"].map((name) => ({
@@ -10420,7 +10752,7 @@ async function setupCommand(_opts = {}) {
10420
10752
  const existingKey = loadApiKey();
10421
10753
  const existing = readConfig();
10422
10754
  const { waitUntilExit, unmount } = render4(
10423
- /* @__PURE__ */ React23.createElement(
10755
+ /* @__PURE__ */ React24.createElement(
10424
10756
  Wizard,
10425
10757
  {
10426
10758
  existingApiKey: existingKey,