ken-you-code
Version:
Connect your codebase to Kimi: Ultra-fast AI code analysis with Kimi-K2 model via MCP
134 lines (111 loc) • 3.52 kB
text/typescript
import { createPatch } from 'diff';
import { writeFile, copyFile } from 'fs/promises';
import path from 'path';
import { randomUUID } from 'crypto';
import { FileOperations } from './fileOperations.js';
export interface DiffOperation {
id: string;
filePath: string;
originalContent: string;
newContent: string;
patch: string;
reason: string;
createdAt: Date;
applied: boolean;
}
export class DiffManager {
private static pendingOperations = new Map<string, DiffOperation>();
static async createDiff(
filePath: string,
newContent: string,
reason: string
): Promise<DiffOperation> {
try {
// Read current file content
const fileResult = await FileOperations.readFileSecurely(filePath);
const originalContent = fileResult.content;
// Generate diff patch
const patch = createPatch(
path.basename(filePath),
originalContent,
newContent,
'current',
'proposed'
);
// Create operation
const operation: DiffOperation = {
id: randomUUID(),
filePath,
originalContent,
newContent,
patch,
reason,
createdAt: new Date(),
applied: false,
};
// Store pending operation
this.pendingOperations.set(operation.id, operation);
return operation;
} catch (error) {
throw new Error(
`Failed to create diff: ${error instanceof Error ? error.message : 'Unknown error'}`
);
}
}
static getPendingOperation(operationId: string): DiffOperation | undefined {
return this.pendingOperations.get(operationId);
}
static async applyDiff(
operationId: string,
approved: boolean
): Promise<{ success: boolean; message: string }> {
const operation = this.pendingOperations.get(operationId);
if (!operation) {
return { success: false, message: 'Operation not found' };
}
if (operation.applied) {
return { success: false, message: 'Operation already applied' };
}
if (!approved) {
this.pendingOperations.delete(operationId);
return { success: true, message: 'Operation cancelled by user' };
}
try {
// Create backup
const backupPath = `${operation.filePath}.backup.${Date.now()}`;
await copyFile(operation.filePath, backupPath);
// Apply changes
await writeFile(operation.filePath, operation.newContent, 'utf-8');
// Mark as applied
operation.applied = true;
// Clean up pending operation
this.pendingOperations.delete(operationId);
return {
success: true,
message: `File updated successfully. Backup created at: ${backupPath}`,
};
} catch (error) {
return {
success: false,
message: `Failed to apply diff: ${error instanceof Error ? error.message : 'Unknown error'}`,
};
}
}
static listPendingOperations(): DiffOperation[] {
return Array.from(this.pendingOperations.values());
}
static cancelOperation(operationId: string): boolean {
return this.pendingOperations.delete(operationId);
}
static clearExpiredOperations(maxAgeMinutes: number = 60): number {
const cutoff = new Date(Date.now() - maxAgeMinutes * 60 * 1000);
let cleared = 0;
for (const [id, operation] of this.pendingOperations.entries()) {
if (operation.createdAt < cutoff && !operation.applied) {
this.pendingOperations.delete(id);
cleared++;
}
}
return cleared;
}
}