@zqagent/core 0.3.13 → 0.3.22

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 (37) hide show
  1. package/dist/{TurnEngine-BZjUGic_.d.ts → TurnEngine-C8qqbilC.d.ts} +18 -3
  2. package/dist/{TurnEngine-CMGdcSdp.d.cts → TurnEngine-DKSczYaA.d.cts} +18 -3
  3. package/dist/{conflictGuard-BBOOIxxK.d.cts → conflictGuard-BuICnWEI.d.cts} +8 -0
  4. package/dist/{conflictGuard-BBOOIxxK.d.ts → conflictGuard-BuICnWEI.d.ts} +8 -0
  5. package/dist/index.cjs +3451 -1129
  6. package/dist/index.cjs.map +1 -1
  7. package/dist/index.d.cts +242 -6
  8. package/dist/index.d.ts +242 -6
  9. package/dist/index.js +3500 -1178
  10. package/dist/index.js.map +1 -1
  11. package/dist/internal/engine/FileLogHook.d.cts +1 -1
  12. package/dist/internal/engine/FileLogHook.d.ts +1 -1
  13. package/dist/internal/update/controller.cjs +1 -1
  14. package/dist/internal/update/controller.js +1 -1
  15. package/dist/internal/update/platform.cjs +1 -1
  16. package/dist/internal/update/platform.js +1 -1
  17. package/dist/tools/diff.d.cts +1 -1
  18. package/dist/tools/diff.d.ts +1 -1
  19. package/dist/tools/explore.cjs +504 -127
  20. package/dist/tools/explore.cjs.map +1 -1
  21. package/dist/tools/explore.d.cts +1 -1
  22. package/dist/tools/explore.d.ts +1 -1
  23. package/dist/tools/explore.js +504 -127
  24. package/dist/tools/explore.js.map +1 -1
  25. package/dist/tools/files.cjs +267 -47
  26. package/dist/tools/files.cjs.map +1 -1
  27. package/dist/tools/files.d.cts +1 -1
  28. package/dist/tools/files.d.ts +1 -1
  29. package/dist/tools/files.js +267 -47
  30. package/dist/tools/files.js.map +1 -1
  31. package/dist/tools/search.cjs +103 -24
  32. package/dist/tools/search.cjs.map +1 -1
  33. package/dist/tools/search.d.cts +8 -2
  34. package/dist/tools/search.d.ts +8 -2
  35. package/dist/tools/search.js +103 -24
  36. package/dist/tools/search.js.map +1 -1
  37. package/package.json +6 -5
@@ -1,5 +1,5 @@
1
1
  import { T as ToolRegistry } from '../ToolRegistry-DouDvn9E.cjs';
2
- import { C as ConflictGuard } from '../conflictGuard-BBOOIxxK.cjs';
2
+ import { C as ConflictGuard } from '../conflictGuard-BuICnWEI.cjs';
3
3
  import '../types-BYyXWWPW.cjs';
4
4
  import '@zqagent/shared/model-capabilities';
5
5
 
@@ -1,5 +1,5 @@
1
1
  import { T as ToolRegistry } from '../ToolRegistry-Cy0fQxrb.js';
2
- import { C as ConflictGuard } from '../conflictGuard-BBOOIxxK.js';
2
+ import { C as ConflictGuard } from '../conflictGuard-BuICnWEI.js';
3
3
  import '../types-BYyXWWPW.js';
4
4
  import '@zqagent/shared/model-capabilities';
5
5
 
@@ -1,6 +1,8 @@
1
1
  // src/tools/files.ts
2
- import * as fs2 from "fs";
3
- import * as path2 from "path";
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 = path2.resolve(workspace);
57
- function readOne(filePath, startLine, maxLines) {
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 (!fs2.existsSync(target) || !fs2.statSync(target).isFile()) {
227
+ if (!fs3.existsSync(target) || !fs3.statSync(target).isFile()) {
64
228
  return { path: filePath, error: `\u4E0D\u662F\u6587\u4EF6: ${filePath}` };
65
229
  }
66
- const selected = [];
67
- let total = 0;
230
+ let read;
68
231
  try {
69
- const content = fs2.readFileSync(target, "utf-8");
70
- guard?.noteRead(target, content);
71
- const lines = content.split("\n");
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: path2.relative(resolved.root, target),
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 (end < total) {
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 maxLines = Math.min(Number(args.max_lines ?? DEFAULT_MAX_LINES), DEFAULT_MAX_LINES);
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 = path2.resolve(workspace);
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 (fs2.existsSync(target) && fs2.statSync(target).isDirectory()) {
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 (fs2.existsSync(target) && !overwrite) {
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
- fs2.mkdirSync(path2.dirname(target), { recursive: true });
144
- fs2.writeFileSync(target, content, "utf-8");
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 path2.relative(resolved.root, target);
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 = path2.resolve(workspace);
318
+ const root = path3.resolve(workspace);
154
319
  return function list_files(args) {
155
320
  const dirPath = String(args.path ?? ".");
156
- const maxResults = Math.min(Number(args.max_results ?? 100), 100);
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 (!fs2.existsSync(target) || !fs2.statSync(target).isDirectory()) {
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
- const entries = fs2.readdirSync(target, { withFileTypes: true });
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
- for (const e of entries) {
167
- if (results.length >= maxResults) break;
168
- if (e.name.startsWith(".")) continue;
169
- if (e.name === "node_modules" || e.name === "__pycache__" || e.name === "dist") continue;
170
- results.push(e.isDirectory() ? `${e.name}/` : e.name);
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
- results.sort();
173
- return { path: path2.relative(resolved.root, target), entries: results.slice(0, maxResults) };
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 = path2.resolve(workspace);
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 (!fs2.existsSync(target) || !fs2.statSync(target).isFile()) {
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 = fs2.readFileSync(target, "utf-8");
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
- fs2.writeFileSync(target, updated, "utf-8");
411
+ fs3.writeFileSync(target, updated, "utf-8");
203
412
  guard?.noteWrite(target, updated);
204
413
  return {
205
- path: path2.relative(resolved.root, target),
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\u6587\u4EF6\u548C node_modules \u7B49\u76EE\u5F55\u3002",
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
- max_results: { type: "integer", description: "\u6700\u5927\u7ED3\u679C\u6570 (\u9ED8\u8BA4 100)" }
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
  }