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
package/dist/index.js CHANGED
@@ -766,6 +766,20 @@ function loadApiKey(path2 = defaultConfigPath()) {
766
766
  if (process.env.DEEPSEEK_API_KEY) return process.env.DEEPSEEK_API_KEY;
767
767
  return readConfig(path2).apiKey;
768
768
  }
769
+ function loadBaseUrl(path2 = defaultConfigPath()) {
770
+ if (process.env.DEEPSEEK_BASE_URL) return process.env.DEEPSEEK_BASE_URL;
771
+ return readConfig(path2).baseUrl;
772
+ }
773
+ function saveBaseUrl(url, path2 = defaultConfigPath()) {
774
+ const cfg = readConfig(path2);
775
+ const trimmed = url.trim();
776
+ if (trimmed) {
777
+ cfg.baseUrl = trimmed;
778
+ } else {
779
+ cfg.baseUrl = void 0;
780
+ }
781
+ writeConfig(cfg, path2);
782
+ }
769
783
  function webSearchEngine(path2 = defaultConfigPath()) {
770
784
  const cfg = readConfig(path2).webSearchEngine;
771
785
  if (cfg === "searxng") return "searxng";
@@ -806,7 +820,8 @@ function addProjectShellAllowed(rootDir, prefix, path2 = defaultConfigPath()) {
806
820
  }
807
821
  function isPlausibleKey(key) {
808
822
  const trimmed = key.trim();
809
- return /^sk-[A-Za-z0-9_-]{16,}$/.test(trimmed);
823
+ if (trimmed.length < 16) return false;
824
+ return !/\s/.test(trimmed);
810
825
  }
811
826
  function redactKey(key) {
812
827
  if (!key) return "";
@@ -889,14 +904,15 @@ var EN = {
889
904
  sections: [
890
905
  {
891
906
  rows: [
892
- { key: "wheel", text: "scrolls the chat history above" },
907
+ { key: "drag", text: "select text \u2014 terminal-native, no modifier needed" },
893
908
  {
894
909
  key: "right-click",
895
- text: "captured by the app \u2014 use Ctrl+V (Win/Linux) or Cmd+V (macOS) to paste"
910
+ text: "your terminal's native menu (paste / copy on Windows Terminal etc.)"
896
911
  },
912
+ { key: "wheel", text: "scrolls chat history (works on web/cloud/SSH terminals too)" },
897
913
  {
898
- key: "Shift + drag",
899
- text: "selects text natively (Option on iTerm2, Shift on Win Term / Alacritty / WezTerm)"
914
+ key: "\u2191 / \u2193",
915
+ text: "scroll chat \xB7 use Ctrl+P / Ctrl+N for prompt history + multi-line cursor"
900
916
  }
901
917
  ]
902
918
  }
@@ -911,7 +927,11 @@ var EN = {
911
927
  rows: [
912
928
  { key: "Enter", text: "submit the prompt" },
913
929
  { key: "Shift+Enter", text: "insert a newline in the prompt" },
914
- { key: "Ctrl+P / Ctrl+N", text: "recall previous / next prompt from history" },
930
+ { key: "\u2191 / \u2193", text: "scroll chat history (mouse wheel routes here too)" },
931
+ {
932
+ key: "Ctrl+P / Ctrl+N",
933
+ text: "previous / next prompt history \xB7 cursor up / down in a multi-line draft"
934
+ },
915
935
  { key: "Ctrl+A / Ctrl+E", text: "jump to start / end of the current line" },
916
936
  { key: "Ctrl+W", text: "delete the word before the cursor" },
917
937
  { key: "Ctrl+U", text: "clear the entire prompt buffer" },
@@ -919,10 +939,6 @@ var EN = {
919
939
  { key: "Shift+Tab", text: "edit-gate: toggle review \u2194 AUTO mode" },
920
940
  { key: "Esc", text: "dismiss picker \xB7 abort the running model turn" },
921
941
  { key: "Ctrl+C", text: "abort the running model turn (NOT copy \u2014 see clipboard)" },
922
- {
923
- key: "\u2191 / \u2193",
924
- text: "scroll chat history (PromptInput cursor when buffer non-empty)"
925
- },
926
942
  { key: "PgUp / PgDn", text: "scroll chat history a page at a time" },
927
943
  { key: "End", text: "jump chat to the most recent line" }
928
944
  ]
@@ -930,18 +946,18 @@ var EN = {
930
946
  {
931
947
  title: "mouse",
932
948
  rows: [
933
- { key: "wheel", text: "scrolls the chat history" },
934
- { key: "right-click", text: "captured by the app \u2014 use Ctrl+V / Cmd+V to paste" },
935
- { key: "left-click", text: "captured (no action yet \u2014 reserved for future use)" }
949
+ { key: "wheel", text: "scrolls chat history (works on web/cloud/SSH terminals too)" },
950
+ { key: "drag", text: "selects text natively \u2014 direct copy works, no modifier" },
951
+ { key: "right-click", text: "terminal-native (paste menu on Windows Terminal etc.)" }
936
952
  ]
937
953
  },
938
954
  {
939
955
  title: "copy / paste",
940
956
  rows: [
941
- { key: "select text", text: "hold Shift while dragging (Option on iTerm2)" },
957
+ { key: "select text", text: "drag to select \u2014 terminal-native (no modifier needed)" },
942
958
  {
943
959
  key: "copy",
944
- text: "Ctrl+Shift+C (Win/Linux) \xB7 Cmd+C (macOS) \u2014 terminal-native after selection"
960
+ text: "Ctrl+Shift+C (Win/Linux) \xB7 Cmd+C (macOS) \u2014 or auto-copy-on-select if your terminal does it"
945
961
  },
946
962
  { key: "paste", text: "Ctrl+V or Ctrl+Shift+V (Win/Linux) \xB7 Cmd+V (macOS)" },
947
963
  {
@@ -959,7 +975,7 @@ var EN = {
959
975
  ]
960
976
  }
961
977
  ],
962
- footer: "Mouse tracking is on so the wheel scrolls chat instead of moving the cursor \u2014 that's why right-click no longer does the terminal's native paste."
978
+ footer: "Wheel\u2192\u2191/\u2193 via DECSET 1007 (alternate-scroll) \u2014 wheel scrolls chat on most terminals (web/cloud/SSH included) without disturbing native selection. Drag to select stays modifier-free. Pass --no-mouse to opt out."
963
979
  },
964
980
  tipShownOnce: "shown once",
965
981
  modelOverride: "override the default model",
@@ -1160,8 +1176,22 @@ var EN = {
1160
1176
  apiKeyGetOne: "Get one at: https://platform.deepseek.com/api_keys",
1161
1177
  apiKeySavedLocally: "Saved locally to {path}",
1162
1178
  apiKeyInputLabel: "key \u203A ",
1163
- apiKeyInvalid: "Doesn't look like a DeepSeek key. They start with 'sk-' and are 30+ chars.",
1179
+ apiKeyInvalid: "Key looks too short \u2014 paste the full token (16+ chars, no spaces).",
1164
1180
  apiKeyPreview: "preview: {redacted}",
1181
+ themeTitle: "Choose a theme",
1182
+ themeSubtitle: "Preview updates live as you navigate. Change later with /theme.",
1183
+ themeSampleHeading: "Sample",
1184
+ themeFooter: "[\u2191\u2193] navigate \xB7 [Enter] confirm \xB7 [Esc] cancel",
1185
+ themeCaption: {
1186
+ default: "GitHub dark (default)",
1187
+ dark: "Cool dark tones",
1188
+ light: "Clean light mode",
1189
+ "tokyo-night": "Tokyo Night palette",
1190
+ "github-dark": "GitHub dark",
1191
+ "github-light": "GitHub light",
1192
+ "high-contrast": "Accessibility"
1193
+ },
1194
+ reviewLabelTheme: "Theme",
1165
1195
  presetTitle: "Pick a preset",
1166
1196
  mcpTitle: "Which MCP servers should Reasonix wire up for you?",
1167
1197
  mcpUserArgsHint: "(you'll provide {arg})",
@@ -1188,6 +1218,68 @@ var EN = {
1188
1218
  selectFooter: "[\u2191\u2193] navigate \xB7 [Enter] confirm \xB7 [Esc] cancel",
1189
1219
  stepCounter: "Step {step}/{total} \xB7 "
1190
1220
  },
1221
+ planFlow: {
1222
+ approveCardTitle: "Approve plan",
1223
+ approveCardMetaRight: "awaiting",
1224
+ openQuestionsBanner: "\u25B2 the plan flags open questions or risks \u2014 pick {refine} to write concrete answers before the model moves on.",
1225
+ openQuestionsHeader: "Open questions / risks",
1226
+ truncatedBodyMore: "\u2026 {n} more line above in scrollback",
1227
+ truncatedBodyMorePlural: "\u2026 {n} more lines above in scrollback",
1228
+ picker: {
1229
+ accept: "accept",
1230
+ acceptHint: "run it now, in order",
1231
+ refine: "refine",
1232
+ refineHint: "give the agent more guidance, draft a new plan",
1233
+ revise: "revise",
1234
+ reviseHint: "edit the plan inline before running (skip / reorder steps)",
1235
+ reject: "reject",
1236
+ rejectHint: "discard, agent will retry from scratch"
1237
+ },
1238
+ refineFooter: "\u23CE send \xB7 esc return to picker",
1239
+ refineQuestionsHeading: "Answer these or describe the change you want:",
1240
+ modes: {
1241
+ approve: {
1242
+ title: "approving \u2014 any last instructions?",
1243
+ hint: "Answer questions the plan raised, add constraints, or just press Enter to approve as-is.",
1244
+ blankHint: " (Enter with blank = approve without extra instructions.)"
1245
+ },
1246
+ refine: {
1247
+ title: "refining \u2014 what should the model change?",
1248
+ hint: "Describe what's wrong or missing, or answer questions the plan raised.",
1249
+ blankHint: " (Enter with blank = let the model pick safe defaults for any open questions.)"
1250
+ },
1251
+ reject: {
1252
+ title: "rejecting \u2014 tell the model why (optional)",
1253
+ hint: "Say what the model got wrong about your goal, or what you actually want instead.",
1254
+ blankHint: " (Enter with blank = cancel without explanation; the model will ask what you want.)"
1255
+ },
1256
+ "checkpoint-revise": {
1257
+ title: "revising \u2014 what should change before the next step?",
1258
+ hint: "Scope change, skip steps, alternative approach \u2014 the model adjusts the remaining plan.",
1259
+ blankHint: " (Enter with blank = continue with the current plan.)"
1260
+ },
1261
+ "choice-custom": {
1262
+ title: "custom answer \u2014 type whatever fits",
1263
+ hint: "Free-form reply. The model reads it verbatim and proceeds \u2014 no need to match the listed options.",
1264
+ blankHint: " (Enter with blank = ask the model what you actually want.)"
1265
+ }
1266
+ },
1267
+ checkpoint: {
1268
+ title: "Checkpoint \u2014 step done",
1269
+ continue: "Continue \u2014 run the next step",
1270
+ continueHint: "Model resumes with the next step.",
1271
+ revise: "Revise \u2014 give feedback before the next step",
1272
+ reviseHint: "Stay paused, type guidance; model adjusts the remaining plan.",
1273
+ stop: "Stop \u2014 end the plan here",
1274
+ stopHint: "Model summarizes what was done and ends."
1275
+ },
1276
+ stepList: {
1277
+ counter: "{total} steps",
1278
+ counterSingular: "{total} step",
1279
+ counterDone: "{done}/{total} done ({pct}%) \xB7 {total} steps",
1280
+ counterDoneSingular: "{done}/{total} done ({pct}%) \xB7 {total} step"
1281
+ }
1282
+ },
1191
1283
  app: {
1192
1284
  walkCancelledRemaining: "\u25B8 walk cancelled \u2014 {count} block(s) still pending.",
1193
1285
  walkCancelled: "\u25B8 walk cancelled.",
@@ -1343,8 +1435,8 @@ var EN = {
1343
1435
  updateNpxForce: "to force a refresh sooner: `npm cache clean --force`.",
1344
1436
  updateUpgradeHint: "to upgrade, exit this session and run:",
1345
1437
  updateUpgradeCmd1: " reasonix update (interactive, dry-run supported via --dry-run)",
1346
- updateUpgradeCmd2: " npm install -g reasonix@latest (direct)",
1347
- updateInSessionDisabled: "in-session install is deliberately disabled \u2014 the npm spawn would",
1438
+ updateUpgradeCmd2: " {command} (direct)",
1439
+ updateInSessionDisabled: "in-session install is deliberately disabled \u2014 the install spawn would",
1348
1440
  updateInSessionDisabled2: "corrupt this TUI's rendering and Windows can lock the running binary.",
1349
1441
  statsNoData: "no usage data yet.",
1350
1442
  statsEveryTurn: "every turn you run here appends one record \u2014 this session's turns",
@@ -1598,6 +1690,56 @@ var EN = {
1598
1690
  newCreated: "\u25B8 created skill: {name}\n {path}\n edit it, then `/skill {name}` to invoke",
1599
1691
  newError: "\u25B2 /skill new failed: {reason}"
1600
1692
  }
1693
+ },
1694
+ cardTitles: {
1695
+ usage: "usage",
1696
+ context: "context",
1697
+ search: "search",
1698
+ subagent: "subagent",
1699
+ reply: "reply",
1700
+ reasoning: "reasoning",
1701
+ reasoningAborted: "reasoning (aborted)",
1702
+ reasoningEllipsis: "reasoning\u2026",
1703
+ error: "error"
1704
+ },
1705
+ cardLabels: {
1706
+ prompt: "prompt",
1707
+ reason: "reason",
1708
+ output: "output",
1709
+ cache: "cache",
1710
+ session: "session",
1711
+ balance: "balance",
1712
+ turn: "turn",
1713
+ system: "system",
1714
+ tools: "tools",
1715
+ log: "log",
1716
+ input: "input",
1717
+ topTools: "top tools",
1718
+ logMsgs: "log msgs",
1719
+ hitSingular: "{count} hit \xB7 {files} file",
1720
+ hitsPlural: "{count} hits \xB7 {files} files",
1721
+ moreHitSingular: "\u22EE +{count} more hit",
1722
+ moreHitsPlural: "\u22EE +{count} more hits",
1723
+ earlierLine: "\u22EE {count} earlier line (use /tool to read full)",
1724
+ earlierLines: "\u22EE {count} earlier lines (use /tool to read full)",
1725
+ earlierStackLine: "\u22EE {count} earlier stack line hidden",
1726
+ earlierStackLines: "\u22EE {count} earlier stack lines hidden",
1727
+ agent: "agent \xB7 {name}",
1728
+ response: "response",
1729
+ writing: "writing \u2026",
1730
+ tok: "tok",
1731
+ pilcrow: "\xB6",
1732
+ aborted: "aborted",
1733
+ truncatedByEsc: "[truncated by esc]",
1734
+ rejected: "rejected",
1735
+ exit: "exit {code}",
1736
+ bytesIn: "{bytes} in",
1737
+ elapsedSec: "{secs}s",
1738
+ stackTrace: "stack trace",
1739
+ retries: "retries",
1740
+ reasoningLabel: "reasoning \xB7 {count} \xB6",
1741
+ runningLabel: "running",
1742
+ workingLabel: "working"
1601
1743
  }
1602
1744
  };
1603
1745
 
@@ -1673,14 +1815,15 @@ var zhCN = {
1673
1815
  sections: [
1674
1816
  {
1675
1817
  rows: [
1676
- { key: "\u6EDA\u8F6E", text: "\u6EDA\u52A8\u4E0A\u65B9\u7684\u804A\u5929\u8BB0\u5F55" },
1818
+ { key: "\u62D6\u52A8", text: "\u76F4\u63A5\u9009\u4E2D\u6587\u672C \u2014 \u7EC8\u7AEF\u539F\u751F\uFF0C\u4E0D\u9700\u8981\u6309 Shift" },
1677
1819
  {
1678
1820
  key: "\u53F3\u952E",
1679
- text: "\u5DF2\u88AB\u5E94\u7528\u63A5\u7BA1 \u2014 \u7528 Ctrl+V\uFF08Win/Linux\uFF09\u6216 Cmd+V\uFF08macOS\uFF09\u7C98\u8D34"
1821
+ text: "\u7EC8\u7AEF\u539F\u751F\u83DC\u5355\uFF08Windows Terminal \u7B49\u7684\u590D\u5236 / \u7C98\u8D34\uFF09"
1680
1822
  },
1823
+ { key: "\u6EDA\u8F6E", text: "\u6EDA\u52A8\u804A\u5929\u8BB0\u5F55\uFF08Web / \u4E91\u7AEF / SSH \u7EC8\u7AEF\u4E5F\u80FD\u7528\uFF09" },
1681
1824
  {
1682
- key: "Shift + \u62D6\u52A8",
1683
- text: "\u539F\u751F\u9009\u4E2D\u6587\u672C\uFF08iTerm2 \u7528 Option\uFF0CWin Term / Alacritty / WezTerm \u7528 Shift\uFF09"
1825
+ key: "\u2191 / \u2193",
1826
+ text: "\u6EDA\u52A8\u804A\u5929 \xB7 \u8F93\u5165\u6846\u5386\u53F2 + \u591A\u884C\u5149\u6807\u7528 Ctrl+P / Ctrl+N"
1684
1827
  }
1685
1828
  ]
1686
1829
  }
@@ -1695,7 +1838,11 @@ var zhCN = {
1695
1838
  rows: [
1696
1839
  { key: "Enter", text: "\u63D0\u4EA4\u8F93\u5165" },
1697
1840
  { key: "Shift+Enter", text: "\u5728\u8F93\u5165\u6846\u4E2D\u63D2\u5165\u6362\u884C" },
1698
- { key: "Ctrl+P / Ctrl+N", text: "\u8C03\u51FA\u4E0A\u4E00\u6761 / \u4E0B\u4E00\u6761\u5386\u53F2\u63D0\u793A" },
1841
+ { key: "\u2191 / \u2193", text: "\u6EDA\u52A8\u804A\u5929\u8BB0\u5F55\uFF08\u9F20\u6807\u6EDA\u8F6E\u4E5F\u8D70\u8FD9\u6761\u8DEF\u5F84\uFF09" },
1842
+ {
1843
+ key: "Ctrl+P / Ctrl+N",
1844
+ text: "\u4E0A\u4E00\u6761 / \u4E0B\u4E00\u6761\u8F93\u5165\u5386\u53F2 \xB7 \u591A\u884C\u8349\u7A3F\u4E2D\u6309\u884C\u79FB\u52A8\u5149\u6807"
1845
+ },
1699
1846
  { key: "Ctrl+A / Ctrl+E", text: "\u8DF3\u5230\u5F53\u524D\u884C\u7684\u5F00\u5934 / \u7ED3\u5C3E" },
1700
1847
  { key: "Ctrl+W", text: "\u5220\u9664\u5149\u6807\u524D\u7684\u4E00\u4E2A\u8BCD" },
1701
1848
  { key: "Ctrl+U", text: "\u6E05\u7A7A\u6574\u4E2A\u8F93\u5165\u7F13\u51B2\u533A" },
@@ -1703,7 +1850,6 @@ var zhCN = {
1703
1850
  { key: "Shift+Tab", text: "\u7F16\u8F91\u95E8\u63A7\uFF1A\u5207\u6362 \u9884\u89C8 \u2194 \u81EA\u52A8 \u6A21\u5F0F" },
1704
1851
  { key: "Esc", text: "\u5173\u95ED\u5F39\u51FA\u9009\u62E9\u5668 \xB7 \u4E2D\u6B62\u5F53\u524D\u6A21\u578B\u56DE\u5408" },
1705
1852
  { key: "Ctrl+C", text: "\u4E2D\u6B62\u5F53\u524D\u6A21\u578B\u56DE\u5408\uFF08\u4E0D\u662F\u590D\u5236 \u2014 \u89C1\u526A\u8D34\u677F\u6BB5\uFF09" },
1706
- { key: "\u2191 / \u2193", text: "\u6EDA\u52A8\u804A\u5929\u8BB0\u5F55\uFF08\u7F13\u51B2\u533A\u975E\u7A7A\u65F6\u4E3A\u5149\u6807\u79FB\u52A8\uFF09" },
1707
1853
  { key: "PgUp / PgDn", text: "\u6574\u9875\u6EDA\u52A8\u804A\u5929\u8BB0\u5F55" },
1708
1854
  { key: "End", text: "\u8DF3\u5230\u804A\u5929\u7684\u6700\u65B0\u4E00\u884C" }
1709
1855
  ]
@@ -1711,18 +1857,18 @@ var zhCN = {
1711
1857
  {
1712
1858
  title: "\u9F20\u6807",
1713
1859
  rows: [
1714
- { key: "\u6EDA\u8F6E", text: "\u6EDA\u52A8\u804A\u5929\u8BB0\u5F55" },
1715
- { key: "\u53F3\u952E", text: "\u88AB\u5E94\u7528\u63A5\u7BA1 \u2014 \u7528 Ctrl+V / Cmd+V \u7C98\u8D34" },
1716
- { key: "\u5DE6\u952E", text: "\u88AB\u63A5\u7BA1\uFF08\u6682\u65E0\u52A8\u4F5C \u2014 \u9884\u7559\u7ED9\u540E\u7EED\u529F\u80FD\uFF09" }
1860
+ { key: "\u6EDA\u8F6E", text: "\u6EDA\u52A8\u804A\u5929\u8BB0\u5F55\uFF08Web / \u4E91\u7AEF / SSH \u7EC8\u7AEF\u4E5F\u80FD\u7528\uFF09" },
1861
+ { key: "\u62D6\u52A8", text: "\u539F\u751F\u9009\u4E2D\u6587\u672C \u2014 \u76F4\u63A5\u590D\u5236\uFF0C\u4E0D\u9700\u8981\u4FEE\u9970\u952E" },
1862
+ { key: "\u53F3\u952E", text: "\u7EC8\u7AEF\u539F\u751F\uFF08Windows Terminal \u7B49\u7684\u7C98\u8D34\u83DC\u5355\uFF09" }
1717
1863
  ]
1718
1864
  },
1719
1865
  {
1720
1866
  title: "\u590D\u5236 / \u7C98\u8D34",
1721
1867
  rows: [
1722
- { key: "\u9009\u4E2D\u6587\u5B57", text: "\u62D6\u52A8\u65F6\u6309\u4F4F Shift\uFF08iTerm2 \u7528 Option\uFF09" },
1868
+ { key: "\u9009\u4E2D\u6587\u5B57", text: "\u76F4\u63A5\u62D6\u52A8 \u2014 \u7EC8\u7AEF\u539F\u751F\uFF08\u4E0D\u9700\u8981\u4EFB\u4F55\u4FEE\u9970\u952E\uFF09" },
1723
1869
  {
1724
1870
  key: "\u590D\u5236",
1725
- text: "Ctrl+Shift+C\uFF08Win/Linux\uFF09\xB7 Cmd+C\uFF08macOS\uFF09\u2014 \u9009\u4E2D\u540E\u7531\u7EC8\u7AEF\u539F\u751F\u5904\u7406"
1871
+ text: "Ctrl+Shift+C\uFF08Win/Linux\uFF09\xB7 Cmd+C\uFF08macOS\uFF09\u2014 \u6216\u9009\u4E2D\u5373\u590D\u5236\uFF08\u770B\u7EC8\u7AEF\u8BBE\u7F6E\uFF09"
1726
1872
  },
1727
1873
  { key: "\u7C98\u8D34", text: "Ctrl+V \u6216 Ctrl+Shift+V\uFF08Win/Linux\uFF09\xB7 Cmd+V\uFF08macOS\uFF09" },
1728
1874
  {
@@ -1740,7 +1886,7 @@ var zhCN = {
1740
1886
  ]
1741
1887
  }
1742
1888
  ],
1743
- footer: "\u9F20\u6807\u8FFD\u8E2A\u5DF2\u5F00\u542F\uFF0C\u6EDA\u8F6E\u7528\u6765\u6EDA\u52A8\u804A\u5929\u800C\u4E0D\u662F\u79FB\u52A8\u5149\u6807 \u2014 \u8FD9\u4E5F\u662F\u53F3\u952E\u4E0D\u518D\u89E6\u53D1\u7EC8\u7AEF\u539F\u751F\u7C98\u8D34\u7684\u539F\u56E0\u3002"
1889
+ footer: "\u901A\u8FC7 DECSET 1007\uFF08alternate-scroll\uFF09\uFF0C\u7EC8\u7AEF\u628A\u6EDA\u8F6E\u7FFB\u8BD1\u6210 \u2191/\u2193 \u53D1\u7ED9\u5E94\u7528 \u2014 \u5927\u591A\u6570\u7EC8\u7AEF\uFF08\u542B Web / \u4E91\u7AEF / SSH\uFF09\u90FD\u80FD\u6EDA\uFF0C\u4E14\u4E0D\u5F71\u54CD\u7EC8\u7AEF\u539F\u751F\u9009\u533A\u3002\u76F4\u63A5\u62D6\u52A8\u9009\u4E2D\u6587\u672C\u65E0\u9700 Shift\u3002\u4F20\u5165 --no-mouse \u53EF\u5173\u95ED\u3002"
1744
1890
  },
1745
1891
  tipShownOnce: "\u4EC5\u663E\u793A\u4E00\u6B21",
1746
1892
  modelOverride: "\u8986\u76D6\u9ED8\u8BA4\u6A21\u578B",
@@ -1945,8 +2091,22 @@ var zhCN = {
1945
2091
  apiKeyGetOne: "\u5728\u6B64\u83B7\u53D6\uFF1Ahttps://platform.deepseek.com/api_keys",
1946
2092
  apiKeySavedLocally: "\u4FDD\u5B58\u5728\u672C\u5730\uFF1A{path}",
1947
2093
  apiKeyInputLabel: "key \u203A ",
1948
- apiKeyInvalid: "\u8FD9\u770B\u8D77\u6765\u4E0D\u50CF DeepSeek \u7684 key\u3002\u5B83\u4EEC\u4EE5 'sk-' \u5F00\u5934\uFF0C30 \u5B57\u7B26\u4EE5\u4E0A\u3002",
2094
+ apiKeyInvalid: "key \u957F\u5EA6\u4E0D\u8DB3\u2014\u2014\u8BF7\u7C98\u8D34\u5B8C\u6574 token\uFF0816+ \u5B57\u7B26\uFF0C\u4E0D\u542B\u7A7A\u683C\uFF09\u3002",
1949
2095
  apiKeyPreview: "\u9884\u89C8\uFF1A{redacted}",
2096
+ themeTitle: "\u9009\u62E9\u4E3B\u9898",
2097
+ themeSubtitle: "\u65B9\u5411\u952E\u5207\u6362\u65F6\u5373\u65F6\u9884\u89C8\u6548\u679C\uFF0C\u4E4B\u540E\u53EF\u7528 /theme \u66F4\u6539\u3002",
2098
+ themeSampleHeading: "\u793A\u4F8B",
2099
+ themeFooter: "[\u2191\u2193] \u79FB\u52A8 \xB7 [Enter] \u786E\u8BA4 \xB7 [Esc] \u53D6\u6D88",
2100
+ themeCaption: {
2101
+ default: "GitHub \u6DF1\u8272\uFF08\u9ED8\u8BA4\uFF09",
2102
+ dark: "\u6DF1\u8272\u8C03",
2103
+ light: "\u6E05\u723D\u6D45\u8272",
2104
+ "tokyo-night": "\u4E1C\u4EAC\u591C\u8272",
2105
+ "github-dark": "GitHub \u6DF1\u8272",
2106
+ "github-light": "GitHub \u6D45\u8272",
2107
+ "high-contrast": "\u9AD8\u5BF9\u6BD4\u5EA6\uFF08\u65E0\u969C\u788D\uFF09"
2108
+ },
2109
+ reviewLabelTheme: "\u4E3B\u9898",
1950
2110
  presetTitle: "\u9009\u62E9\u9884\u8BBE",
1951
2111
  mcpTitle: "Reasonix \u8981\u4E3A\u4F60\u63A5\u5165\u54EA\u4E9B MCP \u670D\u52A1\u5668\uFF1F",
1952
2112
  mcpUserArgsHint: "\uFF08\u9700\u8981\u4F60\u63D0\u4F9B {arg}\uFF09",
@@ -1973,6 +2133,68 @@ var zhCN = {
1973
2133
  selectFooter: "[\u2191\u2193] \u79FB\u52A8 \xB7 [Enter] \u786E\u8BA4 \xB7 [Esc] \u53D6\u6D88",
1974
2134
  stepCounter: "\u6B65\u9AA4 {step}/{total} \xB7 "
1975
2135
  },
2136
+ planFlow: {
2137
+ approveCardTitle: "\u786E\u8BA4\u8BA1\u5212",
2138
+ approveCardMetaRight: "\u7B49\u5F85\u4E2D",
2139
+ openQuestionsBanner: "\u25B2 \u8BA1\u5212\u4E2D\u6807\u8BB0\u4E86\u5F85\u786E\u8BA4\u7684\u95EE\u9898\u6216\u98CE\u9669 \u2014\u2014 \u8BF7\u9009 {refine} \u7ED9\u51FA\u660E\u786E\u7B54\u6848\uFF0C\u518D\u8BA9\u6A21\u578B\u7EE7\u7EED\u3002",
2140
+ openQuestionsHeader: "\u5F85\u786E\u8BA4 / \u98CE\u9669",
2141
+ truncatedBodyMore: "\u2026 \u8FD8\u6709 {n} \u884C\u5728\u4E0A\u65B9\u6EDA\u52A8\u5386\u53F2\u4E2D",
2142
+ truncatedBodyMorePlural: "\u2026 \u8FD8\u6709 {n} \u884C\u5728\u4E0A\u65B9\u6EDA\u52A8\u5386\u53F2\u4E2D",
2143
+ picker: {
2144
+ accept: "\u91C7\u7EB3",
2145
+ acceptHint: "\u7ACB\u5373\u6309\u987A\u5E8F\u6267\u884C",
2146
+ refine: "\u7EC6\u5316",
2147
+ refineHint: "\u7ED9\u6A21\u578B\u66F4\u591A\u6307\u5F15\uFF0C\u91CD\u65B0\u51FA\u4E00\u7248\u8BA1\u5212",
2148
+ revise: "\u6539\u5199",
2149
+ reviseHint: "\u5728\u6267\u884C\u524D\u5C31\u5730\u7F16\u8F91\u8BA1\u5212\uFF08\u8DF3\u8FC7 / \u91CD\u6392\u6B65\u9AA4\uFF09",
2150
+ reject: "\u9A73\u56DE",
2151
+ rejectHint: "\u4E22\u5F03\uFF0C\u8BA9\u6A21\u578B\u4ECE\u5934\u518D\u6765"
2152
+ },
2153
+ refineFooter: "\u23CE \u53D1\u9001 \xB7 esc \u8FD4\u56DE\u9009\u9879",
2154
+ refineQuestionsHeading: "\u56DE\u7B54\u4EE5\u4E0B\u95EE\u9898\uFF0C\u6216\u76F4\u63A5\u8BF4\u660E\u4F60\u60F3\u8981\u7684\u4FEE\u6539\uFF1A",
2155
+ modes: {
2156
+ approve: {
2157
+ title: "\u91C7\u7EB3 \u2014\u2014 \u8FD8\u6709\u8865\u5145\u6307\u793A\u5417\uFF1F",
2158
+ hint: "\u56DE\u7B54\u8BA1\u5212\u4E2D\u7684\u95EE\u9898\u3001\u8865\u5145\u7EA6\u675F\uFF0C\u6216\u76F4\u63A5\u56DE\u8F66\u6309\u73B0\u72B6\u91C7\u7EB3\u3002",
2159
+ blankHint: "\uFF08\u7559\u7A7A\u56DE\u8F66 = \u4E0D\u9644\u52A0\u6307\u793A\u76F4\u63A5\u91C7\u7EB3\u3002\uFF09"
2160
+ },
2161
+ refine: {
2162
+ title: "\u7EC6\u5316 \u2014\u2014 \u6A21\u578B\u5E94\u8BE5\u6539\u4EC0\u4E48\uFF1F",
2163
+ hint: "\u8BF4\u660E\u95EE\u9898\u5728\u54EA\u3001\u7F3A\u4EC0\u4E48\uFF0C\u6216\u8005\u56DE\u7B54\u8BA1\u5212\u63D0\u51FA\u7684\u7591\u95EE\u3002",
2164
+ blankHint: "\uFF08\u7559\u7A7A\u56DE\u8F66 = \u8BA9\u6A21\u578B\u5BF9\u6240\u6709\u5F85\u786E\u8BA4\u95EE\u9898\u9009\u7528\u5B89\u5168\u9ED8\u8BA4\u3002\uFF09"
2165
+ },
2166
+ reject: {
2167
+ title: "\u9A73\u56DE \u2014\u2014 \u544A\u8BC9\u6A21\u578B\u539F\u56E0\uFF08\u53EF\u9009\uFF09",
2168
+ hint: "\u8BF4\u660E\u6A21\u578B\u5BF9\u4F60\u7684\u76EE\u6807\u7406\u89E3\u9519\u5728\u54EA\u91CC\uFF0C\u6216\u4F60\u771F\u6B63\u60F3\u8981\u4EC0\u4E48\u3002",
2169
+ blankHint: "\uFF08\u7559\u7A7A\u56DE\u8F66 = \u4E0D\u89E3\u91CA\u76F4\u63A5\u53D6\u6D88\uFF1B\u6A21\u578B\u4F1A\u53CD\u8FC7\u6765\u95EE\u4F60\u60F3\u8981\u4EC0\u4E48\u3002\uFF09"
2170
+ },
2171
+ "checkpoint-revise": {
2172
+ title: "\u6539\u5199 \u2014\u2014 \u4E0B\u4E00\u6B65\u524D\u8981\u8C03\u6574\u4EC0\u4E48\uFF1F",
2173
+ hint: "\u8303\u56F4\u8C03\u6574\u3001\u8DF3\u8FC7\u6B65\u9AA4\u3001\u6362\u4E2A\u601D\u8DEF \u2014\u2014 \u6A21\u578B\u4F1A\u636E\u6B64\u4FEE\u6539\u5269\u4F59\u6B65\u9AA4\u3002",
2174
+ blankHint: "\uFF08\u7559\u7A7A\u56DE\u8F66 = \u6309\u5F53\u524D\u8BA1\u5212\u7EE7\u7EED\u3002\uFF09"
2175
+ },
2176
+ "choice-custom": {
2177
+ title: "\u81EA\u5B9A\u4E49\u56DE\u7B54 \u2014\u2014 \u60F3\u8BF4\u4EC0\u4E48\u90FD\u884C",
2178
+ hint: "\u81EA\u7531\u6587\u672C\u3002\u6A21\u578B\u4F1A\u539F\u6837\u8BFB\u53D6\u5E76\u7EE7\u7EED \u2014\u2014 \u4E0D\u5FC5\u5339\u914D\u5019\u9009\u9879\u3002",
2179
+ blankHint: "\uFF08\u7559\u7A7A\u56DE\u8F66 = \u8BA9\u6A21\u578B\u53CD\u8FC7\u6765\u95EE\u4F60\u60F3\u8981\u4EC0\u4E48\u3002\uFF09"
2180
+ }
2181
+ },
2182
+ checkpoint: {
2183
+ title: "\u68C0\u67E5\u70B9 \u2014\u2014 \u5F53\u524D\u6B65\u9AA4\u5DF2\u5B8C\u6210",
2184
+ continue: "\u7EE7\u7EED \u2014\u2014 \u6267\u884C\u4E0B\u4E00\u6B65",
2185
+ continueHint: "\u6A21\u578B\u4ECE\u4E0B\u4E00\u6B65\u7EE7\u7EED\u3002",
2186
+ revise: "\u8C03\u6574 \u2014\u2014 \u5728\u4E0B\u4E00\u6B65\u524D\u7ED9\u53CD\u9988",
2187
+ reviseHint: "\u5148\u6682\u505C\uFF0C\u8F93\u5165\u6307\u5F15\uFF1B\u6A21\u578B\u4F1A\u8C03\u6574\u5269\u4F59\u8BA1\u5212\u3002",
2188
+ stop: "\u505C\u6B62 \u2014\u2014 \u5728\u6B64\u7ED3\u675F\u8BA1\u5212",
2189
+ stopHint: "\u6A21\u578B\u603B\u7ED3\u5DF2\u5B8C\u6210\u7684\u5DE5\u4F5C\u5E76\u7ED3\u675F\u3002"
2190
+ },
2191
+ stepList: {
2192
+ counter: "{total} \u4E2A\u6B65\u9AA4",
2193
+ counterSingular: "{total} \u4E2A\u6B65\u9AA4",
2194
+ counterDone: "{done}/{total} \u5DF2\u5B8C\u6210\uFF08{pct}%\uFF09 \xB7 \u5171 {total} \u6B65",
2195
+ counterDoneSingular: "{done}/{total} \u5DF2\u5B8C\u6210\uFF08{pct}%\uFF09 \xB7 \u5171 {total} \u6B65"
2196
+ }
2197
+ },
1976
2198
  app: {
1977
2199
  walkCancelledRemaining: "\u25B8 \u6D4F\u89C8\u5DF2\u53D6\u6D88 \u2014 \u8FD8\u6709 {count} \u4E2A\u5F85\u5904\u7406\u7F16\u8F91\u5757\u3002",
1978
2200
  walkCancelled: "\u25B8 \u6D4F\u89C8\u5DF2\u53D6\u6D88\u3002",
@@ -2128,8 +2350,8 @@ var zhCN = {
2128
2350
  updateNpxForce: "\u8981\u5F3A\u5236\u5237\u65B0\uFF1A`npm cache clean --force`\u3002",
2129
2351
  updateUpgradeHint: "\u8981\u5347\u7EA7\uFF0C\u8BF7\u9000\u51FA\u6B64\u4F1A\u8BDD\u5E76\u8FD0\u884C\uFF1A",
2130
2352
  updateUpgradeCmd1: " reasonix update \uFF08\u4EA4\u4E92\u5F0F\uFF0C\u652F\u6301 --dry-run \u9884\u89C8\uFF09",
2131
- updateUpgradeCmd2: " npm install -g reasonix@latest \uFF08\u76F4\u63A5\u5B89\u88C5\uFF09",
2132
- updateInSessionDisabled: "\u4F1A\u8BDD\u5185\u5B89\u88C5\u88AB\u523B\u610F\u7981\u7528 \u2014 npm spawn \u4F1A",
2353
+ updateUpgradeCmd2: " {command} \uFF08\u76F4\u63A5\u5B89\u88C5\uFF09",
2354
+ updateInSessionDisabled: "\u4F1A\u8BDD\u5185\u5B89\u88C5\u88AB\u523B\u610F\u7981\u7528 \u2014 \u5B89\u88C5\u547D\u4EE4\u4F1A",
2133
2355
  updateInSessionDisabled2: "\u7834\u574F\u6B64 TUI \u7684\u6E32\u67D3\uFF0C\u4E14 Windows \u53EF\u80FD\u9501\u5B9A\u8FD0\u884C\u4E2D\u7684\u4E8C\u8FDB\u5236\u6587\u4EF6\u3002",
2134
2356
  statsNoData: "\u5C1A\u65E0\u4F7F\u7528\u6570\u636E\u3002",
2135
2357
  statsEveryTurn: "\u60A8\u5728\u6B64\u8FD0\u884C\u7684\u6BCF\u4E00\u8F6E\u90FD\u4F1A\u8FFD\u52A0\u4E00\u6761\u8BB0\u5F55 \u2014 \u6B64\u4F1A\u8BDD\u7684\u8F6E\u6B21",
@@ -2383,6 +2605,56 @@ var zhCN = {
2383
2605
  newCreated: "\u25B8 \u5DF2\u521B\u5EFA\u6280\u80FD\uFF1A{name}\n {path}\n \u7F16\u8F91\u540E\u7528 `/skill {name}` \u8C03\u7528",
2384
2606
  newError: "\u25B2 /skill new \u5931\u8D25\uFF1A{reason}"
2385
2607
  }
2608
+ },
2609
+ cardTitles: {
2610
+ usage: "\u7528\u91CF",
2611
+ context: "\u4E0A\u4E0B\u6587",
2612
+ search: "\u641C\u7D22",
2613
+ subagent: "\u5B50\u4EE3\u7406",
2614
+ reply: "\u56DE\u590D",
2615
+ reasoning: "\u63A8\u7406\u4E2D",
2616
+ reasoningAborted: "\u63A8\u7406\uFF08\u5DF2\u4E2D\u6B62\uFF09",
2617
+ reasoningEllipsis: "\u63A8\u7406\u4E2D\u2026",
2618
+ error: "\u9519\u8BEF"
2619
+ },
2620
+ cardLabels: {
2621
+ prompt: "\u63D0\u793A",
2622
+ reason: "\u63A8\u7406",
2623
+ output: "\u8F93\u51FA",
2624
+ cache: "\u7F13\u5B58",
2625
+ session: "\u4F1A\u8BDD",
2626
+ balance: "\u4F59\u989D",
2627
+ turn: "\u8F6E",
2628
+ system: "\u7CFB\u7EDF",
2629
+ tools: "\u5DE5\u5177",
2630
+ log: "\u65E5\u5FD7",
2631
+ input: "\u8F93\u5165",
2632
+ topTools: "Top \u5DE5\u5177",
2633
+ logMsgs: "\u65E5\u5FD7\u6D88\u606F",
2634
+ hitSingular: "{count} \u6761\u7ED3\u679C \xB7 {files} \u4E2A\u6587\u4EF6",
2635
+ hitsPlural: "{count} \u6761\u7ED3\u679C \xB7 {files} \u4E2A\u6587\u4EF6",
2636
+ moreHitSingular: "\u22EE +{count} \u6761\u7ED3\u679C",
2637
+ moreHitsPlural: "\u22EE +{count} \u6761\u7ED3\u679C",
2638
+ earlierLine: "\u22EE \u524D {count} \u884C\uFF08\u4F7F\u7528 /tool \u9605\u8BFB\u5168\u6587\uFF09",
2639
+ earlierLines: "\u22EE \u524D {count} \u884C\uFF08\u4F7F\u7528 /tool \u9605\u8BFB\u5168\u6587\uFF09",
2640
+ earlierStackLine: "\u22EE \u524D {count} \u884C\u5806\u6808\u5DF2\u9690\u85CF",
2641
+ earlierStackLines: "\u22EE \u524D {count} \u884C\u5806\u6808\u5DF2\u9690\u85CF",
2642
+ agent: "\u4EE3\u7406 \xB7 {name}",
2643
+ response: "\u56DE\u590D",
2644
+ writing: "\u8F93\u51FA\u4E2D \u2026",
2645
+ tok: "tok",
2646
+ pilcrow: "\xB6",
2647
+ aborted: "\u5DF2\u4E2D\u6B62",
2648
+ truncatedByEsc: "[\u5DF2\u88AB Esc \u622A\u65AD]",
2649
+ rejected: "\u5DF2\u62D2\u7EDD",
2650
+ exit: "\u9000\u51FA\u7801 {code}",
2651
+ bytesIn: "{bytes} \u8F93\u5165",
2652
+ elapsedSec: "{secs}\u79D2",
2653
+ stackTrace: "\u5806\u6808\u8DDF\u8E2A",
2654
+ retries: "\u6B21\u91CD\u8BD5",
2655
+ reasoningLabel: "\u63A8\u7406 \xB7 {count} \xB6",
2656
+ runningLabel: "\u8FD0\u884C\u4E2D",
2657
+ workingLabel: "\u5DE5\u4F5C\u4E2D"
2386
2658
  }
2387
2659
  };
2388
2660
 
@@ -2879,6 +3151,7 @@ var ToolRegistry = class {
2879
3151
  _planMode = false;
2880
3152
  _interceptor = null;
2881
3153
  _auditListener = null;
3154
+ _resultAugmenter = null;
2882
3155
  constructor(opts = {}) {
2883
3156
  this._autoFlatten = opts.autoFlatten !== false;
2884
3157
  }
@@ -2897,6 +3170,10 @@ var ToolRegistry = class {
2897
3170
  setAuditListener(fn) {
2898
3171
  this._auditListener = fn;
2899
3172
  }
3173
+ /** Final-stage post-processor; replaces previous augmenter when called twice. Pass null to clear. */
3174
+ setResultAugmenter(fn) {
3175
+ this._resultAugmenter = fn;
3176
+ }
2900
3177
  register(def) {
2901
3178
  if (!def.name) throw new Error("tool requires a name");
2902
3179
  const internal = { ...def };
@@ -2972,6 +3249,7 @@ var ToolRegistry = class {
2972
3249
  });
2973
3250
  }
2974
3251
  }
3252
+ let finalResult;
2975
3253
  try {
2976
3254
  try {
2977
3255
  this._auditListener?.({ name, args });
@@ -2989,19 +3267,26 @@ var ToolRegistry = class {
2989
3267
  if (opts.maxResultChars !== void 0) {
2990
3268
  clipped = truncateForModel(clipped, opts.maxResultChars);
2991
3269
  }
2992
- return clipped;
3270
+ finalResult = clipped;
2993
3271
  } catch (err) {
2994
3272
  const e = err;
2995
3273
  if (typeof e.toToolResult === "function") {
2996
3274
  try {
2997
- return JSON.stringify(e.toToolResult());
3275
+ finalResult = JSON.stringify(e.toToolResult());
2998
3276
  } catch {
3277
+ finalResult = JSON.stringify({ error: `${e.name}: ${e.message}` });
2999
3278
  }
3279
+ } else {
3280
+ finalResult = JSON.stringify({ error: `${e.name}: ${e.message}` });
3000
3281
  }
3001
- return JSON.stringify({
3002
- error: `${e.name}: ${e.message}`
3003
- });
3004
3282
  }
3283
+ if (this._resultAugmenter) {
3284
+ try {
3285
+ return this._resultAugmenter(name, args, finalResult);
3286
+ } catch {
3287
+ }
3288
+ }
3289
+ return finalResult;
3005
3290
  }
3006
3291
  };
3007
3292
  function isReadOnlyCall(tool, args) {
@@ -5762,6 +6047,10 @@ var SkillStore = class {
5762
6047
  }
5763
6048
  /** Scaffold a new skill stub at the chosen scope. Refuses to overwrite. */
5764
6049
  create(name, scope) {
6050
+ return this.createWithContent(name, scope, skillStubBody(name));
6051
+ }
6052
+ /** Like `create` but writes caller-supplied file contents instead of the stub — used by the scaffold tool. */
6053
+ createWithContent(name, scope, content) {
5765
6054
  if (!isValidSkillName(name)) {
5766
6055
  return { error: `invalid skill name: "${name}" \u2014 use letters, digits, _, -, .` };
5767
6056
  }
@@ -5776,7 +6065,7 @@ var SkillStore = class {
5776
6065
  }
5777
6066
  mkdirSync3(dirname4(flat), { recursive: true });
5778
6067
  try {
5779
- writeFileSync3(flat, skillStubBody(name), { encoding: "utf8", flag: "wx" });
6068
+ writeFileSync3(flat, content, { encoding: "utf8", flag: "wx" });
5780
6069
  } catch (err) {
5781
6070
  if (err.code === "EEXIST") {
5782
6071
  return { error: `skill "${name}" already exists at ${flat}` };
@@ -6643,11 +6932,14 @@ async function searchFiles(ctx, startAbs, args) {
6643
6932
  await walk2(startAbs);
6644
6933
  return matches.length === 0 ? "(no matches)" : matches.join("\n");
6645
6934
  }
6935
+ var MAX_HITS_PER_FILE = 30;
6936
+ var SUMMARY_MODE_TRIGGER_RATIO = 0.8;
6646
6937
  async function searchContent(ctx, startAbs, args) {
6647
6938
  throwIfAborted(args.signal);
6648
6939
  const caseSensitive = args.case_sensitive === true;
6649
6940
  const includeDeps = args.include_deps === true;
6650
6941
  const ctxLines = Math.max(0, Math.min(20, Math.floor(args.context ?? 0)));
6942
+ const summaryOnly = args.summary_only === true;
6651
6943
  let re = null;
6652
6944
  try {
6653
6945
  re = new RegExp(args.pattern, caseSensitive ? "" : "i");
@@ -6659,6 +6951,9 @@ async function searchContent(ctx, startAbs, args) {
6659
6951
  let totalBytes = 0;
6660
6952
  let scanned = 0;
6661
6953
  let truncated = false;
6954
+ let summaryMode = summaryOnly;
6955
+ let summaryNoticeEmitted = false;
6956
+ const fileHitCounts = /* @__PURE__ */ new Map();
6662
6957
  const pushLine = (out) => {
6663
6958
  if (totalBytes + out.length + 1 > ctx.maxListBytes) {
6664
6959
  matches.push(`[\u2026 truncated at ${ctx.maxListBytes} bytes \u2014 refine pattern or path \u2026]`);
@@ -6669,6 +6964,18 @@ async function searchContent(ctx, startAbs, args) {
6669
6964
  totalBytes += out.length + 1;
6670
6965
  return true;
6671
6966
  };
6967
+ const maybeEnterSummaryMode = () => {
6968
+ if (summaryMode) return;
6969
+ if (totalBytes <= SUMMARY_MODE_TRIGGER_RATIO * ctx.maxListBytes) return;
6970
+ summaryMode = true;
6971
+ if (!summaryNoticeEmitted) {
6972
+ const pct2 = Math.round(totalBytes / ctx.maxListBytes * 100);
6973
+ pushLine(
6974
+ `[switching to summary mode \u2014 byte budget at ${pct2}%; remaining files will report match counts only]`
6975
+ );
6976
+ summaryNoticeEmitted = true;
6977
+ }
6978
+ };
6672
6979
  const walk2 = async (dir) => {
6673
6980
  if (truncated) return;
6674
6981
  throwIfAborted(args.signal);
@@ -6727,33 +7034,48 @@ async function searchContent(ctx, startAbs, args) {
6727
7034
  }
6728
7035
  scanned++;
6729
7036
  if (hits.length === 0) continue;
7037
+ fileHitCounts.set(rel, hits.length);
7038
+ if (summaryMode) {
7039
+ if (!pushLine(`${rel}: ${hits.length} match${hits.length === 1 ? "" : "es"}`)) return;
7040
+ continue;
7041
+ }
7042
+ const printable = Math.min(hits.length, MAX_HITS_PER_FILE);
7043
+ const omittedFromFile = hits.length - printable;
7044
+ const printableHits = hits.slice(0, printable);
6730
7045
  if (ctxLines === 0) {
6731
- for (const li of hits) {
7046
+ for (const li of printableHits) {
6732
7047
  if (truncated) return;
6733
7048
  const line = lines[li];
6734
7049
  const display = line.length > 200 ? `${line.slice(0, 200)}\u2026` : line;
6735
7050
  if (!pushLine(`${rel}:${li + 1}: ${display}`)) return;
6736
7051
  }
6737
- continue;
6738
- }
6739
- const hitSet = new Set(hits);
6740
- let prevWindowEnd = -2;
6741
- for (const li of hits) {
6742
- if (truncated) return;
6743
- const winStart = Math.max(0, li - ctxLines);
6744
- const winEnd = Math.min(lines.length - 1, li + ctxLines);
6745
- if (winStart > prevWindowEnd + 1 && prevWindowEnd >= 0) {
6746
- if (!pushLine("--")) return;
6747
- }
6748
- const realStart = winStart > prevWindowEnd + 1 ? winStart : prevWindowEnd + 1;
6749
- for (let i = realStart; i <= winEnd; i++) {
6750
- const line = lines[i];
6751
- const display = line.length > 200 ? `${line.slice(0, 200)}\u2026` : line;
6752
- const sep2 = hitSet.has(i) ? ":" : "-";
6753
- if (!pushLine(`${rel}:${i + 1}${sep2} ${display}`)) return;
7052
+ } else {
7053
+ const hitSet = new Set(printableHits);
7054
+ let prevWindowEnd = -2;
7055
+ for (const li of printableHits) {
7056
+ if (truncated) return;
7057
+ const winStart = Math.max(0, li - ctxLines);
7058
+ const winEnd = Math.min(lines.length - 1, li + ctxLines);
7059
+ if (winStart > prevWindowEnd + 1 && prevWindowEnd >= 0) {
7060
+ if (!pushLine("--")) return;
7061
+ }
7062
+ const realStart = winStart > prevWindowEnd + 1 ? winStart : prevWindowEnd + 1;
7063
+ for (let i = realStart; i <= winEnd; i++) {
7064
+ const line = lines[i];
7065
+ const display = line.length > 200 ? `${line.slice(0, 200)}\u2026` : line;
7066
+ const sep2 = hitSet.has(i) ? ":" : "-";
7067
+ if (!pushLine(`${rel}:${i + 1}${sep2} ${display}`)) return;
7068
+ }
7069
+ prevWindowEnd = winEnd;
6754
7070
  }
6755
- prevWindowEnd = winEnd;
6756
7071
  }
7072
+ if (omittedFromFile > 0) {
7073
+ if (!pushLine(
7074
+ `[${rel}: ${omittedFromFile} more match${omittedFromFile === 1 ? "" : "es"} in this file \u2014 re-grep with a tighter pattern or use read_file to see them]`
7075
+ ))
7076
+ return;
7077
+ }
7078
+ maybeEnterSummaryMode();
6757
7079
  }
6758
7080
  };
6759
7081
  await walk2(startAbs);
@@ -6769,6 +7091,43 @@ var DEFAULT_MAX_LIST_BYTES = 256 * 1024;
6769
7091
  var DEFAULT_AUTO_PREVIEW_LINES = 200;
6770
7092
  var AUTO_PREVIEW_HEAD_LINES = 80;
6771
7093
  var AUTO_PREVIEW_TAIL_LINES = 40;
7094
+ var OUTLINE_MAX_ENTRIES = 30;
7095
+ var OUTLINE_TAIL_KEEP = 5;
7096
+ var TS_EXPORT_RE = /^export\s+(?:default\s+)?(?:async\s+)?(function|class|const|let|var|interface|type|enum)\s+\*?\s*(\w+)/;
7097
+ function extractTsExportOutline(lines) {
7098
+ const out = [];
7099
+ for (let i = 0; i < lines.length; i++) {
7100
+ const line = lines[i];
7101
+ if (!line.startsWith("export ")) continue;
7102
+ const m = TS_EXPORT_RE.exec(line);
7103
+ if (!m) continue;
7104
+ out.push({ line: i + 1, kind: m[1], name: m[2] });
7105
+ }
7106
+ return out;
7107
+ }
7108
+ function formatOutline(entries) {
7109
+ const total = entries.length;
7110
+ if (total === 0) return "";
7111
+ const lastEntry = entries[total - 1];
7112
+ const width = String(lastEntry.line).length;
7113
+ const fmt = (e) => ` L${String(e.line).padStart(width, " ")} export ${e.kind} ${e.name}`;
7114
+ const header = `[outline: ${total} top-level export${total === 1 ? "" : "s"}]`;
7115
+ if (total <= OUTLINE_MAX_ENTRIES) {
7116
+ return [header, ...entries.map(fmt)].join("\n");
7117
+ }
7118
+ const headCount = OUTLINE_MAX_ENTRIES - OUTLINE_TAIL_KEEP;
7119
+ const headEntries = entries.slice(0, headCount);
7120
+ const tailEntries = entries.slice(-OUTLINE_TAIL_KEEP);
7121
+ const omitted = total - OUTLINE_MAX_ENTRIES;
7122
+ const gapStart = headEntries[headEntries.length - 1].line;
7123
+ const gapEnd = tailEntries[0].line;
7124
+ return [
7125
+ header,
7126
+ ...headEntries.map(fmt),
7127
+ ` [\u2026 ${omitted} more export${omitted === 1 ? "" : "s"} between L${gapStart} and L${gapEnd} \u2026]`,
7128
+ ...tailEntries.map(fmt)
7129
+ ].join("\n");
7130
+ }
6772
7131
  var SKIP_DIR_NAMES = new Set(DEFAULT_INDEX_EXCLUDES.dirs);
6773
7132
  var BINARY_EXTENSIONS = new Set(DEFAULT_INDEX_EXCLUDES.exts);
6774
7133
  function displayRel4(rootDir, full) {
@@ -6821,7 +7180,7 @@ function registerFilesystemTools(registry, opts) {
6821
7180
  - head: N \u2192 first N lines (imports, public API, small configs)
6822
7181
  - tail: N \u2192 last N lines (recently-added code, log tails)
6823
7182
  - range: "A-B" \u2192 inclusive line range A..B, 1-indexed (e.g. "120-180" around an edit site)
6824
- When none of these is given AND the file is longer than ${DEFAULT_AUTO_PREVIEW_LINES} lines, the tool auto-returns a head+tail preview with an "N lines omitted" marker rather than dumping everything. If you need the middle, re-call with a range. Prefer search_content to locate a symbol first, then read_file with a range around the hit \u2014 one scoped read beats three full-file reads.`,
7183
+ When none of these is given AND the file is longer than ${DEFAULT_AUTO_PREVIEW_LINES} lines, the tool auto-returns a head+tail preview with an "N lines omitted" marker, plus a top-level export outline (function / class / const / interface / type / enum names with line numbers, capped at ${OUTLINE_MAX_ENTRIES}) so you can pick a smart range without a follow-up grep. If you need the middle, re-call with a range. Prefer search_content to locate a symbol first only when the outline doesn't have what you want \u2014 one scoped read beats three full-file reads.`,
6825
7184
  readOnly: true,
6826
7185
  stormExempt: true,
6827
7186
  parameters: {
@@ -6889,14 +7248,19 @@ ${slice.join("\n")}`;
6889
7248
  const head = lines.slice(0, AUTO_PREVIEW_HEAD_LINES).join("\n");
6890
7249
  const tail = lines.slice(totalLines - AUTO_PREVIEW_TAIL_LINES).join("\n");
6891
7250
  const omitted = totalLines - AUTO_PREVIEW_HEAD_LINES - AUTO_PREVIEW_TAIL_LINES;
6892
- return [
7251
+ const outline = formatOutline(extractTsExportOutline(lines));
7252
+ const parts = [
6893
7253
  `[auto-preview: head ${AUTO_PREVIEW_HEAD_LINES} + tail ${AUTO_PREVIEW_TAIL_LINES} of ${totalLines} lines]`,
6894
- head,
7254
+ head
7255
+ ];
7256
+ if (outline) parts.push("", outline);
7257
+ parts.push(
6895
7258
  `
6896
7259
  [\u2026 ${omitted} lines omitted \u2014 call read_file again with range:"A-B" (1-indexed) or head / tail to get the middle]
6897
7260
  `,
6898
7261
  tail
6899
- ].join("\n");
7262
+ );
7263
+ return parts.join("\n");
6900
7264
  }
6901
7265
  });
6902
7266
  registry.register({
@@ -7029,7 +7393,7 @@ Prefer \`list_directory\` for a single-level view, \`search_files\` to find spec
7029
7393
  registry.register({
7030
7394
  name: "search_content",
7031
7395
  parallelSafe: true,
7032
- description: "Recursively grep file CONTENTS for a substring or regex. This is the right tool for 'find all places that call X', 'where is Y referenced', 'what files contain Z'. Different from search_files (which matches FILE NAMES). Returns one match per line in 'path:line: text' format. Skips dependency / VCS / build directories (node_modules, .git, dist, build, .next, target, .venv) and binary files by default.",
7396
+ description: "Recursively grep file CONTENTS for a substring or regex. This is the right tool for 'find all places that call X', 'where is Y referenced', 'what files contain Z'. Different from search_files (which matches FILE NAMES). Returns one match per line in 'path:line: text' format. Per-file hits are capped at 30 (a footer reports any extras); when the byte budget is mostly spent the remaining files switch to a 'rel: N matches' histogram so distribution stays visible instead of one popular file drowning the rest. Pass `summary_only:true` to skip line content entirely and get just the histogram. Skips dependency / VCS / build directories (node_modules, .git, dist, build, .next, target, .venv) and binary files by default.",
7033
7397
  readOnly: true,
7034
7398
  parameters: {
7035
7399
  type: "object",
@@ -7057,6 +7421,10 @@ Prefer \`list_directory\` for a single-level view, \`search_files\` to find spec
7057
7421
  context: {
7058
7422
  type: "integer",
7059
7423
  description: "Lines of context to show around each match (both before and after). Default 0 (just the matching line). Capped at 20. Output uses ripgrep style: `:` after the line number on the matching line, `-` on context lines, `--` separating non-adjacent windows."
7424
+ },
7425
+ summary_only: {
7426
+ type: "boolean",
7427
+ description: "When true, skip line content and return one 'rel: N matches' line per matching file. Use for 'where does this exist at all' questions before drilling in with a targeted read_file."
7060
7428
  }
7061
7429
  },
7062
7430
  required: ["pattern"]
@@ -7593,9 +7961,14 @@ function registerSubmitPlan(registry, opts) {
7593
7961
  kind: "plan_proposed",
7594
7962
  payload: { plan, steps, summary }
7595
7963
  });
7596
- if (verdict.type === "approve") return "plan approved";
7597
- if (verdict.type === "refine") throw new Error("user requested refinement");
7598
- throw new Error("plan cancelled");
7964
+ const fb = verdict.feedback?.trim();
7965
+ if (verdict.type === "approve") {
7966
+ return fb ? `plan approved. user's additional instructions: ${fb}` : "plan approved";
7967
+ }
7968
+ if (verdict.type === "refine") {
7969
+ throw new Error(fb ? `user requested refinement: ${fb}` : "user requested refinement");
7970
+ }
7971
+ throw new Error(fb ? `plan cancelled: ${fb}` : "plan cancelled");
7599
7972
  }
7600
7973
  });
7601
7974
  }
@@ -7881,6 +8254,10 @@ var DEFAULT_MAX_RESULT_CHARS2 = 8e3;
7881
8254
  var DEFAULT_MAX_ITERS = 16;
7882
8255
  var MIN_MAX_ITERS = 1;
7883
8256
  var MAX_MAX_ITERS = 32;
8257
+ var BUDGET_WARN_THRESHOLD = 3;
8258
+ function budgetParagraph(maxToolIters) {
8259
+ return `Tool budget: you have ${maxToolIters} tool call${maxToolIters === 1 ? "" : "s"} for this task. The cap is enforced from outside \u2014 the call after #${maxToolIters} is refused. Pace yourself: if you can't fully resolve the task within the budget, stop early and return what you have plus what's missing, rather than burning the budget on one branch.`;
8260
+ }
7884
8261
  var DEFAULT_SUBAGENT_MODEL = "deepseek-v4-flash";
7885
8262
  var DEFAULT_SUBAGENT_EFFORT = "high";
7886
8263
  var SUBAGENT_TOOL_NAME = "spawn_subagent";
@@ -7939,8 +8316,26 @@ async function spawnSubagent(opts) {
7939
8316
  new Set(opts.allowedTools),
7940
8317
  NEVER_INHERITED_TOOLS
7941
8318
  ) : forkRegistryExcluding(opts.parentRegistry, NEVER_INHERITED_TOOLS);
8319
+ let dispatchCount = 0;
8320
+ childTools.setResultAugmenter((_name, _args, result) => {
8321
+ dispatchCount++;
8322
+ const remaining = maxToolIters - dispatchCount;
8323
+ if (remaining <= 0) {
8324
+ return `${result}
8325
+
8326
+ [budget: 0 of ${maxToolIters} tool calls left \u2014 finalize NOW; the next tool call will be refused]`;
8327
+ }
8328
+ if (remaining <= BUDGET_WARN_THRESHOLD) {
8329
+ return `${result}
8330
+
8331
+ [budget: ${remaining} of ${maxToolIters} tool call${remaining === 1 ? "" : "s"} left \u2014 wrap up soon]`;
8332
+ }
8333
+ return result;
8334
+ });
7942
8335
  const childPrefix = new ImmutablePrefix({
7943
- system: opts.system,
8336
+ system: `${opts.system}
8337
+
8338
+ ${budgetParagraph(maxToolIters)}`,
7944
8339
  toolSpecs: childTools.specs()
7945
8340
  });
7946
8341
  const childLoop = new CacheFirstLoop({
@@ -9208,6 +9603,7 @@ async function runCommand(cmd, opts) {
9208
9603
  });
9209
9604
  }
9210
9605
  const timeoutMs = timeoutSec * 1e3;
9606
+ const normalizedEnv = normalizeWindowsEnvVars(process.env);
9211
9607
  const spawnOpts = {
9212
9608
  cwd: opts.cwd,
9213
9609
  shell: false,
@@ -9221,9 +9617,9 @@ async function runCommand(cmd, opts) {
9221
9617
  // sees a Python traceback instead of the script's real output
9222
9618
  // and goes around in circles trying to fix the wrong problem.
9223
9619
  // Harmless on non-Python processes (env vars they don't read).
9224
- env: { ...process.env, PYTHONIOENCODING: "utf-8", PYTHONUTF8: "1" }
9620
+ env: { ...normalizedEnv, PYTHONIOENCODING: "utf-8", PYTHONUTF8: "1" }
9225
9621
  };
9226
- const { bin, args, spawnOverrides } = prepareSpawn(argv);
9622
+ const { bin, args, spawnOverrides } = prepareSpawn(argv, { env: normalizedEnv });
9227
9623
  const effectiveSpawnOpts = { ...spawnOpts, ...spawnOverrides };
9228
9624
  return await new Promise((resolve10, reject) => {
9229
9625
  let child;
@@ -9304,9 +9700,9 @@ function resolveExecutable(cmd, opts = {}) {
9304
9700
  if (cmd.includes("/") || cmd.includes("\\") || pathMod7.isAbsolute(cmd)) return cmd;
9305
9701
  if (pathMod7.extname(cmd)) return cmd;
9306
9702
  const env = opts.env ?? process.env;
9307
- const pathExt = (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").map((e) => e.trim()).filter(Boolean);
9703
+ const pathExt = (getEnvCaseInsensitive(env, "PATHEXT") ?? ".COM;.EXE;.BAT;.CMD").split(";").map((e) => e.trim()).filter(Boolean);
9308
9704
  const delimiter2 = opts.pathDelimiter ?? (platform === "win32" ? ";" : pathMod7.delimiter);
9309
- const pathDirs = (env.PATH ?? "").split(delimiter2).filter(Boolean);
9705
+ const pathDirs = (getEnvCaseInsensitive(env, "PATH") ?? "").split(delimiter2).filter(Boolean);
9310
9706
  const isFile = opts.isFile ?? defaultIsFile;
9311
9707
  for (const dir of pathDirs) {
9312
9708
  for (const ext of pathExt) {
@@ -9316,6 +9712,52 @@ function resolveExecutable(cmd, opts = {}) {
9316
9712
  }
9317
9713
  return cmd;
9318
9714
  }
9715
+ function normalizeWindowsEnvVars(env, opts = {}) {
9716
+ const platform = opts.platform ?? process.platform;
9717
+ if (platform !== "win32") return { ...env };
9718
+ const out = {};
9719
+ const pathValues = [];
9720
+ const pathExtValues = [];
9721
+ for (const [key, value] of Object.entries(env)) {
9722
+ const lower = key.toLowerCase();
9723
+ if (lower === "path") {
9724
+ if (typeof value === "string") pathValues.push(value);
9725
+ continue;
9726
+ }
9727
+ if (lower === "pathext") {
9728
+ if (typeof value === "string") pathExtValues.push(value);
9729
+ continue;
9730
+ }
9731
+ out[key] = value;
9732
+ }
9733
+ if (pathValues.length > 0) out.Path = mergeWindowsPathLike(pathValues, ";");
9734
+ if (pathExtValues.length > 0) out.PATHEXT = mergeWindowsPathLike(pathExtValues, ";");
9735
+ return out;
9736
+ }
9737
+ function getEnvCaseInsensitive(env, key) {
9738
+ const exact = env[key];
9739
+ if (exact !== void 0) return exact;
9740
+ const target = key.toLowerCase();
9741
+ for (const [candidate, value] of Object.entries(env)) {
9742
+ if (candidate.toLowerCase() === target) return value;
9743
+ }
9744
+ return void 0;
9745
+ }
9746
+ function mergeWindowsPathLike(values, delimiter2) {
9747
+ const seen = /* @__PURE__ */ new Set();
9748
+ const merged = [];
9749
+ for (const value of values) {
9750
+ for (const part of value.split(delimiter2)) {
9751
+ const entry = part.trim();
9752
+ if (!entry) continue;
9753
+ const normalized = entry.toLowerCase();
9754
+ if (seen.has(normalized)) continue;
9755
+ seen.add(normalized);
9756
+ merged.push(entry);
9757
+ }
9758
+ }
9759
+ return merged.join(delimiter2);
9760
+ }
9319
9761
  function defaultIsFile(full) {
9320
9762
  try {
9321
9763
  return existsSync8(full) && statSync4(full).isFile();
@@ -10536,13 +10978,32 @@ function compareVersions(a, b) {
10536
10978
  if (!bPre) return -1;
10537
10979
  return aPre < bPre ? -1 : aPre > bPre ? 1 : 0;
10538
10980
  }
10981
+ function detectInstallSource(bin) {
10982
+ const raw = bin ?? process.argv[1] ?? "";
10983
+ if (!raw) return "unknown";
10984
+ const norm = raw.replace(/\\/g, "/").toLowerCase();
10985
+ if (/\/_npx\//.test(norm)) return "npx";
10986
+ if (/\/\.pnpm\//.test(norm) && /dlx/i.test(norm)) return "npx";
10987
+ const ua = (process.env.npm_config_user_agent ?? "").toLowerCase();
10988
+ if (ua.includes("npx/")) return "npx";
10989
+ if (/\/\.bun\//.test(norm) || /\/bun\/install\//.test(norm)) return "bun";
10990
+ if (/\/pnpm\/global\//.test(norm) || /\/pnpm\/[^/]+\/node_modules\//.test(norm)) return "pnpm";
10991
+ if (/\/yarn\/global\//.test(norm) || /\/\.yarn\/global\//.test(norm)) return "yarn";
10992
+ if (/\/node_modules\/reasonix(\b|\/)/.test(norm)) return "npm";
10993
+ return "unknown";
10994
+ }
10539
10995
  function isNpxInstall() {
10540
- const bin = process.argv[1] ?? "";
10541
- if (/[/\\]_npx[/\\]/.test(bin)) return true;
10542
- if (/[/\\]\.pnpm[/\\]/.test(bin) && /dlx/i.test(bin)) return true;
10543
- const ua = process.env.npm_config_user_agent ?? "";
10544
- if (ua.includes("npx/")) return true;
10545
- return false;
10996
+ return detectInstallSource() === "npx";
10997
+ }
10998
+ function detectNpmInstallPrefix(bin) {
10999
+ const raw = bin ?? process.argv[1] ?? "";
11000
+ if (!raw) return null;
11001
+ const norm = raw.replace(/\\/g, "/");
11002
+ const posix = norm.match(/^(.+?)\/lib\/node_modules\/reasonix(?:\/|$)/i);
11003
+ if (posix) return posix[1] ?? null;
11004
+ const win = norm.match(/^(.+?)\/node_modules\/reasonix(?:\/|$)/i);
11005
+ if (win) return win[1] ?? null;
11006
+ return null;
10546
11007
  }
10547
11008
 
10548
11009
  // src/mcp/types.ts
@@ -11994,6 +12455,8 @@ export {
11994
12455
  defaultUsageLogPath,
11995
12456
  deleteSession,
11996
12457
  detectAtPicker,
12458
+ detectInstallSource,
12459
+ detectNpmInstallPrefix,
11997
12460
  detectShellOperator,
11998
12461
  diffTranscripts,
11999
12462
  expandAtMentions,
@@ -12025,6 +12488,7 @@ export {
12025
12488
  listFilesWithStatsSync,
12026
12489
  listSessions,
12027
12490
  loadApiKey,
12491
+ loadBaseUrl,
12028
12492
  loadDotenv,
12029
12493
  loadHooks,
12030
12494
  loadSessionMessages,
@@ -12069,6 +12533,7 @@ export {
12069
12533
  sanitizeMemoryName,
12070
12534
  sanitizeName as sanitizeSessionName,
12071
12535
  saveApiKey,
12536
+ saveBaseUrl,
12072
12537
  scavengeToolCalls,
12073
12538
  sessionPath,
12074
12539
  sessionsDir,