mcp-repl
Version:
MCP REPL with code execution, semantic code search, and comprehensive ast-grep integration
83 lines (76 loc) • 4.13 kB
JavaScript
import { spawn } from 'child_process';
import * as path from 'node:path';
// Use CLI-only approach for maximum ARM64 compatibility
// NAPI removed to prevent double free corruption on ARM64 devices
const executeAstGrepCommand = async (args, workingDirectory, timeout = 30000) => {
const startTime = Date.now();
return new Promise((resolve) => {
const childProcess = spawn('ast-grep', args, {
cwd: workingDirectory, timeout, env: process.env, stdio: ['pipe', 'pipe', 'pipe']
});
let stdout = '', stderr = '';
childProcess.stdout.on('data', (data) => { stdout += data; });
childProcess.stderr.on('data', (data) => { stderr += data; });
childProcess.on('close', (code) => {
const executionTimeMs = Date.now() - startTime;
let parsedOutput = null, jsonError = null;
try {
if (stdout.trim()) parsedOutput = JSON.parse(stdout);
} catch (e) { jsonError = e.message; }
resolve({ success: code === 0, stdout: stdout.trim(), stderr: stderr.trim(),
exitCode: code, parsedOutput, jsonError, executionTimeMs,
command: `ast-grep ${args.join(' ')}` });
});
childProcess.on('error', (err) => {
let errorMessage = err.message;
if (err.code === 'ENOENT') {
errorMessage = `ast-grep CLI tool not found. For ARM64/aarch64 devices, install via:
• npm: npm install -g @ast-grep/cli
• pip: pip install ast-grep-cli
• cargo: cargo install ast-grep --locked
Or use other MCP tools: executenodejs, searchcode, batch_execute`;
}
resolve({ success: false, error: errorMessage, executionTimeMs: Date.now() - startTime,
command: `ast-grep ${args.join(' ')}` });
});
});
};
// CLI-only search function for maximum ARM64 compatibility
const astgrepSearchCli = async (pattern, language, paths, context, strictness, outputFormat, workingDirectory) => {
const args = ['run', '--json=compact'];
if (language) args.push('--lang', language);
args.push('--pattern', pattern);
if (context && context > 0) args.push('--context', context.toString());
if (strictness) args.push('--strictness', strictness);
args.push(...(paths && paths.length > 0 ? paths : ['.']));
const result = await executeAstGrepCommand(args, workingDirectory);
if (result.success && result.parsedOutput) {
const transformedResults = Array.isArray(result.parsedOutput)
? result.parsedOutput.map(match => ({
file: match.file, startLine: match.range?.start?.line || 0,
endLine: match.range?.end?.line || 0, text: match.text || match.lines,
metaVariables: match.metaVariables || {}, language: match.language, range: match.range
})) : [];
return { success: true, results: transformedResults, executionTimeMs: result.executionTimeMs,
totalMatches: transformedResults.length };
}
return { success: false, error: result.error || result.stderr || result.jsonError || 'Search failed',
executionTimeMs: result.executionTimeMs };
};
// CLI-only search function for ARM64 compatibility
export const astgrepSearch = async (pattern, language, paths, context, strictness, outputFormat, workingDirectory) => {
return astgrepSearchCli(pattern, language, paths, context, strictness, outputFormat, workingDirectory);
};
export const astgrepReplace = async (pattern, replacement, language, paths, dryRun, interactive, workingDirectory) => {
const args = ['run'];
if (dryRun) args.push('--dry-run');
if (interactive) args.push('--interactive');
args.push('--json=compact');
if (language) args.push('--lang', language);
args.push('--pattern', pattern, '--rewrite', replacement);
args.push(...(paths && paths.length > 0 ? paths : ['.']));
const result = await executeAstGrepCommand(args, workingDirectory);
return { success: result.success, results: result.parsedOutput || [],
executionTimeMs: result.executionTimeMs, dryRun,
error: result.success ? null : (result.error || result.stderr || 'Replace operation failed') };
};