reasonix 0.5.7 → 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);
@@ -6999,6 +7134,50 @@ function formatTokens(n) {
6999
7134
  return k >= 100 ? `${k.toFixed(0)}K` : `${k.toFixed(1)}K`;
7000
7135
  }
7001
7136
 
7137
+ // src/cli/ui/bang.ts
7138
+ function detectBangCommand(text) {
7139
+ if (!text.startsWith("!")) return null;
7140
+ const body = text.slice(1).trim();
7141
+ if (!body) return null;
7142
+ return body;
7143
+ }
7144
+ function formatBangUserMessage(cmd, output) {
7145
+ return `[!${cmd}]
7146
+ ${output}`;
7147
+ }
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
+
7002
7181
  // src/cli/ui/slash.ts
7003
7182
  import { spawnSync } from "child_process";
7004
7183
 
@@ -7115,12 +7294,28 @@ var SLASH_COMMANDS = [
7115
7294
  {
7116
7295
  cmd: "preset",
7117
7296
  argsHint: "<fast|smart|max>",
7118
- 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"
7119
7305
  },
7120
- { cmd: "model", argsHint: "<id>", summary: "switch DeepSeek model id" },
7121
7306
  { cmd: "models", summary: "list available models fetched from DeepSeek /models" },
7122
- { cmd: "harvest", argsHint: "[on|off]", summary: "toggle Pillar-2 plan-state extraction" },
7123
- { 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
+ },
7124
7319
  { cmd: "mcp", summary: "list MCP servers + tools attached to this session" },
7125
7320
  { cmd: "tool", argsHint: "[N]", summary: "dump full output of the Nth tool call (1=latest)" },
7126
7321
  {
@@ -7157,6 +7352,7 @@ var SLASH_COMMANDS = [
7157
7352
  argsHint: "[tokens]",
7158
7353
  summary: "shrink oversized tool results in the log (cap in tokens, default 4000)"
7159
7354
  },
7355
+ { cmd: "keys", summary: "show all keyboard shortcuts and prompt prefixes" },
7160
7356
  { cmd: "sessions", summary: "list saved sessions (current marked with \u25B8)" },
7161
7357
  { cmd: "forget", summary: "delete the current session from disk" },
7162
7358
  { cmd: "setup", summary: "reminds you to exit and run `reasonix setup`" },
@@ -7177,7 +7373,8 @@ var SLASH_COMMANDS = [
7177
7373
  cmd: "plan",
7178
7374
  argsHint: "[on|off]",
7179
7375
  summary: "toggle read-only plan mode (writes bounced until submit_plan + approval)",
7180
- contextual: "code"
7376
+ contextual: "code",
7377
+ argCompleter: ["on", "off"]
7181
7378
  },
7182
7379
  {
7183
7380
  cmd: "apply-plan",
@@ -7192,6 +7389,27 @@ function suggestSlashCommands(prefix, codeMode = false) {
7192
7389
  return c.cmd.startsWith(p);
7193
7390
  });
7194
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
+ }
7195
7413
  function parseSlash(text) {
7196
7414
  if (!text.startsWith("/")) return null;
7197
7415
  const parts = text.slice(1).trim().split(/\s+/);
@@ -7217,12 +7435,42 @@ function handleSlash(cmd, args, loop, ctx = {}) {
7217
7435
  info: `\u25B8 new conversation \u2014 dropped ${dropped} message(s) from context. Same session, fresh slate.`
7218
7436
  };
7219
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
+ };
7220
7467
  case "help":
7221
7468
  case "?":
7222
7469
  return {
7223
7470
  info: [
7224
7471
  "Commands:",
7225
7472
  " /help this message",
7473
+ " /keys keyboard shortcuts + prompt prefixes (!, @, /)",
7226
7474
  " /status show current settings",
7227
7475
  " /preset <fast|smart|max> one-tap presets \u2014 see below",
7228
7476
  " /model <id> deepseek-chat or deepseek-reasoner",
@@ -7250,6 +7498,16 @@ function handleSlash(cmd, args, loop, ctx = {}) {
7250
7498
  " /clear clear displayed scrollback only (context kept \u2014 model still sees it)",
7251
7499
  " /exit quit",
7252
7500
  "",
7501
+ "Shell shortcut:",
7502
+ " !<cmd> run <cmd> in the sandbox root; output goes into",
7503
+ " the conversation so the model sees it next turn.",
7504
+ " No allowlist gate \u2014 user-typed = explicit consent.",
7505
+ " Example: !git status !ls src/ !npm test",
7506
+ "",
7507
+ "File references (code mode):",
7508
+ " @path/to/file inline file content under [Referenced files] on send.",
7509
+ " Type `@` to open the picker (\u2191\u2193 navigate, Tab/Enter pick).",
7510
+ "",
7253
7511
  "Presets:",
7254
7512
  " fast deepseek-chat no harvest no branch ~1\xA2/100turns \u2190 default",
7255
7513
  " smart reasoner harvest ~10x cost, slower",
@@ -8156,11 +8414,19 @@ function App({
8156
8414
  const atFiles = useMemo(() => {
8157
8415
  if (!codeMode?.rootDir) return [];
8158
8416
  try {
8159
- return listFilesSync(codeMode.rootDir, { maxResults: 500 });
8417
+ return listFilesWithStatsSync(codeMode.rootDir, { maxResults: 500 });
8160
8418
  } catch {
8161
8419
  return [];
8162
8420
  }
8163
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
+ }, []);
8164
8430
  const atPicker = useMemo(() => {
8165
8431
  if (!codeMode?.rootDir) return null;
8166
8432
  if (slashMatches !== null) return null;
@@ -8168,7 +8434,10 @@ function App({
8168
8434
  }, [codeMode?.rootDir, input, slashMatches]);
8169
8435
  const atMatches = useMemo(() => {
8170
8436
  if (!atPicker) return null;
8171
- return rankPickerCandidates(atFiles, atPicker.query, 40);
8437
+ return rankPickerCandidates(atFiles, atPicker.query, {
8438
+ limit: 40,
8439
+ recentlyUsed: recentFilesRef.current
8440
+ });
8172
8441
  }, [atPicker, atFiles]);
8173
8442
  useEffect2(() => {
8174
8443
  setAtSelected((prev) => {
@@ -8185,6 +8454,45 @@ function App({
8185
8454
  },
8186
8455
  [atPicker, input]
8187
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
+ );
8188
8496
  const loopRef = useRef2(null);
8189
8497
  const subagentSinkRef = useRef2({ current: null });
8190
8498
  const loop = useMemo(() => {
@@ -8369,6 +8677,21 @@ function App({
8369
8677
  return;
8370
8678
  }
8371
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
+ }
8372
8695
  if (slashMatches && slashMatches.length > 0) {
8373
8696
  if (key.upArrow) {
8374
8697
  setSlashSelected((i) => Math.max(0, i - 1));
@@ -8461,6 +8784,13 @@ function App({
8461
8784
  return;
8462
8785
  }
8463
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
+ }
8464
8794
  if (text.startsWith("/") && !text.includes(" ")) {
8465
8795
  const typed = text.slice(1).toLowerCase();
8466
8796
  const matches = suggestSlashCommands(typed, !!codeMode);
@@ -8478,6 +8808,49 @@ function App({
8478
8808
  promptHistory.current.push(text);
8479
8809
  return;
8480
8810
  }
8811
+ const bangCmd = detectBangCommand(text);
8812
+ if (bangCmd !== null) {
8813
+ const bangRoot = codeMode?.rootDir ?? process.cwd();
8814
+ promptHistory.current.push(text);
8815
+ setHistorical((prev) => [
8816
+ ...prev,
8817
+ {
8818
+ id: `bang-u-${Date.now()}`,
8819
+ role: "user",
8820
+ text,
8821
+ leadSeparator: prev.length > 0
8822
+ }
8823
+ ]);
8824
+ setBusy(true);
8825
+ try {
8826
+ const result = await runCommand(bangCmd, {
8827
+ cwd: bangRoot,
8828
+ timeoutSec: 60,
8829
+ maxOutputChars: 32e3
8830
+ });
8831
+ const formatted = formatCommandResult(bangCmd, result);
8832
+ setHistorical((prev) => [
8833
+ ...prev,
8834
+ { id: `bang-o-${Date.now()}`, role: "info", text: formatted }
8835
+ ]);
8836
+ loop.log.append({
8837
+ role: "user",
8838
+ content: formatBangUserMessage(bangCmd, formatted)
8839
+ });
8840
+ } catch (err) {
8841
+ setHistorical((prev) => [
8842
+ ...prev,
8843
+ {
8844
+ id: `bang-e-${Date.now()}`,
8845
+ role: "warning",
8846
+ text: `! command failed: ${err.message}`
8847
+ }
8848
+ ]);
8849
+ } finally {
8850
+ setBusy(false);
8851
+ }
8852
+ return;
8853
+ }
8481
8854
  const slash = parseSlash(text);
8482
8855
  if (slash) {
8483
8856
  const result = handleSlash(slash.cmd, slash.args, loop, {
@@ -8567,13 +8940,19 @@ function App({
8567
8940
  if (promptReport.blocked) return;
8568
8941
  }
8569
8942
  promptHistory.current.push(text);
8943
+ const pasteDisplay = formatLongPaste(text);
8570
8944
  setHistorical((prev) => [
8571
8945
  ...prev,
8572
8946
  // `leadSeparator`: thin rule above this user turn when history
8573
8947
  // isn't empty — visual pacing for multi-turn sessions. First
8574
8948
  // user message leaves it off so the UI doesn't open with a
8575
8949
  // dangling divider.
8576
- { 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
+ }
8577
8956
  ]);
8578
8957
  const assistantId = `a-${Date.now()}`;
8579
8958
  const streamRef = { id: assistantId, text: "", reasoning: "" };
@@ -8710,6 +9089,19 @@ function App({
8710
9089
  } else if (ev.role === "tool_start") {
8711
9090
  setOngoingTool({ name: ev.toolName ?? "?", args: ev.toolArgs });
8712
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
+ }
8713
9105
  } else if (ev.role === "tool") {
8714
9106
  flush();
8715
9107
  setOngoingTool(null);
@@ -8819,6 +9211,11 @@ function App({
8819
9211
  atPicker,
8820
9212
  atSelected,
8821
9213
  pickAtMention,
9214
+ recordRecentFile,
9215
+ slashArgMatches,
9216
+ slashArgContext,
9217
+ slashArgSelected,
9218
+ pickSlashArg,
8822
9219
  togglePlanMode,
8823
9220
  writeTranscript
8824
9221
  ]
@@ -8969,7 +9366,7 @@ Stay in plan mode \u2014 address the feedback (explore more if needed), then sub
8969
9366
  if (stagedInput?.plan) setPendingPlan(stagedInput.plan);
8970
9367
  setStagedInput(null);
8971
9368
  }, [stagedInput]);
8972
- 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(
8973
9370
  StatsPanel,
8974
9371
  {
8975
9372
  summary,
@@ -8982,21 +9379,21 @@ Stay in plan mode \u2014 address the feedback (explore more if needed), then sub
8982
9379
  busy,
8983
9380
  updateAvailable
8984
9381
  }
8985
- ), /* @__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(
8986
9383
  PlanRefineInput,
8987
9384
  {
8988
9385
  mode: stagedInput.mode,
8989
9386
  onSubmit: handleStagedInputSubmit,
8990
9387
  onCancel: handleStagedInputCancel
8991
9388
  }
8992
- ) : 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(
8993
9390
  ShellConfirm,
8994
9391
  {
8995
9392
  command: pendingShell,
8996
9393
  allowPrefix: derivePrefix(pendingShell),
8997
9394
  onChoose: handleShellConfirm
8998
9395
  }
8999
- ) : /* @__PURE__ */ React13.createElement(React13.Fragment, null, /* @__PURE__ */ React13.createElement(
9396
+ ) : /* @__PURE__ */ React14.createElement(React14.Fragment, null, /* @__PURE__ */ React14.createElement(
9000
9397
  PromptInput,
9001
9398
  {
9002
9399
  value: input,
@@ -9004,27 +9401,36 @@ Stay in plan mode \u2014 address the feedback (explore more if needed), then sub
9004
9401
  onSubmit: handleSubmit,
9005
9402
  disabled: busy
9006
9403
  }
9007
- ), /* @__PURE__ */ React13.createElement(SlashSuggestions, { matches: slashMatches, selectedIndex: slashSelected }), /* @__PURE__ */ React13.createElement(
9404
+ ), /* @__PURE__ */ React14.createElement(SlashSuggestions, { matches: slashMatches, selectedIndex: slashSelected }), /* @__PURE__ */ React14.createElement(
9008
9405
  AtMentionSuggestions,
9009
9406
  {
9010
9407
  matches: atMatches,
9011
9408
  selectedIndex: atSelected,
9012
9409
  query: atPicker?.query ?? ""
9013
9410
  }
9014
- ))));
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)));
9015
9421
  }
9016
9422
  var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
9017
9423
  function StatusRow({ text }) {
9018
9424
  const tick = useTick();
9019
9425
  const elapsed = useElapsedSeconds();
9020
- 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`));
9021
9427
  }
9022
9428
  function SubagentRow({
9023
9429
  activity
9024
9430
  }) {
9025
9431
  const tick = useTick();
9026
9432
  const seconds = (activity.elapsedMs / 1e3).toFixed(1);
9027
- 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`));
9028
9434
  }
9029
9435
  function OngoingToolRow({
9030
9436
  tool,
@@ -9033,7 +9439,7 @@ function OngoingToolRow({
9033
9439
  const tick = useTick();
9034
9440
  const elapsed = useElapsedSeconds();
9035
9441
  const summary = summarizeToolArgs(tool.name, tool.args);
9036
- 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);
9037
9443
  }
9038
9444
  function renderProgressLine(p) {
9039
9445
  const msg = p.message ? ` ${p.message}` : "";
@@ -9099,18 +9505,9 @@ function formatEditResults(results) {
9099
9505
  return [header2, ...lines].join("\n");
9100
9506
  }
9101
9507
  function formatPendingPreview(blocks) {
9102
- const lines = blocks.map((b) => {
9103
- const removed = b.search === "" ? 0 : countLines2(b.search);
9104
- const added = countLines2(b.replace);
9105
- const tag = b.search === "" ? "NEW " : " ";
9106
- return ` ${tag}${b.path} (-${removed} +${added} lines)`;
9107
- });
9108
9508
  const header2 = `\u25B8 ${blocks.length} pending edit block(s) \u2014 /apply (or y) to commit \xB7 /discard (or n) to drop`;
9109
- return [header2, ...lines].join("\n");
9110
- }
9111
- function countLines2(s) {
9112
- if (s.length === 0) return 0;
9113
- return (s.match(/\n/g)?.length ?? 0) + 1;
9509
+ const diffLines = formatAllBlockDiffs(blocks);
9510
+ return [header2, ...diffLines].join("\n");
9114
9511
  }
9115
9512
  function formatUndoResults(results) {
9116
9513
  const lines = results.map((r) => {
@@ -9129,15 +9526,15 @@ function describeRepair(repair) {
9129
9526
  }
9130
9527
 
9131
9528
  // src/cli/ui/SessionPicker.tsx
9132
- import { Box as Box13, Text as Text13 } from "ink";
9133
- import React14 from "react";
9529
+ import { Box as Box14, Text as Text14 } from "ink";
9530
+ import React15 from "react";
9134
9531
  function SessionPicker({
9135
9532
  sessionName,
9136
9533
  messageCount,
9137
9534
  lastActive,
9138
9535
  onChoose
9139
9536
  }) {
9140
- 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(
9141
9538
  SingleSelect,
9142
9539
  {
9143
9540
  initialValue: "new",
@@ -9160,7 +9557,7 @@ function SessionPicker({
9160
9557
  ],
9161
9558
  onSubmit: (v) => onChoose(v)
9162
9559
  }
9163
- ), /* @__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")));
9164
9561
  }
9165
9562
  function relativeTime(date) {
9166
9563
  const ms = Date.now() - date.getTime();
@@ -9176,9 +9573,9 @@ function relativeTime(date) {
9176
9573
  }
9177
9574
 
9178
9575
  // src/cli/ui/Setup.tsx
9179
- 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";
9180
9577
  import TextInput from "ink-text-input";
9181
- import React15, { useState as useState6 } from "react";
9578
+ import React16, { useState as useState6 } from "react";
9182
9579
  function Setup({ onReady }) {
9183
9580
  const [value, setValue] = useState6("");
9184
9581
  const [error, setError] = useState6(null);
@@ -9202,7 +9599,7 @@ function Setup({ onReady }) {
9202
9599
  }
9203
9600
  onReady(trimmed);
9204
9601
  };
9205
- 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(
9206
9603
  TextInput,
9207
9604
  {
9208
9605
  value,
@@ -9211,7 +9608,7 @@ function Setup({ onReady }) {
9211
9608
  mask: "\u2022",
9212
9609
  placeholder: "sk-..."
9213
9610
  }
9214
- )), 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.)")));
9215
9612
  }
9216
9613
 
9217
9614
  // src/cli/commands/chat.tsx
@@ -9227,7 +9624,7 @@ function Root({
9227
9624
  const [key, setKey] = useState7(initialKey);
9228
9625
  const [pending, setPending] = useState7(sessionPreview);
9229
9626
  if (!key) {
9230
- return /* @__PURE__ */ React16.createElement(
9627
+ return /* @__PURE__ */ React17.createElement(
9231
9628
  Setup,
9232
9629
  {
9233
9630
  onReady: (k) => {
@@ -9239,7 +9636,7 @@ function Root({
9239
9636
  }
9240
9637
  process.env.DEEPSEEK_API_KEY = key;
9241
9638
  if (pending && appProps.session) {
9242
- return /* @__PURE__ */ React16.createElement(
9639
+ return /* @__PURE__ */ React17.createElement(
9243
9640
  SessionPicker,
9244
9641
  {
9245
9642
  sessionName: appProps.session,
@@ -9254,7 +9651,7 @@ function Root({
9254
9651
  }
9255
9652
  );
9256
9653
  }
9257
- return /* @__PURE__ */ React16.createElement(
9654
+ return /* @__PURE__ */ React17.createElement(
9258
9655
  App,
9259
9656
  {
9260
9657
  model: appProps.model,
@@ -9357,7 +9754,7 @@ async function chatCommand(opts) {
9357
9754
  rewriteSession(opts.session, []);
9358
9755
  }
9359
9756
  const { waitUntilExit } = render(
9360
- /* @__PURE__ */ React16.createElement(
9757
+ /* @__PURE__ */ React17.createElement(
9361
9758
  Root,
9362
9759
  {
9363
9760
  initialKey,
@@ -9420,34 +9817,34 @@ async function codeCommand(opts = {}) {
9420
9817
  import { writeFileSync as writeFileSync5 } from "fs";
9421
9818
  import { basename as basename2 } from "path";
9422
9819
  import { render as render2 } from "ink";
9423
- import React19 from "react";
9820
+ import React20 from "react";
9424
9821
 
9425
9822
  // src/cli/ui/DiffApp.tsx
9426
- import { Box as Box16, Static as Static2, Text as Text16, useApp as useApp3, useInput as useInput5 } from "ink";
9427
- 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";
9428
9825
 
9429
9826
  // src/cli/ui/RecordView.tsx
9430
- import { Box as Box15, Text as Text15 } from "ink";
9431
- import React17 from "react";
9827
+ import { Box as Box16, Text as Text16 } from "ink";
9828
+ import React18 from "react";
9432
9829
  function RecordView({ rec, compact = false }) {
9433
9830
  const toolArgsMax = compact ? 120 : 200;
9434
9831
  const toolContentMax = compact ? 200 : 400;
9435
9832
  if (rec.role === "user") {
9436
- 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));
9437
9834
  }
9438
9835
  if (rec.role === "assistant_final") {
9439
- 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)"));
9440
9837
  }
9441
9838
  if (rec.role === "tool") {
9442
- 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)));
9443
9840
  }
9444
9841
  if (rec.role === "error") {
9445
- 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));
9446
9843
  }
9447
9844
  if (rec.role === "done" || rec.role === "assistant_delta") {
9448
9845
  return null;
9449
9846
  }
9450
- 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));
9451
9848
  }
9452
9849
  function CacheBadge({ usage }) {
9453
9850
  const hit = usage.prompt_cache_hit_tokens ?? 0;
@@ -9456,7 +9853,7 @@ function CacheBadge({ usage }) {
9456
9853
  if (total === 0) return null;
9457
9854
  const pct2 = hit / total * 100;
9458
9855
  const color = pct2 >= 70 ? "green" : pct2 >= 40 ? "yellow" : "red";
9459
- 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), "%"));
9460
9857
  }
9461
9858
  function truncate3(s, max) {
9462
9859
  return s.length <= max ? s : `${s.slice(0, max)}\u2026 (+${s.length - max} chars)`;
@@ -9490,7 +9887,7 @@ function DiffApp({ report }) {
9490
9887
  }
9491
9888
  });
9492
9889
  const pair = report.pairs[idx];
9493
- 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")));
9494
9891
  }
9495
9892
  function DiffHeader({ report }) {
9496
9893
  const a = report.a;
@@ -9508,15 +9905,15 @@ function DiffHeader({ report }) {
9508
9905
  } else if (a.stats.prefixHashes[0] && a.stats.prefixHashes[0] === b.stats.prefixHashes[0]) {
9509
9906
  prefixLine = `shared prefix hash ${a.stats.prefixHashes[0].slice(0, 12)}\u2026 \u2014 cache delta attributable to log stability, not prompt change.`;
9510
9907
  }
9511
- 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);
9512
9909
  }
9513
9910
  function Pane({
9514
9911
  label,
9515
9912
  headerColor,
9516
9913
  records
9517
9914
  }) {
9518
- return /* @__PURE__ */ React18.createElement(
9519
- Box16,
9915
+ return /* @__PURE__ */ React19.createElement(
9916
+ Box17,
9520
9917
  {
9521
9918
  flexDirection: "column",
9522
9919
  flexGrow: 1,
@@ -9524,21 +9921,21 @@ function Pane({
9524
9921
  borderStyle: "single",
9525
9922
  borderColor: headerColor
9526
9923
  },
9527
- /* @__PURE__ */ React18.createElement(Text16, { color: headerColor, bold: true }, label),
9528
- 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 }))
9529
9926
  );
9530
9927
  }
9531
9928
  function KindBadge({ kind }) {
9532
9929
  if (kind === "match") {
9533
- return /* @__PURE__ */ React18.createElement(Text16, { color: "green" }, "\u2713 match");
9930
+ return /* @__PURE__ */ React19.createElement(Text17, { color: "green" }, "\u2713 match");
9534
9931
  }
9535
9932
  if (kind === "diverge") {
9536
- return /* @__PURE__ */ React18.createElement(Text16, { color: "yellow" }, "\u2605 diverge");
9933
+ return /* @__PURE__ */ React19.createElement(Text17, { color: "yellow" }, "\u2605 diverge");
9537
9934
  }
9538
9935
  if (kind === "only_in_a") {
9539
- return /* @__PURE__ */ React18.createElement(Text16, { color: "blue" }, "\u2190 only in A");
9936
+ return /* @__PURE__ */ React19.createElement(Text17, { color: "blue" }, "\u2190 only in A");
9540
9937
  }
9541
- return /* @__PURE__ */ React18.createElement(Text16, { color: "magenta" }, "\u2192 only in B");
9938
+ return /* @__PURE__ */ React19.createElement(Text17, { color: "magenta" }, "\u2192 only in B");
9542
9939
  }
9543
9940
  function paneRecords(pair, side) {
9544
9941
  if (!pair) return [];
@@ -9569,7 +9966,7 @@ markdown report written to ${opts.mdPath}`);
9569
9966
  return;
9570
9967
  }
9571
9968
  if (wantTui) {
9572
- const { waitUntilExit } = render2(React19.createElement(DiffApp, { report }), {
9969
+ const { waitUntilExit } = render2(React20.createElement(DiffApp, { report }), {
9573
9970
  exitOnCtrlC: true,
9574
9971
  patchConsole: false
9575
9972
  });
@@ -9710,11 +10107,11 @@ function pad2(s, width) {
9710
10107
 
9711
10108
  // src/cli/commands/replay.ts
9712
10109
  import { render as render3 } from "ink";
9713
- import React21 from "react";
10110
+ import React22 from "react";
9714
10111
 
9715
10112
  // src/cli/ui/ReplayApp.tsx
9716
- import { Box as Box17, Static as Static3, Text as Text17, useApp as useApp4, useInput as useInput6 } from "ink";
9717
- 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";
9718
10115
  function ReplayApp({ meta, pages }) {
9719
10116
  const { exit } = useApp4();
9720
10117
  const maxIdx = Math.max(0, pages.length - 1);
@@ -9753,14 +10150,14 @@ function ReplayApp({ meta, pages }) {
9753
10150
  const prefixHash = cumStats.prefixHashes.length === 1 ? cumStats.prefixHashes[0].slice(0, 16) : cumStats.prefixHashes.length === 0 ? "(untracked)" : `(churned \xD7${cumStats.prefixHashes.length})`;
9754
10151
  const currentPage = pages[idx];
9755
10152
  const progressLabel = pages.length === 0 ? "empty transcript" : `turn ${idx + 1} / ${pages.length}`;
9756
- return /* @__PURE__ */ React20.createElement(Box17, { flexDirection: "column" }, /* @__PURE__ */ React20.createElement(
10153
+ return /* @__PURE__ */ React21.createElement(Box18, { flexDirection: "column" }, /* @__PURE__ */ React21.createElement(
9757
10154
  StatsPanel,
9758
10155
  {
9759
10156
  summary,
9760
10157
  model: cumStats.models[0] ?? meta?.model ?? "?",
9761
10158
  prefixHash
9762
10159
  }
9763
- ), /* @__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")));
9764
10161
  }
9765
10162
 
9766
10163
  // src/cli/commands/replay.ts
@@ -9772,7 +10169,7 @@ async function replayCommand(opts) {
9772
10169
  }
9773
10170
  const { parsed } = replayFromFile(opts.path);
9774
10171
  const pages = groupRecordsByTurn(parsed.records);
9775
- const { waitUntilExit } = render3(React21.createElement(ReplayApp, { meta: parsed.meta, pages }), {
10172
+ const { waitUntilExit } = render3(React22.createElement(ReplayApp, { meta: parsed.meta, pages }), {
9776
10173
  exitOnCtrlC: true,
9777
10174
  patchConsole: false
9778
10175
  });
@@ -10077,12 +10474,12 @@ function truncate4(s, max) {
10077
10474
 
10078
10475
  // src/cli/commands/setup.tsx
10079
10476
  import { render as render4 } from "ink";
10080
- import React23 from "react";
10477
+ import React24 from "react";
10081
10478
 
10082
10479
  // src/cli/ui/Wizard.tsx
10083
- 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";
10084
10481
  import TextInput2 from "ink-text-input";
10085
- import React22, { useState as useState10 } from "react";
10482
+ import React23, { useState as useState10 } from "react";
10086
10483
 
10087
10484
  // src/cli/ui/presets.ts
10088
10485
  var PRESETS = {
@@ -10121,7 +10518,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
10121
10518
  if (key.escape && step !== "saved" && onCancel) onCancel();
10122
10519
  });
10123
10520
  if (step === "apiKey") {
10124
- return /* @__PURE__ */ React22.createElement(
10521
+ return /* @__PURE__ */ React23.createElement(
10125
10522
  ApiKeyStep,
10126
10523
  {
10127
10524
  onSubmit: (key) => {
@@ -10135,7 +10532,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
10135
10532
  );
10136
10533
  }
10137
10534
  if (step === "preset") {
10138
- 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(
10139
10536
  SingleSelect,
10140
10537
  {
10141
10538
  items: presetItems(),
@@ -10145,10 +10542,10 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
10145
10542
  setStep("mcp");
10146
10543
  }
10147
10544
  }
10148
- ), /* @__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")));
10149
10546
  }
10150
10547
  if (step === "mcp") {
10151
- 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(
10152
10549
  MultiSelect,
10153
10550
  {
10154
10551
  items: mcpItems(),
@@ -10173,7 +10570,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
10173
10570
  }
10174
10571
  const currentName = pending[0];
10175
10572
  const entry = CATALOG_BY_NAME.get(currentName);
10176
- return /* @__PURE__ */ React22.createElement(
10573
+ return /* @__PURE__ */ React23.createElement(
10177
10574
  McpArgsStep,
10178
10575
  {
10179
10576
  entry,
@@ -10191,7 +10588,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
10191
10588
  }
10192
10589
  if (step === "review") {
10193
10590
  const specs = data.selectedCatalog.map((name) => buildSpec(name, data.catalogArgs));
10194
- 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(
10195
10592
  SummaryLine,
10196
10593
  {
10197
10594
  label: "MCP",
@@ -10199,8 +10596,8 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
10199
10596
  }
10200
10597
  ), specs.map((spec, i) => (
10201
10598
  // biome-ignore lint/suspicious/noArrayIndexKey: review-only render, order fixed
10202
- /* @__PURE__ */ React22.createElement(Box18, { key: i, paddingLeft: 14 }, /* @__PURE__ */ React22.createElement(Text18, { dimColor: true }, "\xB7 ", spec))
10203
- )), /* @__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(
10204
10601
  ReviewConfirm,
10205
10602
  {
10206
10603
  onConfirm: () => {
@@ -10226,7 +10623,7 @@ function Wizard({ onComplete, onCancel, existingApiKey, initial }) {
10226
10623
  }
10227
10624
  ));
10228
10625
  }
10229
- 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 }));
10230
10627
  }
10231
10628
  function ApiKeyStep({
10232
10629
  onSubmit,
@@ -10234,7 +10631,7 @@ function ApiKeyStep({
10234
10631
  onError
10235
10632
  }) {
10236
10633
  const [value, setValue] = useState10("");
10237
- 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(
10238
10635
  TextInput2,
10239
10636
  {
10240
10637
  value,
@@ -10251,7 +10648,7 @@ function ApiKeyStep({
10251
10648
  mask: "\u2022",
10252
10649
  placeholder: "sk-..."
10253
10650
  }
10254
- )), 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);
10255
10652
  }
10256
10653
  function McpArgsStep({
10257
10654
  entry,
@@ -10260,7 +10657,7 @@ function McpArgsStep({
10260
10657
  onError
10261
10658
  }) {
10262
10659
  const [value, setValue] = useState10("");
10263
- 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(
10264
10661
  TextInput2,
10265
10662
  {
10266
10663
  value,
@@ -10276,7 +10673,7 @@ function McpArgsStep({
10276
10673
  },
10277
10674
  placeholder: placeholderFor(entry)
10278
10675
  }
10279
- )), 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));
10280
10677
  }
10281
10678
  function ReviewConfirm({ onConfirm }) {
10282
10679
  useInput7((_i, key) => {
@@ -10296,10 +10693,10 @@ function StepFrame({
10296
10693
  total,
10297
10694
  children
10298
10695
  }) {
10299
- 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));
10300
10697
  }
10301
10698
  function SummaryLine({ label, value }) {
10302
- 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));
10303
10700
  }
10304
10701
  function presetItems() {
10305
10702
  return ["fast", "smart", "max"].map((name) => ({
@@ -10355,7 +10752,7 @@ async function setupCommand(_opts = {}) {
10355
10752
  const existingKey = loadApiKey();
10356
10753
  const existing = readConfig();
10357
10754
  const { waitUntilExit, unmount } = render4(
10358
- /* @__PURE__ */ React23.createElement(
10755
+ /* @__PURE__ */ React24.createElement(
10359
10756
  Wizard,
10360
10757
  {
10361
10758
  existingApiKey: existingKey,