UNPKG

context-forge

Version:

AI orchestration platform with autonomous teams, enhancement planning, migration tools, 25+ slash commands, checkpoints & hooks. Multi-IDE: Claude, Cursor, Windsurf, Cline, Copilot

722 lines 31.3 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.MigrationAnalyzer = void 0; const fs_extra_1 = __importDefault(require("fs-extra")); const path_1 = __importDefault(require("path")); const frameworkDetector_1 = require("./frameworkDetector"); const breakingChangeAnalyzer_1 = require("./breakingChangeAnalyzer"); const dependencyAnalyzer_1 = require("./dependencyAnalyzer"); class MigrationAnalyzer { constructor(sourcePath, targetTechStack) { this.sourcePath = sourcePath; this.targetTechStack = targetTechStack; } async analyzeMigration(basicAnalysis) { const sourceStack = await this.detectCurrentStack(basicAnalysis); const targetStack = this.completeTargetStack(this.targetTechStack); const sharedResources = await this.detectSharedResources(); // Analyze breaking changes if we have framework information let breakingChanges = []; let breakingChangesSummary; if (sourceStack.metadata && targetStack.name) { const breakingAnalyzer = new breakingChangeAnalyzer_1.BreakingChangeAnalyzer(); const sourceFramework = sourceStack.metadata.detectedFrameworks[0]; const targetFramework = { framework: targetStack.name.toLowerCase(), version: targetStack.version, confidence: 100, }; if (sourceFramework && targetFramework) { const breakingAnalysis = await breakingAnalyzer.analyzeBreakingChanges(sourceFramework, targetFramework); breakingChanges = breakingAnalysis.breakingChanges; breakingChangesSummary = { total: breakingChanges.length, critical: breakingAnalysis.criticalCount, automatable: breakingAnalysis.automatableCount, estimatedHours: breakingAnalysis.estimatedHours, }; } } // Analyze dependencies let dependencyAnalysis; const dependencyAnalyzer = new dependencyAnalyzer_1.DependencyAnalyzer(); const sourceFrameworkName = sourceStack.metadata?.detectedFrameworks[0]?.framework || sourceStack.name.toLowerCase(); const targetFrameworkName = targetStack.name.toLowerCase(); dependencyAnalysis = await dependencyAnalyzer.analyzeDependencies(this.sourcePath, sourceFrameworkName, targetFrameworkName); const risks = this.assessMigrationRisks(sourceStack, targetStack, sharedResources, breakingChanges, dependencyAnalysis); const complexity = this.calculateComplexity(sourceStack, targetStack, sharedResources, risks, breakingChanges, dependencyAnalysis); const suggestedPhases = this.generateMigrationPhases(sourceStack, targetStack, complexity, breakingChanges, dependencyAnalysis); return { sourceStack, targetStack, complexity, risks, sharedResources, suggestedPhases, estimatedDuration: this.estimateDuration(complexity, suggestedPhases), recommendedStrategy: this.recommendStrategy(complexity, sharedResources), breakingChanges, breakingChangesSummary, dependencyAnalysis, }; } async detectCurrentStack(analysis) { // Use FrameworkDetector for accurate detection const detector = new frameworkDetector_1.FrameworkDetector(this.sourcePath); const detectionResult = await detector.detectFrameworks(); const stack = { name: 'Current Stack', type: 'fullstack', docs: '', dependencies: [], devDependencies: [], }; // Use primary detected framework if (detectionResult.primary) { stack.name = detectionResult.primary.variant || detectionResult.primary.framework; if (detectionResult.primary.version) { stack.version = detectionResult.primary.version; } } else { // Fallback to basic analysis if (analysis.techStack.includes('React')) stack.name = 'React'; if (analysis.techStack.includes('Next.js')) stack.name = 'Next.js'; if (analysis.techStack.includes('Express')) stack.name = 'Express'; if (analysis.techStack.includes('Flask')) stack.name = 'Flask'; if (analysis.techStack.includes('Django')) stack.name = 'Django'; } // Detect from package.json or requirements.txt const packageJsonPath = path_1.default.join(this.sourcePath, 'package.json'); const requirementsPath = path_1.default.join(this.sourcePath, 'requirements.txt'); if (fs_extra_1.default.existsSync(packageJsonPath)) { const packageJson = fs_extra_1.default.readJsonSync(packageJsonPath); stack.dependencies = Object.keys(packageJson.dependencies || {}); stack.devDependencies = Object.keys(packageJson.devDependencies || {}); } if (fs_extra_1.default.existsSync(requirementsPath)) { const requirements = fs_extra_1.default.readFileSync(requirementsPath, 'utf-8'); stack.dependencies = requirements.split('\n').filter((line) => line.trim()); } // Add framework detection confidence as metadata if (detectionResult.primary) { stack.metadata = { confidence: detectionResult.primary.confidence, detectedFrameworks: detectionResult.allDetected, }; } return stack; } completeTargetStack(partial) { return { name: partial.name || 'Target Stack', type: partial.type || 'fullstack', docs: partial.docs || '', dependencies: partial.dependencies || [], devDependencies: partial.devDependencies || [], ...partial, }; } async detectSharedResources() { const resources = []; // Check for database connections const envPath = path_1.default.join(this.sourcePath, '.env'); const envExamplePath = path_1.default.join(this.sourcePath, '.env.example'); if (fs_extra_1.default.existsSync(envPath) || fs_extra_1.default.existsSync(envExamplePath)) { const envContent = fs_extra_1.default.existsSync(envPath) ? fs_extra_1.default.readFileSync(envPath, 'utf-8') : fs_extra_1.default.readFileSync(envExamplePath, 'utf-8'); if (envContent.includes('DATABASE_URL') || envContent.includes('DB_')) { resources.push({ type: 'database', name: 'Production Database', description: 'Shared database connection detected in environment', criticalityLevel: 'critical', migrationStrategy: 'Maintain compatibility during migration', }); } if (envContent.includes('REDIS_URL') || envContent.includes('CACHE_')) { resources.push({ type: 'cache', name: 'Redis Cache', description: 'Shared cache instance detected', criticalityLevel: 'medium', migrationStrategy: 'Gradual cache key migration', }); } if (envContent.includes('AUTH_') || envContent.includes('JWT_')) { resources.push({ type: 'auth', name: 'Authentication System', description: 'Shared authentication configuration', criticalityLevel: 'critical', migrationStrategy: 'Maintain session compatibility', }); } } // Check for API endpoints const routesExist = await this.checkForRoutes(); if (routesExist) { resources.push({ type: 'api', name: 'API Endpoints', description: 'Existing API contracts that may have consumers', criticalityLevel: 'high', migrationStrategy: 'Version APIs or maintain backward compatibility', }); } return resources; } async checkForRoutes() { const patterns = [ '**/routes/**/*.{js,ts,py}', '**/api/**/*.{js,ts,py}', '**/views/**/*.py', '**/controllers/**/*.{js,ts,py}', ]; // Simple check for route files for (const pattern of patterns) { const routePath = path_1.default.join(this.sourcePath, pattern.split('**/')[1].split('/**')[0]); if (fs_extra_1.default.existsSync(routePath)) { return true; } } return false; } assessMigrationRisks(sourceStack, targetStack, sharedResources, breakingChanges = [], dependencyAnalysis) { const risks = []; // Framework migration risks if (sourceStack.name !== targetStack.name) { risks.push({ category: 'compatibility', description: `Framework migration from ${sourceStack.name} to ${targetStack.name}`, probability: 'high', impact: 'high', mitigation: 'Use adapter patterns and gradual component migration', }); } // Shared database risks const hasSharedDb = sharedResources.some((r) => r.type === 'database'); if (hasSharedDb) { risks.push({ category: 'data-loss', description: 'Shared database requires careful schema management', probability: 'medium', impact: 'critical', mitigation: 'Use database migrations with rollback capability', }); } // Authentication risks const hasSharedAuth = sharedResources.some((r) => r.type === 'auth'); if (hasSharedAuth) { risks.push({ category: 'security', description: 'Authentication system must remain compatible', probability: 'medium', impact: 'critical', mitigation: 'Implement session sharing or token compatibility layer', }); } // API compatibility risks const hasApi = sharedResources.some((r) => r.type === 'api'); if (hasApi) { risks.push({ category: 'compatibility', description: 'API contracts must be maintained for existing consumers', probability: 'high', impact: 'high', mitigation: 'Version APIs or use API gateway for routing', }); } // Breaking change risks if (breakingChanges.length > 0) { const criticalChanges = breakingChanges.filter((c) => c.severity === 'critical'); if (criticalChanges.length > 0) { risks.push({ category: 'compatibility', description: `${criticalChanges.length} critical breaking changes require attention`, probability: 'high', impact: 'critical', mitigation: 'Address all critical breaking changes before migration', }); } const nonAutomatable = breakingChanges.filter((c) => !c.automatable); if (nonAutomatable.length > 0) { risks.push({ category: 'compatibility', description: `${nonAutomatable.length} breaking changes require manual migration`, probability: 'high', impact: 'high', mitigation: 'Allocate time for manual code updates and testing', }); } } // Dependency risks if (dependencyAnalysis && dependencyAnalysis.incompatibleCount > 0) { risks.push({ category: 'compatibility', description: `${dependencyAnalysis.incompatibleCount} incompatible dependencies detected`, probability: 'high', impact: dependencyAnalysis.incompatibleCount > 10 ? 'critical' : 'high', mitigation: 'Replace incompatible packages with suggested alternatives', }); } if (dependencyAnalysis && dependencyAnalysis.migrationComplexity === 'high') { risks.push({ category: 'compatibility', description: 'High dependency migration complexity', probability: 'high', impact: 'high', mitigation: 'Plan phased dependency migration with thorough testing', }); } // Performance risks risks.push({ category: 'performance', description: 'Parallel run strategy may impact system performance', probability: 'medium', impact: 'medium', mitigation: 'Implement load balancing and resource monitoring', }); return risks; } calculateComplexity(sourceStack, targetStack, sharedResources, risks, breakingChanges = [], dependencyAnalysis) { const factors = []; let totalScore = 0; // Framework difference if (sourceStack.name !== targetStack.name) { const frameworkComplexity = this.getFrameworkComplexity(sourceStack.name, targetStack.name); factors.push({ name: 'Framework Migration', impact: frameworkComplexity, description: `Migrating from ${sourceStack.name} to ${targetStack.name}`, }); totalScore += frameworkComplexity * 10; } // Shared resources const resourceComplexity = Math.min(sharedResources.length * 2, 10); factors.push({ name: 'Shared Resources', impact: resourceComplexity, description: `${sharedResources.length} shared resources to maintain`, }); totalScore += resourceComplexity * 5; // Risk factors const criticalRisks = risks.filter((r) => r.impact === 'critical').length; const riskComplexity = Math.min(criticalRisks * 3, 10); factors.push({ name: 'Critical Risks', impact: riskComplexity, description: `${criticalRisks} critical risk factors identified`, }); totalScore += riskComplexity * 7; // Breaking changes complexity if (breakingChanges.length > 0) { const breakingComplexity = Math.min(breakingChanges.length, 10); const manualChanges = breakingChanges.filter((c) => !c.automatable).length; factors.push({ name: 'Breaking Changes', impact: breakingComplexity, description: `${breakingChanges.length} breaking changes (${manualChanges} manual)`, }); totalScore += breakingComplexity * 5; } // Dependency complexity if (dependencyAnalysis) { const depComplexity = dependencyAnalysis.migrationComplexity === 'high' ? 8 : dependencyAnalysis.migrationComplexity === 'medium' ? 5 : 2; factors.push({ name: 'Dependency Migration', impact: depComplexity, description: `${dependencyAnalysis.incompatibleCount}/${dependencyAnalysis.totalDependencies} incompatible dependencies`, }); totalScore += depComplexity * 4; } // Determine level let level; if (totalScore < 30) level = 'low'; else if (totalScore < 60) level = 'medium'; else if (totalScore < 80) level = 'high'; else level = 'critical'; return { score: Math.min(totalScore, 100), factors, level, }; } getFrameworkComplexity(source, target) { // Define complexity matrix for common migrations const complexityMatrix = { Flask: { FastAPI: 4, Django: 6, Express: 8, 'Next.js': 9, }, Django: { FastAPI: 5, Flask: 4, Express: 8, 'Next.js': 9, }, Express: { Fastify: 3, 'Next.js': 5, Flask: 8, Django: 9, }, React: { 'Next.js': 3, Vue: 7, Angular: 8, }, }; return complexityMatrix[source]?.[target] || 7; // Default complexity } generateMigrationPhases(sourceStack, targetStack, complexity, breakingChanges = [], dependencyAnalysis) { const phases = []; // Phase 1: Setup and Planning phases.push({ id: 'setup', name: 'Setup and Planning', description: 'Initialize new project structure and migration tools', criticalCheckpoints: [ { id: 'env-setup', name: 'Environment Setup', description: 'New environment configured with shared resources', category: 'critical', autoTrigger: true, conditions: ['environment', 'config', 'setup'], }, ], dependencies: [], rollbackPoint: false, estimatedDuration: '1-2 days', risks: [], validationCriteria: [ 'New project structure created', 'Development environment running', 'Shared resources accessible', ], }); // Phase 2: Core Infrastructure phases.push({ id: 'infrastructure', name: 'Core Infrastructure Migration', description: 'Migrate core services, authentication, and database connections', criticalCheckpoints: [ { id: 'db-connection', name: 'Database Connection', description: 'Verify database connectivity and compatibility', category: 'critical', autoTrigger: true, conditions: ['database', 'connection', 'schema'], }, ], dependencies: ['setup'], rollbackPoint: true, estimatedDuration: '3-5 days', risks: complexity.factors .filter((f) => f.name === 'Shared Resources') .map((f) => ({ category: 'compatibility', description: f.description, probability: 'medium', impact: 'high', mitigation: 'Test thoroughly in staging', })), validationCriteria: [ 'Database connections working', 'Authentication system compatible', 'Core services operational', ], }); // Phase 3: Breaking Changes (if needed) if (breakingChanges.length > 0) { const automatableChanges = breakingChanges.filter((c) => c.automatable); phases.push({ id: 'breaking-changes', name: 'Breaking Changes Resolution', description: `Address ${breakingChanges.length} breaking changes (${automatableChanges.length} automatable)`, criticalCheckpoints: [ { id: 'breaking-changes-review', name: 'Breaking Changes Review', description: 'Review and plan approach for each breaking change', category: 'critical', autoTrigger: false, conditions: ['breaking', 'changes', 'documented'], }, { id: 'automated-changes', name: 'Automated Changes Applied', description: 'Run and verify automated migration scripts', category: 'important', autoTrigger: true, conditions: ['automated', 'migration', 'complete'], }, ], dependencies: ['infrastructure'], rollbackPoint: true, estimatedDuration: `${Math.ceil(breakingChanges.length / 5)}-${Math.ceil(breakingChanges.length / 2)} days`, risks: [ { category: 'compatibility', description: 'Breaking changes may introduce new issues', probability: 'medium', impact: 'high', mitigation: 'Thorough testing after each change', }, ], validationCriteria: [ 'All breaking changes addressed', 'Code compiles without errors', 'Existing tests updated', 'No regression issues', ], }); } // Phase 4: Dependency Migration (if needed) if (dependencyAnalysis && dependencyAnalysis.incompatibleCount > 0) { phases.push({ id: 'dependencies', name: 'Dependency Migration', description: `Update ${dependencyAnalysis.incompatibleCount} incompatible dependencies`, criticalCheckpoints: [ { id: 'dependency-analysis', name: 'Dependency Analysis Review', description: 'Review replacement suggestions and migration paths', category: 'important', autoTrigger: false, conditions: ['dependencies', 'analyzed', 'replacements'], }, { id: 'dependency-testing', name: 'Dependency Compatibility Test', description: 'Test new dependencies in isolation', category: 'critical', autoTrigger: true, conditions: ['dependencies', 'installed', 'tested'], }, ], dependencies: breakingChanges.length > 0 ? ['breaking-changes'] : ['infrastructure'], rollbackPoint: true, estimatedDuration: `${Math.ceil(dependencyAnalysis.incompatibleCount / 10)}-${Math.ceil(dependencyAnalysis.incompatibleCount / 5)} days`, risks: [ { category: 'compatibility', description: 'New dependencies may have different APIs', probability: 'medium', impact: 'medium', mitigation: 'Test each dependency replacement thoroughly', }, ], validationCriteria: [ 'All incompatible dependencies replaced', 'Package installation successful', 'No version conflicts', 'Application builds successfully', ], }); } // Phase 5: Feature Migration (can be broken down further) const previousDeps = [ ...(breakingChanges.length > 0 ? ['breaking-changes'] : ['infrastructure']), ...(dependencyAnalysis && dependencyAnalysis.incompatibleCount > 0 ? ['dependencies'] : []), ]; phases.push({ id: 'features', name: 'Feature Migration', description: 'Migrate application features incrementally', criticalCheckpoints: [ { id: 'feature-parity', name: 'Feature Parity Check', description: 'Verify migrated features match original functionality', category: 'important', autoTrigger: false, conditions: ['feature', 'complete', 'tested'], }, ], dependencies: previousDeps, rollbackPoint: true, estimatedDuration: '2-4 weeks', risks: [], validationCriteria: [ 'Core features migrated', 'Unit tests passing', 'Integration tests passing', ], }); // Phase 4: Data Migration (if needed) if (complexity.factors.some((f) => f.name.includes('Shared Resources'))) { phases.push({ id: 'data', name: 'Data Migration', description: 'Migrate or transform data structures if needed', criticalCheckpoints: [ { id: 'data-integrity', name: 'Data Integrity Check', description: 'Verify all data migrated correctly', category: 'critical', autoTrigger: true, conditions: ['data', 'migration', 'integrity'], }, ], dependencies: ['features'], rollbackPoint: true, estimatedDuration: '1-2 weeks', risks: [ { category: 'data-loss', description: 'Potential data inconsistency during migration', probability: 'low', impact: 'critical', mitigation: 'Implement comprehensive backup and validation', }, ], validationCriteria: [ 'Data integrity verified', 'No data loss confirmed', 'Performance benchmarks met', ], }); } // Phase 5: Cutover phases.push({ id: 'cutover', name: 'Production Cutover', description: 'Switch production traffic to new system', criticalCheckpoints: [ { id: 'go-live', name: 'Go Live Decision', description: 'Final approval before production cutover', category: 'critical', autoTrigger: false, conditions: ['production', 'ready', 'approved'], }, ], dependencies: phases.map((p) => p.id), rollbackPoint: true, estimatedDuration: '1-2 days', risks: [ { category: 'downtime', description: 'Potential service disruption during cutover', probability: 'medium', impact: 'high', mitigation: 'Use blue-green deployment or gradual rollout', }, ], validationCriteria: [ 'All systems operational', 'Performance metrics acceptable', 'Rollback plan tested and ready', ], }); return phases; } estimateDuration(complexity, phases) { const baseDuration = phases.reduce((total, phase) => { const [min, max] = this.parseDuration(phase.estimatedDuration); return total + (min + max) / 2; }, 0); const complexityMultiplier = 1 + complexity.score / 100; const totalDays = Math.ceil(baseDuration * complexityMultiplier); if (totalDays < 14) return `${totalDays} days`; if (totalDays < 60) return `${Math.ceil(totalDays / 7)} weeks`; return `${Math.ceil(totalDays / 30)} months`; } parseDuration(duration) { const dayMatch = duration.match(/(\d+)-?(\d+)?\s*days?/); const weekMatch = duration.match(/(\d+)-?(\d+)?\s*weeks?/); if (dayMatch) { const min = parseInt(dayMatch[1]); const max = parseInt(dayMatch[2] || dayMatch[1]); return [min, max]; } if (weekMatch) { const min = parseInt(weekMatch[1]) * 7; const max = parseInt(weekMatch[2] || weekMatch[1]) * 7; return [min, max]; } return [7, 14]; // Default } recommendStrategy(complexity, sharedResources) { const hasCriticalShared = sharedResources.some((r) => r.criticalityLevel === 'critical'); if (complexity.level === 'low' && !hasCriticalShared) { return 'big-bang'; } if (complexity.level === 'critical' || hasCriticalShared) { return 'parallel-run'; } return 'incremental'; } async generateRollbackStrategy(phases, sharedResources) { const hasDatabase = sharedResources.some((r) => r.type === 'database'); return { automatic: false, // Default to manual rollback for safety triggers: [ { condition: 'Critical error in production', severity: 'critical', action: 'rollback', }, { condition: 'Data integrity check failed', severity: 'critical', action: 'rollback', }, { condition: 'Performance degradation > 50%', severity: 'error', action: 'pause', }, ], procedures: phases .filter((p) => p.rollbackPoint) .map((phase) => ({ phase: phase.id, steps: [ `Stop all services for ${phase.name}`, 'Restore previous configuration', hasDatabase ? 'Execute database rollback script' : 'Skip database rollback', 'Restart services with old configuration', 'Verify system functionality', ], verificationPoints: [ 'Old system responding correctly', 'Data integrity verified', 'No active errors in logs', ], estimatedDuration: '30-60 minutes', })), dataBackupRequired: hasDatabase, estimatedTime: '1-2 hours', }; } } exports.MigrationAnalyzer = MigrationAnalyzer; //# sourceMappingURL=migrationAnalyzer.js.map