@oxecli/oxe 1.0.51 → 1.0.53
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/cli.js +69 -65
- package/dist/config.js +35 -35
- package/dist/engine.js +56 -43
- package/dist/skills.js +10 -4
- package/dist/system.js +2 -2
- package/dist/tools.js +134 -75
- package/dist/ui.js +396 -297
- package/package.json +1 -1
package/dist/tools.js
CHANGED
|
@@ -6,14 +6,22 @@ import { max_diff_source_chars, max_diff_lines, max_diff_context_lines, max_diff
|
|
|
6
6
|
import { toolLoadSkill } from "./skills.js";
|
|
7
7
|
export { toolLoadSkill };
|
|
8
8
|
// ---------------------------------------------------------------------------
|
|
9
|
+
// Path sanitization helper
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
function sanitizePath(p) {
|
|
12
|
+
if (!p)
|
|
13
|
+
return "";
|
|
14
|
+
return String(p).trim().replace(/^["']|["']$/g, "");
|
|
15
|
+
}
|
|
16
|
+
function escapeRegExp(string) {
|
|
17
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
18
|
+
}
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
9
20
|
// Natural sort + diff helpers
|
|
10
21
|
// ---------------------------------------------------------------------------
|
|
11
22
|
export function naturalSortKey(s) {
|
|
12
23
|
return s.replace(/\d+/g, (m) => m.padStart(12, "0")).toLowerCase();
|
|
13
24
|
}
|
|
14
|
-
function isGlob(s) {
|
|
15
|
-
return /[*?[]/.test(s);
|
|
16
|
-
}
|
|
17
25
|
function truncateDiffLine(line) {
|
|
18
26
|
const ending = line.endsWith("\n") ? "\n" : "";
|
|
19
27
|
const body = ending ? line.slice(0, -1) : line;
|
|
@@ -24,8 +32,8 @@ function truncateDiffLine(line) {
|
|
|
24
32
|
}
|
|
25
33
|
function displayDiff(pathName, oldContent, newContent) {
|
|
26
34
|
if (oldContent.length + newContent.length > max_diff_source_chars) {
|
|
27
|
-
|
|
28
|
-
`(diff hidden: exceeds ${max_diff_source_chars.toLocaleString()} char limit)\x1b[0m`);
|
|
35
|
+
process.stdout.write(`\x1b[2m${pathName}: ${oldContent.length.toLocaleString()} chars -> ${newContent.length.toLocaleString()} chars ` +
|
|
36
|
+
`(diff hidden: exceeds ${max_diff_source_chars.toLocaleString()} char limit)\x1b[0m\n`);
|
|
29
37
|
return;
|
|
30
38
|
}
|
|
31
39
|
const patch = structuredPatch(pathName, pathName, oldContent, newContent, "", "", { context: max_diff_context_lines });
|
|
@@ -44,8 +52,7 @@ function displayDiff(pathName, oldContent, newContent) {
|
|
|
44
52
|
const removed = contentLines.filter((c) => c.kind === "-").length;
|
|
45
53
|
if (!added && !removed)
|
|
46
54
|
return;
|
|
47
|
-
|
|
48
|
-
console.log(`\x1b[31m-${removed}\x1b[0m \x1b[32m+${added}\x1b[0m`);
|
|
55
|
+
process.stdout.write(`\x1b[90m┌─\x1b[0m \x1b[1m${pathName}\x1b[0m \x1b[32m+${added}\x1b[0m \x1b[31m-${removed}\x1b[0m\n`);
|
|
49
56
|
const shown = [];
|
|
50
57
|
let run = [];
|
|
51
58
|
let lastKind = null;
|
|
@@ -70,17 +77,23 @@ function displayDiff(pathName, oldContent, newContent) {
|
|
|
70
77
|
run.push(c);
|
|
71
78
|
}
|
|
72
79
|
flush();
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
.
|
|
78
|
-
|
|
80
|
+
for (const l of shown) {
|
|
81
|
+
if (l.startsWith("+")) {
|
|
82
|
+
process.stdout.write(`\x1b[90m│\x1b[0m \x1b[32m${l}\x1b[0m\n`);
|
|
83
|
+
}
|
|
84
|
+
else if (l.startsWith("-")) {
|
|
85
|
+
process.stdout.write(`\x1b[90m│\x1b[0m \x1b[31m${l}\x1b[0m\n`);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
process.stdout.write(`\x1b[90m│\x1b[0m \x1b[2m${l}\x1b[0m\n`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
process.stdout.write(`\x1b[90m└────────────────────────────────────────\x1b[0m\n`);
|
|
79
92
|
}
|
|
80
93
|
export function truncateToolOutput(output, maxChars = max_output_chars) {
|
|
81
94
|
if (output.length > maxChars) {
|
|
82
95
|
return (output.slice(0, maxChars) +
|
|
83
|
-
`\n\n[... Output truncated! Total characters: ${output.length} ...]`);
|
|
96
|
+
`\n\n[... Output truncated! Total characters: ${output.length.toLocaleString()} ...]`);
|
|
84
97
|
}
|
|
85
98
|
return output;
|
|
86
99
|
}
|
|
@@ -129,11 +142,14 @@ function buildLineIndex(p) {
|
|
|
129
142
|
return null;
|
|
130
143
|
}
|
|
131
144
|
finally {
|
|
132
|
-
if (fd !== null)
|
|
145
|
+
if (fd !== null) {
|
|
133
146
|
try {
|
|
134
147
|
fs.closeSync(fd);
|
|
135
148
|
}
|
|
136
|
-
catch {
|
|
149
|
+
catch {
|
|
150
|
+
/* ignore */
|
|
151
|
+
}
|
|
152
|
+
}
|
|
137
153
|
}
|
|
138
154
|
if (base === 0)
|
|
139
155
|
return [[0], 0];
|
|
@@ -210,7 +226,9 @@ function streamReadRange(p, start, end) {
|
|
|
210
226
|
return [i, numbered];
|
|
211
227
|
}
|
|
212
228
|
export function toolReadFile(pathName, startLine = 1, endLine) {
|
|
213
|
-
const p = pathName;
|
|
229
|
+
const p = sanitizePath(pathName);
|
|
230
|
+
if (!p)
|
|
231
|
+
return "Error: path must not be empty";
|
|
214
232
|
if (!fs.existsSync(p))
|
|
215
233
|
return `Error: file not found: ${pathName}`;
|
|
216
234
|
if (!fs.statSync(p).isFile())
|
|
@@ -224,13 +242,30 @@ export function toolReadFile(pathName, startLine = 1, endLine) {
|
|
|
224
242
|
}
|
|
225
243
|
if (size > max_read_file_bytes) {
|
|
226
244
|
return (`Error: file too large (${size.toLocaleString()} bytes > ${max_read_file_bytes.toLocaleString()} cap). ` +
|
|
227
|
-
|
|
245
|
+
"Use grep or bash to inspect it instead of reading it whole.");
|
|
228
246
|
}
|
|
229
247
|
if (size === 0)
|
|
230
248
|
return "(empty file)";
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
249
|
+
try {
|
|
250
|
+
const head = fs.readFileSync(p, { flag: "r" }).subarray(0, 8192);
|
|
251
|
+
if (head.includes(0))
|
|
252
|
+
return `Error: refusing to read binary file: ${pathName}`;
|
|
253
|
+
}
|
|
254
|
+
catch {
|
|
255
|
+
/* ignore */
|
|
256
|
+
}
|
|
257
|
+
const sVal = typeof startLine === "number"
|
|
258
|
+
? startLine
|
|
259
|
+
: parseInt(String(startLine || 1), 10) || 1;
|
|
260
|
+
const start = Math.max(1, sVal);
|
|
261
|
+
let eVal = null;
|
|
262
|
+
if (endLine != null) {
|
|
263
|
+
const parsed = typeof endLine === "number" ? endLine : parseInt(String(endLine), 10);
|
|
264
|
+
if (!Number.isNaN(parsed))
|
|
265
|
+
eVal = parsed;
|
|
266
|
+
}
|
|
267
|
+
if (eVal != null && eVal < start) {
|
|
268
|
+
return `Error: end_line ${eVal} is before start_line ${start}`;
|
|
234
269
|
}
|
|
235
270
|
let end = null;
|
|
236
271
|
let total = 0;
|
|
@@ -240,9 +275,10 @@ export function toolReadFile(pathName, startLine = 1, endLine) {
|
|
|
240
275
|
if (idx) {
|
|
241
276
|
const [offsets, t] = idx;
|
|
242
277
|
total = t;
|
|
243
|
-
if (start > total)
|
|
278
|
+
if (start > total) {
|
|
244
279
|
return `Error: start_line ${start} exceeds file length (${total} lines)`;
|
|
245
|
-
|
|
280
|
+
}
|
|
281
|
+
end = eVal == null ? total : Math.min(total, eVal);
|
|
246
282
|
capped = end - start + 1 > max_read_lines;
|
|
247
283
|
if (capped)
|
|
248
284
|
end = start + max_read_lines - 1;
|
|
@@ -253,11 +289,12 @@ export function toolReadFile(pathName, startLine = 1, endLine) {
|
|
|
253
289
|
}
|
|
254
290
|
}
|
|
255
291
|
else {
|
|
256
|
-
const collectEnd = Math.min(Math.max(1,
|
|
292
|
+
const collectEnd = Math.min(Math.max(1, eVal != null ? eVal : start + max_read_lines - 1), start + max_read_lines - 1);
|
|
257
293
|
[total, numbered] = streamReadRange(p, start, collectEnd);
|
|
258
|
-
if (start > total)
|
|
294
|
+
if (start > total) {
|
|
259
295
|
return `Error: start_line ${start} exceeds file length (${total} lines)`;
|
|
260
|
-
|
|
296
|
+
}
|
|
297
|
+
end = eVal == null ? total : Math.min(total, eVal);
|
|
261
298
|
capped = end - start + 1 > max_read_lines;
|
|
262
299
|
if (capped) {
|
|
263
300
|
end = start + max_read_lines - 1;
|
|
@@ -278,7 +315,9 @@ export function toolReadFile(pathName, startLine = 1, endLine) {
|
|
|
278
315
|
// write_file
|
|
279
316
|
// ---------------------------------------------------------------------------
|
|
280
317
|
export function toolWriteFile(pathName, content) {
|
|
281
|
-
const p = pathName;
|
|
318
|
+
const p = sanitizePath(pathName);
|
|
319
|
+
if (!p)
|
|
320
|
+
return "Error: path must not be empty";
|
|
282
321
|
if (fs.existsSync(p) && fs.statSync(p).isDirectory()) {
|
|
283
322
|
return `Error: ${pathName} is a directory`;
|
|
284
323
|
}
|
|
@@ -286,8 +325,9 @@ export function toolWriteFile(pathName, content) {
|
|
|
286
325
|
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
287
326
|
if (fs.existsSync(p)) {
|
|
288
327
|
const head = fs.readFileSync(p).subarray(0, 8192);
|
|
289
|
-
if (head.includes(0))
|
|
328
|
+
if (head.includes(0)) {
|
|
290
329
|
return `Error: refusing to overwrite binary file: ${pathName}`;
|
|
330
|
+
}
|
|
291
331
|
const oldContent = fs.readFileSync(p, "utf-8");
|
|
292
332
|
displayDiff(pathName, oldContent, content);
|
|
293
333
|
}
|
|
@@ -299,7 +339,7 @@ export function toolWriteFile(pathName, content) {
|
|
|
299
339
|
catch (err) {
|
|
300
340
|
return `Error: ${err}`;
|
|
301
341
|
}
|
|
302
|
-
return `Wrote ${content.length} characters to ${pathName}`;
|
|
342
|
+
return `Wrote ${content.length.toLocaleString()} characters to ${pathName}`;
|
|
303
343
|
}
|
|
304
344
|
// ---------------------------------------------------------------------------
|
|
305
345
|
// edit_file
|
|
@@ -322,9 +362,12 @@ function withNewlines(text, nl) {
|
|
|
322
362
|
return text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\n/g, "\r\n");
|
|
323
363
|
}
|
|
324
364
|
function findWsBlocks(text, needle) {
|
|
325
|
-
const needleLines = withNewlines(needle, "\n")
|
|
326
|
-
|
|
365
|
+
const needleLines = withNewlines(needle, "\n")
|
|
366
|
+
.split("\n")
|
|
367
|
+
.map((l) => l.trim());
|
|
368
|
+
while (needleLines.length && needleLines[needleLines.length - 1] === "") {
|
|
327
369
|
needleLines.pop();
|
|
370
|
+
}
|
|
328
371
|
if (!needleLines.length)
|
|
329
372
|
return [];
|
|
330
373
|
const textLines = text.split("\n");
|
|
@@ -350,7 +393,9 @@ function findWsBlocks(text, needle) {
|
|
|
350
393
|
return blocks;
|
|
351
394
|
}
|
|
352
395
|
export function toolEditFile(pathName, oldString, newString, replaceAll = false) {
|
|
353
|
-
const p = pathName;
|
|
396
|
+
const p = sanitizePath(pathName);
|
|
397
|
+
if (!p)
|
|
398
|
+
return "Error: path must not be empty";
|
|
354
399
|
if (!fs.existsSync(p))
|
|
355
400
|
return `Error: file not found: ${pathName}`;
|
|
356
401
|
if (!fs.statSync(p).isFile())
|
|
@@ -391,7 +436,7 @@ export function toolEditFile(pathName, oldString, newString, replaceAll = false)
|
|
|
391
436
|
}
|
|
392
437
|
const count = text.split(match).length - 1;
|
|
393
438
|
if (count > 1 && !replaceAll) {
|
|
394
|
-
return
|
|
439
|
+
return `Error: old_string matches ${count} locations; pass replace_all=true or make old_string more specific`;
|
|
395
440
|
}
|
|
396
441
|
const insert = withNewlines(newString, newline);
|
|
397
442
|
const newContent = replaceAll
|
|
@@ -412,7 +457,7 @@ export function toolEditFile(pathName, oldString, newString, replaceAll = false)
|
|
|
412
457
|
// ---------------------------------------------------------------------------
|
|
413
458
|
function terminateProcessTree(proc) {
|
|
414
459
|
try {
|
|
415
|
-
if (
|
|
460
|
+
if (process.platform === "win32") {
|
|
416
461
|
execFile("taskkill", ["/F", "/T", "/PID", String(proc.pid)], { timeout: 10000 });
|
|
417
462
|
}
|
|
418
463
|
else {
|
|
@@ -420,42 +465,43 @@ function terminateProcessTree(proc) {
|
|
|
420
465
|
process.kill(-proc.pid, "SIGKILL");
|
|
421
466
|
}
|
|
422
467
|
catch {
|
|
423
|
-
proc.kill();
|
|
468
|
+
proc.kill("SIGKILL");
|
|
424
469
|
}
|
|
425
470
|
}
|
|
426
471
|
}
|
|
427
472
|
catch {
|
|
428
473
|
try {
|
|
429
|
-
proc.kill();
|
|
474
|
+
proc.kill("SIGKILL");
|
|
430
475
|
}
|
|
431
476
|
catch {
|
|
432
477
|
/* ignore */
|
|
433
478
|
}
|
|
434
479
|
}
|
|
435
480
|
}
|
|
436
|
-
function isWindowsTool() {
|
|
437
|
-
return process.platform === "win32";
|
|
438
|
-
}
|
|
439
481
|
export function toolBash(command, timeout = 60, cwd) {
|
|
440
482
|
try {
|
|
441
483
|
let t = typeof timeout === "number" ? timeout : parseInt(String(timeout), 10);
|
|
442
484
|
if (Number.isNaN(t))
|
|
443
485
|
t = 60;
|
|
444
486
|
t = t > 0 ? Math.min(t, max_bash_timeout_seconds) : 60;
|
|
487
|
+
const workingDir = cwd ? sanitizePath(cwd) : undefined;
|
|
488
|
+
if (workingDir && !fs.existsSync(workingDir)) {
|
|
489
|
+
return Promise.resolve(`Error: working directory not found: ${cwd}`);
|
|
490
|
+
}
|
|
445
491
|
return new Promise((resolve) => {
|
|
446
492
|
let child;
|
|
447
493
|
try {
|
|
448
|
-
if (
|
|
494
|
+
if (process.platform === "win32") {
|
|
449
495
|
child = spawn(command, {
|
|
450
496
|
shell: true,
|
|
451
|
-
cwd:
|
|
452
|
-
windowsHide:
|
|
497
|
+
cwd: workingDir,
|
|
498
|
+
windowsHide: true,
|
|
453
499
|
});
|
|
454
500
|
}
|
|
455
501
|
else {
|
|
456
502
|
child = spawn(command, {
|
|
457
503
|
shell: true,
|
|
458
|
-
cwd:
|
|
504
|
+
cwd: workingDir,
|
|
459
505
|
detached: true,
|
|
460
506
|
});
|
|
461
507
|
}
|
|
@@ -470,7 +516,10 @@ export function toolBash(command, timeout = 60, cwd) {
|
|
|
470
516
|
const timer = setTimeout(() => {
|
|
471
517
|
if (finished)
|
|
472
518
|
return;
|
|
473
|
-
terminateProcessTree({
|
|
519
|
+
terminateProcessTree({
|
|
520
|
+
pid: child.pid,
|
|
521
|
+
kill: (sig) => child.kill(sig),
|
|
522
|
+
});
|
|
474
523
|
const output = (stdout + stderr).trim();
|
|
475
524
|
let msg = `Error: command timed out after ${t}s`;
|
|
476
525
|
if (output)
|
|
@@ -504,17 +553,6 @@ export function toolBash(command, timeout = 60, cwd) {
|
|
|
504
553
|
// ---------------------------------------------------------------------------
|
|
505
554
|
// glob / grep
|
|
506
555
|
// ---------------------------------------------------------------------------
|
|
507
|
-
function pathUnderIgnoredDir(base, f) {
|
|
508
|
-
const rel = path.relative(base, f);
|
|
509
|
-
if (rel.startsWith("..") || path.isAbsolute(rel))
|
|
510
|
-
return false;
|
|
511
|
-
const parts = rel.split(/[\\/]/);
|
|
512
|
-
for (let i = 0; i < parts.length - 1; i++) {
|
|
513
|
-
if (ignoredDirs.has(parts[i]))
|
|
514
|
-
return true;
|
|
515
|
-
}
|
|
516
|
-
return false;
|
|
517
|
-
}
|
|
518
556
|
function matchParts(parts, relParts) {
|
|
519
557
|
if (!parts.length)
|
|
520
558
|
return relParts.length === 0;
|
|
@@ -529,13 +567,12 @@ function matchParts(parts, relParts) {
|
|
|
529
567
|
globMatch(relParts[0], parts[0]) &&
|
|
530
568
|
matchParts(parts.slice(1), relParts.slice(1)));
|
|
531
569
|
}
|
|
532
|
-
// minimal glob matcher (supports * ? [..])
|
|
533
570
|
function globMatch(name, pattern) {
|
|
534
571
|
const regex = pattern
|
|
535
572
|
.replace(/[.+^${}()|[\]\\]/g, "\\$&")
|
|
536
573
|
.replace(/\*/g, ".*")
|
|
537
574
|
.replace(/\?/g, ".");
|
|
538
|
-
return new RegExp(`^${regex}
|
|
575
|
+
return new RegExp(`^${regex}$`, "i").test(name);
|
|
539
576
|
}
|
|
540
577
|
function walkFiles(root, current, rel, parts, out) {
|
|
541
578
|
let entries;
|
|
@@ -557,7 +594,10 @@ function walkFiles(root, current, rel, parts, out) {
|
|
|
557
594
|
}
|
|
558
595
|
else if (entry.isFile()) {
|
|
559
596
|
if (matchParts(parts, rel.concat(name))) {
|
|
560
|
-
out.push([
|
|
597
|
+
out.push([
|
|
598
|
+
path.join(root, ...rel, name),
|
|
599
|
+
fs.statSync(path.join(root, ...rel, name)).size,
|
|
600
|
+
]);
|
|
561
601
|
}
|
|
562
602
|
}
|
|
563
603
|
}
|
|
@@ -611,10 +651,10 @@ function rglobPruned(base, pattern) {
|
|
|
611
651
|
return out;
|
|
612
652
|
}
|
|
613
653
|
export function toolGlob(pattern, pathName = ".", limit = 200) {
|
|
614
|
-
const base = pathName;
|
|
654
|
+
const base = sanitizePath(pathName) || ".";
|
|
615
655
|
if (!fs.existsSync(base))
|
|
616
656
|
return `Error: path not found: ${pathName}`;
|
|
617
|
-
|
|
657
|
+
const lim = typeof limit === "number" ? limit : parseInt(String(limit), 10) || 200;
|
|
618
658
|
let matches;
|
|
619
659
|
try {
|
|
620
660
|
matches = rglobPruned(base, pattern);
|
|
@@ -626,24 +666,25 @@ export function toolGlob(pattern, pathName = ".", limit = 200) {
|
|
|
626
666
|
if (!matches.length)
|
|
627
667
|
return "No files matched";
|
|
628
668
|
const total = matches.length;
|
|
629
|
-
let result = matches.slice(0,
|
|
630
|
-
if (total >
|
|
631
|
-
result += `\n\n[... ${total -
|
|
669
|
+
let result = matches.slice(0, lim).map(([p]) => p).join("\n");
|
|
670
|
+
if (total > lim) {
|
|
671
|
+
result += `\n\n[... ${total - lim} more matches not shown ...]`;
|
|
632
672
|
}
|
|
633
673
|
return result;
|
|
634
674
|
}
|
|
635
675
|
export function toolGrep(pattern, pathName = ".", glob = "*", limit = 200) {
|
|
636
|
-
const base = pathName;
|
|
676
|
+
const base = sanitizePath(pathName) || ".";
|
|
637
677
|
if (!fs.existsSync(base))
|
|
638
678
|
return `Error: path not found: ${pathName}`;
|
|
639
679
|
let regex;
|
|
640
680
|
try {
|
|
641
|
-
regex = new RegExp(pattern);
|
|
681
|
+
regex = new RegExp(pattern, "i");
|
|
642
682
|
}
|
|
643
|
-
catch
|
|
644
|
-
|
|
683
|
+
catch {
|
|
684
|
+
regex = new RegExp(escapeRegExp(pattern), "i");
|
|
645
685
|
}
|
|
646
686
|
glob = glob || "*";
|
|
687
|
+
const lim = typeof limit === "number" ? limit : parseInt(String(limit), 10) || 200;
|
|
647
688
|
let targets;
|
|
648
689
|
if (fs.statSync(base).isFile()) {
|
|
649
690
|
targets =
|
|
@@ -670,7 +711,7 @@ export function toolGrep(pattern, pathName = ".", glob = "*", limit = 200) {
|
|
|
670
711
|
const line = capLine(lines[i]);
|
|
671
712
|
if (regex.test(line)) {
|
|
672
713
|
hits.push(`${fpath}:${i + 1}:${line}`);
|
|
673
|
-
if (hits.length >=
|
|
714
|
+
if (hits.length >= lim)
|
|
674
715
|
break;
|
|
675
716
|
}
|
|
676
717
|
}
|
|
@@ -678,7 +719,7 @@ export function toolGrep(pattern, pathName = ".", glob = "*", limit = 200) {
|
|
|
678
719
|
catch {
|
|
679
720
|
continue;
|
|
680
721
|
}
|
|
681
|
-
if (hits.length >=
|
|
722
|
+
if (hits.length >= lim)
|
|
682
723
|
break;
|
|
683
724
|
}
|
|
684
725
|
let result = hits.length ? hits.join("\n") : "No matches found";
|
|
@@ -710,7 +751,10 @@ export const RESPONSES_TOOLS = [
|
|
|
710
751
|
type: "object",
|
|
711
752
|
properties: {
|
|
712
753
|
path: { type: "string", description: "Path to the file" },
|
|
713
|
-
start_line: {
|
|
754
|
+
start_line: {
|
|
755
|
+
type: "integer",
|
|
756
|
+
description: "First line to read, 1-indexed (default 1)",
|
|
757
|
+
},
|
|
714
758
|
end_line: {
|
|
715
759
|
type: "integer",
|
|
716
760
|
description: `Last line to read, inclusive (default: end of file, capped at ${max_read_lines} lines from start_line)`,
|
|
@@ -784,8 +828,14 @@ export const RESPONSES_TOOLS = [
|
|
|
784
828
|
type: "object",
|
|
785
829
|
properties: {
|
|
786
830
|
pattern: { type: "string", description: "Glob pattern, e.g. '*.py'" },
|
|
787
|
-
path: {
|
|
788
|
-
|
|
831
|
+
path: {
|
|
832
|
+
type: "string",
|
|
833
|
+
description: "Directory to search from (default '.')",
|
|
834
|
+
},
|
|
835
|
+
limit: {
|
|
836
|
+
type: "integer",
|
|
837
|
+
description: "Max number of matches to return (default 200)",
|
|
838
|
+
},
|
|
789
839
|
},
|
|
790
840
|
required: ["pattern"],
|
|
791
841
|
},
|
|
@@ -798,13 +848,22 @@ export const RESPONSES_TOOLS = [
|
|
|
798
848
|
parameters: {
|
|
799
849
|
type: "object",
|
|
800
850
|
properties: {
|
|
801
|
-
pattern: {
|
|
802
|
-
|
|
851
|
+
pattern: {
|
|
852
|
+
type: "string",
|
|
853
|
+
description: "Regular expression to search for",
|
|
854
|
+
},
|
|
855
|
+
path: {
|
|
856
|
+
type: "string",
|
|
857
|
+
description: "Directory to search from (default '.')",
|
|
858
|
+
},
|
|
803
859
|
glob: {
|
|
804
860
|
type: "string",
|
|
805
861
|
description: "Only search files matching this glob (default '*')",
|
|
806
862
|
},
|
|
807
|
-
limit: {
|
|
863
|
+
limit: {
|
|
864
|
+
type: "integer",
|
|
865
|
+
description: "Max number of matches to return (default 200)",
|
|
866
|
+
},
|
|
808
867
|
},
|
|
809
868
|
required: ["pattern"],
|
|
810
869
|
},
|