UNPKG

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
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; } }