Refactor read_file and ripgrep tools to implement file reading and searching functionalities. The read_file tool now validates paths within a restricted directory and handles line skipping and reading limits. The ripgrep tool is implemented to perform pattern searches with optional flags for line numbers and case sensitivity, enhancing search capabilities and error handling.
This commit is contained in:
@@ -1,14 +1,58 @@
|
||||
import { createReadStream } from "node:fs";
|
||||
import { createInterface } from "node:readline";
|
||||
|
||||
import path from "node:path";
|
||||
|
||||
const virtual_chroot = '/home/seb/src/aiTools/tmp';
|
||||
|
||||
// Ensures reads are confined to `virtual_chroot`.
|
||||
|
||||
export default {
|
||||
type: "function", name: "read_file", description: "read a file", strict: true,
|
||||
parameters: {
|
||||
type: "object", required: ["path","linesToSkip","linesToRead"],additionalProperties: false, properties: {
|
||||
path: { type: "string", description: "The path to the file to read.",},
|
||||
linesToSkip: { type: "number", description: "The number of lines to skip. Use 0 to read from the beginning.",},
|
||||
linesToRead: { type: "number",description: "1-400 The number of lines to read. Use 0 or more than 400 to read 400 lines.",}
|
||||
},},};
|
||||
type: "object", required: ["path","linesToSkip","linesToRead"], additionalProperties: false, properties: {
|
||||
path: { type: "string", description: "The path to the file to read.", },
|
||||
linesToSkip: { type: "integer", description: "The number of lines to skip. Use 0 to read from the beginning.", minimum: 0 },
|
||||
linesToRead: { type: "integer", description: "1-400 The number of lines to read. Use 0 or more than 400 to read 400 lines.", minimum: 0 }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export async function run() {
|
||||
return `read_file error (not implemented)`
|
||||
}
|
||||
export async function run(args) {
|
||||
const { path: filePath, linesToSkip, linesToRead } = args;
|
||||
|
||||
// Validate path is within virtual_chroot
|
||||
const fullPath = path.resolve(virtual_chroot, filePath.replace(/^\//, ''));
|
||||
if (!fullPath.startsWith(virtual_chroot)) {
|
||||
return `read_file error: Path outside of allowed directory`;
|
||||
}
|
||||
|
||||
// Normalize linesToRead (1-400, with 0 or >400 meaning 400)
|
||||
const maxLines = (linesToRead <= 0 || linesToRead > 400) ? 400 : linesToRead;
|
||||
|
||||
try {
|
||||
|
||||
const rl = createInterface({
|
||||
input: createReadStream(fullPath),
|
||||
crlfDelay: Infinity
|
||||
});
|
||||
|
||||
let lines = [];
|
||||
let skipped = 0;
|
||||
|
||||
for await (const line of rl) {
|
||||
if (skipped < linesToSkip) {
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
lines.push(line);
|
||||
if (lines.length >= maxLines) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return lines.join('\n');
|
||||
} catch (error) {
|
||||
return `read_file error: ${error.message}`;
|
||||
}
|
||||
}
|
||||
@@ -9,8 +9,53 @@ export default {
|
||||
filePattern: { type: "string", description: "'*.js' for only js files, '!*.log' for all files except log files , '' for all files"},
|
||||
n_flag: { type: "boolean",description: "show line numbers."},
|
||||
i_flag: { type: "boolean",description: "case insensitive search."}
|
||||
},},};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export async function run(args) {
|
||||
return `ripgrep error (not implemented)`
|
||||
const { pattern, filePattern, n_flag, i_flag } = args;
|
||||
|
||||
// Build the args array
|
||||
let rgArgs = [];
|
||||
|
||||
if (n_flag) rgArgs.push('-n');
|
||||
if (i_flag) rgArgs.push('-i');
|
||||
if (filePattern) {
|
||||
rgArgs.push('-g', filePattern);
|
||||
}
|
||||
|
||||
// Add separator and pattern
|
||||
rgArgs.push('--', pattern);
|
||||
|
||||
try {
|
||||
const { spawnSync } = require('child_process');
|
||||
const proc = spawnSync('rg', rgArgs, {
|
||||
cwd: virtual_chroot,
|
||||
encoding: 'utf8',
|
||||
maxBuffer: 1024 * 1024 * 10 // 10MB buffer
|
||||
});
|
||||
|
||||
if (proc.error) {
|
||||
return `ripgrep error: ${proc.error.message}`;
|
||||
}
|
||||
|
||||
let output = proc.stdout;
|
||||
|
||||
if (proc.status !== 0) {
|
||||
if (proc.status === 1) {
|
||||
// No matches found
|
||||
return output || '';
|
||||
}
|
||||
return `ripgrep error: exit ${proc.status}, ${proc.stderr}`;
|
||||
}
|
||||
|
||||
// Limit to 200 lines
|
||||
const lines = output.split('\n');
|
||||
const limitedOutput = lines.slice(0, 200).join('\n');
|
||||
|
||||
return limitedOutput;
|
||||
} catch (error) {
|
||||
return `ripgrep error: ${error.message}`;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user