UNPKG

code-transmute

Version:

Convert any codebase into any language — without changing its brain.

299 lines • 13.7 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.PlanCommand = void 0; const chalk_1 = __importDefault(require("chalk")); const path_1 = __importDefault(require("path")); const fs_extra_1 = __importDefault(require("fs-extra")); const config_1 = require("../core/config"); const openai_1 = require("../core/openai"); class PlanCommand { configManager; openaiService; constructor() { this.configManager = config_1.ConfigManager.getInstance(); this.openaiService = new openai_1.OpenAIService(); } async execute(options) { const config = await this.loadConfig(options); await this.openaiService.initialize(config); const outputPath = options.output || path_1.default.join(config.projectPath, 'code-transmute-output'); await fs_extra_1.default.ensureDir(outputPath); // Load existing review if available const reviewPath = path_1.default.join(outputPath, 'docs', 'review.md'); let codeReview = ''; if (await fs_extra_1.default.pathExists(reviewPath)) { codeReview = await fs_extra_1.default.readFile(reviewPath, 'utf-8'); } else { console.log(chalk_1.default.yellow('No review found. Running analysis first...')); const { ReviewCommand } = await Promise.resolve().then(() => __importStar(require('./review'))); const reviewCommand = new ReviewCommand(); const reviewResult = await reviewCommand.execute({ output: outputPath }); codeReview = reviewResult.review.summary; } console.log(chalk_1.default.blue('🧠 Generating migration plan...')); const migrationPlanMarkdown = await this.openaiService.generateMigrationPlan(codeReview, config.targetLanguage, config.targetFramework); const plan = this.parseMigrationPlan(migrationPlanMarkdown, config); // Generate plan files await this.generatePlanFiles(plan, migrationPlanMarkdown, outputPath); // Display summary this.displayPlanSummary(plan); return { outputPath, plan }; } async loadConfig(options) { const config = await this.configManager.getConfig(); if (!config) { throw new Error('No configuration found. Run "code-transmute init" first.'); } // Override with command line options if (options.projectPath) { config.projectPath = path_1.default.resolve(options.projectPath); } if (options.targetLanguage) { config.targetLanguage = options.targetLanguage; } if (options.targetFramework) { config.targetFramework = options.targetFramework; } return config; } parseMigrationPlan(markdown, _config) { // Parse the markdown to extract structured plan data const phases = this.extractPhases(markdown); const dependencies = this.extractDependencies(markdown); const commands = this.extractCommands(markdown); const warnings = this.extractWarnings(markdown); return { phases, timeline: this.extractTimeline(markdown), dependencies, commands, warnings, estimatedCost: this.estimateCost(phases) }; } extractPhases(markdown) { const phases = []; const phaseRegex = /## Phase (\d+): (.+)/g; let match; while ((match = phaseRegex.exec(markdown)) !== null) { const phaseNumber = parseInt(match[1]); const phaseName = match[2]; // Extract steps for this phase const steps = this.extractStepsForPhase(markdown, phaseNumber); phases.push({ name: phaseName, description: this.extractPhaseDescription(markdown, phaseNumber), steps, estimatedTime: this.extractPhaseTime(markdown, phaseNumber), dependencies: this.extractPhaseDependencies(markdown, phaseNumber) }); } return phases; } extractStepsForPhase(markdown, phaseNumber) { const steps = []; const stepRegex = new RegExp(`## Phase ${phaseNumber}[\\s\\S]*?(?=##|$)`, 'g'); const phaseSection = stepRegex.exec(markdown); if (phaseSection) { const stepMatches = phaseSection[0].match(/\d+\.\s+(.+)/g); if (stepMatches) { stepMatches.forEach((step, index) => { const stepText = step.replace(/^\d+\.\s+/, ''); steps.push({ id: `phase-${phaseNumber}-step-${index + 1}`, action: this.determineAction(stepText), target: this.extractTarget(stepText), description: stepText, commands: this.extractCommandsFromStep(stepText), validation: this.extractValidation(stepText) }); }); } } return steps; } extractPhaseDescription(markdown, phaseNumber) { const phaseRegex = new RegExp(`## Phase ${phaseNumber}[\\s\\S]*?(?=###|##|$)`, 'g'); const match = phaseRegex.exec(markdown); return match ? match[0].replace(/## Phase \d+: .+\n/, '').trim() : ''; } extractPhaseTime(markdown, phaseNumber) { const timeRegex = new RegExp(`Phase ${phaseNumber}[\\s\\S]*?time[^:]*: ([^\\n]+)`, 'i'); const match = timeRegex.exec(markdown); return match ? match[1] : 'Unknown'; } extractPhaseDependencies(markdown, phaseNumber) { const depsRegex = new RegExp(`Phase ${phaseNumber}[\\s\\S]*?dependencies[^:]*: ([^\\n]+)`, 'i'); const match = depsRegex.exec(markdown); return match ? match[1].split(',').map(dep => dep.trim()) : []; } extractDependencies(markdown) { const depsRegex = /dependencies[^:]*: ([^\n]+)/gi; const matches = markdown.match(depsRegex); const dependencies = []; if (matches) { matches.forEach(match => { const deps = match.replace(/dependencies[^:]*: /i, '').split(',').map(dep => dep.trim()); dependencies.push(...deps); }); } return [...new Set(dependencies)]; } extractCommands(markdown) { const commandRegex = /```(?:bash|shell|sh)\n([\s\S]*?)\n```/g; const commands = []; let match; while ((match = commandRegex.exec(markdown)) !== null) { const commandBlock = match[1].trim(); commands.push(...commandBlock.split('\n').filter(cmd => cmd.trim())); } return commands; } extractWarnings(markdown) { const warningRegex = /āš ļø|warning[^:]*: ([^\n]+)/gi; const warnings = []; let match; while ((match = warningRegex.exec(markdown)) !== null) { warnings.push(match[1] || match[0]); } return warnings; } extractTimeline(markdown) { const timelineRegex = /timeline[^:]*: ([^\n]+)/i; const match = timelineRegex.exec(markdown); return match ? match[1] : 'Unknown'; } determineAction(stepText) { const lower = stepText.toLowerCase(); if (lower.includes('create') || lower.includes('generate')) return 'create'; if (lower.includes('modify') || lower.includes('update') || lower.includes('change')) return 'modify'; if (lower.includes('delete') || lower.includes('remove')) return 'delete'; if (lower.includes('install') || lower.includes('add')) return 'install'; if (lower.includes('configure') || lower.includes('setup')) return 'configure'; return 'modify'; } extractTarget(stepText) { // Extract the target file or component from the step text const targetRegex = /(?:to|in|for)\s+([^\s]+)/i; const match = targetRegex.exec(stepText); return match ? match[1] : 'unknown'; } extractCommandsFromStep(stepText) { const commandRegex = /`([^`]+)`/g; const commands = []; let match; while ((match = commandRegex.exec(stepText)) !== null) { commands.push(match[1]); } return commands; } extractValidation(stepText) { if (stepText.toLowerCase().includes('verify') || stepText.toLowerCase().includes('check')) { return 'Manual verification required'; } return 'Automatic validation'; } estimateCost(phases) { // Rough estimation based on number of phases and complexity return phases.length * 0.50; // $0.50 per phase estimate } async generatePlanFiles(plan, markdown, outputPath) { const docsDir = path_1.default.join(outputPath, 'docs'); await fs_extra_1.default.ensureDir(docsDir); // Generate migration_plan.md await fs_extra_1.default.writeFile(path_1.default.join(docsDir, 'migration_plan.md'), markdown); // Generate structured plan.json await fs_extra_1.default.writeJson(path_1.default.join(outputPath, 'migration_plan.json'), plan, { spaces: 2 }); // Generate commands.sh script const commandsScript = this.generateCommandsScript(plan); await fs_extra_1.default.writeFile(path_1.default.join(outputPath, 'migration_commands.sh'), commandsScript); await fs_extra_1.default.chmod(path_1.default.join(outputPath, 'migration_commands.sh'), 0o755); console.log(chalk_1.default.green('āœ… Plan files generated:')); console.log(chalk_1.default.gray(` šŸ“„ ${path_1.default.join(docsDir, 'migration_plan.md')}`)); console.log(chalk_1.default.gray(` šŸ“Š ${path_1.default.join(outputPath, 'migration_plan.json')}`)); console.log(chalk_1.default.gray(` šŸ”§ ${path_1.default.join(outputPath, 'migration_commands.sh')}`)); } generateCommandsScript(plan) { let script = '#!/bin/bash\n\n'; script += '# Generated migration commands\n'; script += '# Run this script to execute the migration plan\n\n'; script += 'set -e # Exit on any error\n\n'; plan.phases.forEach((phase, index) => { script += `echo "Phase ${index + 1}: ${phase.name}"\n`; script += `echo "====================================="\n\n`; phase.steps.forEach((step, stepIndex) => { if (step.commands && step.commands.length > 0) { script += `echo "Step ${stepIndex + 1}: ${step.description}"\n`; step.commands.forEach(cmd => { script += `${cmd}\n`; }); script += 'echo "Step completed"\n\n'; } }); }); script += 'echo "Migration plan completed!"\n'; return script; } displayPlanSummary(plan) { console.log(chalk_1.default.blue.bold('\nšŸ“‹ Migration Plan Summary')); console.log(chalk_1.default.gray('─'.repeat(50))); console.log(chalk_1.default.cyan('Phases:'), chalk_1.default.white(plan.phases.length)); console.log(chalk_1.default.cyan('Total Steps:'), chalk_1.default.white(plan.phases.reduce((sum, phase) => sum + phase.steps.length, 0))); console.log(chalk_1.default.cyan('Commands:'), chalk_1.default.white(plan.commands.length)); console.log(chalk_1.default.cyan('Timeline:'), chalk_1.default.white(plan.timeline)); if (plan.warnings.length > 0) { console.log(chalk_1.default.yellow('\nāš ļø Warnings:')); plan.warnings.forEach(warning => { console.log(chalk_1.default.gray(` • ${warning}`)); }); } if (plan.estimatedCost) { console.log(chalk_1.default.cyan('Estimated Cost:'), chalk_1.default.white(`$${plan.estimatedCost.toFixed(2)}`)); } console.log(chalk_1.default.green('\nāœ… Plan generated! Run "code-transmute convert" to execute the migration.')); } } exports.PlanCommand = PlanCommand; //# sourceMappingURL=plan.js.map