UNPKG

mcp-adr-analysis-server

Version:

MCP server for analyzing Architectural Decision Records and project architecture

1,148 lines (1,113 loc) 54.9 kB
/** * MCP Tool for reviewing existing ADRs against actual code implementation * Validates ADR compliance, identifies gaps, and suggests updates * Uses tree-sitter for accurate code analysis * * Following ADR-018 Atomic Tools Architecture: * - Dependency injection for testability * - No ResearchOrchestrator (deprecated) - direct utility calls * - Self-contained with minimal dependencies */ import { McpAdrError } from '../types/index.js'; import { TreeSitterAnalyzer } from '../utils/tree-sitter-analyzer.js'; import * as path from 'path'; import * as fs from 'fs/promises'; /** * Review existing ADRs and validate against actual code implementation */ export async function reviewExistingAdrs(args) { const { adrDirectory = 'docs/adrs', projectPath = process.cwd(), specificAdr, analysisDepth = 'detailed', includeTreeSitter = true, generateUpdatePlan = true, } = args; // Security: Validate and resolve project path const resolvedProjectPath = path.resolve(projectPath); // Security: Ensure project path is not a system directory // Note: We explicitly ALLOW temp directories (/tmp, /var/folders, /private/tmp) // because tests and legitimate workflows use them const systemPaths = ['/Library', '/System', '/usr', '/etc', '/Applications', '/bin', '/sbin']; const allowedVarPaths = ['/var/folders', '/var/tmp']; // macOS temp directories const homeDir = process.env['HOME'] || ''; const sensitiveHomePaths = ['Library', 'Applications']; // Check for system paths, but allow temp directories const isSystemPath = systemPaths.some(sysPath => resolvedProjectPath.startsWith(sysPath)); const isAllowedVarPath = allowedVarPaths.some(varPath => resolvedProjectPath.startsWith(varPath)); if (isSystemPath) { throw new McpAdrError('INVALID_INPUT', `Security: Cannot analyze system directory: ${resolvedProjectPath}`, { projectPath: resolvedProjectPath }); } // Block /var paths except allowed temp directories if (resolvedProjectPath.startsWith('/var') && !isAllowedVarPath) { throw new McpAdrError('INVALID_INPUT', `Security: Cannot analyze system directory: ${resolvedProjectPath}`, { projectPath: resolvedProjectPath }); } // Block /private paths except /private/tmp (macOS symlink for /tmp) if (resolvedProjectPath.startsWith('/private') && !resolvedProjectPath.startsWith('/private/tmp') && !resolvedProjectPath.startsWith('/private/var/folders')) { throw new McpAdrError('INVALID_INPUT', `Security: Cannot analyze system directory: ${resolvedProjectPath}`, { projectPath: resolvedProjectPath }); } // Check for sensitive paths within home directory if (homeDir.length > 0 && resolvedProjectPath.startsWith(homeDir)) { const relativePath = resolvedProjectPath.slice(homeDir.length + 1); const firstDir = relativePath.split(path.sep)[0]; if (firstDir && sensitiveHomePaths.includes(firstDir)) { throw new McpAdrError('INVALID_INPUT', `Security: Cannot analyze sensitive directory: ${resolvedProjectPath}`, { projectPath: resolvedProjectPath }); } } try { // Step 1: Discover existing ADRs const { discoverAdrsInDirectory } = await import('../utils/adr-discovery.js'); const discoveryResult = await discoverAdrsInDirectory(adrDirectory, resolvedProjectPath, { includeContent: true, includeTimeline: false, }); if (discoveryResult.totalAdrs === 0) { return { content: [ { type: 'text', text: `# ADR Review: No ADRs Found ## Discovery Results - **Directory**: ${adrDirectory} - **Project Path**: ${resolvedProjectPath} - **ADRs Found**: 0 ## Recommendations 1. **Create Initial ADRs**: Use the \`suggest_adrs\` tool to identify architectural decisions 2. **Set Up ADR Directory**: Create the ADR directory structure 3. **Establish ADR Process**: Implement ADR workflow for future decisions ## Next Steps \`\`\`json { "tool": "suggest_adrs", "args": { "projectPath": "${resolvedProjectPath}", "analysisType": "comprehensive" } } \`\`\` `, }, ], }; } // Step 2: Filter ADRs if specific one requested const adrsToReview = specificAdr ? discoveryResult.adrs.filter(adr => adr.filename.includes(specificAdr) || adr.title.toLowerCase().includes(specificAdr.toLowerCase())) : discoveryResult.adrs; if (adrsToReview.length === 0) { throw new McpAdrError(`No ADRs found matching: ${specificAdr}`, 'ADR_NOT_FOUND'); } // Step 3: Analyze environment context (simplified - no ResearchOrchestrator per ADR-018) // The ResearchOrchestrator is deprecated and caused 37+ test timeouts (850s+ test suite) // For environment analysis, users can call environment-analysis-tool separately if needed const environmentContext = ` ## 📊 Code Analysis Context **Analysis Mode**: ${analysisDepth} **Tree-sitter Enabled**: ${includeTreeSitter ? '✅' : '❌'} **Project**: ${path.basename(resolvedProjectPath)} > **Note**: For detailed environment analysis, use the \`environment-analysis-tool\` separately. > This tool focuses on ADR-to-code compliance validation. `; const researchConfidence = 0.85; // Static confidence for basic code analysis // Step 4: Analyze code structure const codeAnalysis = await analyzeCodeStructure(resolvedProjectPath, includeTreeSitter); // Step 5: Review each ADR const reviewResults = []; for (const adr of adrsToReview) { const reviewResult = await reviewSingleAdr(adr, codeAnalysis, analysisDepth, resolvedProjectPath); reviewResults.push(reviewResult); } // Step 5: Generate comprehensive report const overallScore = reviewResults.reduce((sum, result) => sum + result.complianceScore, 0) / reviewResults.length; const totalGaps = reviewResults.reduce((sum, result) => sum + result.codeCompliance.gaps.length, 0); const needsUpdate = reviewResults.filter(result => result.recommendations.updateAdr).length; const needsCodeChanges = reviewResults.filter(result => result.recommendations.updateCode).length; let updatePlan = ''; if (generateUpdatePlan) { updatePlan = await generateUpdatePlanContent(reviewResults, codeAnalysis, resolvedProjectPath); } return { content: [ { type: 'text', text: `# ADR Compliance Review Report ## Overview - **Project**: ${path.basename(resolvedProjectPath)} - **ADRs Reviewed**: ${reviewResults.length} - **Overall Compliance Score**: ${overallScore.toFixed(1)}/10 - **Research Confidence**: ${(researchConfidence * 100).toFixed(1)}% - **Analysis Depth**: ${analysisDepth} - **Tree-sitter Analysis**: ${includeTreeSitter ? '✅ Enabled' : '❌ Disabled'} ${environmentContext} ## Summary Statistics - **Total Gaps Identified**: ${totalGaps} - **ADRs Needing Updates**: ${needsUpdate} - **Code Changes Required**: ${needsCodeChanges} - **Full Compliance**: ${reviewResults.filter(r => r.complianceScore >= 8).length} - **Partial Compliance**: ${reviewResults.filter(r => r.complianceScore >= 5 && r.complianceScore < 8).length} - **Non-Compliance**: ${reviewResults.filter(r => r.complianceScore < 5).length} ## Detailed Reviews ${reviewResults .map(result => ` ### ${result.title} - **File**: ${path.basename(result.adrPath)} - **Status**: ${result.status} - **Compliance Score**: ${result.complianceScore}/10 #### Implementation Status - **Implemented**: ${result.codeCompliance.implemented ? '✅' : '❌'} - **Partially Implemented**: ${result.codeCompliance.partiallyImplemented ? '⚠️' : '❌'} - **Not Implemented**: ${result.codeCompliance.notImplemented ? '🚨' : '✅'} #### Evidence Found ${result.codeCompliance.evidence.length > 0 ? result.codeCompliance.evidence.map(e => `- ${e}`).join('\n') : '- No supporting evidence found in codebase'} #### Gaps Identified ${result.codeCompliance.gaps.length > 0 ? result.codeCompliance.gaps.map(g => `- ${g}`).join('\n') : '- No gaps identified'} #### Recommendations ${result.recommendations.actions.map(a => `- ${a}`).join('\n')} #### Analysis ${result.analysis} --- `) .join('\n')} ## Code Structure Analysis - **Files Analyzed**: ${codeAnalysis.files.length} - **Technologies Detected**: ${codeAnalysis.technologies.join(', ')} - **Architectural Patterns**: ${codeAnalysis.patterns.join(', ')} ### Architectural Elements - **APIs**: ${codeAnalysis.architecturalElements.apis?.length || 0} - **Databases**: ${codeAnalysis.architecturalElements.databases?.length || 0} - **Frameworks**: ${codeAnalysis.architecturalElements.frameworks?.length || 0} - **Patterns**: ${codeAnalysis.architecturalElements.patterns?.length || 0} - **Infrastructure Resources**: ${codeAnalysis.architecturalElements.infrastructureResources?.length || 0} - **DevOps Tools**: ${codeAnalysis.architecturalElements.devopsTools?.length || 0} ### Enterprise Security Analysis (Tree-sitter) ${includeTreeSitter ? ` - **Security Findings**: ${codeAnalysis.architecturalElements.securityFindings?.length || 0} ${(codeAnalysis.architecturalElements.securityFindings?.length || 0) > 0 ? ` #### Critical Security Issues: ${codeAnalysis.architecturalElements .securityFindings.slice(0, 10) .map((finding) => `- ${finding}`) .join('\n')} ${(codeAnalysis.architecturalElements.securityFindings.length || 0) > 10 ? `\n*... and ${(codeAnalysis.architecturalElements.securityFindings.length || 0) - 10} more security findings*` : ''} ` : '- ✅ No security issues detected'} ` : '- Tree-sitter security analysis disabled'} ### DevOps Stack Analysis ${includeTreeSitter && (codeAnalysis.architecturalElements.infrastructureResources?.length || 0) > 0 ? ` #### Infrastructure Resources: ${codeAnalysis.architecturalElements .infrastructureResources.slice(0, 10) .map((resource) => `- ${resource}`) .join('\n')} ${(codeAnalysis.architecturalElements.infrastructureResources.length || 0) > 10 ? `\n*... and ${(codeAnalysis.architecturalElements.infrastructureResources.length || 0) - 10} more resources*` : ''} ` : '- No infrastructure resources detected'} ${includeTreeSitter && (codeAnalysis.architecturalElements.devopsTools?.length || 0) > 0 ? ` #### DevOps Tools Detected: ${codeAnalysis.architecturalElements .devopsTools.slice(0, 10) .map((tool) => `- ${tool}`) .join('\n')} ${(codeAnalysis.architecturalElements.devopsTools.length || 0) > 10 ? `\n*... and ${(codeAnalysis.architecturalElements.devopsTools.length || 0) - 10} more tools*` : ''} ` : '- No DevOps tools detected'} ${generateUpdatePlan ? updatePlan : ''} ## Next Steps ### Immediate Actions (High Priority) ${reviewResults .filter(r => r.complianceScore < 5) .map(r => `- **${r.title}**: ${r.recommendations.actions[0] || 'Requires immediate attention'}`) .join('\n') || '- No immediate actions required'} ### Medium Priority ${reviewResults .filter(r => r.complianceScore >= 5 && r.complianceScore < 8) .map(r => `- **${r.title}**: ${r.recommendations.actions[0] || 'Minor updates needed'}`) .join('\n') || '- No medium priority items'} ### Maintenance ${reviewResults .filter(r => r.complianceScore >= 8) .map(r => `- **${r.title}**: Well implemented, monitor for changes`) .join('\n') || '- No items in maintenance status'} ## Quality Assessment ### Best Practices Compliance - **Documentation Quality**: ${calculateDocumentationQuality(reviewResults)}/10 - **Implementation Fidelity**: ${calculateImplementationFidelity(reviewResults)}/10 - **Architectural Consistency**: ${calculateArchitecturalConsistency(reviewResults)}/10 ### Recommendations for Process Improvement 1. **Regular ADR Reviews**: Schedule quarterly compliance reviews 2. **Automated Validation**: Implement CI/CD checks for ADR compliance 3. **Developer Training**: Ensure team understands ADR importance 4. **Template Updates**: Standardize ADR format for better tracking ## Tools for Follow-up ### Update Specific ADRs \`\`\`json { "tool": "generate_adr_from_decision", "args": { "decisionData": { "title": "Updated [ADR Title]", "context": "Review findings and current implementation", "decision": "Refined decision based on analysis", "consequences": "Updated consequences from review" } } } \`\`\` ### Generate Implementation Plan \`\`\`json { "tool": "generate_implementation_plan", "args": { "adrPath": "[specific ADR path]", "includeCodeChanges": true, "priority": "high" } } \`\`\` `, }, ], }; } catch (error) { throw new McpAdrError(`Failed to review ADRs: ${error instanceof Error ? error.message : String(error)}`, 'REVIEW_ERROR'); } } /** * Analyze code structure to understand current implementation * Enhanced with fast-glob for efficient file discovery */ async function analyzeCodeStructure(projectPath, useTreeSitter) { const result = { files: [], patterns: [], technologies: [], architecturalElements: { apis: [], databases: [], frameworks: [], patterns: [], }, }; try { // Enhanced file system analysis using fast-glob const { analyzeProjectStructure } = await import('../utils/file-system.js'); // Use the new analyzeProjectStructure function for comprehensive analysis const projectAnalysis = await analyzeProjectStructure(projectPath); // Extract file paths from the analysis result.files = projectAnalysis.files.map(f => f.path); result.technologies = projectAnalysis.technologies; result.patterns = projectAnalysis.patterns; // Legacy pattern detection for backward compatibility const legacyPatterns = await detectArchitecturalPatterns(result.files); result.patterns = [...new Set([...result.patterns, ...legacyPatterns])]; // If tree-sitter is enabled, perform detailed analysis if (useTreeSitter) { const detailedAnalysis = await performTreeSitterAnalysis(result.files); result.architecturalElements = detailedAnalysis; } return result; } catch (error) { console.warn('Enhanced code analysis failed, falling back to legacy:', error); // Fallback to legacy implementation try { const files = await findSourceFiles(projectPath); result.files = files; result.technologies = await detectTechnologies(projectPath); result.patterns = await detectArchitecturalPatterns(files); if (useTreeSitter) { const detailedAnalysis = await performTreeSitterAnalysis(files); result.architecturalElements = detailedAnalysis; } } catch (fallbackError) { console.warn('Legacy code analysis also failed:', fallbackError); } return result; } } /** * Review a single ADR against code implementation * Enhanced with Smart Code Linking to find related files */ async function reviewSingleAdr(adr, codeAnalysis, depth, projectPath) { const result = { adrPath: adr.path, title: adr.title, status: adr.status, complianceScore: 0, codeCompliance: { implemented: false, partiallyImplemented: false, notImplemented: false, evidence: [], gaps: [], }, recommendations: { updateAdr: false, updateCode: false, createPlan: false, actions: [], }, analysis: '', }; try { // Parse ADR content for key architectural elements const adrElements = extractAdrElements(adr.content || ''); // SMART CODE LINKING: Find related code files for this specific ADR let relatedFiles = []; try { const { findRelatedCode } = await import('../utils/file-system.js'); const relatedCodeResult = await findRelatedCode(adr.path, adr.content || '', projectPath, { useAI: true, useRipgrep: true, maxFiles: 25, includeContent: false, }); relatedFiles = relatedCodeResult.relatedFiles; // Add Smart Code Linking results to evidence if (relatedFiles.length > 0) { result.codeCompliance.evidence.push(`Smart Code Linking found ${relatedFiles.length} related files (confidence: ${(relatedCodeResult.confidence * 100).toFixed(0)}%)`); // Add top related files as evidence const topFiles = relatedFiles.slice(0, 5); topFiles.forEach(file => { result.codeCompliance.evidence.push(`Related: ${file.path}`); }); } else { result.codeCompliance.gaps.push('No related code files found using Smart Code Linking'); } } catch (error) { console.warn('Smart Code Linking failed:', error); result.codeCompliance.gaps.push('Smart Code Linking unavailable - manual review required'); } // Compare ADR elements with code implementation (enhanced with related files) const complianceAnalysis = await analyzeCompliance(adrElements, codeAnalysis, relatedFiles); // Merge Smart Code Linking evidence with compliance analysis result.codeCompliance.evidence.push(...complianceAnalysis.evidence); result.codeCompliance.gaps.push(...complianceAnalysis.gaps); result.codeCompliance.implemented = complianceAnalysis.implemented || relatedFiles.length > 0; result.codeCompliance.partiallyImplemented = complianceAnalysis.partiallyImplemented || (relatedFiles.length > 0 && relatedFiles.length < 5); result.codeCompliance.notImplemented = complianceAnalysis.notImplemented && relatedFiles.length === 0; result.complianceScore = calculateComplianceScore(result.codeCompliance); // Generate recommendations (enhanced with Smart Code Linking insights) result.recommendations = generateRecommendations(result.codeCompliance, result.complianceScore, relatedFiles); // Generate detailed analysis (include Smart Code Linking results) result.analysis = generateDetailedAnalysis(adrElements, result.codeCompliance, depth, relatedFiles); return result; } catch (error) { result.analysis = `Error analyzing ADR: ${error instanceof Error ? error.message : String(error)}`; return result; } } /** * Extract architectural elements from ADR content */ function extractAdrElements(content) { const elements = { technologies: [], apis: [], databases: [], patterns: [], components: [], constraints: [], decisions: [], }; // Extract from different sections of the ADR const sections = { context: extractSection(content, 'context'), decision: extractSection(content, 'decision'), consequences: extractSection(content, 'consequences'), alternatives: extractSection(content, 'alternatives'), }; // Technology extraction patterns const techPatterns = [ /\b(React|Vue|Angular|Node\.js|Express|FastAPI|Django|Spring|Rails)\b/gi, /\b(PostgreSQL|MySQL|MongoDB|Redis|Elasticsearch)\b/gi, /\b(Docker|Kubernetes|AWS|Azure|GCP)\b/gi, ]; // API patterns const apiPatterns = [ /\b(REST|GraphQL|gRPC|WebSocket)\b/gi, /\b(API|endpoint|service|microservice)\b/gi, ]; // Extract elements from each section for (const [, sectionContent] of Object.entries(sections)) { if (sectionContent) { elements.technologies.push(...extractMatches(sectionContent, techPatterns)); elements.apis.push(...extractMatches(sectionContent, apiPatterns)); } } return elements; } /** * Extract section content from ADR */ function extractSection(content, sectionName) { const regex = new RegExp(`##\\s*${sectionName}[^#]*`, 'gi'); const match = content.match(regex); return match ? match[0] : null; } /** * Extract matches from content using patterns */ function extractMatches(content, patterns) { const matches = []; patterns.forEach(pattern => { const found = content.match(pattern); if (found) { matches.push(...found); } }); return [...new Set(matches)]; // Remove duplicates } /** * Analyze compliance between ADR and code */ async function analyzeCompliance(adrElements, codeAnalysis, relatedFiles = []) { const compliance = { implemented: false, partiallyImplemented: false, notImplemented: false, evidence: [], gaps: [], }; // Check technology alignment const adrTechs = adrElements.technologies || []; const codeTechs = codeAnalysis.technologies || []; const techMatches = adrTechs.filter((tech) => codeTechs.some((codeTech) => codeTech.toLowerCase().includes(tech.toLowerCase()) || tech.toLowerCase().includes(codeTech.toLowerCase()))); if (techMatches.length > 0) { compliance.evidence.push(`Technologies implemented: ${techMatches.join(', ')}`); } const missingTechs = adrTechs.filter((tech) => !techMatches.includes(tech)); if (missingTechs.length > 0) { compliance.gaps.push(`Missing technologies: ${missingTechs.join(', ')}`); } // Smart Code Linking analysis: check if related files provide additional evidence if (relatedFiles.length > 0) { compliance.evidence.push(`Smart Code Linking identified ${relatedFiles.length} potentially related files`); // Analyze file extensions to infer technologies used in related files const relatedExtensions = [...new Set(relatedFiles.map((f) => f.extension))]; const relatedTechHints = relatedExtensions .map(ext => { switch (ext) { case '.ts': case '.tsx': return 'TypeScript'; case '.js': case '.jsx': return 'JavaScript'; case '.py': return 'Python'; case '.java': return 'Java'; case '.cs': return 'C#'; case '.go': return 'Go'; case '.rs': return 'Rust'; default: return null; } }) .filter(Boolean); if (relatedTechHints.length > 0) { compliance.evidence.push(`Related files suggest technologies: ${relatedTechHints.join(', ')}`); } } else { compliance.gaps.push('No related files found - implementation may not exist'); } // Check API implementation const adrApis = adrElements.apis || []; const codeApis = codeAnalysis.architecturalElements.apis || []; const apiMatches = adrApis.filter((api) => codeApis.some((codeApi) => codeApi.toLowerCase().includes(api.toLowerCase()))); if (apiMatches.length > 0) { compliance.evidence.push(`APIs implemented: ${apiMatches.join(', ')}`); } // Determine overall compliance status const totalElements = adrTechs.length + adrApis.length; const implementedElements = techMatches.length + apiMatches.length; if (implementedElements === totalElements && totalElements > 0) { compliance.implemented = true; } else if (implementedElements > 0) { compliance.partiallyImplemented = true; } else { compliance.notImplemented = true; } return compliance; } /** * Calculate compliance score */ function calculateComplianceScore(compliance) { if (compliance.implemented) return 9; if (compliance.partiallyImplemented) return 6; if (compliance.notImplemented) return 2; return 5; // Default/unknown } /** * Generate recommendations based on compliance analysis */ function generateRecommendations(compliance, score, relatedFiles = []) { const recommendations = { updateAdr: false, updateCode: false, createPlan: false, actions: [], }; if (score < 5) { recommendations.createPlan = true; recommendations.actions.push('Create implementation plan for missing elements'); recommendations.actions.push('Consider updating ADR if decisions have changed'); } if (compliance.gaps.length > 0) { recommendations.updateCode = true; recommendations.actions.push('Implement missing architectural elements'); } if (score < 7) { recommendations.updateAdr = true; recommendations.actions.push('Update ADR to reflect current implementation'); } // Smart Code Linking enhancements if (relatedFiles.length > 0) { recommendations.actions.push(`Review ${relatedFiles.length} related files identified by Smart Code Linking`); if (relatedFiles.length > 10) { recommendations.actions.push('Consider refactoring - many files may indicate coupling issues'); } } else { recommendations.actions.push('Use Smart Code Linking to identify implementation files'); } return recommendations; } /** * Generate detailed analysis text */ function generateDetailedAnalysis(adrElements, compliance, depth, relatedFiles = []) { let analysis = ''; if (depth === 'comprehensive') { analysis += ` **Architectural Elements Analysis:** - Technologies mentioned: ${adrElements.technologies?.length || 0} - APIs referenced: ${adrElements.apis?.length || 0} - Implementation evidence: ${compliance.evidence.length} - Identified gaps: ${compliance.gaps.length} **Smart Code Linking Results:** - Related files found: ${relatedFiles.length} ${relatedFiles.length > 0 ? `- Top related files:\n${relatedFiles .slice(0, 3) .map((f) => ` • ${f.path} (${f.name})`) .join('\n')}` : '- No related files identified'} **Compliance Details:** ${compliance.evidence.map((e) => `✅ ${e}`).join('\n')} ${compliance.gaps.map((g) => `❌ ${g}`).join('\n')} `; } else { analysis = `Implementation status: ${compliance.implemented ? 'Fully implemented' : compliance.partiallyImplemented ? 'Partially implemented' : 'Not implemented'}. Smart Code Linking found ${relatedFiles.length} related files.`; } return analysis; } // Helper functions for code analysis // Directories to always exclude for security and performance const EXCLUDED_DIRECTORIES = new Set([ 'node_modules', '.git', '.svn', '.hg', 'dist', 'build', 'coverage', '.cache', '.npm', '.yarn', 'vendor', '__pycache__', '.pytest_cache', 'target', 'bin', 'obj', // System directories that should never be traversed 'Library', 'System', 'Applications', 'usr', 'var', 'etc', 'private', 'cores', 'Volumes', ]); // Maximum recursion depth for directory traversal const MAX_RECURSION_DEPTH = 10; async function findSourceFiles(projectPath, rootPath, currentDepth = 0) { const files = []; const extensions = ['.ts', '.js', '.py', '.java', '.cs', '.go', '.rs', '.rb']; // Security: Initialize and validate root path on first call if (!rootPath) { rootPath = path.resolve(projectPath); // Ensure we're not starting from a system directory const pathParts = rootPath.split(path.sep); if (pathParts.some(part => EXCLUDED_DIRECTORIES.has(part) && part !== pathParts[pathParts.length - 1])) { console.warn(`Security: Refusing to scan potentially sensitive path: ${rootPath}`); return files; } } // Security: Enforce maximum recursion depth if (currentDepth > MAX_RECURSION_DEPTH) { console.warn(`Security: Maximum recursion depth (${MAX_RECURSION_DEPTH}) reached at: ${projectPath}`); return files; } // Security: Resolve current path and verify it's within root boundary const resolvedPath = path.resolve(projectPath); if (!resolvedPath.startsWith(rootPath)) { console.warn(`Security: Path traversal attempt detected. Path ${resolvedPath} is outside root ${rootPath}`); return files; } try { const entries = await fs.readdir(resolvedPath, { withFileTypes: true }); for (const entry of entries) { // Skip hidden directories and excluded directories if (entry.isDirectory()) { if (entry.name.startsWith('.') || EXCLUDED_DIRECTORIES.has(entry.name)) { continue; } const subFiles = await findSourceFiles(path.join(resolvedPath, entry.name), rootPath, currentDepth + 1); files.push(...subFiles); } else if (entry.isFile() && extensions.some(ext => entry.name.endsWith(ext))) { files.push(path.join(resolvedPath, entry.name)); } } } catch (error) { // Only log non-permission errors to avoid noise if (error instanceof Error && !error.message.includes('EPERM') && !error.message.includes('EACCES')) { console.warn('Warning reading directory:', error); } } return files; } async function detectTechnologies(projectPath) { const technologies = []; try { // Check package.json for Node.js technologies const packageJsonPath = path.join(projectPath, 'package.json'); try { const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf-8')); const deps = { ...packageJson.dependencies, ...packageJson.devDependencies }; Object.keys(deps).forEach(dep => { if (dep.includes('react')) technologies.push('React'); if (dep.includes('vue')) technologies.push('Vue'); if (dep.includes('angular')) technologies.push('Angular'); if (dep.includes('express')) technologies.push('Express'); if (dep.includes('fastify')) technologies.push('Fastify'); // Cloud SDKs if (dep.includes('aws-sdk') || dep.includes('@aws-sdk')) technologies.push('AWS SDK'); if (dep.includes('google-cloud') || dep.includes('@google-cloud')) technologies.push('GCP SDK'); if (dep.includes('azure') || dep.includes('@azure')) technologies.push('Azure SDK'); }); } catch { // package.json not found or invalid } // Check for other technology indicators const files = await fs.readdir(projectPath); // Container technologies if (files.includes('Dockerfile')) technologies.push('Docker'); if (files.includes('docker-compose.yml') || files.includes('docker-compose.yaml')) technologies.push('Docker Compose'); // Kubernetes if (files.includes('kubernetes.yaml') || files.includes('k8s.yaml')) technologies.push('Kubernetes'); if (files.some(f => f.endsWith('.yaml') || f.endsWith('.yml'))) { const yamlFiles = files.filter(f => f.endsWith('.yaml') || f.endsWith('.yml')); for (const yamlFile of yamlFiles) { try { const content = await fs.readFile(path.join(projectPath, yamlFile), 'utf-8'); if (content.includes('apiVersion:') && content.includes('kind:')) { technologies.push('Kubernetes'); break; } } catch { // Skip unreadable files } } } // Infrastructure as Code if (files.some(f => f.endsWith('.tf') || f.endsWith('.tfvars'))) technologies.push('Terraform'); if (files.includes('cloudformation.yaml') || files.includes('cloudformation.yml')) technologies.push('CloudFormation'); if (files.includes('ansible.yml') || files.includes('playbook.yml')) technologies.push('Ansible'); // Cloud-specific files if (files.includes('.aws') || files.includes('aws-config.yml')) technologies.push('AWS'); if (files.includes('gcp-config.json') || files.includes('.gcp')) technologies.push('GCP'); if (files.includes('azure-pipelines.yml') || files.includes('.azure')) technologies.push('Azure'); // DevOps and CI/CD if (files.includes('.github')) technologies.push('GitHub Actions'); if (files.includes('.gitlab-ci.yml')) technologies.push('GitLab CI'); if (files.includes('Jenkinsfile')) technologies.push('Jenkins'); if (files.includes('azure-pipelines.yml')) technologies.push('Azure DevOps'); // Monitoring and observability if (files.includes('prometheus.yml')) technologies.push('Prometheus'); if (files.includes('grafana.yml') || files.includes('grafana.json')) technologies.push('Grafana'); if (files.includes('jaeger.yml')) technologies.push('Jaeger'); // Language-specific if (files.includes('requirements.txt')) technologies.push('Python'); if (files.includes('Gemfile')) technologies.push('Ruby'); if (files.includes('pom.xml')) technologies.push('Maven/Java'); if (files.includes('Cargo.toml')) technologies.push('Rust'); if (files.includes('go.mod')) technologies.push('Go'); // Check for cloud provider specific directories/files recursively await checkCloudProviderIndicators(projectPath, technologies); } catch (error) { console.warn('Technology detection warning:', error); } return [...new Set(technologies)]; } /** * Enhanced cloud provider detection based on Tosin Akinosho's expertise * Principal Solution Architect at RedHat, specializing in AWS, GCP, Azure, Docker, Kubernetes, Terraform */ async function checkCloudProviderIndicators(projectPath, technologies) { try { const entries = await fs.readdir(projectPath, { withFileTypes: true }); for (const entry of entries) { if (entry.isDirectory() && !entry.name.startsWith('.') && entry.name !== 'node_modules') { const dirPath = path.join(projectPath, entry.name); // AWS indicators if (entry.name.includes('aws') || entry.name.includes('lambda') || entry.name.includes('ec2')) { technologies.push('AWS'); } // GCP indicators if (entry.name.includes('gcp') || entry.name.includes('google-cloud') || entry.name.includes('gke')) { technologies.push('GCP'); } // Azure indicators if (entry.name.includes('azure') || entry.name.includes('aks')) { technologies.push('Azure'); } // Kubernetes indicators if (entry.name.includes('k8s') || entry.name.includes('kubernetes') || entry.name.includes('helm')) { technologies.push('Kubernetes'); } // Terraform indicators if (entry.name.includes('terraform') || entry.name.includes('tf')) { technologies.push('Terraform'); } // Check for terraform files in subdirectories try { const subFiles = await fs.readdir(dirPath); if (subFiles.some(f => f.endsWith('.tf') || f.endsWith('.tfvars'))) { technologies.push('Terraform'); } if (subFiles.some(f => f.includes('helm') || f.endsWith('.yaml') || f.endsWith('.yml'))) { // Check if it's Kubernetes YAML for (const file of subFiles.filter(f => f.endsWith('.yaml') || f.endsWith('.yml'))) { try { const content = await fs.readFile(path.join(dirPath, file), 'utf-8'); if (content.includes('apiVersion:') && content.includes('kind:')) { technologies.push('Kubernetes'); break; } } catch { // Skip unreadable files } } } } catch { // Skip unreadable directories } } } } catch (error) { console.warn('Cloud provider detection warning:', error); } } async function detectArchitecturalPatterns(files) { const patterns = []; // Pattern detection based on file structure const fileNames = files.map(f => path.basename(f).toLowerCase()); const filePaths = files.map(f => f.toLowerCase()); // Traditional software patterns if (fileNames.some(name => name.includes('controller'))) patterns.push('MVC'); if (fileNames.some(name => name.includes('service'))) patterns.push('Service Layer'); if (fileNames.some(name => name.includes('repository'))) patterns.push('Repository Pattern'); if (fileNames.some(name => name.includes('factory'))) patterns.push('Factory Pattern'); if (fileNames.some(name => name.includes('adapter'))) patterns.push('Adapter Pattern'); if (fileNames.some(name => name.includes('facade'))) patterns.push('Facade Pattern'); if (fileNames.some(name => name.includes('observer'))) patterns.push('Observer Pattern'); // Cloud and DevOps patterns (specialized for Tosin's expertise) // Microservices patterns if (filePaths.some(path => path.includes('microservice') || path.includes('service'))) { patterns.push('Microservices Architecture'); } // Container patterns if (fileNames.some(name => name.includes('dockerfile'))) patterns.push('Containerization'); if (fileNames.some(name => name.includes('docker-compose'))) patterns.push('Multi-Container Application'); // Kubernetes patterns if (fileNames.some(name => name.includes('deployment.yaml') || name.includes('deployment.yml'))) { patterns.push('Kubernetes Deployment'); } if (fileNames.some(name => name.includes('service.yaml') || name.includes('service.yml'))) { patterns.push('Kubernetes Service Mesh'); } if (fileNames.some(name => name.includes('ingress'))) patterns.push('Ingress Controller'); if (fileNames.some(name => name.includes('configmap') || name.includes('secret'))) { patterns.push('Configuration Management'); } // Infrastructure as Code patterns if (fileNames.some(name => name.includes('.tf') || name.includes('terraform'))) { patterns.push('Infrastructure as Code (Terraform)'); } if (fileNames.some(name => name.includes('cloudformation'))) { patterns.push('Infrastructure as Code (CloudFormation)'); } // Cloud-native patterns if (fileNames.some(name => name.includes('lambda') || name.includes('function'))) { patterns.push('Serverless/FaaS'); } if (fileNames.some(name => name.includes('api-gateway') || name.includes('gateway'))) { patterns.push('API Gateway'); } if (fileNames.some(name => name.includes('load-balancer') || name.includes('alb') || name.includes('nlb'))) { patterns.push('Load Balancing'); } // DevOps patterns if (fileNames.some(name => name.includes('pipeline') || name.includes('ci') || name.includes('cd'))) { patterns.push('CI/CD Pipeline'); } if (fileNames.some(name => name.includes('ansible') || name.includes('playbook'))) { patterns.push('Configuration Management (Ansible)'); } // Monitoring and observability patterns if (fileNames.some(name => name.includes('prometheus') || name.includes('metrics'))) { patterns.push('Metrics Collection'); } if (fileNames.some(name => name.includes('grafana') || name.includes('dashboard'))) { patterns.push('Observability Dashboard'); } if (fileNames.some(name => name.includes('jaeger') || name.includes('zipkin') || name.includes('tracing'))) { patterns.push('Distributed Tracing'); } if (fileNames.some(name => name.includes('logging') || name.includes('fluentd') || name.includes('logstash'))) { patterns.push('Centralized Logging'); } // Cloud provider specific patterns if (fileNames.some(name => name.includes('vpc') || name.includes('subnet'))) { patterns.push('Virtual Private Cloud'); } if (fileNames.some(name => name.includes('s3') || name.includes('bucket'))) { patterns.push('Object Storage'); } if (fileNames.some(name => name.includes('rds') || name.includes('database'))) { patterns.push('Managed Database'); } if (fileNames.some(name => name.includes('eks') || name.includes('gke') || name.includes('aks'))) { patterns.push('Managed Kubernetes'); } // Security patterns if (fileNames.some(name => name.includes('iam') || name.includes('rbac'))) { patterns.push('Identity and Access Management'); } if (fileNames.some(name => name.includes('security-group') || name.includes('firewall'))) { patterns.push('Network Security'); } if (fileNames.some(name => name.includes('vault') || name.includes('secrets'))) { patterns.push('Secret Management'); } // Data patterns if (fileNames.some(name => name.includes('etl') || name.includes('pipeline'))) { patterns.push('Data Pipeline'); } if (fileNames.some(name => name.includes('kafka') || name.includes('pubsub') || name.includes('queue'))) { patterns.push('Event-Driven Architecture'); } return [...new Set(patterns)]; } async function performTreeSitterAnalysis(files) { const analyzer = new TreeSitterAnalyzer(); const architecturalElements = { apis: [], databases: [], frameworks: [], patterns: [], securityFindings: [], infrastructureResources: [], devopsTools: [], }; try { for (const filePath of files.slice(0, 50)) { // Limit to first 50 files for performance try { const analysis = await analyzer.analyzeFile(filePath); // Extract API endpoints and routes if (analysis.functions) { analysis.functions.forEach(func => { if (func.name && isApiFunction(func.name)) { architecturalElements.apis.push(`${func.name} (${filePath})`); } // Enhanced: Add security-sensitive functions if (func.securitySensitive) { architecturalElements.securityFindings.push(`Security-sensitive function: ${func.name} in ${filePath}`); } }); } // Enhanced database and framework detection if (analysis.imports) { analysis.imports.forEach(imp => { if (isDatabaseImport(imp.module)) { architecturalElements.databases.push(`${imp.module} (${filePath})`); } if (isFrameworkImport(imp.module)) { architecturalElements.frameworks.push(`${imp.module} (${filePath})`); } // Enhanced: Add DevOps tools detection if (isDevOpsImport(imp.module)) { architecturalElements.devopsTools.push(`${imp.module} (${filePath})`); } // Enhanced: Track dangerous imports if (imp.isDangerous) { architecturalElements.securityFindings.push(`Dangerous import: ${imp.module} in ${filePath}${imp.reason ? ` - ${imp.reason}` : ''}`); } }); } // Enhanced infrastructure analysis for enterprise DevOps if (analysis.infraStructure) { analysis.infraStructure.forEach(infra => { architecturalElements.patterns.push(`${infra.resourceType}: ${infra.name} (${infra.provider})`); architecturalElements.infrastructureResources.push(`${infra.provider}: ${infra.resourceType} (${filePath})`); // Enhanced: Track security risks in infrastructure if (infra.securityRisks.length > 0) { infra.securityRisks.forEach(risk => { architecturalElements.securityFindings.push(`Infrastructure security risk in ${filePath}: ${risk}`); }); } }); } // Enhanced security analysis if (analysis.hasSecrets) { analysis.secrets.forEach(secret => { architecturalElements.securityFindings.push(`${secret.type} detected in ${filePath} at line ${secret.location.line} (confidence: ${secret.confidence})`); }); } // Enhanced: Security issues from tree-sitter analysis if (analysis.securityIssues && analysis.securityIssues.length > 0) { analysis.securityIssues.forEach(issue => { architecturalElements.securityFindings.push(`${issue.severity.toUpperCase()}: ${issue.message} in ${filePath} at line ${issue.location.line}`); }); } // Enhanced: Architectural violations if (analysis.architecturalViolations && analysis.architecturalViolations.length > 0) { analysis.architecturalViolations.forEach(violation => { architecturalElements.patterns.push(`Architectural violation in ${filePath}: ${violation.message}`); }); } // Enhanced: Track language-specific patterns if (analysis.language) { switch (analysis.language) { case 'python': architecturalElements.patterns.push(`Python microservice detected (${filePath})`); break; case 'typescript': case 'javascript': architecturalElements.patterns.push(`Node.js application detected (${filePath})`); break; case 'yaml': architecturalElements.patterns.push(`Configuration/Infrastructure YAML (${filePath})`); break; case 'hcl': architecturalElements.patterns.push(`Terraform infrastructure (${filePath})`); break; case 'dockerfile': architecturalElements.patterns.push(`Container configuration (${filePath})`); break; case 'bash': architecturalElements.patterns.push(`Shell automation script (${filePath})`); break; } } } catch (fileError) { // Gracefully skip files that can't be analyzed console.warn(`Skipping file analysis for ${filePath}:`, fileError); } } // Deduplicate results architecturalElements.apis = [...new Set(architecturalElements.apis)]; architecturalElements.databases = [...new Set(architecturalElements.databa