UNPKG

tree-ast-grep-mcp

Version:

Simple, direct ast-grep wrapper for AI coding agents. Zero abstractions, maximum performance.

136 lines 5.13 kB
import { ValidationError, ExecutionError } from '../types/errors.js'; /** * Direct search tool that calls ast-grep run with minimal overhead */ export class SearchTool { binaryManager; workspaceManager; constructor(binaryManager, workspaceManager) { this.binaryManager = binaryManager; this.workspaceManager = workspaceManager; } async execute(params) { // Basic validation - only what's absolutely necessary if (!params.pattern || typeof params.pattern !== 'string') { throw new ValidationError('Pattern is required'); } // Build ast-grep command directly const args = ['run', '--pattern', params.pattern.trim()]; // Add language if provided if (params.language) { args.push('--lang', params.language); } // Always use JSON stream for parsing args.push('--json=stream'); // Add context if requested if (params.context && params.context > 0) { args.push('--context', params.context.toString()); } // Handle inline code vs file paths let executeOptions = { cwd: this.workspaceManager.getWorkspaceRoot(), timeout: params.timeoutMs || 30000 }; if (params.code) { // Inline code mode args.push('--stdin'); if (!params.language) { throw new ValidationError('Language required for inline code'); } executeOptions.stdin = params.code; } else { // File mode - add paths (default to current directory) const paths = params.paths || ['.']; args.push(...paths); } try { const result = await this.binaryManager.executeAstGrep(args, executeOptions); return this.parseResults(result.stdout, params); } catch (error) { const message = error instanceof Error ? error.message : String(error); throw new ExecutionError(`Search failed: ${message}`); } } parseResults(stdout, params) { const matches = []; if (!stdout.trim()) { return { matches, summary: { totalMatches: 0, executionTime: 0 } }; } // Parse JSONL output const lines = stdout.trim().split('\n'); for (const line of lines) { if (!line.trim()) continue; try { const match = JSON.parse(line); matches.push({ file: match.file || '', line: (match.range?.start?.line || 0) + 1, // Convert to 1-based column: match.range?.start?.column || 0, text: match.text || '', context: { before: match.context?.before || [], after: match.context?.after || [] } }); } catch (e) { // Skip malformed lines } } return { matches: matches.slice(0, params.maxMatches || 100), summary: { totalMatches: matches.length, executionTime: 0 // We don't need precise timing } }; } static getSchema() { return { name: 'ast_search', description: '🚀 SIMPLIFIED: Direct ast-grep search with minimal overhead. Fast, reliable AST pattern matching.', inputSchema: { type: 'object', properties: { pattern: { type: 'string', description: 'AST pattern: console.log($ARG), function $NAME($PARAMS) { $$$ }, etc.' }, code: { type: 'string', description: 'Search inline code directly (recommended for testing)' }, paths: { type: 'array', items: { type: 'string' }, description: 'File paths to search (default: current directory)' }, language: { type: 'string', description: 'Language: javascript, typescript, python, java, etc.' }, context: { type: 'number', default: 3, description: 'Lines of context around matches' }, maxMatches: { type: 'number', default: 100, description: 'Maximum matches to return' }, timeoutMs: { type: 'number', default: 30000, description: 'Timeout in milliseconds' } }, required: ['pattern'] } }; } } //# sourceMappingURL=search.js.map