UNPKG

ultimate-mcp-server

Version:

The definitive all-in-one Model Context Protocol server for AI-assisted coding across 30+ platforms

161 lines 5.36 kB
/** * Base Context Extractor * Common functionality for all language-specific extractors */ import { Logger } from '../../utils/logger.js'; const logger = new Logger('ContextExtractor'); export class BaseContextExtractor { /** * Estimate token count for content */ estimateTokens(content) { // Simple estimation: ~4 characters per token return Math.ceil(content.length / 4); } /** * Extract context around a specific line */ extractAroundLine(content, targetLine, contextLines = 5) { const lines = content.split('\n'); const startLine = Math.max(1, targetLine - contextLines); const endLine = Math.min(lines.length, targetLine + contextLines); const contextContent = lines .slice(startLine - 1, endLine) .join('\n'); return { content: contextContent, startLine, endLine }; } /** * Extract function/method context */ extractFunctionContext(content, functionName, startLine, endLine) { const lines = content.split('\n'); const functionContent = lines.slice(startLine - 1, endLine).join('\n'); return { id: `${this.language}:function:${functionName}:${startLine}`, filePath: '', language: this.language, content: functionContent, startLine, endLine, type: 'function', metadata: { name: functionName, complexity: this.calculateComplexity(functionContent) } }; } /** * Extract class context */ extractClassContext(content, className, startLine, endLine, options) { const lines = content.split('\n'); let classContent = lines.slice(startLine - 1, endLine).join('\n'); // Optionally strip method implementations to save tokens if (options.maxTokens && this.estimateTokens(classContent) > options.maxTokens * 0.5) { classContent = this.stripMethodBodies(classContent); } return { id: `${this.language}:class:${className}:${startLine}`, filePath: '', language: this.language, content: classContent, startLine, endLine, type: 'class', metadata: { name: className } }; } /** * Calculate cyclomatic complexity */ calculateComplexity(code) { let complexity = 1; // Common control flow keywords const controlFlowPatterns = [ /\bif\b/g, /\belse\s+if\b/g, /\bwhile\b/g, /\bfor\b/g, /\bcase\b/g, /\bcatch\b/g, /\b\?\s*[^:]+\s*:/g, // ternary operator /\&\&/g, /\|\|/g ]; for (const pattern of controlFlowPatterns) { const matches = code.match(pattern); if (matches) { complexity += matches.length; } } return complexity; } /** * Strip method bodies to reduce token count */ stripMethodBodies(classContent) { // This is a simplified version - real implementation would use AST const lines = classContent.split('\n'); const result = []; let inMethod = false; let braceCount = 0; for (const line of lines) { if (inMethod) { if (line.includes('{')) braceCount++; if (line.includes('}')) braceCount--; if (braceCount === 0) { inMethod = false; result.push(' // ... implementation'); result.push(line); } } else { result.push(line); // Simple method detection if (line.match(/^\s*(public|private|protected|static|async)?\s*\w+\s*\([^)]*\)\s*{/) || line.match(/^\s*\w+\s*\([^)]*\)\s*{/)) { inMethod = true; braceCount = 1; } } } return result.join('\n'); } /** * Extract docstring/comments */ extractDocstring(content, startLine) { const lines = content.split('\n'); const commentLines = []; // Look backwards from startLine for comments for (let i = startLine - 2; i >= 0; i--) { const line = lines[i].trim(); if (line.startsWith('/**') || line.startsWith('/*') || line.startsWith('*') || line.startsWith('//') || line.startsWith('#')) { commentLines.unshift(line); } else if (line.length > 0) { break; } } return commentLines.length > 0 ? commentLines.join('\n') : undefined; } /** * Filter contexts based on relevance */ filterByRelevance(contexts, minRelevance = 0.5) { return contexts.filter(ctx => (ctx.relevanceScore || 1) >= minRelevance); } /** * Sort contexts by relevance */ sortByRelevance(contexts) { return contexts.sort((a, b) => (b.relevanceScore || 0) - (a.relevanceScore || 0)); } } //# sourceMappingURL=base.js.map