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.
- package/README.md +7 -0
- package/README.zh-CN.md +7 -0
- package/dist/{batch-WIKFEOAZ.js → batch-PKY63TME.js} +2 -2
- package/dist/{chunk-IFZX26K7.js → chunk-7NTFMLJJ.js} +1 -1
- package/dist/{chunk-HW2ALWQJ.js → chunk-FGUKBIYO.js} +2 -2
- package/dist/{chunk-LU6FBJQ5.js → chunk-G3PW3AOB.js} +1 -1
- package/dist/{chunk-GBBVCZ4W.js → chunk-IWLVH32D.js} +1 -1
- package/dist/{chunk-KE4B3NOQ.js → chunk-J35V3IAH.js} +1 -1
- package/dist/{chunk-NYCBOVNF.js → chunk-MRGWPCFQ.js} +1 -1
- package/dist/{chunk-N5LB3PPL.js → chunk-THNECAAY.js} +250 -59
- package/dist/{chunk-HX6N6QEY.js → chunk-UDMU4FUF.js} +1 -1
- package/dist/{ci-X3LNUFZV.js → ci-HBZ6CIW5.js} +2 -2
- package/dist/{constants-KDWG4KR4.js → constants-CAUBTSFW.js} +1 -1
- package/dist/{doctor-cli-TJTWIW7U.js → doctor-cli-5Z5UWYW6.js} +4 -4
- package/dist/electron-server.js +473 -214
- package/dist/{hub-JWSV4MKC.js → hub-ZRADBR76.js} +1 -1
- package/dist/index.js +79 -18
- package/dist/{run-tests-D7YBY4XB.js → run-tests-7J3PNWVS.js} +1 -1
- package/dist/{run-tests-H4Q2PRX6.js → run-tests-DSOJBJWA.js} +2 -2
- package/dist/{server-OFKGDRYI.js → server-PWBWHVYB.js} +4 -4
- package/dist/{server-VNCN7AY5.js → server-U4I7RI42.js} +83 -7
- package/dist/{task-orchestrator-AL2E642M.js → task-orchestrator-RUXWRMQV.js} +4 -4
- package/dist/{usage-LB6REWPO.js → usage-2P6TIBTN.js} +2 -2
- package/dist/web/client/app.js +1 -1
- package/package.json +1 -1
|
@@ -5,10 +5,10 @@ import {
|
|
|
5
5
|
} from "./chunk-T2NL5ZIA.js";
|
|
6
6
|
import {
|
|
7
7
|
runTestsTool
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-UDMU4FUF.js";
|
|
9
9
|
import {
|
|
10
10
|
runTool
|
|
11
|
-
} from "./chunk-
|
|
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-
|
|
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
|
|
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((
|
|
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
|
-
|
|
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 (
|
|
2492
|
+
if (existsSync6(filePath)) {
|
|
2306
2493
|
let oldContent;
|
|
2307
2494
|
try {
|
|
2308
|
-
oldContent =
|
|
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 || !
|
|
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 =
|
|
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((
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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 (!
|
|
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 =
|
|
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
|
|
3141
|
-
import { join as
|
|
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 (!
|
|
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(
|
|
3408
|
+
listRecursive(join3(basePath, entry.name), indent + " ", true, lines);
|
|
3222
3409
|
}
|
|
3223
3410
|
} else {
|
|
3224
3411
|
try {
|
|
3225
|
-
const stat = statSync3(
|
|
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
|
|
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
|
|
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 (!
|
|
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(
|
|
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 =
|
|
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 =
|
|
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
|
|
3479
|
-
import { join as
|
|
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 (!
|
|
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 =
|
|
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((
|
|
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
|
-
|
|
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
|
-
|
|
3898
|
+
resolve7(`${prefixWarnings}Exit code ${code}:
|
|
3712
3899
|
${output}`);
|
|
3713
3900
|
} else {
|
|
3714
|
-
|
|
3901
|
+
resolve7(`${prefixWarnings}${output || "(no output)"}`);
|
|
3715
3902
|
}
|
|
3716
3903
|
});
|
|
3717
3904
|
child.on("error", (err) => {
|
|
3718
3905
|
clearTimeout(timer);
|
|
3719
|
-
|
|
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
|
|
4388
|
-
import { join as
|
|
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
|
|
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 =
|
|
4416
|
-
if (!
|
|
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 =
|
|
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((
|
|
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
|
-
|
|
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
|
|
5237
|
-
import { join as
|
|
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 (
|
|
5243
|
-
const parent =
|
|
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
|
|
5695
|
+
import { readFileSync as readFileSync9, existsSync as existsSync13 } from "fs";
|
|
5509
5696
|
import { writeFile } from "fs/promises";
|
|
5510
|
-
import { resolve as
|
|
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 =
|
|
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 (!
|
|
5750
|
+
if (!existsSync13(absPath)) {
|
|
5564
5751
|
throw new ToolError("notebook_edit", `Notebook not found: ${filePath}`);
|
|
5565
5752
|
}
|
|
5566
|
-
const raw =
|
|
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
|
|
5984
|
-
import { join as
|
|
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 (!
|
|
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) => ` + ${
|
|
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(
|
|
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,
|
|
@@ -10,11 +10,11 @@ import {
|
|
|
10
10
|
import "./chunk-XPBEJB27.js";
|
|
11
11
|
import {
|
|
12
12
|
ConfigManager
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-MRGWPCFQ.js";
|
|
14
14
|
import "./chunk-TZQHYZKT.js";
|
|
15
15
|
import {
|
|
16
16
|
VERSION
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-IWLVH32D.js";
|
|
18
18
|
import "./chunk-IW3Q7AE5.js";
|
|
19
19
|
|
|
20
20
|
// src/cli/ci.ts
|