# Action: Apply Fix Apply a selected fix to the target skill with backup and rollback capability. ## Purpose - Apply fix changes to target skill files - Create backup before modifications - Track applied fixes for verification - Support rollback if needed ## Preconditions - [ ] state.status === 'running' - [ ] state.pending_fixes.length > 0 - [ ] state.proposed_fixes contains the fix to apply ## Execution ```javascript async function execute(state, workDir) { const pendingFixes = state.pending_fixes; const proposedFixes = state.proposed_fixes; const targetPath = state.target_skill.path; const backupDir = state.backup_dir; if (pendingFixes.length === 0) { return { stateUpdates: {}, outputFiles: [], summary: 'No pending fixes to apply' }; } // Get next fix to apply const fixId = pendingFixes[0]; const fix = proposedFixes.find(f => f.id === fixId); if (!fix) { return { stateUpdates: { pending_fixes: pendingFixes.slice(1), errors: [...state.errors, { action: 'action-apply-fix', message: `Fix ${fixId} not found in proposals`, timestamp: new Date().toISOString(), recoverable: true }] }, outputFiles: [], summary: `Fix ${fixId} not found, skipping` }; } console.log(`Applying fix ${fix.id}: ${fix.description}`); // Create fix-specific backup const fixBackupDir = `${backupDir}/before-${fix.id}`; Bash(`mkdir -p "${fixBackupDir}"`); const appliedChanges = []; let success = true; for (const change of fix.changes) { try { // Resolve file path (handle wildcards) let targetFiles = []; if (change.file.includes('*')) { targetFiles = Glob(`${targetPath}/${change.file}`); } else { targetFiles = [`${targetPath}/${change.file}`]; } for (const targetFile of targetFiles) { // Backup original const relativePath = targetFile.replace(targetPath + '/', ''); const backupPath = `${fixBackupDir}/${relativePath}`; if (Glob(targetFile).length > 0) { const originalContent = Read(targetFile); Bash(`mkdir -p "$(dirname "${backupPath}")"`); Write(backupPath, originalContent); } // Apply change based on action type if (change.action === 'modify' && change.diff) { // For now, append the diff as a comment/note // Real implementation would parse and apply the diff const existingContent = Read(targetFile); // Simple diff application: look for context and apply // This is a simplified version - real implementation would be more sophisticated const newContent = existingContent + `\n\n\n`; Write(targetFile, newContent); appliedChanges.push({ file: relativePath, action: 'modified', backup: backupPath }); } else if (change.action === 'create') { Write(targetFile, change.new_content || ''); appliedChanges.push({ file: relativePath, action: 'created', backup: null }); } } } catch (error) { console.log(`Error applying change to ${change.file}: ${error.message}`); success = false; } } // Record applied fix const appliedFix = { fix_id: fix.id, applied_at: new Date().toISOString(), success: success, backup_path: fixBackupDir, verification_result: 'pending', rollback_available: true, changes_made: appliedChanges }; // Update applied fixes log const appliedFixesPath = `${workDir}/fixes/applied-fixes.json`; let existingApplied = []; try { existingApplied = JSON.parse(Read(appliedFixesPath)); } catch (e) { existingApplied = []; } existingApplied.push(appliedFix); Write(appliedFixesPath, JSON.stringify(existingApplied, null, 2)); return { stateUpdates: { applied_fixes: [...state.applied_fixes, appliedFix], pending_fixes: pendingFixes.slice(1) // Remove applied fix from pending }, outputFiles: [appliedFixesPath], summary: `Applied fix ${fix.id}: ${success ? 'success' : 'partial'}, ${appliedChanges.length} files modified` }; } ``` ## State Updates ```javascript return { stateUpdates: { applied_fixes: [...existingApplied, newAppliedFix], pending_fixes: remainingPendingFixes } }; ``` ## Rollback Function ```javascript async function rollbackFix(fixId, state, workDir) { const appliedFix = state.applied_fixes.find(f => f.fix_id === fixId); if (!appliedFix || !appliedFix.rollback_available) { throw new Error(`Cannot rollback fix ${fixId}`); } const backupDir = appliedFix.backup_path; const targetPath = state.target_skill.path; // Restore from backup const backupFiles = Glob(`${backupDir}/**/*`); for (const backupFile of backupFiles) { const relativePath = backupFile.replace(backupDir + '/', ''); const targetFile = `${targetPath}/${relativePath}`; const content = Read(backupFile); Write(targetFile, content); } return { stateUpdates: { applied_fixes: state.applied_fixes.map(f => f.fix_id === fixId ? { ...f, rollback_available: false, verification_result: 'rolled_back' } : f ) } }; } ``` ## Error Handling | Error Type | Recovery | |------------|----------| | File not found | Skip file, log warning | | Write permission error | Retry with sudo or report | | Backup creation failed | Abort fix, don't modify | ## Next Actions - If pending_fixes.length > 0: action-apply-fix (continue) - If all fixes applied: action-verify