reasonix 0.35.0 → 0.36.1

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.
Files changed (82) hide show
  1. package/README.md +17 -0
  2. package/README.zh-CN.md +17 -0
  3. package/dashboard/dist/app.js +42 -3
  4. package/dashboard/dist/app.js.map +1 -1
  5. package/dist/cli/{chat-AB5D7I3V.js → chat-7AF5SPAJ.js} +16 -15
  6. package/dist/cli/{chunk-RJ5GUVS2.js → chunk-2MCYGFLK.js} +10 -10
  7. package/dist/cli/chunk-2MCYGFLK.js.map +1 -0
  8. package/dist/cli/{chunk-GPHBJWCV.js → chunk-3OBWN2NH.js} +650 -493
  9. package/dist/cli/chunk-3OBWN2NH.js.map +1 -0
  10. package/dist/cli/{chunk-SW3CCXEV.js → chunk-4Q3GRJIU.js} +2 -2
  11. package/dist/cli/{chunk-5JXXEPDM.js → chunk-BHLHOS5Y.js} +8 -2
  12. package/dist/cli/chunk-BHLHOS5Y.js.map +1 -0
  13. package/dist/cli/{chunk-IDP65VCC.js → chunk-BJ376EN3.js} +9 -8
  14. package/dist/cli/chunk-BJ376EN3.js.map +1 -0
  15. package/dist/cli/{chunk-2AWTGJ2C.js → chunk-CRPQUBP6.js} +26 -9
  16. package/dist/cli/{chunk-2AWTGJ2C.js.map → chunk-CRPQUBP6.js.map} +1 -1
  17. package/dist/cli/{chunk-JJTOZPM3.js → chunk-IPCPEZWQ.js} +2 -2
  18. package/dist/cli/{chunk-SN7YH6FC.js → chunk-MLXUGPJE.js} +168 -35
  19. package/dist/cli/chunk-MLXUGPJE.js.map +1 -0
  20. package/dist/cli/{chunk-SX6L4HZZ.js → chunk-QPNZWUZF.js} +53 -6
  21. package/dist/cli/chunk-QPNZWUZF.js.map +1 -0
  22. package/dist/cli/{chunk-N2IC4XDL.js → chunk-QRUQ2BFT.js} +13 -8
  23. package/dist/cli/{chunk-N2IC4XDL.js.map → chunk-QRUQ2BFT.js.map} +1 -1
  24. package/dist/cli/{chunk-KZHMKOJH.js → chunk-T52GAWPP.js} +25 -3
  25. package/dist/cli/chunk-T52GAWPP.js.map +1 -0
  26. package/dist/cli/{chunk-I6YIAK6C.js → chunk-UNMYFZPZ.js} +2 -2
  27. package/dist/cli/{update-4TJWRUIN.js → chunk-WJ3YX4PZ.js} +51 -12
  28. package/dist/cli/chunk-WJ3YX4PZ.js.map +1 -0
  29. package/dist/cli/{chunk-RXGEGA7K.js → chunk-XQIFIB3U.js} +18 -7
  30. package/dist/cli/{chunk-RXGEGA7K.js.map → chunk-XQIFIB3U.js.map} +1 -1
  31. package/dist/cli/{chunk-2EBODRRO.js → chunk-ZJR4QLXB.js} +5 -1
  32. package/dist/cli/{chunk-2EBODRRO.js.map → chunk-ZJR4QLXB.js.map} +1 -1
  33. package/dist/cli/{code-XBEFHXVM.js → code-SWI4EBME.js} +19 -17
  34. package/dist/cli/code-SWI4EBME.js.map +1 -0
  35. package/dist/cli/{commands-MEZPSEHV.js → commands-FE2UDFBC.js} +3 -3
  36. package/dist/cli/{commit-CE4EFTUQ.js → commit-3IAGB22T.js} +5 -4
  37. package/dist/cli/commit-3IAGB22T.js.map +1 -0
  38. package/dist/cli/{doctor-A565GMWD.js → doctor-DKD34EFD.js} +7 -7
  39. package/dist/cli/index.js +33 -31
  40. package/dist/cli/index.js.map +1 -1
  41. package/dist/cli/{mcp-LDFK5QJI.js → mcp-2RDEQST6.js} +2 -2
  42. package/dist/cli/{mcp-browse-FYHEITCM.js → mcp-browse-VM5GLRBQ.js} +2 -2
  43. package/dist/cli/{mcp-inspect-T2HBR22P.js → mcp-inspect-CWSVCZUQ.js} +3 -3
  44. package/dist/cli/{replay-P2WC5N5X.js → replay-D7RT2DR7.js} +2 -2
  45. package/dist/cli/{run-QBWJETS3.js → run-FK5UBIIM.js} +11 -10
  46. package/dist/cli/run-FK5UBIIM.js.map +1 -0
  47. package/dist/cli/{server-SMLVXIW4.js → server-W4XJK4GX.js} +16 -16
  48. package/dist/cli/{server-SMLVXIW4.js.map → server-W4XJK4GX.js.map} +1 -1
  49. package/dist/cli/{sessions-55RIZVWG.js → sessions-YZXWMIWW.js} +8 -8
  50. package/dist/cli/{setup-QXMONZ4P.js → setup-IIAJXHP4.js} +196 -130
  51. package/dist/cli/setup-IIAJXHP4.js.map +1 -0
  52. package/dist/cli/update-GUCWB4UN.js +13 -0
  53. package/dist/cli/update-GUCWB4UN.js.map +1 -0
  54. package/dist/cli/{version-Q2HA3AAC.js → version-DWD6RLIU.js} +10 -10
  55. package/dist/index.d.ts +14 -2
  56. package/dist/index.js +270 -47
  57. package/dist/index.js.map +1 -1
  58. package/package.json +1 -1
  59. package/dist/cli/chunk-5JXXEPDM.js.map +0 -1
  60. package/dist/cli/chunk-GPHBJWCV.js.map +0 -1
  61. package/dist/cli/chunk-IDP65VCC.js.map +0 -1
  62. package/dist/cli/chunk-KZHMKOJH.js.map +0 -1
  63. package/dist/cli/chunk-RJ5GUVS2.js.map +0 -1
  64. package/dist/cli/chunk-SN7YH6FC.js.map +0 -1
  65. package/dist/cli/chunk-SX6L4HZZ.js.map +0 -1
  66. package/dist/cli/code-XBEFHXVM.js.map +0 -1
  67. package/dist/cli/commit-CE4EFTUQ.js.map +0 -1
  68. package/dist/cli/run-QBWJETS3.js.map +0 -1
  69. package/dist/cli/setup-QXMONZ4P.js.map +0 -1
  70. package/dist/cli/update-4TJWRUIN.js.map +0 -1
  71. /package/dist/cli/{chat-AB5D7I3V.js.map → chat-7AF5SPAJ.js.map} +0 -0
  72. /package/dist/cli/{chunk-SW3CCXEV.js.map → chunk-4Q3GRJIU.js.map} +0 -0
  73. /package/dist/cli/{chunk-JJTOZPM3.js.map → chunk-IPCPEZWQ.js.map} +0 -0
  74. /package/dist/cli/{chunk-I6YIAK6C.js.map → chunk-UNMYFZPZ.js.map} +0 -0
  75. /package/dist/cli/{commands-MEZPSEHV.js.map → commands-FE2UDFBC.js.map} +0 -0
  76. /package/dist/cli/{doctor-A565GMWD.js.map → doctor-DKD34EFD.js.map} +0 -0
  77. /package/dist/cli/{mcp-LDFK5QJI.js.map → mcp-2RDEQST6.js.map} +0 -0
  78. /package/dist/cli/{mcp-browse-FYHEITCM.js.map → mcp-browse-VM5GLRBQ.js.map} +0 -0
  79. /package/dist/cli/{mcp-inspect-T2HBR22P.js.map → mcp-inspect-CWSVCZUQ.js.map} +0 -0
  80. /package/dist/cli/{replay-P2WC5N5X.js.map → replay-D7RT2DR7.js.map} +0 -0
  81. /package/dist/cli/{sessions-55RIZVWG.js.map → sessions-YZXWMIWW.js.map} +0 -0
  82. /package/dist/cli/{version-Q2HA3AAC.js.map → version-DWD6RLIU.js.map} +0 -0
@@ -34,7 +34,7 @@ import {
34
34
  toWholeFileEditBlock,
35
35
  walkFilesStream,
36
36
  webFetch
37
- } from "./chunk-N2IC4XDL.js";
37
+ } from "./chunk-QRUQ2BFT.js";
38
38
  import {
39
39
  McpClient,
40
40
  SseTransport,
@@ -42,7 +42,7 @@ import {
42
42
  StreamableHttpTransport,
43
43
  inspectMcpServer,
44
44
  parseMcpSpec
45
- } from "./chunk-I6YIAK6C.js";
45
+ } from "./chunk-UNMYFZPZ.js";
46
46
  import {
47
47
  openTranscriptFile,
48
48
  recordFromLoopEvent,
@@ -55,7 +55,7 @@ import {
55
55
  KeystrokeProvider,
56
56
  SingleSelect,
57
57
  useKeystroke
58
- } from "./chunk-KZHMKOJH.js";
58
+ } from "./chunk-T52GAWPP.js";
59
59
  import {
60
60
  COLOR,
61
61
  GLYPH,
@@ -63,7 +63,7 @@ import {
63
63
  ThemeProvider,
64
64
  useColor,
65
65
  useThemeTokens
66
- } from "./chunk-2EBODRRO.js";
66
+ } from "./chunk-ZJR4QLXB.js";
67
67
  import {
68
68
  PRESETS,
69
69
  PRESET_DESCRIPTIONS,
@@ -71,7 +71,7 @@ import {
71
71
  } from "./chunk-MHDNZXJJ.js";
72
72
  import {
73
73
  runDoctorChecks
74
- } from "./chunk-IDP65VCC.js";
74
+ } from "./chunk-BJ376EN3.js";
75
75
  import {
76
76
  countTokens
77
77
  } from "./chunk-DAEAAVDF.js";
@@ -84,6 +84,10 @@ import {
84
84
  import {
85
85
  renderDashboard
86
86
  } from "./chunk-4DCHFFEY.js";
87
+ import {
88
+ MANUAL_UPDATE_COMMANDS,
89
+ planUpdate
90
+ } from "./chunk-WJ3YX4PZ.js";
87
91
  import {
88
92
  SLASH_COMMANDS,
89
93
  archivePlanState,
@@ -97,23 +101,23 @@ import {
97
101
  resolveSlashAlias,
98
102
  savePlanState,
99
103
  suggestSlashCommands
100
- } from "./chunk-RJ5GUVS2.js";
101
- import {
102
- eventLogPath,
103
- openEventSink
104
- } from "./chunk-G3XNWSFN.js";
104
+ } from "./chunk-2MCYGFLK.js";
105
105
  import {
106
106
  fetchSmitheryDetail,
107
107
  loadMorePages,
108
108
  openRegistry,
109
109
  specStringFor
110
110
  } from "./chunk-SOZE7V7V.js";
111
+ import {
112
+ eventLogPath,
113
+ openEventSink
114
+ } from "./chunk-G3XNWSFN.js";
111
115
  import {
112
116
  BUILTIN_ALLOWLIST,
113
117
  formatCommandResult,
114
118
  pauseGate,
115
119
  runCommand
116
- } from "./chunk-SX6L4HZZ.js";
120
+ } from "./chunk-QPNZWUZF.js";
117
121
  import {
118
122
  PROJECT_MEMORY_FILE,
119
123
  SkillStore,
@@ -127,13 +131,7 @@ import {
127
131
  loadHooks,
128
132
  projectSettingsPath,
129
133
  runHooks
130
- } from "./chunk-JJTOZPM3.js";
131
- import {
132
- VERSION,
133
- compareVersions,
134
- getLatestVersion,
135
- isNpxInstall
136
- } from "./chunk-2AWTGJ2C.js";
134
+ } from "./chunk-IPCPEZWQ.js";
137
135
  import {
138
136
  deleteSession,
139
137
  detectGitBranch,
@@ -154,7 +152,7 @@ import {
154
152
  setLanguage,
155
153
  t,
156
154
  tObj
157
- } from "./chunk-SN7YH6FC.js";
155
+ } from "./chunk-MLXUGPJE.js";
158
156
  import {
159
157
  addProjectShellAllowed,
160
158
  clearProjectShellAllowed,
@@ -162,6 +160,7 @@ import {
162
160
  editModeHintShown,
163
161
  isPlausibleKey,
164
162
  loadApiKey,
163
+ loadBaseUrl,
165
164
  loadEditMode,
166
165
  loadProjectShellAllowed,
167
166
  loadReasoningEffort,
@@ -181,7 +180,7 @@ import {
181
180
  webSearchEndpoint,
182
181
  webSearchEngine,
183
182
  writeConfig
184
- } from "./chunk-5JXXEPDM.js";
183
+ } from "./chunk-BHLHOS5Y.js";
185
184
  import {
186
185
  CARD,
187
186
  FG,
@@ -205,6 +204,13 @@ import {
205
204
  DEEPSEEK_PRICING,
206
205
  DEFAULT_CONTEXT_TOKENS
207
206
  } from "./chunk-ORM6PK57.js";
207
+ import {
208
+ VERSION,
209
+ compareVersions,
210
+ detectInstallSource,
211
+ detectNpmInstallPrefix,
212
+ getLatestVersion
213
+ } from "./chunk-CRPQUBP6.js";
208
214
 
209
215
  // src/cli/commands/chat.tsx
210
216
  import { render } from "ink";
@@ -231,7 +237,7 @@ function buildMcpServerSummary(opts) {
231
237
  // src/cli/ui/App.tsx
232
238
  import { statSync } from "fs";
233
239
  import { resolve as resolve2 } from "path";
234
- import { Box as Box49, Text as Text52, useStdout as useStdout14 } from "ink";
240
+ import { Box as Box49, Text as Text52, useStdout as useStdout15 } from "ink";
235
241
  import React60, { useCallback as useCallback11, useEffect as useEffect12, useMemo as useMemo9, useRef as useRef9, useState as useState20 } from "react";
236
242
 
237
243
  // src/code/checkpoints.ts
@@ -5322,9 +5328,17 @@ function processMultilineKey(value, cursor, keyIn) {
5322
5328
  return cursor === value.length ? NOOP : { next: null, cursor: value.length, submit: false };
5323
5329
  }
5324
5330
  if (key.ctrl && key.input === "p") {
5331
+ if (value.includes("\n")) {
5332
+ const moved = moveCursorUp(value, cursor);
5333
+ if (moved !== cursor) return { next: null, cursor: moved, submit: false };
5334
+ }
5325
5335
  return { ...NOOP, historyHandoff: "prev" };
5326
5336
  }
5327
5337
  if (key.ctrl && key.input === "n") {
5338
+ if (value.includes("\n")) {
5339
+ const moved = moveCursorDown(value, cursor);
5340
+ if (moved !== cursor) return { next: null, cursor: moved, submit: false };
5341
+ }
5328
5342
  return { ...NOOP, historyHandoff: "next" };
5329
5343
  }
5330
5344
  if (key.leftArrow) {
@@ -5333,15 +5347,8 @@ function processMultilineKey(value, cursor, keyIn) {
5333
5347
  if (key.rightArrow) {
5334
5348
  return { next: null, cursor: Math.min(value.length, cursor + 1), submit: false };
5335
5349
  }
5336
- if (key.upArrow) {
5337
- if (value.length === 0) return { ...NOOP, historyHandoff: "prev" };
5338
- const moved = moveCursorUp(value, cursor);
5339
- return moved === cursor ? NOOP : { next: null, cursor: moved, submit: false };
5340
- }
5341
- if (key.downArrow) {
5342
- if (value.length === 0) return { ...NOOP, historyHandoff: "next" };
5343
- const moved = moveCursorDown(value, cursor);
5344
- return moved === cursor ? NOOP : { next: null, cursor: moved, submit: false };
5350
+ if (key.upArrow || key.downArrow) {
5351
+ return NOOP;
5345
5352
  }
5346
5353
  if (key.ctrl && key.input === "a" || key.home) {
5347
5354
  return { next: null, cursor: startOfLine(value, cursor), submit: false };
@@ -5907,7 +5914,8 @@ function HintRow() {
5907
5914
  const items = [
5908
5915
  { key: "\u23CE", sep: "send" },
5909
5916
  { key: "\u21E7\u23CE", sep: "newline" },
5910
- { key: "\u2191\u2193", sep: "history" },
5917
+ { key: "\u2191\u2193", sep: "scroll" },
5918
+ { key: "^P/^N", sep: "history" },
5911
5919
  { key: "esc", sep: "abort" },
5912
5920
  { key: "^C", sep: "quit" }
5913
5921
  ];
@@ -6423,8 +6431,12 @@ function ArgRow({ value, isSelected }) {
6423
6431
  }
6424
6432
 
6425
6433
  // src/cli/ui/SlashSuggestions.tsx
6426
- import { Box as Box23, Text as Text23 } from "ink";
6434
+ import { Box as Box23, Text as Text23, useStdout as useStdout8 } from "ink";
6427
6435
  import React27 from "react";
6436
+ var GROUP_MODE_MAX_ROWS = 24;
6437
+ var SEARCH_MODE_MAX_ROWS = 8;
6438
+ var COMMAND_NAME_CELLS = 14;
6439
+ var ARGS_CELLS = 14;
6428
6440
  var GROUP_LABEL = {
6429
6441
  chat: "CHAT",
6430
6442
  setup: "SETUP",
@@ -6442,26 +6454,82 @@ function SlashSuggestions({
6442
6454
  advancedHidden
6443
6455
  }) {
6444
6456
  const color = useColor();
6457
+ const { stdout } = useStdout8();
6458
+ const cols = stdout?.columns ?? 80;
6459
+ const [rememberedWindowStart, setRememberedWindowStart] = React27.useState(0);
6460
+ const maxRows = groupMode ? GROUP_MODE_MAX_ROWS : SEARCH_MODE_MAX_ROWS;
6461
+ const safeMatches = matches ?? [];
6462
+ const windowStart = computeWindowStart(
6463
+ safeMatches,
6464
+ maxRows,
6465
+ selectedIndex,
6466
+ rememberedWindowStart,
6467
+ groupMode
6468
+ );
6469
+ React27.useEffect(() => {
6470
+ setRememberedWindowStart(windowStart);
6471
+ }, [windowStart]);
6445
6472
  if (matches === null) return null;
6446
6473
  if (matches.length === 0) {
6447
6474
  return /* @__PURE__ */ React27.createElement(Box23, { paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React27.createElement(Text23, { color: color.warn, bold: true }, GLYPH.warn), /* @__PURE__ */ React27.createElement(Text23, null, " "), /* @__PURE__ */ React27.createElement(Text23, { color: color.warn }, "no slash command matches that prefix"), /* @__PURE__ */ React27.createElement(Text23, { dimColor: true }, " \u2014 Backspace to edit, or /help for the full list"));
6448
6475
  }
6449
- const MAX = groupMode ? 24 : 8;
6450
6476
  const total = matches.length;
6451
- const windowStart = total <= MAX ? 0 : Math.max(0, Math.min(selectedIndex - Math.floor(MAX / 2), total - MAX));
6452
- const shown = matches.slice(windowStart, windowStart + MAX);
6477
+ const items = buildVisibleItems(matches, windowStart, maxRows, groupMode);
6478
+ const shownCommands = items.filter((item) => item.kind === "command");
6453
6479
  const hiddenAbove = windowStart;
6454
- const hiddenBelow = total - windowStart - shown.length;
6455
- let lastGroup = null;
6456
- if (windowStart > 0) lastGroup = matches[windowStart - 1]?.group ?? null;
6457
- return /* @__PURE__ */ React27.createElement(Box23, { flexDirection: "column", paddingX: 1, marginTop: 1 }, /* @__PURE__ */ React27.createElement(Box23, null, /* @__PURE__ */ React27.createElement(Text23, { color: color.accent, bold: true }, "/ "), /* @__PURE__ */ React27.createElement(Text23, { dimColor: true }, `${total} command${total === 1 ? "" : "s"}`), hiddenAbove > 0 ? /* @__PURE__ */ React27.createElement(Text23, { dimColor: true }, ` \u2191 ${hiddenAbove} above`) : null), shown.map((spec, i) => {
6458
- const idx = windowStart + i;
6459
- const showHeader = groupMode && spec.group !== lastGroup;
6460
- lastGroup = spec.group;
6461
- return /* @__PURE__ */ React27.createElement(React27.Fragment, { key: spec.cmd }, showHeader ? /* @__PURE__ */ React27.createElement(Box23, { marginTop: idx === 0 ? 0 : 1 }, /* @__PURE__ */ React27.createElement(Text23, { dimColor: true }, ` ${GROUP_LABEL[spec.group]}`)) : null, /* @__PURE__ */ React27.createElement(SuggestionRow, { spec, isSelected: idx === selectedIndex }));
6480
+ const hiddenBelow = total - windowStart - shownCommands.length;
6481
+ return /* @__PURE__ */ React27.createElement(Box23, { flexDirection: "column", paddingX: 1, marginTop: 1, flexShrink: 0, flexWrap: "nowrap" }, /* @__PURE__ */ React27.createElement(Box23, null, /* @__PURE__ */ React27.createElement(Text23, { color: color.accent, bold: true }, "/ "), /* @__PURE__ */ React27.createElement(Text23, { dimColor: true }, `${total} command${total === 1 ? "" : "s"}`), hiddenAbove > 0 ? /* @__PURE__ */ React27.createElement(Text23, { dimColor: true }, ` \u2191 ${hiddenAbove} above`) : null), items.map((item) => {
6482
+ if (item.kind === "group") {
6483
+ return /* @__PURE__ */ React27.createElement(GroupHeader, { key: `group:${item.group}:${item.beforeIndex}`, group: item.group });
6484
+ }
6485
+ return /* @__PURE__ */ React27.createElement(
6486
+ SuggestionRow,
6487
+ {
6488
+ key: `cmd:${item.spec.group}:${item.spec.cmd}`,
6489
+ spec: item.spec,
6490
+ isSelected: item.index === selectedIndex,
6491
+ columns: cols
6492
+ }
6493
+ );
6462
6494
  }), hiddenBelow > 0 ? /* @__PURE__ */ React27.createElement(Text23, { dimColor: true }, ` \u2193 ${hiddenBelow} below`) : null, groupMode && advancedHidden && advancedHidden > 0 ? /* @__PURE__ */ React27.createElement(Box23, { marginTop: 1 }, /* @__PURE__ */ React27.createElement(Text23, { dimColor: true }, ` + ${advancedHidden} advanced \xB7 type a letter to search`)) : null, /* @__PURE__ */ React27.createElement(Box23, { marginTop: 0 }, /* @__PURE__ */ React27.createElement(Text23, { dimColor: true }, " \u2191\u2193 navigate \xB7 Tab / \u23CE pick \xB7 esc cancel")));
6463
6495
  }
6464
- function SuggestionRow({ spec, isSelected }) {
6496
+ function computeWindowStart(matches, maxRows, selectedIndex, currentWindowStart, groupMode = false) {
6497
+ if (matches.length <= 0) return 0;
6498
+ const maxWindowStart = Math.max(0, matches.length - 1);
6499
+ let start = Math.max(0, Math.min(currentWindowStart, maxWindowStart));
6500
+ const clampedSelectedIndex = Math.max(0, Math.min(selectedIndex, matches.length - 1));
6501
+ if (clampedSelectedIndex < start) start = clampedSelectedIndex;
6502
+ while (start < clampedSelectedIndex) {
6503
+ const visibleCommandIndexes = buildVisibleItems(matches, start, maxRows, groupMode).filter((item) => item.kind === "command").map((item) => item.index);
6504
+ if (visibleCommandIndexes.includes(clampedSelectedIndex)) break;
6505
+ start += 1;
6506
+ }
6507
+ return Math.min(start, maxWindowStart);
6508
+ }
6509
+ function buildVisibleItems(matches, windowStart, maxRows, groupMode = false) {
6510
+ const out = [];
6511
+ for (let idx = windowStart; idx < matches.length && out.length < maxRows; idx += 1) {
6512
+ const spec = matches[idx];
6513
+ if (groupMode && shouldShowGroupHeader(matches, idx)) {
6514
+ if (out.length >= maxRows) break;
6515
+ out.push({ kind: "group", group: spec.group, beforeIndex: idx });
6516
+ }
6517
+ if (out.length >= maxRows) break;
6518
+ out.push({ kind: "command", spec, index: idx });
6519
+ }
6520
+ return out;
6521
+ }
6522
+ function shouldShowGroupHeader(matches, idx) {
6523
+ return idx === 0 || matches[idx]?.group !== matches[idx - 1]?.group;
6524
+ }
6525
+ function GroupHeader({ group }) {
6526
+ return /* @__PURE__ */ React27.createElement(Box23, { flexShrink: 0, height: 1, flexWrap: "nowrap" }, /* @__PURE__ */ React27.createElement(Text23, { dimColor: true, wrap: "truncate" }, ` ${GROUP_LABEL[group]}`));
6527
+ }
6528
+ function SuggestionRow({
6529
+ spec,
6530
+ isSelected,
6531
+ columns
6532
+ }) {
6465
6533
  const color = useColor();
6466
6534
  const name = `/${spec.cmd}`;
6467
6535
  const argsSuffix = spec.argsHint ? spec.argsHint : "";
@@ -6469,7 +6537,19 @@ function SuggestionRow({ spec, isSelected }) {
6469
6537
  const translated = t(key);
6470
6538
  const summary = translated === key ? spec.summary : translated;
6471
6539
  const aliasHint = spec.aliases?.length ? ` \xB7 /${spec.aliases.join(" /")}` : "";
6472
- return /* @__PURE__ */ React27.createElement(Box23, null, /* @__PURE__ */ React27.createElement(Text23, { color: isSelected ? color.primary : color.info, bold: isSelected }, isSelected ? `${GLYPH.cur} ` : " "), /* @__PURE__ */ React27.createElement(Text23, { color: color.accent, bold: isSelected }, name.padEnd(14)), /* @__PURE__ */ React27.createElement(Text23, { dimColor: true }, argsSuffix.padEnd(14)), /* @__PURE__ */ React27.createElement(Text23, null, " "), /* @__PURE__ */ React27.createElement(Text23, { color: isSelected ? color.user : color.info, dimColor: !isSelected }, summary), aliasHint ? /* @__PURE__ */ React27.createElement(Text23, { dimColor: true }, aliasHint) : null);
6540
+ const reservedCells = 2 + COMMAND_NAME_CELLS + ARGS_CELLS + 2 + 2;
6541
+ const summaryBudget = Math.max(8, columns - reservedCells);
6542
+ const summaryText = truncateCells(`${summary}${aliasHint}`, summaryBudget);
6543
+ return /* @__PURE__ */ React27.createElement(Box23, { flexDirection: "row", flexWrap: "nowrap", flexShrink: 0, height: 1, minHeight: 1 }, /* @__PURE__ */ React27.createElement(Text23, { color: isSelected ? color.primary : color.info, bold: isSelected, wrap: "truncate" }, isSelected ? `${GLYPH.cur} ` : " "), /* @__PURE__ */ React27.createElement(Text23, { color: color.accent, bold: isSelected, wrap: "truncate" }, padOrTrim(name, COMMAND_NAME_CELLS)), /* @__PURE__ */ React27.createElement(Text23, { dimColor: true, wrap: "truncate" }, padOrTrim(argsSuffix, ARGS_CELLS)), /* @__PURE__ */ React27.createElement(Text23, { wrap: "truncate" }, " "), /* @__PURE__ */ React27.createElement(Text23, { color: isSelected ? color.user : color.info, dimColor: !isSelected, wrap: "truncate" }, summaryText));
6544
+ }
6545
+ function padOrTrim(value, cells) {
6546
+ const trimmed = truncateCells(value, cells);
6547
+ return trimmed.padEnd(cells);
6548
+ }
6549
+ function truncateCells(value, maxCells) {
6550
+ if (value.length <= maxCells) return value;
6551
+ if (maxCells <= 1) return value.slice(0, Math.max(0, maxCells));
6552
+ return `${value.slice(0, maxCells - 1)}\u2026`;
6473
6553
  }
6474
6554
 
6475
6555
  // src/cli/ui/WelcomeBanner.tsx
@@ -6814,6 +6894,10 @@ function handleToolStart(ev, ctx) {
6814
6894
  }
6815
6895
  }
6816
6896
  function handleErrorEvent(ev, ctx) {
6897
+ ctx.setOngoingTool(null);
6898
+ ctx.setToolProgress(null);
6899
+ ctx.toolStartedAtRef.current = null;
6900
+ ctx.translator.toolAbort(ev.error ?? ev.content);
6817
6901
  ctx.log.pushError("tool error", ev.error ?? ev.content);
6818
6902
  }
6819
6903
  function handleWarningEvent(ev, ctx) {
@@ -6855,272 +6939,6 @@ function handleToolEvent(ev, ctx) {
6855
6939
  }
6856
6940
  }
6857
6941
 
6858
- // src/cli/ui/hooks/useAgentSession.ts
6859
- import { useMemo as useMemo6 } from "react";
6860
- function useAgentSession({
6861
- sessionId,
6862
- model: model2,
6863
- workspace,
6864
- branch
6865
- }) {
6866
- return useMemo6(
6867
- () => ({
6868
- id: sessionId ?? "default",
6869
- branch: branch ?? "main",
6870
- workspace,
6871
- model: model2
6872
- }),
6873
- [sessionId, branch, workspace, model2]
6874
- );
6875
- }
6876
-
6877
- // src/cli/ui/hooks/useChatScroll.ts
6878
- import { useCallback as useCallback2, useEffect as useEffect4, useRef as useRef2, useState as useState14 } from "react";
6879
- var SCROLL_PAGE_ROWS = 3;
6880
- var COALESCE_MS = 16;
6881
- function useChatScroll() {
6882
- const [scrollRows, setScrollRows] = useState14(0);
6883
- const [pinned, setPinned] = useState14(true);
6884
- const [maxScroll, setMaxScrollState] = useState14(0);
6885
- const maxScrollRef = useRef2(0);
6886
- const pendingDelta = useRef2(0);
6887
- const flushTimer = useRef2(null);
6888
- const flush = useCallback2(() => {
6889
- flushTimer.current = null;
6890
- const d = pendingDelta.current;
6891
- pendingDelta.current = 0;
6892
- if (d === 0) return;
6893
- if (d < 0) setPinned(false);
6894
- setScrollRows((o) => {
6895
- const next = Math.max(0, Math.min(maxScrollRef.current, o + d));
6896
- if (next >= maxScrollRef.current) setPinned(true);
6897
- return next;
6898
- });
6899
- }, []);
6900
- const schedule = useCallback2(
6901
- (delta) => {
6902
- pendingDelta.current += delta;
6903
- if (flushTimer.current !== null) return;
6904
- flushTimer.current = setTimeout(flush, COALESCE_MS);
6905
- },
6906
- [flush]
6907
- );
6908
- useEffect4(() => {
6909
- return () => {
6910
- if (flushTimer.current !== null) {
6911
- clearTimeout(flushTimer.current);
6912
- flushTimer.current = null;
6913
- }
6914
- };
6915
- }, []);
6916
- useEffect4(() => {
6917
- if (pinned) setScrollRows(maxScroll);
6918
- }, [pinned, maxScroll]);
6919
- useEffect4(() => {
6920
- if (scrollRows > maxScroll) setScrollRows(maxScroll);
6921
- }, [scrollRows, maxScroll]);
6922
- const scrollUp = useCallback2(() => schedule(-SCROLL_PAGE_ROWS), [schedule]);
6923
- const scrollDown = useCallback2(() => schedule(SCROLL_PAGE_ROWS), [schedule]);
6924
- const jumpToBottom = useCallback2(() => {
6925
- pendingDelta.current = 0;
6926
- if (flushTimer.current !== null) {
6927
- clearTimeout(flushTimer.current);
6928
- flushTimer.current = null;
6929
- }
6930
- setPinned(true);
6931
- }, []);
6932
- const setMaxScroll = useCallback2((rows) => {
6933
- maxScrollRef.current = rows;
6934
- setMaxScrollState(rows);
6935
- }, []);
6936
- return { scrollRows, pinned, scrollUp, scrollDown, jumpToBottom, setMaxScroll };
6937
- }
6938
-
6939
- // src/cli/ui/hooks/useCodeMode.ts
6940
- import { useCallback as useCallback3 } from "react";
6941
- function useCodeMode(opts) {
6942
- const { codeMode, pendingEdits, currentRootDir, session, syncPendingCount, recordEdit } = opts;
6943
- const codeApply = useCallback3(
6944
- (indices) => {
6945
- if (!codeMode) return "not in code mode";
6946
- const blocks = pendingEdits.current;
6947
- if (blocks.length === 0) {
6948
- return "nothing pending \u2014 the model hasn't proposed edits since the last /apply or /discard.";
6949
- }
6950
- const useSubset = indices !== void 0 && indices.length > 0;
6951
- const { selected, remaining } = useSubset ? partitionEdits(blocks, indices) : { selected: blocks, remaining: [] };
6952
- if (selected.length === 0) {
6953
- return "\u25B8 no edits matched those indices \u2014 nothing applied. Use /apply with no args to commit them all.";
6954
- }
6955
- const snaps = snapshotBeforeEdits(selected, currentRootDir);
6956
- const results = applyEditBlocks(selected, currentRootDir);
6957
- const anyApplied = results.some((r) => r.status === "applied" || r.status === "created");
6958
- if (anyApplied) recordEdit("review-apply", selected, results, snaps);
6959
- pendingEdits.current = remaining;
6960
- if (remaining.length === 0) clearPendingEdits(session ?? null);
6961
- else savePendingEdits(session ?? null, remaining);
6962
- syncPendingCount();
6963
- const tail = remaining.length > 0 ? `
6964
- \u25B8 ${remaining.length} edit block(s) still pending \u2014 /apply or /discard to clear them.` : "";
6965
- return formatEditResults(results) + tail;
6966
- },
6967
- [codeMode, currentRootDir, session, syncPendingCount, recordEdit, pendingEdits]
6968
- );
6969
- const codeDiscard = useCallback3(
6970
- (indices) => {
6971
- const blocks = pendingEdits.current;
6972
- if (blocks.length === 0) return "nothing pending to discard.";
6973
- const useSubset = indices !== void 0 && indices.length > 0;
6974
- const { selected, remaining } = useSubset ? partitionEdits(blocks, indices) : { selected: blocks, remaining: [] };
6975
- if (selected.length === 0) {
6976
- return "\u25B8 no edits matched those indices \u2014 nothing discarded.";
6977
- }
6978
- pendingEdits.current = remaining;
6979
- if (remaining.length === 0) clearPendingEdits(session ?? null);
6980
- else savePendingEdits(session ?? null, remaining);
6981
- syncPendingCount();
6982
- const tail = remaining.length > 0 ? ` (${remaining.length} block(s) still pending)` : ". Nothing was written to disk.";
6983
- return `\u25B8 discarded ${selected.length} pending edit block(s)${tail}`;
6984
- },
6985
- [session, syncPendingCount, pendingEdits]
6986
- );
6987
- return { codeApply, codeDiscard };
6988
- }
6989
-
6990
- // src/cli/ui/hooks/useInputRecall.ts
6991
- import { useCallback as useCallback4, useRef as useRef3 } from "react";
6992
- function useInputRecall(setInput) {
6993
- const promptHistory = useRef3([]);
6994
- const historyCursor = useRef3(-1);
6995
- const recallPrev = useCallback4(() => {
6996
- const hist = promptHistory.current;
6997
- if (hist.length === 0) return;
6998
- const nextCursor = Math.min(historyCursor.current + 1, hist.length - 1);
6999
- historyCursor.current = nextCursor;
7000
- setInput(hist[hist.length - 1 - nextCursor] ?? "");
7001
- }, [setInput]);
7002
- const recallNext = useCallback4(() => {
7003
- if (historyCursor.current < 0) return;
7004
- const hist = promptHistory.current;
7005
- const nextCursor = historyCursor.current - 1;
7006
- historyCursor.current = nextCursor;
7007
- setInput(nextCursor < 0 ? "" : hist[hist.length - 1 - nextCursor] ?? "");
7008
- }, [setInput]);
7009
- const pushHistory = useCallback4((text) => {
7010
- promptHistory.current.push(text);
7011
- }, []);
7012
- const resetCursor = useCallback4(() => {
7013
- historyCursor.current = -1;
7014
- }, []);
7015
- return { recallPrev, recallNext, pushHistory, resetCursor };
7016
- }
7017
-
7018
- // src/cli/ui/hooks/useLoopMode.ts
7019
- import { useCallback as useCallback5, useEffect as useEffect5, useRef as useRef4, useState as useState15 } from "react";
7020
- function useLoopMode(opts) {
7021
- const { log, busyRef, handleSubmitRef } = opts;
7022
- const [activeLoop, setActiveLoop] = useState15(null);
7023
- const activeLoopRef = useRef4(null);
7024
- const loopTimerRef = useRef4(null);
7025
- const loopFiringRef = useRef4(false);
7026
- useEffect5(() => {
7027
- activeLoopRef.current = activeLoop;
7028
- }, [activeLoop]);
7029
- const stopLoop = useCallback5(() => {
7030
- if (loopTimerRef.current) {
7031
- clearTimeout(loopTimerRef.current);
7032
- loopTimerRef.current = null;
7033
- }
7034
- const cur = activeLoopRef.current;
7035
- if (!cur) return;
7036
- setActiveLoop(null);
7037
- log.pushInfo(`\u25B8 loop stopped (after ${cur.iter} iter${cur.iter === 1 ? "" : "s"}).`);
7038
- }, [log]);
7039
- const startLoop = useCallback5((intervalMs, prompt) => {
7040
- if (loopTimerRef.current) {
7041
- clearTimeout(loopTimerRef.current);
7042
- loopTimerRef.current = null;
7043
- }
7044
- setActiveLoop({
7045
- prompt,
7046
- intervalMs,
7047
- nextFireAt: Date.now() + intervalMs,
7048
- iter: 0
7049
- });
7050
- }, []);
7051
- const getLoopStatus = useCallback5(() => {
7052
- const cur = activeLoopRef.current;
7053
- if (!cur) return null;
7054
- return {
7055
- prompt: cur.prompt,
7056
- intervalMs: cur.intervalMs,
7057
- iter: cur.iter,
7058
- nextFireMs: Math.max(0, cur.nextFireAt - Date.now())
7059
- };
7060
- }, []);
7061
- const isLoopActive = useCallback5(() => activeLoopRef.current !== null, []);
7062
- const isLoopFiring = useCallback5(() => loopFiringRef.current, []);
7063
- const clearFiringFlag = useCallback5(() => {
7064
- loopFiringRef.current = false;
7065
- }, []);
7066
- useEffect5(() => {
7067
- if (!activeLoop) return;
7068
- const delay = Math.max(0, activeLoop.nextFireAt - Date.now());
7069
- const timer = setTimeout(async () => {
7070
- loopTimerRef.current = null;
7071
- if (busyRef.current) {
7072
- setActiveLoop((cur2) => cur2 ? { ...cur2, nextFireAt: Date.now() + 1e3 } : cur2);
7073
- return;
7074
- }
7075
- const cur = activeLoopRef.current;
7076
- if (!cur) return;
7077
- const nextIter = cur.iter + 1;
7078
- setActiveLoop(
7079
- (c) => c ? { ...c, iter: nextIter, nextFireAt: Date.now() + cur.intervalMs } : c
7080
- );
7081
- log.pushInfo(`\u25B8 /loop iter ${nextIter} \u2192 ${cur.prompt}`);
7082
- loopFiringRef.current = true;
7083
- try {
7084
- await handleSubmitRef.current?.(cur.prompt);
7085
- } catch {
7086
- stopLoop();
7087
- } finally {
7088
- loopFiringRef.current = false;
7089
- }
7090
- }, delay);
7091
- loopTimerRef.current = timer;
7092
- return () => clearTimeout(timer);
7093
- }, [activeLoop, stopLoop, log, busyRef, handleSubmitRef]);
7094
- return {
7095
- startLoop,
7096
- stopLoop,
7097
- getLoopStatus,
7098
- isLoopActive,
7099
- isLoopFiring,
7100
- clearFiringFlag,
7101
- activeLoop
7102
- };
7103
- }
7104
-
7105
- // src/cli/ui/hooks/useQuit.ts
7106
- import { useCallback as useCallback6, useEffect as useEffect6 } from "react";
7107
- function useQuit(transcriptRef) {
7108
- const quitProcess = useCallback6(() => {
7109
- transcriptRef.current?.end();
7110
- process.exit(0);
7111
- }, [transcriptRef]);
7112
- useEffect6(() => {
7113
- process.on("SIGINT", quitProcess);
7114
- return () => {
7115
- process.off("SIGINT", quitProcess);
7116
- };
7117
- }, [quitProcess]);
7118
- return quitProcess;
7119
- }
7120
-
7121
- // src/cli/ui/hooks/useScrollback.ts
7122
- import { useMemo as useMemo7 } from "react";
7123
-
7124
6942
  // src/cli/ui/state/provider.tsx
7125
6943
  import React29 from "react";
7126
6944
 
@@ -7450,68 +7268,343 @@ function initialState(session, cards = []) {
7450
7268
  sessionCost: 0,
7451
7269
  cacheHit: 0
7452
7270
  },
7453
- focusedCardId: null,
7454
- toasts: [],
7455
- turnInProgress: false
7456
- };
7271
+ focusedCardId: null,
7272
+ toasts: [],
7273
+ turnInProgress: false
7274
+ };
7275
+ }
7276
+
7277
+ // src/cli/ui/state/store.ts
7278
+ function createStore(session, initialCards) {
7279
+ let state = initialState(session, initialCards);
7280
+ const stateListeners = /* @__PURE__ */ new Set();
7281
+ const eventListeners = /* @__PURE__ */ new Set();
7282
+ return {
7283
+ getState() {
7284
+ return state;
7285
+ },
7286
+ dispatch(event) {
7287
+ state = reduce(state, event);
7288
+ for (const listener of stateListeners) listener();
7289
+ for (const listener of eventListeners) listener(event);
7290
+ },
7291
+ subscribe(listener) {
7292
+ stateListeners.add(listener);
7293
+ return () => {
7294
+ stateListeners.delete(listener);
7295
+ };
7296
+ },
7297
+ onEvent(listener) {
7298
+ eventListeners.add(listener);
7299
+ return () => {
7300
+ eventListeners.delete(listener);
7301
+ };
7302
+ }
7303
+ };
7304
+ }
7305
+
7306
+ // src/cli/ui/state/provider.tsx
7307
+ var StoreCtx = React29.createContext(null);
7308
+ function AgentStoreProvider({
7309
+ session,
7310
+ initialCards,
7311
+ children
7312
+ }) {
7313
+ const initialCardsRef = React29.useRef(initialCards);
7314
+ const store = React29.useMemo(() => createStore(session, initialCardsRef.current), [session]);
7315
+ return /* @__PURE__ */ React29.createElement(StoreCtx.Provider, { value: store }, children);
7316
+ }
7317
+ function useAgentStore() {
7318
+ const store = React29.useContext(StoreCtx);
7319
+ if (!store) throw new Error("useAgentStore must be used inside AgentStoreProvider");
7320
+ return store;
7321
+ }
7322
+ function useAgentState(selector) {
7323
+ const store = useAgentStore();
7324
+ const subscribe = React29.useCallback((cb) => store.subscribe(cb), [store]);
7325
+ const getSnapshot = React29.useCallback(() => selector(store.getState()), [store, selector]);
7326
+ return React29.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
7327
+ }
7328
+ function useDispatch() {
7329
+ return useAgentStore().dispatch;
7330
+ }
7331
+
7332
+ // src/cli/ui/hooks/useActivityPhase.ts
7333
+ function deriveActivityLabel(cards) {
7334
+ if (cards.some((c) => c.kind === "reasoning" && c.streaming)) return "thinking\u2026";
7335
+ const last = cards[cards.length - 1];
7336
+ if (!last || last.kind === "user") return "waiting for model\u2026";
7337
+ return "processing\u2026";
7338
+ }
7339
+ function useActivityLabel() {
7340
+ return useAgentState((s) => deriveActivityLabel(s.cards));
7341
+ }
7342
+
7343
+ // src/cli/ui/hooks/useAgentSession.ts
7344
+ import { useMemo as useMemo6 } from "react";
7345
+ function useAgentSession({
7346
+ sessionId,
7347
+ model: model2,
7348
+ workspace,
7349
+ branch
7350
+ }) {
7351
+ return useMemo6(
7352
+ () => ({
7353
+ id: sessionId ?? "default",
7354
+ branch: branch ?? "main",
7355
+ workspace,
7356
+ model: model2
7357
+ }),
7358
+ [sessionId, branch, workspace, model2]
7359
+ );
7360
+ }
7361
+
7362
+ // src/cli/ui/hooks/useChatScroll.ts
7363
+ import { useCallback as useCallback2, useEffect as useEffect4, useRef as useRef2, useState as useState14 } from "react";
7364
+ var SCROLL_PAGE_ROWS = 3;
7365
+ var COALESCE_MS = 16;
7366
+ function useChatScroll() {
7367
+ const [scrollRows, setScrollRows] = useState14(0);
7368
+ const [pinned, setPinned] = useState14(true);
7369
+ const [maxScroll, setMaxScrollState] = useState14(0);
7370
+ const maxScrollRef = useRef2(0);
7371
+ const pendingDelta = useRef2(0);
7372
+ const flushTimer = useRef2(null);
7373
+ const flush = useCallback2(() => {
7374
+ flushTimer.current = null;
7375
+ const d = pendingDelta.current;
7376
+ pendingDelta.current = 0;
7377
+ if (d === 0) return;
7378
+ if (d < 0) setPinned(false);
7379
+ setScrollRows((o) => {
7380
+ const next = Math.max(0, Math.min(maxScrollRef.current, o + d));
7381
+ if (next >= maxScrollRef.current) setPinned(true);
7382
+ return next;
7383
+ });
7384
+ }, []);
7385
+ const schedule = useCallback2(
7386
+ (delta) => {
7387
+ pendingDelta.current += delta;
7388
+ if (flushTimer.current !== null) return;
7389
+ flushTimer.current = setTimeout(flush, COALESCE_MS);
7390
+ },
7391
+ [flush]
7392
+ );
7393
+ useEffect4(() => {
7394
+ return () => {
7395
+ if (flushTimer.current !== null) {
7396
+ clearTimeout(flushTimer.current);
7397
+ flushTimer.current = null;
7398
+ }
7399
+ };
7400
+ }, []);
7401
+ useEffect4(() => {
7402
+ if (pinned) setScrollRows(maxScroll);
7403
+ }, [pinned, maxScroll]);
7404
+ useEffect4(() => {
7405
+ if (scrollRows > maxScroll) setScrollRows(maxScroll);
7406
+ }, [scrollRows, maxScroll]);
7407
+ const scrollUp = useCallback2(() => schedule(-SCROLL_PAGE_ROWS), [schedule]);
7408
+ const scrollDown = useCallback2(() => schedule(SCROLL_PAGE_ROWS), [schedule]);
7409
+ const jumpToBottom = useCallback2(() => {
7410
+ pendingDelta.current = 0;
7411
+ if (flushTimer.current !== null) {
7412
+ clearTimeout(flushTimer.current);
7413
+ flushTimer.current = null;
7414
+ }
7415
+ setPinned(true);
7416
+ }, []);
7417
+ const setMaxScroll = useCallback2((rows) => {
7418
+ maxScrollRef.current = rows;
7419
+ setMaxScrollState(rows);
7420
+ }, []);
7421
+ return { scrollRows, pinned, scrollUp, scrollDown, jumpToBottom, setMaxScroll };
7422
+ }
7423
+
7424
+ // src/cli/ui/hooks/useCodeMode.ts
7425
+ import { useCallback as useCallback3 } from "react";
7426
+ function useCodeMode(opts) {
7427
+ const { codeMode, pendingEdits, currentRootDir, session, syncPendingCount, recordEdit } = opts;
7428
+ const codeApply = useCallback3(
7429
+ (indices) => {
7430
+ if (!codeMode) return "not in code mode";
7431
+ const blocks = pendingEdits.current;
7432
+ if (blocks.length === 0) {
7433
+ return "nothing pending \u2014 the model hasn't proposed edits since the last /apply or /discard.";
7434
+ }
7435
+ const useSubset = indices !== void 0 && indices.length > 0;
7436
+ const { selected, remaining } = useSubset ? partitionEdits(blocks, indices) : { selected: blocks, remaining: [] };
7437
+ if (selected.length === 0) {
7438
+ return "\u25B8 no edits matched those indices \u2014 nothing applied. Use /apply with no args to commit them all.";
7439
+ }
7440
+ const snaps = snapshotBeforeEdits(selected, currentRootDir);
7441
+ const results = applyEditBlocks(selected, currentRootDir);
7442
+ const anyApplied = results.some((r) => r.status === "applied" || r.status === "created");
7443
+ if (anyApplied) recordEdit("review-apply", selected, results, snaps);
7444
+ pendingEdits.current = remaining;
7445
+ if (remaining.length === 0) clearPendingEdits(session ?? null);
7446
+ else savePendingEdits(session ?? null, remaining);
7447
+ syncPendingCount();
7448
+ const tail = remaining.length > 0 ? `
7449
+ \u25B8 ${remaining.length} edit block(s) still pending \u2014 /apply or /discard to clear them.` : "";
7450
+ return formatEditResults(results) + tail;
7451
+ },
7452
+ [codeMode, currentRootDir, session, syncPendingCount, recordEdit, pendingEdits]
7453
+ );
7454
+ const codeDiscard = useCallback3(
7455
+ (indices) => {
7456
+ const blocks = pendingEdits.current;
7457
+ if (blocks.length === 0) return "nothing pending to discard.";
7458
+ const useSubset = indices !== void 0 && indices.length > 0;
7459
+ const { selected, remaining } = useSubset ? partitionEdits(blocks, indices) : { selected: blocks, remaining: [] };
7460
+ if (selected.length === 0) {
7461
+ return "\u25B8 no edits matched those indices \u2014 nothing discarded.";
7462
+ }
7463
+ pendingEdits.current = remaining;
7464
+ if (remaining.length === 0) clearPendingEdits(session ?? null);
7465
+ else savePendingEdits(session ?? null, remaining);
7466
+ syncPendingCount();
7467
+ const tail = remaining.length > 0 ? ` (${remaining.length} block(s) still pending)` : ". Nothing was written to disk.";
7468
+ return `\u25B8 discarded ${selected.length} pending edit block(s)${tail}`;
7469
+ },
7470
+ [session, syncPendingCount, pendingEdits]
7471
+ );
7472
+ return { codeApply, codeDiscard };
7457
7473
  }
7458
7474
 
7459
- // src/cli/ui/state/store.ts
7460
- function createStore(session, initialCards) {
7461
- let state = initialState(session, initialCards);
7462
- const stateListeners = /* @__PURE__ */ new Set();
7463
- const eventListeners = /* @__PURE__ */ new Set();
7464
- return {
7465
- getState() {
7466
- return state;
7467
- },
7468
- dispatch(event) {
7469
- state = reduce(state, event);
7470
- for (const listener of stateListeners) listener();
7471
- for (const listener of eventListeners) listener(event);
7472
- },
7473
- subscribe(listener) {
7474
- stateListeners.add(listener);
7475
- return () => {
7476
- stateListeners.delete(listener);
7477
- };
7478
- },
7479
- onEvent(listener) {
7480
- eventListeners.add(listener);
7481
- return () => {
7482
- eventListeners.delete(listener);
7483
- };
7475
+ // src/cli/ui/hooks/useInputRecall.ts
7476
+ import { useCallback as useCallback4, useRef as useRef3 } from "react";
7477
+ function useInputRecall(setInput) {
7478
+ const promptHistory = useRef3([]);
7479
+ const historyCursor = useRef3(-1);
7480
+ const recallPrev = useCallback4(() => {
7481
+ const hist = promptHistory.current;
7482
+ if (hist.length === 0) return;
7483
+ const nextCursor = Math.min(historyCursor.current + 1, hist.length - 1);
7484
+ historyCursor.current = nextCursor;
7485
+ setInput(hist[hist.length - 1 - nextCursor] ?? "");
7486
+ }, [setInput]);
7487
+ const recallNext = useCallback4(() => {
7488
+ if (historyCursor.current < 0) return;
7489
+ const hist = promptHistory.current;
7490
+ const nextCursor = historyCursor.current - 1;
7491
+ historyCursor.current = nextCursor;
7492
+ setInput(nextCursor < 0 ? "" : hist[hist.length - 1 - nextCursor] ?? "");
7493
+ }, [setInput]);
7494
+ const pushHistory = useCallback4((text) => {
7495
+ promptHistory.current.push(text);
7496
+ }, []);
7497
+ const resetCursor = useCallback4(() => {
7498
+ historyCursor.current = -1;
7499
+ }, []);
7500
+ return { recallPrev, recallNext, pushHistory, resetCursor };
7501
+ }
7502
+
7503
+ // src/cli/ui/hooks/useLoopMode.ts
7504
+ import { useCallback as useCallback5, useEffect as useEffect5, useRef as useRef4, useState as useState15 } from "react";
7505
+ function useLoopMode(opts) {
7506
+ const { log, busyRef, handleSubmitRef } = opts;
7507
+ const [activeLoop, setActiveLoop] = useState15(null);
7508
+ const activeLoopRef = useRef4(null);
7509
+ const loopTimerRef = useRef4(null);
7510
+ const loopFiringRef = useRef4(false);
7511
+ useEffect5(() => {
7512
+ activeLoopRef.current = activeLoop;
7513
+ }, [activeLoop]);
7514
+ const stopLoop = useCallback5(() => {
7515
+ if (loopTimerRef.current) {
7516
+ clearTimeout(loopTimerRef.current);
7517
+ loopTimerRef.current = null;
7518
+ }
7519
+ const cur = activeLoopRef.current;
7520
+ if (!cur) return;
7521
+ setActiveLoop(null);
7522
+ log.pushInfo(`\u25B8 loop stopped (after ${cur.iter} iter${cur.iter === 1 ? "" : "s"}).`);
7523
+ }, [log]);
7524
+ const startLoop = useCallback5((intervalMs, prompt) => {
7525
+ if (loopTimerRef.current) {
7526
+ clearTimeout(loopTimerRef.current);
7527
+ loopTimerRef.current = null;
7484
7528
  }
7529
+ setActiveLoop({
7530
+ prompt,
7531
+ intervalMs,
7532
+ nextFireAt: Date.now() + intervalMs,
7533
+ iter: 0
7534
+ });
7535
+ }, []);
7536
+ const getLoopStatus = useCallback5(() => {
7537
+ const cur = activeLoopRef.current;
7538
+ if (!cur) return null;
7539
+ return {
7540
+ prompt: cur.prompt,
7541
+ intervalMs: cur.intervalMs,
7542
+ iter: cur.iter,
7543
+ nextFireMs: Math.max(0, cur.nextFireAt - Date.now())
7544
+ };
7545
+ }, []);
7546
+ const isLoopActive = useCallback5(() => activeLoopRef.current !== null, []);
7547
+ const isLoopFiring = useCallback5(() => loopFiringRef.current, []);
7548
+ const clearFiringFlag = useCallback5(() => {
7549
+ loopFiringRef.current = false;
7550
+ }, []);
7551
+ useEffect5(() => {
7552
+ if (!activeLoop) return;
7553
+ const delay = Math.max(0, activeLoop.nextFireAt - Date.now());
7554
+ const timer = setTimeout(async () => {
7555
+ loopTimerRef.current = null;
7556
+ if (busyRef.current) {
7557
+ setActiveLoop((cur2) => cur2 ? { ...cur2, nextFireAt: Date.now() + 1e3 } : cur2);
7558
+ return;
7559
+ }
7560
+ const cur = activeLoopRef.current;
7561
+ if (!cur) return;
7562
+ const nextIter = cur.iter + 1;
7563
+ setActiveLoop(
7564
+ (c) => c ? { ...c, iter: nextIter, nextFireAt: Date.now() + cur.intervalMs } : c
7565
+ );
7566
+ log.pushInfo(`\u25B8 /loop iter ${nextIter} \u2192 ${cur.prompt}`);
7567
+ loopFiringRef.current = true;
7568
+ try {
7569
+ await handleSubmitRef.current?.(cur.prompt);
7570
+ } catch {
7571
+ stopLoop();
7572
+ } finally {
7573
+ loopFiringRef.current = false;
7574
+ }
7575
+ }, delay);
7576
+ loopTimerRef.current = timer;
7577
+ return () => clearTimeout(timer);
7578
+ }, [activeLoop, stopLoop, log, busyRef, handleSubmitRef]);
7579
+ return {
7580
+ startLoop,
7581
+ stopLoop,
7582
+ getLoopStatus,
7583
+ isLoopActive,
7584
+ isLoopFiring,
7585
+ clearFiringFlag,
7586
+ activeLoop
7485
7587
  };
7486
7588
  }
7487
7589
 
7488
- // src/cli/ui/state/provider.tsx
7489
- var StoreCtx = React29.createContext(null);
7490
- function AgentStoreProvider({
7491
- session,
7492
- initialCards,
7493
- children
7494
- }) {
7495
- const initialCardsRef = React29.useRef(initialCards);
7496
- const store = React29.useMemo(() => createStore(session, initialCardsRef.current), [session]);
7497
- return /* @__PURE__ */ React29.createElement(StoreCtx.Provider, { value: store }, children);
7498
- }
7499
- function useAgentStore() {
7500
- const store = React29.useContext(StoreCtx);
7501
- if (!store) throw new Error("useAgentStore must be used inside AgentStoreProvider");
7502
- return store;
7503
- }
7504
- function useAgentState(selector) {
7505
- const store = useAgentStore();
7506
- const subscribe = React29.useCallback((cb) => store.subscribe(cb), [store]);
7507
- const getSnapshot = React29.useCallback(() => selector(store.getState()), [store, selector]);
7508
- return React29.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
7509
- }
7510
- function useDispatch() {
7511
- return useAgentStore().dispatch;
7590
+ // src/cli/ui/hooks/useQuit.ts
7591
+ import { useCallback as useCallback6, useEffect as useEffect6 } from "react";
7592
+ function useQuit(transcriptRef) {
7593
+ const quitProcess = useCallback6(() => {
7594
+ transcriptRef.current?.end();
7595
+ process.exit(0);
7596
+ }, [transcriptRef]);
7597
+ useEffect6(() => {
7598
+ process.on("SIGINT", quitProcess);
7599
+ return () => {
7600
+ process.off("SIGINT", quitProcess);
7601
+ };
7602
+ }, [quitProcess]);
7603
+ return quitProcess;
7512
7604
  }
7513
7605
 
7514
7606
  // src/cli/ui/hooks/useScrollback.ts
7607
+ import { useMemo as useMemo7 } from "react";
7515
7608
  var seq = 0;
7516
7609
  function nextId2(prefix) {
7517
7610
  seq += 1;
@@ -7838,10 +7931,16 @@ function CtxCard({ card }) {
7838
7931
  {
7839
7932
  glyph: "\u2318",
7840
7933
  tone: TONE.brand,
7841
- title: "context",
7934
+ title: t("cardTitles.context"),
7842
7935
  meta: [`${used.toLocaleString()} / ${cap.toLocaleString()} (${usedPct.toFixed(1)}%)`]
7843
7936
  }
7844
- ), row("system", card.systemTokens, card.systemTokens / cap, TONE.brand), row("tools", card.toolsTokens, card.toolsTokens / cap, TONE.warn), row("log", card.logTokens, card.logTokens / cap, TONE.ok), row("input", card.inputTokens, card.inputTokens / cap, TONE.accent), card.topTools.length > 0 ? /* @__PURE__ */ React32.createElement(React32.Fragment, null, /* @__PURE__ */ React32.createElement(Text26, { color: FG.faint }, `top tools \xB7 ${card.toolsCount} total \xB7 ${card.logMessages} log msgs`), card.topTools.slice(0, 5).map((t2) => /* @__PURE__ */ React32.createElement(Box27, { key: `${t2.turn}-${t2.name}`, flexDirection: "row", gap: 1 }, /* @__PURE__ */ React32.createElement(Text26, { color: FG.sub }, t2.name), /* @__PURE__ */ React32.createElement(Text26, { color: FG.faint }, `\xB7 turn ${t2.turn} \xB7 ${t2.tokens.toLocaleString()}`)))) : null);
7937
+ ), row(t("cardLabels.system"), card.systemTokens, card.systemTokens / cap, TONE.brand), row(t("cardLabels.tools"), card.toolsTokens, card.toolsTokens / cap, TONE.warn), row(t("cardLabels.log"), card.logTokens, card.logTokens / cap, TONE.ok), row(t("cardLabels.input"), card.inputTokens, card.inputTokens / cap, TONE.accent), card.topTools.length > 0 ? /* @__PURE__ */ React32.createElement(React32.Fragment, null, /* @__PURE__ */ React32.createElement(Text26, { color: FG.faint }, `${t("cardLabels.topTools")} \xB7 ${card.toolsCount} ${t("cardLabels.tools")} \xB7 ${card.logMessages} ${t("cardLabels.logMsgs")}`), card.topTools.slice(0, 5).map((tool) => /* @__PURE__ */ React32.createElement(Box27, { key: `${tool.turn}-${tool.name}`, flexDirection: "row", gap: 1 }, /* @__PURE__ */ React32.createElement(Text26, { color: FG.sub }, tool.name), /* @__PURE__ */ React32.createElement(
7938
+ Text26,
7939
+ {
7940
+ color: FG.faint
7941
+ },
7942
+ `\xB7 ${t("cardLabels.turn")} ${tool.turn} \xB7 ${tool.tokens.toLocaleString()}`
7943
+ )))) : null);
7845
7944
  }
7846
7945
 
7847
7946
  // src/cli/ui/cards/DiffCard.tsx
@@ -7909,7 +8008,7 @@ import { Box as Box30, Text as Text29 } from "ink";
7909
8008
  import React35 from "react";
7910
8009
  var STACK_TAIL = 5;
7911
8010
  function ErrorCard({ card }) {
7912
- const retryNote = card.retries !== void 0 && card.retries > 0 ? `${card.retries} retr${card.retries === 1 ? "y" : "ies"}` : null;
8011
+ const retryNote = card.retries !== void 0 && card.retries > 0 ? `${card.retries} ${t("cardLabels.retries")}` : null;
7913
8012
  const stackLines = card.stack ? card.stack.split("\n") : [];
7914
8013
  const stackTrunc = stackLines.length > STACK_TAIL;
7915
8014
  const stackVisible = stackTrunc ? stackLines.slice(-STACK_TAIL) : stackLines;
@@ -7921,10 +8020,13 @@ function ErrorCard({ card }) {
7921
8020
  {
7922
8021
  glyph: "\u2716",
7923
8022
  tone: TONE.err,
7924
- title: card.title || "error",
8023
+ title: card.title || t("cardTitles.error"),
7925
8024
  meta: retryNote ? [retryNote] : void 0
7926
8025
  }
7927
- ), messageLines.map((line, i) => /* @__PURE__ */ React35.createElement(Text29, { key: `${card.id}:msg:${i}`, color: TONE.err }, line || " ")), hasStack ? /* @__PURE__ */ React35.createElement(Box30, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React35.createElement(Text29, { color: FG.meta }, "stack trace"), stackHidden > 0 ? /* @__PURE__ */ React35.createElement(Text29, { color: FG.faint }, `\u22EE ${stackHidden} earlier stack line${stackHidden === 1 ? "" : "s"} hidden`) : null, stackVisible.map((line, i) => /* @__PURE__ */ React35.createElement(Text29, { key: `${card.id}:stk:${stackHidden + i}`, color: FG.meta }, line || " "))) : null);
8026
+ ), messageLines.map((line, i) => /* @__PURE__ */ React35.createElement(Text29, { key: `${card.id}:msg:${i}`, color: TONE.err }, line || " ")), hasStack ? /* @__PURE__ */ React35.createElement(Box30, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React35.createElement(Text29, { color: FG.meta }, t("cardLabels.stackTrace")), stackHidden > 0 ? /* @__PURE__ */ React35.createElement(Text29, { color: FG.faint }, t(
8027
+ stackHidden === 1 ? "cardLabels.earlierStackLine" : "cardLabels.earlierStackLines",
8028
+ { count: stackHidden }
8029
+ )) : null, stackVisible.map((line, i) => /* @__PURE__ */ React35.createElement(Text29, { key: `${card.id}:stk:${stackHidden + i}`, color: FG.meta }, line || " "))) : null);
7928
8030
  }
7929
8031
 
7930
8032
  // src/cli/ui/cards/LiveCard.tsx
@@ -8090,7 +8192,7 @@ function anchorIndex(steps) {
8090
8192
  }
8091
8193
 
8092
8194
  // src/cli/ui/cards/ReasoningCard.tsx
8093
- import { Box as Box34, Text as Text35, useStdout as useStdout8 } from "ink";
8195
+ import { Box as Box34, Text as Text35, useStdout as useStdout9 } from "ink";
8094
8196
  import React41 from "react";
8095
8197
 
8096
8198
  // src/frame/width.ts
@@ -8157,7 +8259,7 @@ function ReasoningCard({
8157
8259
  card,
8158
8260
  expanded
8159
8261
  }) {
8160
- const { stdout } = useStdout8();
8262
+ const { stdout } = useStdout9();
8161
8263
  const cols = stdout?.columns ?? 80;
8162
8264
  const lineCells = Math.max(20, cols - 4);
8163
8265
  const allLines = card.text.length > 0 ? card.text.split("\n") : [];
@@ -8169,7 +8271,7 @@ function ReasoningHeader({ card }) {
8169
8271
  const streamingActive = card.streaming && !card.aborted;
8170
8272
  const headColor = card.aborted ? TONE.err : streamingActive ? TONE_ACTIVE.accent : TONE.accent;
8171
8273
  const glyph = streamingActive ? "\u25C7" : "\u25C6";
8172
- const title = streamingActive ? "reasoning\u2026" : card.aborted ? "reasoning (aborted)" : "reasoning";
8274
+ const title = streamingActive ? t("cardTitles.reasoningEllipsis") : card.aborted ? t("cardTitles.reasoningAborted") : t("cardTitles.reasoning");
8173
8275
  const meta = [];
8174
8276
  const m = headerMeta(card);
8175
8277
  if (m) meta.push(m);
@@ -8191,11 +8293,11 @@ function ReasoningHeader({ card }) {
8191
8293
  }
8192
8294
  function headerMeta(card) {
8193
8295
  if (card.streaming) {
8194
- return card.tokens > 0 ? `${card.tokens.toLocaleString()} tok` : "";
8296
+ return card.tokens > 0 ? `${card.tokens.toLocaleString()} ${t("cardLabels.tok")}` : "";
8195
8297
  }
8196
8298
  const parts = [];
8197
- if (card.tokens > 0) parts.push(`${card.tokens.toLocaleString()} tok`);
8198
- if (card.paragraphs > 0) parts.push(`${card.paragraphs} \xB6`);
8299
+ if (card.tokens > 0) parts.push(`${card.tokens.toLocaleString()} ${t("cardLabels.tok")}`);
8300
+ if (card.paragraphs > 0) parts.push(`${card.paragraphs} ${t("cardLabels.pilcrow")}`);
8199
8301
  return parts.join(" \xB7 ");
8200
8302
  }
8201
8303
  function headerDuration(card) {
@@ -8246,18 +8348,24 @@ import React42 from "react";
8246
8348
  function SearchCard({ card }) {
8247
8349
  const fileCount = new Set(card.hits.map((h) => h.file)).size;
8248
8350
  const elapsed = `${(card.elapsedMs / 1e3).toFixed(2)}s`;
8249
- const stats2 = `${card.hits.length} hit${card.hits.length === 1 ? "" : "s"} \xB7 ${fileCount} file${fileCount === 1 ? "" : "s"}`;
8351
+ const stats2 = t(card.hits.length === 1 ? "cardLabels.hitSingular" : "cardLabels.hitsPlural", {
8352
+ count: card.hits.length,
8353
+ files: fileCount
8354
+ });
8250
8355
  const grouped = groupByFile(card.hits.slice(0, 10));
8251
8356
  return /* @__PURE__ */ React42.createElement(Card, { tone: TONE.info }, /* @__PURE__ */ React42.createElement(
8252
8357
  CardHeader,
8253
8358
  {
8254
8359
  glyph: "\u2299",
8255
8360
  tone: TONE.info,
8256
- title: "search",
8361
+ title: t("cardTitles.search"),
8257
8362
  subtitle: `"${card.query}"`,
8258
8363
  meta: [stats2, elapsed]
8259
8364
  }
8260
- ), grouped.map(([file, hits]) => /* @__PURE__ */ React42.createElement(Box35, { key: file, flexDirection: "column" }, /* @__PURE__ */ React42.createElement(Text36, { bold: true, color: FG.strong }, file), hits.map((h, i) => /* @__PURE__ */ React42.createElement(Box35, { key: `${file}:${h.line}:${i}`, flexDirection: "row", gap: 1 }, /* @__PURE__ */ React42.createElement(Text36, { color: FG.faint }, `${h.line.toString().padStart(4)} \u2502`), /* @__PURE__ */ React42.createElement(HighlightedLine, { text: h.preview, start: h.matchStart, end: h.matchEnd }))))), card.hits.length > 10 ? /* @__PURE__ */ React42.createElement(Text36, { color: FG.faint }, `\u22EE +${card.hits.length - 10} more hits`) : null);
8365
+ ), grouped.map(([file, hits]) => /* @__PURE__ */ React42.createElement(Box35, { key: file, flexDirection: "column" }, /* @__PURE__ */ React42.createElement(Text36, { bold: true, color: FG.strong }, file), hits.map((h, i) => /* @__PURE__ */ React42.createElement(Box35, { key: `${file}:${h.line}:${i}`, flexDirection: "row", gap: 1 }, /* @__PURE__ */ React42.createElement(Text36, { color: FG.faint }, `${h.line.toString().padStart(4)} \u2502`), /* @__PURE__ */ React42.createElement(HighlightedLine, { text: h.preview, start: h.matchStart, end: h.matchEnd }))))), card.hits.length > 10 ? /* @__PURE__ */ React42.createElement(Text36, { color: FG.faint }, t(
8366
+ card.hits.length - 10 === 1 ? "cardLabels.moreHitSingular" : "cardLabels.moreHitsPlural",
8367
+ { count: card.hits.length - 10 }
8368
+ )) : null);
8261
8369
  }
8262
8370
  function HighlightedLine({
8263
8371
  text,
@@ -8280,7 +8388,7 @@ function groupByFile(hits) {
8280
8388
  }
8281
8389
 
8282
8390
  // src/cli/ui/cards/StreamingCard.tsx
8283
- import { Box as Box37, Text as Text38, useStdout as useStdout10 } from "ink";
8391
+ import { Box as Box37, Text as Text38, useStdout as useStdout11 } from "ink";
8284
8392
  import React44, { useContext as useContext5 } from "react";
8285
8393
 
8286
8394
  // src/cli/ui/layout/LiveExpandContext.ts
@@ -8289,7 +8397,7 @@ var LiveExpandContext = createContext3(false);
8289
8397
 
8290
8398
  // src/cli/ui/markdown.tsx
8291
8399
  import { highlight, supportsLanguage } from "cli-highlight";
8292
- import { Box as Box36, Text as Text37, useStdout as useStdout9 } from "ink";
8400
+ import { Box as Box36, Text as Text37, useStdout as useStdout10 } from "ink";
8293
8401
  import React43 from "react";
8294
8402
  import stringWidth from "string-width";
8295
8403
  var BODY_LEFT_CELLS = 7;
@@ -8297,7 +8405,7 @@ var MarkdownWidthCtx = React43.createContext(void 0);
8297
8405
  function useWidth() {
8298
8406
  const ctx = React43.useContext(MarkdownWidthCtx);
8299
8407
  if (ctx !== void 0) return ctx;
8300
- return (useStdout9()?.stdout?.columns ?? process.stdout.columns ?? 80) - BODY_LEFT_CELLS;
8408
+ return (useStdout10()?.stdout?.columns ?? process.stdout.columns ?? 80) - BODY_LEFT_CELLS;
8301
8409
  }
8302
8410
  marked.setOptions({ gfm: true, breaks: false });
8303
8411
  function Markdown({ text, width }) {
@@ -8618,7 +8726,7 @@ function tokenRate(text, startTs, endTs) {
8618
8726
  }
8619
8727
  var PILL_RATE = { bg: "#11141a", fg: "#8b949e" };
8620
8728
  function StreamingCard({ card }) {
8621
- const { stdout } = useStdout10();
8729
+ const { stdout } = useStdout11();
8622
8730
  const cols = stdout?.columns ?? 80;
8623
8731
  const expanded = useContext5(LiveExpandContext);
8624
8732
  const reserveCap = expanded ? EXPANDED_MAX_LINES + 2 : STREAMING_PREVIEW_LINES2 + 2;
@@ -8637,7 +8745,7 @@ function StreamingCard({ card }) {
8637
8745
  {
8638
8746
  glyph: "\u2039",
8639
8747
  tone: TONE.ok,
8640
- title: "reply",
8748
+ title: t("cardTitles.reply"),
8641
8749
  right: /* @__PURE__ */ React44.createElement(React44.Fragment, null, ratePill, modelPill)
8642
8750
  }
8643
8751
  ), /* @__PURE__ */ React44.createElement(Markdown, { text: card.text }));
@@ -8651,7 +8759,7 @@ function StreamingCard({ card }) {
8651
8759
  const aborted = !!card.aborted;
8652
8760
  const headColor = aborted ? TONE.err : TONE_ACTIVE.brand;
8653
8761
  const glyph = aborted ? "\u2039" : "\u25C8";
8654
- const headLabel = aborted ? "aborted" : "writing\u2026";
8762
+ const headLabel = aborted ? t("cardLabels.aborted") : t("cardLabels.writing");
8655
8763
  const { tokens: liveTokens, tps: liveTps } = tokenRate(card.text, card.ts, Date.now());
8656
8764
  const liveRatePill = !aborted && liveTokens >= MIN_TOKENS_FOR_RATE && liveTps !== null ? /* @__PURE__ */ React44.createElement(Pill, { label: `${liveTps} t/s`, ...PILL_RATE, bold: false }) : null;
8657
8765
  const expandPill = !aborted ? /* @__PURE__ */ React44.createElement(Pill, { label: expanded ? "expanded \u2303o" : "preview \u2303o", ...PILL_RATE, bold: false }) : null;
@@ -8663,13 +8771,9 @@ function StreamingCard({ card }) {
8663
8771
  title: headLabel,
8664
8772
  right: /* @__PURE__ */ React44.createElement(React44.Fragment, null, liveRatePill, expandPill, aborted ? null : /* @__PURE__ */ React44.createElement(Spinner, { kind: "braille", color: TONE_ACTIVE.brand }), modelPill)
8665
8773
  }
8666
- ), expanded && droppedAbove > 0 ? /* @__PURE__ */ React44.createElement(
8667
- Text38,
8668
- {
8669
- color: FG.faint
8670
- },
8671
- `\u22EF ${droppedAbove} earlier line${droppedAbove === 1 ? "" : "s"} above`
8672
- ) : null, visible.map((line, i) => /* @__PURE__ */ React44.createElement(Box37, { key: `${card.id}:${visualLines.length - visible.length + i}`, flexDirection: "row" }, /* @__PURE__ */ React44.createElement(Text38, { color: aborted ? FG.meta : FG.body }, clipToCells(line, lineCells)))), aborted ? /* @__PURE__ */ React44.createElement(Text38, { color: FG.faint }, "[truncated by esc]") : null);
8774
+ ), expanded && droppedAbove > 0 ? /* @__PURE__ */ React44.createElement(Text38, { color: FG.faint }, t(droppedAbove === 1 ? "cardLabels.earlierLine" : "cardLabels.earlierLines", {
8775
+ count: droppedAbove
8776
+ })) : null, visible.map((line, i) => /* @__PURE__ */ React44.createElement(Box37, { key: `${card.id}:${visualLines.length - visible.length + i}`, flexDirection: "row" }, /* @__PURE__ */ React44.createElement(Text38, { color: aborted ? FG.meta : FG.body }, clipToCells(line, lineCells)))), aborted ? /* @__PURE__ */ React44.createElement(Text38, { color: FG.faint }, t("cardLabels.truncatedByEsc")) : null);
8673
8777
  }
8674
8778
 
8675
8779
  // src/cli/ui/cards/SubAgentCard.tsx
@@ -8683,22 +8787,22 @@ function SubAgentCard({ card }) {
8683
8787
  failed: tone.err
8684
8788
  };
8685
8789
  const headColor = statusColor[card.status];
8686
- const headGlyph = card.status === "failed" ? "\u2716" : "\u232C";
8790
+ const headGlyph = card.status === "failed" ? "\u2717" : "\u232C";
8687
8791
  const runningChildren = card.children.filter((c) => !isChildDone(c)).length;
8688
8792
  const isRunning = card.status === "running";
8689
8793
  const inLive = useContext6(ActiveCardContext);
8690
- const headerMeta2 = isRunning ? runningChildren > 0 ? [`${runningChildren} running`] : ["working"] : [{ text: card.status, color: headColor }];
8794
+ const headerMeta2 = isRunning ? runningChildren > 0 ? [`${runningChildren} ${t("cardLabels.runningLabel")}`] : [t("cardLabels.workingLabel")] : [{ text: card.status, color: headColor }];
8691
8795
  return /* @__PURE__ */ React45.createElement(Card, { tone: headColor }, /* @__PURE__ */ React45.createElement(
8692
8796
  CardHeader,
8693
8797
  {
8694
8798
  glyph: headGlyph,
8695
8799
  tone: headColor,
8696
- title: "subagent",
8800
+ title: t("cardTitles.subagent"),
8697
8801
  titleColor: tone.violet,
8698
8802
  subtitle: card.task,
8699
8803
  meta: headerMeta2
8700
8804
  }
8701
- ), card.name ? /* @__PURE__ */ React45.createElement(Text39, { color: fg.faint }, `agent \xB7 ${card.name}`) : null, card.tools && card.tools.length > 0 && /* @__PURE__ */ React45.createElement(Text39, { color: fg.faint }, `tools \xB7 ${card.tools.join(", ")}`), card.children.map((child) => /* @__PURE__ */ React45.createElement(Box38, { key: child.id, flexDirection: "row", gap: 1 }, inLive ? null : /* @__PURE__ */ React45.createElement(Text39, { color: tone.violet }, "\u258E"), /* @__PURE__ */ React45.createElement(ChildRow, { card: child }))));
8805
+ ), card.name ? /* @__PURE__ */ React45.createElement(Text39, { color: fg.faint }, `${t("cardLabels.agent")} \xB7 ${card.name}`) : null, card.tools && card.tools.length > 0 && /* @__PURE__ */ React45.createElement(Text39, { color: fg.faint }, `${t("cardLabels.tools")} \xB7 ${card.tools.join(", ")}`), card.children.map((child) => /* @__PURE__ */ React45.createElement(Box38, { key: child.id, flexDirection: "row", gap: 1 }, inLive ? null : /* @__PURE__ */ React45.createElement(Text39, { color: tone.violet }, "\u258E"), /* @__PURE__ */ React45.createElement(ChildRow, { card: child }))));
8702
8806
  }
8703
8807
  function isChildDone(card) {
8704
8808
  switch (card.kind) {
@@ -8734,7 +8838,7 @@ function childVisual(card, doneColor, failedColor, fallbackColor) {
8734
8838
  statusGlyph: done ? doneGlyph(doneColor) : runningGlyph(CARD.reasoning.color),
8735
8839
  kindGlyph: "\u25C6",
8736
8840
  kindColor: CARD.reasoning.color,
8737
- text: `reasoning \xB7 ${card.paragraphs} \xB6`
8841
+ text: t("cardLabels.reasoningLabel", { count: card.paragraphs })
8738
8842
  };
8739
8843
  }
8740
8844
  case "tool": {
@@ -8751,7 +8855,7 @@ function childVisual(card, doneColor, failedColor, fallbackColor) {
8751
8855
  statusGlyph: card.done ? doneGlyph(doneColor) : runningGlyph(CARD.streaming.color),
8752
8856
  kindGlyph: "\u25C8",
8753
8857
  kindColor: CARD.streaming.color,
8754
- text: card.done ? "response" : "writing \u2026"
8858
+ text: card.done ? t("cardLabels.response") : t("cardLabels.writing")
8755
8859
  };
8756
8860
  case "diff":
8757
8861
  return {
@@ -8848,7 +8952,7 @@ function TipRowRender({
8848
8952
  }
8849
8953
 
8850
8954
  // src/cli/ui/cards/ToolCard.tsx
8851
- import { Text as Text42, useStdout as useStdout11 } from "ink";
8955
+ import { Text as Text42, useStdout as useStdout12 } from "ink";
8852
8956
  import React48 from "react";
8853
8957
  var READ_TAIL = 2;
8854
8958
  var OTHER_TAIL = 5;
@@ -8857,7 +8961,7 @@ function tailLinesFor(name) {
8857
8961
  return /(?:^|_)(read|search|list|tree|get|status|diff|fetch|grep)(_|$)/.test(lower) || lower === "job_output" ? READ_TAIL : OTHER_TAIL;
8858
8962
  }
8859
8963
  function ToolCard({ card }) {
8860
- const { stdout } = useStdout11();
8964
+ const { stdout } = useStdout12();
8861
8965
  const cols = stdout?.columns ?? 80;
8862
8966
  const lineCells = Math.max(20, cols - 4);
8863
8967
  const argsLabel = formatArgsSummary(card.args);
@@ -8876,7 +8980,7 @@ function ToolCard({ card }) {
8876
8980
  meta.push({ text: `\u21BB ${card.retry.attempt}/${card.retry.max}`, color: TONE.warn });
8877
8981
  }
8878
8982
  if (card.rejected) {
8879
- meta.push({ text: "rejected", color: TONE.err });
8983
+ meta.push({ text: t("cardLabels.rejected"), color: TONE.err });
8880
8984
  }
8881
8985
  for (const part of metaTrail(card)) meta.push(part);
8882
8986
  return /* @__PURE__ */ React48.createElement(Card, { tone: headColor }, /* @__PURE__ */ React48.createElement(
@@ -8889,7 +8993,9 @@ function ToolCard({ card }) {
8889
8993
  meta: meta.length > 0 ? meta : void 0,
8890
8994
  right: status2 === "running" ? /* @__PURE__ */ React48.createElement(Spinner, { kind: "braille", color: TONE_ACTIVE.brand, bold: true }) : void 0
8891
8995
  }
8892
- ), showBody && (subagentMarkdown !== null ? /* @__PURE__ */ React48.createElement(Markdown, { text: subagentMarkdown, width: lineCells }) : /* @__PURE__ */ React48.createElement(React48.Fragment, null, hidden > 0 ? /* @__PURE__ */ React48.createElement(Text42, { color: FG.faint }, `\u22EE ${hidden} earlier line${hidden === 1 ? "" : "s"} (use /tool to read full)`) : null, visible.map((line, i) => /* @__PURE__ */ React48.createElement(
8996
+ ), showBody && (subagentMarkdown !== null ? /* @__PURE__ */ React48.createElement(Markdown, { text: subagentMarkdown, width: lineCells }) : /* @__PURE__ */ React48.createElement(React48.Fragment, null, hidden > 0 ? /* @__PURE__ */ React48.createElement(Text42, { color: FG.faint }, t(hidden === 1 ? "cardLabels.earlierLine" : "cardLabels.earlierLines", {
8997
+ count: hidden
8998
+ })) : null, visible.map((line, i) => /* @__PURE__ */ React48.createElement(
8893
8999
  Text42,
8894
9000
  {
8895
9001
  key: `${card.id}:${hidden + i}`,
@@ -8949,10 +9055,11 @@ function headerColorFor(s) {
8949
9055
  function metaTrail(card) {
8950
9056
  const parts = [];
8951
9057
  const inputBytes = largestStringInputBytes(card.args);
8952
- if (inputBytes !== null) parts.push(`${formatBytes(inputBytes)} in`);
8953
- if (card.elapsedMs > 0) parts.push(`${(card.elapsedMs / 1e3).toFixed(2)}s`);
9058
+ if (inputBytes !== null) parts.push(t("cardLabels.bytesIn", { bytes: formatBytes(inputBytes) }));
9059
+ if (card.elapsedMs > 0)
9060
+ parts.push(t("cardLabels.elapsedSec", { secs: (card.elapsedMs / 1e3).toFixed(2) }));
8954
9061
  if (card.done && !card.rejected && !card.aborted && card.exitCode !== void 0 && card.exitCode !== 0) {
8955
- parts.push(`exit ${card.exitCode}`);
9062
+ parts.push(t("cardLabels.exit", { code: card.exitCode }));
8956
9063
  }
8957
9064
  return parts;
8958
9065
  }
@@ -9009,13 +9116,16 @@ function UsageCard({ card }) {
9009
9116
  const promptRatio = card.tokens.prompt / cap;
9010
9117
  const reasonRatio = card.tokens.reason / cap;
9011
9118
  const outputRatio = card.tokens.output / cap;
9012
- const headerMeta2 = [`turn ${card.turn}`, formatCost(card.cost, card.balanceCurrency)];
9119
+ const headerMeta2 = [
9120
+ `${t("cardLabels.turn")} ${card.turn}`,
9121
+ formatCost(card.cost, card.balanceCurrency)
9122
+ ];
9013
9123
  if (card.elapsedMs !== void 0) headerMeta2.push(`${(card.elapsedMs / 1e3).toFixed(1)}s`);
9014
- return /* @__PURE__ */ React49.createElement(Card, { tone: FG.meta }, /* @__PURE__ */ React49.createElement(CardHeader, { glyph: "\u03A3", tone: FG.meta, title: "usage", meta: headerMeta2 }), /* @__PURE__ */ React49.createElement(Box42, { flexDirection: "row", gap: 1 }, /* @__PURE__ */ React49.createElement(Text43, { color: FG.sub }, "prompt"), bar(promptRatio, TONE.brand), /* @__PURE__ */ React49.createElement(Text43, { bold: true, color: FG.body }, card.tokens.prompt.toLocaleString()), /* @__PURE__ */ React49.createElement(Text43, { color: FG.faint }, `/ 1M \xB7 ${(promptRatio * 100).toFixed(1)}%`)), /* @__PURE__ */ React49.createElement(Box42, { flexDirection: "row", gap: 1 }, /* @__PURE__ */ React49.createElement(Text43, { color: FG.sub }, "reason"), bar(reasonRatio, TONE.accent), /* @__PURE__ */ React49.createElement(Text43, { bold: true, color: FG.body }, card.tokens.reason.toLocaleString())), /* @__PURE__ */ React49.createElement(Box42, { flexDirection: "row", gap: 1 }, /* @__PURE__ */ React49.createElement(Text43, { color: FG.sub }, "output"), bar(outputRatio, TONE.brand), /* @__PURE__ */ React49.createElement(Text43, { bold: true, color: FG.body }, card.tokens.output.toLocaleString())), /* @__PURE__ */ React49.createElement(Box42, { flexDirection: "row", gap: 1 }, /* @__PURE__ */ React49.createElement(Text43, { color: FG.sub }, "cache "), bar(card.cacheHit, TONE.ok), /* @__PURE__ */ React49.createElement(Text43, { bold: true, color: TONE.ok }, `${(card.cacheHit * 100).toFixed(1)}%`)), /* @__PURE__ */ React49.createElement(Box42, { flexDirection: "row", gap: 1 }, /* @__PURE__ */ React49.createElement(Text43, { color: FG.faint }, "session"), /* @__PURE__ */ React49.createElement(Text43, { bold: true, color: FG.body }, `\u26C1 ${formatCost(card.sessionCost, card.balanceCurrency, 3)}`), card.balance !== void 0 ? /* @__PURE__ */ React49.createElement(React49.Fragment, null, /* @__PURE__ */ React49.createElement(Text43, { color: FG.faint }, "\xB7 balance"), /* @__PURE__ */ React49.createElement(Text43, { bold: true, color: TONE.brand }, formatBalance(card.balance, card.balanceCurrency))) : null));
9124
+ return /* @__PURE__ */ React49.createElement(Card, { tone: FG.meta }, /* @__PURE__ */ React49.createElement(CardHeader, { glyph: "\u03A3", tone: FG.meta, title: t("cardTitles.usage"), meta: headerMeta2 }), /* @__PURE__ */ React49.createElement(Box42, { flexDirection: "row", gap: 1 }, /* @__PURE__ */ React49.createElement(Text43, { color: FG.sub }, t("cardLabels.prompt")), bar(promptRatio, TONE.brand), /* @__PURE__ */ React49.createElement(Text43, { bold: true, color: FG.body }, card.tokens.prompt.toLocaleString()), /* @__PURE__ */ React49.createElement(Text43, { color: FG.faint }, `/ 1M \xB7 ${(promptRatio * 100).toFixed(1)}%`)), /* @__PURE__ */ React49.createElement(Box42, { flexDirection: "row", gap: 1 }, /* @__PURE__ */ React49.createElement(Text43, { color: FG.sub }, t("cardLabels.reason")), bar(reasonRatio, TONE.accent), /* @__PURE__ */ React49.createElement(Text43, { bold: true, color: FG.body }, card.tokens.reason.toLocaleString())), /* @__PURE__ */ React49.createElement(Box42, { flexDirection: "row", gap: 1 }, /* @__PURE__ */ React49.createElement(Text43, { color: FG.sub }, t("cardLabels.output")), bar(outputRatio, TONE.brand), /* @__PURE__ */ React49.createElement(Text43, { bold: true, color: FG.body }, card.tokens.output.toLocaleString())), /* @__PURE__ */ React49.createElement(Box42, { flexDirection: "row", gap: 1 }, /* @__PURE__ */ React49.createElement(Text43, { color: FG.sub }, t("cardLabels.cache"), " "), bar(card.cacheHit, TONE.ok), /* @__PURE__ */ React49.createElement(Text43, { bold: true, color: TONE.ok }, `${(card.cacheHit * 100).toFixed(1)}%`)), /* @__PURE__ */ React49.createElement(Box42, { flexDirection: "row", gap: 1 }, /* @__PURE__ */ React49.createElement(Text43, { color: FG.faint }, t("cardLabels.session")), /* @__PURE__ */ React49.createElement(Text43, { bold: true, color: FG.body }, `\u26C1 ${formatCost(card.sessionCost, card.balanceCurrency, 3)}`), card.balance !== void 0 ? /* @__PURE__ */ React49.createElement(React49.Fragment, null, /* @__PURE__ */ React49.createElement(Text43, { color: FG.faint }, `\xB7 ${t("cardLabels.balance")}`), /* @__PURE__ */ React49.createElement(Text43, { bold: true, color: TONE.brand }, formatBalance(card.balance, card.balanceCurrency))) : null));
9015
9125
  }
9016
9126
  function CompactUsageRow({ card }) {
9017
9127
  const elapsed = card.elapsedMs !== void 0 ? ` \xB7 ${(card.elapsedMs / 1e3).toFixed(1)}s` : "";
9018
- return /* @__PURE__ */ React49.createElement(Box42, { flexDirection: "row", gap: 1, marginTop: 1 }, /* @__PURE__ */ React49.createElement(Text43, { color: FG.meta }, "\u03A3"), /* @__PURE__ */ React49.createElement(Text43, { color: FG.faint }, `turn ${card.turn}`), /* @__PURE__ */ React49.createElement(Text43, { color: FG.meta }, `\xB7 ${compactNum(card.tokens.prompt)} prompt \xB7 ${compactNum(card.tokens.output)} out`), /* @__PURE__ */ React49.createElement(Text43, { color: FG.faint }, "\xB7 cache"), /* @__PURE__ */ React49.createElement(Text43, { color: TONE.ok }, `${(card.cacheHit * 100).toFixed(0)}%`), /* @__PURE__ */ React49.createElement(Text43, { color: FG.faint }, `\xB7 ${formatCost(card.cost, card.balanceCurrency)}${elapsed}`), card.balance !== void 0 ? /* @__PURE__ */ React49.createElement(Text43, { color: TONE.brand }, `\xB7 ${formatBalance(card.balance, card.balanceCurrency)}`) : null);
9128
+ return /* @__PURE__ */ React49.createElement(Box42, { flexDirection: "row", gap: 1, marginTop: 1 }, /* @__PURE__ */ React49.createElement(Text43, { color: FG.meta }, "\u03A3"), /* @__PURE__ */ React49.createElement(Text43, { color: FG.faint }, `${t("cardLabels.turn")} ${card.turn}`), /* @__PURE__ */ React49.createElement(Text43, { color: FG.meta }, `\xB7 ${compactNum(card.tokens.prompt)} ${t("cardLabels.prompt")} \xB7 ${compactNum(card.tokens.output)} ${t("cardLabels.output")}`), /* @__PURE__ */ React49.createElement(Text43, { color: FG.faint }, `\xB7 ${t("cardLabels.cache")}`), /* @__PURE__ */ React49.createElement(Text43, { color: TONE.ok }, `${(card.cacheHit * 100).toFixed(0)}%`), /* @__PURE__ */ React49.createElement(Text43, { color: FG.faint }, `\xB7 ${formatCost(card.cost, card.balanceCurrency)}${elapsed}`), card.balance !== void 0 ? /* @__PURE__ */ React49.createElement(Text43, { color: TONE.brand }, `\xB7 ${formatBalance(card.balance, card.balanceCurrency)}`) : null);
9019
9129
  }
9020
9130
 
9021
9131
  // src/cli/ui/cards/UserCard.tsx
@@ -9338,7 +9448,7 @@ function summarizeToolArgs(name, args) {
9338
9448
  }
9339
9449
 
9340
9450
  // src/cli/ui/layout/StatusRow.tsx
9341
- import { Box as Box46, Text as Text49, useStdout as useStdout12 } from "ink";
9451
+ import { Box as Box46, Text as Text49, useStdout as useStdout13 } from "ink";
9342
9452
  import React56 from "react";
9343
9453
 
9344
9454
  // src/cli/ui/primitives/Countdown.tsx
@@ -9359,21 +9469,28 @@ var FEEDBACK_HINT_MIN_COLS = 100;
9359
9469
  function StatusRow() {
9360
9470
  const status2 = useAgentState((s) => s.status);
9361
9471
  const session = useAgentState((s) => s.session);
9362
- const { stdout } = useStdout12();
9472
+ const { stdout } = useStdout13();
9363
9473
  const cols = stdout?.columns ?? 80;
9364
9474
  const ruleWidth = Math.max(RULE_MIN, cols - RULE_PAD);
9365
9475
  const hasTurn = status2.cost > 0;
9366
9476
  const hasSession = status2.sessionCost > 0;
9367
9477
  const hasBalance = typeof status2.balance === "number";
9368
9478
  const showWallet = cols >= WALLET_MIN_COLS && (hasSession || hasBalance);
9369
- return /* @__PURE__ */ React56.createElement(Box46, { flexDirection: "column" }, /* @__PURE__ */ React56.createElement(Box46, null, /* @__PURE__ */ React56.createElement(Text49, null, " "), /* @__PURE__ */ React56.createElement(Text49, { color: FG.faint }, "\u2500".repeat(ruleWidth))), /* @__PURE__ */ React56.createElement(Box46, { flexDirection: "row" }, /* @__PURE__ */ React56.createElement(Text49, null, " "), status2.recording ? /* @__PURE__ */ React56.createElement(RecordingPill, { rec: status2.recording }) : status2.countdownSeconds !== void 0 ? /* @__PURE__ */ React56.createElement(CountdownRow, { mode: status2.mode, secondsLeft: status2.countdownSeconds }) : /* @__PURE__ */ React56.createElement(ModePill2, { mode: status2.mode, network: status2.network, detail: status2.networkDetail }), /* @__PURE__ */ React56.createElement(Sep, null), /* @__PURE__ */ React56.createElement(Text49, { color: FG.sub }, `${session.id} \xB7 ${session.branch}`), hasTurn && /* @__PURE__ */ React56.createElement(React56.Fragment, null, /* @__PURE__ */ React56.createElement(Sep, null), /* @__PURE__ */ React56.createElement(Text49, { bold: true, color: TONE.brand }, "\u25B8 "), /* @__PURE__ */ React56.createElement(Text49, { bold: true, color: FG.body }, `${formatCost(status2.cost, status2.balanceCurrency)} turn`)), /* @__PURE__ */ React56.createElement(Sep, null), /* @__PURE__ */ React56.createElement(Text49, { color: TONE.accent }, `cache ${Math.round(status2.cacheHit * 100)}%`), showWallet && /* @__PURE__ */ React56.createElement(
9479
+ return /* @__PURE__ */ React56.createElement(Box46, { flexDirection: "column", flexShrink: 0, flexWrap: "nowrap" }, /* @__PURE__ */ React56.createElement(Box46, { height: 1, flexWrap: "nowrap" }, /* @__PURE__ */ React56.createElement(Text49, null, " "), /* @__PURE__ */ React56.createElement(Text49, { color: FG.faint, wrap: "truncate" }, "\u2500".repeat(ruleWidth))), /* @__PURE__ */ React56.createElement(Box46, { flexDirection: "row", height: 1, minHeight: 1, flexWrap: "nowrap", flexShrink: 0 }, /* @__PURE__ */ React56.createElement(Text49, { wrap: "truncate" }, " "), status2.recording ? /* @__PURE__ */ React56.createElement(RecordingPill, { rec: status2.recording }) : status2.countdownSeconds !== void 0 ? /* @__PURE__ */ React56.createElement(CountdownRow, { mode: status2.mode, secondsLeft: status2.countdownSeconds }) : /* @__PURE__ */ React56.createElement(ModePill2, { mode: status2.mode, network: status2.network, detail: status2.networkDetail }), /* @__PURE__ */ React56.createElement(Sep, null), /* @__PURE__ */ React56.createElement(Text49, { color: FG.sub, wrap: "truncate" }, `${session.id} \xB7 ${session.branch}`), hasTurn && /* @__PURE__ */ React56.createElement(React56.Fragment, null, /* @__PURE__ */ React56.createElement(Sep, null), /* @__PURE__ */ React56.createElement(Text49, { bold: true, color: TONE.brand, wrap: "truncate" }, "\u25B8 "), /* @__PURE__ */ React56.createElement(Text49, { bold: true, color: FG.body, wrap: "truncate" }, `${formatCost(status2.cost, status2.balanceCurrency)} turn`)), /* @__PURE__ */ React56.createElement(Sep, null), /* @__PURE__ */ React56.createElement(
9480
+ Text49,
9481
+ {
9482
+ color: TONE.accent,
9483
+ wrap: "truncate"
9484
+ },
9485
+ `cache ${Math.round(status2.cacheHit * 100)}%`
9486
+ ), showWallet && /* @__PURE__ */ React56.createElement(
9370
9487
  WalletPill,
9371
9488
  {
9372
9489
  sessionCostUsd: status2.sessionCost,
9373
9490
  balance: status2.balance,
9374
9491
  currency: status2.balanceCurrency
9375
9492
  }
9376
- ), cols >= VERSION_MIN_COLS && /* @__PURE__ */ React56.createElement(React56.Fragment, null, /* @__PURE__ */ React56.createElement(Sep, null), /* @__PURE__ */ React56.createElement(Text49, { color: FG.faint }, `v${VERSION}`), cols >= FEEDBACK_HINT_MIN_COLS && /* @__PURE__ */ React56.createElement(React56.Fragment, null, /* @__PURE__ */ React56.createElement(Text49, { color: FG.faint }, " \xB7 "), /* @__PURE__ */ React56.createElement(Text49, { color: FG.meta }, "\u2691 "), /* @__PURE__ */ React56.createElement(Text49, { color: FG.sub }, "/feedback")))));
9493
+ ), cols >= VERSION_MIN_COLS && /* @__PURE__ */ React56.createElement(React56.Fragment, null, /* @__PURE__ */ React56.createElement(Sep, null), /* @__PURE__ */ React56.createElement(Text49, { color: FG.faint, wrap: "truncate" }, `v${VERSION}`), cols >= FEEDBACK_HINT_MIN_COLS && /* @__PURE__ */ React56.createElement(React56.Fragment, null, /* @__PURE__ */ React56.createElement(Text49, { color: FG.faint, wrap: "truncate" }, " \xB7 "), /* @__PURE__ */ React56.createElement(Text49, { color: FG.meta, wrap: "truncate" }, "\u2691 "), /* @__PURE__ */ React56.createElement(Text49, { color: FG.sub, wrap: "truncate" }, "/feedback")))));
9377
9494
  }
9378
9495
  function WalletPill({
9379
9496
  sessionCostUsd,
@@ -9382,7 +9499,14 @@ function WalletPill({
9382
9499
  }) {
9383
9500
  const showSpent = sessionCostUsd > 0;
9384
9501
  const showBalance = typeof balance === "number";
9385
- return /* @__PURE__ */ React56.createElement(React56.Fragment, null, /* @__PURE__ */ React56.createElement(Sep, null), /* @__PURE__ */ React56.createElement(Text49, { color: FG.meta }, "\u26C1 "), showSpent && /* @__PURE__ */ React56.createElement(Text49, { color: FG.body }, `${formatCost(sessionCostUsd, currency, 2)} spent`), showSpent && showBalance && /* @__PURE__ */ React56.createElement(Text49, { color: FG.meta }, " / "), showBalance && /* @__PURE__ */ React56.createElement(Text49, { bold: true, color: balanceColor(balance, currency) }, formatBalance(balance, currency, { fractionDigits: 2 })), showBalance && /* @__PURE__ */ React56.createElement(Text49, { color: FG.faint }, " left"));
9502
+ return /* @__PURE__ */ React56.createElement(React56.Fragment, null, /* @__PURE__ */ React56.createElement(Sep, null), /* @__PURE__ */ React56.createElement(Text49, { color: FG.meta, wrap: "truncate" }, "\u26C1 "), showSpent && /* @__PURE__ */ React56.createElement(
9503
+ Text49,
9504
+ {
9505
+ color: FG.body,
9506
+ wrap: "truncate"
9507
+ },
9508
+ `${formatCost(sessionCostUsd, currency, 2)} spent`
9509
+ ), showSpent && showBalance && /* @__PURE__ */ React56.createElement(Text49, { color: FG.meta, wrap: "truncate" }, " / "), showBalance && /* @__PURE__ */ React56.createElement(Text49, { bold: true, color: balanceColor(balance, currency), wrap: "truncate" }, formatBalance(balance, currency, { fractionDigits: 2 })), showBalance && /* @__PURE__ */ React56.createElement(Text49, { color: FG.faint, wrap: "truncate" }, " left"));
9386
9510
  }
9387
9511
  function ModePill2({
9388
9512
  mode: mode2,
@@ -9391,18 +9515,18 @@ function ModePill2({
9391
9515
  }) {
9392
9516
  if (network === "online") {
9393
9517
  const pill = modeGlyph(mode2);
9394
- return /* @__PURE__ */ React56.createElement(Box46, { flexDirection: "row" }, /* @__PURE__ */ React56.createElement(Text49, { color: pill.color }, pill.glyph), /* @__PURE__ */ React56.createElement(Text49, { color: FG.sub }, ` ${mode2}`));
9518
+ return /* @__PURE__ */ React56.createElement(Box46, { flexDirection: "row", height: 1, flexWrap: "nowrap" }, /* @__PURE__ */ React56.createElement(Text49, { color: pill.color, wrap: "truncate" }, pill.glyph), /* @__PURE__ */ React56.createElement(Text49, { color: FG.sub, wrap: "truncate" }, ` ${mode2}`));
9395
9519
  }
9396
9520
  const dot = networkDot(network);
9397
9521
  if (network === "slow") {
9398
9522
  const tail = detail ? ` \xB7 ${detail}` : "";
9399
- return /* @__PURE__ */ React56.createElement(Box46, { flexDirection: "row" }, /* @__PURE__ */ React56.createElement(Text49, { color: dot.color }, dot.glyph), /* @__PURE__ */ React56.createElement(Text49, { color: dot.color }, ` ${mode2} \xB7 slow${tail}`));
9523
+ return /* @__PURE__ */ React56.createElement(Box46, { flexDirection: "row", height: 1, flexWrap: "nowrap" }, /* @__PURE__ */ React56.createElement(Text49, { color: dot.color, wrap: "truncate" }, dot.glyph), /* @__PURE__ */ React56.createElement(Text49, { color: dot.color, wrap: "truncate" }, ` ${mode2} \xB7 slow${tail}`));
9400
9524
  }
9401
9525
  if (network === "disconnected") {
9402
9526
  const tail = detail ? ` \xB7 ${detail}` : "";
9403
- return /* @__PURE__ */ React56.createElement(Box46, { flexDirection: "row" }, /* @__PURE__ */ React56.createElement(Text49, { color: dot.color }, dot.glyph), /* @__PURE__ */ React56.createElement(Text49, { color: dot.color }, ` disconnect${tail}`));
9527
+ return /* @__PURE__ */ React56.createElement(Box46, { flexDirection: "row", height: 1, flexWrap: "nowrap" }, /* @__PURE__ */ React56.createElement(Text49, { color: dot.color, wrap: "truncate" }, dot.glyph), /* @__PURE__ */ React56.createElement(Text49, { color: dot.color, wrap: "truncate" }, ` disconnect${tail}`));
9404
9528
  }
9405
- return /* @__PURE__ */ React56.createElement(Box46, { flexDirection: "row" }, /* @__PURE__ */ React56.createElement(Text49, { color: dot.color }, dot.glyph), /* @__PURE__ */ React56.createElement(Text49, { color: dot.color }, " reconnecting\u2026"));
9529
+ return /* @__PURE__ */ React56.createElement(Box46, { flexDirection: "row", height: 1, flexWrap: "nowrap" }, /* @__PURE__ */ React56.createElement(Text49, { color: dot.color, wrap: "truncate" }, dot.glyph), /* @__PURE__ */ React56.createElement(Text49, { color: dot.color, wrap: "truncate" }, " reconnecting\u2026"));
9406
9530
  }
9407
9531
  function CountdownRow({
9408
9532
  mode: mode2,
@@ -9410,14 +9534,14 @@ function CountdownRow({
9410
9534
  }) {
9411
9535
  const pill = modeGlyph(mode2);
9412
9536
  const endsAt = Date.now() + secondsLeft * 1e3;
9413
- return /* @__PURE__ */ React56.createElement(Box46, { flexDirection: "row" }, /* @__PURE__ */ React56.createElement(Text49, { color: pill.color }, pill.glyph), /* @__PURE__ */ React56.createElement(Text49, { color: FG.sub }, ` ${mode2} \xB7 `), /* @__PURE__ */ React56.createElement(Text49, { color: TONE.warn }, "approving in "), /* @__PURE__ */ React56.createElement(Countdown, { endsAt }), /* @__PURE__ */ React56.createElement(Text49, { color: TONE.warn }, "s \xB7 esc to interrupt"));
9537
+ return /* @__PURE__ */ React56.createElement(Box46, { flexDirection: "row", height: 1, flexWrap: "nowrap" }, /* @__PURE__ */ React56.createElement(Text49, { color: pill.color, wrap: "truncate" }, pill.glyph), /* @__PURE__ */ React56.createElement(Text49, { color: FG.sub, wrap: "truncate" }, ` ${mode2} \xB7 `), /* @__PURE__ */ React56.createElement(Text49, { color: TONE.warn, wrap: "truncate" }, "approving in "), /* @__PURE__ */ React56.createElement(Countdown, { endsAt }), /* @__PURE__ */ React56.createElement(Text49, { color: TONE.warn, wrap: "truncate" }, "s \xB7 esc to interrupt"));
9414
9538
  }
9415
9539
  function RecordingPill({ rec }) {
9416
9540
  const sizeMb = (rec.sizeBytes / (1024 * 1024)).toFixed(1);
9417
- return /* @__PURE__ */ React56.createElement(Box46, { flexDirection: "row" }, /* @__PURE__ */ React56.createElement(Text49, { bold: true, color: TONE.err }, "\u25CFREC"), /* @__PURE__ */ React56.createElement(Text49, { color: TONE.err }, ` ${sizeMb} MB \xB7 ${rec.events} evt`));
9541
+ return /* @__PURE__ */ React56.createElement(Box46, { flexDirection: "row", height: 1, flexWrap: "nowrap" }, /* @__PURE__ */ React56.createElement(Text49, { bold: true, color: TONE.err, wrap: "truncate" }, "\u25CFREC"), /* @__PURE__ */ React56.createElement(Text49, { color: TONE.err, wrap: "truncate" }, ` ${sizeMb} MB \xB7 ${rec.events} evt`));
9418
9542
  }
9419
9543
  function Sep() {
9420
- return /* @__PURE__ */ React56.createElement(Text49, { color: FG.meta }, " \xB7 ");
9544
+ return /* @__PURE__ */ React56.createElement(Text49, { color: FG.meta, wrap: "truncate" }, " \xB7 ");
9421
9545
  }
9422
9546
  function modeGlyph(mode2) {
9423
9547
  switch (mode2) {
@@ -9445,7 +9569,7 @@ function networkDot(state) {
9445
9569
  }
9446
9570
 
9447
9571
  // src/cli/ui/layout/ToastRail.tsx
9448
- import { Box as Box47, Text as Text50, useStdout as useStdout13 } from "ink";
9572
+ import { Box as Box47, Text as Text50, useStdout as useStdout14 } from "ink";
9449
9573
  import React57, { useEffect as useEffect8 } from "react";
9450
9574
  var TONE_COLOR = {
9451
9575
  ok: TONE.ok,
@@ -9468,7 +9592,7 @@ function ToastRail() {
9468
9592
  const toasts = useAgentState((s) => s.toasts);
9469
9593
  const dispatch = useDispatch();
9470
9594
  useSlowTick();
9471
- const { stdout } = useStdout13();
9595
+ const { stdout } = useStdout14();
9472
9596
  const cols = stdout?.columns ?? 80;
9473
9597
  const rule = "\u2501".repeat(Math.max(20, cols - 4));
9474
9598
  const now = Date.now();
@@ -9873,24 +9997,28 @@ var update = (_args, _loop, ctx) => {
9873
9997
  return { info: lines.join("\n") };
9874
9998
  }
9875
9999
  lines.push(t("handlers.admin.updateLatest", { version: latest }));
9876
- const diff = compareVersions(VERSION, latest);
9877
- if (diff >= 0) {
10000
+ if (compareVersions(VERSION, latest) >= 0) {
9878
10001
  lines.push("", t("handlers.admin.updateUpToDate"));
9879
10002
  return { info: lines.join("\n") };
9880
10003
  }
9881
- if (isNpxInstall()) {
10004
+ const installSource = detectInstallSource();
10005
+ const npmPrefix = installSource === "npm" ? detectNpmInstallPrefix() : null;
10006
+ const plan2 = planUpdate({ current: VERSION, latest, installSource, npmPrefix });
10007
+ if (plan2.action === "npx-hint") {
9882
10008
  lines.push("", t("handlers.admin.updateNpxHint"), t("handlers.admin.updateNpxForce"));
10009
+ return { info: lines.join("\n") };
10010
+ }
10011
+ lines.push("", t("handlers.admin.updateUpgradeHint"), t("handlers.admin.updateUpgradeCmd1"));
10012
+ if (plan2.action === "run-install" && plan2.command) {
10013
+ lines.push(t("handlers.admin.updateUpgradeCmd2", { command: plan2.command.join(" ") }));
9883
10014
  } else {
9884
- lines.push(
9885
- "",
9886
- t("handlers.admin.updateUpgradeHint"),
9887
- t("handlers.admin.updateUpgradeCmd1"),
9888
- t("handlers.admin.updateUpgradeCmd2"),
9889
- "",
9890
- t("handlers.admin.updateInSessionDisabled"),
9891
- t("handlers.admin.updateInSessionDisabled2")
9892
- );
10015
+ lines.push(...MANUAL_UPDATE_COMMANDS.map((c) => ` ${c}`));
9893
10016
  }
10017
+ lines.push(
10018
+ "",
10019
+ t("handlers.admin.updateInSessionDisabled"),
10020
+ t("handlers.admin.updateInSessionDisabled2")
10021
+ );
9894
10022
  return { info: lines.join("\n") };
9895
10023
  };
9896
10024
  var stats = () => {
@@ -11067,7 +11195,9 @@ function buildFeedbackDiagnostic(input) {
11067
11195
  }
11068
11196
  function formatVersion(installed, latest) {
11069
11197
  if (!latest) return installed;
11070
- if (latest === installed) return `${installed} (latest)`;
11198
+ const cmp = compareVersions(installed, latest);
11199
+ if (cmp === 0) return `${installed} (latest)`;
11200
+ if (cmp > 0) return installed;
11071
11201
  return `${installed} (latest: ${latest})`;
11072
11202
  }
11073
11203
  function formatModel(model2, effort) {
@@ -11715,6 +11845,31 @@ var handlers17 = {
11715
11845
  se: (args, loop2, ctx) => handlers17["search-engine"](args, loop2, ctx)
11716
11846
  };
11717
11847
 
11848
+ // src/cli/ui/slash/nearest.ts
11849
+ function nearestCommands(input, all, opts = {}) {
11850
+ if (!input) return [];
11851
+ const max = opts.max ?? 3;
11852
+ const maxDistance = Math.min(opts.maxDistance ?? 3, Math.floor(input.length / 2));
11853
+ if (max <= 0 || maxDistance <= 0) return [];
11854
+ return all.map((name) => ({ name, distance: levenshtein(input, name) })).filter((entry) => entry.distance <= maxDistance).sort((a, b) => a.distance - b.distance || a.name.localeCompare(b.name)).slice(0, max).map((entry) => entry.name);
11855
+ }
11856
+ function levenshtein(a, b) {
11857
+ if (a === b) return 0;
11858
+ if (!a) return b.length;
11859
+ if (!b) return a.length;
11860
+ let prev = Array.from({ length: b.length + 1 }, (_, i) => i);
11861
+ let next = new Array(b.length + 1).fill(0);
11862
+ for (let i = 0; i < a.length; i += 1) {
11863
+ next[0] = i + 1;
11864
+ for (let j = 0; j < b.length; j += 1) {
11865
+ const cost2 = a[i] === b[j] ? 0 : 1;
11866
+ next[j + 1] = Math.min((next[j] ?? 0) + 1, (prev[j + 1] ?? 0) + 1, (prev[j] ?? 0) + cost2);
11867
+ }
11868
+ [prev, next] = [next, prev];
11869
+ }
11870
+ return prev[b.length] ?? 0;
11871
+ }
11872
+
11718
11873
  // src/cli/ui/slash/dispatch.ts
11719
11874
  var HANDLERS = {
11720
11875
  ...handlers,
@@ -11738,6 +11893,11 @@ var HANDLERS = {
11738
11893
  function handleSlash(cmd, args, loop2, ctx = {}) {
11739
11894
  const h = HANDLERS[resolveSlashAlias(cmd)];
11740
11895
  if (h) return h(args, loop2, ctx);
11896
+ const suggestions = nearestCommands(cmd, Object.keys(HANDLERS));
11897
+ if (suggestions.length > 0) {
11898
+ const list2 = suggestions.map((name) => `/${name}`).join(", ");
11899
+ return { unknown: true, info: `unknown command: /${cmd} \u2014 did you mean ${list2}?` };
11900
+ }
11741
11901
  return { unknown: true, info: `unknown command: /${cmd} (try /help)` };
11742
11902
  }
11743
11903
 
@@ -11774,6 +11934,16 @@ var TurnTranslator = class {
11774
11934
  this.toolCardId = null;
11775
11935
  }
11776
11936
  }
11937
+ toolAbort(output) {
11938
+ if (this.toolCardId) {
11939
+ this.log.endTool(this.toolCardId, {
11940
+ output,
11941
+ elapsedMs: Date.now() - this.toolStartedAt,
11942
+ aborted: true
11943
+ });
11944
+ this.toolCardId = null;
11945
+ }
11946
+ }
11777
11947
  toolRetry(attempt, max) {
11778
11948
  if (this.toolCardId) this.log.retryTool(this.toolCardId, attempt, max);
11779
11949
  }
@@ -12658,9 +12828,9 @@ function useSubagent({
12658
12828
  // src/cli/ui/App.tsx
12659
12829
  var FLUSH_INTERVAL_MS = (() => {
12660
12830
  const raw = process.env.REASONIX_FLUSH_MS;
12661
- if (!raw) return 33;
12831
+ if (!raw) return 16;
12662
12832
  const parsed = Number(raw);
12663
- if (!Number.isFinite(parsed) || parsed < 16 || parsed > 1e3) return 33;
12833
+ if (!Number.isFinite(parsed) || parsed < 16 || parsed > 1e3) return 16;
12664
12834
  return Math.round(parsed);
12665
12835
  })();
12666
12836
  var PLAIN_UI = process.env.REASONIX_UI === "plain";
@@ -12702,7 +12872,8 @@ function AppInner({
12702
12872
  progressSink,
12703
12873
  codeMode,
12704
12874
  noDashboard,
12705
- onSwitchSession
12875
+ onSwitchSession,
12876
+ mouse = true
12706
12877
  }) {
12707
12878
  markPhase("app_inner_start");
12708
12879
  const log = useScrollback();
@@ -12711,6 +12882,7 @@ function AppInner({
12711
12882
  (s) => s.cards.some((c) => c.kind === "user" || c.kind === "streaming")
12712
12883
  );
12713
12884
  const isStreaming = useAgentState((s) => s.cards.some((c) => c.kind === "streaming" && !c.done));
12885
+ const activityLabel = useActivityLabel();
12714
12886
  const chatScroll = useChatScroll();
12715
12887
  const [input, setInput] = useState20("");
12716
12888
  const [busy, setBusy] = useState20(false);
@@ -12734,18 +12906,18 @@ function AppInner({
12734
12906
  }, [busy]);
12735
12907
  const [ongoingTool, setOngoingTool] = useState20(null);
12736
12908
  const [toolProgress, setToolProgress] = useState20(null);
12737
- const { stdout } = useStdout14();
12909
+ const { stdout } = useStdout15();
12738
12910
  useEffect12(() => {
12739
12911
  if (!stdout || !stdout.isTTY) return;
12740
12912
  stdout.write("\x1B[?2004h");
12741
12913
  stdout.write("\x1B[>4;2m");
12742
- stdout.write("\x1B[?1006h\x1B[?1000h");
12914
+ if (mouse) stdout.write("\x1B[?1007h");
12743
12915
  return () => {
12744
- stdout.write("\x1B[?1000l\x1B[?1006l");
12916
+ if (mouse) stdout.write("\x1B[?1007l");
12745
12917
  stdout.write("\x1B[?2004l");
12746
12918
  stdout.write("\x1B[>4m");
12747
12919
  };
12748
- }, [stdout]);
12920
+ }, [stdout, mouse]);
12749
12921
  const walletCurrencyRef = useRef9(void 0);
12750
12922
  const { activities: subagentActivities, sinkRef: subagentSinkRef } = useSubagent({
12751
12923
  session,
@@ -12894,7 +13066,7 @@ function AppInner({
12894
13066
  const loopRef = useRef9(null);
12895
13067
  const loop2 = useMemo9(() => {
12896
13068
  if (loopRef.current) return loopRef.current;
12897
- const client = new DeepSeekClient();
13069
+ const client = new DeepSeekClient({ baseUrl: loadBaseUrl() });
12898
13070
  if (tools && !tools.has("run_skill")) {
12899
13071
  registerSkillTools(tools, {
12900
13072
  projectRoot: codeMode?.rootDir,
@@ -13214,11 +13386,12 @@ function AppInner({
13214
13386
  }, [session, loop2, codeMode, syncPendingCount, log]);
13215
13387
  const quitProcess = useQuit(transcriptRef);
13216
13388
  useKeystroke((ev) => {
13389
+ const pickerOwnsArrows = (atState?.entries.length ?? 0) > 0 || (slashMatches?.length ?? 0) > 0 || (slashArgMatches?.length ?? 0) > 0 || pendingShell != null;
13217
13390
  if (ev.pageUp || ev.mouseScrollUp) chatScroll.scrollUp();
13218
13391
  else if (ev.pageDown || ev.mouseScrollDown) chatScroll.scrollDown();
13219
13392
  else if (ev.end) chatScroll.jumpToBottom();
13220
- else if ((!chatScroll.pinned || busy) && ev.upArrow) chatScroll.scrollUp();
13221
- else if ((!chatScroll.pinned || busy) && ev.downArrow) chatScroll.scrollDown();
13393
+ else if (!pickerOwnsArrows && ev.upArrow) chatScroll.scrollUp();
13394
+ else if (!pickerOwnsArrows && ev.downArrow) chatScroll.scrollDown();
13222
13395
  }, !modalOpen);
13223
13396
  useKeystroke((ev) => {
13224
13397
  const chKey = ev.input;
@@ -13433,7 +13606,7 @@ function AppInner({
13433
13606
  if (dashboardRef.current) return dashboardRef.current.url;
13434
13607
  if (dashboardStartingRef.current) return dashboardStartingRef.current;
13435
13608
  const startup = (async () => {
13436
- const { startDashboardServer } = await import("./server-SMLVXIW4.js");
13609
+ const { startDashboardServer } = await import("./server-W4XJK4GX.js");
13437
13610
  const handle = await startDashboardServer({
13438
13611
  mode: "attached",
13439
13612
  configPath: defaultConfigPath(),
@@ -13757,7 +13930,7 @@ function AppInner({
13757
13930
  }
13758
13931
  if (text.startsWith("/") && !text.includes(" ")) {
13759
13932
  const typed = text.slice(1).toLowerCase();
13760
- const matches = suggestSlashCommands(typed, !!codeMode);
13933
+ const matches = suggestSlashCommands(typed, !!codeMode, slashUsage);
13761
13934
  const exact = matches.find((m) => m.cmd === typed);
13762
13935
  if (!exact && matches.length > 0) {
13763
13936
  const chosen = matches[slashSelected] ?? matches[0];
@@ -14197,7 +14370,13 @@ function AppInner({
14197
14370
  codeModeOn: !!codeMode
14198
14371
  });
14199
14372
  } else if (ev.role === "error") {
14200
- handleErrorEvent(ev, { log });
14373
+ handleErrorEvent(ev, {
14374
+ log,
14375
+ setOngoingTool,
14376
+ setToolProgress,
14377
+ toolStartedAtRef,
14378
+ translator
14379
+ });
14201
14380
  } else if (ev.role === "warning") {
14202
14381
  handleWarningEvent(ev, { log, setTurnOnPro });
14203
14382
  }
@@ -14251,6 +14430,7 @@ function AppInner({
14251
14430
  planMode,
14252
14431
  session,
14253
14432
  slashSelected,
14433
+ slashUsage,
14254
14434
  atState,
14255
14435
  atSelected,
14256
14436
  pickAtMention,
@@ -14376,7 +14556,7 @@ function AppInner({
14376
14556
  }
14377
14557
  if (!staged) return;
14378
14558
  const trimmed = feedback2.trim();
14379
- let synthetic;
14559
+ const tail = trimmed.length > 50 ? `${trimmed.slice(0, 50)}\u2026` : trimmed;
14380
14560
  let marker;
14381
14561
  if (staged.mode === "approve") {
14382
14562
  togglePlanMode(false);
@@ -14394,19 +14574,7 @@ function AppInner({
14394
14574
  });
14395
14575
  persistPlanState();
14396
14576
  }
14397
- if (trimmed) {
14398
- synthetic = `The plan above has been approved. Implement it now. You are out of plan mode \u2014 use edit_file / write_file / run_command as needed.
14399
-
14400
- User's additional instructions / answers to your open questions:
14401
-
14402
- ${trimmed}
14403
-
14404
- Factor these in before the first edit. Stick to the plan unless you discover a concrete reason to deviate; if you do, tell me and wait for a response.`;
14405
- marker = `\u25B8 plan approved + instructions \u2014 ${trimmed.length > 50 ? `${trimmed.slice(0, 50)}\u2026` : trimmed}`;
14406
- } else {
14407
- synthetic = "The plan above has been approved. Implement it now. You are out of plan mode \u2014 use edit_file / write_file / run_command as needed. If the plan listed open questions and I didn't answer them, default to the safest interpretation and call them out in your first reply. Don't fabricate preferences \u2014 if a question is truly unanswerable without me, stop and ask.";
14408
- marker = "\u25B8 plan approved \u2014 implementing";
14409
- }
14577
+ marker = trimmed ? `\u25B8 plan approved + instructions \u2014 ${tail}` : "\u25B8 plan approved \u2014 implementing";
14410
14578
  } else if (staged.mode === "reject") {
14411
14579
  planStepsRef.current = null;
14412
14580
  completedStepIdsRef.current = /* @__PURE__ */ new Set();
@@ -14415,38 +14583,20 @@ Factor these in before the first edit. Stick to the plan unless you discover a c
14415
14583
  persistPlanState();
14416
14584
  togglePlanMode(false);
14417
14585
  agentStore.dispatch({ type: "plan.drop" });
14418
- if (trimmed) {
14419
- synthetic = `The plan was rejected. User's reason / what they actually want:
14420
-
14421
- ${trimmed}
14422
-
14423
- Drop the proposed plan entirely. Use this guidance to understand what the user is after \u2014 ask follow-up questions if anything is still unclear, otherwise propose a different plan that addresses it.`;
14424
- marker = `\u25B8 plan rejected \u2014 ${trimmed.length > 50 ? `${trimmed.slice(0, 50)}\u2026` : trimmed}`;
14425
- } else {
14426
- synthetic = "The plan was cancelled. Drop it entirely. Ask me what I actually want before proposing another plan or making any changes.";
14427
- marker = "\u25B8 plan cancelled";
14428
- }
14586
+ marker = trimmed ? `\u25B8 plan rejected \u2014 ${tail}` : "\u25B8 plan cancelled";
14429
14587
  } else {
14430
- if (trimmed) {
14431
- synthetic = `The plan needs refinement. User feedback / answers:
14432
-
14433
- ${trimmed}
14434
-
14435
- Stay in plan mode \u2014 address the feedback (explore more if needed), then submit an improved submit_plan call. Don't propose a near-identical plan unless you explain why the feedback doesn't apply.`;
14436
- marker = `\u25B8 refining \u2014 ${trimmed.length > 50 ? `${trimmed.slice(0, 50)}\u2026` : trimmed}`;
14437
- } else {
14438
- synthetic = "The plan needs refinement. The user saw your open questions / risks block and chose not to answer specifics. Pick the safest default for each open question, call those defaults out explicitly in the new plan, and submit the refined submit_plan. Do not re-ask \u2014 the user already saw the questions.";
14439
- marker = "\u25B8 refining \u2014 using safe defaults";
14440
- }
14588
+ marker = trimmed ? `\u25B8 refining \u2014 ${tail}` : "\u25B8 refining \u2014 using safe defaults";
14441
14589
  }
14590
+ log.pushInfo(marker);
14442
14591
  const gateId = pendingGateIdRef.current;
14443
14592
  if (gateId !== null) {
14593
+ const fb = trimmed || void 0;
14444
14594
  if (staged.mode === "approve") {
14445
- pauseGate.resolve(gateId, { type: "approve" });
14595
+ pauseGate.resolve(gateId, { type: "approve", feedback: fb });
14446
14596
  } else if (staged.mode === "reject") {
14447
- pauseGate.resolve(gateId, { type: "cancel" });
14597
+ pauseGate.resolve(gateId, { type: "cancel", feedback: fb });
14448
14598
  } else {
14449
- pauseGate.resolve(gateId, { type: "refine" });
14599
+ pauseGate.resolve(gateId, { type: "refine", feedback: fb });
14450
14600
  }
14451
14601
  }
14452
14602
  },
@@ -14740,7 +14890,7 @@ Stay in plan mode \u2014 address the feedback (explore more if needed), then sub
14740
14890
  dashboardUrl,
14741
14891
  languageVersion
14742
14892
  }
14743
- ) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !pendingReviseEditor && !pendingSessionsPicker && !pendingCheckpointPicker && !pendingMcpHub && !stagedInput && !pendingEditReview && ongoingTool ? /* @__PURE__ */ React60.createElement(OngoingToolRow, { tool: ongoingTool, progress: toolProgress }) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !pendingReviseEditor && !pendingSessionsPicker && !pendingCheckpointPicker && !pendingMcpHub && !stagedInput && !pendingEditReview && subagentActivities.length > 0 ? /* @__PURE__ */ React60.createElement(SubagentLiveStack, { activities: subagentActivities, max: 3 }) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !pendingReviseEditor && !pendingSessionsPicker && !pendingCheckpointPicker && !pendingMcpHub && !stagedInput && !pendingEditReview && !ongoingTool && statusLine ? /* @__PURE__ */ React60.createElement(ThinkingRow, { text: statusLine }) : null, !PLAIN_UI && undoBanner && !pendingShell && !pendingPlan && !pendingReviseEditor && !pendingSessionsPicker && !pendingCheckpointPicker && !pendingMcpHub && !stagedInput && !pendingEditReview && !pendingChoice && !stagedChoiceCustom && !pendingRevision && !stagedCheckpointRevise && !pendingCheckpoint ? /* @__PURE__ */ React60.createElement(UndoBanner, { banner: undoBanner }) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !pendingReviseEditor && !pendingSessionsPicker && !pendingCheckpointPicker && !pendingMcpHub && !stagedInput && !pendingEditReview && busy && !isStreaming && !ongoingTool && !statusLine ? /* @__PURE__ */ React60.createElement(ThinkingRow, { text: "processing\u2026" }) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !pendingReviseEditor && !pendingSessionsPicker && !pendingCheckpointPicker && !pendingMcpHub && !stagedInput && !pendingEditReview ? /* @__PURE__ */ React60.createElement(PlanLiveRow, null) : null, /* @__PURE__ */ React60.createElement(ToastRail, null)), stagedInput ? /* @__PURE__ */ React60.createElement(
14893
+ ) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !pendingReviseEditor && !pendingSessionsPicker && !pendingCheckpointPicker && !pendingMcpHub && !stagedInput && !pendingEditReview && ongoingTool ? /* @__PURE__ */ React60.createElement(OngoingToolRow, { tool: ongoingTool, progress: toolProgress }) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !pendingReviseEditor && !pendingSessionsPicker && !pendingCheckpointPicker && !pendingMcpHub && !stagedInput && !pendingEditReview && subagentActivities.length > 0 ? /* @__PURE__ */ React60.createElement(SubagentLiveStack, { activities: subagentActivities, max: 3 }) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !pendingReviseEditor && !pendingSessionsPicker && !pendingCheckpointPicker && !pendingMcpHub && !stagedInput && !pendingEditReview && !ongoingTool && statusLine ? /* @__PURE__ */ React60.createElement(ThinkingRow, { text: statusLine }) : null, !PLAIN_UI && undoBanner && !pendingShell && !pendingPlan && !pendingReviseEditor && !pendingSessionsPicker && !pendingCheckpointPicker && !pendingMcpHub && !stagedInput && !pendingEditReview && !pendingChoice && !stagedChoiceCustom && !pendingRevision && !stagedCheckpointRevise && !pendingCheckpoint ? /* @__PURE__ */ React60.createElement(UndoBanner, { banner: undoBanner }) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !pendingReviseEditor && !pendingSessionsPicker && !pendingCheckpointPicker && !pendingMcpHub && !stagedInput && !pendingEditReview && busy && !isStreaming && !ongoingTool && !statusLine ? /* @__PURE__ */ React60.createElement(ThinkingRow, { text: activityLabel }) : null, !PLAIN_UI && !pendingShell && !pendingPlan && !pendingReviseEditor && !pendingSessionsPicker && !pendingCheckpointPicker && !pendingMcpHub && !stagedInput && !pendingEditReview ? /* @__PURE__ */ React60.createElement(PlanLiveRow, null) : null, /* @__PURE__ */ React60.createElement(ToastRail, null)), stagedInput ? /* @__PURE__ */ React60.createElement(
14744
14894
  PlanRefineInput,
14745
14895
  {
14746
14896
  mode: stagedInput.mode,
@@ -14989,7 +15139,7 @@ Stay in plan mode \u2014 address the feedback (explore more if needed), then sub
14989
15139
  block: pendingEdits.current[0],
14990
15140
  onChoose: handleWalkChoice
14991
15141
  }
14992
- ) : !chatScroll.pinned ? /* @__PURE__ */ React60.createElement(Text52, { color: FG.faint }, " \u{1F4D6} reading history \u2014 End / PgDn to return \xB7 \u2193 to advance one line") : /* @__PURE__ */ React60.createElement(React60.Fragment, null, codeMode ? /* @__PURE__ */ React60.createElement(
15142
+ ) : !chatScroll.pinned ? /* @__PURE__ */ React60.createElement(Text52, { color: FG.faint }, " \u{1F4D6} reading history \u2014 End / PgDn to return \xB7 \u2193 to advance one line") : /* @__PURE__ */ React60.createElement(Box49, { flexDirection: "column", flexShrink: 0, flexWrap: "nowrap" }, /* @__PURE__ */ React60.createElement(Box49, { flexDirection: "column", flexShrink: 0, flexWrap: "nowrap" }, codeMode ? /* @__PURE__ */ React60.createElement(
14993
15143
  ModeStatusBar,
14994
15144
  {
14995
15145
  editMode,
@@ -15009,15 +15159,16 @@ Stay in plan mode \u2014 address the feedback (explore more if needed), then sub
15009
15159
  onHistoryPrev: recallPrev,
15010
15160
  onHistoryNext: recallNext
15011
15161
  }
15012
- ), slashMatches !== null ? /* @__PURE__ */ React60.createElement(
15162
+ )), /* @__PURE__ */ React60.createElement(Box49, { flexDirection: "column", flexShrink: 0, flexWrap: "nowrap" }, slashMatches !== null ? /* @__PURE__ */ React60.createElement(
15013
15163
  SlashSuggestions,
15014
15164
  {
15165
+ key: `slash-suggestions:${slashGroupMode ? "group" : "search"}`,
15015
15166
  matches: slashMatches,
15016
15167
  selectedIndex: slashSelected,
15017
15168
  groupMode: slashGroupMode,
15018
15169
  advancedHidden: slashAdvancedHidden
15019
15170
  }
15020
- ) : null, atState !== null ? /* @__PURE__ */ React60.createElement(AtMentionSuggestions, { state: atState, selectedIndex: atSelected }) : null, slashArgContext ? /* @__PURE__ */ React60.createElement(
15171
+ ) : null, atState !== null ? /* @__PURE__ */ React60.createElement(AtMentionSuggestions, { state: atState, selectedIndex: atSelected }) : null), slashArgContext ? /* @__PURE__ */ React60.createElement(
15021
15172
  SlashArgPicker,
15022
15173
  {
15023
15174
  matches: slashArgMatches,
@@ -15089,7 +15240,7 @@ function Setup({ onReady }) {
15089
15240
  return;
15090
15241
  }
15091
15242
  if (!isPlausibleKey(trimmed)) {
15092
- setError("Doesn't look like a DeepSeek key. They start with 'sk-' and are 30+ chars.");
15243
+ setError("Key looks too short \u2014 paste the full token (16+ chars, no spaces).");
15093
15244
  setValue("");
15094
15245
  return;
15095
15246
  }
@@ -15382,6 +15533,7 @@ function Root({
15382
15533
  progressSink,
15383
15534
  codeMode: appProps.codeMode,
15384
15535
  noDashboard: appProps.noDashboard,
15536
+ mouse: appProps.mouse,
15385
15537
  onSwitchSession: setActiveSession
15386
15538
  }
15387
15539
  ));
@@ -15456,7 +15608,12 @@ async function chatCommand(opts) {
15456
15608
  exitOnCtrlC: true,
15457
15609
  // patchConsole:false — winpty/MINTTY redraw-glitch source.
15458
15610
  patchConsole: false,
15459
- incrementalRendering: true,
15611
+ // incrementalRendering:false — Ink's diff drifts when stringWidth
15612
+ // misjudges CJK / emoji ZWJ width or when async terminal-event
15613
+ // bytes interleave mid-render, leaving residual rows. Full-frame
15614
+ // redraws cost more stdout bytes per flush but eliminate the
15615
+ // ghost class.
15616
+ incrementalRendering: false,
15460
15617
  // Default true — alt-screen is the only mode without scrollback-
15461
15618
  // reflow ghosting. `--no-alt-screen` opts back into scrollback mode
15462
15619
  // for users who need chat output preserved in shell history on exit.
@@ -15474,4 +15631,4 @@ async function chatCommand(opts) {
15474
15631
  export {
15475
15632
  chatCommand
15476
15633
  };
15477
- //# sourceMappingURL=chunk-GPHBJWCV.js.map
15634
+ //# sourceMappingURL=chunk-3OBWN2NH.js.map