undo
Restore previous file states by removing the latest checkpoint from the undo stack. Call repeatedly to revert multiple changes and recover earlier versions.
Instructions
Undo the last checkpoint (pops from stack and restores files). Each call removes the latest checkpoint from the stack. To undo multiple changes, call this function repeatedly until the desired state is reached.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- src/changeTracker.ts:45-143 (handler)Core handler function that executes the undo logic: pops the latest checkpoint from the stack, restores modified files to their previous state, removes newly created files, manages errors by pushing back the checkpoint if partial failure, and returns detailed success/failure info.async undo(): Promise<{ success: boolean; message?: string; restoredFiles?: string[]; description?: string; }> { // Deduplicate checkpoints before proceeding this.deduplicateCheckpoints(); if (this.undoStack.length === 0) { return { success: false, message: "No checkpoints to undo" }; } const checkpoint = this.undoStack.pop()!; const restoredFiles: string[] = []; const errors: string[] = []; console.error(`[DEBUG] Starting undo for checkpoint: ${checkpoint.description}`); console.error(`[DEBUG] Files to restore: ${Array.from(checkpoint.files.keys()).join(', ')}`); console.error(`[DEBUG] Files to remove: ${Array.from(checkpoint.createdFiles).join(', ') || 'none'}`); try { // First, restore existing files for (const [filepath, content] of checkpoint.files.entries()) { try { console.error(`[DEBUG] Restoring file: ${filepath}`); console.error(`[DEBUG] Content length: ${content.length}`); // If file doesn't exist, it was deleted - restore it const wasDeleted = !existsSync(filepath); if (wasDeleted) { console.error(`[DEBUG] File was deleted, restoring: ${filepath}`); // Ensure directory exists before creating file const dir = dirname(filepath); if (!existsSync(dir)) { mkdirSync(dir, { recursive: true }); } } writeFileSync(filepath, content, "utf-8"); restoredFiles.push(wasDeleted ? `${filepath} (restored from deletion)` : filepath); console.error(`[DEBUG] Successfully restored: ${filepath}`); } catch (fileError) { const errorMsg = `Failed to restore ${filepath}: ${fileError}`; errors.push(errorMsg); console.error(`[DEBUG] Error restoring ${filepath}:`, fileError); console.error(`[DEBUG] Full error details:`, { filepath, contentLength: content.length, fileExists: existsSync(filepath), dirExists: existsSync(dirname(filepath)), error: fileError }); } } // Then, remove files that were created (undo file creation) for (const filepath of checkpoint.createdFiles) { try { console.error(`[DEBUG] Removing created file: ${filepath}`); if (existsSync(filepath)) { unlinkSync(filepath); restoredFiles.push(`${filepath} (deleted)`); console.error(`[DEBUG] Successfully removed: ${filepath}`); } else { console.error(`[DEBUG] Created file ${filepath} already doesn't exist`); } } catch (fileError) { errors.push(`Failed to remove created file ${filepath}: ${fileError}`); console.error(`[DEBUG] Error removing ${filepath}:`, fileError); } } if (errors.length > 0) { // Put checkpoint back if any files failed this.undoStack.push(checkpoint); return { success: false, message: `Some files failed to restore: ${errors.join('; ')}`, }; } console.error(`[DEBUG] Undo completed successfully. Restored ${restoredFiles.length} files`); return { success: true, restoredFiles, description: checkpoint.description, }; } catch (error) { // Put checkpoint back if restore failed this.undoStack.push(checkpoint); console.error(`[DEBUG] Undo failed with error:`, error); return { success: false, message: `Failed to restore checkpoint: ${error}`, }; } }
- src/index.ts:45-52 (registration)Tool registration entry in the TOOLS array, including name, description, and empty input schema. This is returned by listTools.{ name: "undo", description: "Undo the last checkpoint (pops from stack and restores files). Each call removes the latest checkpoint from the stack. To undo multiple changes, call this function repeatedly until the desired state is reached.", inputSchema: { type: "object", properties: {}, }, },
- src/index.ts:105-117 (handler)MCP CallToolRequestSchema handler case for 'undo': calls changeTracker.undo() and formats the text response for the MCP protocol.case "undo": { const result = await changeTracker.undo(); return { content: [ { type: "text", text: result.success ? `✅ Undone: "${result.description}"\nRestored files:\n${result.restoredFiles?.map(f => ` - ${f}`).join('\n')}` : result.message || "Failed to undo", }, ], }; }
- src/changeTracker.ts:4-9 (schema)TypeScript interface defining the structure of each undo checkpoint stored in the stack, used by the undo handler.interface UndoCheckpoint { files: Map<string, string>; // filepath -> content createdFiles: Set<string>; // files that were created (didn't exist before) timestamp: Date; description: string; }