reasonix 0.34.1 → 0.36.0

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 (91) hide show
  1. package/dashboard/app.css +16 -14
  2. package/dashboard/dist/app.js +77 -18
  3. package/dashboard/dist/app.js.map +1 -1
  4. package/dist/cli/{chat-TD6GR3QK.js → chat-RGMYAOY2.js} +18 -17
  5. package/dist/cli/{chunk-SA4UGZPG.js → chunk-2MCYGFLK.js} +15 -10
  6. package/dist/cli/chunk-2MCYGFLK.js.map +1 -0
  7. package/dist/cli/{chunk-SW3CCXEV.js → chunk-4Q3GRJIU.js} +2 -2
  8. package/dist/cli/{chunk-5JXXEPDM.js → chunk-BHLHOS5Y.js} +8 -2
  9. package/dist/cli/chunk-BHLHOS5Y.js.map +1 -0
  10. package/dist/cli/{chunk-F3ILWP2L.js → chunk-BJ376EN3.js} +9 -8
  11. package/dist/cli/chunk-BJ376EN3.js.map +1 -0
  12. package/dist/cli/{chunk-2AWTGJ2C.js → chunk-CRPQUBP6.js} +26 -9
  13. package/dist/cli/{chunk-2AWTGJ2C.js.map → chunk-CRPQUBP6.js.map} +1 -1
  14. package/dist/cli/{chunk-EINEIIIW.js → chunk-EN4LAZW5.js} +563 -250
  15. package/dist/cli/chunk-EN4LAZW5.js.map +1 -0
  16. package/dist/cli/{chunk-OERAGRJX.js → chunk-IPCPEZWQ.js} +2 -2
  17. package/dist/cli/{chunk-U3V2ZQ5J.js → chunk-KJQIA4US.js} +6 -2
  18. package/dist/cli/chunk-KJQIA4US.js.map +1 -0
  19. package/dist/cli/{chunk-Q36KBLSU.js → chunk-MLXUGPJE.js} +292 -35
  20. package/dist/cli/chunk-MLXUGPJE.js.map +1 -0
  21. package/dist/cli/{chunk-SX6L4HZZ.js → chunk-QPNZWUZF.js} +53 -6
  22. package/dist/cli/chunk-QPNZWUZF.js.map +1 -0
  23. package/dist/cli/{chunk-LNTORE5K.js → chunk-QRUQ2BFT.js} +159 -40
  24. package/dist/cli/chunk-QRUQ2BFT.js.map +1 -0
  25. package/dist/cli/{chunk-KZHMKOJH.js → chunk-T52GAWPP.js} +25 -3
  26. package/dist/cli/chunk-T52GAWPP.js.map +1 -0
  27. package/dist/cli/{chunk-I6YIAK6C.js → chunk-UNMYFZPZ.js} +2 -2
  28. package/dist/cli/{update-4TJWRUIN.js → chunk-WJ3YX4PZ.js} +51 -12
  29. package/dist/cli/chunk-WJ3YX4PZ.js.map +1 -0
  30. package/dist/cli/{chunk-RXGEGA7K.js → chunk-XQIFIB3U.js} +18 -7
  31. package/dist/cli/{chunk-RXGEGA7K.js.map → chunk-XQIFIB3U.js.map} +1 -1
  32. package/dist/cli/{chunk-2EBODRRO.js → chunk-ZJR4QLXB.js} +5 -1
  33. package/dist/cli/{chunk-2EBODRRO.js.map → chunk-ZJR4QLXB.js.map} +1 -1
  34. package/dist/cli/{chunk-6TMHAK5D.js → chunk-ZU45XW3P.js} +2 -2
  35. package/dist/cli/code-KJB3WDU6.js +435 -0
  36. package/dist/cli/code-KJB3WDU6.js.map +1 -0
  37. package/dist/cli/{commands-MEZPSEHV.js → commands-FE2UDFBC.js} +3 -3
  38. package/dist/cli/{commit-CE4EFTUQ.js → commit-3IAGB22T.js} +5 -4
  39. package/dist/cli/commit-3IAGB22T.js.map +1 -0
  40. package/dist/cli/{doctor-YASM64X6.js → doctor-DKD34EFD.js} +7 -7
  41. package/dist/cli/index.js +35 -33
  42. package/dist/cli/index.js.map +1 -1
  43. package/dist/cli/{mcp-LDFK5QJI.js → mcp-2RDEQST6.js} +2 -2
  44. package/dist/cli/{mcp-browse-FYHEITCM.js → mcp-browse-VM5GLRBQ.js} +2 -2
  45. package/dist/cli/{mcp-inspect-T2HBR22P.js → mcp-inspect-CWSVCZUQ.js} +3 -3
  46. package/dist/cli/{prompt-V47QKSAR.js → prompt-YEKXMNNV.js} +3 -3
  47. package/dist/cli/{replay-JEDLU7F2.js → replay-D7RT2DR7.js} +3 -3
  48. package/dist/cli/replay-D7RT2DR7.js.map +1 -0
  49. package/dist/cli/{run-NHD2RSTD.js → run-FK5UBIIM.js} +13 -12
  50. package/dist/cli/run-FK5UBIIM.js.map +1 -0
  51. package/dist/cli/{server-MC4A4WAJ.js → server-W4XJK4GX.js} +17 -17
  52. package/dist/cli/{server-MC4A4WAJ.js.map → server-W4XJK4GX.js.map} +1 -1
  53. package/dist/cli/{sessions-ZHWJEW4L.js → sessions-YZXWMIWW.js} +10 -10
  54. package/dist/cli/{setup-DK43MT47.js → setup-IIAJXHP4.js} +196 -130
  55. package/dist/cli/setup-IIAJXHP4.js.map +1 -0
  56. package/dist/cli/update-GUCWB4UN.js +13 -0
  57. package/dist/cli/update-GUCWB4UN.js.map +1 -0
  58. package/dist/cli/{version-O362UKPM.js → version-DWD6RLIU.js} +12 -12
  59. package/dist/index.d.ts +19 -2
  60. package/dist/index.js +543 -78
  61. package/dist/index.js.map +1 -1
  62. package/package.json +1 -1
  63. package/dist/cli/chunk-5JXXEPDM.js.map +0 -1
  64. package/dist/cli/chunk-EINEIIIW.js.map +0 -1
  65. package/dist/cli/chunk-F3ILWP2L.js.map +0 -1
  66. package/dist/cli/chunk-KZHMKOJH.js.map +0 -1
  67. package/dist/cli/chunk-LNTORE5K.js.map +0 -1
  68. package/dist/cli/chunk-Q36KBLSU.js.map +0 -1
  69. package/dist/cli/chunk-SA4UGZPG.js.map +0 -1
  70. package/dist/cli/chunk-SX6L4HZZ.js.map +0 -1
  71. package/dist/cli/chunk-U3V2ZQ5J.js.map +0 -1
  72. package/dist/cli/code-TGUOQBRJ.js +0 -153
  73. package/dist/cli/code-TGUOQBRJ.js.map +0 -1
  74. package/dist/cli/commit-CE4EFTUQ.js.map +0 -1
  75. package/dist/cli/replay-JEDLU7F2.js.map +0 -1
  76. package/dist/cli/run-NHD2RSTD.js.map +0 -1
  77. package/dist/cli/setup-DK43MT47.js.map +0 -1
  78. package/dist/cli/update-4TJWRUIN.js.map +0 -1
  79. /package/dist/cli/{chat-TD6GR3QK.js.map → chat-RGMYAOY2.js.map} +0 -0
  80. /package/dist/cli/{chunk-SW3CCXEV.js.map → chunk-4Q3GRJIU.js.map} +0 -0
  81. /package/dist/cli/{chunk-OERAGRJX.js.map → chunk-IPCPEZWQ.js.map} +0 -0
  82. /package/dist/cli/{chunk-I6YIAK6C.js.map → chunk-UNMYFZPZ.js.map} +0 -0
  83. /package/dist/cli/{chunk-6TMHAK5D.js.map → chunk-ZU45XW3P.js.map} +0 -0
  84. /package/dist/cli/{commands-MEZPSEHV.js.map → commands-FE2UDFBC.js.map} +0 -0
  85. /package/dist/cli/{doctor-YASM64X6.js.map → doctor-DKD34EFD.js.map} +0 -0
  86. /package/dist/cli/{mcp-LDFK5QJI.js.map → mcp-2RDEQST6.js.map} +0 -0
  87. /package/dist/cli/{mcp-browse-FYHEITCM.js.map → mcp-browse-VM5GLRBQ.js.map} +0 -0
  88. /package/dist/cli/{mcp-inspect-T2HBR22P.js.map → mcp-inspect-CWSVCZUQ.js.map} +0 -0
  89. /package/dist/cli/{prompt-V47QKSAR.js.map → prompt-YEKXMNNV.js.map} +0 -0
  90. /package/dist/cli/{sessions-ZHWJEW4L.js.map → sessions-YZXWMIWW.js.map} +0 -0
  91. /package/dist/cli/{version-O362UKPM.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-LNTORE5K.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,
@@ -50,12 +50,12 @@ import {
50
50
  } from "./chunk-XHQIK7B6.js";
51
51
  import {
52
52
  MemoryStore
53
- } from "./chunk-6TMHAK5D.js";
53
+ } from "./chunk-ZU45XW3P.js";
54
54
  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-F3ILWP2L.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,29 +101,29 @@ import {
97
101
  resolveSlashAlias,
98
102
  savePlanState,
99
103
  suggestSlashCommands
100
- } from "./chunk-SA4UGZPG.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,
120
124
  memoryEnabled,
121
125
  readProjectMemory
122
- } from "./chunk-U3V2ZQ5J.js";
126
+ } from "./chunk-KJQIA4US.js";
123
127
  import {
124
128
  HOOK_EVENTS,
125
129
  formatHookOutcomeMessage,
@@ -127,13 +131,7 @@ import {
127
131
  loadHooks,
128
132
  projectSettingsPath,
129
133
  runHooks
130
- } from "./chunk-OERAGRJX.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-Q36KBLSU.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
@@ -2376,7 +2382,12 @@ function PlanStepListInner({ steps, statuses, focusStepId }) {
2376
2382
  const doneCount = statusList.filter((s) => s === "done").length;
2377
2383
  const pct = Math.round(doneCount / total * 100);
2378
2384
  const showProgress = doneCount > 0;
2379
- return /* @__PURE__ */ React15.createElement(Box12, { flexDirection: "column" }, /* @__PURE__ */ React15.createElement(Box12, null, /* @__PURE__ */ React15.createElement(Text13, { dimColor: true }, showProgress ? `${doneCount}/${total} done (${pct}%) \xB7 ${total} step${total === 1 ? "" : "s"}` : `${total} step${total === 1 ? "" : "s"}`)), /* @__PURE__ */ React15.createElement(Box12, { flexDirection: "column" }, steps.map((step, i) => {
2385
+ return /* @__PURE__ */ React15.createElement(Box12, { flexDirection: "column" }, /* @__PURE__ */ React15.createElement(Box12, null, /* @__PURE__ */ React15.createElement(Text13, { dimColor: true }, showProgress ? t(
2386
+ total === 1 ? "planFlow.stepList.counterDoneSingular" : "planFlow.stepList.counterDone",
2387
+ { done: doneCount, total, pct }
2388
+ ) : t(total === 1 ? "planFlow.stepList.counterSingular" : "planFlow.stepList.counter", {
2389
+ total
2390
+ }))), /* @__PURE__ */ React15.createElement(Box12, { flexDirection: "column" }, steps.map((step, i) => {
2380
2391
  const status2 = statusList[i];
2381
2392
  const isLast = i === total - 1;
2382
2393
  const isCur = focusStepId === step.id;
@@ -2413,25 +2424,25 @@ function PlanCheckpointConfirmInner({
2413
2424
  const isLast = total > 0 && completed >= total;
2414
2425
  const statuses = buildStatusMap(steps, completedStepIds, stepId, isLast);
2415
2426
  const subtitle = counter ? `${counter} \xB7 ${label}` : label;
2416
- return /* @__PURE__ */ React16.createElement(ApprovalCard, { tone: "ok", glyph: "\u26C1", title: "Checkpoint \u2014 step done", metaRight: subtitle }, steps && steps.length > 0 ? /* @__PURE__ */ React16.createElement(Box13, { marginBottom: 1, flexDirection: "column" }, /* @__PURE__ */ React16.createElement(PlanStepList, { steps, statuses, focusStepId: stepId })) : null, /* @__PURE__ */ React16.createElement(
2427
+ return /* @__PURE__ */ React16.createElement(ApprovalCard, { tone: "ok", glyph: "\u26C1", title: t("planFlow.checkpoint.title"), metaRight: subtitle }, steps && steps.length > 0 ? /* @__PURE__ */ React16.createElement(Box13, { marginBottom: 1, flexDirection: "column" }, /* @__PURE__ */ React16.createElement(PlanStepList, { steps, statuses, focusStepId: stepId })) : null, /* @__PURE__ */ React16.createElement(
2417
2428
  SingleSelect,
2418
2429
  {
2419
2430
  initialValue: isLast ? "stop" : "continue",
2420
2431
  items: [
2421
2432
  {
2422
2433
  value: "continue",
2423
- label: "Continue \u2014 run the next step",
2424
- hint: "Model resumes with the next step."
2434
+ label: t("planFlow.checkpoint.continue"),
2435
+ hint: t("planFlow.checkpoint.continueHint")
2425
2436
  },
2426
2437
  {
2427
2438
  value: "revise",
2428
- label: "Revise \u2014 give feedback before the next step",
2429
- hint: "Stay paused, type guidance; model adjusts the remaining plan."
2439
+ label: t("planFlow.checkpoint.revise"),
2440
+ hint: t("planFlow.checkpoint.reviseHint")
2430
2441
  },
2431
2442
  {
2432
2443
  value: "stop",
2433
- label: "Stop \u2014 end the plan here",
2434
- hint: "Model summarizes what was done and ends."
2444
+ label: t("planFlow.checkpoint.stop"),
2445
+ hint: t("planFlow.checkpoint.stopHint")
2435
2446
  }
2436
2447
  ],
2437
2448
  onSubmit: (v) => onChoose(v),
@@ -4886,53 +4897,89 @@ function SpanText({
4886
4897
  );
4887
4898
  }
4888
4899
 
4900
+ // src/cli/ui/plan-open-questions.ts
4901
+ var HEADER_RE = /^(#{1,6})\s*(open[-\s]?questions?|risks?|unknowns?|assumptions?|unclear|待确认|开放问题|风险|未知|假设|不确定)(?:[\s::/、,,].*)?$/im;
4902
+ function extractOpenQuestionsSection(plan2) {
4903
+ const lines = plan2.split("\n");
4904
+ let startIdx = -1;
4905
+ let startLevel = 0;
4906
+ for (let i = 0; i < lines.length; i++) {
4907
+ const line = lines[i] ?? "";
4908
+ const m = line.match(HEADER_RE);
4909
+ if (m) {
4910
+ startIdx = i;
4911
+ startLevel = (m[1] ?? "#").length;
4912
+ break;
4913
+ }
4914
+ }
4915
+ if (startIdx === -1) return null;
4916
+ let endIdx = lines.length;
4917
+ for (let j = startIdx + 1; j < lines.length; j++) {
4918
+ const line = lines[j] ?? "";
4919
+ const lh = line.match(/^(#{1,6})\s+\S/);
4920
+ if (lh && (lh[1] ?? "").length <= startLevel) {
4921
+ endIdx = j;
4922
+ break;
4923
+ }
4924
+ }
4925
+ const block2 = lines.slice(startIdx, endIdx).join("\n").replace(/\s+$/g, "");
4926
+ return block2.length > 0 ? block2 : null;
4927
+ }
4928
+
4889
4929
  // src/cli/ui/PlanConfirm.tsx
4890
4930
  var PLAN_BODY_PREVIEW_LINES = 24;
4891
4931
  function PlanConfirmInner({ plan: plan2, steps, onChoose }) {
4892
4932
  const stepRows = steps?.length ?? 0;
4893
4933
  const hasSteps = stepRows > 0;
4934
+ const openQuestions = extractOpenQuestionsSection(plan2);
4894
4935
  const planLines = plan2.split("\n");
4895
4936
  const truncatedBody = planLines.length > PLAN_BODY_PREVIEW_LINES;
4896
4937
  const previewBody = truncatedBody ? planLines.slice(0, PLAN_BODY_PREVIEW_LINES).join("\n") : plan2;
4897
4938
  const previewRows = truncatedBody ? PLAN_BODY_PREVIEW_LINES : Math.min(planLines.length, PLAN_BODY_PREVIEW_LINES);
4898
4939
  const reservedFor = hasSteps ? stepRows : previewRows;
4899
- useReserveRows("modal", { min: 10, max: Math.max(16, reservedFor + 14) });
4900
- const hasOpenQuestions = /^#{1,6}\s*(open[-\s]?questions?|risks?|unknowns?|assumptions?|unclear)/im.test(plan2) || /^#{1,6}\s*(待确认|开放问题|风险|未知|假设|不确定)/im.test(plan2);
4940
+ const oqRows = openQuestions ? openQuestions.split("\n").length : 0;
4941
+ useReserveRows("modal", { min: 10, max: Math.max(16, reservedFor + oqRows + 14) });
4942
+ const refineLabel = t("planFlow.picker.refine");
4943
+ const bannerTemplate = t("planFlow.openQuestionsBanner");
4944
+ const [bannerBefore, bannerAfter] = bannerTemplate.split("{refine}");
4901
4945
  return /* @__PURE__ */ React18.createElement(
4902
4946
  ApprovalCard,
4903
4947
  {
4904
4948
  tone: "accent",
4905
4949
  glyph: "\u229E",
4906
- title: "Approve plan",
4907
- metaRight: "awaiting",
4950
+ title: t("planFlow.approveCardTitle"),
4951
+ metaRight: t("planFlow.approveCardMetaRight"),
4908
4952
  metaRightColor: CARD.plan.color
4909
4953
  },
4910
- hasOpenQuestions ? /* @__PURE__ */ React18.createElement(Box15, { marginBottom: 1 }, /* @__PURE__ */ React18.createElement(Text15, { color: TONE.warn }, "\u25B2 the plan flags open questions or risks \u2014 pick ", /* @__PURE__ */ React18.createElement(Text15, { bold: true }, "refine"), " to write concrete answers before the model moves on.")) : null,
4911
- hasSteps ? /* @__PURE__ */ React18.createElement(Box15, { marginBottom: 1, flexDirection: "column" }, /* @__PURE__ */ React18.createElement(PlanStepList, { steps })) : plan2.trim().length > 0 ? /* @__PURE__ */ React18.createElement(Box15, { marginBottom: 1, flexDirection: "column" }, /* @__PURE__ */ React18.createElement(MarkdownView, { text: previewBody }), truncatedBody ? /* @__PURE__ */ React18.createElement(Text15, { color: FG.faint }, `\u2026 ${planLines.length - PLAN_BODY_PREVIEW_LINES} more line${planLines.length - PLAN_BODY_PREVIEW_LINES === 1 ? "" : "s"} above in scrollback`) : null) : null,
4954
+ openQuestions ? /* @__PURE__ */ React18.createElement(Box15, { marginBottom: 1, flexDirection: "column" }, /* @__PURE__ */ React18.createElement(Text15, { color: TONE.warn }, bannerBefore ?? "", /* @__PURE__ */ React18.createElement(Text15, { bold: true }, refineLabel), bannerAfter ?? ""), /* @__PURE__ */ React18.createElement(Box15, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React18.createElement(Text15, { color: TONE.warn, bold: true }, t("planFlow.openQuestionsHeader")), /* @__PURE__ */ React18.createElement(MarkdownView, { text: openQuestions }))) : null,
4955
+ hasSteps ? /* @__PURE__ */ React18.createElement(Box15, { marginBottom: 1, flexDirection: "column" }, /* @__PURE__ */ React18.createElement(PlanStepList, { steps })) : plan2.trim().length > 0 ? /* @__PURE__ */ React18.createElement(Box15, { marginBottom: 1, flexDirection: "column" }, /* @__PURE__ */ React18.createElement(MarkdownView, { text: previewBody }), truncatedBody ? /* @__PURE__ */ React18.createElement(Text15, { color: FG.faint }, t(
4956
+ planLines.length - PLAN_BODY_PREVIEW_LINES === 1 ? "planFlow.truncatedBodyMore" : "planFlow.truncatedBodyMorePlural",
4957
+ { n: planLines.length - PLAN_BODY_PREVIEW_LINES }
4958
+ )) : null) : null,
4912
4959
  /* @__PURE__ */ React18.createElement(
4913
4960
  SingleSelect,
4914
4961
  {
4915
- initialValue: hasOpenQuestions ? "refine" : "approve",
4962
+ initialValue: openQuestions ? "refine" : "approve",
4916
4963
  items: [
4917
4964
  {
4918
4965
  value: "approve",
4919
- label: "accept",
4920
- hint: "run it now, in order"
4966
+ label: t("planFlow.picker.accept"),
4967
+ hint: t("planFlow.picker.acceptHint")
4921
4968
  },
4922
4969
  {
4923
4970
  value: "refine",
4924
- label: "refine",
4925
- hint: "give the agent more guidance, draft a new plan"
4971
+ label: refineLabel,
4972
+ hint: t("planFlow.picker.refineHint")
4926
4973
  },
4927
4974
  {
4928
4975
  value: "revise",
4929
- label: "revise",
4930
- hint: "edit the plan inline before running (skip / reorder steps)"
4976
+ label: t("planFlow.picker.revise"),
4977
+ hint: t("planFlow.picker.reviseHint")
4931
4978
  },
4932
4979
  {
4933
4980
  value: "cancel",
4934
- label: "reject",
4935
- hint: "discard, agent will retry from scratch"
4981
+ label: t("planFlow.picker.reject"),
4982
+ hint: t("planFlow.picker.rejectHint")
4936
4983
  }
4937
4984
  ],
4938
4985
  onSubmit: (v) => onChoose(v),
@@ -4974,49 +5021,25 @@ function useElapsedSeconds() {
4974
5021
  }
4975
5022
 
4976
5023
  // src/cli/ui/PlanRefineInput.tsx
4977
- var MODES = {
4978
- approve: {
4979
- title: "approving \u2014 any last instructions?",
4980
- glyph: "\u25C7",
4981
- tone: "user",
4982
- cursorColor: CARD.user.color,
4983
- hint: "Answer questions the plan raised, add constraints, or just press Enter to approve as-is.",
4984
- blankHint: " (Enter with blank = approve without extra instructions.)"
4985
- },
4986
- refine: {
4987
- title: "refining \u2014 what should the model change?",
4988
- glyph: "\u270E",
4989
- tone: "warn",
4990
- cursorColor: CARD.warn.color,
4991
- hint: "Describe what's wrong or missing, or answer questions the plan raised.",
4992
- blankHint: " (Enter with blank = ask the model to list concrete questions.)"
4993
- },
4994
- reject: {
4995
- title: "rejecting \u2014 tell the model why (optional)",
4996
- glyph: "\u2717",
4997
- tone: "error",
4998
- cursorColor: CARD.error.color,
4999
- hint: "Say what the model got wrong about your goal, or what you actually want instead.",
5000
- blankHint: " (Enter with blank = cancel without explanation; the model will ask what you want.)"
5001
- },
5002
- "checkpoint-revise": {
5003
- title: "revising \u2014 what should change before the next step?",
5004
- glyph: "\u270E",
5005
- tone: "warn",
5006
- cursorColor: CARD.warn.color,
5007
- hint: "Scope change, skip steps, alternative approach \u2014 the model adjusts the remaining plan.",
5008
- blankHint: " (Enter with blank = continue with the current plan.)"
5009
- },
5010
- "choice-custom": {
5011
- title: "custom answer \u2014 type whatever fits",
5012
- glyph: "\u2325",
5013
- tone: "accent",
5014
- cursorColor: CARD.plan.color,
5015
- hint: "Free-form reply. The model reads it verbatim and proceeds \u2014 no need to match the listed options.",
5016
- blankHint: " (Enter with blank = ask the model what you actually want.)"
5017
- }
5024
+ var MODE_VISUALS = {
5025
+ approve: { glyph: "\u25C7", tone: "user", cursorColor: CARD.user.color },
5026
+ refine: { glyph: "\u270E", tone: "warn", cursorColor: CARD.warn.color },
5027
+ reject: { glyph: "\u2717", tone: "error", cursorColor: CARD.error.color },
5028
+ "checkpoint-revise": { glyph: "\u270E", tone: "warn", cursorColor: CARD.warn.color },
5029
+ "choice-custom": { glyph: "\u2325", tone: "accent", cursorColor: CARD.plan.color }
5018
5030
  };
5019
- function PlanRefineInput({ mode: mode2, onSubmit, onCancel }) {
5031
+ function modeMeta(mode2) {
5032
+ const v = MODE_VISUALS[mode2];
5033
+ return {
5034
+ title: t(`planFlow.modes.${mode2}.title`),
5035
+ hint: t(`planFlow.modes.${mode2}.hint`),
5036
+ blankHint: t(`planFlow.modes.${mode2}.blankHint`),
5037
+ glyph: v.glyph,
5038
+ tone: v.tone,
5039
+ cursorColor: v.cursorColor
5040
+ };
5041
+ }
5042
+ function PlanRefineInput({ mode: mode2, questions, onSubmit, onCancel }) {
5020
5043
  const [value, setValue] = useState9("");
5021
5044
  useKeystroke((ev) => {
5022
5045
  if (ev.paste) {
@@ -5041,15 +5064,17 @@ function PlanRefineInput({ mode: mode2, onSubmit, onCancel }) {
5041
5064
  });
5042
5065
  const tick = useTick();
5043
5066
  const cursorOn = Math.floor(tick / 4) % 2 === 0;
5044
- const meta = MODES[mode2];
5067
+ const meta = modeMeta(mode2);
5068
+ const showQuestions = mode2 === "refine" && !!questions && questions.trim().length > 0;
5045
5069
  return /* @__PURE__ */ React20.createElement(
5046
5070
  ApprovalCard,
5047
5071
  {
5048
5072
  tone: meta.tone,
5049
5073
  glyph: meta.glyph,
5050
5074
  title: meta.title,
5051
- footerHint: "\u23CE send \xB7 esc return to picker"
5075
+ footerHint: t("planFlow.refineFooter")
5052
5076
  },
5077
+ showQuestions ? /* @__PURE__ */ React20.createElement(Box16, { marginBottom: 1, flexDirection: "column" }, /* @__PURE__ */ React20.createElement(Text16, { color: TONE.warn, bold: true }, t("planFlow.refineQuestionsHeading")), /* @__PURE__ */ React20.createElement(MarkdownView, { text: questions })) : null,
5053
5078
  /* @__PURE__ */ React20.createElement(Box16, { marginBottom: 1 }, /* @__PURE__ */ React20.createElement(Text16, { color: FG.sub }, meta.hint, value === "" ? meta.blankHint : "")),
5054
5079
  /* @__PURE__ */ React20.createElement(Box16, null, /* @__PURE__ */ React20.createElement(Text16, { color: meta.cursorColor, bold: true }, "\u203A "), /* @__PURE__ */ React20.createElement(Text16, null, value), /* @__PURE__ */ React20.createElement(Text16, { color: meta.cursorColor, bold: true }, cursorOn ? "\u258D" : " "))
5055
5080
  );
@@ -5303,9 +5328,17 @@ function processMultilineKey(value, cursor, keyIn) {
5303
5328
  return cursor === value.length ? NOOP : { next: null, cursor: value.length, submit: false };
5304
5329
  }
5305
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
+ }
5306
5335
  return { ...NOOP, historyHandoff: "prev" };
5307
5336
  }
5308
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
+ }
5309
5342
  return { ...NOOP, historyHandoff: "next" };
5310
5343
  }
5311
5344
  if (key.leftArrow) {
@@ -5314,15 +5347,8 @@ function processMultilineKey(value, cursor, keyIn) {
5314
5347
  if (key.rightArrow) {
5315
5348
  return { next: null, cursor: Math.min(value.length, cursor + 1), submit: false };
5316
5349
  }
5317
- if (key.upArrow) {
5318
- if (value.length === 0) return { ...NOOP, historyHandoff: "prev" };
5319
- const moved = moveCursorUp(value, cursor);
5320
- return moved === cursor ? NOOP : { next: null, cursor: moved, submit: false };
5321
- }
5322
- if (key.downArrow) {
5323
- if (value.length === 0) return { ...NOOP, historyHandoff: "next" };
5324
- const moved = moveCursorDown(value, cursor);
5325
- return moved === cursor ? NOOP : { next: null, cursor: moved, submit: false };
5350
+ if (key.upArrow || key.downArrow) {
5351
+ return NOOP;
5326
5352
  }
5327
5353
  if (key.ctrl && key.input === "a" || key.home) {
5328
5354
  return { next: null, cursor: startOfLine(value, cursor), submit: false };
@@ -5888,7 +5914,8 @@ function HintRow() {
5888
5914
  const items = [
5889
5915
  { key: "\u23CE", sep: "send" },
5890
5916
  { key: "\u21E7\u23CE", sep: "newline" },
5891
- { key: "\u2191\u2193", sep: "history" },
5917
+ { key: "\u2191\u2193", sep: "scroll" },
5918
+ { key: "^P/^N", sep: "history" },
5892
5919
  { key: "esc", sep: "abort" },
5893
5920
  { key: "^C", sep: "quit" }
5894
5921
  ];
@@ -6404,8 +6431,12 @@ function ArgRow({ value, isSelected }) {
6404
6431
  }
6405
6432
 
6406
6433
  // src/cli/ui/SlashSuggestions.tsx
6407
- import { Box as Box23, Text as Text23 } from "ink";
6434
+ import { Box as Box23, Text as Text23, useStdout as useStdout8 } from "ink";
6408
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;
6409
6440
  var GROUP_LABEL = {
6410
6441
  chat: "CHAT",
6411
6442
  setup: "SETUP",
@@ -6423,26 +6454,82 @@ function SlashSuggestions({
6423
6454
  advancedHidden
6424
6455
  }) {
6425
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]);
6426
6472
  if (matches === null) return null;
6427
6473
  if (matches.length === 0) {
6428
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"));
6429
6475
  }
6430
- const MAX = groupMode ? 24 : 8;
6431
6476
  const total = matches.length;
6432
- const windowStart = total <= MAX ? 0 : Math.max(0, Math.min(selectedIndex - Math.floor(MAX / 2), total - MAX));
6433
- const shown = matches.slice(windowStart, windowStart + MAX);
6477
+ const items = buildVisibleItems(matches, windowStart, maxRows, groupMode);
6478
+ const shownCommands = items.filter((item) => item.kind === "command");
6434
6479
  const hiddenAbove = windowStart;
6435
- const hiddenBelow = total - windowStart - shown.length;
6436
- let lastGroup = null;
6437
- if (windowStart > 0) lastGroup = matches[windowStart - 1]?.group ?? null;
6438
- 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) => {
6439
- const idx = windowStart + i;
6440
- const showHeader = groupMode && spec.group !== lastGroup;
6441
- lastGroup = spec.group;
6442
- 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
+ );
6443
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")));
6444
6495
  }
6445
- 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
+ }) {
6446
6533
  const color = useColor();
6447
6534
  const name = `/${spec.cmd}`;
6448
6535
  const argsSuffix = spec.argsHint ? spec.argsHint : "";
@@ -6450,7 +6537,19 @@ function SuggestionRow({ spec, isSelected }) {
6450
6537
  const translated = t(key);
6451
6538
  const summary = translated === key ? spec.summary : translated;
6452
6539
  const aliasHint = spec.aliases?.length ? ` \xB7 /${spec.aliases.join(" /")}` : "";
6453
- 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`;
6454
6553
  }
6455
6554
 
6456
6555
  // src/cli/ui/WelcomeBanner.tsx
@@ -7819,10 +7918,16 @@ function CtxCard({ card }) {
7819
7918
  {
7820
7919
  glyph: "\u2318",
7821
7920
  tone: TONE.brand,
7822
- title: "context",
7921
+ title: t("cardTitles.context"),
7823
7922
  meta: [`${used.toLocaleString()} / ${cap.toLocaleString()} (${usedPct.toFixed(1)}%)`]
7824
7923
  }
7825
- ), 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);
7924
+ ), 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(
7925
+ Text26,
7926
+ {
7927
+ color: FG.faint
7928
+ },
7929
+ `\xB7 ${t("cardLabels.turn")} ${tool.turn} \xB7 ${tool.tokens.toLocaleString()}`
7930
+ )))) : null);
7826
7931
  }
7827
7932
 
7828
7933
  // src/cli/ui/cards/DiffCard.tsx
@@ -7890,7 +7995,7 @@ import { Box as Box30, Text as Text29 } from "ink";
7890
7995
  import React35 from "react";
7891
7996
  var STACK_TAIL = 5;
7892
7997
  function ErrorCard({ card }) {
7893
- const retryNote = card.retries !== void 0 && card.retries > 0 ? `${card.retries} retr${card.retries === 1 ? "y" : "ies"}` : null;
7998
+ const retryNote = card.retries !== void 0 && card.retries > 0 ? `${card.retries} ${t("cardLabels.retries")}` : null;
7894
7999
  const stackLines = card.stack ? card.stack.split("\n") : [];
7895
8000
  const stackTrunc = stackLines.length > STACK_TAIL;
7896
8001
  const stackVisible = stackTrunc ? stackLines.slice(-STACK_TAIL) : stackLines;
@@ -7902,10 +8007,13 @@ function ErrorCard({ card }) {
7902
8007
  {
7903
8008
  glyph: "\u2716",
7904
8009
  tone: TONE.err,
7905
- title: card.title || "error",
8010
+ title: card.title || t("cardTitles.error"),
7906
8011
  meta: retryNote ? [retryNote] : void 0
7907
8012
  }
7908
- ), 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);
8013
+ ), 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(
8014
+ stackHidden === 1 ? "cardLabels.earlierStackLine" : "cardLabels.earlierStackLines",
8015
+ { count: stackHidden }
8016
+ )) : null, stackVisible.map((line, i) => /* @__PURE__ */ React35.createElement(Text29, { key: `${card.id}:stk:${stackHidden + i}`, color: FG.meta }, line || " "))) : null);
7909
8017
  }
7910
8018
 
7911
8019
  // src/cli/ui/cards/LiveCard.tsx
@@ -8071,7 +8179,7 @@ function anchorIndex(steps) {
8071
8179
  }
8072
8180
 
8073
8181
  // src/cli/ui/cards/ReasoningCard.tsx
8074
- import { Box as Box34, Text as Text35, useStdout as useStdout8 } from "ink";
8182
+ import { Box as Box34, Text as Text35, useStdout as useStdout9 } from "ink";
8075
8183
  import React41 from "react";
8076
8184
 
8077
8185
  // src/frame/width.ts
@@ -8138,7 +8246,7 @@ function ReasoningCard({
8138
8246
  card,
8139
8247
  expanded
8140
8248
  }) {
8141
- const { stdout } = useStdout8();
8249
+ const { stdout } = useStdout9();
8142
8250
  const cols = stdout?.columns ?? 80;
8143
8251
  const lineCells = Math.max(20, cols - 4);
8144
8252
  const allLines = card.text.length > 0 ? card.text.split("\n") : [];
@@ -8150,7 +8258,7 @@ function ReasoningHeader({ card }) {
8150
8258
  const streamingActive = card.streaming && !card.aborted;
8151
8259
  const headColor = card.aborted ? TONE.err : streamingActive ? TONE_ACTIVE.accent : TONE.accent;
8152
8260
  const glyph = streamingActive ? "\u25C7" : "\u25C6";
8153
- const title = streamingActive ? "reasoning\u2026" : card.aborted ? "reasoning (aborted)" : "reasoning";
8261
+ const title = streamingActive ? t("cardTitles.reasoningEllipsis") : card.aborted ? t("cardTitles.reasoningAborted") : t("cardTitles.reasoning");
8154
8262
  const meta = [];
8155
8263
  const m = headerMeta(card);
8156
8264
  if (m) meta.push(m);
@@ -8172,11 +8280,11 @@ function ReasoningHeader({ card }) {
8172
8280
  }
8173
8281
  function headerMeta(card) {
8174
8282
  if (card.streaming) {
8175
- return card.tokens > 0 ? `${card.tokens.toLocaleString()} tok` : "";
8283
+ return card.tokens > 0 ? `${card.tokens.toLocaleString()} ${t("cardLabels.tok")}` : "";
8176
8284
  }
8177
8285
  const parts = [];
8178
- if (card.tokens > 0) parts.push(`${card.tokens.toLocaleString()} tok`);
8179
- if (card.paragraphs > 0) parts.push(`${card.paragraphs} \xB6`);
8286
+ if (card.tokens > 0) parts.push(`${card.tokens.toLocaleString()} ${t("cardLabels.tok")}`);
8287
+ if (card.paragraphs > 0) parts.push(`${card.paragraphs} ${t("cardLabels.pilcrow")}`);
8180
8288
  return parts.join(" \xB7 ");
8181
8289
  }
8182
8290
  function headerDuration(card) {
@@ -8227,18 +8335,24 @@ import React42 from "react";
8227
8335
  function SearchCard({ card }) {
8228
8336
  const fileCount = new Set(card.hits.map((h) => h.file)).size;
8229
8337
  const elapsed = `${(card.elapsedMs / 1e3).toFixed(2)}s`;
8230
- const stats2 = `${card.hits.length} hit${card.hits.length === 1 ? "" : "s"} \xB7 ${fileCount} file${fileCount === 1 ? "" : "s"}`;
8338
+ const stats2 = t(card.hits.length === 1 ? "cardLabels.hitSingular" : "cardLabels.hitsPlural", {
8339
+ count: card.hits.length,
8340
+ files: fileCount
8341
+ });
8231
8342
  const grouped = groupByFile(card.hits.slice(0, 10));
8232
8343
  return /* @__PURE__ */ React42.createElement(Card, { tone: TONE.info }, /* @__PURE__ */ React42.createElement(
8233
8344
  CardHeader,
8234
8345
  {
8235
8346
  glyph: "\u2299",
8236
8347
  tone: TONE.info,
8237
- title: "search",
8348
+ title: t("cardTitles.search"),
8238
8349
  subtitle: `"${card.query}"`,
8239
8350
  meta: [stats2, elapsed]
8240
8351
  }
8241
- ), 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);
8352
+ ), 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(
8353
+ card.hits.length - 10 === 1 ? "cardLabels.moreHitSingular" : "cardLabels.moreHitsPlural",
8354
+ { count: card.hits.length - 10 }
8355
+ )) : null);
8242
8356
  }
8243
8357
  function HighlightedLine({
8244
8358
  text,
@@ -8261,7 +8375,7 @@ function groupByFile(hits) {
8261
8375
  }
8262
8376
 
8263
8377
  // src/cli/ui/cards/StreamingCard.tsx
8264
- import { Box as Box37, Text as Text38, useStdout as useStdout10 } from "ink";
8378
+ import { Box as Box37, Text as Text38, useStdout as useStdout11 } from "ink";
8265
8379
  import React44, { useContext as useContext5 } from "react";
8266
8380
 
8267
8381
  // src/cli/ui/layout/LiveExpandContext.ts
@@ -8270,7 +8384,7 @@ var LiveExpandContext = createContext3(false);
8270
8384
 
8271
8385
  // src/cli/ui/markdown.tsx
8272
8386
  import { highlight, supportsLanguage } from "cli-highlight";
8273
- import { Box as Box36, Text as Text37, useStdout as useStdout9 } from "ink";
8387
+ import { Box as Box36, Text as Text37, useStdout as useStdout10 } from "ink";
8274
8388
  import React43 from "react";
8275
8389
  import stringWidth from "string-width";
8276
8390
  var BODY_LEFT_CELLS = 7;
@@ -8278,7 +8392,7 @@ var MarkdownWidthCtx = React43.createContext(void 0);
8278
8392
  function useWidth() {
8279
8393
  const ctx = React43.useContext(MarkdownWidthCtx);
8280
8394
  if (ctx !== void 0) return ctx;
8281
- return (useStdout9()?.stdout?.columns ?? process.stdout.columns ?? 80) - BODY_LEFT_CELLS;
8395
+ return (useStdout10()?.stdout?.columns ?? process.stdout.columns ?? 80) - BODY_LEFT_CELLS;
8282
8396
  }
8283
8397
  marked.setOptions({ gfm: true, breaks: false });
8284
8398
  function Markdown({ text, width }) {
@@ -8599,7 +8713,7 @@ function tokenRate(text, startTs, endTs) {
8599
8713
  }
8600
8714
  var PILL_RATE = { bg: "#11141a", fg: "#8b949e" };
8601
8715
  function StreamingCard({ card }) {
8602
- const { stdout } = useStdout10();
8716
+ const { stdout } = useStdout11();
8603
8717
  const cols = stdout?.columns ?? 80;
8604
8718
  const expanded = useContext5(LiveExpandContext);
8605
8719
  const reserveCap = expanded ? EXPANDED_MAX_LINES + 2 : STREAMING_PREVIEW_LINES2 + 2;
@@ -8618,7 +8732,7 @@ function StreamingCard({ card }) {
8618
8732
  {
8619
8733
  glyph: "\u2039",
8620
8734
  tone: TONE.ok,
8621
- title: "reply",
8735
+ title: t("cardTitles.reply"),
8622
8736
  right: /* @__PURE__ */ React44.createElement(React44.Fragment, null, ratePill, modelPill)
8623
8737
  }
8624
8738
  ), /* @__PURE__ */ React44.createElement(Markdown, { text: card.text }));
@@ -8632,7 +8746,7 @@ function StreamingCard({ card }) {
8632
8746
  const aborted = !!card.aborted;
8633
8747
  const headColor = aborted ? TONE.err : TONE_ACTIVE.brand;
8634
8748
  const glyph = aborted ? "\u2039" : "\u25C8";
8635
- const headLabel = aborted ? "aborted" : "writing\u2026";
8749
+ const headLabel = aborted ? t("cardLabels.aborted") : t("cardLabels.writing");
8636
8750
  const { tokens: liveTokens, tps: liveTps } = tokenRate(card.text, card.ts, Date.now());
8637
8751
  const liveRatePill = !aborted && liveTokens >= MIN_TOKENS_FOR_RATE && liveTps !== null ? /* @__PURE__ */ React44.createElement(Pill, { label: `${liveTps} t/s`, ...PILL_RATE, bold: false }) : null;
8638
8752
  const expandPill = !aborted ? /* @__PURE__ */ React44.createElement(Pill, { label: expanded ? "expanded \u2303o" : "preview \u2303o", ...PILL_RATE, bold: false }) : null;
@@ -8644,13 +8758,9 @@ function StreamingCard({ card }) {
8644
8758
  title: headLabel,
8645
8759
  right: /* @__PURE__ */ React44.createElement(React44.Fragment, null, liveRatePill, expandPill, aborted ? null : /* @__PURE__ */ React44.createElement(Spinner, { kind: "braille", color: TONE_ACTIVE.brand }), modelPill)
8646
8760
  }
8647
- ), expanded && droppedAbove > 0 ? /* @__PURE__ */ React44.createElement(
8648
- Text38,
8649
- {
8650
- color: FG.faint
8651
- },
8652
- `\u22EF ${droppedAbove} earlier line${droppedAbove === 1 ? "" : "s"} above`
8653
- ) : 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);
8761
+ ), expanded && droppedAbove > 0 ? /* @__PURE__ */ React44.createElement(Text38, { color: FG.faint }, t(droppedAbove === 1 ? "cardLabels.earlierLine" : "cardLabels.earlierLines", {
8762
+ count: droppedAbove
8763
+ })) : 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);
8654
8764
  }
8655
8765
 
8656
8766
  // src/cli/ui/cards/SubAgentCard.tsx
@@ -8664,22 +8774,22 @@ function SubAgentCard({ card }) {
8664
8774
  failed: tone.err
8665
8775
  };
8666
8776
  const headColor = statusColor[card.status];
8667
- const headGlyph = card.status === "failed" ? "\u2716" : "\u232C";
8777
+ const headGlyph = card.status === "failed" ? "\u2717" : "\u232C";
8668
8778
  const runningChildren = card.children.filter((c) => !isChildDone(c)).length;
8669
8779
  const isRunning = card.status === "running";
8670
8780
  const inLive = useContext6(ActiveCardContext);
8671
- const headerMeta2 = isRunning ? runningChildren > 0 ? [`${runningChildren} running`] : ["working"] : [{ text: card.status, color: headColor }];
8781
+ const headerMeta2 = isRunning ? runningChildren > 0 ? [`${runningChildren} ${t("cardLabels.runningLabel")}`] : [t("cardLabels.workingLabel")] : [{ text: card.status, color: headColor }];
8672
8782
  return /* @__PURE__ */ React45.createElement(Card, { tone: headColor }, /* @__PURE__ */ React45.createElement(
8673
8783
  CardHeader,
8674
8784
  {
8675
8785
  glyph: headGlyph,
8676
8786
  tone: headColor,
8677
- title: "subagent",
8787
+ title: t("cardTitles.subagent"),
8678
8788
  titleColor: tone.violet,
8679
8789
  subtitle: card.task,
8680
8790
  meta: headerMeta2
8681
8791
  }
8682
- ), 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 }))));
8792
+ ), 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 }))));
8683
8793
  }
8684
8794
  function isChildDone(card) {
8685
8795
  switch (card.kind) {
@@ -8715,7 +8825,7 @@ function childVisual(card, doneColor, failedColor, fallbackColor) {
8715
8825
  statusGlyph: done ? doneGlyph(doneColor) : runningGlyph(CARD.reasoning.color),
8716
8826
  kindGlyph: "\u25C6",
8717
8827
  kindColor: CARD.reasoning.color,
8718
- text: `reasoning \xB7 ${card.paragraphs} \xB6`
8828
+ text: t("cardLabels.reasoningLabel", { count: card.paragraphs })
8719
8829
  };
8720
8830
  }
8721
8831
  case "tool": {
@@ -8732,7 +8842,7 @@ function childVisual(card, doneColor, failedColor, fallbackColor) {
8732
8842
  statusGlyph: card.done ? doneGlyph(doneColor) : runningGlyph(CARD.streaming.color),
8733
8843
  kindGlyph: "\u25C8",
8734
8844
  kindColor: CARD.streaming.color,
8735
- text: card.done ? "response" : "writing \u2026"
8845
+ text: card.done ? t("cardLabels.response") : t("cardLabels.writing")
8736
8846
  };
8737
8847
  case "diff":
8738
8848
  return {
@@ -8829,7 +8939,7 @@ function TipRowRender({
8829
8939
  }
8830
8940
 
8831
8941
  // src/cli/ui/cards/ToolCard.tsx
8832
- import { Text as Text42, useStdout as useStdout11 } from "ink";
8942
+ import { Text as Text42, useStdout as useStdout12 } from "ink";
8833
8943
  import React48 from "react";
8834
8944
  var READ_TAIL = 2;
8835
8945
  var OTHER_TAIL = 5;
@@ -8838,10 +8948,11 @@ function tailLinesFor(name) {
8838
8948
  return /(?:^|_)(read|search|list|tree|get|status|diff|fetch|grep)(_|$)/.test(lower) || lower === "job_output" ? READ_TAIL : OTHER_TAIL;
8839
8949
  }
8840
8950
  function ToolCard({ card }) {
8841
- const { stdout } = useStdout11();
8951
+ const { stdout } = useStdout12();
8842
8952
  const cols = stdout?.columns ?? 80;
8843
8953
  const lineCells = Math.max(20, cols - 4);
8844
8954
  const argsLabel = formatArgsSummary(card.args);
8955
+ const subagentMarkdown = unwrapSubagentMarkdown(card);
8845
8956
  const allLines = card.output.length > 0 ? card.output.split("\n") : [];
8846
8957
  const tail = tailLinesFor(card.name);
8847
8958
  const truncated = allLines.length > tail;
@@ -8850,13 +8961,13 @@ function ToolCard({ card }) {
8850
8961
  const status2 = toolStatus(card);
8851
8962
  const headColor = headerColorFor(status2);
8852
8963
  const errColor = card.exitCode && card.exitCode !== 0 ? TONE.err : FG.sub;
8853
- const showBody = !card.rejected && visible.length > 0;
8964
+ const showBody = !card.rejected && (subagentMarkdown !== null || visible.length > 0);
8854
8965
  const meta = [];
8855
8966
  if (card.retry) {
8856
8967
  meta.push({ text: `\u21BB ${card.retry.attempt}/${card.retry.max}`, color: TONE.warn });
8857
8968
  }
8858
8969
  if (card.rejected) {
8859
- meta.push({ text: "rejected", color: TONE.err });
8970
+ meta.push({ text: t("cardLabels.rejected"), color: TONE.err });
8860
8971
  }
8861
8972
  for (const part of metaTrail(card)) meta.push(part);
8862
8973
  return /* @__PURE__ */ React48.createElement(Card, { tone: headColor }, /* @__PURE__ */ React48.createElement(
@@ -8869,7 +8980,9 @@ function ToolCard({ card }) {
8869
8980
  meta: meta.length > 0 ? meta : void 0,
8870
8981
  right: status2 === "running" ? /* @__PURE__ */ React48.createElement(Spinner, { kind: "braille", color: TONE_ACTIVE.brand, bold: true }) : void 0
8871
8982
  }
8872
- ), showBody && /* @__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(
8983
+ ), 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", {
8984
+ count: hidden
8985
+ })) : null, visible.map((line, i) => /* @__PURE__ */ React48.createElement(
8873
8986
  Text42,
8874
8987
  {
8875
8988
  key: `${card.id}:${hidden + i}`,
@@ -8877,7 +8990,21 @@ function ToolCard({ card }) {
8877
8990
  dimColor: !card.exitCode || card.exitCode === 0
8878
8991
  },
8879
8992
  clipToCells(line, lineCells) || " "
8880
- ))));
8993
+ )))));
8994
+ }
8995
+ function unwrapSubagentMarkdown(card) {
8996
+ if (card.name !== "spawn_subagent") return null;
8997
+ if (card.output.length === 0) return null;
8998
+ try {
8999
+ const parsed = JSON.parse(card.output);
9000
+ if (!parsed || typeof parsed !== "object") return null;
9001
+ const obj = parsed;
9002
+ if (obj.success !== true) return null;
9003
+ if (typeof obj.output !== "string") return null;
9004
+ return obj.output;
9005
+ } catch {
9006
+ return null;
9007
+ }
8881
9008
  }
8882
9009
  function toolStatus(card) {
8883
9010
  if (card.rejected) return "rejected";
@@ -8915,10 +9042,11 @@ function headerColorFor(s) {
8915
9042
  function metaTrail(card) {
8916
9043
  const parts = [];
8917
9044
  const inputBytes = largestStringInputBytes(card.args);
8918
- if (inputBytes !== null) parts.push(`${formatBytes(inputBytes)} in`);
8919
- if (card.elapsedMs > 0) parts.push(`${(card.elapsedMs / 1e3).toFixed(2)}s`);
9045
+ if (inputBytes !== null) parts.push(t("cardLabels.bytesIn", { bytes: formatBytes(inputBytes) }));
9046
+ if (card.elapsedMs > 0)
9047
+ parts.push(t("cardLabels.elapsedSec", { secs: (card.elapsedMs / 1e3).toFixed(2) }));
8920
9048
  if (card.done && !card.rejected && !card.aborted && card.exitCode !== void 0 && card.exitCode !== 0) {
8921
- parts.push(`exit ${card.exitCode}`);
9049
+ parts.push(t("cardLabels.exit", { code: card.exitCode }));
8922
9050
  }
8923
9051
  return parts;
8924
9052
  }
@@ -8975,13 +9103,16 @@ function UsageCard({ card }) {
8975
9103
  const promptRatio = card.tokens.prompt / cap;
8976
9104
  const reasonRatio = card.tokens.reason / cap;
8977
9105
  const outputRatio = card.tokens.output / cap;
8978
- const headerMeta2 = [`turn ${card.turn}`, formatCost(card.cost, card.balanceCurrency)];
9106
+ const headerMeta2 = [
9107
+ `${t("cardLabels.turn")} ${card.turn}`,
9108
+ formatCost(card.cost, card.balanceCurrency)
9109
+ ];
8979
9110
  if (card.elapsedMs !== void 0) headerMeta2.push(`${(card.elapsedMs / 1e3).toFixed(1)}s`);
8980
- 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));
9111
+ 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));
8981
9112
  }
8982
9113
  function CompactUsageRow({ card }) {
8983
9114
  const elapsed = card.elapsedMs !== void 0 ? ` \xB7 ${(card.elapsedMs / 1e3).toFixed(1)}s` : "";
8984
- 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);
9115
+ 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);
8985
9116
  }
8986
9117
 
8987
9118
  // src/cli/ui/cards/UserCard.tsx
@@ -9304,7 +9435,7 @@ function summarizeToolArgs(name, args) {
9304
9435
  }
9305
9436
 
9306
9437
  // src/cli/ui/layout/StatusRow.tsx
9307
- import { Box as Box46, Text as Text49, useStdout as useStdout12 } from "ink";
9438
+ import { Box as Box46, Text as Text49, useStdout as useStdout13 } from "ink";
9308
9439
  import React56 from "react";
9309
9440
 
9310
9441
  // src/cli/ui/primitives/Countdown.tsx
@@ -9320,24 +9451,33 @@ function Countdown({ endsAt, color = TONE.brand }) {
9320
9451
  var RULE_PAD = 4;
9321
9452
  var RULE_MIN = 20;
9322
9453
  var WALLET_MIN_COLS = 90;
9454
+ var VERSION_MIN_COLS = 70;
9455
+ var FEEDBACK_HINT_MIN_COLS = 100;
9323
9456
  function StatusRow() {
9324
9457
  const status2 = useAgentState((s) => s.status);
9325
9458
  const session = useAgentState((s) => s.session);
9326
- const { stdout } = useStdout12();
9459
+ const { stdout } = useStdout13();
9327
9460
  const cols = stdout?.columns ?? 80;
9328
9461
  const ruleWidth = Math.max(RULE_MIN, cols - RULE_PAD);
9329
9462
  const hasTurn = status2.cost > 0;
9330
9463
  const hasSession = status2.sessionCost > 0;
9331
9464
  const hasBalance = typeof status2.balance === "number";
9332
9465
  const showWallet = cols >= WALLET_MIN_COLS && (hasSession || hasBalance);
9333
- 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(
9466
+ 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(
9467
+ Text49,
9468
+ {
9469
+ color: TONE.accent,
9470
+ wrap: "truncate"
9471
+ },
9472
+ `cache ${Math.round(status2.cacheHit * 100)}%`
9473
+ ), showWallet && /* @__PURE__ */ React56.createElement(
9334
9474
  WalletPill,
9335
9475
  {
9336
9476
  sessionCostUsd: status2.sessionCost,
9337
9477
  balance: status2.balance,
9338
9478
  currency: status2.balanceCurrency
9339
9479
  }
9340
- )));
9480
+ ), 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")))));
9341
9481
  }
9342
9482
  function WalletPill({
9343
9483
  sessionCostUsd,
@@ -9346,7 +9486,14 @@ function WalletPill({
9346
9486
  }) {
9347
9487
  const showSpent = sessionCostUsd > 0;
9348
9488
  const showBalance = typeof balance === "number";
9349
- 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"));
9489
+ 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(
9490
+ Text49,
9491
+ {
9492
+ color: FG.body,
9493
+ wrap: "truncate"
9494
+ },
9495
+ `${formatCost(sessionCostUsd, currency, 2)} spent`
9496
+ ), 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"));
9350
9497
  }
9351
9498
  function ModePill2({
9352
9499
  mode: mode2,
@@ -9355,18 +9502,18 @@ function ModePill2({
9355
9502
  }) {
9356
9503
  if (network === "online") {
9357
9504
  const pill = modeGlyph(mode2);
9358
- return /* @__PURE__ */ React56.createElement(Box46, { flexDirection: "row" }, /* @__PURE__ */ React56.createElement(Text49, { color: pill.color }, pill.glyph), /* @__PURE__ */ React56.createElement(Text49, { color: FG.sub }, ` ${mode2}`));
9505
+ 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}`));
9359
9506
  }
9360
9507
  const dot = networkDot(network);
9361
9508
  if (network === "slow") {
9362
9509
  const tail = detail ? ` \xB7 ${detail}` : "";
9363
- 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}`));
9510
+ 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}`));
9364
9511
  }
9365
9512
  if (network === "disconnected") {
9366
9513
  const tail = detail ? ` \xB7 ${detail}` : "";
9367
- 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}`));
9514
+ 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}`));
9368
9515
  }
9369
- 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"));
9516
+ 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"));
9370
9517
  }
9371
9518
  function CountdownRow({
9372
9519
  mode: mode2,
@@ -9374,14 +9521,14 @@ function CountdownRow({
9374
9521
  }) {
9375
9522
  const pill = modeGlyph(mode2);
9376
9523
  const endsAt = Date.now() + secondsLeft * 1e3;
9377
- 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"));
9524
+ 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"));
9378
9525
  }
9379
9526
  function RecordingPill({ rec }) {
9380
9527
  const sizeMb = (rec.sizeBytes / (1024 * 1024)).toFixed(1);
9381
- 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`));
9528
+ 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`));
9382
9529
  }
9383
9530
  function Sep() {
9384
- return /* @__PURE__ */ React56.createElement(Text49, { color: FG.meta }, " \xB7 ");
9531
+ return /* @__PURE__ */ React56.createElement(Text49, { color: FG.meta, wrap: "truncate" }, " \xB7 ");
9385
9532
  }
9386
9533
  function modeGlyph(mode2) {
9387
9534
  switch (mode2) {
@@ -9409,7 +9556,7 @@ function networkDot(state) {
9409
9556
  }
9410
9557
 
9411
9558
  // src/cli/ui/layout/ToastRail.tsx
9412
- import { Box as Box47, Text as Text50, useStdout as useStdout13 } from "ink";
9559
+ import { Box as Box47, Text as Text50, useStdout as useStdout14 } from "ink";
9413
9560
  import React57, { useEffect as useEffect8 } from "react";
9414
9561
  var TONE_COLOR = {
9415
9562
  ok: TONE.ok,
@@ -9432,7 +9579,7 @@ function ToastRail() {
9432
9579
  const toasts = useAgentState((s) => s.toasts);
9433
9580
  const dispatch = useDispatch();
9434
9581
  useSlowTick();
9435
- const { stdout } = useStdout13();
9582
+ const { stdout } = useStdout14();
9436
9583
  const cols = stdout?.columns ?? 80;
9437
9584
  const rule = "\u2501".repeat(Math.max(20, cols - 4));
9438
9585
  const now = Date.now();
@@ -9837,24 +9984,28 @@ var update = (_args, _loop, ctx) => {
9837
9984
  return { info: lines.join("\n") };
9838
9985
  }
9839
9986
  lines.push(t("handlers.admin.updateLatest", { version: latest }));
9840
- const diff = compareVersions(VERSION, latest);
9841
- if (diff >= 0) {
9987
+ if (compareVersions(VERSION, latest) >= 0) {
9842
9988
  lines.push("", t("handlers.admin.updateUpToDate"));
9843
9989
  return { info: lines.join("\n") };
9844
9990
  }
9845
- if (isNpxInstall()) {
9991
+ const installSource = detectInstallSource();
9992
+ const npmPrefix = installSource === "npm" ? detectNpmInstallPrefix() : null;
9993
+ const plan2 = planUpdate({ current: VERSION, latest, installSource, npmPrefix });
9994
+ if (plan2.action === "npx-hint") {
9846
9995
  lines.push("", t("handlers.admin.updateNpxHint"), t("handlers.admin.updateNpxForce"));
9996
+ return { info: lines.join("\n") };
9997
+ }
9998
+ lines.push("", t("handlers.admin.updateUpgradeHint"), t("handlers.admin.updateUpgradeCmd1"));
9999
+ if (plan2.action === "run-install" && plan2.command) {
10000
+ lines.push(t("handlers.admin.updateUpgradeCmd2", { command: plan2.command.join(" ") }));
9847
10001
  } else {
9848
- lines.push(
9849
- "",
9850
- t("handlers.admin.updateUpgradeHint"),
9851
- t("handlers.admin.updateUpgradeCmd1"),
9852
- t("handlers.admin.updateUpgradeCmd2"),
9853
- "",
9854
- t("handlers.admin.updateInSessionDisabled"),
9855
- t("handlers.admin.updateInSessionDisabled2")
9856
- );
10002
+ lines.push(...MANUAL_UPDATE_COMMANDS.map((c) => ` ${c}`));
9857
10003
  }
10004
+ lines.push(
10005
+ "",
10006
+ t("handlers.admin.updateInSessionDisabled"),
10007
+ t("handlers.admin.updateInSessionDisabled2")
10008
+ );
9858
10009
  return { info: lines.join("\n") };
9859
10010
  };
9860
10011
  var stats = () => {
@@ -10931,6 +11082,32 @@ var handlers10 = {
10931
11082
  budget
10932
11083
  };
10933
11084
 
11085
+ // src/cli/ui/slash/handlers/observability.ts
11086
+ import { release } from "os";
11087
+
11088
+ // src/cli/ui/clipboard.ts
11089
+ import { mkdtempSync, writeFileSync as writeFileSync4 } from "fs";
11090
+ import { tmpdir } from "os";
11091
+ import { join as join6 } from "path";
11092
+ var OSC_52_LIMIT = 75e3;
11093
+ function writeClipboard(text) {
11094
+ const dir = mkdtempSync(join6(tmpdir(), "reasonix-clip-"));
11095
+ const filePath = join6(dir, "clip.txt");
11096
+ let osc52 = false;
11097
+ if (text.length <= OSC_52_LIMIT) {
11098
+ const b64 = Buffer.from(text, "utf8").toString("base64");
11099
+ process.stdout.write(`\x1B]52;c;${b64}\x1B\\`);
11100
+ osc52 = true;
11101
+ }
11102
+ let writtenPath = null;
11103
+ try {
11104
+ writeFileSync4(filePath, text, "utf8");
11105
+ writtenPath = filePath;
11106
+ } catch {
11107
+ }
11108
+ return { osc52, filePath: writtenPath, size: text.length };
11109
+ }
11110
+
10934
11111
  // src/cli/ui/ctx-breakdown.tsx
10935
11112
  import { Box as Box48, Text as Text51 } from "ink";
10936
11113
  import React59 from "react";
@@ -10975,6 +11152,92 @@ function computeCtxBreakdown(loop2) {
10975
11152
  };
10976
11153
  }
10977
11154
 
11155
+ // src/cli/ui/feedback.ts
11156
+ var FEEDBACK_ISSUE_BASE = "https://github.com/esengine/DeepSeek-Reasonix/issues/new";
11157
+ var FEEDBACK_BODY_QUERY_LIMIT = 6e3;
11158
+ function buildFeedbackIssueUrl(diagnostic) {
11159
+ const trimmed = diagnostic.length > FEEDBACK_BODY_QUERY_LIMIT ? diagnostic.slice(0, FEEDBACK_BODY_QUERY_LIMIT) : diagnostic;
11160
+ return `${FEEDBACK_ISSUE_BASE}?body=${encodeURIComponent(trimmed)}`;
11161
+ }
11162
+ function buildFeedbackDiagnostic(input) {
11163
+ const lines = [];
11164
+ lines.push(`**Reasonix**: ${formatVersion(input.version, input.latestVersion)}`);
11165
+ lines.push(`**Platform**: ${input.platform} (${input.osRelease})`);
11166
+ lines.push(`**Terminal**: ${formatTerminal(input)}`);
11167
+ if (typeof input.cols === "number" && typeof input.rows === "number") {
11168
+ lines.push(`**Size**: ${input.cols}\xD7${input.rows}`);
11169
+ }
11170
+ lines.push(`**Node**: ${input.nodeVersion}`);
11171
+ lines.push(`**Locale**: ${input.locale}`);
11172
+ if (input.theme) lines.push(`**Theme**: ${input.theme}`);
11173
+ lines.push(`**Model**: ${formatModel(input.model, input.reasoningEffort)}`);
11174
+ const modeLine = formatMode(input.editMode, input.planMode);
11175
+ if (modeLine) lines.push(`**Mode**: ${modeLine}`);
11176
+ if (typeof input.mcpServerCount === "number") {
11177
+ lines.push(`**MCP**: ${input.mcpServerCount} server(s)`);
11178
+ }
11179
+ if (input.sessionId) lines.push(`**Session**: ${input.sessionId}`);
11180
+ lines.push("", "<!-- describe what you were doing when this happened -->", "");
11181
+ return lines.join("\n");
11182
+ }
11183
+ function formatVersion(installed, latest) {
11184
+ if (!latest) return installed;
11185
+ const cmp = compareVersions(installed, latest);
11186
+ if (cmp === 0) return `${installed} (latest)`;
11187
+ if (cmp > 0) return installed;
11188
+ return `${installed} (latest: ${latest})`;
11189
+ }
11190
+ function formatModel(model2, effort) {
11191
+ return effort ? `${model2} \xB7 effort=${effort}` : model2;
11192
+ }
11193
+ function formatMode(editMode, planMode) {
11194
+ const parts = [];
11195
+ if (editMode) parts.push(`edit=${editMode}`);
11196
+ parts.push(`plan=${planMode ? "on" : "off"}`);
11197
+ return parts.join(" \xB7 ");
11198
+ }
11199
+ function formatTerminal(input) {
11200
+ const head = input.termProgram ?? "(unknown)";
11201
+ const env = [];
11202
+ if (input.termProgram) env.push(`TERM_PROGRAM=${input.termProgram}`);
11203
+ if (input.term) env.push(`TERM=${input.term}`);
11204
+ if (input.colorTerm) env.push(`COLORTERM=${input.colorTerm}`);
11205
+ if (input.inWindowsTerminal) env.push("WT_SESSION=set");
11206
+ if (input.inTmux) env.push("TMUX=set");
11207
+ if (input.inSsh) env.push("SSH_TTY=set");
11208
+ if (input.wslDistro) env.push(`WSL=${input.wslDistro}`);
11209
+ if (env.length === 0) return head;
11210
+ return `${head} (${env.join(", ")})`;
11211
+ }
11212
+
11213
+ // src/cli/ui/open-url.ts
11214
+ import { spawn } from "child_process";
11215
+ import { platform } from "os";
11216
+ function openUrl(url) {
11217
+ if (process.env.CI) return { opened: false, reason: "ci" };
11218
+ if (process.env.REASONIX_NO_OPEN) return { opened: false, reason: "disabled" };
11219
+ const os = platform();
11220
+ let cmd;
11221
+ let args;
11222
+ if (os === "win32") {
11223
+ cmd = "cmd";
11224
+ args = ["/c", "start", "", url];
11225
+ } else if (os === "darwin") {
11226
+ cmd = "open";
11227
+ args = [url];
11228
+ } else {
11229
+ cmd = "xdg-open";
11230
+ args = [url];
11231
+ }
11232
+ try {
11233
+ const child = spawn(cmd, args, { detached: true, stdio: "ignore" });
11234
+ child.unref();
11235
+ return { opened: true };
11236
+ } catch {
11237
+ return { opened: false, reason: "spawn-failed" };
11238
+ }
11239
+ }
11240
+
10978
11241
  // src/cli/ui/slash/handlers/observability.ts
10979
11242
  var context = (_args, loop2) => {
10980
11243
  const breakdown = computeCtxBreakdown(loop2);
@@ -11004,11 +11267,11 @@ var status = (_args, loop2, ctx) => {
11004
11267
  }) : t("handlers.observability.statusCtxNone");
11005
11268
  const cost2 = summary.totalCostUsd;
11006
11269
  const cacheLine = summary.turns > 3 ? (() => {
11007
- const cachePct = Math.round(summary.cacheHitRatio * 100);
11270
+ const cachePct = summary.cacheHitRatio * 100;
11008
11271
  return t("handlers.observability.statusCost", {
11009
11272
  cost: cost2.toFixed(4),
11010
11273
  bar: renderTinyBar(cachePct, 12),
11011
- pct: cachePct,
11274
+ pct: cachePct.toFixed(1),
11012
11275
  turns: summary.turns
11013
11276
  });
11014
11277
  })() : t("handlers.observability.statusCostCold", {
@@ -11147,11 +11410,48 @@ function estimateCost(userText, loop2) {
11147
11410
  ];
11148
11411
  return { info: lines.join("\n") };
11149
11412
  }
11413
+ var feedback = (_args, loop2, ctx) => {
11414
+ const themeName = resolveThemePreference(loadTheme(), process.env.REASONIX_THEME);
11415
+ const diagnostic = buildFeedbackDiagnostic({
11416
+ version: VERSION,
11417
+ latestVersion: ctx.latestVersion ?? void 0,
11418
+ platform: process.platform,
11419
+ osRelease: release(),
11420
+ termProgram: process.env.TERM_PROGRAM,
11421
+ term: process.env.TERM,
11422
+ colorTerm: process.env.COLORTERM,
11423
+ inWindowsTerminal: !!process.env.WT_SESSION,
11424
+ inTmux: !!process.env.TMUX,
11425
+ inSsh: !!process.env.SSH_TTY,
11426
+ wslDistro: process.env.WSL_DISTRO_NAME,
11427
+ cols: process.stdout.columns,
11428
+ rows: process.stdout.rows,
11429
+ nodeVersion: process.version,
11430
+ locale: getLanguage(),
11431
+ theme: themeName,
11432
+ model: loop2.model,
11433
+ reasoningEffort: loop2.reasoningEffort,
11434
+ editMode: ctx.editMode,
11435
+ planMode: ctx.planMode,
11436
+ mcpServerCount: ctx.mcpServers?.length ?? ctx.mcpSpecs?.length,
11437
+ sessionId: ctx.sessionId
11438
+ });
11439
+ writeClipboard(diagnostic);
11440
+ const url = buildFeedbackIssueUrl(diagnostic);
11441
+ const opened = openUrl(url);
11442
+ const lines = [
11443
+ opened.opened ? "\u25B8 issue page opened with the diagnostic block pre-filled. Just describe what you were doing and submit." : `\u25B8 couldn't open the browser (${opened.reason ?? "unknown"}). Diagnostic info is on your clipboard; open this URL manually: ${url}`,
11444
+ "",
11445
+ diagnostic
11446
+ ];
11447
+ return { info: lines.join("\n") };
11448
+ };
11150
11449
  var handlers11 = {
11151
11450
  context,
11152
11451
  status,
11153
11452
  compact,
11154
- cost
11453
+ cost,
11454
+ feedback
11155
11455
  };
11156
11456
 
11157
11457
  // src/cli/ui/slash/handlers/permissions.ts
@@ -11532,6 +11832,31 @@ var handlers17 = {
11532
11832
  se: (args, loop2, ctx) => handlers17["search-engine"](args, loop2, ctx)
11533
11833
  };
11534
11834
 
11835
+ // src/cli/ui/slash/nearest.ts
11836
+ function nearestCommands(input, all, opts = {}) {
11837
+ if (!input) return [];
11838
+ const max = opts.max ?? 3;
11839
+ const maxDistance = Math.min(opts.maxDistance ?? 3, Math.floor(input.length / 2));
11840
+ if (max <= 0 || maxDistance <= 0) return [];
11841
+ 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);
11842
+ }
11843
+ function levenshtein(a, b) {
11844
+ if (a === b) return 0;
11845
+ if (!a) return b.length;
11846
+ if (!b) return a.length;
11847
+ let prev = Array.from({ length: b.length + 1 }, (_, i) => i);
11848
+ let next = new Array(b.length + 1).fill(0);
11849
+ for (let i = 0; i < a.length; i += 1) {
11850
+ next[0] = i + 1;
11851
+ for (let j = 0; j < b.length; j += 1) {
11852
+ const cost2 = a[i] === b[j] ? 0 : 1;
11853
+ next[j + 1] = Math.min((next[j] ?? 0) + 1, (prev[j + 1] ?? 0) + 1, (prev[j] ?? 0) + cost2);
11854
+ }
11855
+ [prev, next] = [next, prev];
11856
+ }
11857
+ return prev[b.length] ?? 0;
11858
+ }
11859
+
11535
11860
  // src/cli/ui/slash/dispatch.ts
11536
11861
  var HANDLERS = {
11537
11862
  ...handlers,
@@ -11555,6 +11880,11 @@ var HANDLERS = {
11555
11880
  function handleSlash(cmd, args, loop2, ctx = {}) {
11556
11881
  const h = HANDLERS[resolveSlashAlias(cmd)];
11557
11882
  if (h) return h(args, loop2, ctx);
11883
+ const suggestions = nearestCommands(cmd, Object.keys(HANDLERS));
11884
+ if (suggestions.length > 0) {
11885
+ const list2 = suggestions.map((name) => `/${name}`).join(", ");
11886
+ return { unknown: true, info: `unknown command: /${cmd} \u2014 did you mean ${list2}?` };
11887
+ }
11558
11888
  return { unknown: true, info: `unknown command: /${cmd} (try /help)` };
11559
11889
  }
11560
11890
 
@@ -12475,9 +12805,9 @@ function useSubagent({
12475
12805
  // src/cli/ui/App.tsx
12476
12806
  var FLUSH_INTERVAL_MS = (() => {
12477
12807
  const raw = process.env.REASONIX_FLUSH_MS;
12478
- if (!raw) return 33;
12808
+ if (!raw) return 16;
12479
12809
  const parsed = Number(raw);
12480
- if (!Number.isFinite(parsed) || parsed < 16 || parsed > 1e3) return 33;
12810
+ if (!Number.isFinite(parsed) || parsed < 16 || parsed > 1e3) return 16;
12481
12811
  return Math.round(parsed);
12482
12812
  })();
12483
12813
  var PLAIN_UI = process.env.REASONIX_UI === "plain";
@@ -12519,7 +12849,8 @@ function AppInner({
12519
12849
  progressSink,
12520
12850
  codeMode,
12521
12851
  noDashboard,
12522
- onSwitchSession
12852
+ onSwitchSession,
12853
+ mouse = true
12523
12854
  }) {
12524
12855
  markPhase("app_inner_start");
12525
12856
  const log = useScrollback();
@@ -12551,18 +12882,18 @@ function AppInner({
12551
12882
  }, [busy]);
12552
12883
  const [ongoingTool, setOngoingTool] = useState20(null);
12553
12884
  const [toolProgress, setToolProgress] = useState20(null);
12554
- const { stdout } = useStdout14();
12885
+ const { stdout } = useStdout15();
12555
12886
  useEffect12(() => {
12556
12887
  if (!stdout || !stdout.isTTY) return;
12557
12888
  stdout.write("\x1B[?2004h");
12558
12889
  stdout.write("\x1B[>4;2m");
12559
- stdout.write("\x1B[?1006h\x1B[?1000h");
12890
+ if (mouse) stdout.write("\x1B[?1007h");
12560
12891
  return () => {
12561
- stdout.write("\x1B[?1000l\x1B[?1006l");
12892
+ if (mouse) stdout.write("\x1B[?1007l");
12562
12893
  stdout.write("\x1B[?2004l");
12563
12894
  stdout.write("\x1B[>4m");
12564
12895
  };
12565
- }, [stdout]);
12896
+ }, [stdout, mouse]);
12566
12897
  const walletCurrencyRef = useRef9(void 0);
12567
12898
  const { activities: subagentActivities, sinkRef: subagentSinkRef } = useSubagent({
12568
12899
  session,
@@ -12711,7 +13042,7 @@ function AppInner({
12711
13042
  const loopRef = useRef9(null);
12712
13043
  const loop2 = useMemo9(() => {
12713
13044
  if (loopRef.current) return loopRef.current;
12714
- const client = new DeepSeekClient();
13045
+ const client = new DeepSeekClient({ baseUrl: loadBaseUrl() });
12715
13046
  if (tools && !tools.has("run_skill")) {
12716
13047
  registerSkillTools(tools, {
12717
13048
  projectRoot: codeMode?.rootDir,
@@ -13031,11 +13362,12 @@ function AppInner({
13031
13362
  }, [session, loop2, codeMode, syncPendingCount, log]);
13032
13363
  const quitProcess = useQuit(transcriptRef);
13033
13364
  useKeystroke((ev) => {
13365
+ const pickerOwnsArrows = (atState?.entries.length ?? 0) > 0 || (slashMatches?.length ?? 0) > 0 || (slashArgMatches?.length ?? 0) > 0 || pendingShell != null;
13034
13366
  if (ev.pageUp || ev.mouseScrollUp) chatScroll.scrollUp();
13035
13367
  else if (ev.pageDown || ev.mouseScrollDown) chatScroll.scrollDown();
13036
13368
  else if (ev.end) chatScroll.jumpToBottom();
13037
- else if ((!chatScroll.pinned || busy) && ev.upArrow) chatScroll.scrollUp();
13038
- else if ((!chatScroll.pinned || busy) && ev.downArrow) chatScroll.scrollDown();
13369
+ else if (!pickerOwnsArrows && ev.upArrow) chatScroll.scrollUp();
13370
+ else if (!pickerOwnsArrows && ev.downArrow) chatScroll.scrollDown();
13039
13371
  }, !modalOpen);
13040
13372
  useKeystroke((ev) => {
13041
13373
  const chKey = ev.input;
@@ -13250,7 +13582,7 @@ function AppInner({
13250
13582
  if (dashboardRef.current) return dashboardRef.current.url;
13251
13583
  if (dashboardStartingRef.current) return dashboardStartingRef.current;
13252
13584
  const startup = (async () => {
13253
- const { startDashboardServer } = await import("./server-MC4A4WAJ.js");
13585
+ const { startDashboardServer } = await import("./server-W4XJK4GX.js");
13254
13586
  const handle = await startDashboardServer({
13255
13587
  mode: "attached",
13256
13588
  configPath: defaultConfigPath(),
@@ -13685,6 +14017,7 @@ function AppInner({
13685
14017
  startDashboard,
13686
14018
  stopDashboard,
13687
14019
  getDashboardUrl,
14020
+ sessionId: session,
13688
14021
  jobs: codeMode?.jobs,
13689
14022
  postInfo: (text2) => log.pushInfo(text2),
13690
14023
  postDoctor: (checks) => log.showDoctor(checks),
@@ -14151,7 +14484,8 @@ function AppInner({
14151
14484
  }
14152
14485
  if (choice === "refine" || choice === "approve") {
14153
14486
  if (pendingPlan) {
14154
- setStagedInput({ plan: pendingPlan, mode: choice });
14487
+ const questions = extractOpenQuestionsSection(pendingPlan) ?? void 0;
14488
+ setStagedInput({ plan: pendingPlan, mode: choice, questions });
14155
14489
  setPendingPlan(null);
14156
14490
  } else if (choice === "approve") {
14157
14491
  setStagedInput({ plan: "", mode: "approve" });
@@ -14166,7 +14500,8 @@ function AppInner({
14166
14500
  return;
14167
14501
  }
14168
14502
  if (pendingPlan) {
14169
- setStagedInput({ plan: pendingPlan, mode: "reject" });
14503
+ const questions = extractOpenQuestionsSection(pendingPlan) ?? void 0;
14504
+ setStagedInput({ plan: pendingPlan, mode: "reject", questions });
14170
14505
  setPendingPlan(null);
14171
14506
  }
14172
14507
  },
@@ -14181,7 +14516,7 @@ function AppInner({
14181
14516
  []
14182
14517
  );
14183
14518
  const handleStagedInputSubmit = useCallback11(
14184
- async (feedback, override) => {
14519
+ async (feedback2, override) => {
14185
14520
  const staged = override ?? stagedInput;
14186
14521
  if (override) {
14187
14522
  setPendingPlan(null);
@@ -14189,8 +14524,8 @@ function AppInner({
14189
14524
  setStagedInput(null);
14190
14525
  }
14191
14526
  if (!staged) return;
14192
- const trimmed = feedback.trim();
14193
- let synthetic;
14527
+ const trimmed = feedback2.trim();
14528
+ const tail = trimmed.length > 50 ? `${trimmed.slice(0, 50)}\u2026` : trimmed;
14194
14529
  let marker;
14195
14530
  if (staged.mode === "approve") {
14196
14531
  togglePlanMode(false);
@@ -14208,19 +14543,7 @@ function AppInner({
14208
14543
  });
14209
14544
  persistPlanState();
14210
14545
  }
14211
- if (trimmed) {
14212
- 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.
14213
-
14214
- User's additional instructions / answers to your open questions:
14215
-
14216
- ${trimmed}
14217
-
14218
- 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.`;
14219
- marker = `\u25B8 plan approved + instructions \u2014 ${trimmed.length > 50 ? `${trimmed.slice(0, 50)}\u2026` : trimmed}`;
14220
- } else {
14221
- 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.";
14222
- marker = "\u25B8 plan approved \u2014 implementing";
14223
- }
14546
+ marker = trimmed ? `\u25B8 plan approved + instructions \u2014 ${tail}` : "\u25B8 plan approved \u2014 implementing";
14224
14547
  } else if (staged.mode === "reject") {
14225
14548
  planStepsRef.current = null;
14226
14549
  completedStepIdsRef.current = /* @__PURE__ */ new Set();
@@ -14229,38 +14552,20 @@ Factor these in before the first edit. Stick to the plan unless you discover a c
14229
14552
  persistPlanState();
14230
14553
  togglePlanMode(false);
14231
14554
  agentStore.dispatch({ type: "plan.drop" });
14232
- if (trimmed) {
14233
- synthetic = `The plan was rejected. User's reason / what they actually want:
14234
-
14235
- ${trimmed}
14236
-
14237
- 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.`;
14238
- marker = `\u25B8 plan rejected \u2014 ${trimmed.length > 50 ? `${trimmed.slice(0, 50)}\u2026` : trimmed}`;
14239
- } else {
14240
- synthetic = "The plan was cancelled. Drop it entirely. Ask me what I actually want before proposing another plan or making any changes.";
14241
- marker = "\u25B8 plan cancelled";
14242
- }
14555
+ marker = trimmed ? `\u25B8 plan rejected \u2014 ${tail}` : "\u25B8 plan cancelled";
14243
14556
  } else {
14244
- if (trimmed) {
14245
- synthetic = `The plan needs refinement. User feedback / answers:
14246
-
14247
- ${trimmed}
14248
-
14249
- 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.`;
14250
- marker = `\u25B8 refining \u2014 ${trimmed.length > 50 ? `${trimmed.slice(0, 50)}\u2026` : trimmed}`;
14251
- } else {
14252
- synthetic = "The plan needs refinement, but the user didn't give specifics. Ask them one or two concrete questions \u2014 scope, approach, file boundaries, or the risks you flagged \u2014 then wait for their answer before submitting an updated plan.";
14253
- marker = "\u25B8 refining \u2014 asking the model to clarify";
14254
- }
14557
+ marker = trimmed ? `\u25B8 refining \u2014 ${tail}` : "\u25B8 refining \u2014 using safe defaults";
14255
14558
  }
14559
+ log.pushInfo(marker);
14256
14560
  const gateId = pendingGateIdRef.current;
14257
14561
  if (gateId !== null) {
14562
+ const fb = trimmed || void 0;
14258
14563
  if (staged.mode === "approve") {
14259
- pauseGate.resolve(gateId, { type: "approve" });
14564
+ pauseGate.resolve(gateId, { type: "approve", feedback: fb });
14260
14565
  } else if (staged.mode === "reject") {
14261
- pauseGate.resolve(gateId, { type: "cancel" });
14566
+ pauseGate.resolve(gateId, { type: "cancel", feedback: fb });
14262
14567
  } else {
14263
- pauseGate.resolve(gateId, { type: "refine" });
14568
+ pauseGate.resolve(gateId, { type: "refine", feedback: fb });
14264
14569
  }
14265
14570
  }
14266
14571
  },
@@ -14453,12 +14758,12 @@ Stay in plan mode \u2014 address the feedback (explore more if needed), then sub
14453
14758
  []
14454
14759
  );
14455
14760
  const handleCheckpointReviseSubmit = useCallback11(
14456
- (feedback, snapOverride) => {
14761
+ (feedback2, snapOverride) => {
14457
14762
  const snap = snapOverride;
14458
14763
  setStagedCheckpointRevise(null);
14459
14764
  if (!snap) return;
14460
14765
  const label = snap.title ? `${snap.stepId} \xB7 ${snap.title}` : snap.stepId;
14461
- const trimmed = feedback.trim();
14766
+ const trimmed = feedback2.trim();
14462
14767
  const gid = pendingGateIdRef.current;
14463
14768
  if (gid !== null) {
14464
14769
  pauseGate.resolve(
@@ -14558,6 +14863,7 @@ Stay in plan mode \u2014 address the feedback (explore more if needed), then sub
14558
14863
  PlanRefineInput,
14559
14864
  {
14560
14865
  mode: stagedInput.mode,
14866
+ questions: stagedInput.questions,
14561
14867
  onSubmit: handleStagedInputSubmit,
14562
14868
  onCancel: handleStagedInputCancel
14563
14869
  }
@@ -14802,7 +15108,7 @@ Stay in plan mode \u2014 address the feedback (explore more if needed), then sub
14802
15108
  block: pendingEdits.current[0],
14803
15109
  onChoose: handleWalkChoice
14804
15110
  }
14805
- ) : !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(
15111
+ ) : !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(
14806
15112
  ModeStatusBar,
14807
15113
  {
14808
15114
  editMode,
@@ -14822,15 +15128,16 @@ Stay in plan mode \u2014 address the feedback (explore more if needed), then sub
14822
15128
  onHistoryPrev: recallPrev,
14823
15129
  onHistoryNext: recallNext
14824
15130
  }
14825
- ), slashMatches !== null ? /* @__PURE__ */ React60.createElement(
15131
+ )), /* @__PURE__ */ React60.createElement(Box49, { flexDirection: "column", flexShrink: 0, flexWrap: "nowrap" }, slashMatches !== null ? /* @__PURE__ */ React60.createElement(
14826
15132
  SlashSuggestions,
14827
15133
  {
15134
+ key: `slash-suggestions:${slashGroupMode ? "group" : "search"}`,
14828
15135
  matches: slashMatches,
14829
15136
  selectedIndex: slashSelected,
14830
15137
  groupMode: slashGroupMode,
14831
15138
  advancedHidden: slashAdvancedHidden
14832
15139
  }
14833
- ) : null, atState !== null ? /* @__PURE__ */ React60.createElement(AtMentionSuggestions, { state: atState, selectedIndex: atSelected }) : null, slashArgContext ? /* @__PURE__ */ React60.createElement(
15140
+ ) : null, atState !== null ? /* @__PURE__ */ React60.createElement(AtMentionSuggestions, { state: atState, selectedIndex: atSelected }) : null), slashArgContext ? /* @__PURE__ */ React60.createElement(
14834
15141
  SlashArgPicker,
14835
15142
  {
14836
15143
  matches: slashArgMatches,
@@ -14902,7 +15209,7 @@ function Setup({ onReady }) {
14902
15209
  return;
14903
15210
  }
14904
15211
  if (!isPlausibleKey(trimmed)) {
14905
- setError("Doesn't look like a DeepSeek key. They start with 'sk-' and are 30+ chars.");
15212
+ setError("Key looks too short \u2014 paste the full token (16+ chars, no spaces).");
14906
15213
  setValue("");
14907
15214
  return;
14908
15215
  }
@@ -15195,6 +15502,7 @@ function Root({
15195
15502
  progressSink,
15196
15503
  codeMode: appProps.codeMode,
15197
15504
  noDashboard: appProps.noDashboard,
15505
+ mouse: appProps.mouse,
15198
15506
  onSwitchSession: setActiveSession
15199
15507
  }
15200
15508
  ));
@@ -15269,7 +15577,12 @@ async function chatCommand(opts) {
15269
15577
  exitOnCtrlC: true,
15270
15578
  // patchConsole:false — winpty/MINTTY redraw-glitch source.
15271
15579
  patchConsole: false,
15272
- incrementalRendering: true,
15580
+ // incrementalRendering:false — Ink's diff drifts when stringWidth
15581
+ // misjudges CJK / emoji ZWJ width or when async terminal-event
15582
+ // bytes interleave mid-render, leaving residual rows. Full-frame
15583
+ // redraws cost more stdout bytes per flush but eliminate the
15584
+ // ghost class.
15585
+ incrementalRendering: false,
15273
15586
  // Default true — alt-screen is the only mode without scrollback-
15274
15587
  // reflow ghosting. `--no-alt-screen` opts back into scrollback mode
15275
15588
  // for users who need chat output preserved in shell history on exit.
@@ -15287,4 +15600,4 @@ async function chatCommand(opts) {
15287
15600
  export {
15288
15601
  chatCommand
15289
15602
  };
15290
- //# sourceMappingURL=chunk-EINEIIIW.js.map
15603
+ //# sourceMappingURL=chunk-EN4LAZW5.js.map