UNPKG

codecrucible-synth

Version:

Production-Ready AI Development Platform with Multi-Voice Synthesis, Smithery MCP Integration, Enterprise Security, and Zero-Timeout Reliability

675 lines 26.3 kB
/** * Project Intelligence System - Enhanced Context Awareness * Iteration 3: Add enhanced context awareness and project intelligence */ import { EventEmitter } from 'events'; import { readdir, readFile, stat } from 'fs/promises'; import { join, relative, extname, basename } from 'path'; import { Logger } from '../logger.js'; import { unifiedCache } from '../cache/unified-cache-system.js'; export class ProjectIntelligenceSystem extends EventEmitter { logger; analysisInProgress = new Set(); constructor() { super(); this.logger = new Logger('ProjectIntelligenceSystem'); } /** * Analyze project and generate comprehensive intelligence */ async analyzeProject(rootPath, options = {}) { const normalizedPath = join(rootPath); // Check if analysis is already in progress if (this.analysisInProgress.has(normalizedPath)) { throw new Error(`Analysis already in progress for ${normalizedPath}`); } // Check cache first const cacheKey = `project-intel:${normalizedPath}`; if (!options.force) { const cacheResult = await unifiedCache.get(cacheKey); if (cacheResult?.hit) { this.logger.info(`Using cached analysis for ${normalizedPath}`); return cacheResult.value; } } this.analysisInProgress.add(normalizedPath); this.logger.info(`Starting comprehensive project analysis for ${normalizedPath}`); try { const startTime = Date.now(); // Phase 1: Structural Analysis this.emit('analysis:phase', { phase: 'structure', progress: 0 }); const structure = await this.analyzeProjectStructure(rootPath, options); // Phase 2: Code Analysis this.emit('analysis:phase', { phase: 'code', progress: 20 }); const insights = await this.generateProjectInsights(structure, options); // Phase 3: Dependency Analysis this.emit('analysis:phase', { phase: 'dependencies', progress: 40 }); const dependencies = await this.analyzeDependencies(structure, options); // Phase 4: Architecture Analysis this.emit('analysis:phase', { phase: 'architecture', progress: 60 }); const patterns = await this.identifyArchitecturePatterns(structure, insights, dependencies); // Phase 5: Metadata Extraction this.emit('analysis:phase', { phase: 'metadata', progress: 80 }); const metadata = await this.extractProjectMetadata(structure, options); // Phase 6: Generate Recommendations this.emit('analysis:phase', { phase: 'recommendations', progress: 90 }); const recommendations = await this.generateRecommendations(structure, insights, dependencies, patterns, metadata); const intelligence = { structure, insights, dependencies, patterns, metadata, recommendations, }; // Cache the results with 1 hour TTL and project intelligence tags await unifiedCache.set(cacheKey, intelligence, { ttl: 3600000, // 1 hour tags: ['project-intelligence', 'analysis'], metadata: { path: normalizedPath, analysisTime: Date.now() - startTime }, }); const analysisTime = Date.now() - startTime; this.logger.info(`Project analysis completed in ${analysisTime}ms`); this.emit('analysis:complete', { path: normalizedPath, intelligence, time: analysisTime }); return intelligence; } finally { this.analysisInProgress.delete(normalizedPath); } } /** * Analyze project structure and file organization */ async analyzeProjectStructure(rootPath, options) { const files = []; const directories = []; const packageFiles = []; const configFiles = []; const documentationFiles = []; const testFiles = []; const buildFiles = []; let totalSize = 0; const scanDirectory = async (dirPath, depth = 0) => { try { const entries = await readdir(dirPath, { withFileTypes: true }); for (const entry of entries) { const fullPath = join(dirPath, entry.name); const relativePath = relative(rootPath, fullPath); // Skip common ignore patterns if (this.shouldIgnoreFile(relativePath)) continue; if (entry.isDirectory()) { const dirNode = { path: relativePath, name: entry.name, depth, fileCount: 0, childDirectories: [], purpose: this.determineDirectoryPurpose(entry.name, relativePath), importance: this.assessDirectoryImportance(entry.name, relativePath), }; directories.push(dirNode); // Recursively scan subdirectories if (depth < (options.maxDepth || 10)) { await scanDirectory(fullPath, depth + 1); } } else if (entry.isFile()) { const stats = await stat(fullPath); const fileNode = await this.analyzeFile(fullPath, relativePath, stats); files.push(fileNode); totalSize += stats.size; // Categorize special files this.categorizeSpecialFile(fileNode, packageFiles, configFiles, documentationFiles, testFiles, buildFiles); } } } catch (error) { this.logger.warn(`Error scanning directory ${dirPath}:`, error); } }; await scanDirectory(rootPath); // Update directory file counts this.updateDirectoryFileCounts(directories, files); return { rootPath, directories, files, packageFiles, configFiles, documentationFiles, testFiles, buildFiles, totalFiles: files.length, totalDirectories: directories.length, codebaseSize: totalSize, }; } /** * Generate comprehensive project insights */ async generateProjectInsights(structure, options) { // Analyze languages const languageStats = this.analyzeLanguageDistribution(structure.files); const primaryLanguage = Object.keys(languageStats).reduce((a, b) => (languageStats[a] > languageStats[b] ? a : b), ''); // Detect frameworks const frameworksDetected = await this.detectFrameworks(structure); // Determine project type and architecture const projectType = this.determineProjectType(structure, frameworksDetected); const architecturePattern = this.identifyPrimaryArchitecture(structure, frameworksDetected); // Assess maturity and quality const maturityLevel = this.assessProjectMaturity(structure); const codeQuality = await this.assessCodeQuality(structure); const technicalDebt = await this.analyzeTechnicalDebt(structure); // Security and performance analysis const securityConcerns = await this.identifySecurityConcerns(structure); const performanceIndicators = await this.analyzePerformanceIndicators(structure); return { primaryLanguage, languageDistribution: languageStats, frameworksDetected, architecturePattern, projectType, maturityLevel, codeQuality, technicalDebt, securityConcerns, performanceIndicators, }; } /** * Analyze project dependencies and relationships */ async analyzeDependencies(structure, options) { // This is a simplified implementation - in practice, this would involve // parsing import statements, package files, etc. const nodes = []; const edges = []; const externalDependencies = []; const internalDependencies = []; // Analyze package files for external dependencies for (const pkg of structure.packageFiles) { for (const dep of pkg.dependencies) { externalDependencies.push({ name: dep, version: 'unknown', type: 'production', usage: [], risk: 'low', }); } } // Analyze internal file dependencies (simplified) for (const file of structure.files) { nodes.push({ id: file.path, name: file.name, type: 'internal', importance: file.importance === 'critical' ? 1 : file.importance === 'high' ? 0.8 : 0.5, }); } return { nodes, edges, cycles: [], criticalPath: [], modularity: { cohesion: 0.7, coupling: 0.3, modularity: 0.8, instability: 0.4, }, externalDependencies, internalDependencies, }; } /** * Identify architecture patterns and design principles */ async identifyArchitecturePatterns(structure, insights, dependencies) { // Simplified pattern detection const primaryPattern = insights.architecturePattern || 'modular'; return { primaryPattern, secondaryPatterns: ['layered'], designPrinciples: ['separation of concerns', 'single responsibility'], antiPatterns: [], refactoringOpportunities: [], }; } /** * Extract project metadata */ async extractProjectMetadata(structure, options) { let metadata = { name: basename(structure.rootPath), lastModified: new Date(), }; // Try to extract from package.json or similar for (const pkg of structure.packageFiles) { if (pkg.path.endsWith('package.json')) { try { const content = await readFile(join(structure.rootPath, pkg.path), 'utf8'); const packageInfo = JSON.parse(content); metadata = { ...metadata, name: packageInfo.name || metadata.name, version: packageInfo.version, description: packageInfo.description, author: typeof packageInfo.author === 'string' ? packageInfo.author : packageInfo.author?.name, license: packageInfo.license, repository: typeof packageInfo.repository === 'string' ? packageInfo.repository : packageInfo.repository?.url, homepage: packageInfo.homepage, keywords: packageInfo.keywords, }; break; } catch (error) { this.logger.warn(`Error parsing ${pkg.path}:`, error); } } } // Calculate project statistics const stats = this.calculateProjectStats(structure); return { name: metadata.name || 'Unknown Project', version: metadata.version, description: metadata.description, author: metadata.author, license: metadata.license, repository: metadata.repository, homepage: metadata.homepage, keywords: metadata.keywords, lastModified: metadata.lastModified, stats, }; } /** * Generate intelligent recommendations */ async generateRecommendations(structure, insights, dependencies, patterns, metadata) { return { codeImprovement: [], architecture: [], performance: [], security: [], testing: [], documentation: [], maintenance: [], }; } // Helper methods (simplified implementations) shouldIgnoreFile(path) { const ignorePatterns = [ 'node_modules', '.git', '.vscode', '.idea', 'dist', 'build', 'coverage', '.nyc_output', '.DS_Store', 'Thumbs.db', '*.log', ]; return ignorePatterns.some(pattern => path.includes(pattern)); } determineDirectoryPurpose(name, path) { const purposeMap = { src: 'source', source: 'source', lib: 'source', test: 'tests', tests: 'tests', __tests__: 'tests', spec: 'tests', build: 'build', dist: 'build', config: 'config', docs: 'docs', doc: 'docs', assets: 'assets', static: 'assets', node_modules: 'dependencies', }; return purposeMap[name.toLowerCase()] || 'other'; } assessDirectoryImportance(name, path) { if (['src', 'source', 'lib'].includes(name.toLowerCase())) return 'high'; if (['test', 'tests', 'config', 'docs'].includes(name.toLowerCase())) return 'medium'; return 'low'; } async analyzeFile(fullPath, relativePath, stats) { const ext = extname(relativePath); const name = basename(relativePath); const language = this.detectLanguage(ext); let linesOfCode = 0; let complexity = { cyclomaticComplexity: 0, cognitiveComplexity: 0, nestingDepth: 0, functionCount: 0, classCount: 0, }; // Read and analyze file content for code files if (this.isCodeFile(ext)) { try { const content = await readFile(fullPath, 'utf8'); linesOfCode = content.split('\n').length; complexity = this.analyzeCodeComplexity(content, language); } catch (error) { this.logger.warn(`Error reading file ${fullPath}:`, error); } } return { path: relativePath, name, extension: ext, language, size: stats.size, linesOfCode, complexity, dependencies: [], exports: [], imports: [], functions: [], classes: [], interfaces: [], purpose: this.determineFilePurpose(name, relativePath), lastModified: stats.mtime, importance: this.assessFileImportance(name, relativePath), }; } detectLanguage(extension) { const languageMap = { '.js': 'JavaScript', '.ts': 'TypeScript', '.jsx': 'JavaScript', '.tsx': 'TypeScript', '.py': 'Python', '.java': 'Java', '.cs': 'C#', '.cpp': 'C++', '.c': 'C', '.go': 'Go', '.rs': 'Rust', '.php': 'PHP', '.rb': 'Ruby', '.swift': 'Swift', '.kt': 'Kotlin', }; return languageMap[extension.toLowerCase()] || 'Unknown'; } isCodeFile(extension) { const codeExtensions = [ '.js', '.ts', '.jsx', '.tsx', '.py', '.java', '.cs', '.cpp', '.c', '.go', '.rs', '.php', '.rb', '.swift', '.kt', '.scala', '.r', '.m', ]; return codeExtensions.includes(extension.toLowerCase()); } determineFilePurpose(name, path) { if (name.toLowerCase().includes('test') || path.includes('/test/') || path.includes('/__tests__/')) return 'test'; if (name.toLowerCase().includes('config') || name.includes('.config.')) return 'config'; if (['index.js', 'index.ts', 'main.js', 'main.ts', 'app.js', 'app.ts'].includes(name.toLowerCase())) return 'entry'; if (name.toLowerCase().includes('util') || name.toLowerCase().includes('helper')) return 'utility'; if (['readme.md', 'readme.txt', 'changelog.md'].includes(name.toLowerCase())) return 'documentation'; return 'core'; } assessFileImportance(name, path) { if (['index.js', 'index.ts', 'main.js', 'main.ts', 'app.js', 'app.ts'].includes(name.toLowerCase())) return 'critical'; if (path.startsWith('src/') && !path.includes('/test/')) return 'high'; if (name.toLowerCase().includes('test')) return 'medium'; return 'low'; } analyzeCodeComplexity(content, language) { // Simplified complexity analysis const lines = content.split('\n'); const functionMatches = content.match(/function\s+\w+|def\s+\w+|class\s+\w+/gi) || []; const classMatches = content.match(/class\s+\w+/gi) || []; return { cyclomaticComplexity: Math.min(functionMatches.length * 2, 50), cognitiveComplexity: Math.min(functionMatches.length * 1.5, 40), nestingDepth: Math.min(lines.filter(line => line.trim().startsWith('if') || line.trim().startsWith('for')).length, 10), functionCount: functionMatches.length, classCount: classMatches.length, }; } categorizeSpecialFile(file, packageFiles, configFiles, documentationFiles, testFiles, buildFiles) { const name = file.name.toLowerCase(); if (name === 'package.json' || name === 'requirements.txt' || name === 'cargo.toml' || name === 'pom.xml') { packageFiles.push({ path: file.path, type: name.includes('.json') ? 'package.json' : 'other', dependencies: [], devDependencies: [], scripts: {}, }); } if (name.includes('config') || name.includes('.config.') || name.includes('.env')) { configFiles.push({ path: file.path, type: file.extension, purpose: 'configuration', settings: {}, }); } if (name.includes('readme') || name.includes('doc') || file.extension === '.md') { documentationFiles.push({ path: file.path, type: name.includes('readme') ? 'readme' : 'other', quality: 'fair', coverage: [], }); } if (file.purpose === 'test') { testFiles.push({ path: file.path, type: 'unit', framework: 'unknown', coverage: [], }); } if (name.includes('build') || name.includes('webpack') || name.includes('rollup') || name.includes('gulp')) { buildFiles.push({ path: file.path, type: file.extension, commands: [], targets: [], }); } } updateDirectoryFileCounts(directories, files) { for (const dir of directories) { dir.fileCount = files.filter(file => file.path.startsWith(dir.path + '/')).length; dir.childDirectories = directories .filter(d => d.path.startsWith(dir.path + '/') && d.depth === dir.depth + 1) .map(d => d.path); } } analyzeLanguageDistribution(files) { const distribution = {}; for (const file of files) { if (this.isCodeFile(file.extension)) { distribution[file.language] = (distribution[file.language] || 0) + file.linesOfCode; } } return distribution; } async detectFrameworks(structure) { const frameworks = []; // Check package files for framework dependencies for (const pkg of structure.packageFiles) { if (pkg.dependencies.includes('react')) { frameworks.push({ name: 'React', confidence: 0.9, evidence: ['package dependency'] }); } if (pkg.dependencies.includes('vue')) { frameworks.push({ name: 'Vue', confidence: 0.9, evidence: ['package dependency'] }); } if (pkg.dependencies.includes('angular')) { frameworks.push({ name: 'Angular', confidence: 0.9, evidence: ['package dependency'] }); } if (pkg.dependencies.includes('express')) { frameworks.push({ name: 'Express', confidence: 0.9, evidence: ['package dependency'] }); } } return frameworks; } determineProjectType(structure, frameworks) { // Check for library indicators if (structure.packageFiles.some(pkg => pkg.path.includes('package.json'))) { return 'library'; } // Check for application indicators if (frameworks.some(f => ['React', 'Vue', 'Angular'].includes(f.name))) { return 'application'; } // Check for service indicators if (frameworks.some(f => f.name === 'Express')) { return 'service'; } return 'unknown'; } identifyPrimaryArchitecture(structure, frameworks) { // Simple architecture detection based on directory structure const hasControllers = structure.directories.some(d => d.name.toLowerCase().includes('controller')); const hasModels = structure.directories.some(d => d.name.toLowerCase().includes('model')); const hasViews = structure.directories.some(d => d.name.toLowerCase().includes('view')); if (hasControllers && hasModels && hasViews) { return 'mvc'; } if (structure.directories.some(d => d.name.toLowerCase().includes('service'))) { return 'microservices'; } return 'modular'; } assessProjectMaturity(structure) { const hasTests = structure.testFiles.length > 0; const hasDocumentation = structure.documentationFiles.length > 0; const hasConfig = structure.configFiles.length > 0; const hasBuildSystem = structure.buildFiles.length > 0; const maturityScore = [hasTests, hasDocumentation, hasConfig, hasBuildSystem].filter(Boolean).length; if (maturityScore >= 3) return 'production'; if (maturityScore >= 2) return 'development'; return 'prototype'; } async assessCodeQuality(structure) { // Simplified quality assessment const totalFiles = structure.files.filter(f => this.isCodeFile(f.extension)).length; const testFiles = structure.testFiles.length; return { maintainabilityIndex: 75, duplication: 0.1, testCoverage: totalFiles > 0 ? (testFiles / totalFiles) * 100 : 0, commentDensity: 0.15, naming: { score: 0.8, issues: [], suggestions: [] }, structure: { score: 0.75, issues: [], suggestions: [] }, consistency: { score: 0.85, issues: [], suggestions: [] }, }; } async analyzeTechnicalDebt(structure) { // Simplified debt analysis return { totalDebt: 5, debtItems: [], debtRatio: 0.1, prioritizedFixes: [], }; } async identifySecurityConcerns(structure) { // Simplified security analysis return []; } async analyzePerformanceIndicators(structure) { // Simplified performance analysis return []; } calculateProjectStats(structure) { const codeFiles = structure.files.filter(f => this.isCodeFile(f.extension)); const totalLines = codeFiles.reduce((sum, file) => sum + file.linesOfCode, 0); return { totalFiles: structure.files.length, totalLines, codeLines: Math.floor(totalLines * 0.8), // Estimate commentLines: Math.floor(totalLines * 0.15), // Estimate blankLines: Math.floor(totalLines * 0.05), // Estimate testFiles: structure.testFiles.length, configFiles: structure.configFiles.length, documentFiles: structure.documentationFiles.length, }; } /** * Get cached analysis if available */ async getCachedAnalysis(path) { const cacheKey = `project-intel:${join(path)}`; const cacheResult = await unifiedCache.get(cacheKey); return cacheResult?.value || null; } /** * Clear analysis cache */ async clearCache(path) { if (path) { const cacheKey = `project-intel:${join(path)}`; await unifiedCache.delete(cacheKey); } else { await unifiedCache.clearByTags(['project-intelligence']); } } /** * Get system metrics */ async getSystemMetrics() { const cacheStats = await unifiedCache.getStats(); return { cachedAnalyses: cacheStats.size || 0, activeAnalyses: this.analysisInProgress.size, memoryUsage: process.memoryUsage(), }; } } export default ProjectIntelligenceSystem; //# sourceMappingURL=project-intelligence-system.js.map