vibe-coder-mcp
Version:
Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.
347 lines (346 loc) • 15.1 kB
JavaScript
import { getNodeText } from '../astAnalyzer.js';
import logger from '../../../logger.js';
import { ContextTracker } from '../context/contextTracker.js';
export class BaseLanguageHandler {
contextTracker = new ContextTracker();
extractFunctions(rootNode, sourceCode, options = {}) {
this.contextTracker.clear();
const queryPatterns = this.getFunctionQueryPatterns();
const functions = [];
for (const pattern of queryPatterns) {
try {
rootNode.descendantsOfType(pattern).forEach(node => {
if (!options.isMethodExtraction && this.isNestedFunction(node)) {
return;
}
if (options.maxNestedFunctionDepth !== undefined &&
this.getNodeDepth(node) > options.maxNestedFunctionDepth) {
return;
}
const functionInfo = this.contextTracker.withContext('function', node, undefined, () => {
const name = this.extractFunctionName(node, sourceCode, options);
if (name) {
this.contextTracker.exitContext();
this.contextTracker.enterContext('function', node, name);
}
const signature = this.extractFunctionSignature(node, sourceCode);
const comment = this.extractFunctionComment(node, sourceCode) ||
this.generateHeuristicComment(name, options.isMethodExtraction ? 'method' : 'function', signature, options.className);
return {
name,
signature,
comment,
startLine: node.startPosition.row + 1,
endLine: node.endPosition.row + 1,
isAsync: this.isAsyncFunction(node, sourceCode),
isExported: this.isExportedFunction(node, sourceCode),
isMethod: options.isMethodExtraction || false,
isConstructor: name === 'constructor',
isGetter: name.startsWith('get') && name.length > 3,
isSetter: name.startsWith('set') && name.length > 3,
isGenerator: this.isGeneratorFunction ? this.isGeneratorFunction(node, sourceCode) : false,
isHook: name.startsWith('use') && name.length > 3 && name[3] === name[3].toUpperCase(),
isEventHandler: name.startsWith('handle') || name.startsWith('on'),
framework: this.detectFramework(sourceCode) || undefined,
class: options.className,
};
});
functions.push(functionInfo);
});
}
catch (error) {
logger.warn({ err: error, pattern }, `Error processing pattern ${pattern} for function extraction`);
}
}
return functions;
}
extractClasses(rootNode, sourceCode, options = {}) {
const classes = [];
const queryPatterns = this.getClassQueryPatterns();
for (const pattern of queryPatterns) {
try {
rootNode.descendantsOfType(pattern).forEach(node => {
if (!options.extractNestedClasses && this.isNestedClass(node)) {
return;
}
if (options.maxNestedClassDepth !== undefined &&
this.getNodeDepth(node) > options.maxNestedClassDepth) {
return;
}
const classInfo = this.contextTracker.withContext('class', node, undefined, () => {
const name = this.extractClassName(node, sourceCode);
if (name) {
this.contextTracker.exitContext();
this.contextTracker.enterContext('class', node, name);
}
const methods = options.extractMethods !== false ?
this.extractFunctions(node, sourceCode, { isMethodExtraction: true, className: name }) : [];
const properties = options.extractProperties !== false ?
this.extractClassProperties(node, sourceCode) : [];
const parentClass = this.extractParentClass(node, sourceCode);
const implementedInterfaces = this.extractImplementedInterfaces(node, sourceCode);
const comment = this.extractClassComment(node, sourceCode) ||
this.generateHeuristicComment(name, 'class');
return {
name,
methods,
properties,
parentClass,
implementedInterfaces,
comment,
startLine: node.startPosition.row + 1,
endLine: node.endPosition.row + 1,
isExported: this.isExportedClass(node, sourceCode),
};
});
classes.push(classInfo);
});
}
catch (error) {
logger.warn({ err: error, pattern }, `Error processing pattern ${pattern} for class extraction`);
}
}
return classes;
}
extractImports(rootNode, sourceCode, options = {}) {
const imports = [];
const queryPatterns = this.getImportQueryPatterns();
for (const pattern of queryPatterns) {
try {
rootNode.descendantsOfType(pattern).forEach(node => {
try {
const path = this.extractImportPath(node, sourceCode);
const importInfo = this.contextTracker.withContext('import', node, path, () => {
const importedItems = this.extractImportedItems(node, sourceCode);
const isDefault = this.isDefaultImport(node, sourceCode);
const alias = this.extractImportAlias(node, sourceCode);
const comment = options.extractComments ?
this.extractImportComment(node, sourceCode) : undefined;
return {
path,
importedItems,
isDefault,
alias,
comment,
startLine: node.startPosition.row + 1,
endLine: node.endPosition.row + 1,
};
});
imports.push(importInfo);
}
catch (error) {
logger.warn({ err: error, node: node.type }, `Error extracting import information`);
}
});
}
catch (error) {
logger.warn({ err: error, pattern }, `Error processing pattern ${pattern} for import extraction`);
}
}
return imports;
}
detectFramework(_sourceCode) {
return null;
}
extractFunctionSignature(node, sourceCode) {
const nameNode = node.childForFieldName('name');
const paramsNode = node.childForFieldName('parameters');
const name = nameNode ? getNodeText(nameNode, sourceCode) : 'anonymous';
const params = paramsNode ? getNodeText(paramsNode, sourceCode) : '()';
return `${name}${params}`;
}
extractFunctionComment(_node, _sourceCode) {
return undefined;
}
extractClassComment(_node, _sourceCode) {
return undefined;
}
extractImportComment(_node, _sourceCode) {
return undefined;
}
extractParentClass(_node, _sourceCode) {
return undefined;
}
extractImplementedInterfaces(_node, _sourceCode) {
return undefined;
}
extractImportedItems(_node, _sourceCode) {
return undefined;
}
isDefaultImport(_node, _sourceCode) {
return undefined;
}
extractImportAlias(_node, _sourceCode) {
return undefined;
}
generateHeuristicComment(name, type, signature, parentClass) {
if (type === 'function' || type === 'method') {
if (name.startsWith('get') && name.length > 3) {
const propertyName = name.charAt(3).toLowerCase() + name.slice(4);
return `Gets the ${propertyName}.`;
}
else if (name.startsWith('set') && name.length > 3) {
const propertyName = name.charAt(3).toLowerCase() + name.slice(4);
return `Sets the ${propertyName}.`;
}
else if (name.startsWith('is') && name.length > 2) {
const propertyName = name.charAt(2).toLowerCase() + name.slice(3);
return `Checks if ${propertyName}.`;
}
else if (name.startsWith('has') && name.length > 3) {
const propertyName = name.charAt(3).toLowerCase() + name.slice(4);
return `Checks if has ${propertyName}.`;
}
else if (name.startsWith('on') && name.length > 2) {
const eventName = name.charAt(2).toLowerCase() + name.slice(3);
return `Handles the ${eventName} event.`;
}
else if (name.includes('callback')) {
return `Callback function for handling an operation.`;
}
else if (name.includes('handler')) {
return `Handler function for processing an event or action.`;
}
else if (name === 'constructor') {
return parentClass ? `Creates a new instance of ${parentClass}.` : `Creates a new instance.`;
}
else {
return `Performs an action related to ${name}.`;
}
}
else if (type === 'class') {
return `Represents a ${name} object.`;
}
else if (type === 'property') {
return `The ${name} property.`;
}
else if (type === 'import') {
return `Imports from ${name}.`;
}
else if (type === 'file') {
return `Contains functionality related to ${name}.`;
}
return `Performs an action related to ${name}.`;
}
isAsyncFunction(node, _sourceCode) {
return node.text.startsWith('async ');
}
isGeneratorFunction(_node, _sourceCode) {
return false;
}
isExportedFunction(node, _sourceCode) {
return node.parent?.type === 'export_statement';
}
isExportedClass(node, _sourceCode) {
return node.parent?.type === 'export_statement';
}
isNestedFunction(node) {
let parent = node.parent;
while (parent) {
if (this.getFunctionQueryPatterns().includes(parent.type)) {
return true;
}
parent = parent.parent;
}
return false;
}
isNestedClass(node) {
let parent = node.parent;
while (parent) {
if (this.getClassQueryPatterns().includes(parent.type)) {
return true;
}
parent = parent.parent;
}
return false;
}
getNodeDepth(node) {
let depth = 0;
let parent = node.parent;
while (parent) {
depth++;
parent = parent.parent;
}
return depth;
}
getCurrentContext() {
const context = this.contextTracker.getCurrentContext();
if (!context)
return undefined;
return {
type: context.type,
name: context.name,
parent: context.parent ? {
type: context.parent.type,
name: context.parent.name,
parent: context.parent.parent
} : undefined
};
}
extractClassProperties(_node, _sourceCode) {
return [];
}
async enhanceImportInfo(filePath, imports, options) {
try {
const { ImportResolverFactory } = await import('../importResolvers/importResolverFactory.js');
const opts = options;
const factory = new ImportResolverFactory({
allowedDir: opts.allowedDir,
outputDir: opts.outputDir,
maxDepth: opts.maxDepth || 3,
tsConfig: opts.tsConfig,
pythonPath: opts.pythonPath,
pythonVersion: opts.pythonVersion,
venvPath: opts.venvPath,
clangdPath: opts.clangdPath,
compileFlags: opts.compileFlags,
includePaths: opts.includePaths,
semgrepPatterns: opts.semgrepPatterns,
semgrepTimeout: opts.semgrepTimeout,
semgrepMaxMemory: opts.semgrepMaxMemory,
disableSemgrepFallback: opts.disableSemgrepFallback
});
const resolver = factory.getImportResolver(filePath);
if (!resolver) {
return imports;
}
const enhancedImports = await resolver.analyzeImports(filePath, opts);
return this.mergeImportInfo(imports, enhancedImports);
}
catch (error) {
logger.error({ err: error, filePath }, 'Error enhancing import info in base handler');
return imports;
}
}
mergeImportInfo(original, enhanced) {
const result = [];
const originalImportMap = new Map();
for (const importInfo of original) {
originalImportMap.set(importInfo.path, importInfo);
}
for (const enhancedImport of enhanced) {
const originalImport = originalImportMap.get(enhancedImport.path);
if (originalImport) {
result.push({
...originalImport,
metadata: {
...originalImport.metadata,
...enhancedImport.metadata
},
isCore: enhancedImport.isCore,
isDynamic: enhancedImport.isDynamic,
isExternalPackage: enhancedImport.isExternalPackage,
moduleSystem: enhancedImport.moduleSystem || originalImport.moduleSystem
});
originalImportMap.delete(enhancedImport.path);
}
else {
result.push(enhancedImport);
}
}
for (const importInfo of originalImportMap.values()) {
result.push(importInfo);
}
return result;
}
}