reasonix 0.37.0 → 0.38.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 (74) hide show
  1. package/README.md +1 -0
  2. package/README.zh-CN.md +1 -0
  3. package/dist/cli/{chat-7257YAPG.js → chat-FPEYKTMI.js} +13 -14
  4. package/dist/cli/{chunk-T52GAWPP.js → chunk-3VTV4WAH.js} +2 -2
  5. package/dist/cli/{chunk-MSKUP6PD.js → chunk-4PNXH2MH.js} +910 -659
  6. package/dist/cli/chunk-4PNXH2MH.js.map +1 -0
  7. package/dist/cli/{chunk-YER7WCHF.js → chunk-A63QT566.js} +24 -10
  8. package/dist/cli/chunk-A63QT566.js.map +1 -0
  9. package/dist/cli/{chunk-4Q3GRJIU.js → chunk-AATCLE5N.js} +2 -2
  10. package/dist/cli/{chunk-BHLHOS5Y.js → chunk-BW2HWSYH.js} +315 -5
  11. package/dist/cli/chunk-BW2HWSYH.js.map +1 -0
  12. package/dist/cli/{chunk-ZJR4QLXB.js → chunk-FB46F6H4.js} +2 -2
  13. package/dist/cli/{chunk-GKZJXYMY.js → chunk-FYKZB6TX.js} +415 -11
  14. package/dist/cli/chunk-FYKZB6TX.js.map +1 -0
  15. package/dist/cli/{chunk-XQIFIB3U.js → chunk-JOFZ6AW5.js} +2 -2
  16. package/dist/cli/{chunk-JGZKTAOH.js → chunk-LMNAMITH.js} +2 -2
  17. package/dist/cli/{chunk-S4GF3HPO.js → chunk-LY352GTC.js} +6 -4
  18. package/dist/cli/chunk-LY352GTC.js.map +1 -0
  19. package/dist/cli/{chunk-VF57YX2M.js → chunk-NYP2DDDV.js} +40 -1
  20. package/dist/cli/chunk-NYP2DDDV.js.map +1 -0
  21. package/dist/cli/{chunk-JULZ7JTO.js → chunk-T5U5JO7Q.js} +11 -8
  22. package/dist/cli/chunk-T5U5JO7Q.js.map +1 -0
  23. package/dist/cli/{chunk-SEFXUF24.js → chunk-YJKLNYCP.js} +113 -24
  24. package/dist/cli/chunk-YJKLNYCP.js.map +1 -0
  25. package/dist/cli/{code-64EG5IU2.js → code-GTE65OUT.js} +23 -17
  26. package/dist/cli/{code-64EG5IU2.js.map → code-GTE65OUT.js.map} +1 -1
  27. package/dist/cli/{commands-FE2UDFBC.js → commands-R4JWISND.js} +3 -4
  28. package/dist/cli/{commands-FE2UDFBC.js.map → commands-R4JWISND.js.map} +1 -1
  29. package/dist/cli/{commit-3IAGB22T.js → commit-TQ4DMUNS.js} +2 -3
  30. package/dist/cli/{commit-3IAGB22T.js.map → commit-TQ4DMUNS.js.map} +1 -1
  31. package/dist/cli/{doctor-BW5HSQDW.js → doctor-GGK2JKTA.js} +6 -7
  32. package/dist/cli/index.js +24 -25
  33. package/dist/cli/index.js.map +1 -1
  34. package/dist/cli/{mcp-2RDEQST6.js → mcp-M7I23TQ7.js} +2 -3
  35. package/dist/cli/{mcp-2RDEQST6.js.map → mcp-M7I23TQ7.js.map} +1 -1
  36. package/dist/cli/{mcp-browse-VM5GLRBQ.js → mcp-browse-TWO7RYT4.js} +2 -3
  37. package/dist/cli/{mcp-browse-VM5GLRBQ.js.map → mcp-browse-TWO7RYT4.js.map} +1 -1
  38. package/dist/cli/{prompt-KGIUONO3.js → prompt-ODPFOKSH.js} +2 -2
  39. package/dist/cli/{replay-D7RT2DR7.js → replay-R3QRXPI2.js} +13 -9
  40. package/dist/cli/replay-R3QRXPI2.js.map +1 -0
  41. package/dist/cli/{run-RWCOA32G.js → run-WGSPYYOJ.js} +7 -8
  42. package/dist/cli/{run-RWCOA32G.js.map → run-WGSPYYOJ.js.map} +1 -1
  43. package/dist/cli/{server-6ZW4TQUP.js → server-IZPWQYG3.js} +8 -9
  44. package/dist/cli/{server-6ZW4TQUP.js.map → server-IZPWQYG3.js.map} +1 -1
  45. package/dist/cli/{sessions-5ISNWFMU.js → sessions-E4UH5JYL.js} +7 -8
  46. package/dist/cli/{sessions-5ISNWFMU.js.map → sessions-E4UH5JYL.js.map} +1 -1
  47. package/dist/cli/{setup-HJG23NKJ.js → setup-FTZNN3TZ.js} +60 -15
  48. package/dist/cli/setup-FTZNN3TZ.js.map +1 -0
  49. package/dist/cli/{version-BXAN7Q4V.js → version-MDVCFTKA.js} +7 -8
  50. package/dist/cli/{version-BXAN7Q4V.js.map → version-MDVCFTKA.js.map} +1 -1
  51. package/dist/index.d.ts +3 -0
  52. package/dist/index.js +568 -40
  53. package/dist/index.js.map +1 -1
  54. package/package.json +1 -1
  55. package/dist/cli/chunk-BHLHOS5Y.js.map +0 -1
  56. package/dist/cli/chunk-GKZJXYMY.js.map +0 -1
  57. package/dist/cli/chunk-JULZ7JTO.js.map +0 -1
  58. package/dist/cli/chunk-MSKUP6PD.js.map +0 -1
  59. package/dist/cli/chunk-S4GF3HPO.js.map +0 -1
  60. package/dist/cli/chunk-SEFXUF24.js.map +0 -1
  61. package/dist/cli/chunk-VF57YX2M.js.map +0 -1
  62. package/dist/cli/chunk-WUI3P4RA.js +0 -319
  63. package/dist/cli/chunk-WUI3P4RA.js.map +0 -1
  64. package/dist/cli/chunk-YER7WCHF.js.map +0 -1
  65. package/dist/cli/replay-D7RT2DR7.js.map +0 -1
  66. package/dist/cli/setup-HJG23NKJ.js.map +0 -1
  67. /package/dist/cli/{chat-7257YAPG.js.map → chat-FPEYKTMI.js.map} +0 -0
  68. /package/dist/cli/{chunk-T52GAWPP.js.map → chunk-3VTV4WAH.js.map} +0 -0
  69. /package/dist/cli/{chunk-4Q3GRJIU.js.map → chunk-AATCLE5N.js.map} +0 -0
  70. /package/dist/cli/{chunk-ZJR4QLXB.js.map → chunk-FB46F6H4.js.map} +0 -0
  71. /package/dist/cli/{chunk-XQIFIB3U.js.map → chunk-JOFZ6AW5.js.map} +0 -0
  72. /package/dist/cli/{chunk-JGZKTAOH.js.map → chunk-LMNAMITH.js.map} +0 -0
  73. /package/dist/cli/{doctor-BW5HSQDW.js.map → doctor-GGK2JKTA.js.map} +0 -0
  74. /package/dist/cli/{prompt-KGIUONO3.js.map → prompt-ODPFOKSH.js.map} +0 -0
package/dist/index.js CHANGED
@@ -980,6 +980,10 @@ var EN = {
980
980
  title: "copy / paste",
981
981
  rows: [
982
982
  { key: "select text", text: "drag to select \u2014 terminal-native (no modifier needed)" },
983
+ {
984
+ key: "/copy",
985
+ text: "vim/tmux-style copy mode \u2014 works in SSH/mosh/tmux where drag-select can't extend past the viewport"
986
+ },
983
987
  {
984
988
  key: "copy",
985
989
  text: "Ctrl+Shift+C (Win/Linux) \xB7 Cmd+C (macOS) \u2014 or auto-copy-on-select if your terminal does it"
@@ -1062,6 +1066,9 @@ var EN = {
1062
1066
  },
1063
1067
  slash: {
1064
1068
  help: { description: "show the full command reference" },
1069
+ copy: {
1070
+ description: "open vim/tmux-style copy mode \u2014 j/k navigate, v select, y yank to clipboard"
1071
+ },
1065
1072
  status: { description: "current model, flags, context, session" },
1066
1073
  preset: {
1067
1074
  description: "model bundle \u2014 auto escalates flash \u2192 pro, flash/pro lock",
@@ -1069,6 +1076,10 @@ var EN = {
1069
1076
  },
1070
1077
  model: { description: "switch DeepSeek model id", argsHint: "<id>" },
1071
1078
  models: { description: "list available models fetched from DeepSeek /models" },
1079
+ theme: {
1080
+ description: "show or persist the terminal theme preference. Bare opens picker.",
1081
+ argsHint: "[auto|default|dark|light|tokyo-night|github-dark|github-light|high-contrast]"
1082
+ },
1072
1083
  language: {
1073
1084
  description: "switch the runtime language",
1074
1085
  argsHint: "<EN|zh-CN>",
@@ -1124,9 +1135,15 @@ var EN = {
1124
1135
  context: { description: "show context-window breakdown (system / tools / log / input)" },
1125
1136
  retry: { description: "truncate & resend your last message (fresh sample)" },
1126
1137
  compact: {
1127
- description: "shrink oversized tool results AND tool-call args (edit_file search/replace) in the log; cap in tokens, default 4000",
1138
+ description: "narrow oversized tool results + tool-call args in the log; cap at tokens, default 4000",
1128
1139
  argsHint: "[tokens]"
1129
1140
  },
1141
+ cwd: {
1142
+ description: "switch the workspace root mid-session \u2014 re-points fs / shell / memory tools, reloads project hooks, refreshes the at-mention walker",
1143
+ argsHint: "<path>"
1144
+ },
1145
+ stop: { description: "abort the current model turn (typed alternative to Esc)" },
1146
+ feedback: { description: "open a GitHub issue with diagnostic info copied to clipboard" },
1130
1147
  keys: { description: "keyboard + mouse + copy/paste reference" },
1131
1148
  plans: { description: "list this session's active + archived plans, newest first" },
1132
1149
  replay: {
@@ -1202,6 +1219,9 @@ var EN = {
1202
1219
  apiKeySavedLocally: "Saved locally to {path}",
1203
1220
  apiKeyInputLabel: "key \u203A ",
1204
1221
  apiKeyInvalid: "Key looks too short \u2014 paste the full token (16+ chars, no spaces).",
1222
+ apiKeyChecking: "Checking API key\u2026",
1223
+ apiKeyRejected: "DeepSeek rejected this API key. Paste a valid key, or press Esc to cancel setup.",
1224
+ apiKeyCheckFailed: "Could not verify this API key right now ({message}). Check your network or try again.",
1205
1225
  apiKeyPreview: "preview: {redacted}",
1206
1226
  themeTitle: "Choose a theme",
1207
1227
  themeSubtitle: "Preview updates live as you navigate. Change later with /theme.",
@@ -1749,12 +1769,157 @@ var EN = {
1749
1769
  waitingForResponse: "\u2026waiting for response\u2026",
1750
1770
  hintSend: "send",
1751
1771
  hintNewline: "newline",
1772
+ hintClear: "clear",
1752
1773
  hintScroll: "scroll",
1753
1774
  hintHistory: "history",
1754
1775
  hintAbort: "abort",
1755
1776
  hintQuit: "quit",
1756
1777
  abortedHint: "turn aborted by user \xB7 esc again to clear \xB7 \u23CE to ask a follow-up"
1757
1778
  },
1779
+ shellConfirm: {
1780
+ title: "Shell command",
1781
+ bgTitle: "Background process",
1782
+ subtitle: "model wants to run a shell command",
1783
+ bgSubtitle: "long-running process \u2014 keeps running after approval, /kill to stop",
1784
+ denyTitle: "Deny \u2014 provide context",
1785
+ optional: "optional",
1786
+ denyFooter: "type context \xB7 \u23CE submit with reason \xB7 esc skip (deny without reason)",
1787
+ awaiting: "awaiting",
1788
+ pickFooter: "\u2191\u2193 pick \xB7 \u23CE confirm \xB7 Tab add context \xB7 esc cancel",
1789
+ allowOnce: "allow once",
1790
+ allowOnceDesc: "run this command, ask again next time",
1791
+ allowAlways: "allow always",
1792
+ allowAlwaysDesc: "remember `{prefix}` for this project",
1793
+ deny: "deny",
1794
+ denyDesc: "press Tab to add context telling the model why"
1795
+ },
1796
+ editConfirm: {
1797
+ footer: "[y/Enter] apply \xB7 [n] reject with reason \xB7 [a] apply rest \xB7 [A] flip AUTO \xB7 [\u2191\u2193/Space] scroll \xB7 [Esc] abort",
1798
+ newTag: "NEW",
1799
+ editTag: "EDIT",
1800
+ linesCount: "-{removed} +{added} lines",
1801
+ viewingRange: "viewing {start}-{end}/{total}",
1802
+ denyFooter: "\u23CE submit \xB7 esc skip (deny without reason)",
1803
+ oldLabel: " - old",
1804
+ newLabel: " + new",
1805
+ sideBySide: " side-by-side \xB7 removed lines on the left, added on the right \xB7 paired by offset",
1806
+ linesAbove: " \u2191 {count} line above (\u2191/k or PgUp)",
1807
+ linesAbovePlural: " \u2191 {count} lines above (\u2191/k or PgUp)",
1808
+ linesBelow: " \u2193 {count} line below (\u2193/j or Space/PgDn)",
1809
+ linesBelowPlural: " \u2193 {count} lines below (\u2193/j or Space/PgDn)"
1810
+ },
1811
+ sessionPicker: {
1812
+ header: " \u25C8 REASONIX \xB7 pick a session ",
1813
+ title: "pick a session \u2014 {workspace}",
1814
+ messages: "{count} message",
1815
+ messagesPlural: "{count} messages",
1816
+ turns: "{count} turns",
1817
+ pickerHint: "\u2191\u2193 pick \xB7 \u23CE open \xB7 [n] new \xB7 [d] delete \xB7 [r] rename \xB7 esc quit",
1818
+ empty: " no saved sessions in this workspace yet \u2014 press ",
1819
+ emptyNew: " to start a new one",
1820
+ renamePrompt: ' rename "{from}" \u2192 ',
1821
+ renameHint: " \u23CE confirm rename \xB7 esc cancel",
1822
+ emptyHint: " \u23CE new session \xB7 esc quit",
1823
+ justNow: "just now",
1824
+ minAgo: "{count} min ago",
1825
+ yesterday: "yesterday",
1826
+ hoursAgo: "{count}h ago",
1827
+ daysAgo: "{count} days ago"
1828
+ },
1829
+ modelPicker: {
1830
+ header: " \u25C8 REASONIX \xB7 pick a setup ",
1831
+ loading: " \xB7 loading catalog\u2026",
1832
+ catalogEmpty: " \xB7 catalog empty \u2014 using known fallbacks",
1833
+ modelsAvailable: " \xB7 {count} models available",
1834
+ presetsHeader: " PRESETS \xB7 recommended \u2014 model + effort + auto-escalate",
1835
+ modelsHeader: " MODELS \xB7 raw pick \u2014 auto-escalate stays as-is",
1836
+ pickerFooter: " \u2191\u2193 pick \xB7 \u23CE confirm \xB7 [r] refresh \xB7 esc cancel",
1837
+ currentLabel: " \xB7 current"
1838
+ },
1839
+ slashSuggestions: {
1840
+ noMatch: "no slash command matches that prefix",
1841
+ backspaceHint: " \u2014 Backspace to edit, or /help for the full list",
1842
+ commandCount: "{count} command",
1843
+ commandCountPlural: "{count} commands",
1844
+ aboveLabel: " \u2191 {count} above",
1845
+ belowLabel: " \u2193 {count} below",
1846
+ advancedHint: " + {count} advanced \xB7 type a letter to search",
1847
+ footerHint: " \u2191\u2193 navigate \xB7 Tab / \u23CE pick \xB7 esc cancel",
1848
+ groupChat: "CHAT",
1849
+ groupSetup: "SETUP",
1850
+ groupInfo: "INFO",
1851
+ groupSession: "SESSION",
1852
+ groupExtend: "EXTEND",
1853
+ groupCode: "CODE",
1854
+ groupJobs: "JOBS",
1855
+ groupAdvanced: "ADVANCED"
1856
+ },
1857
+ atMentions: {
1858
+ loading: "loading\u2026",
1859
+ entrySingular: "{count} entry",
1860
+ entryPlural: "{count} entries",
1861
+ searching: "searching\u2026",
1862
+ scanned: "scanned",
1863
+ match: "match",
1864
+ matches: "matches",
1865
+ forFilter: 'for "{filter}"',
1866
+ noMatch: 'no files match "{filter}"',
1867
+ emptyDir: "empty directory",
1868
+ scanning: "scanning the tree\u2026",
1869
+ footerBrowse: "\u2191\u2193 navigate \xB7 Tab drill into folder \xB7 \u23CE insert \xB7 esc cancel",
1870
+ footerBrowseSearch: "\u2191\u2193 navigate \xB7 Tab / \u23CE insert as @path \xB7 esc cancel",
1871
+ footerInsert: "\u2191\u2193 navigate \xB7 Tab / \u23CE insert as @path \xB7 esc cancel"
1872
+ },
1873
+ statsPanel: {
1874
+ modePlan: "PLAN",
1875
+ modeYolo: "yolo",
1876
+ modeAuto: "auto",
1877
+ modeReview: "review",
1878
+ pro: "\u21E7 pro",
1879
+ budget: " budget "
1880
+ },
1881
+ welcomeBanner: {
1882
+ workspace: "\u25B8 workspace",
1883
+ relaunchHint: " (relaunch with --dir <path> to switch)",
1884
+ dashboard: "\u25B8 web"
1885
+ },
1886
+ ctxBreakdown: {
1887
+ title: "\u25A3 context",
1888
+ compactHint: " /compact folds (auto at 50%) \xB7 /new wipes log",
1889
+ topTools: " top tool results by cost ({count}):",
1890
+ msg: "msg",
1891
+ turnLabel: "turn"
1892
+ },
1893
+ startup: {
1894
+ codeRooted: '\u25B8 reasonix code: rooted at {rootDir}, session "{session}" \xB7 {tools} native tool(s){semantic}',
1895
+ ephemeral: "(ephemeral)",
1896
+ semanticOn: " \xB7 semantic_search on"
1897
+ },
1898
+ doctorErrors: {
1899
+ unreadable: "{path} unreadable \u2014 {message}",
1900
+ cannotList: "cannot list \u2014 {message}",
1901
+ parseFailed: "couldn't parse settings.json \u2014 {message}",
1902
+ probeFailed: "probe failed \u2014 {message}"
1903
+ },
1904
+ webErrors: {
1905
+ status: "web_search {status}",
1906
+ mojeekBlocked: "web_search: Mojeek anti-bot page \u2014 rate-limited or blocked",
1907
+ mojeekNoResults: "web_search: 0 results but response doesn't look like a real empty page ({chars} chars, first 120: {preview})",
1908
+ invalidEndpoint: 'web_search: invalid SearXNG endpoint "{endpoint}"',
1909
+ endpointMustBeHttp: "web_search: SearXNG endpoint must be http(s), got {protocol}",
1910
+ cannotReach: "web_search: Cannot reach SearXNG server at {endpoint}. Please install SearXNG (https://github.com/searxng/searxng) and start it (e.g. `docker run -d -p 8080:8080 searxng/searxng`), or switch to the default engine with /search-engine mojeek.",
1911
+ searxngNoResults: "web_search: 0 results but SearXNG response doesn't look like an empty results page ({chars} chars)",
1912
+ fetchStatus: "web_fetch {status} for {url}",
1913
+ fetchTooLarge: "web_fetch refused: content-length {len} bytes exceeds {cap}-byte cap ({url})",
1914
+ fetchBodyTooLarge: "web_fetch refused: response body exceeded {cap}-byte cap ({seen} bytes seen)",
1915
+ fetchInvalidUrl: "web_fetch: url must start with http:// or https://"
1916
+ },
1917
+ choiceConfirm: {
1918
+ customLabel: "Let me type my own answer",
1919
+ customDesc: "None of the above fits \u2014 type a free-form reply. The model reads it verbatim.",
1920
+ cancelLabel: "Cancel \u2014 drop the question",
1921
+ cancelDesc: "Model stops and asks what you want instead."
1922
+ },
1758
1923
  cardTitles: {
1759
1924
  usage: "usage",
1760
1925
  context: "context",
@@ -1764,7 +1929,9 @@ var EN = {
1764
1929
  reasoning: "reasoning",
1765
1930
  reasoningAborted: "reasoning (aborted)",
1766
1931
  reasoningEllipsis: "reasoning\u2026",
1767
- error: "error"
1932
+ error: "error",
1933
+ doctor: "doctor",
1934
+ you: "you"
1768
1935
  },
1769
1936
  cardLabels: {
1770
1937
  prompt: "prompt",
@@ -1803,7 +1970,42 @@ var EN = {
1803
1970
  retries: "retries",
1804
1971
  reasoningLabel: "reasoning \xB7 {count} \xB6",
1805
1972
  runningLabel: "running",
1806
- workingLabel: "working"
1973
+ workingLabel: "working",
1974
+ defaultFooter: "\u2191\u2193 pick \xB7 \u23CE confirm \xB7 esc cancel",
1975
+ applyAction: "[a] apply",
1976
+ skipAction: "[s] skip",
1977
+ rejectAction: "[r] reject",
1978
+ levelOk: "OK",
1979
+ levelWarn: "warn",
1980
+ levelFail: "FAIL",
1981
+ checksLabel: "checks",
1982
+ passed: "passed",
1983
+ warnTag: "warn",
1984
+ failTag: "fail",
1985
+ stepLabel: "step",
1986
+ done: "done",
1987
+ inProgress: "\u2190 in progress",
1988
+ upcoming: "upcoming",
1989
+ resumed: "resumed \xB7 ",
1990
+ archive: "\u23EA archive \xB7 ",
1991
+ more: "\u22EE +{count} more",
1992
+ categoryUser: "user",
1993
+ categoryFeedback: "feedback",
1994
+ categoryProject: "project",
1995
+ categoryReference: "reference"
1996
+ },
1997
+ copyMode: {
1998
+ title: "\u2500\u2500 COPY MODE \u2500\u2500",
1999
+ help: "j/k or \u2191/\u2193 move \xB7 v select \xB7 y yank \xB7 g/G top/bottom \xB7 q quit",
2000
+ statusBar: "line {cur}/{total} \xB7 selection: {sel}",
2001
+ statusYanked: "yanked {size} chars (osc52={osc52})",
2002
+ statusEmpty: "nothing selected",
2003
+ empty: "(no chat content yet \u2014 say something to the model first)",
2004
+ labelUser: "you",
2005
+ labelAssistant: "assistant",
2006
+ labelReasoning: "reasoning",
2007
+ yankedToast: "\u25B8 copied {size} chars to clipboard (osc52)",
2008
+ yankedToastFile: "\u25B8 copied {size} chars \xB7 file: {path}"
1807
2009
  }
1808
2010
  };
1809
2011
 
@@ -1930,6 +2132,10 @@ var zhCN = {
1930
2132
  title: "\u590D\u5236 / \u7C98\u8D34",
1931
2133
  rows: [
1932
2134
  { 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" },
2135
+ {
2136
+ key: "/copy",
2137
+ text: "vim/tmux \u98CE\u683C\u590D\u5236\u6A21\u5F0F \u2014 SSH / mosh / tmux \u4E0B\u62D6\u9009\u8D8A\u8FC7\u53EF\u89C6\u533A\u65E0\u6548\u65F6\u7528\u8FD9\u4E2A"
2138
+ },
1933
2139
  {
1934
2140
  key: "\u590D\u5236",
1935
2141
  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"
@@ -2012,6 +2218,9 @@ var zhCN = {
2012
2218
  },
2013
2219
  slash: {
2014
2220
  help: { description: "\u663E\u793A\u5B8C\u6574\u547D\u4EE4\u53C2\u8003" },
2221
+ copy: {
2222
+ description: "\u8FDB\u5165 vim/tmux \u98CE\u683C\u590D\u5236\u6A21\u5F0F \u2014 j/k \u79FB\u52A8\u3001v \u8D77\u9009\u533A\u3001y \u590D\u5236\u5230\u526A\u8D34\u677F"
2223
+ },
2015
2224
  status: { description: "\u5F53\u524D\u6A21\u578B\u3001\u6807\u5FD7\u3001\u4E0A\u4E0B\u6587\u3001\u4F1A\u8BDD" },
2016
2225
  preset: {
2017
2226
  description: "\u6A21\u578B\u7EC4\u5408 \u2014 \u81EA\u52A8\u5728 flash \u2192 pro \u4E4B\u95F4\u5207\u6362\uFF0C\u6216\u9501\u5B9A flash/pro",
@@ -2019,6 +2228,10 @@ var zhCN = {
2019
2228
  },
2020
2229
  model: { description: "\u5207\u6362 DeepSeek \u6A21\u578B ID", argsHint: "<id>" },
2021
2230
  models: { description: "\u5217\u51FA\u4ECE DeepSeek /models \u83B7\u53D6\u7684\u53EF\u7528\u6A21\u578B" },
2231
+ theme: {
2232
+ description: "\u663E\u793A\u6216\u6301\u4E45\u5316\u7EC8\u7AEF\u4E3B\u9898\u504F\u597D\u3002\u65E0\u53C2\u6570\u65F6\u6253\u5F00\u9009\u62E9\u5668\u3002",
2233
+ argsHint: "[auto|default|dark|light|tokyo-night|github-dark|github-light|high-contrast]"
2234
+ },
2022
2235
  language: {
2023
2236
  description: "\u5207\u6362\u8FD0\u884C\u65F6\u8BED\u8A00",
2024
2237
  argsHint: "<en|zh-CN>",
@@ -2080,6 +2293,12 @@ var zhCN = {
2080
2293
  argsHint: "[tokens]"
2081
2294
  },
2082
2295
  keys: { description: "\u952E\u76D8 + \u9F20\u6807 + \u590D\u5236\u7C98\u8D34\u53C2\u8003" },
2296
+ cwd: {
2297
+ description: "\u5207\u6362\u5DE5\u4F5C\u533A\u6839\u76EE\u5F55 \u2014 \u91CD\u65B0\u6307\u5411\u6587\u4EF6/Shell/\u8BB0\u5FC6\u5DE5\u5177\uFF0C\u91CD\u8F7D\u9879\u76EE hooks\uFF0C\u5237\u65B0 @ \u5F15\u7528\u904D\u5386\u5668",
2298
+ argsHint: "<path>"
2299
+ },
2300
+ stop: { description: "\u4E2D\u6B62\u5F53\u524D\u6A21\u578B\u56DE\u5408\uFF08\u6309 Esc \u7684\u66FF\u4EE3\u65B9\u5F0F\uFF09" },
2301
+ feedback: { description: "\u6253\u5F00 GitHub Issue\uFF0C\u8BCA\u65AD\u4FE1\u606F\u5DF2\u590D\u5236\u5230\u526A\u8D34\u677F" },
2083
2302
  plans: { description: "\u5217\u51FA\u6B64\u4F1A\u8BDD\u7684\u6D3B\u8DC3 + \u5F52\u6863\u8BA1\u5212\uFF08\u6700\u65B0\u5728\u524D\uFF09" },
2084
2303
  replay: {
2085
2304
  description: "\u52A0\u8F7D\u5F52\u6863\u8BA1\u5212\u4E3A\u53EA\u8BFB\u7684\u65F6\u95F4\u65C5\u884C\u5FEB\u7167\uFF08\u9ED8\u8BA4\uFF1A\u6700\u65B0\uFF09",
@@ -2156,6 +2375,9 @@ var zhCN = {
2156
2375
  apiKeySavedLocally: "\u4FDD\u5B58\u5728\u672C\u5730\uFF1A{path}",
2157
2376
  apiKeyInputLabel: "key \u203A ",
2158
2377
  apiKeyInvalid: "key \u957F\u5EA6\u4E0D\u8DB3\u2014\u2014\u8BF7\u7C98\u8D34\u5B8C\u6574 token\uFF0816+ \u5B57\u7B26\uFF0C\u4E0D\u542B\u7A7A\u683C\uFF09\u3002",
2378
+ apiKeyChecking: "\u6B63\u5728\u68C0\u67E5 API key\u2026",
2379
+ apiKeyRejected: "DeepSeek \u62D2\u7EDD\u4E86\u8FD9\u4E2A API key\u3002\u8BF7\u7C98\u8D34\u6709\u6548 key\uFF0C\u6216\u6309 Esc \u53D6\u6D88\u8BBE\u7F6E\u3002",
2380
+ apiKeyCheckFailed: "\u6682\u65F6\u65E0\u6CD5\u9A8C\u8BC1 API key\uFF08{message}\uFF09\u3002\u8BF7\u68C0\u67E5\u7F51\u7EDC\u540E\u91CD\u8BD5\u3002",
2159
2381
  apiKeyPreview: "\u9884\u89C8\uFF1A{redacted}",
2160
2382
  themeTitle: "\u9009\u62E9\u4E3B\u9898",
2161
2383
  themeSubtitle: "\u65B9\u5411\u952E\u5207\u6362\u65F6\u5373\u65F6\u9884\u89C8\u6548\u679C\uFF0C\u4E4B\u540E\u53EF\u7528 /theme \u66F4\u6539\u3002",
@@ -2689,10 +2911,10 @@ var zhCN = {
2689
2911
  plan: "\u8BA1\u5212",
2690
2912
  yolo: "\u81EA\u7531",
2691
2913
  auto: "\u81EA\u52A8",
2692
- review: "\u5BA1\u6838",
2693
- writesGated: " \u5199\u5165\u53D7\u9650 \xB7 /plan off \u89E3\u9664",
2914
+ review: "\u5BA1\u67E5",
2915
+ writesGated: " \u5DF2\u9650\u5236\u5199\u5165 \xB7 /plan off \u89E3\u9664",
2694
2916
  editsShellAuto: "\u7F16\u8F91 + Shell \u81EA\u52A8 \xB7 /undo \u53EF\u56DE\u6EDA",
2695
- editsLandNow: "\u7F16\u8F91\u7ACB\u5373\u751F\u6548 \xB7 \u6309 u \u64A4\u6D88",
2917
+ editsLandNow: "\u7F16\u8F91\u5DF2\u751F\u6548 \xB7 \u6309 u \u64A4\u6D88",
2696
2918
  queuedApplyDiscard: "{count} \u4E2A\u5F85\u5904\u7406 \xB7 y \u5E94\u7528 \xB7 n \u4E22\u5F03",
2697
2919
  editsQueued: "\u7F16\u8F91\u5DF2\u6392\u961F \xB7 y \u5E94\u7528 \xB7 n \u4E22\u5F03",
2698
2920
  shiftTabFlip: " {mid} \xB7 Shift+Tab \u5207\u6362",
@@ -2703,12 +2925,157 @@ var zhCN = {
2703
2925
  waitingForResponse: "\u2026\u7B49\u5F85\u54CD\u5E94\u2026",
2704
2926
  hintSend: "\u53D1\u9001",
2705
2927
  hintNewline: "\u6362\u884C",
2928
+ hintClear: "\u6E05\u7A7A",
2706
2929
  hintScroll: "\u6EDA\u52A8",
2707
2930
  hintHistory: "\u5386\u53F2",
2708
2931
  hintAbort: "\u4E2D\u6B62",
2709
2932
  hintQuit: "\u9000\u51FA",
2710
2933
  abortedHint: "\u7528\u6237\u5DF2\u4E2D\u6B62\u672C\u8F6E \xB7 \u518D\u6309 Esc \u6E05\u9664 \xB7 \u23CE \u7EE7\u7EED\u63D0\u95EE"
2711
2934
  },
2935
+ shellConfirm: {
2936
+ title: "Shell \u547D\u4EE4",
2937
+ bgTitle: "\u540E\u53F0\u8FDB\u7A0B",
2938
+ subtitle: "\u6A21\u578B\u8BF7\u6C42\u6267\u884C Shell \u547D\u4EE4",
2939
+ bgSubtitle: "\u957F\u65F6\u95F4\u8FD0\u884C \u2014 \u6279\u51C6\u540E\u6301\u7EED\u8FD0\u884C\uFF0C/kill \u53EF\u505C\u6B62",
2940
+ denyTitle: "\u62D2\u7EDD \u2014 \u63D0\u4F9B\u539F\u56E0",
2941
+ optional: "\u53EF\u9009",
2942
+ denyFooter: "\u8F93\u5165\u539F\u56E0 \xB7 \u23CE \u63D0\u4EA4 \xB7 Esc \u8DF3\u8FC7\uFF08\u76F4\u63A5\u62D2\u7EDD\uFF09",
2943
+ awaiting: "\u7B49\u5F85\u4E2D",
2944
+ pickFooter: "\u2191\u2193 \u9009\u62E9 \xB7 \u23CE \u786E\u8BA4 \xB7 Tab \u6DFB\u52A0\u8BF4\u660E \xB7 Esc \u53D6\u6D88",
2945
+ allowOnce: "\u5141\u8BB8\u4E00\u6B21",
2946
+ allowOnceDesc: "\u6267\u884C\u6B64\u547D\u4EE4\uFF0C\u4E0B\u6B21\u518D\u95EE",
2947
+ allowAlways: "\u59CB\u7EC8\u5141\u8BB8",
2948
+ allowAlwaysDesc: "\u8BB0\u4F4F `{prefix}`\uFF0C\u672C\u9879\u76EE\u5185\u4E0D\u518D\u8BE2\u95EE",
2949
+ deny: "\u62D2\u7EDD",
2950
+ denyDesc: "\u6309 Tab \u6DFB\u52A0\u8BF4\u660E\uFF0C\u544A\u8BC9\u6A21\u578B\u539F\u56E0"
2951
+ },
2952
+ editConfirm: {
2953
+ footer: "[y/Enter] \u5E94\u7528 \xB7 [n] \u62D2\u7EDD\u5E76\u8BF4\u660E \xB7 [a] \u5E94\u7528\u5269\u4F59 \xB7 [A] \u5207\u6362 AUTO \xB7 [\u2191\u2193/Space] \u6EDA\u52A8 \xB7 [Esc] \u4E2D\u6B62",
2954
+ newTag: "\u65B0\u589E",
2955
+ editTag: "\u7F16\u8F91",
2956
+ linesCount: "-{removed} +{added} \u884C",
2957
+ viewingRange: "\u6B63\u5728\u67E5\u770B {start}-{end}/{total}",
2958
+ denyFooter: "\u23CE \u63D0\u4EA4 \xB7 Esc \u8DF3\u8FC7\uFF08\u76F4\u63A5\u62D2\u7EDD\uFF09",
2959
+ oldLabel: " \u65E7\u5185\u5BB9",
2960
+ newLabel: " \u65B0\u5185\u5BB9",
2961
+ sideBySide: " \u5DE6\u53F3\u5BF9\u6BD4 \xB7 \u5DE6\u4FA7\u5220\u9664\uFF0C\u53F3\u4FA7\u65B0\u589E \xB7 \u6309\u504F\u79FB\u914D\u5BF9",
2962
+ linesAbove: " \u2191 \u4E0A\u65B9 {count} \u884C\uFF08\u2191/k \u6216 PgUp\uFF09",
2963
+ linesAbovePlural: " \u2191 \u4E0A\u65B9 {count} \u884C\uFF08\u2191/k \u6216 PgUp\uFF09",
2964
+ linesBelow: " \u2193 \u4E0B\u65B9 {count} \u884C\uFF08\u2193/j \u6216 Space/PgDn\uFF09",
2965
+ linesBelowPlural: " \u2193 \u4E0B\u65B9 {count} \u884C\uFF08\u2193/j \u6216 Space/PgDn\uFF09"
2966
+ },
2967
+ sessionPicker: {
2968
+ header: " \u25C8 REASONIX \xB7 \u9009\u62E9\u4F1A\u8BDD ",
2969
+ title: "\u9009\u62E9\u4F1A\u8BDD \u2014 {workspace}",
2970
+ messages: "{count} \u6761\u6D88\u606F",
2971
+ messagesPlural: "{count} \u6761\u6D88\u606F",
2972
+ turns: "{count} \u8F6E",
2973
+ pickerHint: "\u2191\u2193 \u9009\u62E9 \xB7 \u23CE \u6253\u5F00 \xB7 [n] \u65B0\u5EFA \xB7 [d] \u5220\u9664 \xB7 [r] \u91CD\u547D\u540D \xB7 Esc \u9000\u51FA",
2974
+ empty: " \u6B64\u5DE5\u4F5C\u533A\u6682\u65E0\u5DF2\u4FDD\u5B58\u7684\u4F1A\u8BDD \u2014 \u6309 ",
2975
+ emptyNew: " \u5F00\u59CB\u65B0\u4F1A\u8BDD",
2976
+ renamePrompt: ' \u91CD\u547D\u540D "{from}" \u2192 ',
2977
+ renameHint: " \u23CE \u786E\u8BA4\u91CD\u547D\u540D \xB7 Esc \u53D6\u6D88",
2978
+ emptyHint: " \u23CE \u65B0\u5EFA\u4F1A\u8BDD \xB7 Esc \u9000\u51FA",
2979
+ justNow: "\u521A\u521A",
2980
+ minAgo: "{count} \u5206\u949F\u524D",
2981
+ yesterday: "\u6628\u5929",
2982
+ hoursAgo: "{count} \u5C0F\u65F6\u524D",
2983
+ daysAgo: "{count} \u5929\u524D"
2984
+ },
2985
+ modelPicker: {
2986
+ header: " \u25C8 REASONIX \xB7 \u9009\u62E9\u914D\u7F6E ",
2987
+ loading: " \xB7 \u52A0\u8F7D\u76EE\u5F55\u2026",
2988
+ catalogEmpty: " \xB7 \u76EE\u5F55\u4E3A\u7A7A \u2014 \u4F7F\u7528\u5DF2\u77E5\u5907\u9009",
2989
+ modelsAvailable: " \xB7 {count} \u4E2A\u6A21\u578B\u53EF\u7528",
2990
+ presetsHeader: " \u9884\u8BBE \xB7 \u63A8\u8350 \u2014 \u6A21\u578B + \u5F3A\u5EA6 + \u81EA\u52A8\u5347\u7EA7",
2991
+ modelsHeader: " \u6A21\u578B \xB7 \u76F4\u63A5\u9009\u62E9 \u2014 \u81EA\u52A8\u5347\u7EA7\u4FDD\u6301\u4E0D\u53D8",
2992
+ pickerFooter: " \u2191\u2193 \u9009\u62E9 \xB7 \u23CE \u786E\u8BA4 \xB7 [r] \u5237\u65B0 \xB7 Esc \u53D6\u6D88",
2993
+ currentLabel: " \xB7 \u5F53\u524D"
2994
+ },
2995
+ slashSuggestions: {
2996
+ noMatch: "\u6CA1\u6709\u5339\u914D\u6B64\u524D\u7F00\u7684\u659C\u6760\u547D\u4EE4",
2997
+ backspaceHint: " \u2014 \u6309 Backspace \u4FEE\u6539\uFF0C\u6216 /help \u67E5\u770B\u5B8C\u6574\u5217\u8868",
2998
+ commandCount: "{count} \u4E2A\u547D\u4EE4",
2999
+ commandCountPlural: "{count} \u4E2A\u547D\u4EE4",
3000
+ aboveLabel: " \u2191 {count} \u4E2A\u4EE5\u4E0A",
3001
+ belowLabel: " \u2193 {count} \u4E2A\u4EE5\u4E0B",
3002
+ advancedHint: " + {count} \u4E2A\u9AD8\u7EA7\u547D\u4EE4 \xB7 \u8F93\u5165\u5B57\u6BCD\u641C\u7D22",
3003
+ footerHint: " \u2191\u2193 \u5BFC\u822A \xB7 Tab / \u23CE \u9009\u62E9 \xB7 Esc \u53D6\u6D88",
3004
+ groupChat: "\u804A\u5929",
3005
+ groupSetup: "\u8BBE\u7F6E",
3006
+ groupInfo: "\u4FE1\u606F",
3007
+ groupSession: "\u4F1A\u8BDD",
3008
+ groupExtend: "\u6269\u5C55",
3009
+ groupCode: "\u4EE3\u7801",
3010
+ groupJobs: "\u4EFB\u52A1",
3011
+ groupAdvanced: "\u9AD8\u7EA7"
3012
+ },
3013
+ atMentions: {
3014
+ loading: "\u52A0\u8F7D\u4E2D\u2026",
3015
+ entrySingular: "{count} \u6761",
3016
+ entryPlural: "{count} \u6761",
3017
+ searching: "\u641C\u7D22\u4E2D\u2026",
3018
+ scanned: "\u5DF2\u626B\u63CF",
3019
+ match: "\u4E2A\u5339\u914D",
3020
+ matches: "\u4E2A\u5339\u914D",
3021
+ forFilter: '\u5339\u914D "{filter}"',
3022
+ noMatch: '\u6CA1\u6709\u5339\u914D "{filter}" \u7684\u6587\u4EF6',
3023
+ emptyDir: "\u7A7A\u76EE\u5F55",
3024
+ scanning: "\u6B63\u5728\u626B\u63CF\u76EE\u5F55\u6811\u2026",
3025
+ footerBrowse: "\u2191\u2193 \u5BFC\u822A \xB7 Tab \u8FDB\u5165\u6587\u4EF6\u5939 \xB7 \u23CE \u63D2\u5165 \xB7 Esc \u53D6\u6D88",
3026
+ footerBrowseSearch: "\u2191\u2193 \u5BFC\u822A \xB7 Tab / \u23CE \u4EE5 @path \u63D2\u5165 \xB7 Esc \u53D6\u6D88",
3027
+ footerInsert: "\u2191\u2193 \u5BFC\u822A \xB7 Tab / \u23CE \u4EE5 @path \u63D2\u5165 \xB7 Esc \u53D6\u6D88"
3028
+ },
3029
+ statsPanel: {
3030
+ modePlan: "\u8BA1\u5212",
3031
+ modeYolo: "\u81EA\u7531",
3032
+ modeAuto: "\u81EA\u52A8",
3033
+ modeReview: "\u5BA1\u67E5",
3034
+ pro: "\u21E7 \u4E13\u4E1A",
3035
+ budget: " \u9884\u7B97 "
3036
+ },
3037
+ welcomeBanner: {
3038
+ workspace: "\u25B8 \u5DE5\u4F5C\u533A",
3039
+ relaunchHint: "\uFF08\u91CD\u542F\u65F6\u7528 --dir <path> \u5207\u6362\uFF09",
3040
+ dashboard: "\u25B8 \u7F51\u9875"
3041
+ },
3042
+ ctxBreakdown: {
3043
+ title: "\u25A3 \u4E0A\u4E0B\u6587",
3044
+ compactHint: " /compact \u6298\u53E0\uFF08\u8D85\u8FC7 50% \u81EA\u52A8\u89E6\u53D1\uFF09\xB7 /new \u6E05\u7A7A\u65E5\u5FD7",
3045
+ topTools: " \u5E38\u7528\u5DE5\u5177\uFF08\u6309\u6210\u672C\u6392\u5E8F\uFF0C{count} \u4E2A\uFF09\uFF1A",
3046
+ msg: "\u6761",
3047
+ turnLabel: "\u8F6E"
3048
+ },
3049
+ startup: {
3050
+ codeRooted: '\u25B8 reasonix code\uFF1A\u6839\u76EE\u5F55 {rootDir}\uFF0C\u4F1A\u8BDD "{session}" \xB7 {tools} \u4E2A\u539F\u751F\u5DE5\u5177{semantic}',
3051
+ ephemeral: "\uFF08\u4E34\u65F6\uFF09",
3052
+ semanticOn: " \xB7 \u8BED\u4E49\u641C\u7D22\u5DF2\u5F00\u542F"
3053
+ },
3054
+ doctorErrors: {
3055
+ unreadable: "{path} \u65E0\u6CD5\u8BFB\u53D6 \u2014 {message}",
3056
+ cannotList: "\u65E0\u6CD5\u5217\u51FA \u2014 {message}",
3057
+ parseFailed: "\u65E0\u6CD5\u89E3\u6790 settings.json \u2014 {message}",
3058
+ probeFailed: "\u63A2\u6D4B\u5931\u8D25 \u2014 {message}"
3059
+ },
3060
+ webErrors: {
3061
+ status: "web_search \u72B6\u6001\u7801 {status}",
3062
+ mojeekBlocked: "web_search: Mojeek \u53CD\u722C\u9875\u9762 \u2014 \u9891\u7387\u9650\u5236\u6216\u88AB\u5C4F\u853D",
3063
+ mojeekNoResults: "web_search: \u8FD4\u56DE 0 \u6761\u7ED3\u679C\u4F46\u54CD\u5E94\u770B\u8D77\u6765\u4E0D\u662F\u6B63\u5E38\u7A7A\u7ED3\u679C\u9875\uFF08{chars} \u5B57\u7B26\uFF0C\u524D 120 \u5B57\u7B26\uFF1A{preview}\uFF09",
3064
+ invalidEndpoint: 'web_search: \u65E0\u6548\u7684 SearXNG \u7AEF\u70B9 "{endpoint}"',
3065
+ endpointMustBeHttp: "web_search: SearXNG \u7AEF\u70B9\u5FC5\u987B\u662F http(s) \u534F\u8BAE\uFF0C\u5F53\u524D\u4E3A {protocol}",
3066
+ cannotReach: "web_search: \u65E0\u6CD5\u8BBF\u95EE SearXNG \u670D\u52A1\u5668 {endpoint}\u3002\u8BF7\u5B89\u88C5 SearXNG\uFF08https://github.com/searxng/searxng\uFF09\u5E76\u542F\u52A8\uFF08\u4F8B\u5982 `docker run -d -p 8080:8080 searxng/searxng`\uFF09\uFF0C\u6216\u4F7F\u7528 /search-engine mojeek \u5207\u6362\u5230\u9ED8\u8BA4\u5F15\u64CE\u3002",
3067
+ searxngNoResults: "web_search: \u8FD4\u56DE 0 \u6761\u7ED3\u679C\u4F46 SearXNG \u54CD\u5E94\u770B\u8D77\u6765\u4E0D\u662F\u6B63\u5E38\u7A7A\u7ED3\u679C\u9875\uFF08{chars} \u5B57\u7B26\uFF09",
3068
+ fetchStatus: "web_fetch \u72B6\u6001\u7801 {status}\uFF08{url}\uFF09",
3069
+ fetchTooLarge: "web_fetch \u62D2\u7EDD\uFF1Acontent-length {len} \u5B57\u8282\u8D85\u8FC7\u4E0A\u9650 {cap} \u5B57\u8282\uFF08{url}\uFF09",
3070
+ fetchBodyTooLarge: "web_fetch \u62D2\u7EDD\uFF1A\u54CD\u5E94\u4F53\u8D85\u8FC7 {cap} \u5B57\u8282\u4E0A\u9650\uFF08\u5DF2\u63A5\u6536 {seen} \u5B57\u8282\uFF09",
3071
+ fetchInvalidUrl: "web_fetch: URL \u5FC5\u987B\u4EE5 http:// \u6216 https:// \u5F00\u5934"
3072
+ },
3073
+ choiceConfirm: {
3074
+ customLabel: "\u81EA\u5B9A\u4E49\u56DE\u7B54",
3075
+ customDesc: "\u4EE5\u4E0A\u9009\u9879\u90FD\u4E0D\u5408\u9002 \u2014 \u8F93\u5165\u81EA\u7531\u683C\u5F0F\u56DE\u590D\uFF0C\u6A21\u578B\u4F1A\u539F\u6837\u8BFB\u53D6",
3076
+ cancelLabel: "\u53D6\u6D88 \u2014 \u653E\u5F03\u95EE\u9898",
3077
+ cancelDesc: "\u6A21\u578B\u505C\u6B62\u5E76\u8BE2\u95EE\u4F60\u771F\u6B63\u7684\u9700\u6C42"
3078
+ },
2712
3079
  cardTitles: {
2713
3080
  usage: "\u7528\u91CF",
2714
3081
  context: "\u4E0A\u4E0B\u6587",
@@ -2718,7 +3085,9 @@ var zhCN = {
2718
3085
  reasoning: "\u63A8\u7406\u4E2D",
2719
3086
  reasoningAborted: "\u63A8\u7406\uFF08\u5DF2\u4E2D\u6B62\uFF09",
2720
3087
  reasoningEllipsis: "\u63A8\u7406\u4E2D\u2026",
2721
- error: "\u9519\u8BEF"
3088
+ error: "\u9519\u8BEF",
3089
+ doctor: "\u73AF\u5883\u8BCA\u65AD",
3090
+ you: "\u4F60"
2722
3091
  },
2723
3092
  cardLabels: {
2724
3093
  prompt: "\u63D0\u793A",
@@ -2732,7 +3101,7 @@ var zhCN = {
2732
3101
  tools: "\u5DE5\u5177",
2733
3102
  log: "\u65E5\u5FD7",
2734
3103
  input: "\u8F93\u5165",
2735
- topTools: "Top \u5DE5\u5177",
3104
+ topTools: "\u5E38\u7528\u5DE5\u5177",
2736
3105
  logMsgs: "\u65E5\u5FD7\u6D88\u606F",
2737
3106
  hitSingular: "{count} \u6761\u7ED3\u679C \xB7 {files} \u4E2A\u6587\u4EF6",
2738
3107
  hitsPlural: "{count} \u6761\u7ED3\u679C \xB7 {files} \u4E2A\u6587\u4EF6",
@@ -2757,7 +3126,42 @@ var zhCN = {
2757
3126
  retries: "\u6B21\u91CD\u8BD5",
2758
3127
  reasoningLabel: "\u63A8\u7406 \xB7 {count} \xB6",
2759
3128
  runningLabel: "\u8FD0\u884C\u4E2D",
2760
- workingLabel: "\u5DE5\u4F5C\u4E2D"
3129
+ workingLabel: "\u5904\u7406\u4E2D",
3130
+ defaultFooter: "\u2191\u2193 \u9009\u62E9 \xB7 \u23CE \u786E\u8BA4 \xB7 Esc \u53D6\u6D88",
3131
+ applyAction: "[a] \u5E94\u7528",
3132
+ skipAction: "[s] \u8DF3\u8FC7",
3133
+ rejectAction: "[r] \u62D2\u7EDD",
3134
+ levelOk: "\u6B63\u5E38",
3135
+ levelWarn: "\u8B66\u544A",
3136
+ levelFail: "\u5931\u8D25",
3137
+ checksLabel: "\u68C0\u67E5\u9879",
3138
+ passed: "\u901A\u8FC7",
3139
+ warnTag: "\u8B66\u544A",
3140
+ failTag: "\u5931\u8D25",
3141
+ stepLabel: "\u6B65\u9AA4",
3142
+ done: "\u5DF2\u5B8C\u6210",
3143
+ inProgress: "\u2190 \u8FDB\u884C\u4E2D",
3144
+ upcoming: "\u5F85\u5904\u7406",
3145
+ resumed: "\u5DF2\u6062\u590D \xB7 ",
3146
+ archive: "\u23EA \u5F52\u6863 \xB7 ",
3147
+ more: "\u22EE +{count} \u66F4\u591A",
3148
+ categoryUser: "\u7528\u6237",
3149
+ categoryFeedback: "\u53CD\u9988",
3150
+ categoryProject: "\u9879\u76EE",
3151
+ categoryReference: "\u53C2\u8003"
3152
+ },
3153
+ copyMode: {
3154
+ title: "\u2500\u2500 \u590D\u5236\u6A21\u5F0F \u2500\u2500",
3155
+ help: "j/k \u6216 \u2191/\u2193 \u79FB\u52A8 \xB7 v \u8D77\u9009\u533A \xB7 y \u590D\u5236 \xB7 g/G \u9876/\u5E95 \xB7 q \u9000\u51FA",
3156
+ statusBar: "\u7B2C {cur}/{total} \u884C \xB7 \u9009\u533A\uFF1A{sel}",
3157
+ statusYanked: "\u5DF2\u590D\u5236 {size} \u5B57\u7B26\uFF08osc52={osc52}\uFF09",
3158
+ statusEmpty: "\u672A\u9009\u4E2D\u5185\u5BB9",
3159
+ empty: "\uFF08\u8FD8\u6CA1\u6709\u804A\u5929\u5185\u5BB9 \u2014 \u5148\u548C\u6A21\u578B\u8BF4\u70B9\u4EC0\u4E48\uFF09",
3160
+ labelUser: "\u4F60",
3161
+ labelAssistant: "\u52A9\u624B",
3162
+ labelReasoning: "\u63A8\u7406",
3163
+ yankedToast: "\u25B8 \u5DF2\u590D\u5236 {size} \u5B57\u7B26\u5230\u526A\u8D34\u677F (osc52)",
3164
+ yankedToastFile: "\u25B8 \u5DF2\u590D\u5236 {size} \u5B57\u7B26 \xB7 \u6587\u4EF6\uFF1A{path}"
2761
3165
  }
2762
3166
  };
2763
3167
 
@@ -3277,6 +3681,10 @@ var ToolRegistry = class {
3277
3681
  setResultAugmenter(fn) {
3278
3682
  this._resultAugmenter = fn;
3279
3683
  }
3684
+ /** True when an augmenter is already wired — lets late-installing callers skip clobbering an earlier one. */
3685
+ get hasResultAugmenter() {
3686
+ return this._resultAugmenter !== null;
3687
+ }
3280
3688
  register(def) {
3281
3689
  if (!def.name) throw new Error("tool requires a name");
3282
3690
  const internal = { ...def };
@@ -4799,6 +5207,7 @@ function signature(call) {
4799
5207
 
4800
5208
  // src/loop.ts
4801
5209
  var ESCALATION_MODEL = "deepseek-v4-pro";
5210
+ var PARENT_BUDGET_WARN_THRESHOLD = 5;
4802
5211
  var CacheFirstLoop = class {
4803
5212
  client;
4804
5213
  prefix;
@@ -4835,6 +5244,7 @@ var CacheFirstLoop = class {
4835
5244
  _turnFailures = new TurnFailureTracker();
4836
5245
  _turnSelfCorrected = false;
4837
5246
  _foldedThisTurn = false;
5247
+ _toolDispatchesThisStep = 0;
4838
5248
  context;
4839
5249
  /** Subscribe API so UI hooks can derive `running` from finally-guaranteed insertions. */
4840
5250
  get inflight() {
@@ -4889,6 +5299,23 @@ var CacheFirstLoop = class {
4889
5299
  stormThreshold: parsePositiveIntEnv(process.env.REASONIX_STORM_THRESHOLD),
4890
5300
  stormWindow: parsePositiveIntEnv(process.env.REASONIX_STORM_WINDOW)
4891
5301
  });
5302
+ if (!this.tools.hasResultAugmenter) {
5303
+ this.tools.setResultAugmenter((_name, _args, result) => {
5304
+ this._toolDispatchesThisStep++;
5305
+ const remaining = this.maxToolIters - this._toolDispatchesThisStep;
5306
+ if (remaining <= 0) {
5307
+ return `${result}
5308
+
5309
+ [budget: 0 of ${this.maxToolIters} tool calls left this turn \u2014 finalize NOW; the next iter forces a summary]`;
5310
+ }
5311
+ if (remaining <= PARENT_BUDGET_WARN_THRESHOLD) {
5312
+ return `${result}
5313
+
5314
+ [budget: ${remaining} of ${this.maxToolIters} tool calls left this turn \u2014 wrap up soon]`;
5315
+ }
5316
+ return result;
5317
+ });
5318
+ }
4892
5319
  this.sessionName = opts.session ?? null;
4893
5320
  if (this.sessionName) {
4894
5321
  const prior = loadSessionMessages(this.sessionName);
@@ -5142,6 +5569,7 @@ ${reason}`
5142
5569
  this._turnSelfCorrected = false;
5143
5570
  this._escalateThisTurn = false;
5144
5571
  this._foldedThisTurn = false;
5572
+ this._toolDispatchesThisStep = 0;
5145
5573
  let armedConsumed = false;
5146
5574
  if (this._proArmedForNextTurn) {
5147
5575
  this._escalateThisTurn = true;
@@ -7823,6 +8251,74 @@ Prefer \`list_directory\` for a single-level view, \`search_files\` to find spec
7823
8251
  return `moved ${displayRel4(rootDir, src)} \u2192 ${displayRel4(rootDir, dst)}`;
7824
8252
  }
7825
8253
  });
8254
+ registry.register({
8255
+ name: "delete_file",
8256
+ description: "Delete one file under the sandbox root. Refuses directories \u2014 use delete_directory for those. Errors if the path doesn't exist.",
8257
+ parameters: {
8258
+ type: "object",
8259
+ properties: { path: { type: "string" } },
8260
+ required: ["path"]
8261
+ },
8262
+ fn: async (args) => {
8263
+ const abs = safePath(args.path);
8264
+ const st = await fs4.lstat(abs);
8265
+ if (st.isDirectory()) {
8266
+ throw new Error(
8267
+ `delete_file: ${args.path} is a directory \u2014 use delete_directory to remove it`
8268
+ );
8269
+ }
8270
+ await fs4.unlink(abs);
8271
+ return `deleted ${displayRel4(rootDir, abs)}`;
8272
+ }
8273
+ });
8274
+ registry.register({
8275
+ name: "delete_directory",
8276
+ description: "Recursively delete a directory under the sandbox root. Pass `recursive:false` to refuse non-empty directories. Errors if the path doesn't exist.",
8277
+ parameters: {
8278
+ type: "object",
8279
+ properties: {
8280
+ path: { type: "string" },
8281
+ recursive: {
8282
+ type: "boolean",
8283
+ description: "When true (default) deletes the directory and all its contents. When false, only removes empty directories \u2014 non-empty refuses with an error."
8284
+ }
8285
+ },
8286
+ required: ["path"]
8287
+ },
8288
+ fn: async (args) => {
8289
+ const abs = safePath(args.path);
8290
+ const st = await fs4.lstat(abs);
8291
+ if (!st.isDirectory()) {
8292
+ throw new Error(`delete_directory: ${args.path} is a file \u2014 use delete_file to remove it`);
8293
+ }
8294
+ const recursive = args.recursive !== false;
8295
+ if (recursive) {
8296
+ await fs4.rm(abs, { recursive: true, force: false });
8297
+ } else {
8298
+ await fs4.rmdir(abs);
8299
+ }
8300
+ return `deleted ${displayRel4(rootDir, abs)}/${recursive ? " (recursive)" : ""}`;
8301
+ }
8302
+ });
8303
+ registry.register({
8304
+ name: "copy_file",
8305
+ description: "Copy a file or directory under the sandbox root. Both source and destination resolve under the sandbox. Parent directories of the destination are created as needed. Refuses to overwrite an existing destination \u2014 delete it first if you want to replace it.",
8306
+ parameters: {
8307
+ type: "object",
8308
+ properties: {
8309
+ source: { type: "string" },
8310
+ destination: { type: "string" }
8311
+ },
8312
+ required: ["source", "destination"]
8313
+ },
8314
+ fn: async (args) => {
8315
+ const src = safePath(args.source);
8316
+ const dst = safePath(args.destination);
8317
+ await fs4.mkdir(pathMod4.dirname(dst), { recursive: true });
8318
+ await fs4.cp(src, dst, { recursive: true, force: false, errorOnExist: true });
8319
+ return `copied ${displayRel4(rootDir, src)} \u2192 ${displayRel4(rootDir, dst)}`;
8320
+ }
8321
+ });
7826
8322
  return registry;
7827
8323
  }
7828
8324
 
@@ -8479,6 +8975,18 @@ var DEFAULT_SUBAGENT_MODEL = "deepseek-v4-flash";
8479
8975
  var DEFAULT_SUBAGENT_EFFORT = "high";
8480
8976
  var SUBAGENT_TOOL_NAME = "spawn_subagent";
8481
8977
  var NEVER_INHERITED_TOOLS = /* @__PURE__ */ new Set([SUBAGENT_TOOL_NAME, "submit_plan"]);
8978
+ var SOFT_HINT_AFTER_SPAWNS = 1;
8979
+ var STRONG_HINT_AFTER_SPAWNS = 4;
8980
+ var STRONG_HINT_TOKEN_THRESHOLD = 5e4;
8981
+ function subagentBudgetHint(spawnCount, totalTokens) {
8982
+ if (spawnCount > STRONG_HINT_AFTER_SPAWNS || totalTokens >= STRONG_HINT_TOKEN_THRESHOLD) {
8983
+ return `[budget: this session has now spawned ${spawnCount} subagents totalling ${totalTokens} tokens. Each spawn pays a fresh prefix-cache miss plus a full child loop \u2014 confirm the next spawn is genuinely needed (parallel fan-out or >10-read context blow-up) before calling spawn_subagent again. If you can answer with direct tools, do that instead.]`;
8984
+ }
8985
+ if (spawnCount > SOFT_HINT_AFTER_SPAWNS) {
8986
+ return `[note: this session has spawned ${spawnCount} subagents totalling ${totalTokens} tokens; confirm this one is worth it.]`;
8987
+ }
8988
+ return null;
8989
+ }
8482
8990
  async function spawnSubagent(opts) {
8483
8991
  const model = opts.model ?? DEFAULT_SUBAGENT_MODEL;
8484
8992
  const maxToolIters = opts.maxToolIters ?? DEFAULT_MAX_ITERS;
@@ -8700,10 +9208,12 @@ function registerSubagentTool(parentRegistry, opts) {
8700
9208
  const maxToolIters = opts.maxToolIters ?? DEFAULT_MAX_ITERS;
8701
9209
  const maxResultChars = opts.maxResultChars ?? DEFAULT_MAX_RESULT_CHARS2;
8702
9210
  const sink = opts.sink;
9211
+ let sessionSpawnCount = 0;
9212
+ let sessionSpawnTokens = 0;
8703
9213
  parentRegistry.register({
8704
9214
  name: SUBAGENT_TOOL_NAME,
8705
9215
  parallelSafe: true,
8706
- description: "Spawn an isolated subagent to handle a self-contained subtask in a fresh context, returning only its final answer. Use for: deep codebase exploration that would flood the main context, multi-step research where you only need the conclusion, or any focused subtask whose intermediate reasoning the user does not need to see. The subagent inherits all your tools (filesystem, shell, web, MCP, etc.) but runs in its own isolated message log \u2014 its tool calls and reasoning never enter your context. Only the final assistant message comes back as this tool's result. Keep tasks focused; the subagent has a stricter iter budget than you do.",
9216
+ description: "Spawn an isolated subagent to handle a self-contained subtask in a fresh context, returning only its final answer. **Prefer direct tools.** Spawn primarily for parallel fan-out (2+ independent investigations issued in one tool batch) or when the work would otherwise need >10 file reads/searches whose trail you don't need to keep. Single greps, 1-3 file cross-references, and 'keep my context clean for one question' are NOT good reasons to spawn \u2014 direct tools are cheaper and let you reference the evidence later. Each spawn pays a fresh prefix-cache miss plus a full child loop. The subagent inherits your tools but runs in its own isolated message log; only the final assistant message comes back. Keep tasks focused; the subagent has a stricter iter budget than you do.",
8707
9217
  parameters: {
8708
9218
  type: "object",
8709
9219
  properties: {
@@ -8758,7 +9268,12 @@ ${escalationContract(model)}`;
8758
9268
  sink,
8759
9269
  parentSignal: ctx?.signal
8760
9270
  });
8761
- return formatSubagentResult(result);
9271
+ sessionSpawnCount++;
9272
+ sessionSpawnTokens += result.usage.totalTokens;
9273
+ const formatted = formatSubagentResult(result);
9274
+ const hint = subagentBudgetHint(sessionSpawnCount, sessionSpawnTokens);
9275
+ return hint ? `${formatted}
9276
+ ${hint}` : formatted;
8762
9277
  }
8763
9278
  });
8764
9279
  return parentRegistry;
@@ -10074,7 +10589,7 @@ function registerShellTools(registry, opts) {
10074
10589
  const isAllowAll = typeof opts.allowAll === "function" ? opts.allowAll : () => opts.allowAll === true;
10075
10590
  registry.register({
10076
10591
  name: "run_command",
10077
- description: "Run a shell command in the project root and return its combined stdout+stderr.\n\nConstraints (read these before the first call):\n\u2022 Chain operators `|`, `||`, `&&`, `;` ARE supported \u2014 parsed natively, no shell invoked, so semantics are identical on Windows / macOS / Linux. Each chain segment is allowlist-checked individually: `git status | grep main` runs if both halves are allowed.\n\u2022 File redirects ARE supported: `>` truncate, `>>` append, `<` stdin from file, `2>` / `2>>` stderr to file, `2>&1` merge stderr\u2192stdout, `&>` both to file. Targets resolve relative to the project root. At most one redirect per fd per segment.\n\u2022 Background `&`, heredoc `<<`, command substitution `$(\u2026)`, subshells `(\u2026)`, and process substitution `<(\u2026)` are NOT supported. Wrap a literal `&` arg in quotes; for input use a `<` file or the binary's own --input flag.\n\u2022 Env-var expansion `$VAR` is NOT performed \u2014 `$VAR` is passed as a literal string. Use the binary's own --env flag or substitute the value yourself.\n\u2022 `cd` DOES NOT PERSIST between calls \u2014 each call spawns a fresh process rooted at the project. `cd` also does not persist within parsed chains like `cd dir && command`. Use a command-native cwd flag instead: `npm --prefix <dir> run <script>`, `npm --prefix <dir> exec -- <bin>`, `git -C <dir> ...`, `cargo -C <dir> ...`, `pytest <dir>/tests`.\n\u2022 Glob patterns (`*.ts`) are passed through as literal arguments \u2014 no shell expansion. Use `grep -r`, `rg`, `find -name`, etc.\n\u2022 Avoid commands with unbounded output (`netstat -ano`, `find /`, etc.) \u2014 they waste tokens. Filter at source: `netstat -ano -p TCP`, `find src -name '*.ts'`, `grep -c`, `wc -l`.\n\nCommon read-only inspection and test/lint/typecheck commands run immediately; anything that could mutate state, install dependencies, or touch the network is refused until the user confirms it in the TUI. Prefer this over asking the user to run a command manually \u2014 after edits, run the project's tests to verify.",
10592
+ description: "Run a shell command in the project root; returns combined stdout+stderr. Allowlisted read-only / test / lint / typecheck commands run immediately; anything that could mutate state, install deps, or touch the network is gated by user confirmation. Prefer this over asking the user to run a command manually \u2014 after edits, run the project's tests to verify.\n\nConstraints (no real shell \u2014 argv is parsed natively for cross-platform parity):\n\u2022 Supported: chain ops `|` / `||` / `&&` / `;` (each segment allowlist-checked individually), file redirects `>` / `>>` / `<` / `2>` / `2>>` / `2>&1` / `&>` (target paths resolve relative to project root, max one redirect per fd per segment).\n\u2022 NOT supported: background `&`, heredoc `<<`, command substitution `$(\u2026)`, subshells `(\u2026)`, process substitution `<(\u2026)`, `$VAR` env expansion, glob expansion. To pass an operator char as literal arg, quote it (`grep \"a|b\" file`).\n\u2022 `cd` does NOT persist \u2014 between calls OR within a chain like `cd dir && cmd`. Use the binary's own cwd flag: `npm --prefix <dir>`, `git -C <dir>`, `cargo -C <dir>`, `pytest <dir>/tests`.\n\u2022 Filter at source \u2014 unbounded output (`netstat -ano`, `find /`) wastes tokens. Use `grep -c`, `wc -l`, narrower paths, etc.",
10078
10593
  // Plan-mode gate: allow allowlisted commands through (git status,
10079
10594
  // cargo check, ls, grep …) so the model can actually investigate
10080
10595
  // during planning. Anything that would otherwise trigger a
@@ -10126,7 +10641,7 @@ function registerShellTools(registry, opts) {
10126
10641
  });
10127
10642
  registry.register({
10128
10643
  name: "run_background",
10129
- description: "Spawn a long-running process (dev server, watcher, any command that doesn't naturally exit) and detach. Waits up to `waitSec` seconds for startup (or until the output matches a readiness signal like 'Local:', 'listening on', 'compiled successfully'), then returns the job id + startup preview. The process keeps running; call `job_output` to tail its logs, `stop_job` to kill it, `list_jobs` to see all running jobs.\n\nSame shell constraints as run_command: NO `&&` / `||` / `|` / `;` / `>` / `<` / `2>&1`, `cd` doesn't persist. Dev servers that need a subdirectory: use the tool's own --prefix / --cwd flag. For Vite specifically, `--prefix` on npm only tells npm where package.json is; vite's server root still defaults to process cwd, so pass `vite <project-dir>` or configure via `vite.config.ts` root.\n\nUSE THIS \u2014 not `run_command` \u2014 for: npm/yarn/pnpm run dev, uvicorn / flask run, go run, cargo watch, tsc --watch, webpack serve, anything with 'dev' / 'serve' / 'watch' in the name.",
10644
+ description: "Spawn a long-running process (dev server, watcher) and detach. Waits up to `waitSec` for startup or a readiness signal ('Local:', 'listening on', 'compiled successfully'), then returns the job id + startup preview. Tail logs with `job_output`, kill with `stop_job`, list with `list_jobs`.\n\nSingle process only \u2014 chains / redirects / `cd` work as in run_command, but a typical dev-server invocation is one binary. Use the binary's own --cwd / --prefix flag for subdirectories. Vite gotcha: npm's `--prefix` only finds package.json; vite's server root still uses process cwd \u2014 pass `vite <project-dir>` instead.\n\nUSE THIS \u2014 not run_command \u2014 for: npm/yarn/pnpm dev, uvicorn / flask run, cargo watch, tsc --watch, webpack serve, anything with dev/serve/watch in the name.",
10130
10645
  parameters: {
10131
10646
  type: "object",
10132
10647
  properties: {
@@ -10198,6 +10713,8 @@ function registerShellTools(registry, opts) {
10198
10713
  name: "wait_for_job",
10199
10714
  description: "Block until a background job exits or produces new output, bounded by `timeoutMs`. Use this instead of polling `job_output` with identical args when you're intentionally waiting for state to change. Returns JSON with `exited`, `exitCode`, and `latestOutput`.",
10200
10715
  readOnly: true,
10716
+ parallelSafe: true,
10717
+ stormExempt: true,
10201
10718
  parameters: {
10202
10719
  type: "object",
10203
10720
  properties: {
@@ -10316,16 +10833,19 @@ async function searchMojeek(query, opts = {}) {
10316
10833
  signal: opts.signal,
10317
10834
  redirect: "follow"
10318
10835
  });
10319
- if (!resp.ok) throw new Error(`web_search ${resp.status}`);
10836
+ if (!resp.ok) throw new Error(t("webErrors.status", { status: resp.status }));
10320
10837
  const html = await resp.text();
10321
10838
  const results = parseMojeekResults(html).slice(0, topK);
10322
10839
  if (results.length === 0) {
10323
10840
  if (/no results found|did not match any documents/i.test(html)) return [];
10324
10841
  if (/captcha|verify you are human|access denied|forbidden/i.test(html)) {
10325
- throw new Error("web_search: Mojeek anti-bot page \u2014 rate-limited or blocked");
10842
+ throw new Error(t("webErrors.mojeekBlocked"));
10326
10843
  }
10327
10844
  throw new Error(
10328
- `web_search: 0 results but response doesn't look like a real empty page (${html.length} chars, first 120: ${html.slice(0, 120).replace(/\s+/g, " ")})`
10845
+ t("webErrors.mojeekNoResults", {
10846
+ chars: html.length,
10847
+ preview: html.slice(0, 120).replace(/\s+/g, " ")
10848
+ })
10329
10849
  );
10330
10850
  }
10331
10851
  return results;
@@ -10335,10 +10855,10 @@ function normalizeSearxngEndpoint(raw) {
10335
10855
  try {
10336
10856
  url = new URL(raw.includes("://") ? raw : `http://${raw}`);
10337
10857
  } catch {
10338
- throw new Error(`web_search: invalid SearXNG endpoint "${raw}"`);
10858
+ throw new Error(t("webErrors.invalidEndpoint", { endpoint: raw }));
10339
10859
  }
10340
10860
  if (url.protocol !== "http:" && url.protocol !== "https:") {
10341
- throw new Error(`web_search: SearXNG endpoint must be http(s), got ${url.protocol}`);
10861
+ throw new Error(t("webErrors.endpointMustBeHttp", { protocol: url.protocol }));
10342
10862
  }
10343
10863
  return url.origin;
10344
10864
  }
@@ -10358,19 +10878,17 @@ async function searchSearxng(query, opts = {}) {
10358
10878
  } catch (err) {
10359
10879
  if (err instanceof TypeError && err.message.includes("fetch")) {
10360
10880
  throw new Error(
10361
- `web_search: Cannot reach SearXNG server at ${opts.endpoint ?? "http://localhost:8080"}. Please install SearXNG (https://github.com/searxng/searxng) and start it (e.g. \`docker run -d -p 8080:8080 searxng/searxng\`), or switch to the default engine with /search-engine mojeek.`
10881
+ t("webErrors.cannotReach", { endpoint: opts.endpoint ?? "http://localhost:8080" })
10362
10882
  );
10363
10883
  }
10364
10884
  throw err;
10365
10885
  }
10366
- if (!resp.ok) throw new Error(`web_search ${resp.status}`);
10886
+ if (!resp.ok) throw new Error(t("webErrors.status", { status: resp.status }));
10367
10887
  const html = await resp.text();
10368
10888
  const results = parseSearxngHtmlResults(html).slice(0, topK);
10369
10889
  if (results.length === 0) {
10370
10890
  if (/no results found|did not match any documents/i.test(html)) return [];
10371
- throw new Error(
10372
- `web_search: 0 results but SearXNG response doesn't look like an empty results page (${html.length} chars)`
10373
- );
10891
+ throw new Error(t("webErrors.searxngNoResults", { chars: html.length }));
10374
10892
  }
10375
10893
  return results;
10376
10894
  }
@@ -10464,13 +10982,11 @@ async function webFetch(url, opts = {}) {
10464
10982
  clearTimeout(timer);
10465
10983
  opts.signal?.removeEventListener("abort", cancel);
10466
10984
  }
10467
- if (!resp.ok) throw new Error(`web_fetch ${resp.status} for ${url}`);
10985
+ if (!resp.ok) throw new Error(t("webErrors.fetchStatus", { status: resp.status, url }));
10468
10986
  const contentType = resp.headers.get("content-type") ?? "";
10469
10987
  const declaredLen = Number(resp.headers.get("content-length") ?? "");
10470
10988
  if (Number.isFinite(declaredLen) && declaredLen > FETCH_MAX_BYTES) {
10471
- throw new Error(
10472
- `web_fetch refused: content-length ${declaredLen} bytes exceeds ${FETCH_MAX_BYTES}-byte cap (${url})`
10473
- );
10989
+ throw new Error(t("webErrors.fetchTooLarge", { len: declaredLen, cap: FETCH_MAX_BYTES, url }));
10474
10990
  }
10475
10991
  const raw = await readBodyCapped(resp, FETCH_MAX_BYTES);
10476
10992
  const title = extractTitle(raw);
@@ -10497,9 +11013,7 @@ async function readBodyCapped(resp, maxBytes) {
10497
11013
  await reader.cancel();
10498
11014
  } catch {
10499
11015
  }
10500
- throw new Error(
10501
- `web_fetch refused: response body exceeded ${maxBytes}-byte cap (${total} bytes seen)`
10502
- );
11016
+ throw new Error(t("webErrors.fetchBodyTooLarge", { cap: maxBytes, seen: total }));
10503
11017
  }
10504
11018
  out += decoder.decode(value, { stream: true });
10505
11019
  }
@@ -10627,7 +11141,7 @@ function registerWebTools(registry, opts = {}) {
10627
11141
  },
10628
11142
  fn: async (args, ctx) => {
10629
11143
  if (!/^https?:\/\//i.test(args.url)) {
10630
- throw new Error("web_fetch: url must start with http:// or https://");
11144
+ throw new Error(t("webErrors.fetchInvalidUrl"));
10631
11145
  }
10632
11146
  const page = await webFetch(args.url, { maxChars: maxFetchChars, signal: ctx?.signal });
10633
11147
  const header = page.title ? `${page.title}
@@ -12180,6 +12694,17 @@ If you are about to write "X is missing" or "Y is not implemented" \u2014 **STOP
12180
12694
 
12181
12695
  Asserting absence without a search is the #1 way evaluative answers go wrong. Treat the urge to write "missing" as a red flag in your own reasoning.
12182
12696
 
12697
+ # When auditing or reviewing this codebase
12698
+
12699
+ When you're asked to audit / review / critique Reasonix itself ("what tools are missing?", "review the prompt system", "anything wrong with how X works?"), the failure mode isn't hallucinating absences \u2014 it's building confident, well-structured proposals on factually wrong premises. Six rails:
12700
+
12701
+ - **Auto-preview is for locating, not auditing.** Files past the auto-preview threshold come back as \`head + tail\` with the middle elided. Don't conclude what's in the elided section \u2014 runtime behavior, current architectural state, whether a plan doc is still accurate \u2014 off the preview. Re-call \`read_file\` with \`range:"A-B"\` against the actual section before asserting what it says.
12702
+ - **Flag \u2192 consumer trace.** Reading a type field (\`parallelSafe?: boolean\`, \`stormExempt?: boolean\`) is not understanding behavior. Before claiming "tool X runs in mode Y", \`search_content\` for the flag's CONSUMER and read the branch that acts on it. **For inventory claims** ("which tools have flag F?"), grep the flag \u2014 don't enumerate from memory; the field is set per-tool and easily mis-recalled.
12703
+ - **No fabricated percentages.** "Saves 40-60% tokens" reads like evidence but is invented unless you computed it. Ground numbers in a cited transcript / token count, or use hedged language ("small but non-zero", "may compound") \u2014 never present an unmeasured number as a measured one.
12704
+ - **Schema cost is real.** Every tool's description ships in every request. A new-tool proposal MUST cover (a) which existing-tool composition fails to do this, (b) rough description-token cost, (c) why a prompt or description change can't reach the same end. Default to "tighten prompt / existing tool" before "add tool".
12705
+ - **MEMORY.md is part of the design space.** The pinned memory blocks above are loaded user feedback \u2014 recommendations contradicting them ("auto-commit checkpoints", "free-credit messaging", anything the user has explicitly ruled out) are wrong by construction. Cross-check before proposing.
12706
+ - **User-facing \u2260 model-facing \u2260 library-facing.** Reasonix has four action surfaces: slash commands (user), tools (model), UI (user), and library exports (\`src/index.ts\`). Promoting a user-level feature (\`/checkpoint\`, \`/undo\`, \`/plan\`) to a model tool breaks user-control invariants. Treating a library export as "dead code" because the CLI doesn't register it to the model misreads the design \u2014 embedders consume \`src/index.ts\` directly.
12707
+
12183
12708
  # When to propose a plan (submit_plan)
12184
12709
 
12185
12710
  You have a \`submit_plan\` tool that shows the user a markdown plan and lets them Approve / Refine / Cancel before you execute. Use it proactively when the task is large enough to deserve a review gate:
@@ -12224,7 +12749,7 @@ Call shape: \`{ todos: [{ content, activeForm, status }, ...] }\` \u2014 \`conte
12224
12749
  # Plan mode (/plan)
12225
12750
 
12226
12751
  The user can ALSO enter "plan mode" via /plan, which is a stronger, explicit constraint:
12227
- - Write tools (edit_file, multi_edit, write_file, create_directory, move_file) and non-allowlisted run_command calls are BOUNCED at dispatch \u2014 you'll get a tool result like "unavailable in plan mode". Don't retry them.
12752
+ - Write tools (edit_file, multi_edit, write_file, create_directory, move_file, copy_file, delete_file, delete_directory) and non-allowlisted run_command calls are BOUNCED at dispatch \u2014 you'll get a tool result like "unavailable in plan mode". Don't retry them.
12228
12753
  - Read tools (read_file, list_directory, search_files, directory_tree, get_file_info) and allowlisted read-only / test shell commands still work \u2014 use them to investigate.
12229
12754
  - You MUST call submit_plan before anything will execute. Approve exits plan mode; Refine stays in; Cancel exits without implementing.
12230
12755
 
@@ -12239,15 +12764,18 @@ Two built-ins ship by default:
12239
12764
  - **explore** \`[\u{1F9EC} subagent]\` \u2014 read-only investigation across the codebase. Use when the user says things like "find all places that...", "how does X work across the project", "survey the code for Y". Pass \`arguments\` describing the concrete question.
12240
12765
  - **research** \`[\u{1F9EC} subagent]\` \u2014 combines web search + code reading. Use for "is X supported by lib Y", "what's the canonical way to Z", "compare our impl to the spec".
12241
12766
 
12242
- When to delegate (call \`run_skill\` with a subagent skill):
12243
- - The task would otherwise need >5 file reads or searches.
12244
- - You only need the conclusion, not the exploration trail.
12245
- - The work is self-contained (you can describe it in one paragraph).
12767
+ **Default: don't delegate.** Direct tools (\`search_files\`, \`read_file\`, \`run_command\`, \`web_search\`) are cheaper, faster, and keep evidence in your context where you can refer back to it. A subagent spawn pays a fresh prefix-cache miss and a full child loop \u2014 hundreds of ms of overhead and full input pricing for the child's first turn. For most questions the spawn costs more than it saves.
12768
+
12769
+ Spawn ONLY in these two cases:
12770
+ 1. **True parallelism** \u2014 you have 2+ independent investigations that can run concurrently in the same tool batch. The wall-time win is real and only achievable via fan-out.
12771
+ 2. **Context blow-up** \u2014 the work would otherwise need >10 file reads/searches and you only need the conclusion. Keeping the trail out of your context is the actual saving.
12246
12772
 
12247
- When NOT to delegate:
12248
- - Direct, narrow questions answerable in 1-2 tool calls \u2014 just do them.
12249
- - Anything where you need to track intermediate results yourself (planning, multi-step edits).
12250
- - Anything that requires user interaction (subagents can't submit plans or ask you for clarification).
12773
+ Anti-patterns \u2014 do NOT spawn for any of these:
12774
+ - single grep / single file read \u2192 call the tool directly
12775
+ - 1-3 file cross-reference \u2192 read them directly
12776
+ - "to keep my context clean for one question" \u2192 not enough saving to justify the spawn
12777
+ - anything that needs user interaction (subagents can't submit plans or ask for clarification)
12778
+ - anything where you need to track intermediate results yourself (planning, multi-step edits)
12251
12779
 
12252
12780
  Always pass a clear, self-contained \`arguments\` \u2014 that text is the **only** context the subagent gets.
12253
12781