Skip to main content
Glama

CodeAnalysis MCP Server

by 0xjcf
evolution-planner.ts30.4 kB
import { getRepository, listFiles } from "../../utils/repository-analyzer.js"; import { analyzeCode } from "../basic-analysis/analyzer.js"; import { buildKnowledgeGraph, queryKnowledgeGraph, } from "../knowledge-graph/graph-manager.js"; import { retrieveMemories } from "../memory/memory-manager.js"; import path from "path"; import fs from "fs"; /** * Generate an evolution plan for a codebase based on a specific goal and timeframe */ export async function generateEvolutionPlan( repositoryUrl: string, targetGoal: | "modernize-architecture" | "improve-performance" | "enhance-security" | "reduce-technical-debt", timeframe: "immediate" | "sprint" | "quarter" | "year", includeImplementationDetails: boolean = true ): Promise<any> { console.log( `Generating ${targetGoal} evolution plan for ${repositoryUrl} (${timeframe} timeframe)` ); // Step 1: Clone/update the repository const repoPath = await getRepository(repositoryUrl); // Step 2: Analyze the codebase const files = listFiles(repoPath); const fileAnalyses: Record<string, any> = {}; // Analyze a subset of files to avoid performance issues with large repositories const filesToAnalyze = selectRepresentativeFiles(files, 50); console.log(`Analyzing ${filesToAnalyze.length} representative files...`); for (const file of filesToAnalyze) { try { const fullPath = path.join(repoPath, file); const code = fs.readFileSync(fullPath, "utf8"); const fileLanguage = path.extname(file).slice(1); const analysis = analyzeCode(code, fileLanguage); fileAnalyses[file] = analysis; } catch (error) { console.warn(`Error analyzing file ${file}: ${(error as Error).message}`); } } // Step 3: Build knowledge graph console.log(`Building knowledge graph...`); const { nodes, relationships } = await buildKnowledgeGraph( repositoryUrl, 2, false ); // Step 4: Retrieve insights from memory console.log(`Retrieving memories about this repository...`); const memories = await retrieveMemories({ repositoryUrl, limit: 10, }); // Step 5: Generate the evolution plan based on target goal and timeframe console.log(`Generating ${targetGoal} plan...`); // Analyze major frameworks and libraries used const frameworks = detectFrameworks(files, fileAnalyses); // Analyze project structure const projectStructure = analyzeProjectStructure(files); // Generate plan based on target goal let plan; switch (targetGoal) { case "modernize-architecture": plan = generateModernizeArchitecturePlan( repositoryUrl, frameworks, projectStructure, fileAnalyses, timeframe, includeImplementationDetails ); break; case "improve-performance": plan = generatePerformancePlan( repositoryUrl, frameworks, fileAnalyses, timeframe, includeImplementationDetails ); break; case "enhance-security": plan = generateSecurityPlan( repositoryUrl, frameworks, fileAnalyses, timeframe, includeImplementationDetails ); break; case "reduce-technical-debt": plan = generateTechnicalDebtPlan( repositoryUrl, frameworks, projectStructure, fileAnalyses, timeframe, includeImplementationDetails ); break; default: throw new Error(`Unknown target goal: ${targetGoal}`); } // Return the evolution plan return { repository: { url: repositoryUrl, summary: summarizeRepository( files, fileAnalyses, frameworks, projectStructure ), }, targetGoal, timeframe, plan, }; } /** * Select a representative subset of files to analyze */ function selectRepresentativeFiles( files: string[], maxFiles: number ): string[] { // If we have fewer files than the max, return all files if (files.length <= maxFiles) { return files; } // Group files by extension const filesByExt: Record<string, string[]> = {}; for (const file of files) { const ext = path.extname(file).toLowerCase(); if (!filesByExt[ext]) { filesByExt[ext] = []; } filesByExt[ext].push(file); } // Select representative files from each extension group const selectedFiles: string[] = []; const extensions = Object.keys(filesByExt); // Calculate how many files to select from each extension const filesPerExt = Math.max(1, Math.floor(maxFiles / extensions.length)); for (const ext of extensions) { const extFiles = filesByExt[ext]; // Prioritize files at different directory depths const byDepth: Record<number, string[]> = {}; for (const file of extFiles) { const depth = file.split("/").length; if (!byDepth[depth]) { byDepth[depth] = []; } byDepth[depth].push(file); } const depths = Object.keys(byDepth).map(Number); const selectedForExt: string[] = []; // Select files from each depth level for ( let i = 0; i < filesPerExt && selectedForExt.length < filesPerExt; i++ ) { const depthIndex = i % depths.length; const depth = depths[depthIndex]; if (byDepth[depth].length > 0) { // Take the first file from this depth level selectedForExt.push(byDepth[depth].shift()!); } } selectedFiles.push(...selectedForExt); // If we've selected enough files, stop if (selectedFiles.length >= maxFiles) { break; } } // Include key configuration files const importantFiles = files.filter( (file) => file.includes("package.json") || file.includes("tsconfig.json") || file.includes("webpack.config") || file.includes(".eslintrc") || file.includes("vitest.config") || file.includes("Dockerfile") || file.includes("docker-compose.yml") ); // Merge important files with selected files, ensuring we don't exceed maxFiles const remainingSlots = Math.max(0, maxFiles - selectedFiles.length); const additionalImportantFiles = importantFiles .filter((file) => !selectedFiles.includes(file)) .slice(0, remainingSlots); return [...selectedFiles, ...additionalImportantFiles].slice(0, maxFiles); } /** * Detect frameworks and libraries used in the project */ function detectFrameworks( files: string[], fileAnalyses: Record<string, any> ): Record<string, any> { const frameworks: Record<string, any> = {}; // Check for package.json to identify JS/TS frameworks const hasPackageJson = files.some((file) => file.endsWith("package.json")); if (hasPackageJson) { const packageJsonFile = files.find((file) => file.endsWith("package.json")); if (packageJsonFile) { try { const fullPath = path.join( process.cwd(), "data", "repositories", path.basename(packageJsonFile) ); const packageData = JSON.parse(fs.readFileSync(fullPath, "utf8")); const dependencies = { ...(packageData.dependencies || {}), ...(packageData.devDependencies || {}), }; // Check for common frameworks if (dependencies.react) { frameworks.react = { version: dependencies.react }; } if (dependencies.vue) { frameworks.vue = { version: dependencies.vue }; } if (dependencies.angular || dependencies["@angular/core"]) { frameworks.angular = { version: dependencies.angular || dependencies["@angular/core"], }; } if (dependencies.express) { frameworks.express = { version: dependencies.express }; } if (dependencies.next) { frameworks.next = { version: dependencies.next }; } if (dependencies.typescript) { frameworks.typescript = { version: dependencies.typescript }; } if (dependencies.jest || dependencies["@jest/core"]) { frameworks.jest = { version: dependencies.jest || dependencies["@jest/core"], }; } if (dependencies.vitest) { frameworks.vitest = { version: dependencies.vitest }; } // Store all dependencies for reference frameworks.allDependencies = dependencies; } catch (error) { console.warn(`Error parsing package.json: ${(error as Error).message}`); } } } // Check for Python frameworks const hasPythonFiles = files.some((file) => file.endsWith(".py")); if (hasPythonFiles) { const requirementsFile = files.find((file) => file.endsWith("requirements.txt") ); if (requirementsFile) { try { const fullPath = path.join( process.cwd(), "data", "repositories", path.basename(requirementsFile) ); const requirements = fs.readFileSync(fullPath, "utf8").split("\n"); if (requirements.some((r) => r.includes("django"))) { frameworks.django = { detected: true }; } if (requirements.some((r) => r.includes("flask"))) { frameworks.flask = { detected: true }; } if (requirements.some((r) => r.includes("fastapi"))) { frameworks.fastapi = { detected: true }; } } catch (error) { console.warn( `Error parsing requirements.txt: ${(error as Error).message}` ); } } } return frameworks; } /** * Analyze the project structure */ function analyzeProjectStructure(files: string[]): Record<string, any> { const directoryStructure: Record<string, number> = {}; // Count files per directory for (const file of files) { const dir = path.dirname(file); directoryStructure[dir] = (directoryStructure[dir] || 0) + 1; } // Identify top-level directories const topLevelDirs = new Set<string>(); for (const dir of Object.keys(directoryStructure)) { const topLevel = dir.split("/")[0] || "."; topLevelDirs.add(topLevel); } return { directoryStructure, topLevelDirectories: Array.from(topLevelDirs), fileCount: files.length, }; } /** * Generate a plan for modernizing architecture */ function generateModernizeArchitecturePlan( repositoryUrl: string, frameworks: Record<string, any>, projectStructure: Record<string, any>, fileAnalyses: Record<string, any>, timeframe: "immediate" | "sprint" | "quarter" | "year", includeImplementationDetails: boolean ): any { const recommendations: any[] = []; // Check for outdated frameworks if (frameworks.react) { const version = frameworks.react.version; if ((version && version.startsWith("15.")) || version.startsWith("16.")) { recommendations.push({ title: "Upgrade React to the latest version", description: "The project is using an older version of React. Upgrading will provide access to the latest features, performance improvements, and security patches.", priority: "high", effort: "medium", impact: "high", }); } } // Check for component architecture const hasComponentDir = projectStructure.topLevelDirectories.some( (dir: string) => dir === "components" || dir === "Components" ); if ( !hasComponentDir && (frameworks.react || frameworks.vue || frameworks.angular) ) { recommendations.push({ title: "Implement a clear component architecture", description: "Organize components into a dedicated directory structure to improve code organization and reusability.", priority: "medium", effort: "medium", impact: "high", }); } // Check for TypeScript usage if ( !frameworks.typescript && (frameworks.react || frameworks.vue || frameworks.angular) ) { recommendations.push({ title: "Migrate to TypeScript", description: "Adding TypeScript will improve type safety, developer experience, and make the codebase more maintainable.", priority: "medium", effort: "high", impact: "high", }); } // Generate timeframe-specific recommendations const timeframeRecommendations = filterRecommendationsByTimeframe( recommendations, timeframe ); // Add implementation details if requested if (includeImplementationDetails) { for (const rec of timeframeRecommendations) { rec.implementationSteps = generateImplementationSteps( rec.title, frameworks ); } } return { summary: `The architecture modernization plan focuses on ${timeframeRecommendations.length} key areas to improve over the ${timeframe} timeframe.`, recommendations: timeframeRecommendations, suggestedArchitecture: generateSuggestedArchitecture( frameworks, projectStructure ), }; } /** * Generate a plan for improving performance */ function generatePerformancePlan( repositoryUrl: string, frameworks: Record<string, any>, fileAnalyses: Record<string, any>, timeframe: "immediate" | "sprint" | "quarter" | "year", includeImplementationDetails: boolean ): any { const recommendations: any[] = []; // Check for React performance opportunities if (frameworks.react) { recommendations.push({ title: "Implement React.memo for functional components", description: "Prevent unnecessary re-renders by memoizing functional components.", priority: "medium", effort: "low", impact: "medium", }); recommendations.push({ title: "Implement code splitting with React.lazy", description: "Improve initial load time by splitting your code into smaller chunks.", priority: "high", effort: "medium", impact: "high", }); } // General performance recommendations recommendations.push({ title: "Optimize asset loading", description: "Implement lazy loading for images and other assets to improve page load times.", priority: "medium", effort: "medium", impact: "high", }); recommendations.push({ title: "Implement caching strategies", description: "Add appropriate caching for API responses and static assets.", priority: "high", effort: "medium", impact: "high", }); // Database query optimization recommendations const hasDatabase = detectDatabaseUsage(fileAnalyses); if (hasDatabase) { recommendations.push({ title: "Optimize database queries", description: "Review and optimize database queries for performance.", priority: "high", effort: "high", impact: "high", }); } // Generate timeframe-specific recommendations const timeframeRecommendations = filterRecommendationsByTimeframe( recommendations, timeframe ); // Add implementation details if requested if (includeImplementationDetails) { for (const rec of timeframeRecommendations) { rec.implementationSteps = generateImplementationSteps( rec.title, frameworks ); } } return { summary: `The performance improvement plan focuses on ${timeframeRecommendations.length} key areas to optimize over the ${timeframe} timeframe.`, recommendations: timeframeRecommendations, }; } /** * Generate a plan for enhancing security */ function generateSecurityPlan( repositoryUrl: string, frameworks: Record<string, any>, fileAnalyses: Record<string, any>, timeframe: "immediate" | "sprint" | "quarter" | "year", includeImplementationDetails: boolean ): any { const recommendations: any[] = []; // Check for dependency vulnerabilities (this would normally use a security scanner) recommendations.push({ title: "Update dependencies to address security vulnerabilities", description: "Run security scanning tools and update dependencies with known vulnerabilities.", priority: "high", effort: "medium", impact: "high", }); // Authentication and authorization recommendations recommendations.push({ title: "Implement proper authentication and authorization", description: "Ensure all endpoints and resources are properly protected with authentication and authorization.", priority: "high", effort: "high", impact: "high", }); // Input validation recommendations.push({ title: "Implement comprehensive input validation", description: "Add proper validation for all user inputs to prevent injection attacks.", priority: "high", effort: "medium", impact: "high", }); // Web vulnerabilities if (frameworks.react || frameworks.vue || frameworks.angular) { recommendations.push({ title: "Add Content Security Policy (CSP)", description: "Implement CSP headers to prevent XSS attacks.", priority: "medium", effort: "medium", impact: "high", }); } // API security if (frameworks.express || detectApiEndpoints(fileAnalyses)) { recommendations.push({ title: "Implement API rate limiting", description: "Add rate limiting to API endpoints to prevent abuse.", priority: "medium", effort: "low", impact: "medium", }); } // Generate timeframe-specific recommendations const timeframeRecommendations = filterRecommendationsByTimeframe( recommendations, timeframe ); // Add implementation details if requested if (includeImplementationDetails) { for (const rec of timeframeRecommendations) { rec.implementationSteps = generateImplementationSteps( rec.title, frameworks ); } } return { summary: `The security enhancement plan focuses on ${timeframeRecommendations.length} key areas to improve over the ${timeframe} timeframe.`, recommendations: timeframeRecommendations, }; } /** * Generate a plan for reducing technical debt */ function generateTechnicalDebtPlan( repositoryUrl: string, frameworks: Record<string, any>, projectStructure: Record<string, any>, fileAnalyses: Record<string, any>, timeframe: "immediate" | "sprint" | "quarter" | "year", includeImplementationDetails: boolean ): any { const recommendations: any[] = []; // Code quality recommendations recommendations.push({ title: "Implement consistent code formatting", description: "Add tooling for automatic code formatting (e.g., Prettier, ESLint) to ensure consistent code style.", priority: "medium", effort: "low", impact: "medium", }); recommendations.push({ title: "Increase test coverage", description: "Add unit and integration tests to improve code reliability and enable safer refactoring.", priority: "high", effort: "high", impact: "high", }); // Code organization recommendations.push({ title: "Refactor directory structure", description: "Organize code by feature or domain rather than by technical layer to improve maintainability.", priority: "medium", effort: "high", impact: "high", }); // Documentation recommendations.push({ title: "Improve documentation", description: "Add or update documentation for key components, APIs, and architecture decisions.", priority: "medium", effort: "medium", impact: "high", }); // Deprecated APIs and patterns if (detectDeprecatedApiUsage(fileAnalyses, frameworks)) { recommendations.push({ title: "Replace deprecated APIs and patterns", description: "Identify and replace usage of deprecated APIs and patterns.", priority: "high", effort: "high", impact: "medium", }); } // Generate timeframe-specific recommendations const timeframeRecommendations = filterRecommendationsByTimeframe( recommendations, timeframe ); // Add implementation details if requested if (includeImplementationDetails) { for (const rec of timeframeRecommendations) { rec.implementationSteps = generateImplementationSteps( rec.title, frameworks ); } } return { summary: `The technical debt reduction plan focuses on ${timeframeRecommendations.length} key areas to improve over the ${timeframe} timeframe.`, recommendations: timeframeRecommendations, }; } /** * Filter recommendations based on timeframe */ function filterRecommendationsByTimeframe( recommendations: any[], timeframe: "immediate" | "sprint" | "quarter" | "year" ): any[] { // Sort recommendations by priority and effort const sortedRecs = [...recommendations].sort((a, b) => { const priorityScore = (rec: any) => rec.priority === "high" ? 3 : rec.priority === "medium" ? 2 : 1; const effortScore = (rec: any) => rec.effort === "low" ? 3 : rec.effort === "medium" ? 2 : 1; const aScore = priorityScore(a) * effortScore(a); const bScore = priorityScore(b) * effortScore(b); return bScore - aScore; // Higher score first }); // Select recommendations based on timeframe switch (timeframe) { case "immediate": // Only high priority, low effort items return sortedRecs .filter((rec) => rec.priority === "high" && rec.effort === "low") .slice(0, 3); case "sprint": // High and medium priority items with low to medium effort return sortedRecs .filter( (rec) => (rec.priority === "high" || rec.priority === "medium") && (rec.effort === "low" || rec.effort === "medium") ) .slice(0, 5); case "quarter": // All high priority items plus medium priority with high impact return sortedRecs.filter( (rec) => rec.priority === "high" || (rec.priority === "medium" && rec.impact === "high") ); case "year": // All recommendations return sortedRecs; default: return sortedRecs; } } /** * Generate implementation steps for a recommendation */ function generateImplementationSteps( title: string, frameworks: Record<string, any> ): string[] { // This would normally contain more detailed, recommendation-specific steps // For now, we'll provide some generic steps based on the recommendation title if (title.includes("React.memo")) { return [ "Identify components that render frequently but rarely change", "Wrap these components with React.memo", "Add proper dependency arrays to useEffect and useCallback hooks", "Test performance before and after changes", ]; } if (title.includes("code splitting")) { return [ "Identify large components that aren't needed on initial load", "Use React.lazy and Suspense to split these components", "Add loading fallbacks for lazy-loaded components", "Test loading performance before and after changes", ]; } if (title.includes("TypeScript")) { return [ "Set up TypeScript configuration", "Gradually convert files from .js to .ts", "Add interfaces for component props", "Add type definitions for API responses", "Configure ESLint and other tools to work with TypeScript", ]; } // Generic steps for other recommendations return [ "Analyze current implementation", "Research best practices", "Create implementation plan", "Implement changes", "Test and validate", "Document changes", ]; } /** * Generate a suggested architecture based on frameworks and project structure */ function generateSuggestedArchitecture( frameworks: Record<string, any>, projectStructure: Record<string, any> ): any { if (frameworks.react) { return { type: "Modern React Architecture", structure: [ { directory: "src/components", description: "Reusable UI components organized by feature", }, { directory: "src/hooks", description: "Custom React hooks for shared logic", }, { directory: "src/contexts", description: "React context providers for state management", }, { directory: "src/services", description: "API and external service integrations", }, { directory: "src/utils", description: "Utility functions and helpers", }, { directory: "src/types", description: "TypeScript type definitions", }, ], }; } if (frameworks.express) { return { type: "Modern Express Architecture", structure: [ { directory: "src/controllers", description: "Route handlers and controller logic", }, { directory: "src/services", description: "Business logic and external service integration", }, { directory: "src/models", description: "Data models and database schema", }, { directory: "src/middleware", description: "Express middleware functions", }, { directory: "src/utils", description: "Utility functions and helpers", }, { directory: "src/routes", description: "API route definitions", }, ], }; } // Default suggested architecture return { type: "Domain-Driven Architecture", structure: [ { directory: "src/features", description: "Code organized by feature or domain, each with its own models, services, and utilities", }, { directory: "src/shared", description: "Shared components, utilities, and types used across features", }, { directory: "src/core", description: "Core business logic and application services", }, { directory: "src/infrastructure", description: "Database, API clients, and other infrastructure concerns", }, ], }; } /** * Generate a summary of the repository */ function summarizeRepository( files: string[], fileAnalyses: Record<string, any>, frameworks: Record<string, any>, projectStructure: Record<string, any> ): any { // Count files by extension const fileExtensions: Record<string, number> = {}; for (const file of files) { const ext = path.extname(file).toLowerCase(); if (ext) { fileExtensions[ext] = (fileExtensions[ext] || 0) + 1; } } // Extract primary language let primaryLanguage = "unknown"; let maxCount = 0; for (const [ext, count] of Object.entries(fileExtensions)) { if (count > maxCount) { maxCount = count; switch (ext) { case ".js": primaryLanguage = "JavaScript"; break; case ".ts": case ".tsx": primaryLanguage = "TypeScript"; break; case ".py": primaryLanguage = "Python"; break; case ".java": primaryLanguage = "Java"; break; case ".go": primaryLanguage = "Go"; break; case ".rb": primaryLanguage = "Ruby"; break; default: primaryLanguage = ext.slice(1).toUpperCase(); } } } // Determine project type based on frameworks and file structure let projectType = "unknown"; if (frameworks.react || frameworks.vue || frameworks.angular) { projectType = "Frontend Application"; } else if (frameworks.express || frameworks.django || frameworks.flask) { projectType = "Backend Application"; } else if (frameworks.next) { projectType = "Full-Stack Application"; } else if ( files.some( (file) => file.includes("Dockerfile") || file.includes("docker-compose.yml") ) ) { projectType = "Containerized Application"; } return { totalFiles: files.length, primaryLanguage, projectType, frameworks: Object.keys(frameworks).filter( (key) => key !== "allDependencies" ), topLevelDirectories: projectStructure.topLevelDirectories, fileExtensions, }; } /** * Detect if the codebase uses a database */ function detectDatabaseUsage(fileAnalyses: Record<string, any>): boolean { // This is a simplistic implementation - in reality, we would do deeper analysis // Look for common database imports in the file analyses for (const analysis of Object.values(fileAnalyses)) { if (!analysis.imports) continue; for (const importItem of analysis.imports) { if ( importItem.includes("mysql") || importItem.includes("postgres") || importItem.includes("sqlite") || importItem.includes("mongodb") || importItem.includes("mongoose") || importItem.includes("sequelize") || importItem.includes("typeorm") || importItem.includes("prisma") ) { return true; } } } return false; } /** * Detect if the codebase contains API endpoints */ function detectApiEndpoints(fileAnalyses: Record<string, any>): boolean { // This is a simplistic implementation - in reality, we would do deeper analysis // Look for common API patterns in the file analyses for (const analysis of Object.values(fileAnalyses)) { if (!analysis.content) continue; const content = analysis.content.toLowerCase(); if ( content.includes("router.get") || content.includes("router.post") || content.includes("app.get") || content.includes("app.post") || content.includes("@get") || content.includes("@post") || content.includes("handlerequest") ) { return true; } } return false; } /** * Detect if the codebase uses deprecated APIs */ function detectDeprecatedApiUsage( fileAnalyses: Record<string, any>, frameworks: Record<string, any> ): boolean { // This is a simplistic implementation - in reality, we would check specific APIs // Check for old React lifecycle methods if using React if (frameworks.react) { for (const analysis of Object.values(fileAnalyses)) { if (!analysis.content) continue; const content = analysis.content; if ( content.includes("componentWillMount") || content.includes("componentWillReceiveProps") || content.includes("componentWillUpdate") ) { return true; } } } return false; }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/0xjcf/MCP_CodeAnalysis'

If you have feedback or need assistance with the MCP directory API, please join our Discord server