jinzd-ai-cli 0.4.223 → 0.4.224

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 (36) hide show
  1. package/dist/{batch-PJIRZJCT.js → batch-EQER63CQ.js} +2 -2
  2. package/dist/{chat-index-FDSGKWQV.js → chat-index-O56HDGFI.js} +2 -2
  3. package/dist/{chat-index-PS274XM7.js → chat-index-WSI7ULRQ.js} +1 -1
  4. package/dist/{chunk-OABKF7MR.js → chunk-34RV4ZKA.js} +1 -1
  5. package/dist/{chunk-WDUKU5HH.js → chunk-35OPVVEK.js} +1 -1
  6. package/dist/{chunk-OPAOQZDC.js → chunk-4CZZJQXJ.js} +2 -2
  7. package/dist/{chunk-5ZPO47YE.js → chunk-4RQPCOEE.js} +281 -41
  8. package/dist/{chunk-PNFOWBKL.js → chunk-6FEZAWH5.js} +3 -3
  9. package/dist/{chunk-RGL2KKCO.js → chunk-A2AVDSXJ.js} +1 -1
  10. package/dist/{chunk-SLSWPBK3.js → chunk-B5TYJO7V.js} +4 -1
  11. package/dist/{chunk-OJXYL2W2.js → chunk-DEHVRQWW.js} +1 -1
  12. package/dist/{chunk-GH37CLOE.js → chunk-HLXQXV23.js} +1 -1
  13. package/dist/{chunk-35AVZVXZ.js → chunk-JDMFVRFG.js} +3 -3
  14. package/dist/{chunk-ZILWADNL.js → chunk-JU7OI4BN.js} +1 -1
  15. package/dist/{chunk-VHY6NVMQ.js → chunk-LF3OUJHK.js} +4 -1
  16. package/dist/{chunk-TB4W4Y4T.js → chunk-NVUCDUXE.js} +1 -1
  17. package/dist/{chunk-GXAGBILB.js → chunk-QJUAQKH2.js} +1 -1
  18. package/dist/{chunk-5GTWZ4QF.js → chunk-QYQCCIBT.js} +1 -1
  19. package/dist/{ci-ESZOGOGZ.js → ci-UAQFOAMS.js} +4 -4
  20. package/dist/{ci-format-TIDA4V7T.js → ci-format-MNUHYUTO.js} +2 -2
  21. package/dist/{constants-VFBSBHFO.js → constants-PGDTYD2R.js} +1 -1
  22. package/dist/{doctor-cli-W3I33E4Q.js → doctor-cli-F6A3GOVY.js} +4 -4
  23. package/dist/electron-server.js +285 -55
  24. package/dist/{hub-7BLT2JUU.js → hub-EDTZKS7E.js} +2 -2
  25. package/dist/index.js +26 -22
  26. package/dist/{persist-GJ2JGZFR.js → persist-XUXIPYUU.js} +3 -3
  27. package/dist/{pr-7YET7ODT.js → pr-YC7DMVC5.js} +4 -4
  28. package/dist/{run-tests-HGBTHWNT.js → run-tests-AUEKHJCC.js} +2 -2
  29. package/dist/{run-tests-4PNOUHC3.js → run-tests-S36TE2W2.js} +1 -1
  30. package/dist/{server-APQHU4TR.js → server-347YIBHS.js} +6 -6
  31. package/dist/{server-AN7HZVNG.js → server-LQFHSFN4.js} +22 -16
  32. package/dist/{task-orchestrator-L7LBMQUQ.js → task-orchestrator-FIR5MCND.js} +6 -6
  33. package/dist/{usage-BTP5SSGC.js → usage-3MXPFFBU.js} +2 -2
  34. package/dist/web/client/app.js +3 -2
  35. package/dist/web/client/util.js +42 -0
  36. package/package.json +1 -1
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ConfigManager
4
- } from "./chunk-WDUKU5HH.js";
4
+ } from "./chunk-35OPVVEK.js";
5
5
  import "./chunk-5ULLIOVC.js";
6
- import "./chunk-GXAGBILB.js";
6
+ import "./chunk-QJUAQKH2.js";
7
7
  import {
8
8
  atomicWriteFileSync
9
9
  } from "./chunk-IW3Q7AE5.js";
@@ -6,9 +6,9 @@ import {
6
6
  getChatIndexStatus,
7
7
  loadChatIndex,
8
8
  searchChatMemory
9
- } from "./chunk-TB4W4Y4T.js";
9
+ } from "./chunk-NVUCDUXE.js";
10
10
  import "./chunk-KHYD3WXE.js";
11
- import "./chunk-SLSWPBK3.js";
11
+ import "./chunk-B5TYJO7V.js";
12
12
  import "./chunk-IW3Q7AE5.js";
13
13
  export {
14
14
  buildChatIndex,
@@ -5,7 +5,7 @@ import {
5
5
  getChatIndexStatus,
6
6
  loadChatIndex,
7
7
  searchChatMemory
8
- } from "./chunk-VHY6NVMQ.js";
8
+ } from "./chunk-LF3OUJHK.js";
9
9
  import "./chunk-JV5N65KN.js";
10
10
  import "./chunk-6BUTA5VW.js";
11
11
  export {
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  VERSION
4
- } from "./chunk-GXAGBILB.js";
4
+ } from "./chunk-QJUAQKH2.js";
5
5
 
6
6
  // src/cli/ci-format.ts
7
7
  var DEFAULT_CI_THRESHOLDS = {
@@ -8,7 +8,7 @@ import {
8
8
  CONFIG_FILE_NAME,
9
9
  HISTORY_DIR_NAME,
10
10
  PLUGINS_DIR_NAME
11
- } from "./chunk-GXAGBILB.js";
11
+ } from "./chunk-QJUAQKH2.js";
12
12
  import {
13
13
  atomicWriteFileSync
14
14
  } from "./chunk-IW3Q7AE5.js";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  truncateForPersist
4
- } from "./chunk-5ZPO47YE.js";
4
+ } from "./chunk-4RQPCOEE.js";
5
5
  import {
6
6
  APP_NAME,
7
7
  CONFIG_DIR_NAME,
@@ -11,7 +11,7 @@ import {
11
11
  MCP_PROTOCOL_VERSION,
12
12
  MCP_TOOL_PREFIX,
13
13
  VERSION
14
- } from "./chunk-GXAGBILB.js";
14
+ } from "./chunk-QJUAQKH2.js";
15
15
  import {
16
16
  atomicWriteFileSync
17
17
  } from "./chunk-IW3Q7AE5.js";
@@ -14,7 +14,7 @@ import {
14
14
  runHook,
15
15
  runLifecycleHooks,
16
16
  runTool
17
- } from "./chunk-RGL2KKCO.js";
17
+ } from "./chunk-A2AVDSXJ.js";
18
18
  import {
19
19
  getDangerLevel,
20
20
  isFileWriteTool,
@@ -33,7 +33,7 @@ import {
33
33
  } from "./chunk-4BKXL7SM.js";
34
34
  import {
35
35
  runTestsTool
36
- } from "./chunk-ZILWADNL.js";
36
+ } from "./chunk-JU7OI4BN.js";
37
37
  import {
38
38
  CONFIG_DIR_NAME,
39
39
  DEFAULT_MAX_TOOL_OUTPUT_CHARS_CAP,
@@ -43,23 +43,219 @@ import {
43
43
  SUBAGENT_ALLOWED_TOOLS,
44
44
  SUBAGENT_DEFAULT_MAX_ROUNDS,
45
45
  SUBAGENT_MAX_ROUNDS_LIMIT
46
- } from "./chunk-GXAGBILB.js";
46
+ } from "./chunk-QJUAQKH2.js";
47
47
  import {
48
48
  loadChatIndex,
49
49
  searchChatMemory
50
- } from "./chunk-TB4W4Y4T.js";
50
+ } from "./chunk-NVUCDUXE.js";
51
51
  import {
52
52
  DEFAULT_PATTERNS,
53
53
  redactString
54
- } from "./chunk-SLSWPBK3.js";
54
+ } from "./chunk-B5TYJO7V.js";
55
55
  import {
56
56
  atomicWriteFileSync
57
57
  } from "./chunk-IW3Q7AE5.js";
58
58
 
59
- // src/tools/builtin/bash.ts
59
+ // src/tools/shell-pool.ts
60
60
  import { spawn } from "child_process";
61
+ import { platform } from "os";
62
+ var IS_WINDOWS = platform() === "win32";
63
+ var SHELL = IS_WINDOWS ? "powershell.exe" : process.env["SHELL"] ?? "/bin/bash";
64
+ var pool = /* @__PURE__ */ new Map();
65
+ var IDLE_EVICT_MS = 5 * 6e4;
66
+ var evictionTimer = null;
67
+ function spawnShell(cwd) {
68
+ const shellArgs = IS_WINDOWS ? ["-NoProfile", "-NonInteractive", "-Command", "-"] : ["-s"];
69
+ const proc = spawn(SHELL, shellArgs, {
70
+ cwd,
71
+ windowsHide: true,
72
+ stdio: ["pipe", "pipe", "pipe"]
73
+ });
74
+ proc.on("exit", () => {
75
+ for (const [key, entry] of pool) {
76
+ if (entry.proc === proc) {
77
+ pool.delete(key);
78
+ break;
79
+ }
80
+ }
81
+ });
82
+ return proc;
83
+ }
84
+ function startEvictionTimer() {
85
+ if (evictionTimer) return;
86
+ evictionTimer = setInterval(() => {
87
+ const now = Date.now();
88
+ for (const [key, entry] of pool) {
89
+ if (now - entry.lastUsed > IDLE_EVICT_MS) {
90
+ try {
91
+ entry.proc.kill();
92
+ } catch {
93
+ }
94
+ pool.delete(key);
95
+ }
96
+ }
97
+ }, 6e4).unref();
98
+ }
99
+ async function execInPool(sessionKey, command, timeout, cwd, abortSignal) {
100
+ let entry = pool.get(sessionKey);
101
+ if (!entry || entry.proc.exitCode !== null || entry.cwd !== cwd) {
102
+ if (entry) {
103
+ try {
104
+ entry.proc.kill();
105
+ } catch {
106
+ }
107
+ }
108
+ const proc = spawnShell(cwd);
109
+ entry = { proc, cwd, lastUsed: Date.now() };
110
+ pool.set(sessionKey, entry);
111
+ }
112
+ entry.lastUsed = Date.now();
113
+ startEvictionTimer();
114
+ const id = `${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
115
+ const markerStart = `AICLI_MARKER_START:${id}`;
116
+ const markerEnd = `AICLI_MARKER_END:${id}`;
117
+ const wrappedCmd = IS_WINDOWS ? `echo "${markerStart}"; ${command}; echo "${markerEnd}:0"` : `echo "${markerStart}"; ${command}; echo "${markerEnd}:$?"`;
118
+ return new Promise((resolve8) => {
119
+ let stdout = "";
120
+ let stderr = "";
121
+ let timedOut = false;
122
+ let aborted = false;
123
+ let resolved = false;
124
+ const markerStartPattern = `AICLI_MARKER_START:${id}`;
125
+ const markerEndPattern = `AICLI_MARKER_END:${id}`;
126
+ const onData = (chunk) => {
127
+ stdout += chunk.toString("utf-8");
128
+ if (!resolved && stdout.includes(markerEndPattern)) {
129
+ const endLine = stdout.split("\n").find((l) => l.includes(markerEndPattern));
130
+ const exitCode = endLine ? parseEndMarker(endLine, markerEndPattern) : null;
131
+ stdout = stripMarkerOutput(stdout, markerStartPattern, markerEndPattern);
132
+ resolved = true;
133
+ cleanup();
134
+ resolve8({ stdout, stderr, exitCode, timedOut, aborted });
135
+ }
136
+ };
137
+ const onErr = (chunk) => {
138
+ stderr += chunk.toString("utf-8");
139
+ };
140
+ const proc = entry.proc;
141
+ proc.stdout?.on("data", onData);
142
+ proc.stderr?.on("data", onErr);
143
+ const timeoutTimer = setTimeout(() => {
144
+ if (resolved) return;
145
+ timedOut = true;
146
+ resolved = true;
147
+ stdout = stripMarkerOutput(stdout, markerStartPattern, markerEndPattern);
148
+ cleanup();
149
+ resolve8({ stdout, stderr, exitCode: null, timedOut, aborted });
150
+ try {
151
+ proc.kill();
152
+ } catch {
153
+ }
154
+ pool.delete(sessionKey);
155
+ }, timeout);
156
+ const onAbort = () => {
157
+ if (resolved) return;
158
+ aborted = true;
159
+ resolved = true;
160
+ cleanup();
161
+ stdout = stripMarkerOutput(stdout, markerStartPattern, markerEndPattern);
162
+ resolve8({ stdout, stderr, exitCode: null, timedOut, aborted });
163
+ try {
164
+ proc.kill();
165
+ } catch {
166
+ }
167
+ pool.delete(sessionKey);
168
+ };
169
+ if (abortSignal?.aborted) {
170
+ onAbort();
171
+ } else {
172
+ abortSignal?.addEventListener("abort", onAbort, { once: true });
173
+ }
174
+ const onError = (err) => {
175
+ if (resolved) return;
176
+ resolved = true;
177
+ stderr += `
178
+ [pool error] ${err.message}`;
179
+ cleanup();
180
+ resolve8({ stdout, stderr, exitCode: null, timedOut, aborted });
181
+ pool.delete(sessionKey);
182
+ };
183
+ proc.on("error", onError);
184
+ function cleanup() {
185
+ clearTimeout(timeoutTimer);
186
+ abortSignal?.removeEventListener("abort", onAbort);
187
+ proc.stdout?.removeListener("data", onData);
188
+ proc.stderr?.removeListener("data", onErr);
189
+ proc.removeListener("error", onError);
190
+ }
191
+ try {
192
+ proc.stdin?.write(wrappedCmd + "\n");
193
+ } catch (err) {
194
+ if (!resolved) {
195
+ cleanup();
196
+ pool.delete(sessionKey);
197
+ }
198
+ }
199
+ });
200
+ }
201
+ function parseEndMarker(line, markerEndPattern) {
202
+ const idx = line.lastIndexOf(":");
203
+ if (idx < 0) return null;
204
+ const code = line.slice(idx + 1).trim();
205
+ return /^\d+$/.test(code) ? Number(code) : null;
206
+ }
207
+ function stripMarkerOutput(stdout, startMarker, endMarker) {
208
+ const lines = stdout.split("\n");
209
+ let capturing = false;
210
+ const out = [];
211
+ for (const line of lines) {
212
+ if (line.includes(startMarker)) {
213
+ capturing = true;
214
+ continue;
215
+ }
216
+ if (line.includes(endMarker)) {
217
+ capturing = false;
218
+ continue;
219
+ }
220
+ if (capturing) {
221
+ out.push(line);
222
+ }
223
+ }
224
+ if (!stdout.includes(endMarker) && out.length === 0) {
225
+ const startIdx = stdout.indexOf(startMarker);
226
+ if (startIdx >= 0) {
227
+ return stdout.slice(startIdx + startMarker.length).trimStart();
228
+ }
229
+ }
230
+ return out.join("\n").trim();
231
+ }
232
+ function cleanupPoolSession(sessionKey) {
233
+ const entry = pool.get(sessionKey);
234
+ if (!entry) return;
235
+ try {
236
+ entry.proc.kill();
237
+ } catch {
238
+ }
239
+ pool.delete(sessionKey);
240
+ }
241
+ function cleanupAllSessions() {
242
+ for (const [key, entry] of pool) {
243
+ try {
244
+ entry.proc.kill();
245
+ } catch {
246
+ }
247
+ pool.delete(key);
248
+ }
249
+ if (evictionTimer) {
250
+ clearInterval(evictionTimer);
251
+ evictionTimer = null;
252
+ }
253
+ }
254
+
255
+ // src/tools/builtin/bash.ts
256
+ import { spawn as spawn2 } from "child_process";
61
257
  import { existsSync as existsSync2, readdirSync, statSync } from "fs";
62
- import { platform as platform2 } from "os";
258
+ import { platform as platform3 } from "os";
63
259
  import { resolve } from "path";
64
260
 
65
261
  // src/tools/undo-stack.ts
@@ -212,11 +408,11 @@ function runWithSessionKey(sessionKey, fn) {
212
408
  }
213
409
 
214
410
  // src/tools/win-shell.ts
215
- import { platform } from "os";
216
- var IS_WINDOWS = platform() === "win32";
411
+ import { platform as platform2 } from "os";
412
+ var IS_WINDOWS2 = platform2() === "win32";
217
413
  var WIN_UTF8_PREAMBLE = "[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; $OutputEncoding = [System.Text.Encoding]::UTF8; ";
218
414
  function shellInvocation(command) {
219
- if (IS_WINDOWS) {
415
+ if (IS_WINDOWS2) {
220
416
  return {
221
417
  shell: "powershell.exe",
222
418
  args: ["-NoProfile", "-NonInteractive", "-Command", WIN_UTF8_PREAMBLE + command]
@@ -235,8 +431,8 @@ function decodeWindowsBuffer(buf) {
235
431
  }
236
432
 
237
433
  // src/tools/builtin/bash.ts
238
- var IS_WINDOWS2 = platform2() === "win32";
239
- var SHELL = IS_WINDOWS2 ? "powershell.exe" : process.env["SHELL"] ?? "/bin/bash";
434
+ var IS_WINDOWS3 = platform3() === "win32";
435
+ var SHELL2 = IS_WINDOWS3 ? "powershell.exe" : process.env["SHELL"] ?? "/bin/bash";
240
436
  var cwdBySession = /* @__PURE__ */ new Map();
241
437
  function getCwd() {
242
438
  const key = getCurrentSessionKey();
@@ -250,17 +446,21 @@ function getCwd() {
250
446
  function setCwd(next) {
251
447
  cwdBySession.set(getCurrentSessionKey(), next);
252
448
  }
449
+ function clearBashSession(sessionKey) {
450
+ cwdBySession.delete(sessionKey);
451
+ cleanupPoolSession(sessionKey);
452
+ }
253
453
  var bashTool = {
254
454
  definition: {
255
455
  name: "bash",
256
- description: IS_WINDOWS2 ? `Execute commands in PowerShell. Supports mkdir, ls, cat, python, etc.
456
+ description: IS_WINDOWS3 ? `Execute commands in PowerShell. Supports mkdir, ls, cat, python, etc.
257
457
  Important rules:
258
458
  1. Each bash call runs in an independent subprocess; cd commands do not persist. To run in a specific directory, use the cwd parameter, or combine commands: e.g. "cd mydir; ls" or "mkdir mydir; cd mydir; New-Item file.txt".
259
459
  2. If a command fails (returns an error or non-zero exit code), stop immediately, report the error to the user, and do not retry the same or similar commands.
260
460
  3. Multiple commands can be combined with semicolons in a single call to reduce rounds.
261
461
  4. To delete directories, use Remove-Item -Recurse (the system will automatically optimize to a more reliable method).
262
462
  5. IMPORTANT: On Windows, "curl" is an alias for Invoke-WebRequest and does NOT support curl flags like -s, -X, -H. Use Invoke-RestMethod instead for HTTP requests. Example: Invoke-RestMethod -Uri "http://localhost:3000/api/health" -Method Get
263
- 6. NEVER run long-running / never-exiting commands through bash \u2014 they block until timeout. This includes starting an Android emulator (emulator -avd X), streaming logs (adb logcat), and dev servers (node server.js, npm run dev, metro / react-native start). Use the 'task_create' tool to run these in the background, then poll status with short bash calls (e.g. adb shell getprop sys.boot_completed).` : `Execute commands in ${SHELL}.
463
+ 6. NEVER run long-running / never-exiting commands through bash \u2014 they block until timeout. This includes starting an Android emulator (emulator -avd X), streaming logs (adb logcat), and dev servers (node server.js, npm run dev, metro / react-native start). Use the 'task_create' tool to run these in the background, then poll status with short bash calls (e.g. adb shell getprop sys.boot_completed).` : `Execute commands in ${SHELL2}.
264
464
  Important rules:
265
465
  1. Each bash call runs in an independent subprocess; cd commands do not persist. To run in a specific directory, use the cwd parameter, or combine commands: e.g. "cd mydir && ls" or "mkdir -p mydir && touch mydir/file.txt".
266
466
  2. If a command fails (returns an error or non-zero exit code), stop immediately, report the error to the user, and do not retry the same or similar commands.
@@ -269,7 +469,7 @@ Important rules:
269
469
  parameters: {
270
470
  command: {
271
471
  type: "string",
272
- description: IS_WINDOWS2 ? `PowerShell command to execute. Combine multiple commands with semicolons, e.g.: "mkdir mydir; Set-Content mydir/file.txt 'content'"` : `${SHELL} command to execute. Combine multiple commands with &&, e.g.: "mkdir -p mydir && echo 'content' > mydir/file.txt"`,
472
+ description: IS_WINDOWS3 ? `PowerShell command to execute. Combine multiple commands with semicolons, e.g.: "mkdir mydir; Set-Content mydir/file.txt 'content'"` : `${SHELL2} command to execute. Combine multiple commands with &&, e.g.: "mkdir -p mydir && echo 'content' > mydir/file.txt"`,
273
473
  required: true
274
474
  },
275
475
  cwd: {
@@ -321,7 +521,7 @@ Important rules:
321
521
  setCwd(resolved);
322
522
  }
323
523
  let actualCommand;
324
- if (IS_WINDOWS2) {
524
+ if (IS_WINDOWS3) {
325
525
  const fixedCommand = fixWindowsDeleteCommand(command);
326
526
  actualCommand = WIN_UTF8_PREAMBLE + fixedCommand;
327
527
  } else {
@@ -333,6 +533,44 @@ Important rules:
333
533
  for (const t of parsedTargets) {
334
534
  parsedTargetsBefore.set(t, existsSync2(t));
335
535
  }
536
+ let poolResult = null;
537
+ try {
538
+ poolResult = await execInPool(
539
+ getCurrentSessionKey(),
540
+ actualCommand,
541
+ timeout,
542
+ effectiveCwd,
543
+ currentAbortSignal()
544
+ );
545
+ } catch {
546
+ poolResult = null;
547
+ }
548
+ if (poolResult) {
549
+ const { stdout, stderr, exitCode, timedOut, aborted } = poolResult;
550
+ const status = exitCode;
551
+ if (aborted) {
552
+ pushBashUndoEntries(beforeSnapshot, parsedTargetsBefore, effectiveCwd);
553
+ throw new ToolError(
554
+ "bash",
555
+ `Command interrupted by user (Ctrl+C).
556
+
557
+ [The user pressed Ctrl+C while this command was running. Stop the current task, summarize what was done so far, and wait for the user's next instruction. Do NOT retry this command or continue with further tool calls unless explicitly asked.]`
558
+ );
559
+ }
560
+ if (status !== 0 || timedOut) {
561
+ throw {
562
+ status,
563
+ signal: null,
564
+ code: timedOut ? "ETIMEDOUT" : void 0,
565
+ stdout: Buffer.from(stdout, "utf-8"),
566
+ stderr: Buffer.from(stderr, "utf-8"),
567
+ message: timedOut ? "ETIMEDOUT" : `exit ${status}`
568
+ };
569
+ }
570
+ pushBashUndoEntries(beforeSnapshot, parsedTargetsBefore, effectiveCwd);
571
+ updateCwdFromCommand(command, effectiveCwd);
572
+ return stdout || "(command completed with no output)";
573
+ }
336
574
  try {
337
575
  const { stdout, stderr, status, signal, timedOut, aborted } = await runShell(
338
576
  actualCommand,
@@ -367,7 +605,7 @@ Important rules:
367
605
  }
368
606
  updateCwdFromCommand(command, effectiveCwd);
369
607
  pushBashUndoEntries(beforeSnapshot, parsedTargetsBefore, effectiveCwd);
370
- const result = Buffer.isBuffer(stdout) ? IS_WINDOWS2 ? decodeWindowsBuffer(stdout) : stdout.toString("utf-8") : String(stdout ?? "");
608
+ const result = Buffer.isBuffer(stdout) ? IS_WINDOWS3 ? decodeWindowsBuffer(stdout) : stdout.toString("utf-8") : String(stdout ?? "");
371
609
  return result || "(command completed with no output)";
372
610
  } catch (err) {
373
611
  pushBashUndoEntries(beforeSnapshot, parsedTargetsBefore, effectiveCwd);
@@ -392,8 +630,8 @@ How to recover (pick ONE \u2014 do NOT retry the same command):
392
630
  );
393
631
  }
394
632
  if ("status" in execErr && execErr.status !== void 0) {
395
- const stderr = IS_WINDOWS2 && Buffer.isBuffer(execErr.stderr) ? decodeWindowsBuffer(execErr.stderr).trim() : execErr.stderr?.toString().trim() ?? "";
396
- const stdout = IS_WINDOWS2 && Buffer.isBuffer(execErr.stdout) ? decodeWindowsBuffer(execErr.stdout).trim() : execErr.stdout?.toString().trim() ?? "";
633
+ const stderr = IS_WINDOWS3 && Buffer.isBuffer(execErr.stderr) ? decodeWindowsBuffer(execErr.stderr).trim() : execErr.stderr?.toString().trim() ?? "";
634
+ const stdout = IS_WINDOWS3 && Buffer.isBuffer(execErr.stdout) ? decodeWindowsBuffer(execErr.stdout).trim() : execErr.stdout?.toString().trim() ?? "";
397
635
  const combined = [stdout, stderr].filter(Boolean).join("\n");
398
636
  const hint = buildErrorHint(command, combined);
399
637
  throw new ToolError(
@@ -429,12 +667,12 @@ function fixWindowsDeleteCommand(command) {
429
667
  }
430
668
  function buildErrorHint(command, stderr) {
431
669
  const hints = [];
432
- if (IS_WINDOWS2 && /\|\|/.test(command) && /(\|\||is not a valid argument|不是此版本中的有效|ParserError|Unexpected token)/i.test(stderr)) {
670
+ if (IS_WINDOWS3 && /\|\|/.test(command) && /(\|\||is not a valid argument|不是此版本中的有效|ParserError|Unexpected token)/i.test(stderr)) {
433
671
  hints.push(
434
672
  `Hint: PowerShell parses '||' as a pipeline operator (PS 5.x) or logical-or (PS 7+), it CANNOT be passed inline as SQL string concatenation. Workaround: write the SQL to a local .sql file with write_file, then 'scp' it to the remote host and run 'psql -f /tmp/x.sql'. Do NOT try to escape || or wrap the command differently \u2014 it will not work.`
435
673
  );
436
674
  }
437
- if (IS_WINDOWS2 && /\bpython3\b/.test(command)) {
675
+ if (IS_WINDOWS3 && /\bpython3\b/.test(command)) {
438
676
  const trivialStderr = stderr.trim().length === 0 || /^[·\s]*exit 1\b/i.test(stderr.trim());
439
677
  if (/(not recognized|is not recognized|无法将.*识别)/i.test(stderr) || trivialStderr) {
440
678
  hints.push(
@@ -442,27 +680,27 @@ function buildErrorHint(command, stderr) {
442
680
  );
443
681
  }
444
682
  }
445
- if (IS_WINDOWS2 && /^\s*ssh\b/.test(command) && /\\"/.test(command) && /(syntax error|ERROR:|ParserError|unexpected|parser|意外的|语法|不是此版本|所在位置\s+行)/i.test(stderr)) {
683
+ if (IS_WINDOWS3 && /^\s*ssh\b/.test(command) && /\\"/.test(command) && /(syntax error|ERROR:|ParserError|unexpected|parser|意外的|语法|不是此版本|所在位置\s+行)/i.test(stderr)) {
446
684
  hints.push(
447
685
  `Hint: SSH + nested quotes ("...\\"...\\"") is fragile across PS \u2192 ssh \u2192 remote-shell \u2192 psql layers. Use the file-based flow: (1) write_file locally to a .sql file, (2) 'scp x.sql root@host:/tmp/', (3) 'ssh root@host "sudo -u postgres psql -d <db> -f /tmp/x.sql"'. Or for remote-only SQL: 'ssh host "cat > /tmp/x.sql << \\'SQLEOF\\'\\n...\\nSQLEOF"' then run psql -f. Avoid inline psql -c with embedded quotes.`
448
686
  );
449
687
  }
450
- if (IS_WINDOWS2 && /\bmkdir\b/.test(command) && /\s-p\b/.test(command) && /(已存在|already exists|ItemExistsUnauthorizedAccessError|exists)/i.test(stderr)) {
688
+ if (IS_WINDOWS3 && /\bmkdir\b/.test(command) && /\s-p\b/.test(command) && /(已存在|already exists|ItemExistsUnauthorizedAccessError|exists)/i.test(stderr)) {
451
689
  hints.push(
452
690
  `Hint: PowerShell's mkdir does not support the '-p' flag. When the directory already exists it exits with code 1. Use instead: New-Item -ItemType Directory -Force -Path <dir> ('-Force' silently succeeds even if the directory already exists, mimicking 'mkdir -p').`
453
691
  );
454
692
  }
455
- if (IS_WINDOWS2 && /Remove-Item/i.test(command) && /cannot find path|no such file|exit 1/i.test(stderr || command)) {
693
+ if (IS_WINDOWS3 && /Remove-Item/i.test(command) && /cannot find path|no such file|exit 1/i.test(stderr || command)) {
456
694
  hints.push(
457
695
  `Hint: On Windows, "Remove-Item ... -ErrorAction SilentlyContinue" still makes the powershell process exit 1 when no files match (even though the error is silenced). Use Test-Path as a guard: @('tmp_a.sql','tmp_b.sql','tmp_c.csv') | Where-Object { Test-Path $_ } | Remove-Item -Force \u2014 Test-Path returns a bool without raising, so missing paths are filtered out and exit code is 0.`
458
696
  );
459
697
  }
460
- if (IS_WINDOWS2 && /-(?:EA|ErrorAction)\s+(?:SilentlyContinue|Ignore)\b/i.test(command) && /\b(Get-ChildItem|Get-Item|Get-Content|gci|gi|gc|dir|ls)\b/i.test(command) && !/Remove-Item/i.test(command) && !/Test-Path/i.test(command) && /cannot find path|no such file|does not exist|exit 1|不存在|找不到/i.test(stderr || command)) {
698
+ if (IS_WINDOWS3 && /-(?:EA|ErrorAction)\s+(?:SilentlyContinue|Ignore)\b/i.test(command) && /\b(Get-ChildItem|Get-Item|Get-Content|gci|gi|gc|dir|ls)\b/i.test(command) && !/Remove-Item/i.test(command) && !/Test-Path/i.test(command) && /cannot find path|no such file|does not exist|exit 1|不存在|找不到/i.test(stderr || command)) {
461
699
  hints.push(
462
700
  `Hint: On Windows, "-ErrorAction SilentlyContinue" silences the error MESSAGE but does NOT change the exit code \u2014 the process still exits 1 when the path doesn't exist (because powershell.exe -NonInteractive flips the exit code on any non-terminating error, silenced or not). If you're just probing for existence, use Test-Path instead \u2014 it returns $true/$false without raising: \`if (Test-Path ./mydir) { Get-ChildItem ./mydir }\` or unconditionally use \`New-Item -ItemType Directory -Force -Path ./mydir\` which is idempotent. Don't retry the same -ErrorAction SilentlyContinue probe expecting a different result.`
463
701
  );
464
702
  }
465
- if (IS_WINDOWS2 && /\bWrite-(?:Content|File)\b/i.test(command) && /(not recognized|无法将.*识别|不是.*cmdlet|CommandNotFoundException)/i.test(stderr)) {
703
+ if (IS_WINDOWS3 && /\bWrite-(?:Content|File)\b/i.test(command) && /(not recognized|无法将.*识别|不是.*cmdlet|CommandNotFoundException)/i.test(stderr)) {
466
704
  hints.push(
467
705
  `Hint: 'Write-Content' / 'Write-File' are NOT PowerShell cmdlets (common LLM hallucination). To write a file, prefer the built-in 'write_file' tool \u2014 it handles encoding/escaping cleanly and supports diff preview. If you must stay in bash: use 'Set-Content -Path <file> -Value <string> -Encoding utf8' (overwrites) or 'Add-Content' (appends) or 'Out-File -FilePath <file> -Encoding utf8'.`
468
706
  );
@@ -574,8 +812,8 @@ function pushBashUndoEntries(beforeSnapshot, parsedTargetsBefore, cwd) {
574
812
  }
575
813
  function runShell(command, opts) {
576
814
  return new Promise((resolvePromise) => {
577
- const shellArgs = IS_WINDOWS2 ? ["-NoProfile", "-NonInteractive", "-Command", command] : ["-c", command];
578
- const child = spawn(SHELL, shellArgs, {
815
+ const shellArgs = IS_WINDOWS3 ? ["-NoProfile", "-NonInteractive", "-Command", command] : ["-c", command];
816
+ const child = spawn2(SHELL2, shellArgs, {
579
817
  cwd: opts.cwd,
580
818
  env: opts.env,
581
819
  windowsHide: true,
@@ -637,9 +875,9 @@ function runShell(command, opts) {
637
875
  }
638
876
  function killChild(child) {
639
877
  if (child.killed || child.exitCode !== null) return;
640
- if (IS_WINDOWS2 && child.pid) {
878
+ if (IS_WINDOWS3 && child.pid) {
641
879
  try {
642
- spawn("taskkill", ["/PID", String(child.pid), "/T", "/F"], {
880
+ spawn2("taskkill", ["/PID", String(child.pid), "/T", "/F"], {
643
881
  windowsHide: true,
644
882
  stdio: "ignore"
645
883
  });
@@ -3626,9 +3864,9 @@ function collectMatchingFiles(dirPath, rootPath, regex, results, maxResults) {
3626
3864
  }
3627
3865
 
3628
3866
  // src/tools/builtin/run-interactive.ts
3629
- import { spawn as spawn2 } from "child_process";
3630
- import { platform as platform3 } from "os";
3631
- var IS_WINDOWS3 = platform3() === "win32";
3867
+ import { spawn as spawn3 } from "child_process";
3868
+ import { platform as platform4 } from "os";
3869
+ var IS_WINDOWS4 = platform4() === "win32";
3632
3870
  var runInteractiveTool = {
3633
3871
  definition: {
3634
3872
  name: "run_interactive",
@@ -3690,7 +3928,7 @@ var runInteractiveTool = {
3690
3928
  };
3691
3929
  const prefixWarnings = [argsTypeWarning, stdinTypeWarning].filter(Boolean).join("");
3692
3930
  return new Promise((resolve8) => {
3693
- const child = spawn2(executable, cmdArgs.map(String), {
3931
+ const child = spawn3(executable, cmdArgs.map(String), {
3694
3932
  cwd: process.cwd(),
3695
3933
  env,
3696
3934
  stdio: ["pipe", "pipe", "pipe"]
@@ -3708,7 +3946,7 @@ var runInteractiveTool = {
3708
3946
  let lineIdx = 0;
3709
3947
  const writeNextLine = () => {
3710
3948
  if (lineIdx < stdinLines.length && !child.stdin.destroyed) {
3711
- const line = stdinLines[lineIdx++] + (IS_WINDOWS3 ? "\r\n" : "\n");
3949
+ const line = stdinLines[lineIdx++] + (IS_WINDOWS4 ? "\r\n" : "\n");
3712
3950
  const canContinue = child.stdin.write(line);
3713
3951
  if (canContinue) {
3714
3952
  setTimeout(writeNextLine, 150);
@@ -5549,9 +5787,9 @@ var spawnAgentTool = {
5549
5787
  };
5550
5788
 
5551
5789
  // src/tools/builtin/task-manager.ts
5552
- import { spawn as spawn3 } from "child_process";
5790
+ import { spawn as spawn4 } from "child_process";
5553
5791
  import { randomUUID as randomUUID2 } from "crypto";
5554
- import { platform as platform4 } from "os";
5792
+ import { platform as platform5 } from "os";
5555
5793
  var MAX_OUTPUT_CHARS = 1e4;
5556
5794
  var MAX_OUTPUT_BYTES = 64 * 1024;
5557
5795
  var MAX_TASKS = 20;
@@ -5565,7 +5803,7 @@ function capChunks(chunks) {
5565
5803
  }
5566
5804
  function decodeField(chunks) {
5567
5805
  const buf = Buffer.concat(chunks);
5568
- const text = IS_WINDOWS ? decodeWindowsBuffer(buf) : buf.toString("utf-8");
5806
+ const text = IS_WINDOWS2 ? decodeWindowsBuffer(buf) : buf.toString("utf-8");
5569
5807
  return text.length > MAX_OUTPUT_CHARS ? "... [truncated] ...\n" + text.slice(-MAX_OUTPUT_CHARS) : text;
5570
5808
  }
5571
5809
  function appendOutput(task, field, chunk) {
@@ -5585,7 +5823,7 @@ function createTask(command, description) {
5585
5823
  }
5586
5824
  const id = randomUUID2().slice(0, 8);
5587
5825
  const { shell, args } = shellInvocation(command);
5588
- const proc = spawn3(shell, args, {
5826
+ const proc = spawn4(shell, args, {
5589
5827
  stdio: ["ignore", "pipe", "pipe"],
5590
5828
  detached: false,
5591
5829
  env: { ...process.env, PYTHONUTF8: "1", PYTHONIOENCODING: "utf-8" }
@@ -5635,9 +5873,9 @@ function stopTask(id) {
5635
5873
  if (!task || task.status !== "running") return false;
5636
5874
  try {
5637
5875
  const proc = task.process;
5638
- if (platform4() === "win32" && proc.pid) {
5876
+ if (platform5() === "win32" && proc.pid) {
5639
5877
  try {
5640
- spawn3("taskkill", ["/PID", String(proc.pid), "/T", "/F"], {
5878
+ spawn4("taskkill", ["/PID", String(proc.pid), "/T", "/F"], {
5641
5879
  windowsHide: true,
5642
5880
  stdio: "ignore"
5643
5881
  });
@@ -6733,6 +6971,7 @@ var ToolRegistry = class {
6733
6971
  };
6734
6972
 
6735
6973
  export {
6974
+ cleanupAllSessions,
6736
6975
  initTheme,
6737
6976
  theme,
6738
6977
  undoStack,
@@ -6755,6 +6994,7 @@ export {
6755
6994
  requestInterrupt,
6756
6995
  resetInterrupt,
6757
6996
  runWithSessionKey,
6997
+ clearBashSession,
6758
6998
  rlInternal,
6759
6999
  groupCallsByPhase,
6760
7000
  runSafePhases,
@@ -11,10 +11,10 @@ import {
11
11
  listHooks,
12
12
  listInstalledPlugins,
13
13
  resetStats
14
- } from "./chunk-RGL2KKCO.js";
14
+ } from "./chunk-A2AVDSXJ.js";
15
15
  import {
16
16
  ConfigManager
17
- } from "./chunk-WDUKU5HH.js";
17
+ } from "./chunk-35OPVVEK.js";
18
18
  import {
19
19
  getGitRoot
20
20
  } from "./chunk-HOSJZMQS.js";
@@ -26,7 +26,7 @@ import {
26
26
  MCP_PROJECT_CONFIG_NAME,
27
27
  MEMORY_FILE_NAME,
28
28
  VERSION
29
- } from "./chunk-GXAGBILB.js";
29
+ } from "./chunk-QJUAQKH2.js";
30
30
 
31
31
  // src/diagnostics/doctor-report.ts
32
32
  import { existsSync as existsSync3, statSync as statSync2 } from "fs";
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  CONFIG_DIR_NAME,
4
4
  PLUGINS_DIR_NAME
5
- } from "./chunk-GXAGBILB.js";
5
+ } from "./chunk-QJUAQKH2.js";
6
6
  import {
7
7
  atomicWriteFileSync
8
8
  } from "./chunk-IW3Q7AE5.js";
@@ -38,16 +38,19 @@ var DEFAULT_PATTERNS = [
38
38
  // Bearer <token> in Authorization headers
39
39
  { kind: "bearer", regex: /\b(Authorization:\s*Bearer\s+)([a-zA-Z0-9_\-.=]{20,500})/g },
40
40
  // Private key PEM blocks — catch the header+footer together
41
- { kind: "private-key", regex: /-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g }
41
+ // P2-PERF-02: PEM bodies use strict Base64 + =\n narrowed char class avoids backtracking
42
+ { kind: "private-key", regex: /-----BEGIN [A-Z ]*PRIVATE KEY-----[ \t\n\r]*[A-Za-z0-9+/=\n\r-]*[ \t\n\r]*-----END [A-Z ]*PRIVATE KEY-----/g }
42
43
  ];
43
44
  var MAX_CUSTOM = 32;
44
45
  var MAX_PATTERN_LEN = 500;
45
46
  var SUSPICIOUS_REDOS = /\([^)]*[+*][^)]*\)\s*[+*{]/;
47
+ var MAX_REDACT_INPUT = 512e3;
46
48
  function render(placeholder, kind) {
47
49
  return placeholder.replace("{kind}", kind);
48
50
  }
49
51
  function redactString(input, options) {
50
52
  if (!options.enabled || !input) return { redacted: input, hits: [] };
53
+ if (input.length > MAX_REDACT_INPUT) return { redacted: input, hits: [] };
51
54
  const placeholder = options.placeholder ?? "[REDACTED:{kind}]";
52
55
  const customSrcs = (options.customRegexes ?? []).slice(0, MAX_CUSTOM);
53
56
  const patterns = [
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  SessionManager
4
- } from "./chunk-5GTWZ4QF.js";
4
+ } from "./chunk-QYQCCIBT.js";
5
5
 
6
6
  // src/hub/persist.ts
7
7
  import { join } from "path";