jinzd-ai-cli 0.4.212 → 0.4.213

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.
@@ -5,10 +5,10 @@ import {
5
5
  } from "./chunk-T2NL5ZIA.js";
6
6
  import {
7
7
  runTestsTool
8
- } from "./chunk-HX6N6QEY.js";
8
+ } from "./chunk-UDMU4FUF.js";
9
9
  import {
10
10
  runTool
11
- } from "./chunk-IFZX26K7.js";
11
+ } from "./chunk-7NTFMLJJ.js";
12
12
  import {
13
13
  getDangerLevel,
14
14
  isFileWriteTool,
@@ -26,7 +26,7 @@ import {
26
26
  SUBAGENT_ALLOWED_TOOLS,
27
27
  SUBAGENT_DEFAULT_MAX_ROUNDS,
28
28
  SUBAGENT_MAX_ROUNDS_LIMIT
29
- } from "./chunk-GBBVCZ4W.js";
29
+ } from "./chunk-IWLVH32D.js";
30
30
  import {
31
31
  fileCheckpoints
32
32
  } from "./chunk-4BKXL7SM.js";
@@ -1055,7 +1055,7 @@ import { dirname as dirname2 } from "path";
1055
1055
 
1056
1056
  // src/tools/executor.ts
1057
1057
  import chalk3 from "chalk";
1058
- import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
1058
+ import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
1059
1059
  import { tmpdir } from "os";
1060
1060
 
1061
1061
  // src/core/readline-internal.ts
@@ -1860,6 +1860,157 @@ var theme = new Proxy(DARK_THEME, {
1860
1860
  }
1861
1861
  });
1862
1862
 
1863
+ // src/tools/action-classifier.ts
1864
+ import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
1865
+ import { isAbsolute as isAbsolute2, join as join2, resolve as resolve4 } from "path";
1866
+ var RECENT_DENIED_LIMIT = 50;
1867
+ var recentlyDenied = [];
1868
+ function recordRecentlyDeniedAutoAction(call, classification) {
1869
+ recentlyDenied.unshift({
1870
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1871
+ tool: call.name,
1872
+ reason: classification.reason,
1873
+ ruleId: classification.ruleId,
1874
+ argsPreview: JSON.stringify(call.arguments).slice(0, 240)
1875
+ });
1876
+ recentlyDenied.splice(RECENT_DENIED_LIMIT);
1877
+ }
1878
+ function getRecentlyDeniedAutoActions() {
1879
+ return [...recentlyDenied];
1880
+ }
1881
+ function clearRecentlyDeniedAutoActions() {
1882
+ recentlyDenied.splice(0, recentlyDenied.length);
1883
+ }
1884
+ function normalizePath(value) {
1885
+ return value.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
1886
+ }
1887
+ function isInside(root, target) {
1888
+ const absoluteRoot = resolve4(root);
1889
+ const absoluteTarget = isAbsolute2(target) ? target : resolve4(root, target);
1890
+ const base = normalizePath(absoluteRoot);
1891
+ const t = normalizePath(absoluteTarget);
1892
+ return t === base || t.startsWith(base + "/");
1893
+ }
1894
+ function getPathArg(call) {
1895
+ const raw = call.arguments.path ?? call.arguments.filePath ?? call.arguments.outputPath ?? call.arguments.file;
1896
+ return typeof raw === "string" && raw.trim() ? raw.trim() : null;
1897
+ }
1898
+ function splitShellWords(command) {
1899
+ const out = [];
1900
+ const re = /"([^"]*)"|'([^']*)'|([^\s]+)/g;
1901
+ let m;
1902
+ while (m = re.exec(command)) out.push(m[1] ?? m[2] ?? m[3] ?? "");
1903
+ return out.filter(Boolean);
1904
+ }
1905
+ function readPackageManifest(root) {
1906
+ const p = join2(root, "package.json");
1907
+ if (!existsSync5(p)) return null;
1908
+ try {
1909
+ return JSON.parse(readFileSync4(p, "utf-8"));
1910
+ } catch {
1911
+ return null;
1912
+ }
1913
+ }
1914
+ function manifestHasDependency(root, name) {
1915
+ const manifest = readPackageManifest(root);
1916
+ if (!manifest) return false;
1917
+ for (const key of ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"]) {
1918
+ const deps = manifest[key];
1919
+ if (deps && typeof deps === "object" && Object.prototype.hasOwnProperty.call(deps, name)) return true;
1920
+ }
1921
+ return false;
1922
+ }
1923
+ function lockfileMentions(root, name) {
1924
+ for (const file of ["package-lock.json", "pnpm-lock.yaml", "yarn.lock"]) {
1925
+ const p = join2(root, file);
1926
+ if (!existsSync5(p)) continue;
1927
+ try {
1928
+ if (readFileSync4(p, "utf-8").includes(name)) return true;
1929
+ } catch {
1930
+ }
1931
+ }
1932
+ return false;
1933
+ }
1934
+ function getInstallPackages(command) {
1935
+ const words = splitShellWords(command);
1936
+ const cmd = words[0]?.toLowerCase();
1937
+ const sub = words[1]?.toLowerCase();
1938
+ if (!cmd || !sub) return [];
1939
+ const isInstall = cmd === "npm" && (sub === "install" || sub === "i" || sub === "add") || (cmd === "pnpm" || cmd === "yarn") && (sub === "add" || sub === "install");
1940
+ if (!isInstall) return [];
1941
+ return words.slice(2).filter((w) => !w.startsWith("-")).filter((w) => w !== "." && w !== "./").map((w) => w.replace(/^@([^/]+)\/([^@]+)(?:@.+)?$/, "@$1/$2").replace(/^([^@]+)@.+$/, "$1"));
1942
+ }
1943
+ function isPlainGitPush(command) {
1944
+ if (!/^\s*git\s+push\b/i.test(command)) return false;
1945
+ if (/--force|-f\b|\+[^\s]+/i.test(command)) return false;
1946
+ return !/[;&|`$<>\n\r]/.test(command);
1947
+ }
1948
+ function hasSecretExfiltrationShape(command) {
1949
+ return /(?:\.env|config\.json|id_rsa|id_ed25519|AWS_SECRET|AICLI_API_KEY|OPENAI_API_KEY|ANTHROPIC_API_KEY)/i.test(command) && /\b(curl|wget|scp|rsync|nc|netcat|Invoke-WebRequest|Invoke-RestMethod)\b/i.test(command);
1950
+ }
1951
+ var RuleBasedActionClassifier = class {
1952
+ classify(call, dangerLevel, ctx) {
1953
+ if (call.name === "bash") return this.classifyShell(call, dangerLevel, ctx);
1954
+ if (call.name === "web_fetch" || call.name === "web_search" || call.name === "google_search") {
1955
+ return { decision: "auto-approve", reason: "read-only HTTP tool", ruleId: "auto.readonly-http", confidence: "high" };
1956
+ }
1957
+ if (dangerLevel === "safe") {
1958
+ return { decision: "auto-approve", reason: "safe read-only tool", ruleId: "auto.safe-tool", confidence: "high" };
1959
+ }
1960
+ if (dangerLevel === "write") {
1961
+ const p = getPathArg(call);
1962
+ if (p && (isInside(ctx.workspaceRoot, p) || (ctx.tempDirs ?? []).some((root) => isInside(root, p)))) {
1963
+ return { decision: "auto-approve", reason: "explicit write inside workspace/temp roots", ruleId: "auto.workspace-write", confidence: "high" };
1964
+ }
1965
+ return { decision: "confirm", reason: "write action is not proven low-risk", ruleId: "auto.write-confirm", confidence: "medium" };
1966
+ }
1967
+ return { decision: "confirm", reason: "destructive actions require confirmation", ruleId: "auto.destructive-confirm", confidence: "high" };
1968
+ }
1969
+ classifyShell(call, dangerLevel, ctx) {
1970
+ const command = String(call.arguments.command ?? "");
1971
+ if (/\bcurl\b[^\n|;&]*\|\s*(?:sudo\s+)?(?:bash|sh|zsh)\b/i.test(command)) {
1972
+ return { decision: "deny", reason: "curl pipe shell execution is blocked in auto mode", ruleId: "deny.curl-pipe-shell", confidence: "high" };
1973
+ }
1974
+ if (/\bgit\s+push\b/i.test(command) && /--force|-f\b|\+[^\s]+/i.test(command)) {
1975
+ return { decision: "deny", reason: "force push is blocked in auto mode", ruleId: "deny.force-push", confidence: "high" };
1976
+ }
1977
+ if (hasSecretExfiltrationShape(command)) {
1978
+ return { decision: "deny", reason: "possible secret exfiltration is blocked in auto mode", ruleId: "deny.secret-exfiltration", confidence: "high" };
1979
+ }
1980
+ if (/\b(terraform|tofu)\s+destroy\b|\bkubectl\s+delete\b|\bcdk\s+destroy\b/i.test(command)) {
1981
+ return { decision: "confirm", reason: "infrastructure destroy requires confirmation", ruleId: "confirm.iac-destroy", confidence: "high" };
1982
+ }
1983
+ if (/\b(prisma|sequelize|knex|typeorm)\b[^\n]*\b(migrate|migration)\b|\bdb:migrate\b/i.test(command)) {
1984
+ return { decision: "confirm", reason: "database migration requires confirmation", ruleId: "confirm.database-migration", confidence: "high" };
1985
+ }
1986
+ if (/\b(vercel|netlify|flyctl|railway|wrangler|serverless|firebase)\b[^\n]*\bdeploy\b|\bdeploy\b[^\n]*\b(prod|production)\b/i.test(command)) {
1987
+ return { decision: "confirm", reason: "production deployment requires confirmation", ruleId: "confirm.production-deploy", confidence: "high" };
1988
+ }
1989
+ if (/\b(iam|policy|token|secret|api[-_]?key|credential|permission|role)\b/i.test(command)) {
1990
+ return { decision: "confirm", reason: "credential or permission change requires confirmation", ruleId: "confirm.credentials-permissions", confidence: "medium" };
1991
+ }
1992
+ if (/\b(npx|pnpm\s+dlx|yarn\s+dlx)\s+(claude|codex|aider|cursor-agent|gemini)\b/i.test(command)) {
1993
+ return { decision: "confirm", reason: "third-party agent loop requires confirmation", ruleId: "confirm.third-party-agent", confidence: "high" };
1994
+ }
1995
+ if (isPlainGitPush(command)) {
1996
+ return { decision: "auto-approve", reason: "plain git push without force", ruleId: "auto.git-push", confidence: "high" };
1997
+ }
1998
+ const pkgs = getInstallPackages(command);
1999
+ if (pkgs.length > 0) {
2000
+ const allDeclared = pkgs.every((p) => manifestHasDependency(ctx.workspaceRoot, p) || lockfileMentions(ctx.workspaceRoot, p));
2001
+ return allDeclared ? { decision: "auto-approve", reason: "installing dependencies already declared in manifest/lockfile", ruleId: "auto.declared-dependency-install", confidence: "medium" } : { decision: "confirm", reason: "dependency install includes undeclared packages", ruleId: "confirm.undeclared-dependency-install", confidence: "medium" };
2002
+ }
2003
+ if (/\b(curl|wget|Invoke-WebRequest|Invoke-RestMethod)\b/i.test(command) && !/[>|;&`$]/.test(command)) {
2004
+ return { decision: "auto-approve", reason: "read-only HTTP shell command", ruleId: "auto.readonly-http-shell", confidence: "medium" };
2005
+ }
2006
+ if (dangerLevel === "safe") {
2007
+ return { decision: "auto-approve", reason: "safe shell action", ruleId: "auto.safe-shell", confidence: "medium" };
2008
+ }
2009
+ return { decision: "confirm", reason: "shell action is not proven low-risk", ruleId: "confirm.shell-default", confidence: "medium" };
2010
+ }
2011
+ };
2012
+ var defaultActionClassifier = new RuleBasedActionClassifier();
2013
+
1863
2014
  // src/tools/executor.ts
1864
2015
  function buildErrorPreview(content) {
1865
2016
  const meaningful = content.split("\n").filter(
@@ -1899,6 +2050,8 @@ var ToolExecutor = class {
1899
2050
  * destructive 操作仍然必须逐次确认。
1900
2051
  */
1901
2052
  sessionAutoApprove = false;
2053
+ /** Session-level Auto Mode: rule-based low-risk auto approval. */
2054
+ sessionAutoMode = false;
1902
2055
  /**
1903
2056
  * Logical session key used to scope per-session state in stateful tools
1904
2057
  * (currently only `bash`'s persistent cwd). Web mode sets this per-tab so
@@ -2058,6 +2211,40 @@ var ToolExecutor = class {
2058
2211
  };
2059
2212
  }
2060
2213
  }
2214
+ if (this.sessionAutoMode && networkPermission?.action !== "confirm") {
2215
+ const classification = defaultActionClassifier.classify(call, dangerLevel, {
2216
+ workspaceRoot: this.workspaceRoot,
2217
+ tempDirs: [tmpdir()]
2218
+ });
2219
+ if (classification.decision === "deny") {
2220
+ recordRecentlyDeniedAutoAction(call, classification);
2221
+ return {
2222
+ callId: call.id,
2223
+ content: `[Auto denied] ${classification.reason}. Do not retry without asking.`,
2224
+ isError: true
2225
+ };
2226
+ }
2227
+ if (classification.decision === "auto-approve") {
2228
+ this.printToolCall(call);
2229
+ if (dangerLevel === "write") this.printDiffPreview(call);
2230
+ console.log(theme.warning(` \u26A1 Auto-approved (/auto: ${classification.ruleId})`));
2231
+ try {
2232
+ const rawContent = await runTool(tool, call.arguments, call.name);
2233
+ const content = truncateOutput(rawContent, call.name);
2234
+ const wasTruncated = content !== rawContent;
2235
+ this.printToolResult(call.name, rawContent, false, wasTruncated);
2236
+ runHook(this.hookConfig?.postToolExecution, { tool: call.name, status: "ok" });
2237
+ runLifecycleHooks(this.hookConfig, "PostToolUse", { tool: call.name, status: "ok" }, { configDir: this.hookConfigDir });
2238
+ return { callId: call.id, content, isError: false };
2239
+ } catch (err) {
2240
+ const message = err instanceof Error ? err.message : String(err);
2241
+ this.printToolResult(call.name, message, true, false);
2242
+ runHook(this.hookConfig?.postToolExecution, { tool: call.name, status: "error" });
2243
+ runLifecycleHooks(this.hookConfig, "PostToolUse", { tool: call.name, status: "error", error: message }, { configDir: this.hookConfigDir });
2244
+ return { callId: call.id, content: message, isError: true };
2245
+ }
2246
+ }
2247
+ }
2061
2248
  if (this.sessionAutoApprove && dangerLevel === "write") {
2062
2249
  this.printToolCall(call);
2063
2250
  if (dangerLevel === "write") this.printDiffPreview(call);
@@ -2225,7 +2412,7 @@ var ToolExecutor = class {
2225
2412
  rl.resume();
2226
2413
  process.stdout.write(prompt);
2227
2414
  this.confirming = true;
2228
- return new Promise((resolve6) => {
2415
+ return new Promise((resolve7) => {
2229
2416
  let completed = false;
2230
2417
  const cleanup = (result) => {
2231
2418
  if (completed) return;
@@ -2235,7 +2422,7 @@ var ToolExecutor = class {
2235
2422
  rl.pause();
2236
2423
  rlAny.output = savedOutput;
2237
2424
  this.confirming = false;
2238
- resolve6(result);
2425
+ resolve7(result);
2239
2426
  };
2240
2427
  const onLine = (line) => {
2241
2428
  const trimmed = line.trim();
@@ -2302,10 +2489,10 @@ var ToolExecutor = class {
2302
2489
  const filePath = String(call.arguments["path"] ?? "");
2303
2490
  const newContent = String(call.arguments["content"] ?? "");
2304
2491
  if (!filePath) return;
2305
- if (existsSync5(filePath)) {
2492
+ if (existsSync6(filePath)) {
2306
2493
  let oldContent;
2307
2494
  try {
2308
- oldContent = readFileSync4(filePath, "utf-8");
2495
+ oldContent = readFileSync5(filePath, "utf-8");
2309
2496
  } catch {
2310
2497
  return;
2311
2498
  }
@@ -2328,7 +2515,7 @@ var ToolExecutor = class {
2328
2515
  }
2329
2516
  } else if (call.name === "edit_file") {
2330
2517
  const filePath = String(call.arguments["path"] ?? "");
2331
- if (!filePath || !existsSync5(filePath)) return;
2518
+ if (!filePath || !existsSync6(filePath)) return;
2332
2519
  const oldStr = call.arguments["old_str"];
2333
2520
  const newStr = call.arguments["new_str"];
2334
2521
  if (oldStr !== void 0) {
@@ -2355,7 +2542,7 @@ var ToolExecutor = class {
2355
2542
  const to = Number(call.arguments["delete_to_line"] ?? from);
2356
2543
  let fileContent;
2357
2544
  try {
2358
- fileContent = readFileSync4(filePath, "utf-8");
2545
+ fileContent = readFileSync5(filePath, "utf-8");
2359
2546
  } catch {
2360
2547
  return;
2361
2548
  }
@@ -2409,7 +2596,7 @@ var ToolExecutor = class {
2409
2596
  rl.resume();
2410
2597
  process.stdout.write(color("Proceed? [y/N] (type y + Enter to confirm) "));
2411
2598
  this.confirming = true;
2412
- return new Promise((resolve6) => {
2599
+ return new Promise((resolve7) => {
2413
2600
  let completed = false;
2414
2601
  const cleanup = (answer) => {
2415
2602
  if (completed) return;
@@ -2419,7 +2606,7 @@ var ToolExecutor = class {
2419
2606
  rl.pause();
2420
2607
  rlAny.output = savedOutput;
2421
2608
  this.confirming = false;
2422
- resolve6(answer === "y");
2609
+ resolve7(answer === "y");
2423
2610
  };
2424
2611
  const onLine = (line) => {
2425
2612
  const trimmed = line.trim();
@@ -2447,11 +2634,11 @@ var ToolExecutor = class {
2447
2634
  };
2448
2635
 
2449
2636
  // src/tools/sensitive-paths.ts
2450
- import { resolve as resolve4, sep as sep2, basename as basename2 } from "path";
2637
+ import { resolve as resolve5, sep as sep2, basename as basename2 } from "path";
2451
2638
  import { homedir as homedir2 } from "os";
2452
2639
  var home = homedir2();
2453
2640
  function norm(p) {
2454
- const abs = resolve4(p);
2641
+ const abs = resolve5(p);
2455
2642
  return process.platform === "win32" ? abs.toLowerCase() : abs;
2456
2643
  }
2457
2644
  function homeRel(p) {
@@ -2610,7 +2797,7 @@ Do NOT split a long document into many write_file(append=true) calls. That patte
2610
2797
  };
2611
2798
 
2612
2799
  // src/tools/builtin/edit-file.ts
2613
- import { readFileSync as readFileSync5, existsSync as existsSync6 } from "fs";
2800
+ import { readFileSync as readFileSync6, existsSync as existsSync7 } from "fs";
2614
2801
 
2615
2802
  // src/tools/builtin/patch-apply.ts
2616
2803
  function parseUnifiedDiff(patch) {
@@ -2971,7 +3158,7 @@ Note: Path can be absolute or relative to cwd.`,
2971
3158
  const filePath = String(args["path"] ?? "");
2972
3159
  const encoding = args["encoding"] ?? "utf-8";
2973
3160
  if (!filePath) throw new ToolError("edit_file", "path is required");
2974
- if (!existsSync6(filePath)) throw new ToolError("edit_file", `File not found: ${filePath}`);
3161
+ if (!existsSync7(filePath)) throw new ToolError("edit_file", `File not found: ${filePath}`);
2975
3162
  const verdict = classifyWritePath(filePath);
2976
3163
  if (verdict.sensitive && subAgentGuard.active) {
2977
3164
  throw new ToolError(
@@ -2979,7 +3166,7 @@ Note: Path can be absolute or relative to cwd.`,
2979
3166
  `Refused: sub-agents cannot edit sensitive paths \u2014 ${verdict.reason}. If this is genuinely needed, ask the parent agent to perform the edit so the user can approve it.`
2980
3167
  );
2981
3168
  }
2982
- const original = readFileSync5(filePath, encoding);
3169
+ const original = readFileSync6(filePath, encoding);
2983
3170
  if (args["patch"] !== void 0) {
2984
3171
  const patchText = String(args["patch"] ?? "");
2985
3172
  const stopOnError = args["stop_on_error"] !== false;
@@ -3137,8 +3324,8 @@ function truncatePreview(str, maxLen = 80) {
3137
3324
  }
3138
3325
 
3139
3326
  // src/tools/builtin/list-dir.ts
3140
- import { readdirSync as readdirSync3, statSync as statSync3, existsSync as existsSync7 } from "fs";
3141
- import { join as join2, basename as basename3 } from "path";
3327
+ import { readdirSync as readdirSync3, statSync as statSync3, existsSync as existsSync8 } from "fs";
3328
+ import { join as join3, basename as basename3 } from "path";
3142
3329
  var listDirTool = {
3143
3330
  definition: {
3144
3331
  name: "list_dir",
@@ -3160,7 +3347,7 @@ var listDirTool = {
3160
3347
  async execute(args) {
3161
3348
  const dirPath = String(args["path"] ?? process.cwd());
3162
3349
  const recursive = Boolean(args["recursive"] ?? false);
3163
- if (!existsSync7(dirPath)) {
3350
+ if (!existsSync8(dirPath)) {
3164
3351
  const targetName = basename3(dirPath).toLowerCase();
3165
3352
  const cwd = process.cwd();
3166
3353
  const suggestions = [];
@@ -3218,11 +3405,11 @@ function listRecursive(basePath, indent, recursive, lines) {
3218
3405
  if (entry.isDirectory()) {
3219
3406
  lines.push(`${indent}\u{1F4C1} ${entry.name}/`);
3220
3407
  if (recursive) {
3221
- listRecursive(join2(basePath, entry.name), indent + " ", true, lines);
3408
+ listRecursive(join3(basePath, entry.name), indent + " ", true, lines);
3222
3409
  }
3223
3410
  } else {
3224
3411
  try {
3225
- const stat = statSync3(join2(basePath, entry.name));
3412
+ const stat = statSync3(join3(basePath, entry.name));
3226
3413
  const size = formatSize(stat.size);
3227
3414
  lines.push(`${indent}\u{1F4C4} ${entry.name} (${size})`);
3228
3415
  } catch {
@@ -3238,9 +3425,9 @@ function formatSize(bytes) {
3238
3425
  }
3239
3426
 
3240
3427
  // src/tools/builtin/grep-files.ts
3241
- import { readdirSync as readdirSync4, readFileSync as readFileSync6, statSync as statSync4, existsSync as existsSync8 } from "fs";
3428
+ import { readdirSync as readdirSync4, readFileSync as readFileSync7, statSync as statSync4, existsSync as existsSync9 } from "fs";
3242
3429
  import { readFile } from "fs/promises";
3243
- import { join as join3, relative } from "path";
3430
+ import { join as join4, relative } from "path";
3244
3431
  var grepFilesTool = {
3245
3432
  definition: {
3246
3433
  name: "grep_files",
@@ -3291,7 +3478,7 @@ Supports regex. Automatically skips node_modules, dist, .git directories.`,
3291
3478
  const contextLines = Math.max(0, Number(args["context_lines"] ?? 0));
3292
3479
  const maxResults = Math.max(1, Number(args["max_results"] ?? 50));
3293
3480
  if (!pattern) throw new ToolError("grep_files", "pattern is required");
3294
- if (!existsSync8(rootPath)) throw new ToolError("grep_files", `Path not found: ${rootPath}`);
3481
+ if (!existsSync9(rootPath)) throw new ToolError("grep_files", `Path not found: ${rootPath}`);
3295
3482
  const MAX_PATTERN_LENGTH = 1e3;
3296
3483
  if (pattern.length > MAX_PATTERN_LENGTH) {
3297
3484
  throw new ToolError("grep_files", `Pattern too long (${pattern.length} chars, max ${MAX_PATTERN_LENGTH}). Use a shorter pattern.`);
@@ -3385,11 +3572,11 @@ function collectFilePaths(rootPath, filePattern, maxFiles) {
3385
3572
  if (paths.length >= maxFiles) return;
3386
3573
  if (entry.isDirectory()) {
3387
3574
  if (SKIP_DIRS.has(entry.name) || entry.name.startsWith(".")) continue;
3388
- walk(join3(dirPath, entry.name));
3575
+ walk(join4(dirPath, entry.name));
3389
3576
  } else if (entry.isFile()) {
3390
3577
  if (isBinary(entry.name)) continue;
3391
3578
  if (filePattern && !matchesFilePattern(entry.name, filePattern)) continue;
3392
- const fullPath = join3(dirPath, entry.name);
3579
+ const fullPath = join4(dirPath, entry.name);
3393
3580
  try {
3394
3581
  if (statSync4(fullPath).size > 1e6) continue;
3395
3582
  } catch {
@@ -3444,7 +3631,7 @@ function searchInFile(fullPath, displayPath, regex, contextLines, maxResults, re
3444
3631
  }
3445
3632
  let content;
3446
3633
  try {
3447
- content = readFileSync6(fullPath, "utf-8");
3634
+ content = readFileSync7(fullPath, "utf-8");
3448
3635
  } catch {
3449
3636
  return;
3450
3637
  }
@@ -3475,8 +3662,8 @@ function searchInFile(fullPath, displayPath, regex, contextLines, maxResults, re
3475
3662
  }
3476
3663
 
3477
3664
  // src/tools/builtin/glob-files.ts
3478
- import { readdirSync as readdirSync5, statSync as statSync5, existsSync as existsSync9 } from "fs";
3479
- import { join as join4, relative as relative2, basename as basename4 } from "path";
3665
+ import { readdirSync as readdirSync5, statSync as statSync5, existsSync as existsSync10 } from "fs";
3666
+ import { join as join5, relative as relative2, basename as basename4 } from "path";
3480
3667
  var globFilesTool = {
3481
3668
  definition: {
3482
3669
  name: "glob_files",
@@ -3509,7 +3696,7 @@ Results sorted by most recent modification time. Automatically skips node_module
3509
3696
  const rootPath = String(args["path"] ?? process.cwd());
3510
3697
  const maxResults = Math.max(1, Number(args["max_results"] ?? 100));
3511
3698
  if (!pattern) throw new ToolError("glob_files", "pattern is required");
3512
- if (!existsSync9(rootPath)) throw new ToolError("glob_files", `Path not found: ${rootPath}`);
3699
+ if (!existsSync10(rootPath)) throw new ToolError("glob_files", `Path not found: ${rootPath}`);
3513
3700
  const regex = globToRegex(pattern);
3514
3701
  const matches = [];
3515
3702
  collectMatchingFiles(rootPath, rootPath, regex, matches, maxResults);
@@ -3586,7 +3773,7 @@ function collectMatchingFiles(dirPath, rootPath, regex, results, maxResults) {
3586
3773
  }
3587
3774
  for (const entry of entries) {
3588
3775
  if (results.length >= maxResults) break;
3589
- const fullPath = join4(dirPath, entry.name);
3776
+ const fullPath = join5(dirPath, entry.name);
3590
3777
  if (entry.isDirectory()) {
3591
3778
  if (SKIP_DIRS2.has(entry.name) || entry.name.startsWith(".")) continue;
3592
3779
  collectMatchingFiles(fullPath, rootPath, regex, results, maxResults);
@@ -3668,7 +3855,7 @@ var runInteractiveTool = {
3668
3855
  PYTHONDONTWRITEBYTECODE: "1"
3669
3856
  };
3670
3857
  const prefixWarnings = [argsTypeWarning, stdinTypeWarning].filter(Boolean).join("");
3671
- return new Promise((resolve6) => {
3858
+ return new Promise((resolve7) => {
3672
3859
  const child = spawn2(executable, cmdArgs.map(String), {
3673
3860
  cwd: process.cwd(),
3674
3861
  env,
@@ -3701,22 +3888,22 @@ var runInteractiveTool = {
3701
3888
  setTimeout(writeNextLine, 400);
3702
3889
  const timer = setTimeout(() => {
3703
3890
  child.kill();
3704
- resolve6(`${prefixWarnings}[Timeout after ${timeout}ms]
3891
+ resolve7(`${prefixWarnings}[Timeout after ${timeout}ms]
3705
3892
  ${buildOutput(stdout, stderr)}`);
3706
3893
  }, timeout);
3707
3894
  child.on("close", (code) => {
3708
3895
  clearTimeout(timer);
3709
3896
  const output = buildOutput(stdout, stderr);
3710
3897
  if (code !== 0 && code !== null) {
3711
- resolve6(`${prefixWarnings}Exit code ${code}:
3898
+ resolve7(`${prefixWarnings}Exit code ${code}:
3712
3899
  ${output}`);
3713
3900
  } else {
3714
- resolve6(`${prefixWarnings}${output || "(no output)"}`);
3901
+ resolve7(`${prefixWarnings}${output || "(no output)"}`);
3715
3902
  }
3716
3903
  });
3717
3904
  child.on("error", (err) => {
3718
3905
  clearTimeout(timer);
3719
- resolve6(
3906
+ resolve7(
3720
3907
  `${prefixWarnings}Failed to start process "${executable}": ${err.message}
3721
3908
  Hint: On Windows, use the full path to the executable, e.g.:
3722
3909
  C:\\Users\\Jinzd\\anaconda3\\envs\\python312\\python.exe`
@@ -4384,11 +4571,11 @@ Any of these triggers means use save_last_response, NOT write_file:
4384
4571
  };
4385
4572
 
4386
4573
  // src/tools/builtin/save-memory.ts
4387
- import { existsSync as existsSync10, readFileSync as readFileSync7, statSync as statSync6, mkdirSync as mkdirSync4 } from "fs";
4388
- import { join as join5 } from "path";
4574
+ import { existsSync as existsSync11, readFileSync as readFileSync8, statSync as statSync6, mkdirSync as mkdirSync4 } from "fs";
4575
+ import { join as join6 } from "path";
4389
4576
  import { homedir as homedir3 } from "os";
4390
4577
  function getMemoryFilePath() {
4391
- return join5(homedir3(), CONFIG_DIR_NAME, MEMORY_FILE_NAME);
4578
+ return join6(homedir3(), CONFIG_DIR_NAME, MEMORY_FILE_NAME);
4392
4579
  }
4393
4580
  function formatTimestamp() {
4394
4581
  const now = /* @__PURE__ */ new Date();
@@ -4412,8 +4599,8 @@ var saveMemoryTool = {
4412
4599
  const content = String(args["content"] ?? "").trim();
4413
4600
  if (!content) throw new ToolError("save_memory", "content is required");
4414
4601
  const memoryPath = getMemoryFilePath();
4415
- const configDir = join5(homedir3(), CONFIG_DIR_NAME);
4416
- if (!existsSync10(configDir)) {
4602
+ const configDir = join6(homedir3(), CONFIG_DIR_NAME);
4603
+ if (!existsSync11(configDir)) {
4417
4604
  mkdirSync4(configDir, { recursive: true });
4418
4605
  }
4419
4606
  const timestamp = formatTimestamp();
@@ -4421,7 +4608,7 @@ var saveMemoryTool = {
4421
4608
  ## ${timestamp}
4422
4609
  ${content}
4423
4610
  `;
4424
- const previous = existsSync10(memoryPath) ? readFileSync7(memoryPath, "utf-8") : "";
4611
+ const previous = existsSync11(memoryPath) ? readFileSync8(memoryPath, "utf-8") : "";
4425
4612
  atomicWriteFileSync(memoryPath, previous + entry);
4426
4613
  const byteSize = statSync6(memoryPath).size;
4427
4614
  return `Memory saved successfully. File size: ${byteSize} bytes in ${MEMORY_FILE_NAME}`;
@@ -4468,7 +4655,7 @@ function promptUser(rl, question) {
4468
4655
  console.log();
4469
4656
  console.log(chalk4.cyan("\u2753 ") + chalk4.bold(question));
4470
4657
  process.stdout.write(chalk4.cyan("> "));
4471
- return new Promise((resolve6) => {
4658
+ return new Promise((resolve7) => {
4472
4659
  let completed = false;
4473
4660
  const cleanup = (answer) => {
4474
4661
  if (completed) return;
@@ -4478,7 +4665,7 @@ function promptUser(rl, question) {
4478
4665
  rl.pause();
4479
4666
  rlAny.output = savedOutput;
4480
4667
  askUserContext.prompting = false;
4481
- resolve6(answer);
4668
+ resolve7(answer);
4482
4669
  };
4483
4670
  const onLine = (line) => {
4484
4671
  cleanup(line);
@@ -5233,14 +5420,14 @@ var taskStopTool = {
5233
5420
 
5234
5421
  // src/tools/builtin/git-tools.ts
5235
5422
  import { execFileSync as execFileSync2 } from "child_process";
5236
- import { existsSync as existsSync11 } from "fs";
5237
- import { join as join6 } from "path";
5423
+ import { existsSync as existsSync12 } from "fs";
5424
+ import { join as join7 } from "path";
5238
5425
  function assertGitRepo(cwd) {
5239
5426
  let dir = cwd;
5240
5427
  const root = dir.split(/[\\/]/)[0] + (dir.includes("\\") ? "\\" : "/");
5241
5428
  while (dir && dir !== root) {
5242
- if (existsSync11(join6(dir, ".git"))) return;
5243
- const parent = join6(dir, "..");
5429
+ if (existsSync12(join7(dir, ".git"))) return;
5430
+ const parent = join7(dir, "..");
5244
5431
  if (parent === dir) break;
5245
5432
  dir = parent;
5246
5433
  }
@@ -5505,9 +5692,9 @@ ${commitOutput.trim()}`;
5505
5692
  };
5506
5693
 
5507
5694
  // src/tools/builtin/notebook-edit.ts
5508
- import { readFileSync as readFileSync8, existsSync as existsSync12 } from "fs";
5695
+ import { readFileSync as readFileSync9, existsSync as existsSync13 } from "fs";
5509
5696
  import { writeFile } from "fs/promises";
5510
- import { resolve as resolve5, extname as extname2 } from "path";
5697
+ import { resolve as resolve6, extname as extname2 } from "path";
5511
5698
  var notebookEditTool = {
5512
5699
  definition: {
5513
5700
  name: "notebook_edit",
@@ -5556,14 +5743,14 @@ var notebookEditTool = {
5556
5743
  if (!Number.isInteger(cellIndexRaw) || cellIndexRaw < 1) {
5557
5744
  throw new ToolError("notebook_edit", "cell_index must be a positive integer (1-based)");
5558
5745
  }
5559
- const absPath = resolve5(filePath);
5746
+ const absPath = resolve6(filePath);
5560
5747
  if (extname2(absPath).toLowerCase() !== ".ipynb") {
5561
5748
  throw new ToolError("notebook_edit", "path must point to a .ipynb file");
5562
5749
  }
5563
- if (!existsSync12(absPath)) {
5750
+ if (!existsSync13(absPath)) {
5564
5751
  throw new ToolError("notebook_edit", `Notebook not found: ${filePath}`);
5565
5752
  }
5566
- const raw = readFileSync8(absPath, "utf-8");
5753
+ const raw = readFileSync9(absPath, "utf-8");
5567
5754
  const nb = parseNotebook(raw);
5568
5755
  const cellIdx0 = cellIndexRaw - 1;
5569
5756
  undoStack.push(absPath, `notebook_edit (${action}): ${filePath}`);
@@ -5980,8 +6167,8 @@ function estimateToolDefinitionTokens(def) {
5980
6167
 
5981
6168
  // src/tools/registry.ts
5982
6169
  import { pathToFileURL } from "url";
5983
- import { existsSync as existsSync13, mkdirSync as mkdirSync5, readdirSync as readdirSync6 } from "fs";
5984
- import { join as join7 } from "path";
6170
+ import { existsSync as existsSync14, mkdirSync as mkdirSync5, readdirSync as readdirSync6 } from "fs";
6171
+ import { join as join8 } from "path";
5985
6172
  var ToolRegistry = class {
5986
6173
  tools = /* @__PURE__ */ new Map();
5987
6174
  pluginToolNames = /* @__PURE__ */ new Set();
@@ -6142,7 +6329,7 @@ var ToolRegistry = class {
6142
6329
  * Returns the number of successfully loaded plugins.
6143
6330
  */
6144
6331
  async loadPlugins(pluginsDir, allowPlugins = false) {
6145
- if (!existsSync13(pluginsDir)) {
6332
+ if (!existsSync14(pluginsDir)) {
6146
6333
  try {
6147
6334
  mkdirSync5(pluginsDir, { recursive: true });
6148
6335
  } catch {
@@ -6168,12 +6355,12 @@ var ToolRegistry = class {
6168
6355
  process.stderr.write(
6169
6356
  `
6170
6357
  [plugins] \u26A0 Loading ${files.length} plugin(s) with FULL system privileges:
6171
- ` + files.map((f) => ` + ${join7(pluginsDir, f)}`).join("\n") + "\n\n"
6358
+ ` + files.map((f) => ` + ${join8(pluginsDir, f)}`).join("\n") + "\n\n"
6172
6359
  );
6173
6360
  let loaded = 0;
6174
6361
  for (const file of files) {
6175
6362
  try {
6176
- const fileUrl = pathToFileURL(join7(pluginsDir, file)).href;
6363
+ const fileUrl = pathToFileURL(join8(pluginsDir, file)).href;
6177
6364
  const mod = await import(fileUrl);
6178
6365
  const tool = mod.tool ?? mod.default?.tool ?? mod.default;
6179
6366
  if (!tool || typeof tool.execute !== "function" || !tool.definition?.name) {
@@ -6211,6 +6398,10 @@ export {
6211
6398
  getPendingHookTrust,
6212
6399
  runLifecycleHooks,
6213
6400
  runHook,
6401
+ recordRecentlyDeniedAutoAction,
6402
+ getRecentlyDeniedAutoActions,
6403
+ clearRecentlyDeniedAutoActions,
6404
+ defaultActionClassifier,
6214
6405
  isInterrupted,
6215
6406
  requestInterrupt,
6216
6407
  resetInterrupt,
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  TEST_TIMEOUT
4
- } from "./chunk-GBBVCZ4W.js";
4
+ } from "./chunk-IWLVH32D.js";
5
5
 
6
6
  // src/tools/builtin/run-tests.ts
7
7
  import { execSync, spawnSync } from "child_process";
@@ -10,11 +10,11 @@ import {
10
10
  import "./chunk-XPBEJB27.js";
11
11
  import {
12
12
  ConfigManager
13
- } from "./chunk-NYCBOVNF.js";
13
+ } from "./chunk-MRGWPCFQ.js";
14
14
  import "./chunk-TZQHYZKT.js";
15
15
  import {
16
16
  VERSION
17
- } from "./chunk-GBBVCZ4W.js";
17
+ } from "./chunk-IWLVH32D.js";
18
18
  import "./chunk-IW3Q7AE5.js";
19
19
 
20
20
  // src/cli/ci.ts
@@ -37,7 +37,7 @@ import {
37
37
  TEST_TIMEOUT,
38
38
  VERSION,
39
39
  buildUserIdentityPrompt
40
- } from "./chunk-GBBVCZ4W.js";
40
+ } from "./chunk-IWLVH32D.js";
41
41
  export {
42
42
  AGENTIC_BEHAVIOR_GUIDELINE,
43
43
  APP_NAME,