UNPKG

ccundo

Version:

Intelligent undo for Claude Code sessions - Revert individual operations with cascading safety and detailed previews

97 lines (85 loc) 2.69 kB
import fs from 'fs/promises'; import path from 'path'; import { Operation, OperationType } from './Operation.js'; import { fileURLToPath } from 'url'; import { dirname } from 'path'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); export class SessionTracker { constructor(sessionId = null) { this.sessionId = sessionId || new Date().toISOString().replace(/[:.]/g, '-'); this.operations = []; this.sessionDir = path.join(process.env.HOME, '.ccundo', 'sessions'); this.sessionFile = path.join(this.sessionDir, `${this.sessionId}.json`); } async init() { await fs.mkdir(this.sessionDir, { recursive: true }); await this.load(); } async load() { try { const data = await fs.readFile(this.sessionFile, 'utf8'); const session = JSON.parse(data); this.operations = session.operations.map(op => Operation.fromJSON(op)); } catch (error) { if (error.code !== 'ENOENT') { throw error; } } } async save() { const sessionData = { sessionId: this.sessionId, operations: this.operations.map(op => op.toJSON()) }; await fs.writeFile(this.sessionFile, JSON.stringify(sessionData, null, 2)); } async addOperation(operation) { this.operations.push(operation); await this.save(); } async getOperations(includeUndone = false) { return includeUndone ? this.operations : this.operations.filter(op => !op.undone); } async getOperation(id) { return this.operations.find(op => op.id === id); } async markUndone(operationId) { const operation = await this.getOperation(operationId); if (operation) { operation.undone = true; await this.save(); } return operation; } static async listSessions() { const sessionDir = path.join(process.env.HOME, '.ccundo', 'sessions'); try { const files = await fs.readdir(sessionDir); return files .filter(f => f.endsWith('.json')) .map(f => f.replace('.json', '')); } catch (error) { if (error.code === 'ENOENT') { return []; } throw error; } } static async getCurrentSession() { const currentFile = path.join(process.env.HOME, '.ccundo', 'current-session'); try { const sessionId = await fs.readFile(currentFile, 'utf8'); return sessionId.trim(); } catch (error) { return null; } } static async setCurrentSession(sessionId) { const currentFile = path.join(process.env.HOME, '.ccundo', 'current-session'); await fs.mkdir(path.dirname(currentFile), { recursive: true }); await fs.writeFile(currentFile, sessionId); } }