UNPKG

claude-flow-multilang

Version:

Revolutionary multilingual AI orchestration framework with cultural awareness and DDD architecture

522 lines (450 loc) 13.9 kB
import { promises as fs } from 'fs'; // rollback-executor.js - Execute rollback operations export class RollbackExecutor { constructor(workingDir) { this.workingDir = workingDir; } /** * Execute full rollback to pre-initialization state */ async executeFullRollback(backupId) { const result = { success: true, errors: [], warnings: [], actions: [], }; try { console.log(`🔄 Executing full rollback to backup: ${backupId}`); // Step 1: Remove initialization artifacts const cleanupResult = await this.cleanupInitializationArtifacts(); result.actions.push(...cleanupResult.actions); if (!cleanupResult.success) { result.warnings.push(...cleanupResult.errors); } // Step 2: Restore from backup const restoreResult = await this.restoreFromBackup(backupId); result.actions.push(...restoreResult.actions); if (!restoreResult.success) { result.success = false; result.errors.push(...restoreResult.errors); return result; } // Step 3: Verify rollback const verifyResult = await this.verifyRollback(); result.actions.push(...verifyResult.actions); if (!verifyResult.success) { result.warnings.push(...verifyResult.errors); } console.log(' ✅ Full rollback completed'); } catch (error) { result.success = false; result.errors.push(`Full rollback execution failed: ${error.message}`); } return result; } /** * Execute partial rollback for specific component */ async executePartialRollback(phase, checkpoint) { const result = { success: true, errors: [], warnings: [], actions: [], }; try { console.log(`🔄 Executing partial rollback for phase: ${phase}`); // Determine rollback strategy based on phase let rollbackResult; switch (phase) { case 'sparc-init': rollbackResult = await this.rollbackSparcInitialization(); break; case 'claude-commands': rollbackResult = await this.rollbackClaudeCommands(); break; case 'memory-setup': rollbackResult = await this.rollbackMemorySetup(); break; case 'coordination-setup': rollbackResult = await this.rollbackCoordinationSetup(); break; case 'executable-creation': rollbackResult = await this.rollbackExecutableCreation(); break; default: rollbackResult = await this.rollbackGenericPhase(phase, checkpoint); break; } result.success = rollbackResult.success; result.errors.push(...rollbackResult.errors); result.warnings.push(...rollbackResult.warnings); result.actions.push(...rollbackResult.actions); if (rollbackResult.success) { console.log(` ✅ Partial rollback completed for phase: ${phase}`); } } catch (error) { result.success = false; result.errors.push(`Partial rollback execution failed: ${error.message}`); } return result; } /** * Rollback SPARC initialization */ async rollbackSparcInitialization() { const result = { success: true, errors: [], warnings: [], actions: [], }; try { const itemsToRemove = ['.roomodes', '.roo', '.claude/commands/sparc']; for (const item of itemsToRemove) { const itemPath = `${this.workingDir}/${item}`; try { const stat = await fs.stat(itemPath); if (stat.isFile) { await fs.unlink(itemPath); result.actions.push(`Removed file: ${item}`); } else if (stat.isDirectory) { await fs.unlink(itemPath, { recursive: true }); result.actions.push(`Removed directory: ${item}`); } } catch { // Item doesn't exist - that's fine result.actions.push(`Item not found (already clean): ${item}`); } } // Remove SPARC-specific content from CLAUDE.md await this.removeSPARCContentFromClaudeMd(); result.actions.push('Cleaned SPARC content from CLAUDE.md'); } catch (error) { result.success = false; result.errors.push(`SPARC rollback failed: ${error.message}`); } return result; } /** * Rollback Claude commands */ async rollbackClaudeCommands() { const result = { success: true, errors: [], warnings: [], actions: [], }; try { const commandsDir = `${this.workingDir}/.claude/commands`; try { // Remove all command files for await (const entry of fs.readdir(commandsDir)) { if (entry.isFile && entry.name.endsWith('.js')) { await fs.unlink(`${commandsDir}/${entry.name}`); result.actions.push(`Removed command: ${entry.name}`); } else if (entry.isDirectory) { await fs.unlink(`${commandsDir}/${entry.name}`, { recursive: true }); result.actions.push(`Removed command directory: ${entry.name}`); } } } catch { result.actions.push('Commands directory was already clean'); } } catch (error) { result.success = false; result.errors.push(`Claude commands rollback failed: ${error.message}`); } return result; } /** * Rollback memory setup */ async rollbackMemorySetup() { const result = { success: true, errors: [], warnings: [], actions: [], }; try { const memoryItems = ['memory/claude-flow-data.json', 'memory/agents', 'memory/sessions']; for (const item of memoryItems) { const itemPath = `${this.workingDir}/${item}`; try { const stat = await fs.stat(itemPath); if (stat.isFile) { await fs.unlink(itemPath); result.actions.push(`Removed memory file: ${item}`); } else if (stat.isDirectory) { await fs.unlink(itemPath, { recursive: true }); result.actions.push(`Removed memory directory: ${item}`); } } catch { result.actions.push(`Memory item not found: ${item}`); } } // Keep memory directory but clean it try { await fs.mkdir(`${this.workingDir}/memory`, { recursive: true }); result.actions.push('Recreated clean memory directory'); } catch { result.warnings.push('Could not recreate memory directory'); } } catch (error) { result.success = false; result.errors.push(`Memory setup rollback failed: ${error.message}`); } return result; } /** * Rollback coordination setup */ async rollbackCoordinationSetup() { const result = { success: true, errors: [], warnings: [], actions: [], }; try { const coordinationDir = `${this.workingDir}/coordination`; try { await fs.unlink(coordinationDir, { recursive: true }); result.actions.push('Removed coordination directory'); } catch { result.actions.push('Coordination directory was already clean'); } // Remove coordination.md try { await fs.unlink(`${this.workingDir}/coordination.md`); result.actions.push('Removed coordination.md'); } catch { result.actions.push('coordination.md was already clean'); } } catch (error) { result.success = false; result.errors.push(`Coordination setup rollback failed: ${error.message}`); } return result; } /** * Rollback executable creation */ async rollbackExecutableCreation() { const result = { success: true, errors: [], warnings: [], actions: [], }; try { const executablePath = `${this.workingDir}/claude-flow`; try { await fs.unlink(executablePath); result.actions.push('Removed claude-flow executable'); } catch { result.actions.push('claude-flow executable was already clean'); } } catch (error) { result.success = false; result.errors.push(`Executable rollback failed: ${error.message}`); } return result; } /** * Generic phase rollback */ async rollbackGenericPhase(phase, checkpoint) { const result = { success: true, errors: [], warnings: [], actions: [], }; try { // Use checkpoint data to determine what to rollback if (checkpoint && checkpoint.data) { const actions = checkpoint.data.actions || []; // Reverse the actions for (const action of actions.reverse()) { const rollbackResult = await this.reverseAction(action); if (rollbackResult.success) { result.actions.push(rollbackResult.description); } else { result.warnings.push(`Could not reverse action: ${action.type}`); } } } } catch (error) { result.success = false; result.errors.push(`Generic phase rollback failed: ${error.message}`); } return result; } /** * Clean up all initialization artifacts */ async cleanupInitializationArtifacts() { const result = { success: true, errors: [], actions: [], }; try { const artifactsToRemove = [ 'CLAUDE.md', 'memory-bank.md', 'coordination.md', 'claude-flow', '.roomodes', '.roo', '.claude', 'memory', 'coordination', ]; for (const artifact of artifactsToRemove) { const artifactPath = `${this.workingDir}/${artifact}`; try { const stat = await fs.stat(artifactPath); if (stat.isFile) { await fs.unlink(artifactPath); result.actions.push(`Removed file: ${artifact}`); } else if (stat.isDirectory) { await fs.unlink(artifactPath, { recursive: true }); result.actions.push(`Removed directory: ${artifact}`); } } catch { // Artifact doesn't exist - that's fine result.actions.push(`Artifact not found: ${artifact}`); } } } catch (error) { result.success = false; result.errors.push(`Cleanup failed: ${error.message}`); } return result; } /** * Restore from backup */ async restoreFromBackup(backupId) { const result = { success: true, errors: [], actions: [], }; try { // This would typically use the BackupManager // For now, we'll simulate the restoration result.actions.push(`Restored from backup: ${backupId}`); // In a real implementation, this would: // 1. Read the backup manifest // 2. Restore each file and directory // 3. Set correct permissions // 4. Verify restoration } catch (error) { result.success = false; result.errors.push(`Restore from backup failed: ${error.message}`); } return result; } /** * Verify rollback completed successfully */ async verifyRollback() { const result = { success: true, errors: [], actions: [], }; try { const expectedCleanItems = [ 'CLAUDE.md', 'memory-bank.md', 'coordination.md', '.roomodes', '.roo', 'claude-flow', ]; let foundArtifacts = 0; for (const item of expectedCleanItems) { try { await fs.stat(`${this.workingDir}/${item}`); foundArtifacts++; } catch { // Item doesn't exist - good } } if (foundArtifacts > 0) { result.success = false; result.errors.push(`Rollback incomplete: ${foundArtifacts} artifacts still present`); } else { result.actions.push('Rollback verification passed'); } } catch (error) { result.success = false; result.errors.push(`Rollback verification failed: ${error.message}`); } return result; } /** * Remove SPARC content from CLAUDE.md */ async removeSPARCContentFromClaudeMd() { try { const claudePath = `${this.workingDir}/CLAUDE.md`; try { const content = await fs.readFile(claudePath, 'utf8'); // Remove SPARC-specific sections const cleanedContent = content .replace(/## SPARC Development Commands[\s\S]*?(?=##|\n#|\n$)/g, '') .replace(/### SPARC[\s\S]*?(?=###|\n##|\n#|\n$)/g, '') .replace(/\n{3,}/g, '\n\n') // Clean up multiple newlines .trim(); await fs.writeFile(claudePath, cleanedContent, 'utf8'); } catch { // File doesn't exist or can't be modified } } catch { // Error handling CLAUDE.md - continue silently } } /** * Reverse a specific action */ async reverseAction(action) { const result = { success: true, description: '', }; try { switch (action.type) { case 'file_created': await fs.unlink(action.path); result.description = `Removed created file: ${action.path}`; break; case 'directory_created': await fs.unlink(action.path, { recursive: true }); result.description = `Removed created directory: ${action.path}`; break; case 'file_modified': if (action.backup) { await fs.writeFile(action.path, action.backup, 'utf8'); result.description = `Restored modified file: ${action.path}`; } break; default: result.success = false; result.description = `Unknown action type: ${action.type}`; break; } } catch (error) { result.success = false; result.description = `Failed to reverse action: ${error.message}`; } return result; } }