@zqagent/core 0.3.12 → 0.3.16
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/dist/{TurnEngine-BU4R7q_0.d.ts → TurnEngine-C8qqbilC.d.ts} +29 -3
- package/dist/{TurnEngine-BSciNywD.d.cts → TurnEngine-DKSczYaA.d.cts} +29 -3
- package/dist/{conflictGuard-BBOOIxxK.d.cts → conflictGuard-BuICnWEI.d.cts} +8 -0
- package/dist/{conflictGuard-BBOOIxxK.d.ts → conflictGuard-BuICnWEI.d.ts} +8 -0
- package/dist/index.cjs +3364 -1107
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +221 -6
- package/dist/index.d.ts +221 -6
- package/dist/index.js +3414 -1157
- package/dist/index.js.map +1 -1
- package/dist/internal/engine/FileLogHook.d.cts +1 -1
- package/dist/internal/engine/FileLogHook.d.ts +1 -1
- package/dist/internal/llmConfig.cjs +15 -5
- package/dist/internal/llmConfig.cjs.map +1 -1
- package/dist/internal/llmConfig.d.cts +17 -1
- package/dist/internal/llmConfig.d.ts +17 -1
- package/dist/internal/llmConfig.js +15 -5
- package/dist/internal/llmConfig.js.map +1 -1
- package/dist/internal/update/controller.cjs +1 -1
- package/dist/internal/update/controller.js +1 -1
- package/dist/internal/update/platform.cjs +1 -1
- package/dist/internal/update/platform.js +1 -1
- package/dist/tools/diff.d.cts +1 -1
- package/dist/tools/diff.d.ts +1 -1
- package/dist/tools/explore.cjs +504 -127
- package/dist/tools/explore.cjs.map +1 -1
- package/dist/tools/explore.d.cts +1 -1
- package/dist/tools/explore.d.ts +1 -1
- package/dist/tools/explore.js +504 -127
- package/dist/tools/explore.js.map +1 -1
- package/dist/tools/files.cjs +267 -47
- package/dist/tools/files.cjs.map +1 -1
- package/dist/tools/files.d.cts +1 -1
- package/dist/tools/files.d.ts +1 -1
- package/dist/tools/files.js +267 -47
- package/dist/tools/files.js.map +1 -1
- package/dist/tools/search.cjs +103 -24
- package/dist/tools/search.cjs.map +1 -1
- package/dist/tools/search.d.cts +8 -2
- package/dist/tools/search.d.ts +8 -2
- package/dist/tools/search.js +103 -24
- package/dist/tools/search.js.map +1 -1
- package/package.json +6 -5
package/dist/tools/files.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { T as ToolRegistry } from '../ToolRegistry-DouDvn9E.cjs';
|
|
2
|
-
import { C as ConflictGuard } from '../conflictGuard-
|
|
2
|
+
import { C as ConflictGuard } from '../conflictGuard-BuICnWEI.cjs';
|
|
3
3
|
import '../types-BYyXWWPW.cjs';
|
|
4
4
|
import '@zqagent/shared/model-capabilities';
|
|
5
5
|
|
package/dist/tools/files.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { T as ToolRegistry } from '../ToolRegistry-Cy0fQxrb.js';
|
|
2
|
-
import { C as ConflictGuard } from '../conflictGuard-
|
|
2
|
+
import { C as ConflictGuard } from '../conflictGuard-BuICnWEI.js';
|
|
3
3
|
import '../types-BYyXWWPW.js';
|
|
4
4
|
import '@zqagent/shared/model-capabilities';
|
|
5
5
|
|
package/dist/tools/files.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// src/tools/files.ts
|
|
2
|
-
import * as
|
|
3
|
-
import * as
|
|
2
|
+
import * as fs3 from "fs";
|
|
3
|
+
import * as path3 from "path";
|
|
4
|
+
import { createHash } from "crypto";
|
|
5
|
+
import { StringDecoder } from "string_decoder";
|
|
4
6
|
|
|
5
7
|
// src/tools/pathSafety.ts
|
|
6
8
|
import * as fs from "fs";
|
|
@@ -48,61 +50,220 @@ function resolveWithinRoot(workspace, target) {
|
|
|
48
50
|
return { root: rootReal, real };
|
|
49
51
|
}
|
|
50
52
|
|
|
53
|
+
// src/paths.ts
|
|
54
|
+
var IS_WINDOWS = process.platform === "win32";
|
|
55
|
+
|
|
56
|
+
// src/engine/toolResultBudget.ts
|
|
57
|
+
var DEFAULT_TOOL_RESULT_MAX_CHARS = 24e3;
|
|
58
|
+
var TOOL_MAX_CHARS = {
|
|
59
|
+
read_file: 24e3,
|
|
60
|
+
explore: 16e3,
|
|
61
|
+
run_shell: 12e3,
|
|
62
|
+
web_fetch: 12e3,
|
|
63
|
+
git_diff: 12e3,
|
|
64
|
+
grep: 1e4,
|
|
65
|
+
web_search: 8e3,
|
|
66
|
+
list_files: 8e3,
|
|
67
|
+
shell_task_output: 8e3,
|
|
68
|
+
/* repo_map 自带 16K 上限(tools/repoMap.ts 的 MAX_CHARS)。这里显式登记,
|
|
69
|
+
* 而不是落到 24K 默认值 —— 免得将来工具上限调大时被二次裁剪,口径也保持一致。 */
|
|
70
|
+
repo_map: 16e3,
|
|
71
|
+
git_log: 6e3,
|
|
72
|
+
git_status: 4e3
|
|
73
|
+
};
|
|
74
|
+
function globalBudgetChars() {
|
|
75
|
+
const n = Number(process.env.TOOL_RESULT_MAX_CHARS);
|
|
76
|
+
return Number.isFinite(n) && n > 0 ? Math.floor(n) : void 0;
|
|
77
|
+
}
|
|
78
|
+
function toolResultBudget(tool) {
|
|
79
|
+
return globalBudgetChars() ?? TOOL_MAX_CHARS[tool] ?? DEFAULT_TOOL_RESULT_MAX_CHARS;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// src/tools/ignore.ts
|
|
83
|
+
import * as fs2 from "fs";
|
|
84
|
+
import * as path2 from "path";
|
|
85
|
+
var ALWAYS_SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
86
|
+
// JS / TS
|
|
87
|
+
"node_modules",
|
|
88
|
+
"dist",
|
|
89
|
+
"build",
|
|
90
|
+
"out",
|
|
91
|
+
".next",
|
|
92
|
+
".nuxt",
|
|
93
|
+
".cache",
|
|
94
|
+
// JVM
|
|
95
|
+
"target",
|
|
96
|
+
".gradle",
|
|
97
|
+
".mvn",
|
|
98
|
+
// .NET
|
|
99
|
+
"bin",
|
|
100
|
+
"obj",
|
|
101
|
+
".vs",
|
|
102
|
+
"TestResults",
|
|
103
|
+
// Python / Ruby / PHP
|
|
104
|
+
"__pycache__",
|
|
105
|
+
".venv",
|
|
106
|
+
"venv",
|
|
107
|
+
".pytest_cache",
|
|
108
|
+
".mypy_cache",
|
|
109
|
+
"vendor",
|
|
110
|
+
// 移动端
|
|
111
|
+
"Pods",
|
|
112
|
+
".dart_tool",
|
|
113
|
+
"DerivedData",
|
|
114
|
+
// 覆盖率 / 通用
|
|
115
|
+
"coverage",
|
|
116
|
+
// 版本库
|
|
117
|
+
".git",
|
|
118
|
+
".svn",
|
|
119
|
+
".hg"
|
|
120
|
+
]);
|
|
121
|
+
function loadGitignore(dir) {
|
|
122
|
+
const file = path2.join(dir, ".gitignore");
|
|
123
|
+
let text;
|
|
124
|
+
try {
|
|
125
|
+
const stat = fs2.statSync(file);
|
|
126
|
+
if (stat.size > 64 * 1024) return [];
|
|
127
|
+
text = fs2.readFileSync(file, "utf-8");
|
|
128
|
+
} catch {
|
|
129
|
+
return [];
|
|
130
|
+
}
|
|
131
|
+
const matchers = [];
|
|
132
|
+
for (const rawLine of text.split("\n")) {
|
|
133
|
+
let line = rawLine.replace(/\r$/, "").trim();
|
|
134
|
+
if (!line || line.startsWith("#")) continue;
|
|
135
|
+
if (line.startsWith("!")) continue;
|
|
136
|
+
const anchored = rawLine.trim().startsWith("/");
|
|
137
|
+
const dirOnly = /\/\s*$/.test(rawLine.trim());
|
|
138
|
+
line = line.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
139
|
+
if (!line) continue;
|
|
140
|
+
const pattern = line.split("").map((ch) => {
|
|
141
|
+
if (ch === "*") return ".*";
|
|
142
|
+
if (ch === "?") return ".";
|
|
143
|
+
return ch.replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
|
144
|
+
}).join("");
|
|
145
|
+
const re = new RegExp(line.includes("/") || anchored ? `^${pattern}$` : `(^|/)${pattern}$`);
|
|
146
|
+
matchers.push((rel, isDir) => {
|
|
147
|
+
if (dirOnly && !isDir) return false;
|
|
148
|
+
return re.test(rel);
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
return matchers;
|
|
152
|
+
}
|
|
153
|
+
function makeIgnoreFilter(root) {
|
|
154
|
+
const matchers = loadGitignore(root);
|
|
155
|
+
return (rel, isDir) => {
|
|
156
|
+
const name = rel.slice(rel.lastIndexOf("/") + 1);
|
|
157
|
+
if (name.startsWith(".")) return true;
|
|
158
|
+
if (isDir && ALWAYS_SKIP_DIRS.has(name)) return true;
|
|
159
|
+
return matchers.some((m) => m(rel, isDir));
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
51
163
|
// src/tools/files.ts
|
|
52
164
|
var DEFAULT_MAX_LINES = 2e3;
|
|
53
165
|
var MULTI_READ_MAX_TOTAL_LINES = 1500;
|
|
54
166
|
var MAX_LINE_CHARS = 500;
|
|
167
|
+
var READ_CHUNK_BYTES = 64 * 1024;
|
|
168
|
+
var RESULT_META_RESERVE = 800;
|
|
55
169
|
function makeReadFile(workspace, guard) {
|
|
56
|
-
const root =
|
|
57
|
-
function
|
|
170
|
+
const root = path3.resolve(workspace);
|
|
171
|
+
function readWindow(target, startLine, maxLines, charBudget, onDigest) {
|
|
172
|
+
const fd = fs3.openSync(target, "r");
|
|
173
|
+
try {
|
|
174
|
+
const decoder = new StringDecoder("utf-8");
|
|
175
|
+
const hash = createHash("sha256");
|
|
176
|
+
const buf = Buffer.allocUnsafe(READ_CHUNK_BYTES);
|
|
177
|
+
const lines = [];
|
|
178
|
+
let lineNo = 0;
|
|
179
|
+
let pending = "";
|
|
180
|
+
let chars = 0;
|
|
181
|
+
let capped = false;
|
|
182
|
+
let pos = 0;
|
|
183
|
+
const takeLine = (text) => {
|
|
184
|
+
lineNo += 1;
|
|
185
|
+
if (capped || lineNo < startLine || lines.length >= maxLines) return;
|
|
186
|
+
const clipped = text.length > MAX_LINE_CHARS ? text.slice(0, MAX_LINE_CHARS) + "\u2026 (line truncated)" : text;
|
|
187
|
+
const rendered = `${lineNo}|${clipped}`;
|
|
188
|
+
if (chars + rendered.length + 1 > charBudget) {
|
|
189
|
+
capped = true;
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
chars += rendered.length + 1;
|
|
193
|
+
lines.push(rendered);
|
|
194
|
+
};
|
|
195
|
+
for (; ; ) {
|
|
196
|
+
const n = fs3.readSync(fd, buf, 0, READ_CHUNK_BYTES, pos);
|
|
197
|
+
if (n <= 0) break;
|
|
198
|
+
pos += n;
|
|
199
|
+
const decoded = decoder.write(buf.subarray(0, n));
|
|
200
|
+
hash.update(decoded, "utf8");
|
|
201
|
+
const text = pending + decoded;
|
|
202
|
+
let from = 0;
|
|
203
|
+
for (; ; ) {
|
|
204
|
+
const idx = text.indexOf("\n", from);
|
|
205
|
+
if (idx < 0) break;
|
|
206
|
+
takeLine(text.slice(from, idx).replace(/\r$/, ""));
|
|
207
|
+
from = idx + 1;
|
|
208
|
+
}
|
|
209
|
+
pending = text.slice(from);
|
|
210
|
+
}
|
|
211
|
+
const rest = decoder.end();
|
|
212
|
+
if (rest) hash.update(rest, "utf8");
|
|
213
|
+
const tail = pending + rest;
|
|
214
|
+
if (tail.length > 0) takeLine(tail.replace(/\r$/, ""));
|
|
215
|
+
onDigest(hash.digest("hex").slice(0, 16));
|
|
216
|
+
return { lines, totalLines: lineNo, capped };
|
|
217
|
+
} finally {
|
|
218
|
+
fs3.closeSync(fd);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
function readOne(filePath, startLine, maxLines, charBudget) {
|
|
58
222
|
const resolved = resolveWithinRoot(root, filePath);
|
|
59
223
|
if (!resolved) {
|
|
60
224
|
return { path: filePath, error: "\u8DEF\u5F84\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916" };
|
|
61
225
|
}
|
|
62
226
|
const target = resolved.real;
|
|
63
|
-
if (!
|
|
227
|
+
if (!fs3.existsSync(target) || !fs3.statSync(target).isFile()) {
|
|
64
228
|
return { path: filePath, error: `\u4E0D\u662F\u6587\u4EF6: ${filePath}` };
|
|
65
229
|
}
|
|
66
|
-
|
|
67
|
-
let total = 0;
|
|
230
|
+
let read;
|
|
68
231
|
try {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
total = lines.length;
|
|
73
|
-
for (let i = startLine - 1; i < Math.min(startLine - 1 + maxLines, lines.length); i++) {
|
|
74
|
-
let text = lines[i];
|
|
75
|
-
if (text.length > MAX_LINE_CHARS) {
|
|
76
|
-
text = text.slice(0, MAX_LINE_CHARS) + "\u2026 (line truncated)";
|
|
77
|
-
}
|
|
78
|
-
selected.push(`${i + 1}|${text}`);
|
|
79
|
-
}
|
|
232
|
+
read = readWindow(target, startLine, maxLines, charBudget, (digest) => {
|
|
233
|
+
guard?.noteReadDigest(target, digest);
|
|
234
|
+
});
|
|
80
235
|
} catch (exc) {
|
|
81
236
|
return { path: filePath, error: `\u8BFB\u53D6\u5931\u8D25: ${String(exc)}` };
|
|
82
237
|
}
|
|
238
|
+
const { lines: selected, totalLines: total, capped } = read;
|
|
83
239
|
const end = startLine + selected.length - 1;
|
|
84
240
|
const result = {
|
|
85
|
-
path:
|
|
241
|
+
path: path3.relative(resolved.root, target),
|
|
86
242
|
start_line: startLine,
|
|
87
243
|
end_line: end,
|
|
88
244
|
total_lines: total,
|
|
89
245
|
content: selected.join("\n")
|
|
90
246
|
};
|
|
91
|
-
if (
|
|
247
|
+
if (capped) {
|
|
248
|
+
result.note = `\u5DF2\u8FD4\u56DE\u7B2C ${startLine}-${end} \u884C\uFF08\u672C\u6B21\u5DF2\u8FBE\u5355\u6B21\u8BFB\u53D6\u4E0A\u9650\u7EA6 ${charBudget} \u5B57\u7B26\uFF0C\u5171 ${total} \u884C\uFF09\uFF1B\u8C03\u7528 read_file(start_line=${end + 1}) \u7EE7\u7EED\u9605\u8BFB`;
|
|
249
|
+
} else if (end < total) {
|
|
92
250
|
result.note = `\u663E\u793A\u7B2C ${startLine}-${end} \u884C\uFF0C\u5171 ${total} \u884C\uFF1B\u8C03\u7528 read_file(start_line=${end + 1}) \u7EE7\u7EED\u9605\u8BFB`;
|
|
93
251
|
}
|
|
94
252
|
return result;
|
|
95
253
|
}
|
|
96
254
|
return function read_file(args) {
|
|
97
255
|
const startLine = Math.max(1, Number(args.start_line ?? 1));
|
|
98
|
-
const
|
|
256
|
+
const requestedLines = Number(args.max_lines ?? DEFAULT_MAX_LINES);
|
|
257
|
+
const maxLines = Number.isFinite(requestedLines) ? Math.min(Math.max(1, Math.floor(requestedLines)), DEFAULT_MAX_LINES) : DEFAULT_MAX_LINES;
|
|
258
|
+
const fullBudget = Math.max(1e3, toolResultBudget("read_file") - RESULT_META_RESERVE);
|
|
99
259
|
const paths = Array.isArray(args.paths) ? args.paths.map((p) => String(p ?? "")).filter(Boolean) : [];
|
|
100
260
|
if (paths.length > 0) {
|
|
261
|
+
const perFileBudget = Math.max(1e3, Math.floor(fullBudget / paths.length));
|
|
101
262
|
const files = [];
|
|
102
263
|
let remainingLines = MULTI_READ_MAX_TOTAL_LINES;
|
|
103
264
|
for (const filePath2 of paths) {
|
|
104
265
|
const perFile = Math.min(maxLines, Math.max(1, remainingLines));
|
|
105
|
-
const one = readOne(filePath2, startLine, perFile);
|
|
266
|
+
const one = readOne(filePath2, startLine, perFile, perFileBudget);
|
|
106
267
|
files.push(one);
|
|
107
268
|
if (!one.error) remainingLines -= one.content.split("\n").length;
|
|
108
269
|
if (remainingLines <= 0) break;
|
|
@@ -117,11 +278,11 @@ function makeReadFile(workspace, guard) {
|
|
|
117
278
|
if (!filePath) {
|
|
118
279
|
return { error: "\u7F3A\u5C11 path\uFF08\u6216 paths\uFF09\u53C2\u6570" };
|
|
119
280
|
}
|
|
120
|
-
return readOne(filePath, startLine, maxLines);
|
|
281
|
+
return readOne(filePath, startLine, maxLines, fullBudget);
|
|
121
282
|
};
|
|
122
283
|
}
|
|
123
284
|
function makeWriteFile(workspace, guard) {
|
|
124
|
-
const root =
|
|
285
|
+
const root = path3.resolve(workspace);
|
|
125
286
|
return function write_file(args) {
|
|
126
287
|
const filePath = String(args.path ?? "");
|
|
127
288
|
const content = String(args.content ?? "");
|
|
@@ -131,53 +292,101 @@ function makeWriteFile(workspace, guard) {
|
|
|
131
292
|
return { error: "\u8DEF\u5F84\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916" };
|
|
132
293
|
}
|
|
133
294
|
const target = resolved.real;
|
|
134
|
-
if (
|
|
295
|
+
if (fs3.existsSync(target) && fs3.statSync(target).isDirectory()) {
|
|
135
296
|
return { error: "\u8DEF\u5F84\u662F\u76EE\u5F55\uFF0C\u4E0D\u662F\u6587\u4EF6" };
|
|
136
297
|
}
|
|
137
|
-
if (
|
|
298
|
+
if (fs3.existsSync(target) && !overwrite) {
|
|
138
299
|
return { error: "\u6587\u4EF6\u5DF2\u5B58\u5728 (overwrite=false)" };
|
|
139
300
|
}
|
|
140
301
|
const conflict = guard?.checkWrite(target, filePath);
|
|
141
302
|
if (conflict) return { error: conflict.message };
|
|
142
303
|
try {
|
|
143
|
-
|
|
144
|
-
|
|
304
|
+
fs3.mkdirSync(path3.dirname(target), { recursive: true });
|
|
305
|
+
fs3.writeFileSync(target, content, "utf-8");
|
|
145
306
|
guard?.noteWrite(target, content);
|
|
146
307
|
} catch (exc) {
|
|
147
308
|
return { error: `\u5199\u5165\u5931\u8D25: ${String(exc)}` };
|
|
148
309
|
}
|
|
149
|
-
return
|
|
310
|
+
return path3.relative(resolved.root, target);
|
|
150
311
|
};
|
|
151
312
|
}
|
|
313
|
+
var DEFAULT_LIST_DEPTH = 3;
|
|
314
|
+
var MAX_LIST_DEPTH = 6;
|
|
315
|
+
var DEFAULT_RECURSIVE_LIMIT = 400;
|
|
316
|
+
var MAX_RECURSIVE_LIMIT = 1e3;
|
|
152
317
|
function makeListFiles(workspace) {
|
|
153
|
-
const root =
|
|
318
|
+
const root = path3.resolve(workspace);
|
|
154
319
|
return function list_files(args) {
|
|
155
320
|
const dirPath = String(args.path ?? ".");
|
|
156
|
-
const
|
|
321
|
+
const recursive = args.recursive === true;
|
|
322
|
+
const requestedMax = Number(args.max_results ?? (recursive ? DEFAULT_RECURSIVE_LIMIT : 100));
|
|
323
|
+
const hardCap = recursive ? MAX_RECURSIVE_LIMIT : 100;
|
|
324
|
+
const maxResults = Number.isFinite(requestedMax) ? Math.max(1, Math.min(Math.floor(requestedMax), hardCap)) : hardCap;
|
|
325
|
+
const requestedDepth = Number(args.max_depth ?? DEFAULT_LIST_DEPTH);
|
|
326
|
+
const maxDepth = Number.isFinite(requestedDepth) ? Math.max(1, Math.min(Math.floor(requestedDepth), MAX_LIST_DEPTH)) : DEFAULT_LIST_DEPTH;
|
|
157
327
|
const resolved = resolveWithinRoot(root, dirPath);
|
|
158
328
|
if (!resolved) return { error: "\u8DEF\u5F84\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916" };
|
|
159
329
|
const target = resolved.real;
|
|
160
|
-
if (!
|
|
330
|
+
if (!fs3.existsSync(target) || !fs3.statSync(target).isDirectory()) {
|
|
161
331
|
return { error: `\u4E0D\u662F\u76EE\u5F55: ${dirPath}` };
|
|
162
332
|
}
|
|
333
|
+
const realRoot = resolved.root;
|
|
334
|
+
const isIgnored = makeIgnoreFilter(realRoot);
|
|
163
335
|
try {
|
|
164
|
-
|
|
336
|
+
if (!recursive) {
|
|
337
|
+
const results2 = [];
|
|
338
|
+
for (const e of fs3.readdirSync(target, { withFileTypes: true })) {
|
|
339
|
+
if (results2.length >= maxResults) break;
|
|
340
|
+
const rel = path3.relative(realRoot, path3.join(target, e.name)).split(path3.sep).join("/");
|
|
341
|
+
if (isIgnored(rel, e.isDirectory())) continue;
|
|
342
|
+
results2.push(e.isDirectory() ? `${e.name}/` : e.name);
|
|
343
|
+
}
|
|
344
|
+
results2.sort();
|
|
345
|
+
return { path: path3.relative(realRoot, target), entries: results2.slice(0, maxResults) };
|
|
346
|
+
}
|
|
165
347
|
const results = [];
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
348
|
+
let truncated = false;
|
|
349
|
+
const queue = [{ dir: target, depth: 1 }];
|
|
350
|
+
while (queue.length > 0) {
|
|
351
|
+
const { dir, depth } = queue.shift();
|
|
352
|
+
let entries;
|
|
353
|
+
try {
|
|
354
|
+
entries = fs3.readdirSync(dir, { withFileTypes: true });
|
|
355
|
+
} catch {
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
entries.sort((a, b) => {
|
|
359
|
+
if (a.isDirectory() !== b.isDirectory()) return a.isDirectory() ? -1 : 1;
|
|
360
|
+
return a.name.localeCompare(b.name, "zh");
|
|
361
|
+
});
|
|
362
|
+
for (const e of entries) {
|
|
363
|
+
if (results.length >= maxResults) {
|
|
364
|
+
truncated = true;
|
|
365
|
+
break;
|
|
366
|
+
}
|
|
367
|
+
const full = path3.join(dir, e.name);
|
|
368
|
+
const rel = path3.relative(realRoot, full).split(path3.sep).join("/");
|
|
369
|
+
const isDir = e.isDirectory();
|
|
370
|
+
if (isIgnored(rel, isDir)) continue;
|
|
371
|
+
results.push(isDir ? `${rel}/` : rel);
|
|
372
|
+
if (isDir && depth < maxDepth) queue.push({ dir: full, depth: depth + 1 });
|
|
373
|
+
}
|
|
374
|
+
if (truncated) break;
|
|
171
375
|
}
|
|
172
|
-
|
|
173
|
-
|
|
376
|
+
return {
|
|
377
|
+
path: path3.relative(realRoot, target),
|
|
378
|
+
entries: results,
|
|
379
|
+
...truncated ? {
|
|
380
|
+
note: `\u5DF2\u8FBE\u5230\u6761\u76EE\u4E0A\u9650 ${maxResults}\uFF0C\u7ED3\u679C\u88AB\u622A\u65AD\uFF1B\u8BF7\u7F29\u5C0F path \u5230\u5177\u4F53\u5B50\u76EE\u5F55\uFF0C\u6216\u964D\u4F4E max_depth\uFF08\u5F53\u524D ${maxDepth}\uFF09`
|
|
381
|
+
} : {}
|
|
382
|
+
};
|
|
174
383
|
} catch (exc) {
|
|
175
384
|
return { error: `\u5217\u51FA\u6587\u4EF6\u5931\u8D25: ${String(exc)}` };
|
|
176
385
|
}
|
|
177
386
|
};
|
|
178
387
|
}
|
|
179
388
|
function makeReplaceInFile(workspace, guard) {
|
|
180
|
-
const root =
|
|
389
|
+
const root = path3.resolve(workspace);
|
|
181
390
|
return function replace_in_file(args) {
|
|
182
391
|
const filePath = String(args.path ?? "");
|
|
183
392
|
const oldStr = String(args.old ?? "");
|
|
@@ -187,22 +396,22 @@ function makeReplaceInFile(workspace, guard) {
|
|
|
187
396
|
const resolved = resolveWithinRoot(root, filePath);
|
|
188
397
|
if (!resolved) return { error: "\u8DEF\u5F84\u5728\u5DE5\u4F5C\u533A\u4E4B\u5916" };
|
|
189
398
|
const target = resolved.real;
|
|
190
|
-
if (!
|
|
399
|
+
if (!fs3.existsSync(target) || !fs3.statSync(target).isFile()) {
|
|
191
400
|
return { error: `\u4E0D\u662F\u6587\u4EF6: ${filePath}` };
|
|
192
401
|
}
|
|
193
402
|
const conflict = guard?.checkWrite(target, filePath);
|
|
194
403
|
if (conflict) return { error: conflict.message };
|
|
195
404
|
try {
|
|
196
|
-
const content =
|
|
405
|
+
const content = fs3.readFileSync(target, "utf-8");
|
|
197
406
|
const count = content.split(oldStr).length - 1;
|
|
198
407
|
if (count !== expectedReplacements) {
|
|
199
408
|
return { error: `\u671F\u671B ${expectedReplacements} \u5904\u66FF\u6362\uFF0C\u5B9E\u9645\u627E\u5230 ${count} \u5904: ${filePath}` };
|
|
200
409
|
}
|
|
201
410
|
const updated = content.replaceAll(oldStr, newStr);
|
|
202
|
-
|
|
411
|
+
fs3.writeFileSync(target, updated, "utf-8");
|
|
203
412
|
guard?.noteWrite(target, updated);
|
|
204
413
|
return {
|
|
205
|
-
path:
|
|
414
|
+
path: path3.relative(resolved.root, target),
|
|
206
415
|
replacements: count,
|
|
207
416
|
chars_before: content.length,
|
|
208
417
|
chars_after: updated.length
|
|
@@ -246,12 +455,23 @@ function registerReadOnlyFileTools(registry, workspace, guard) {
|
|
|
246
455
|
type: "function",
|
|
247
456
|
function: {
|
|
248
457
|
name: "list_files",
|
|
249
|
-
description: "\u5217\u51FA\u76EE\u5F55\u4E0B\u7684\u6587\u4EF6\u548C\u5B50\u76EE\u5F55\u3002\u8DF3\u8FC7\u9690\u85CF\
|
|
458
|
+
description: "\u5217\u51FA\u76EE\u5F55\u4E0B\u7684\u6587\u4EF6\u548C\u5B50\u76EE\u5F55\u3002\u8DF3\u8FC7\u9690\u85CF\u9879\u3001\u5E38\u89C1\u6784\u5EFA\u4EA7\u7269\u76EE\u5F55\uFF0C\u5E76\u9075\u5B88\u5DE5\u4F5C\u533A\u6839\u76EE\u5F55\u7684 .gitignore\u3002\u4E86\u89E3\u5927\u4ED3\u5E93\u7ED3\u6784\u65F6\u7528 recursive=true \u4E00\u6B21\u62FF\u5230\u6709\u9650\u6DF1\u5EA6\uFF08\u9ED8\u8BA4 3 \u5C42\uFF09\u7684\u6811\uFF0C\u6BD4\u9010\u5C42\u8C03\u7528\u7701\u5F88\u591A\u5F80\u8FD4\uFF1B\u6761\u76EE\u8FC7\u591A\u65F6\u4F1A\u622A\u65AD\u5E76\u63D0\u793A\u3002",
|
|
250
459
|
parameters: {
|
|
251
460
|
type: "object",
|
|
252
461
|
properties: {
|
|
253
462
|
path: { type: "string", description: "\u76EE\u5F55\u8DEF\u5F84 (\u76F8\u5BF9\u4E8E\u5DE5\u4F5C\u533A\uFF0C\u9ED8\u8BA4 '.')" },
|
|
254
|
-
|
|
463
|
+
recursive: {
|
|
464
|
+
type: "boolean",
|
|
465
|
+
description: "\u9012\u5F52\u5217\u51FA\u5B50\u76EE\u5F55\uFF08\u9ED8\u8BA4 false\uFF0C\u53EA\u5217\u4E00\u5C42\uFF09"
|
|
466
|
+
},
|
|
467
|
+
max_depth: {
|
|
468
|
+
type: "integer",
|
|
469
|
+
description: `\u9012\u5F52\u6DF1\u5EA6 (\u9ED8\u8BA4 ${DEFAULT_LIST_DEPTH}\uFF0C\u4E0A\u9650 ${MAX_LIST_DEPTH}\uFF1B\u4EC5 recursive \u65F6\u751F\u6548)`
|
|
470
|
+
},
|
|
471
|
+
max_results: {
|
|
472
|
+
type: "integer",
|
|
473
|
+
description: `\u6700\u5927\u7ED3\u679C\u6570\uFF08\u975E\u9012\u5F52\u9ED8\u8BA4 100 / \u4E0A\u9650 100\uFF1B\u9012\u5F52\u9ED8\u8BA4 ${DEFAULT_RECURSIVE_LIMIT} / \u4E0A\u9650 ${MAX_RECURSIVE_LIMIT}\uFF09`
|
|
474
|
+
}
|
|
255
475
|
}
|
|
256
476
|
}
|
|
257
477
|
}
|