import { spawnSync } from "node:child_process"; const virtual_chroot = '/workspaces/aiTools/root'; export default { type: "function", name: "ripgrep", strict: true, description: "ripgrep (rg) search ( rg pattern -g filePattern | head -200) , always limited to 200 lines", parameters: { type: "object", required: ["pattern","filePattern","n_flag","i_flag"], additionalProperties: false, properties: { pattern: {type: "string", description: 'The pattern to search for.'}, 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) { 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, pattern, and explicit search path '.' so rg scans the chroot cwd rgArgs.push('--', pattern, '.'); try { 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}`; } // Normalize paths (strip leading './') and limit to 200 lines const lines = output.split('\n').map((l) => l.replace(/^\.\//, '')); let limitedOutput = lines.slice(0, 200).join('\n'); // Remove a single trailing newline if present to align with tests if (limitedOutput.endsWith('\n')) { limitedOutput = limitedOutput.replace(/\n$/, ''); } return limitedOutput; } catch (error) { return `ripgrep error: ${error.message}`; } }