UNPKG

codecrucible-synth

Version:

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

498 lines (434 loc) 14.4 kB
import { exec } from 'child_process'; import { promisify } from 'util'; import * as fs from 'fs'; import * as path from 'path'; const execAsync = promisify(exec); export interface CodebaseAnalysis { overview: { totalFiles: number; totalLines: number; languages: Record<string, number>; directories: string[]; }; dependencies: { production: string[]; development: string[]; security: { vulnerabilities: number; issues: string[]; }; }; codeQuality: { complexity: number; maintainability: string; testCoverage?: number; codeSmells: string[]; }; architecture: { patterns: string[]; frameworks: string[]; structure: Record<string, any>; }; security: { secrets: string[]; permissions: string[]; risks: string[]; }; performance: { buildTime?: number; bundleSize?: number; optimizations: string[]; }; } export class AutonomousCodebaseAnalyzer { private workingDirectory: string; constructor(workingDirectory: string = process.cwd()) { this.workingDirectory = workingDirectory; } async performComprehensiveAnalysis(): Promise<CodebaseAnalysis> { console.log('🔍 Starting comprehensive codebase analysis...'); const [overview, dependencies, codeQuality, architecture, security, performance] = await Promise.allSettled([ this.analyzeOverview(), this.analyzeDependencies(), this.analyzeCodeQuality(), this.analyzeArchitecture(), this.analyzeSecurity(), this.analyzePerformance(), ]); return { overview: overview.status === 'fulfilled' ? overview.value : this.getDefaultOverview(), dependencies: dependencies.status === 'fulfilled' ? dependencies.value : this.getDefaultDependencies(), codeQuality: codeQuality.status === 'fulfilled' ? codeQuality.value : this.getDefaultCodeQuality(), architecture: architecture.status === 'fulfilled' ? architecture.value : this.getDefaultArchitecture(), security: security.status === 'fulfilled' ? security.value : this.getDefaultSecurity(), performance: performance.status === 'fulfilled' ? performance.value : this.getDefaultPerformance(), }; } private async analyzeOverview() { const files = await this.findAllFiles(); const languages = this.detectLanguages(files); const directories = this.getDirectoryStructure(); let totalLines = 0; for (const file of files) { try { const content = fs.readFileSync(path.join(this.workingDirectory, file), 'utf-8'); totalLines += content.split('\n').length; } catch (error) { // Skip files that can't be read } } return { totalFiles: files.length, totalLines, languages, directories: directories.slice(0, 20), // Limit for performance }; } private async analyzeDependencies() { const packageJsonPath = path.join(this.workingDirectory, 'package.json'); let production: string[] = []; let development: string[] = []; let vulnerabilities = 0; let issues: string[] = []; if (fs.existsSync(packageJsonPath)) { try { const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')); production = Object.keys(packageJson.dependencies || {}); development = Object.keys(packageJson.devDependencies || {}); // Try to run npm audit try { const { stdout } = await execAsync('npm audit --json', { cwd: this.workingDirectory, timeout: 30000, }); const auditResult = JSON.parse(stdout); vulnerabilities = auditResult.metadata?.vulnerabilities?.total || 0; issues = Object.keys(auditResult.advisories || {}).slice(0, 10); } catch (auditError) { // npm audit failed, continue without security info } } catch (parseError) { // Invalid package.json } } return { production, development, security: { vulnerabilities, issues }, }; } private async analyzeCodeQuality() { let complexity = 1; let maintainability = 'good'; const codeSmells: string[] = []; // Basic heuristic analysis const files = await this.findAllFiles(['.ts', '.js', '.jsx', '.tsx']); let totalFunctions = 0; let longFunctions = 0; for (const file of files.slice(0, 50)) { // Limit for performance try { const content = fs.readFileSync(path.join(this.workingDirectory, file), 'utf-8'); const functionMatches = content.match(/function\s+\w+|const\s+\w+\s*=.*=>|class\s+\w+/g) || []; totalFunctions += functionMatches.length; // Check for long functions (basic heuristic) const lines = content.split('\n'); let inFunction = false; let functionLength = 0; for (const line of lines) { if (line.includes('function ') || line.includes('=>') || line.includes('class ')) { if (inFunction && functionLength > 50) { longFunctions++; } inFunction = true; functionLength = 0; } else if (line.includes('}') && inFunction) { inFunction = false; } if (inFunction) functionLength++; } // Detect code smells if (content.includes('console.log')) codeSmells.push('Debug statements'); if (content.includes('TODO') || content.includes('FIXME')) codeSmells.push('TODOs/FIXMEs'); if (content.match(/\w{50,}/)) codeSmells.push('Long identifiers'); } catch (error) { // Skip problematic files } } complexity = totalFunctions > 0 ? Math.min(10, Math.ceil((longFunctions / totalFunctions) * 10)) : 1; maintainability = complexity < 3 ? 'excellent' : complexity < 6 ? 'good' : 'needs improvement'; return { complexity, maintainability, codeSmells: [...new Set(codeSmells)].slice(0, 10), }; } private async analyzeArchitecture() { const patterns: string[] = []; const frameworks: string[] = []; const structure: Record<string, any> = {}; // Detect frameworks and patterns const packageJsonPath = path.join(this.workingDirectory, 'package.json'); if (fs.existsSync(packageJsonPath)) { try { const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')); const allDeps = { ...packageJson.dependencies, ...packageJson.devDependencies }; if (allDeps.react) frameworks.push('React'); if (allDeps.vue) frameworks.push('Vue'); if (allDeps.angular) frameworks.push('Angular'); if (allDeps.express) frameworks.push('Express'); if (allDeps.typescript) frameworks.push('TypeScript'); if (allDeps.jest) frameworks.push('Jest'); if (allDeps.webpack) frameworks.push('Webpack'); // Detect patterns based on directory structure const dirs = this.getDirectoryStructure(); if (dirs.includes('src/components')) patterns.push('Component Architecture'); if (dirs.includes('src/services')) patterns.push('Service Layer'); if (dirs.includes('src/utils')) patterns.push('Utility Pattern'); if (dirs.includes('tests') || dirs.includes('__tests__')) patterns.push('Test Organization'); if (dirs.includes('docs')) patterns.push('Documentation'); } catch (error) { // Continue without package.json analysis } } return { patterns, frameworks, structure }; } private async analyzeSecurity() { const secrets: string[] = []; const permissions: string[] = []; const risks: string[] = []; // Basic secret detection const files = await this.findAllFiles(['.js', '.ts', '.json', '.env']); const secretPatterns = [/api[_-]?key/i, /secret/i, /password/i, /token/i, /auth/i]; for (const file of files.slice(0, 30)) { // Limit for performance try { const content = fs.readFileSync(path.join(this.workingDirectory, file), 'utf-8'); for (const pattern of secretPatterns) { if (pattern.test(content)) { secrets.push(file); break; } } // Check for common security risks if (content.includes('eval(')) risks.push('Dynamic code execution'); if (content.includes('innerHTML')) risks.push('XSS vulnerability'); if (content.includes('document.write')) risks.push('DOM manipulation'); } catch (error) { // Skip problematic files } } return { secrets: [...new Set(secrets)].slice(0, 10), permissions: permissions.slice(0, 10), risks: [...new Set(risks)].slice(0, 10), }; } private async analyzePerformance() { const optimizations: string[] = []; // Check for common optimization opportunities const packageJsonPath = path.join(this.workingDirectory, 'package.json'); if (fs.existsSync(packageJsonPath)) { try { const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8')); if (!packageJson.scripts?.build) optimizations.push('Add build script'); if (!packageJson.scripts?.test) optimizations.push('Add test script'); if (packageJson.dependencies && Object.keys(packageJson.dependencies).length > 50) { optimizations.push('Consider dependency reduction'); } } catch (error) { // Continue without package.json analysis } } // Check for large files const files = await this.findAllFiles(); for (const file of files) { try { const stats = fs.statSync(path.join(this.workingDirectory, file)); if (stats.size > 100000) { // > 100KB optimizations.push('Large file detected: ' + file); } } catch (error) { // Skip problematic files } } return { optimizations: optimizations.slice(0, 10), }; } private async findAllFiles(extensions?: string[]): Promise<string[]> { const files: string[] = []; const walkDir = (dir: string, relativePath: string = '') => { try { const items = fs.readdirSync(path.join(this.workingDirectory, dir)); for (const item of items) { if (item.startsWith('.') || item === 'node_modules') continue; const fullPath = path.join(dir, item); const relativeFilePath = path.join(relativePath, item); const stat = fs.statSync(path.join(this.workingDirectory, fullPath)); if (stat.isDirectory()) { walkDir(fullPath, relativeFilePath); } else if (stat.isFile()) { if (!extensions || extensions.some(ext => item.endsWith(ext))) { files.push(relativeFilePath); } } } } catch (error) { // Skip directories that can't be read } }; walkDir('.'); return files.slice(0, 1000); // Limit for performance } private detectLanguages(files: string[]): Record<string, number> { const languages: Record<string, number> = {}; for (const file of files) { const ext = path.extname(file).toLowerCase(); let language = 'Other'; switch (ext) { case '.js': case '.jsx': language = 'JavaScript'; break; case '.ts': case '.tsx': language = 'TypeScript'; break; case '.py': language = 'Python'; break; case '.java': language = 'Java'; break; case '.cpp': case '.cc': case '.cxx': language = 'C++'; break; case '.c': language = 'C'; break; case '.cs': language = 'C#'; break; case '.go': language = 'Go'; break; case '.rs': language = 'Rust'; break; case '.php': language = 'PHP'; break; case '.rb': language = 'Ruby'; break; case '.swift': language = 'Swift'; break; case '.kt': language = 'Kotlin'; break; case '.html': language = 'HTML'; break; case '.css': language = 'CSS'; break; case '.scss': case '.sass': language = 'SCSS'; break; case '.json': language = 'JSON'; break; case '.xml': language = 'XML'; break; case '.yaml': case '.yml': language = 'YAML'; break; case '.md': language = 'Markdown'; break; } languages[language] = (languages[language] || 0) + 1; } return languages; } private getDirectoryStructure(): string[] { const dirs: string[] = []; const walkDirs = (dir: string, depth = 0) => { if (depth > 3) return; // Limit depth try { const items = fs.readdirSync(path.join(this.workingDirectory, dir)); for (const item of items) { if (item.startsWith('.') || item === 'node_modules') continue; const fullPath = path.join(dir, item); const stat = fs.statSync(path.join(this.workingDirectory, fullPath)); if (stat.isDirectory()) { dirs.push(fullPath); walkDirs(fullPath, depth + 1); } } } catch (error) { // Skip problematic directories } }; walkDirs('.'); return dirs; } // Default fallback methods private getDefaultOverview() { return { totalFiles: 0, totalLines: 0, languages: {}, directories: [], }; } private getDefaultDependencies() { return { production: [], development: [], security: { vulnerabilities: 0, issues: [] }, }; } private getDefaultCodeQuality() { return { complexity: 1, maintainability: 'unknown', codeSmells: [], }; } private getDefaultArchitecture() { return { patterns: [], frameworks: [], structure: {}, }; } private getDefaultSecurity() { return { secrets: [], permissions: [], risks: [], }; } private getDefaultPerformance() { return { optimizations: [], }; } }