UNPKG

@aaswe/codebase-ai

Version:

AI-Assisted Software Engineering (AASWE) - Rich codebase context for IDE LLMs

925 lines 36 kB
"use strict"; /** * TTL Context Loader for MCP Server * * Enhanced TTL context loading service that integrates with the automatic * analysis system to provide rich, concrete codebase context to LLMs. */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TTLContextLoader = void 0; const promises_1 = require("fs/promises"); const chokidar_1 = require("chokidar"); const glob_1 = require("glob"); const path_1 = require("path"); const crypto_1 = require("crypto"); const events_1 = require("events"); const logger_1 = __importDefault(require("../../utils/logger")); /** * Enhanced TTL Context Loader * * Provides intelligent context loading from TTL files generated by * the automatic analysis system, with concrete code information * and business context preservation. */ class TTLContextLoader extends events_1.EventEmitter { config; _knowledgeGraphPopulator; _rdfGenerator; _informationExtractor; ttlFiles = new Map(); contextCache = new Map(); fileWatcher = null; loadingQueue = new Set(); metrics; cacheCleanupInterval; constructor(config, knowledgeGraphPopulator, rdfGenerator, informationExtractor) { super(); this.config = config; this._knowledgeGraphPopulator = knowledgeGraphPopulator; this._rdfGenerator = rdfGenerator; this._informationExtractor = informationExtractor; this.metrics = this.initializeMetrics(); logger_1.default.info('TTLContextLoader initialized', { watchEnabled: config.watchEnabled, cacheEnabled: config.cacheEnabled, maxCacheSize: config.maxCacheSize, hasKnowledgeGraphPopulator: !!this._knowledgeGraphPopulator, hasRDFGenerator: !!this._rdfGenerator, hasInformationExtractor: !!this._informationExtractor }); } /** * Initialize metrics */ initializeMetrics() { return { ttlFiles: { total: 0, loaded: 0, failed: 0, watching: 0 }, context: { requests: 0, cacheHits: 0, cacheMisses: 0, averageResponseTime: 0, averageRelevanceScore: 0 }, performance: { memoryUsage: 0, loadingQueueSize: 0, cacheSize: 0, lastCleanup: new Date() } }; } /** * Start the TTL context loader */ async start() { try { logger_1.default.info('Starting TTL context loader...'); // Initialize file watching if enabled if (this.config.watchEnabled) { await this.initializeFileWatcher(); } // Load existing TTL files await this.loadAllTTLFiles(); // Start cache cleanup if enabled if (this.config.cacheEnabled) { this.startCacheCleanup(); } logger_1.default.info('TTL context loader started', { ttlFiles: this.ttlFiles.size, watching: this.config.watchEnabled }); } catch (error) { logger_1.default.error('Failed to start TTL context loader', { error }); throw error; } } /** * Stop the TTL context loader */ async stop() { try { logger_1.default.info('Stopping TTL context loader...'); // Stop file watcher if (this.fileWatcher) { await this.fileWatcher.close(); this.fileWatcher = null; } // Stop cache cleanup if (this.cacheCleanupInterval) { clearInterval(this.cacheCleanupInterval); this.cacheCleanupInterval = undefined; } // Clear caches this.contextCache.clear(); this.loadingQueue.clear(); logger_1.default.info('TTL context loader stopped'); } catch (error) { logger_1.default.error('Failed to stop TTL context loader', { error }); throw error; } } /** * Load context for a specific request */ async loadContext(request) { const startTime = Date.now(); this.metrics.context.requests++; try { // Check cache first if (this.config.cacheEnabled) { const cached = this.getCachedContext(request); if (cached) { this.metrics.context.cacheHits++; return cached; } this.metrics.context.cacheMisses++; } // Select relevant TTL files const selection = await this.selectRelevantTTLFiles(request); // Load concrete context from selected files const sources = await this.loadConcreteSources(selection, request); // Generate enhanced context with business insights const context = await this.generateEnhancedContext(sources, request); // Build response const processingTime = Date.now() - startTime; const response = { context: context.formattedContext, sources, metadata: { totalTokens: this.estimateTokens(context.formattedContext), processingTime, relevanceScore: this.calculateAverageRelevance(sources), cached: false }, suggestions: { relatedFiles: selection.alternatives[0]?.files || [], followUpQueries: context.followUpQueries, improvements: context.improvements } }; // Cache the response if (this.config.cacheEnabled) { this.cacheContext(request, response); } // Update metrics this.updateMetrics(processingTime, response.metadata.relevanceScore); return response; } catch (error) { logger_1.default.error('Failed to load TTL context', { request, error }); throw error; } } /** * Get all loaded TTL files */ getTTLFiles() { return new Map(this.ttlFiles); } /** * Get TTL file by path */ getTTLFile(path) { return this.ttlFiles.get(path); } /** * Refresh TTL file from disk */ async refreshTTLFile(path) { try { await this.loadTTLFile(path); this.clearRelatedCache(path); this.emit('ttl_file_refreshed', { path, timestamp: Date.now() }); } catch (error) { logger_1.default.error('Failed to refresh TTL file', { path, error }); throw error; } } /** * Get loader metrics */ getMetrics() { this.metrics.performance.cacheSize = this.contextCache.size; this.metrics.performance.loadingQueueSize = this.loadingQueue.size; this.metrics.performance.memoryUsage = process.memoryUsage().heapUsed; return { ...this.metrics }; } /** * Initialize file watcher */ async initializeFileWatcher() { const patterns = this.config.watchPatterns || ['**/*.module-knowledge.ttl']; this.fileWatcher = (0, chokidar_1.watch)(patterns, { ignored: this.config.watchIgnored || [/node_modules/, /.git/, /dist/, /build/], persistent: true, ignoreInitial: true, awaitWriteFinish: { stabilityThreshold: this.config.watchDebounce || 1000, pollInterval: 100 } }); this.fileWatcher.on('add', (path) => this.handleFileEvent('created', path)); this.fileWatcher.on('change', (path) => this.handleFileEvent('modified', path)); this.fileWatcher.on('unlink', (path) => this.handleFileEvent('deleted', path)); logger_1.default.info('TTL file watcher initialized', { patterns }); } /** * Handle file system events */ async handleFileEvent(type, path) { try { const event = { type, path, timestamp: Date.now() }; switch (type) { case 'created': case 'modified': await this.loadTTLFile(path); this.clearRelatedCache(path); break; case 'deleted': this.ttlFiles.delete(path); this.clearRelatedCache(path); this.metrics.ttlFiles.total = Math.max(0, this.metrics.ttlFiles.total - 1); break; } this.emit('ttl_file_changed', event); } catch (error) { logger_1.default.error('Failed to handle TTL file event', { type, path, error }); } } /** * Load all TTL files from the project */ async loadAllTTLFiles() { try { const patterns = this.config.loadPatterns || ['**/*.module-knowledge.ttl']; const ttlFiles = []; for (const pattern of patterns) { const files = await (0, glob_1.glob)(pattern, { ignore: this.config.loadIgnored || ['node_modules/**', '.git/**', 'dist/**', 'build/**'], absolute: true }); ttlFiles.push(...files); } logger_1.default.info(`Found ${ttlFiles.length} TTL files to load`); // Load files in parallel with concurrency limit const concurrency = this.config.loadConcurrency || 5; const chunks = this.chunkArray(ttlFiles, concurrency); for (const chunk of chunks) { const loadPromises = chunk.map(filePath => this.loadTTLFile(filePath)); await Promise.allSettled(loadPromises); } this.metrics.ttlFiles.total = ttlFiles.length; this.metrics.ttlFiles.loaded = this.ttlFiles.size; this.metrics.ttlFiles.failed = ttlFiles.length - this.ttlFiles.size; logger_1.default.info(`Successfully loaded ${this.ttlFiles.size}/${ttlFiles.length} TTL files`); } catch (error) { logger_1.default.error('Failed to load TTL files', { error }); throw error; } } /** * Load a single TTL file */ async loadTTLFile(path) { if (this.loadingQueue.has(path)) { return; // Already loading } this.loadingQueue.add(path); try { // Check if file exists and is readable await (0, promises_1.access)(path); const content = await (0, promises_1.readFile)(path, 'utf-8'); const stats = await (0, promises_1.stat)(path); const hash = (0, crypto_1.createHash)('md5').update(content).digest('hex'); // Check if file has changed const existing = this.ttlFiles.get(path); if (existing && existing.hash === hash) { return; // No changes } // Extract concrete information and metadata const metadata = await this.extractTTLMetadata(content, path); const concreteContext = await this.extractConcreteContext(content, path); const ttlFile = { path, content, lastModified: stats.mtime, size: stats.size, hash, parsed: this.parseTTLContent(content), metadata: { ...metadata, concreteContext, loadedAt: new Date(), version: this.extractVersion(content) } }; this.ttlFiles.set(path, ttlFile); logger_1.default.debug('TTL file loaded', { path: (0, path_1.relative)(process.cwd(), path), size: stats.size, module: metadata.module, language: metadata.language }); } catch (error) { logger_1.default.error('Failed to load TTL file', { path, error }); this.metrics.ttlFiles.failed++; } finally { this.loadingQueue.delete(path); } } /** * Extract enhanced TTL metadata */ async extractTTLMetadata(content, path) { const lines = content.split('\n'); const comments = lines.filter(line => line.trim().startsWith('#')); // Extract basic metadata const module = this.extractModuleName(path); const language = this.extractLanguage(path, content); const dependencies = this.extractDependencies(content); const businessContext = this.extractBusinessContext(comments); // Extract architectural patterns const architecturalPatterns = this.extractArchitecturalPatterns(content); // Extract quality metrics const qualityMetrics = this.extractQualityMetrics(content); return { module, language, dependencies, businessContext, architecturalPatterns, qualityMetrics, extractedAt: new Date() }; } /** * Extract concrete code context */ async extractConcreteContext(content, path) { try { // Parse TTL content to extract concrete information const classes = this.extractClasses(content); const methods = this.extractMethods(content); const properties = this.extractProperties(content); const relationships = this.extractRelationships(content); const imports = this.extractImports(content); return { classes, methods, properties, relationships, imports, codeStructure: this.extractCodeStructure(content), businessDomain: this.extractBusinessDomain(content), extractedAt: new Date() }; } catch (error) { logger_1.default.error('Failed to extract concrete context', { path, error }); return { classes: [], methods: [], properties: [], relationships: [], imports: [], codeStructure: {}, businessDomain: {}, extractedAt: new Date() }; } } /** * Select relevant TTL files for context request */ async selectRelevantTTLFiles(request) { const relevanceScores = {}; const selectedFiles = []; const requestDir = (0, path_1.dirname)(request.filePath); const requestLanguage = this.detectLanguageFromPath(request.filePath); for (const [filePath, ttlFile] of this.ttlFiles) { let score = 0; // File proximity scoring (higher weight) const fileDir = (0, path_1.dirname)(filePath); if (fileDir === requestDir) score += 0.4; else if (fileDir.startsWith(requestDir) || requestDir.startsWith(fileDir)) score += 0.25; // Language matching (high weight) if (ttlFile.metadata.language === requestLanguage) score += 0.3; // Query matching (if provided) if (request.query) { const queryLower = request.query.toLowerCase(); if (ttlFile.content.toLowerCase().includes(queryLower)) score += 0.2; if (ttlFile.metadata.businessContext.some(ctx => ctx.toLowerCase().includes(queryLower))) score += 0.15; } // Intent-based scoring if (request.intent) { score += this.calculateIntentScore(request.intent, ttlFile); } // Dependency matching if (ttlFile.metadata.dependencies.some(dep => request.filePath.includes(dep) || dep.includes(request.filePath))) { score += 0.15; } // Concrete context relevance if (ttlFile.metadata.concreteContext) { score += this.calculateConcreteContextScore(request, ttlFile.metadata.concreteContext); } relevanceScores[filePath] = score; if (score > this.config.relevanceThreshold) { selectedFiles.push(filePath); } } // Sort by relevance and limit selectedFiles.sort((a, b) => relevanceScores[b] - relevanceScores[a]); const limitedFiles = selectedFiles.slice(0, this.config.maxFiles); return { selectedFiles: limitedFiles, relevanceScores, reasoning: `Selected ${limitedFiles.length} files based on proximity (40%), language (30%), query matching (20%), and concrete context (10%)`, alternatives: [{ files: selectedFiles.slice(this.config.maxFiles), score: 0.5, reason: 'Alternative files with lower relevance scores' }] }; } /** * Load concrete sources from selected files */ async loadConcreteSources(selection, request) { const sources = []; let totalTokens = 0; for (const filePath of selection.selectedFiles) { if (totalTokens >= (request.maxTokens || this.config.maxTokens)) break; const ttlFile = this.ttlFiles.get(filePath); if (!ttlFile) continue; // Format TTL content for LLM consumption const formattedContent = this.formatTTLForLLM(ttlFile, request); const tokenCount = this.estimateTokens(formattedContent); if (totalTokens + tokenCount <= (request.maxTokens || this.config.maxTokens)) { const source = { type: 'ttl', path: filePath, content: formattedContent, relevanceScore: selection.relevanceScores[filePath] || 0, metadata: { lastModified: ttlFile.lastModified, size: ttlFile.size, language: ttlFile.metadata.language, module: ttlFile.metadata.module } }; sources.push(source); totalTokens += tokenCount; } } return sources; } /** * Generate enhanced context with business insights */ async generateEnhancedContext(sources, request) { const contextLines = [ '# Enhanced Codebase Context', `# Current File: ${request.filePath}`, `# Cursor Position: Line ${request.cursorPosition.line}, Column ${request.cursorPosition.column}`, '' ]; if (request.query) { contextLines.push(`# Query: ${request.query}`, ''); } if (request.intent) { contextLines.push(`# Intent: ${request.intent}`, ''); } contextLines.push('# Concrete Knowledge Sources:'); // Add sources with enhanced formatting for (const source of sources) { const ttlFile = this.ttlFiles.get(source.path); if (!ttlFile) continue; contextLines.push('', `## ${source.metadata.module} (${source.metadata.language?.toUpperCase()})`, `Relevance: ${(source.relevanceScore * 100).toFixed(1)}%`, `Path: ${(0, path_1.relative)(process.cwd(), source.path)}`, `Last Modified: ${source.metadata.lastModified.toISOString()}`, ''); // Add concrete context summary if (ttlFile.metadata.concreteContext) { contextLines.push('### Concrete Code Structure:', `Classes: ${ttlFile.metadata.concreteContext.classes.length}`, `Methods: ${ttlFile.metadata.concreteContext.methods.length}`, `Properties: ${ttlFile.metadata.concreteContext.properties.length}`, ''); } // Add architectural patterns if (ttlFile.metadata.architecturalPatterns?.length) { contextLines.push('### Architectural Patterns:', ...ttlFile.metadata.architecturalPatterns.map(pattern => `- ${pattern}`), ''); } contextLines.push(source.content); } // Generate follow-up queries const followUpQueries = this.generateFollowUpQueries(request, sources); // Generate improvements const improvements = this.generateImprovements(sources); return { formattedContext: contextLines.join('\n'), followUpQueries, improvements }; } /** * Format TTL file for LLM consumption */ formatTTLForLLM(ttlFile, _request) { const lines = [ `# Module: ${ttlFile.metadata.module}`, `# Language: ${ttlFile.metadata.language}`, `# Dependencies: ${ttlFile.metadata.dependencies.join(', ')}`, `# Last Modified: ${ttlFile.lastModified.toISOString()}`, '' ]; // Add concrete context if available if (ttlFile.metadata.concreteContext) { lines.push('# Concrete Code Elements:', `# Classes: ${ttlFile.metadata.concreteContext.classes.map(c => c.name).join(', ')}`, `# Key Methods: ${ttlFile.metadata.concreteContext.methods.slice(0, 5).map(m => m.name).join(', ')}`, ''); } // Add business context if (ttlFile.metadata.businessContext.length > 0) { lines.push('# Business Context:', ...ttlFile.metadata.businessContext.map(ctx => `# ${ctx}`), ''); } // Add the actual TTL content lines.push('# Knowledge Content:', ttlFile.content); return lines.join('\n'); } // Helper methods for extraction and processing extractClasses(content) { const classes = []; const lines = content.split('\n'); for (const line of lines) { // Look for class definitions in TTL const classMatch = line.match(/:(\w+)\s+a\s+:Class/); if (classMatch) { classes.push({ name: classMatch[1], type: 'class', properties: [] }); } } return classes; } extractMethods(content) { const methods = []; const lines = content.split('\n'); for (const line of lines) { // Look for method definitions in TTL const methodMatch = line.match(/:(\w+)\s+a\s+:Method/); if (methodMatch) { methods.push({ name: methodMatch[1], signature: '', returnType: 'unknown' }); } } return methods; } extractProperties(content) { const properties = []; const lines = content.split('\n'); for (const line of lines) { // Look for property definitions in TTL const propMatch = line.match(/:(\w+)\s+a\s+:Property/); if (propMatch) { properties.push({ name: propMatch[1], type: 'unknown', access: 'public' }); } } return properties; } extractRelationships(content) { const relationships = []; const lines = content.split('\n'); for (const line of lines) { // Look for relationships in TTL const relMatch = line.match(/:(\w+)\s+:(\w+)\s+:(\w+)/); if (relMatch) { relationships.push({ from: relMatch[1], to: relMatch[3], type: relMatch[2] }); } } return relationships; } extractImports(content) { const imports = []; const lines = content.split('\n'); for (const line of lines) { // Look for import statements in TTL comments if (line.includes('import') || line.includes('require')) { const importMatch = line.match(/["']([^"']+)["']/); if (importMatch) { imports.push(importMatch[1]); } } } return imports; } extractCodeStructure(content) { return { complexity: this.calculateComplexity(content), patterns: this.extractArchitecturalPatterns(content), metrics: this.extractQualityMetrics(content) }; } extractBusinessDomain(content) { const domain = {}; const lines = content.split('\n'); for (const line of lines) { if (line.includes('domain:') || line.includes('business:')) { const match = line.match(/(?:domain|business):\s*(.+)/i); if (match) { domain.primary = match[1].trim(); } } } return domain; } extractArchitecturalPatterns(content) { const patterns = []; const lowerContent = content.toLowerCase(); const patternKeywords = [ 'singleton', 'factory', 'observer', 'strategy', 'decorator', 'mvc', 'mvp', 'mvvm', 'repository', 'service', 'dao', 'microservice', 'monolith', 'layered', 'hexagonal' ]; for (const pattern of patternKeywords) { if (lowerContent.includes(pattern)) { patterns.push(pattern); } } return patterns; } extractQualityMetrics(content) { return { complexity: this.calculateComplexity(content), maintainability: this.calculateMaintainability(content), testability: this.calculateTestability(content) }; } calculateComplexity(content) { // Simple complexity calculation based on content length and structure const lines = content.split('\n').filter(line => line.trim().length > 0); return Math.min(10, Math.floor(lines.length / 10)); } calculateMaintainability(content) { // Simple maintainability score const hasComments = content.includes('#'); const hasStructure = content.includes(':'); return (hasComments ? 5 : 0) + (hasStructure ? 5 : 0); } calculateTestability(content) { // Simple testability score const hasTests = content.toLowerCase().includes('test'); const hasMocks = content.toLowerCase().includes('mock'); return (hasTests ? 5 : 0) + (hasMocks ? 5 : 0); } extractModuleName(path) { const parts = path.split('/'); const fileName = parts[parts.length - 1]; return fileName.replace('.module-knowledge.ttl', '') || 'unknown'; } extractLanguage(path, content) { // Check path first if (path.includes('/typescript/') || path.includes('.ts')) return 'typescript'; if (path.includes('/javascript/') || path.includes('.js')) return 'javascript'; if (path.includes('/python/') || path.includes('.py')) return 'python'; if (path.includes('/java/') || path.includes('.java')) return 'java'; // Check content for language hints const lowerContent = content.toLowerCase(); if (lowerContent.includes('typescript')) return 'typescript'; if (lowerContent.includes('javascript')) return 'javascript'; if (lowerContent.includes('python')) return 'python'; if (lowerContent.includes('java') && !lowerContent.includes('javascript')) return 'java'; return 'unknown'; } extractDependencies(content) { const dependencies = []; const lines = content.split('\n'); for (const line of lines) { if (line.includes('imports:') || line.includes('depends:') || line.includes('Dependencies:')) { const quotedMatches = line.match(/["']([^"']+)["']/g); if (quotedMatches) { quotedMatches.forEach(match => { const dep = match.replace(/["']/g, ''); if (dep && !dependencies.includes(dep)) { dependencies.push(dep); } }); } } } return dependencies; } extractBusinessContext(comments) { return comments .map(c => c.replace('#', '').trim()) .filter(c => c.length > 0 && !c.startsWith('Module:') && !c.startsWith('Language:')); } extractVersion(content) { const versionMatch = content.match(/version:\s*["']?([^"'\s]+)["']?/i); return versionMatch ? versionMatch[1] : '1.0.0'; } parseTTLContent(_content) { // Simplified TTL parsing return { triples: [], prefixes: {}, classes: [], properties: [], individuals: [] }; } detectLanguageFromPath(filePath) { if (filePath.endsWith('.ts') || filePath.includes('/typescript/')) return 'typescript'; if (filePath.endsWith('.js') || filePath.includes('/javascript/')) return 'javascript'; if (filePath.endsWith('.py') || filePath.includes('/python/')) return 'python'; if (filePath.endsWith('.java') || filePath.includes('/java/')) return 'java'; return 'unknown'; } calculateIntentScore(intent, ttlFile) { const content = ttlFile.content.toLowerCase(); switch (intent) { case 'debugging': return content.includes('error') || content.includes('exception') ? 0.2 : 0; case 'refactoring': return content.includes('pattern') || content.includes('architecture') ? 0.15 : 0; case 'documentation': return content.includes('comment') || content.includes('doc') ? 0.1 : 0; default: return 0; } } calculateConcreteContextScore(request, concreteContext) { let score = 0; if (request.query) { const queryLower = request.query.toLowerCase(); // Check if query matches class names if (concreteContext.classes.some(c => c.name.toLowerCase().includes(queryLower))) { score += 0.15; } // Check if query matches method names if (concreteContext.methods.some(m => m.name.toLowerCase().includes(queryLower))) { score += 0.1; } } return score; } estimateTokens(text) { // Simple token estimation (roughly 4 characters per token) return Math.ceil(text.length / 4); } calculateAverageRelevance(sources) { if (sources.length === 0) return 0; const total = sources.reduce((sum, source) => sum + source.relevanceScore, 0); return total / sources.length; } getCachedContext(request) { const cacheKey = this.generateCacheKey(request); const cached = this.contextCache.get(cacheKey); if (!cached) return null; const now = Date.now(); if (now - cached.timestamp > cached.ttl) { this.contextCache.delete(cacheKey); return null; } cached.hits++; cached.context.metadata.cached = true; return cached.context; } cacheContext(request, response) { const cacheKey = this.generateCacheKey(request); const cached = { key: cacheKey, context: response, timestamp: Date.now(), ttl: this.config.cacheTtl, hits: 0 }; this.contextCache.set(cacheKey, cached); // Limit cache size if (this.contextCache.size > this.config.maxCacheSize) { const oldestKey = this.contextCache.keys().next().value; if (oldestKey) { this.contextCache.delete(oldestKey); } } } generateCacheKey(request) { const key = JSON.stringify({ filePath: request.filePath, cursorPosition: request.cursorPosition, query: request.query, intent: request.intent, maxTokens: request.maxTokens }); return (0, crypto_1.createHash)('md5').update(key).digest('hex'); } clearRelatedCache(filePath) { for (const [key, cached] of this.contextCache.entries()) { if (cached.context.sources.some(s => s.path === filePath)) { this.contextCache.delete(key); } } } updateMetrics(processingTime, relevanceScore) { const total = this.metrics.context.requests; this.metrics.context.averageResponseTime = (this.metrics.context.averageResponseTime * (total - 1) + processingTime) / total; this.metrics.context.averageRelevanceScore = (this.metrics.context.averageRelevanceScore * (total - 1) + relevanceScore) / total; } chunkArray(array, chunkSize) { const chunks = []; for (let i = 0; i < array.length; i += chunkSize) { chunks.push(array.slice(i, i + chunkSize)); } return chunks; } generateFollowUpQueries(request, sources) { const queries = [ 'What are the main dependencies of this module?', 'How does this code relate to other parts of the system?', 'What are the key architectural patterns used here?' ]; if (request.intent === 'debugging') { queries.unshift('What are common issues in this type of code?'); } if (request.intent === 'refactoring') { queries.push('What refactoring opportunities exist here?'); } // Add context-specific queries based on sources if (sources.some(s => s.metadata.language === 'typescript')) { queries.push('What TypeScript-specific patterns are used?'); } return queries.slice(0, 4); } generateImprovements(sources) { const improvements = []; if (sources.length < 3) { improvements.push('Consider adding more detailed business context to TTL files'); } if (sources.some(s => s.metadata.size > 10000)) { improvements.push('Large TTL files detected - consider splitting for better performance'); } const languages = new Set(sources.map(s => s.metadata.language).filter(Boolean)); if (languages.size > 2) { improvements.push('Multi-language codebase detected - ensure consistent patterns across languages'); } improvements.push('Enhance TTL files with more architectural insights'); return improvements; } startCacheCleanup() { this.cacheCleanupInterval = setInterval(() => { const now = Date.now(); for (const [key, cached] of this.contextCache.entries()) { if (now - cached.timestamp > cached.ttl) { this.contextCache.delete(key); } } this.metrics.performance.lastCleanup = new Date(); }, 60000); // Clean every minute } } exports.TTLContextLoader = TTLContextLoader; //# sourceMappingURL=TTLContextLoader.js.map