reasonix 0.4.1 → 0.4.3

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
@@ -1283,6 +1283,39 @@ var CacheFirstLoop = class {
1283
1283
  abort() {
1284
1284
  this._aborted = true;
1285
1285
  }
1286
+ /**
1287
+ * Drop everything in the log after (and including) the most recent
1288
+ * user message. Used by `/retry` so the caller can re-send that
1289
+ * message with a fresh turn instead of layering another response on
1290
+ * top of the prior exchange. Returns the content of the dropped user
1291
+ * message, or `null` if there isn't one yet.
1292
+ *
1293
+ * Persists by rewriting the session file — otherwise the next
1294
+ * launch would rehydrate the old exchange and `/retry` would seem
1295
+ * to have done nothing.
1296
+ */
1297
+ retryLastUser() {
1298
+ const entries = this.log.entries;
1299
+ let lastUserIdx = -1;
1300
+ for (let i = entries.length - 1; i >= 0; i--) {
1301
+ if (entries[i].role === "user") {
1302
+ lastUserIdx = i;
1303
+ break;
1304
+ }
1305
+ }
1306
+ if (lastUserIdx < 0) return null;
1307
+ const raw = entries[lastUserIdx].content;
1308
+ const userText = typeof raw === "string" ? raw : "";
1309
+ const preserved = entries.slice(0, lastUserIdx).map((m) => ({ ...m }));
1310
+ this.log.compactInPlace(preserved);
1311
+ if (this.sessionName) {
1312
+ try {
1313
+ rewriteSession(this.sessionName, preserved);
1314
+ } catch {
1315
+ }
1316
+ }
1317
+ return userText;
1318
+ }
1286
1319
  async *step(userInput) {
1287
1320
  this._turn++;
1288
1321
  this.scratch.reset();
@@ -1296,9 +1329,17 @@ var CacheFirstLoop = class {
1296
1329
  yield {
1297
1330
  turn: this._turn,
1298
1331
  role: "warning",
1299
- content: `aborted at iter ${iter}/${this.maxToolIters} \u2014 forcing summary from what was gathered`
1332
+ content: `aborted at iter ${iter}/${this.maxToolIters} \u2014 stopped without producing a summary (press \u2191 + Enter or /retry to resume)`
1300
1333
  };
1301
- yield* this.forceSummaryAfterIterLimit({ reason: "aborted" });
1334
+ const stoppedMsg = "[aborted by user (Esc) \u2014 no summary produced. Ask again or /retry when ready; prior tool output is still in the log.]";
1335
+ this.appendAndPersist({ role: "assistant", content: stoppedMsg });
1336
+ yield {
1337
+ turn: this._turn,
1338
+ role: "assistant_final",
1339
+ content: stoppedMsg,
1340
+ forcedSummary: true
1341
+ };
1342
+ yield { turn: this._turn, role: "done", content: stoppedMsg };
1302
1343
  return;
1303
1344
  }
1304
1345
  if (!warnedForIterBudget && iter >= warnAt) {
@@ -1520,12 +1561,18 @@ var CacheFirstLoop = class {
1520
1561
  async *forceSummaryAfterIterLimit(opts = { reason: "budget" }) {
1521
1562
  try {
1522
1563
  const messages = this.buildMessages(null);
1564
+ messages.push({
1565
+ role: "user",
1566
+ content: "I'm out of tool-call budget for this turn. Summarize in plain prose what you learned from the tool results above. Do NOT emit any tool calls, function-call markup, DSML invocations, or SEARCH/REPLACE edit blocks \u2014 they will be silently discarded. Just plain text."
1567
+ });
1523
1568
  const resp = await this.client.chat({
1524
1569
  model: this.model,
1525
1570
  messages
1526
1571
  // no tools → model is forced to answer in text
1527
1572
  });
1528
- const summary = resp.content?.trim() || "(model returned no text; try a narrower question or raise --max-tool-iters)";
1573
+ const rawContent = resp.content?.trim() ?? "";
1574
+ const cleaned = stripHallucinatedToolMarkup(rawContent);
1575
+ const summary = cleaned || "(model emitted fake tool-call markup instead of a prose summary \u2014 try /retry with a narrower question, or /think to inspect R1's reasoning)";
1529
1576
  const reasonPrefix = reasonPrefixFor(opts.reason, this.maxToolIters);
1530
1577
  const annotated = `${reasonPrefix}
1531
1578
 
@@ -1566,6 +1613,14 @@ ${summary}`;
1566
1613
  return msg;
1567
1614
  }
1568
1615
  };
1616
+ function stripHallucinatedToolMarkup(s) {
1617
+ let out = s;
1618
+ out = out.replace(/<|DSML|function_calls>[\s\S]*?<\/?|DSML|function_calls>/g, "");
1619
+ out = out.replace(/<\|DSML\|function_calls>[\s\S]*?<\/?\|DSML\|function_calls>/g, "");
1620
+ out = out.replace(/<function_calls>[\s\S]*?<\/function_calls>/g, "");
1621
+ out = out.replace(/<|DSML|[\s\S]*$/g, "");
1622
+ return out.trim();
1623
+ }
1569
1624
  function reasonPrefixFor(reason, iterCap) {
1570
1625
  if (reason === "aborted") return "[aborted by user (Esc) \u2014 summarizing what I found so far]";
1571
1626
  if (reason === "context-guard") {
@@ -2729,7 +2784,7 @@ function sep() {
2729
2784
  }
2730
2785
 
2731
2786
  // src/index.ts
2732
- var VERSION = "0.4.1";
2787
+ var VERSION = "0.4.3";
2733
2788
 
2734
2789
  // src/cli/commands/chat.tsx
2735
2790
  import { render } from "ink";
@@ -2998,7 +3053,10 @@ var EventRow = React3.memo(function EventRow2({ event }) {
2998
3053
  return /* @__PURE__ */ React3.createElement(Box3, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React3.createElement(Box3, null, /* @__PURE__ */ React3.createElement(Text3, { bold: true, color: "green" }, "assistant")), event.branch ? /* @__PURE__ */ React3.createElement(BranchBlock, { branch: event.branch }) : null, event.reasoning ? /* @__PURE__ */ React3.createElement(ReasoningBlock, { reasoning: event.reasoning }) : null, !isPlanStateEmpty(event.planState) ? /* @__PURE__ */ React3.createElement(PlanStateBlock, { planState: event.planState }) : null, event.text ? /* @__PURE__ */ React3.createElement(Markdown, { text: event.text }) : /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, "(no content)"), event.stats ? /* @__PURE__ */ React3.createElement(StatsLine, { stats: event.stats }) : null, event.repair ? /* @__PURE__ */ React3.createElement(Text3, { color: "magenta" }, event.repair) : null);
2999
3054
  }
3000
3055
  if (event.role === "tool") {
3001
- return /* @__PURE__ */ React3.createElement(Box3, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React3.createElement(Text3, { color: "yellow" }, `tool<${event.toolName ?? "?"}> \u2192`), /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, " ", truncate2(event.text, 400)));
3056
+ const isError = event.text.startsWith("ERROR:");
3057
+ const color = isError ? "red" : "yellow";
3058
+ const marker = isError ? "\u2717" : "\u2192";
3059
+ return /* @__PURE__ */ React3.createElement(Box3, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React3.createElement(Text3, { color }, `tool<${event.toolName ?? "?"}> ${marker}`), /* @__PURE__ */ React3.createElement(Text3, { color: isError ? "red" : void 0, dimColor: !isError }, " ", truncate2(event.text, 400)));
3002
3060
  }
3003
3061
  if (event.role === "error") {
3004
3062
  return /* @__PURE__ */ React3.createElement(Box3, { marginTop: 1 }, /* @__PURE__ */ React3.createElement(Text3, { color: "red", bold: true }, "error", " "), /* @__PURE__ */ React3.createElement(Text3, { color: "red" }, event.text));
@@ -3020,9 +3078,9 @@ function BranchBlock({ branch }) {
3020
3078
  return /* @__PURE__ */ React3.createElement(Box3, null, /* @__PURE__ */ React3.createElement(Text3, { color: "blue" }, "\u{1F500} branched ", /* @__PURE__ */ React3.createElement(Text3, { bold: true }, branch.budget), ` samples \u2192 picked #${branch.chosenIndex} `, /* @__PURE__ */ React3.createElement(Text3, { dimColor: true }, per)));
3021
3079
  }
3022
3080
  function ReasoningBlock({ reasoning }) {
3023
- const max = 220;
3081
+ const max = 260;
3024
3082
  const flat = reasoning.replace(/\s+/g, " ").trim();
3025
- const preview = flat.length <= max ? flat : `${flat.slice(0, max)}\u2026 (+${flat.length - max} chars)`;
3083
+ const preview = flat.length <= max ? flat : `\u2026 (+${flat.length - max} earlier chars) ${flat.slice(-max)}`;
3026
3084
  return /* @__PURE__ */ React3.createElement(Box3, { marginBottom: 1 }, /* @__PURE__ */ React3.createElement(Text3, { dimColor: true, italic: true }, "\u21B3 thinking: ", preview));
3027
3085
  }
3028
3086
  function Elapsed() {
@@ -3150,6 +3208,8 @@ function handleSlash(cmd, args, loop, ctx = {}) {
3150
3208
  " /mcp list MCP servers + tools attached to this session",
3151
3209
  " /setup (exit + reconfigure) \u2192 run `reasonix setup`",
3152
3210
  " /compact [cap] shrink large tool results in history (default 4k/result)",
3211
+ " /think dump the most recent turn's full R1 reasoning (reasoner only)",
3212
+ " /retry truncate & resend your last message (fresh sample from the model)",
3153
3213
  " /apply (code mode) commit the pending edit blocks to disk",
3154
3214
  " /discard (code mode) drop pending edits without writing",
3155
3215
  " /undo (code mode) roll back the last applied edit batch",
@@ -3195,6 +3255,31 @@ function handleSlash(cmd, args, loop, ctx = {}) {
3195
3255
  return {
3196
3256
  info: "To reconfigure (preset, MCP servers, API key), exit this chat and run `reasonix setup`. Changes take effect on next launch."
3197
3257
  };
3258
+ case "retry": {
3259
+ const prev = loop.retryLastUser();
3260
+ if (!prev) {
3261
+ return {
3262
+ info: "nothing to retry \u2014 no prior user message in this session's log."
3263
+ };
3264
+ }
3265
+ const preview = prev.length > 80 ? `${prev.slice(0, 80)}\u2026` : prev;
3266
+ return {
3267
+ info: `\u25B8 retrying: "${preview}"`,
3268
+ resubmit: prev
3269
+ };
3270
+ }
3271
+ case "think":
3272
+ case "reasoning": {
3273
+ const raw = loop.scratch.reasoning;
3274
+ if (!raw || !raw.trim()) {
3275
+ return {
3276
+ info: "no reasoning cached. `/think` shows the full R1 thought for the most recent turn \u2014 only `deepseek-reasoner` produces it, and only once the turn completes."
3277
+ };
3278
+ }
3279
+ return { info: `\u21B3 full thinking (${raw.length} chars):
3280
+
3281
+ ${raw.trim()}` };
3282
+ }
3198
3283
  case "undo": {
3199
3284
  if (!ctx.codeUndo) {
3200
3285
  return {
@@ -3279,9 +3364,25 @@ function handleSlash(cmd, args, loop, ctx = {}) {
3279
3364
  }
3280
3365
  case "status": {
3281
3366
  const branchBudget = loop.branchOptions.budget ?? 1;
3282
- return {
3283
- info: `model=${loop.model} harvest=${loop.harvestEnabled ? "on" : "off"} branch=${branchBudget > 1 ? branchBudget : "off"} stream=${loop.stream ? "on" : "off"}`
3284
- };
3367
+ const ctxMax = DEEPSEEK_CONTEXT_TOKENS[loop.model] ?? DEFAULT_CONTEXT_TOKENS;
3368
+ const lastPromptTokens = loop.stats.summary().lastPromptTokens;
3369
+ const ctxPct = ctxMax > 0 ? Math.round(lastPromptTokens / ctxMax * 100) : 0;
3370
+ const ctxLine = lastPromptTokens > 0 ? ` ctx ${compactNum(lastPromptTokens)}/${compactNum(ctxMax)} (${ctxPct}%)` : " ctx no turns yet";
3371
+ const pending = ctx.pendingEditCount ?? 0;
3372
+ const sessionLine = loop.sessionName ? ` session "${loop.sessionName}" \xB7 ${loop.log.length} messages in log (resumed ${loop.resumedMessageCount})` : " session (ephemeral \u2014 no persistence)";
3373
+ const mcpCount = ctx.mcpSpecs?.length ?? 0;
3374
+ const toolCount = loop.prefix.toolSpecs.length;
3375
+ const mcpLine = ` mcp ${mcpCount} server(s), ${toolCount} tool(s) in registry`;
3376
+ const pendingLine = pending > 0 ? ` edits ${pending} pending (/apply to commit, /discard to drop)` : "";
3377
+ const lines = [
3378
+ ` model ${loop.model}`,
3379
+ ` flags harvest=${loop.harvestEnabled ? "on" : "off"} \xB7 branch=${branchBudget > 1 ? branchBudget : "off"} \xB7 stream=${loop.stream ? "on" : "off"}`,
3380
+ ctxLine,
3381
+ mcpLine,
3382
+ sessionLine
3383
+ ];
3384
+ if (pendingLine) lines.push(pendingLine);
3385
+ return { info: lines.join("\n") };
3285
3386
  }
3286
3387
  case "model": {
3287
3388
  const id = args[0];
@@ -3333,6 +3434,11 @@ function handleSlash(cmd, args, loop, ctx = {}) {
3333
3434
  return { unknown: true, info: `unknown command: /${cmd} (try /help)` };
3334
3435
  }
3335
3436
  }
3437
+ function compactNum(n) {
3438
+ if (n < 1e3) return String(n);
3439
+ const k = n / 1e3;
3440
+ return k >= 100 ? `${Math.round(k)}k` : `${k.toFixed(1)}k`;
3441
+ }
3336
3442
  function stripOuterQuotes(s) {
3337
3443
  if (s.length >= 2 && s.startsWith('"') && s.endsWith('"')) {
3338
3444
  return s.slice(1, -1);
@@ -3388,6 +3494,8 @@ function App({
3388
3494
  const [ongoingTool, setOngoingTool] = useState2(null);
3389
3495
  const lastEditSnapshots = useRef(null);
3390
3496
  const pendingEdits = useRef([]);
3497
+ const promptHistory = useRef([]);
3498
+ const historyCursor = useRef(-1);
3391
3499
  const [summary, setSummary] = useState2({
3392
3500
  turns: 0,
3393
3501
  totalCostUsd: 0,
@@ -3456,11 +3564,28 @@ function App({
3456
3564
  }
3457
3565
  }, [session, loop]);
3458
3566
  useInput((_input, key) => {
3459
- if (!key.escape) return;
3460
- if (!busy) return;
3461
- if (abortedThisTurn.current) return;
3462
- abortedThisTurn.current = true;
3463
- loop.abort();
3567
+ if (key.escape && busy) {
3568
+ if (abortedThisTurn.current) return;
3569
+ abortedThisTurn.current = true;
3570
+ loop.abort();
3571
+ return;
3572
+ }
3573
+ if (busy) return;
3574
+ const hist = promptHistory.current;
3575
+ if (key.upArrow) {
3576
+ if (hist.length === 0) return;
3577
+ const nextCursor = Math.min(historyCursor.current + 1, hist.length - 1);
3578
+ historyCursor.current = nextCursor;
3579
+ setInput(hist[hist.length - 1 - nextCursor] ?? "");
3580
+ return;
3581
+ }
3582
+ if (key.downArrow) {
3583
+ if (historyCursor.current < 0) return;
3584
+ const nextCursor = historyCursor.current - 1;
3585
+ historyCursor.current = nextCursor;
3586
+ setInput(nextCursor < 0 ? "" : hist[hist.length - 1 - nextCursor] ?? "");
3587
+ return;
3588
+ }
3464
3589
  });
3465
3590
  const codeUndo = useCallback(() => {
3466
3591
  if (!codeMode) return "not in code mode";
@@ -3502,9 +3627,16 @@ function App({
3502
3627
  );
3503
3628
  const handleSubmit = useCallback(
3504
3629
  async (raw) => {
3505
- const text = raw.trim();
3630
+ let text = raw.trim();
3506
3631
  if (!text || busy) return;
3507
3632
  setInput("");
3633
+ historyCursor.current = -1;
3634
+ if (codeMode && pendingEdits.current.length > 0 && (text === "y" || text === "n")) {
3635
+ const out = text === "y" ? codeApply() : codeDiscard();
3636
+ setHistorical((prev) => [...prev, { id: `sys-${Date.now()}`, role: "info", text: out }]);
3637
+ promptHistory.current.push(text);
3638
+ return;
3639
+ }
3508
3640
  const slash = parseSlash(text);
3509
3641
  if (slash) {
3510
3642
  const result = handleSlash(slash.cmd, slash.args, loop, {
@@ -3512,7 +3644,8 @@ function App({
3512
3644
  codeUndo: codeMode ? codeUndo : void 0,
3513
3645
  codeApply: codeMode ? codeApply : void 0,
3514
3646
  codeDiscard: codeMode ? codeDiscard : void 0,
3515
- codeRoot: codeMode?.rootDir
3647
+ codeRoot: codeMode?.rootDir,
3648
+ pendingEditCount: codeMode ? pendingEdits.current.length : void 0
3516
3649
  });
3517
3650
  if (result.exit) {
3518
3651
  transcriptRef.current?.end();
@@ -3533,8 +3666,14 @@ function App({
3533
3666
  }
3534
3667
  ]);
3535
3668
  }
3536
- return;
3669
+ if (result.resubmit) {
3670
+ text = result.resubmit;
3671
+ } else {
3672
+ promptHistory.current.push(text);
3673
+ return;
3674
+ }
3537
3675
  }
3676
+ promptHistory.current.push(text);
3538
3677
  setHistorical((prev) => [...prev, { id: `u-${Date.now()}`, role: "user", text }]);
3539
3678
  const assistantId = `a-${Date.now()}`;
3540
3679
  const streamRef = { id: assistantId, text: "", reasoning: "" };
@@ -3664,16 +3803,62 @@ function App({
3664
3803
  }
3665
3804
  function OngoingToolRow({ tool }) {
3666
3805
  const [tick, setTick] = useState2(0);
3806
+ const [elapsed, setElapsed] = useState2(0);
3667
3807
  useEffect2(() => {
3668
- const id = setInterval(() => setTick((t) => t + 1), 120);
3669
- return () => clearInterval(id);
3808
+ const start = Date.now();
3809
+ const frameId = setInterval(() => setTick((t) => t + 1), 120);
3810
+ const secId = setInterval(() => setElapsed(Math.floor((Date.now() - start) / 1e3)), 1e3);
3811
+ return () => {
3812
+ clearInterval(frameId);
3813
+ clearInterval(secId);
3814
+ };
3670
3815
  }, []);
3671
3816
  const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
3672
- const argsPreview = tool.args && tool.args.length > 0 && tool.args !== "{}" ? ` ${tool.args.length > 60 ? `${tool.args.slice(0, 60)}\u2026` : tool.args}` : "";
3673
- return /* @__PURE__ */ React6.createElement(Box6, { marginY: 1 }, /* @__PURE__ */ React6.createElement(Text6, { color: "cyan" }, frames[tick % frames.length]), /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, ` tool<${tool.name}> running\u2026`), argsPreview ? /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, argsPreview) : null);
3817
+ const summary = summarizeToolArgs(tool.name, tool.args);
3818
+ return /* @__PURE__ */ React6.createElement(Box6, { marginY: 1, flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "cyan" }, frames[tick % frames.length]), /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, ` tool<${tool.name}> running\u2026`), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, ` ${elapsed}s`)), summary ? /* @__PURE__ */ React6.createElement(Box6, { paddingLeft: 2 }, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, summary)) : null);
3819
+ }
3820
+ function summarizeToolArgs(name, args) {
3821
+ if (!args || args === "{}") return "";
3822
+ let parsed;
3823
+ try {
3824
+ parsed = JSON.parse(args);
3825
+ } catch {
3826
+ return args.length > 80 ? `${args.slice(0, 80)}\u2026` : args;
3827
+ }
3828
+ const hasSuffix = (s) => name === s || name.endsWith(`_${s}`);
3829
+ const path = typeof parsed.path === "string" ? parsed.path : void 0;
3830
+ if (hasSuffix("read_file")) {
3831
+ const head = typeof parsed.head === "number" ? `, head=${parsed.head}` : "";
3832
+ const tail = typeof parsed.tail === "number" ? `, tail=${parsed.tail}` : "";
3833
+ return `path: ${path ?? "?"}${head}${tail}`;
3834
+ }
3835
+ if (hasSuffix("write_file")) {
3836
+ const content = typeof parsed.content === "string" ? parsed.content : "";
3837
+ return `path: ${path ?? "?"} (${content.length} chars)`;
3838
+ }
3839
+ if (hasSuffix("edit_file")) {
3840
+ const edits = Array.isArray(parsed.edits) ? parsed.edits.length : 0;
3841
+ return `path: ${path ?? "?"} (${edits} edit${edits === 1 ? "" : "s"})`;
3842
+ }
3843
+ if (hasSuffix("list_directory") || hasSuffix("directory_tree")) {
3844
+ return `path: ${path ?? "?"}`;
3845
+ }
3846
+ if (hasSuffix("search_files")) {
3847
+ const pattern = typeof parsed.pattern === "string" ? parsed.pattern : "?";
3848
+ return `path: ${path ?? "?"} \xB7 pattern: ${pattern}`;
3849
+ }
3850
+ if (hasSuffix("move_file")) {
3851
+ const src = typeof parsed.source === "string" ? parsed.source : "?";
3852
+ const dst = typeof parsed.destination === "string" ? parsed.destination : "?";
3853
+ return `${src} \u2192 ${dst}`;
3854
+ }
3855
+ if (hasSuffix("get_file_info")) {
3856
+ return `path: ${path ?? "?"}`;
3857
+ }
3858
+ return args.length > 80 ? `${args.slice(0, 80)}\u2026` : args;
3674
3859
  }
3675
3860
  function CommandStrip({ codeMode }) {
3676
- return /* @__PURE__ */ React6.createElement(Box6, { paddingX: 2, flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "/help \xB7 /preset ", "<fast|smart|max>", " \xB7 /mcp \xB7 /compact \xB7 /sessions \xB7 /setup \xB7 /clear \xB7 /exit"), codeMode ? /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, 'code mode: /apply \xB7 /discard \xB7 /undo \xB7 /commit "msg" \u2014 edits NEVER write without /apply') : null, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "Esc (while thinking) \u2014 abort & summarize what was found so far"));
3861
+ return /* @__PURE__ */ React6.createElement(Box6, { paddingX: 2, flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "/help \xB7 /preset ", "<fast|smart|max>", " \xB7 /mcp \xB7 /compact \xB7 /sessions \xB7 /setup \xB7 /clear \xB7 /exit"), codeMode ? /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, 'code mode: /apply (y) \xB7 /discard (n) \xB7 /undo \xB7 /commit "msg" \u2014 edits NEVER write without /apply') : null, /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "\u2191/\u2193 recall prompts \xB7 /retry re-send last \xB7 /think see R1 full reasoning"), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, "Esc (while thinking) \u2014 abort & summarize what was found so far"));
3677
3862
  }
3678
3863
  function formatEditResults(results) {
3679
3864
  const lines = results.map((r) => {
@@ -3693,7 +3878,7 @@ function formatPendingPreview(blocks) {
3693
3878
  const tag = b.search === "" ? "NEW " : " ";
3694
3879
  return ` ${tag}${b.path} (-${removed} +${added} lines)`;
3695
3880
  });
3696
- const header = `\u25B8 ${blocks.length} pending edit block(s) \u2014 /apply to commit to disk, /discard to drop`;
3881
+ const header = `\u25B8 ${blocks.length} pending edit block(s) \u2014 /apply (or y) to commit \xB7 /discard (or n) to drop`;
3697
3882
  return [header, ...lines].join("\n");
3698
3883
  }
3699
3884
  function countLines2(s) {