UNPKG

@sethdouglasford/claude-flow

Version:

Claude Code Flow - Advanced AI-powered development workflows with SPARC methodology

243 lines • 10.8 kB
/** * Migration Analyzer - Analyzes existing projects for migration readiness */ import * as path from "node:path"; import * as fs from "fs-extra"; import * as crypto from "crypto"; import { readFile } from "node:fs/promises"; import { logger } from "./logger"; import chalk from "chalk"; import glob from "glob"; export class MigrationAnalyzer { optimizedCommands = [ "sparc", "sparc-architect", "sparc-code", "sparc-tdd", "claude-flow-help", "claude-flow-memory", "claude-flow-swarm", ]; async analyze(projectPath) { logger.info(`Analyzing project at ${projectPath}...`); const analysis = { projectPath, hasClaudeFolder: false, hasOptimizedPrompts: false, customCommands: [], customConfigurations: {}, conflictingFiles: [], migrationRisks: [], recommendations: [], timestamp: new Date(), }; // Check for .claude folder const claudePath = path.join(projectPath, ".claude"); if (await fs.pathExists(claudePath)) { analysis.hasClaudeFolder = true; // Analyze existing commands await this.analyzeCommands(claudePath, analysis); // Check for optimized prompts await this.checkOptimizedPrompts(claudePath, analysis); // Analyze configurations await this.analyzeConfigurations(projectPath, analysis); // Detect conflicts await this.detectConflicts(projectPath, analysis); } // Generate risks and recommendations this.assessRisks(analysis); this.generateRecommendations(analysis); return analysis; } async analyzeCommands(claudePath, analysis) { const commandsPath = path.join(claudePath, "commands"); if (await fs.pathExists(commandsPath)) { const files = glob.sync("**/*.md", { cwd: commandsPath }); for (const file of files) { const commandName = path.basename(file, ".md"); if (!this.optimizedCommands.includes(commandName)) { analysis.customCommands.push(commandName); } } } } async checkOptimizedPrompts(claudePath, analysis) { // Check for key optimized prompt files const optimizedFiles = [ "BATCHTOOLS_GUIDE.md", "BATCHTOOLS_BEST_PRACTICES.md", "MIGRATION_GUIDE.md", "PERFORMANCE_BENCHMARKS.md", ]; let hasOptimized = 0; for (const file of optimizedFiles) { if (await fs.pathExists(path.join(claudePath, file))) { hasOptimized++; } } analysis.hasOptimizedPrompts = hasOptimized >= 2; } async analyzeConfigurations(projectPath, analysis) { // Check for CLAUDE.md const claudeMdPath = path.join(projectPath, "CLAUDE.md"); if (await fs.pathExists(claudeMdPath)) { const content = await readFile(claudeMdPath, "utf-8"); analysis.customConfigurations["CLAUDE.md"] = { exists: true, size: content.length, hasCustomContent: !content.includes("SPARC Development Environment"), }; } // Check for .roomodes const roomodesPath = path.join(projectPath, ".roomodes"); if (await fs.pathExists(roomodesPath)) { try { const roomodes = await fs.readJson(roomodesPath); analysis.customConfigurations[".roomodes"] = { exists: true, modeCount: Object.keys(roomodes).length, customModes: Object.keys(roomodes).filter(mode => !["architect", "code", "tdd", "debug", "docs-writer"].includes(mode)), }; } catch (error) { analysis.migrationRisks.push({ level: "medium", description: "Invalid .roomodes file", file: roomodesPath, mitigation: "File will be backed up and replaced", }); } } } async detectConflicts(projectPath, analysis) { // Check for files that might conflict with migration const potentialConflicts = [ ".claude/commands/sparc.md", ".claude/BATCHTOOLS_GUIDE.md", "memory/memory-store.json", "coordination/config.json", ]; for (const file of potentialConflicts) { const filePath = path.join(projectPath, file); if (await fs.pathExists(filePath)) { const content = await readFile(filePath, "utf-8"); const checksum = crypto.createHash("md5").update(content).digest("hex"); // Check if it's a custom version if (!this.isStandardFile(file, checksum)) { analysis.conflictingFiles.push(file); } } } } isStandardFile(file, checksum) { // This would contain checksums of standard files // For now, we'll assume all existing files are potentially custom return false; } assessRisks(analysis) { // High risk: Custom commands that might be overwritten if (analysis.customCommands.length > 0) { analysis.migrationRisks.push({ level: "high", description: `Found ${analysis.customCommands.length} custom commands that may be affected`, mitigation: "Use --preserve-custom flag or selective migration", }); } // Medium risk: Existing optimized prompts if (analysis.hasOptimizedPrompts) { analysis.migrationRisks.push({ level: "medium", description: "Project already has some optimized prompts", mitigation: "Consider using merge strategy to preserve customizations", }); } // Low risk: No .claude folder if (!analysis.hasClaudeFolder) { analysis.migrationRisks.push({ level: "low", description: "No existing .claude folder found", mitigation: "Fresh installation will be performed", }); } // High risk: Conflicting files if (analysis.conflictingFiles.length > 0) { analysis.migrationRisks.push({ level: "high", description: `${analysis.conflictingFiles.length} files may have custom modifications`, mitigation: "Files will be backed up before migration", }); } } generateRecommendations(analysis) { // Strategy recommendations if (analysis.customCommands.length > 0 || analysis.conflictingFiles.length > 0) { analysis.recommendations.push("Use \"selective\" or \"merge\" strategy to preserve customizations"); } else if (!analysis.hasClaudeFolder) { analysis.recommendations.push("Use \"full\" strategy for clean installation"); } // Backup recommendations if (analysis.hasClaudeFolder) { analysis.recommendations.push("Create a backup before migration (automatic with default settings)"); } // Custom command recommendations if (analysis.customCommands.length > 0) { analysis.recommendations.push(`Review custom commands: ${analysis.customCommands.join(", ")}`); } // Validation recommendations if (analysis.migrationRisks.some(r => r.level === "high")) { analysis.recommendations.push("Run with --dry-run first to preview changes"); } } printAnalysis(analysis, detailed = false) { console.log(chalk.bold("\nšŸ“Š Migration Analysis Report")); console.log(chalk.gray("─".repeat(50))); console.log(`\n${chalk.bold("Project:")} ${analysis.projectPath}`); console.log(`${chalk.bold("Timestamp:")} ${analysis.timestamp.toISOString()}`); // Status console.log(chalk.bold("\nšŸ“‹ Current Status:")); console.log(` • .claude folder: ${analysis.hasClaudeFolder ? chalk.green("āœ“") : chalk.red("āœ—")}`); console.log(` • Optimized prompts: ${analysis.hasOptimizedPrompts ? chalk.green("āœ“") : chalk.red("āœ—")}`); console.log(` • Custom commands: ${analysis.customCommands.length > 0 ? chalk.yellow(analysis.customCommands.length) : chalk.green("0")}`); console.log(` • Conflicts: ${analysis.conflictingFiles.length > 0 ? chalk.yellow(analysis.conflictingFiles.length) : chalk.green("0")}`); // Risks if (analysis.migrationRisks.length > 0) { console.log(chalk.bold("\nāš ļø Migration Risks:")); analysis.migrationRisks.forEach(risk => { const icon = risk.level === "high" ? "šŸ”“" : risk.level === "medium" ? "🟔" : "🟢"; console.log(` ${icon} ${chalk.bold(risk.level.toUpperCase())}: ${risk.description}`); if (risk.mitigation) { console.log(` ${chalk.gray("→")} ${chalk.italic(risk.mitigation)}`); } }); } // Recommendations if (analysis.recommendations.length > 0) { console.log(chalk.bold("\nšŸ’” Recommendations:")); analysis.recommendations.forEach(rec => { console.log(` • ${rec}`); }); } // Detailed information if (detailed) { if (analysis.customCommands.length > 0) { console.log(chalk.bold("\nšŸ”§ Custom Commands:")); analysis.customCommands.forEach(cmd => { console.log(` • ${cmd}`); }); } if (analysis.conflictingFiles.length > 0) { console.log(chalk.bold("\nšŸ“ Conflicting Files:")); analysis.conflictingFiles.forEach(file => { console.log(` • ${file}`); }); } if (Object.keys(analysis.customConfigurations).length > 0) { console.log(chalk.bold("\nāš™ļø Configurations:")); Object.entries(analysis.customConfigurations).forEach(([file, config]) => { console.log(` • ${file}: ${JSON.stringify(config, null, 2)}`); }); } } console.log(chalk.gray(`\n${"─".repeat(50)}`)); } async saveAnalysis(analysis, outputPath) { await fs.writeJson(outputPath, analysis, { spaces: 2 }); } } //# sourceMappingURL=migration-analyzer.js.map