UNPKG

superaugment

Version:

Enterprise-grade MCP server with world-class C++ analysis, robust error handling, and production-ready architecture for VS Code Augment

389 lines 16.6 kB
import { z } from 'zod'; import { ConfigManager } from '../../config/ConfigManager.js'; import { FileSystemManager } from '../../utils/FileSystemManager.js'; import { logger } from '../../utils/logger.js'; const AnalyzeCodeInputSchema = z.object({ files: z.array(z.string()).optional().describe('Array of file paths or glob patterns to analyze'), code: z.string().optional().describe('Code content to analyze directly'), projectPath: z.string().optional().describe('Project root path (defaults to current directory)'), persona: z.string().optional().describe('Cognitive persona to use for analysis'), depth: z.enum(['basic', 'detailed', 'comprehensive']).default('detailed').describe('Analysis depth'), focus: z.array(z.string()).optional().describe('Specific areas to focus on (e.g., performance, security, maintainability)'), language: z.string().optional().describe('Programming language (auto-detected if not specified)'), includeMetrics: z.boolean().default(true).describe('Include code metrics in analysis'), }); /** * Code analysis tool with cognitive persona support */ export class AnalyzeCodeTool { configManager; name = 'analyze_code'; description = 'Analyze code files or direct code content for quality, patterns, issues, and improvements with cognitive persona support'; inputSchema = AnalyzeCodeInputSchema; fileSystemManager; constructor(configManager) { this.configManager = configManager; this.fileSystemManager = new FileSystemManager(); } async execute(args) { try { logger.info('Starting code analysis', { args }); // Validate input const validatedArgs = AnalyzeCodeInputSchema.parse(args); // Get persona if specified const persona = validatedArgs.persona ? this.configManager.getPersona(validatedArgs.persona) : null; // Get code to analyze const codeData = await this.getCodeToAnalyze(validatedArgs); // Perform analysis based on persona and parameters const analysis = await this.performAnalysis(validatedArgs, codeData, persona); return { content: [ { type: 'text', text: this.formatAnalysisResult(analysis, persona), }, ], }; } catch (error) { logger.error('Code analysis failed:', error); throw error; } } /** * Get code to analyze from files or direct input */ async getCodeToAnalyze(args) { const result = { files: [], }; // If direct code is provided, use it if (args.code) { result.directCode = args.code; return result; } // If files are specified, read them if (args.files && args.files.length > 0) { const rootPath = args.projectPath || process.cwd(); result.files = await this.fileSystemManager.readFiles(args.files, rootPath); // Also get project structure for context if (args.depth === 'comprehensive') { try { result.projectStructure = await this.fileSystemManager.analyzeProjectStructure(rootPath); } catch (error) { logger.warn('Failed to analyze project structure:', error); } } } else { // No files specified, analyze current project const rootPath = args.projectPath || process.cwd(); const defaultPatterns = ['**/*.{js,ts,jsx,tsx,vue,py,java,go,rs}']; result.files = await this.fileSystemManager.readFiles(defaultPatterns, rootPath); if (args.depth === 'comprehensive') { try { result.projectStructure = await this.fileSystemManager.analyzeProjectStructure(rootPath); } catch (error) { logger.warn('Failed to analyze project structure:', error); } } } return result; } async performAnalysis(args, codeData, persona) { const analysis = { summary: '', issues: [], suggestions: [], metrics: {}, files_analyzed: [], project_info: null, persona_insights: null, }; // Analyze direct code if (codeData.directCode) { analysis.summary = `Analyzing ${codeData.directCode.length} characters of direct code input`; analysis.issues = this.detectIssues(codeData.directCode, args.focus); analysis.suggestions = this.generateSuggestions(codeData.directCode, args.depth); if (args.includeMetrics) { analysis.metrics = this.calculateMetrics(codeData.directCode); } } // Analyze files if (codeData.files && codeData.files.length > 0) { analysis.summary = `Analyzing ${codeData.files.length} files`; for (const file of codeData.files) { if (file.content) { const fileAnalysis = { file: file.relativePath, size: file.size, language: this.detectLanguage(file.extension, file.content), issues: this.detectIssues(file.content, args.focus), metrics: args.includeMetrics ? this.calculateMetrics(file.content) : null, }; analysis.files_analyzed.push(fileAnalysis); analysis.issues.push(...fileAnalysis.issues.map((issue) => ({ ...issue, file: file.relativePath, }))); } } // Generate overall suggestions const allCode = codeData.files.map((f) => f.content || '').join('\n'); analysis.suggestions = this.generateSuggestions(allCode, args.depth); if (args.includeMetrics) { analysis.metrics = this.calculateOverallMetrics(codeData.files); } } // Add project structure info if (codeData.projectStructure) { analysis.project_info = { framework: codeData.projectStructure.framework, language: codeData.projectStructure.language, total_files: codeData.projectStructure.files.length, directories: codeData.projectStructure.directories.length, has_package_json: !!codeData.projectStructure.packageJson, }; } // Apply persona-specific insights if (persona) { analysis.persona_insights = this.applyPersonaInsights(analysis, persona); } return analysis; } detectIssues(code, focus) { const issues = []; // Basic issue detection if (code.includes('console.log')) { issues.push({ type: 'code_quality', severity: 'warning', message: 'Console.log statements found - consider using proper logging', line: 1, }); } if (code.includes('var ')) { issues.push({ type: 'modernization', severity: 'info', message: 'Consider using let/const instead of var', line: 1, }); } // Focus-specific analysis if (focus?.includes('security')) { if (code.includes('eval(')) { issues.push({ type: 'security', severity: 'error', message: 'Use of eval() is dangerous and should be avoided', line: 1, }); } } return issues; } generateSuggestions(_code, depth) { const suggestions = []; if (depth === 'comprehensive') { suggestions.push({ category: 'performance', suggestion: 'Consider implementing code splitting for better performance', impact: 'medium', }); suggestions.push({ category: 'maintainability', suggestion: 'Add TypeScript for better type safety', impact: 'high', }); } return suggestions; } calculateMetrics(code) { const lines = code.split('\n'); const nonEmptyLines = lines.filter(line => line.trim().length > 0); const commentLines = lines.filter(line => { const trimmed = line.trim(); return trimmed.startsWith('//') || trimmed.startsWith('/*') || trimmed.startsWith('*') || trimmed.startsWith('#'); }); // Simple complexity estimation const complexityIndicators = [ 'if', 'else', 'for', 'while', 'switch', 'case', 'try', 'catch', 'finally' ]; let complexityScore = 0; for (const indicator of complexityIndicators) { const regex = new RegExp(`\\b${indicator}\\b`, 'g'); const matches = code.match(regex); complexityScore += matches ? matches.length : 0; } const complexity = complexityScore < 10 ? 'low' : complexityScore < 25 ? 'medium' : 'high'; const commentRatio = commentLines.length / nonEmptyLines.length; const maintainabilityIndex = Math.max(0, Math.min(100, 100 - complexityScore + (commentRatio * 20))); return { total_lines: lines.length, lines_of_code: nonEmptyLines.length, comment_lines: commentLines.length, comment_ratio: Math.round(commentRatio * 100) / 100, complexity_score: complexityScore, complexity_level: complexity, maintainability_index: Math.round(maintainabilityIndex), estimated_functions: (code.match(/function\s+\w+|const\s+\w+\s*=\s*\(|def\s+\w+/g) || []).length, }; } calculateOverallMetrics(files) { let totalLines = 0; let totalLoc = 0; let totalComplexity = 0; let totalFunctions = 0; for (const file of files) { if (file.content) { const metrics = this.calculateMetrics(file.content); totalLines += metrics['total_lines']; totalLoc += metrics['lines_of_code']; totalComplexity += metrics['complexity_score']; totalFunctions += metrics['estimated_functions']; } } return { total_files: files.length, total_lines: totalLines, total_lines_of_code: totalLoc, average_file_size: Math.round(totalLoc / files.length), total_complexity_score: totalComplexity, average_complexity: Math.round(totalComplexity / files.length), total_functions: totalFunctions, functions_per_file: Math.round(totalFunctions / files.length), }; } detectLanguage(extension, _content) { const langMap = { '.js': 'JavaScript', '.ts': 'TypeScript', '.jsx': 'JavaScript (React)', '.tsx': 'TypeScript (React)', '.vue': 'Vue.js', '.py': 'Python', '.java': 'Java', '.go': 'Go', '.rs': 'Rust', '.cpp': 'C++', '.c': 'C', }; const detected = langMap[extension]; if (detected) return detected; return 'Unknown'; } applyPersonaInsights(_analysis, persona) { const insights = { persona_name: persona.name, specialized_focus: persona.expertise, recommendations: [], }; // Apply persona-specific analysis switch (persona.name) { case 'architect': insights.recommendations.push('Consider the overall system architecture and scalability'); insights.recommendations.push('Evaluate design patterns and architectural principles'); break; case 'security': insights.recommendations.push('Perform thorough security vulnerability assessment'); insights.recommendations.push('Review authentication and authorization mechanisms'); break; case 'performance': insights.recommendations.push('Analyze performance bottlenecks and optimization opportunities'); insights.recommendations.push('Consider caching strategies and resource utilization'); break; default: insights.recommendations.push(`Apply ${persona.name} expertise to the analysis`); } return insights; } formatAnalysisResult(analysis, persona) { let result = '# Code Analysis Report\n\n'; if (persona) { result += `**Persona**: ${persona.name} (${persona.description})\n\n`; } result += `## Summary\n${analysis.summary}\n\n`; // Project information if (analysis.project_info) { result += '## Project Information\n'; if (analysis.project_info.framework) { result += `- **Framework**: ${analysis.project_info.framework}\n`; } if (analysis.project_info.language) { result += `- **Primary Language**: ${analysis.project_info.language}\n`; } result += `- **Total Files**: ${analysis.project_info.total_files}\n`; result += `- **Directories**: ${analysis.project_info.directories}\n`; result += `- **Has package.json**: ${analysis.project_info.has_package_json ? 'Yes' : 'No'}\n\n`; } // File analysis summary if (analysis.files_analyzed.length > 0) { result += '## Files Analyzed\n'; analysis.files_analyzed.forEach((file) => { result += `- **${file.file}** (${file.language}, ${file.size} bytes)\n`; if (file.issues.length > 0) { result += ` - Issues: ${file.issues.length}\n`; } }); result += '\n'; } // Issues if (analysis.issues.length > 0) { result += '## Issues Found\n'; const issuesByFile = {}; analysis.issues.forEach((issue) => { const file = issue.file || 'Direct Code'; if (!issuesByFile[file]) { issuesByFile[file] = []; } issuesByFile[file].push(issue); }); Object.entries(issuesByFile).forEach(([file, issues]) => { result += `### ${file}\n`; issues.forEach((issue, index) => { result += `${index + 1}. **${issue.type}** (${issue.severity}): ${issue.message}\n`; if (issue.line) { result += ` Line: ${issue.line}\n`; } }); result += '\n'; }); } // Suggestions if (analysis.suggestions.length > 0) { result += '## Suggestions\n'; analysis.suggestions.forEach((suggestion, index) => { result += `${index + 1}. **${suggestion.category}**: ${suggestion.suggestion}`; if (suggestion.impact) { result += ` (Impact: ${suggestion.impact})`; } result += '\n'; }); result += '\n'; } // Metrics if (Object.keys(analysis.metrics).length > 0) { result += '## Code Metrics\n'; Object.entries(analysis.metrics).forEach(([key, value]) => { result += `- **${key.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase())}**: ${value}\n`; }); result += '\n'; } // Persona insights if (analysis.persona_insights) { result += '## Persona Insights\n'; result += `**${analysis.persona_insights.persona_name} Perspective:**\n\n`; analysis.persona_insights.recommendations.forEach((rec, index) => { result += `${index + 1}. ${rec}\n`; }); } return result; } } //# sourceMappingURL=AnalyzeCodeTool.js.map