superaugment
Version:
Enterprise-grade MCP server with world-class C++ analysis, robust error handling, and production-ready architecture for VS Code Augment
359 lines • 16.3 kB
JavaScript
import { z } from 'zod';
import { ConfigManager } from '../../config/ConfigManager.js';
import { FileSystemManager } from '../../utils/FileSystemManager.js';
import { logger } from '../../utils/logger.js';
const AnalyzeProjectInputSchema = z.object({
projectPath: z.string().optional().describe('Project root path (defaults to current directory)'),
persona: z.string().optional().describe('Cognitive persona to use for analysis'),
includeFileTree: z.boolean().default(false).describe('Include detailed file tree in output'),
analyzeDependencies: z.boolean().default(true).describe('Analyze package dependencies'),
detectIssues: z.boolean().default(true).describe('Detect potential project issues'),
});
/**
* Project structure analysis tool
*/
export class AnalyzeProjectTool {
configManager;
name = 'analyze_project';
description = 'Analyze project structure, dependencies, and overall architecture';
inputSchema = AnalyzeProjectInputSchema;
fileSystemManager;
constructor(configManager) {
this.configManager = configManager;
this.fileSystemManager = new FileSystemManager();
}
async execute(args) {
try {
logger.info('Starting project analysis', { args });
const validatedArgs = AnalyzeProjectInputSchema.parse(args);
const persona = validatedArgs.persona
? this.configManager.getPersona(validatedArgs.persona)
: null;
const projectPath = validatedArgs.projectPath || process.cwd();
const projectStructure = await this.fileSystemManager.analyzeProjectStructure(projectPath);
const analysis = await this.performProjectAnalysis(validatedArgs, projectStructure, persona);
return {
content: [
{
type: 'text',
text: this.formatProjectAnalysis(analysis, persona),
},
],
};
}
catch (error) {
logger.error('Project analysis failed:', error);
throw error;
}
}
async performProjectAnalysis(args, structure, persona) {
const analysis = {
project_path: structure.rootPath,
framework: structure.framework,
language: structure.language,
file_stats: this.calculateFileStats(structure),
dependencies: args.analyzeDependencies ? this.analyzeDependencies(structure.packageJson) : null,
architecture: this.analyzeArchitecture(structure),
issues: args.detectIssues ? this.detectProjectIssues(structure) : [],
recommendations: this.generateRecommendations(structure, persona),
file_tree: args.includeFileTree ? this.generateFileTree(structure) : null,
persona_insights: persona ? this.applyPersonaInsights(structure, persona) : null,
};
return analysis;
}
calculateFileStats(structure) {
const filesByExtension = {};
const filesByDirectory = {};
let totalSize = 0;
structure.files.forEach(file => {
// Count by extension
const ext = file.extension || 'no-extension';
filesByExtension[ext] = (filesByExtension[ext] || 0) + 1;
// Count by directory
const dir = file.relativePath.split('/')[0] || 'root';
filesByDirectory[dir] = (filesByDirectory[dir] || 0) + 1;
totalSize += file.size;
});
return {
total_files: structure.files.length,
total_directories: structure.directories.length,
total_size: totalSize,
average_file_size: Math.round(totalSize / structure.files.length),
files_by_extension: filesByExtension,
files_by_directory: filesByDirectory,
};
}
analyzeDependencies(packageJson) {
if (!packageJson) {
return { has_package_json: false };
}
const deps = packageJson.dependencies || {};
const devDeps = packageJson.devDependencies || {};
const allDeps = { ...deps, ...devDeps };
const categories = {
frameworks: [],
testing: [],
build_tools: [],
ui_libraries: [],
utilities: [],
};
// Categorize dependencies
Object.keys(allDeps).forEach(dep => {
if (['react', 'vue', 'angular', 'next', 'nuxt', 'express', 'fastify'].some(fw => dep.includes(fw))) {
categories.frameworks.push(dep);
}
else if (['jest', 'mocha', 'chai', 'cypress', 'playwright', 'vitest'].some(test => dep.includes(test))) {
categories.testing.push(dep);
}
else if (['webpack', 'vite', 'rollup', 'babel', 'typescript', 'eslint'].some(build => dep.includes(build))) {
categories.build_tools.push(dep);
}
else if (['styled-components', 'emotion', 'tailwind', 'material-ui', 'antd'].some(ui => dep.includes(ui))) {
categories.ui_libraries.push(dep);
}
else {
categories.utilities.push(dep);
}
});
return {
has_package_json: true,
total_dependencies: Object.keys(deps).length,
total_dev_dependencies: Object.keys(devDeps).length,
categories,
scripts: Object.keys(packageJson.scripts || {}),
};
}
analyzeArchitecture(structure) {
const hasTests = structure.files.some(f => f.relativePath.includes('test') ||
f.relativePath.includes('spec') ||
f.relativePath.includes('__tests__'));
const hasConfig = structure.files.some(f => ['config', 'configuration'].some(term => f.relativePath.toLowerCase().includes(term)));
const hasDocumentation = structure.files.some(f => f.extension === '.md' || f.relativePath.toLowerCase().includes('doc'));
const hasTypeScript = structure.files.some(f => ['.ts', '.tsx'].includes(f.extension));
const hasDocker = structure.files.some(f => f.relativePath.toLowerCase().includes('dockerfile') ||
f.relativePath.toLowerCase().includes('docker-compose'));
const hasCI = structure.files.some(f => f.relativePath.includes('.github') ||
f.relativePath.includes('.gitlab') ||
f.relativePath.includes('.circleci'));
return {
has_tests: hasTests,
has_config: hasConfig,
has_documentation: hasDocumentation,
has_typescript: hasTypeScript,
has_docker: hasDocker,
has_ci: hasCI,
estimated_complexity: this.estimateProjectComplexity(structure),
};
}
estimateProjectComplexity(structure) {
const fileCount = structure.files.length;
const dirCount = structure.directories.length;
const hasFramework = !!structure.framework;
let score = 0;
if (fileCount > 100)
score += 2;
else if (fileCount > 50)
score += 1;
if (dirCount > 20)
score += 2;
else if (dirCount > 10)
score += 1;
if (hasFramework)
score += 1;
if (score >= 4)
return 'high';
if (score >= 2)
return 'medium';
return 'low';
}
detectProjectIssues(structure) {
const issues = [];
// Check for missing important files
const hasReadme = structure.files.some(f => f.relativePath.toLowerCase().includes('readme'));
if (!hasReadme) {
issues.push({
type: 'documentation',
severity: 'medium',
message: 'Missing README.md file',
recommendation: 'Add a README.md file to document your project',
});
}
const hasGitignore = structure.files.some(f => f.relativePath.includes('.gitignore'));
if (!hasGitignore) {
issues.push({
type: 'configuration',
severity: 'medium',
message: 'Missing .gitignore file',
recommendation: 'Add a .gitignore file to exclude unnecessary files from version control',
});
}
// Check for large files
const largeFiles = structure.files.filter(f => f.size > 1024 * 1024); // > 1MB
if (largeFiles.length > 0) {
issues.push({
type: 'performance',
severity: 'low',
message: `Found ${largeFiles.length} large files (>1MB)`,
recommendation: 'Consider optimizing or moving large files to appropriate storage',
});
}
// Check for too many files in root
const rootFiles = structure.files.filter(f => !f.relativePath.includes('/'));
if (rootFiles.length > 10) {
issues.push({
type: 'organization',
severity: 'low',
message: `Too many files in root directory (${rootFiles.length})`,
recommendation: 'Consider organizing files into subdirectories',
});
}
return issues;
}
generateRecommendations(structure, _persona) {
const recommendations = [];
if (!structure.framework) {
recommendations.push('Consider using a framework to structure your project better');
}
const hasTests = structure.files.some(f => f.relativePath.includes('test'));
if (!hasTests) {
recommendations.push('Add unit tests to improve code quality and reliability');
}
const hasTypeScript = structure.files.some(f => ['.ts', '.tsx'].includes(f.extension));
if (!hasTypeScript && structure.language === 'JavaScript') {
recommendations.push('Consider migrating to TypeScript for better type safety');
}
return recommendations;
}
generateFileTree(structure) {
const tree = ['Project Structure:', ''];
// Group files by directory
const filesByDir = { root: [] };
structure.files.forEach(file => {
const parts = file.relativePath.split('/');
if (parts.length === 1) {
filesByDir['root']?.push(file.relativePath);
}
else {
const dir = parts[0];
if (dir && !filesByDir[dir]) {
filesByDir[dir] = [];
}
if (dir) {
filesByDir[dir]?.push(parts.slice(1).join('/'));
}
}
});
// Generate tree representation
Object.entries(filesByDir).forEach(([dir, files]) => {
if (dir === 'root') {
files.forEach(file => tree.push(`├── ${file}`));
}
else {
tree.push(`├── ${dir}/`);
files.slice(0, 5).forEach((file, index) => {
const isLast = index === Math.min(files.length, 5) - 1;
tree.push(`│ ${isLast ? '└──' : '├──'} ${file}`);
});
if (files.length > 5) {
tree.push(`│ └── ... and ${files.length - 5} more files`);
}
}
});
return tree.join('\n');
}
applyPersonaInsights(_structure, persona) {
const insights = {
persona_name: persona.name,
specialized_analysis: [],
recommendations: [],
};
switch (persona.name) {
case 'architect':
insights.specialized_analysis.push('Analyzing system architecture and scalability patterns');
insights.recommendations.push('Consider implementing clean architecture principles');
insights.recommendations.push('Evaluate service boundaries and dependencies');
break;
case 'security':
insights.specialized_analysis.push('Reviewing security configuration and dependencies');
insights.recommendations.push('Audit dependencies for known vulnerabilities');
insights.recommendations.push('Implement security headers and HTTPS');
break;
case 'performance':
insights.specialized_analysis.push('Analyzing performance bottlenecks and optimization opportunities');
insights.recommendations.push('Optimize bundle size and loading performance');
insights.recommendations.push('Implement caching strategies');
break;
default:
insights.specialized_analysis.push(`Applying ${persona.name} expertise to project analysis`);
}
return insights;
}
formatProjectAnalysis(analysis, persona) {
let result = '# Project Analysis Report\n\n';
if (persona) {
result += `**Analysis Persona**: ${persona.name} (${persona.description})\n\n`;
}
result += `## Project Overview\n`;
result += `- **Path**: ${analysis.project_path}\n`;
if (analysis.framework) {
result += `- **Framework**: ${analysis.framework}\n`;
}
if (analysis.language) {
result += `- **Primary Language**: ${analysis.language}\n`;
}
result += `- **Complexity**: ${analysis.architecture.estimated_complexity}\n\n`;
// File statistics
result += `## File Statistics\n`;
result += `- **Total Files**: ${analysis.file_stats.total_files}\n`;
result += `- **Total Directories**: ${analysis.file_stats.total_directories}\n`;
result += `- **Total Size**: ${Math.round(analysis.file_stats.total_size / 1024)} KB\n`;
result += `- **Average File Size**: ${Math.round(analysis.file_stats.average_file_size / 1024)} KB\n\n`;
// Dependencies
if (analysis.dependencies?.has_package_json) {
result += `## Dependencies\n`;
result += `- **Dependencies**: ${analysis.dependencies.total_dependencies}\n`;
result += `- **Dev Dependencies**: ${analysis.dependencies.total_dev_dependencies}\n`;
result += `- **Scripts**: ${analysis.dependencies.scripts.length}\n\n`;
}
// Architecture
result += `## Architecture Features\n`;
result += `- **Tests**: ${analysis.architecture.has_tests ? '✅' : '❌'}\n`;
result += `- **TypeScript**: ${analysis.architecture.has_typescript ? '✅' : '❌'}\n`;
result += `- **Documentation**: ${analysis.architecture.has_documentation ? '✅' : '❌'}\n`;
result += `- **Docker**: ${analysis.architecture.has_docker ? '✅' : '❌'}\n`;
result += `- **CI/CD**: ${analysis.architecture.has_ci ? '✅' : '❌'}\n\n`;
// Issues
if (analysis.issues.length > 0) {
result += `## Issues Found\n`;
analysis.issues.forEach((issue, index) => {
result += `${index + 1}. **${issue.type}** (${issue.severity}): ${issue.message}\n`;
result += ` Recommendation: ${issue.recommendation}\n\n`;
});
}
// Recommendations
if (analysis.recommendations.length > 0) {
result += `## Recommendations\n`;
analysis.recommendations.forEach((rec, index) => {
result += `${index + 1}. ${rec}\n`;
});
result += '\n';
}
// File tree
if (analysis.file_tree) {
result += `## File Structure\n\`\`\`\n${analysis.file_tree}\n\`\`\`\n\n`;
}
// Persona insights
if (analysis.persona_insights) {
result += `## ${analysis.persona_insights.persona_name} Insights\n`;
analysis.persona_insights.specialized_analysis.forEach((insight) => {
result += `- ${insight}\n`;
});
result += '\n**Recommendations:**\n';
analysis.persona_insights.recommendations.forEach((rec, index) => {
result += `${index + 1}. ${rec}\n`;
});
}
return result;
}
}
//# sourceMappingURL=AnalyzeProjectTool.js.map