arcadiasuite/server/autonomous/tools/filesystem/SearchCodeTool.ts

93 lines
3.2 KiB
TypeScript

/**
* Arcadia Suite - Search Code Tool
*/
import { BaseTool, type ToolParameter, type ToolResult } from "../BaseTool";
import * as fs from "fs/promises";
import * as path from "path";
const SEARCH_DIRS = ['client/src', 'server', 'shared'];
const SEARCH_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.json', '.css', '.html'];
const BLOCKED_DIRS = ['node_modules', '.git', 'dist', 'build'];
export class SearchCodeTool extends BaseTool {
name = "search_code";
description = "Busca por texto ou padrão no código do projeto";
category = "Filesystem";
parameters: ToolParameter[] = [
{ name: "query", type: "string", description: "Texto ou regex a buscar", required: true },
{ name: "path", type: "string", description: "Diretório para buscar (opcional)", required: false },
{ name: "filePattern", type: "string", description: "Padrão de arquivo (ex: *.tsx)", required: false },
{ name: "maxResults", type: "number", description: "Máximo de resultados", required: false, default: 50 },
];
async execute(params: Record<string, any>): Promise<ToolResult> {
const query = params.query as string;
const searchPath = params.path as string || "";
const maxResults = params.maxResults as number || 50;
try {
const regex = new RegExp(query, "gi");
const results: any[] = [];
const dirsToSearch = searchPath ? [searchPath] : SEARCH_DIRS;
for (const dir of dirsToSearch) {
await this.searchInDir(path.resolve(process.cwd(), dir), regex, results, maxResults);
if (results.length >= maxResults) break;
}
return this.formatSuccess(`Encontrados ${results.length} resultados para "${query}"`, {
query,
results: results.slice(0, maxResults),
totalFound: results.length,
});
} catch (error: any) {
return this.formatError(`Erro na busca: ${error.message}`);
}
}
private async searchInDir(dirPath: string, regex: RegExp, results: any[], maxResults: number): Promise<void> {
if (results.length >= maxResults) return;
try {
const entries = await fs.readdir(dirPath, { withFileTypes: true });
for (const entry of entries) {
if (results.length >= maxResults) return;
if (BLOCKED_DIRS.includes(entry.name)) continue;
const fullPath = path.join(dirPath, entry.name);
if (entry.isDirectory()) {
await this.searchInDir(fullPath, regex, results, maxResults);
} else {
const ext = path.extname(entry.name).toLowerCase();
if (!SEARCH_EXTENSIONS.includes(ext)) continue;
try {
const content = await fs.readFile(fullPath, "utf-8");
const lines = content.split("\n");
for (let i = 0; i < lines.length; i++) {
if (results.length >= maxResults) return;
if (regex.test(lines[i])) {
results.push({
file: path.relative(process.cwd(), fullPath),
line: i + 1,
content: lines[i].trim().slice(0, 200),
});
}
}
} catch {
// Skip unreadable files
}
}
}
} catch {
// Skip inaccessible directories
}
}
}