From d1cde65df65bfcca288a47f14eedf5ad5939ed37 Mon Sep 17 00:00:00 2001 From: "Ben Houston (via MyCoder)" Date: Tue, 11 Mar 2025 20:26:03 +0000 Subject: [PATCH] feat: allow textEditor to overwrite existing files with create command This change modifies the textEditor tool to allow overwriting existing files when using the 'create' command, rather than throwing an error when a file already exists. It also adds proper history tracking for undo functionality and includes tests for the new behavior. Fixes #192 --- .../agent/src/tools/io/textEditor.test.ts | 90 +++++++++++++++++++ packages/agent/src/tools/io/textEditor.ts | 31 ++++--- 2 files changed, 109 insertions(+), 12 deletions(-) diff --git a/packages/agent/src/tools/io/textEditor.test.ts b/packages/agent/src/tools/io/textEditor.test.ts index 4b7f974..0ebe83d 100644 --- a/packages/agent/src/tools/io/textEditor.test.ts +++ b/packages/agent/src/tools/io/textEditor.test.ts @@ -303,4 +303,94 @@ describe('textEditor', () => { ); }).rejects.toThrow(/Found 2 occurrences/); }); + + it('should overwrite an existing file with create command', async () => { + const initialContent = 'Initial content'; + const newContent = 'New content that overwrites the file'; + const testPath = join(testDir, `${randomUUID()}.txt`); + + // Create initial file + await textEditorTool.execute( + { + command: 'create', + path: testPath, + file_text: initialContent, + description: 'test', + }, + toolContext, + ); + + // Verify initial content + let content = await readFile(testPath, 'utf8'); + expect(content).toBe(initialContent); + + // Overwrite the file using create command + const result = await textEditorTool.execute( + { + command: 'create', + path: testPath, + file_text: newContent, + description: 'test', + }, + toolContext, + ); + + // Verify return value + expect(result.success).toBe(true); + expect(result.message).toContain('File overwritten'); + + // Verify content has been updated + content = await readFile(testPath, 'utf8'); + expect(content).toBe(newContent); + }); + + it('should be able to undo file overwrite', async () => { + const initialContent = 'Initial content that will be restored'; + const overwrittenContent = 'This content will be undone'; + const testPath = join(testDir, `${randomUUID()}.txt`); + + // Create initial file + await textEditorTool.execute( + { + command: 'create', + path: testPath, + file_text: initialContent, + description: 'test', + }, + toolContext, + ); + + // Overwrite the file + await textEditorTool.execute( + { + command: 'create', + path: testPath, + file_text: overwrittenContent, + description: 'test', + }, + toolContext, + ); + + // Verify overwritten content + let content = await readFile(testPath, 'utf8'); + expect(content).toBe(overwrittenContent); + + // Undo the overwrite + const result = await textEditorTool.execute( + { + command: 'undo_edit', + path: testPath, + description: 'test', + }, + toolContext, + ); + + // Verify return value + expect(result.success).toBe(true); + expect(result.message).toContain('Successfully reverted'); + + // Verify content is back to initial + content = await readFile(testPath, 'utf8'); + expect(content).toBe(initialContent); + }); }); diff --git a/packages/agent/src/tools/io/textEditor.ts b/packages/agent/src/tools/io/textEditor.ts index ce31909..1147468 100644 --- a/packages/agent/src/tools/io/textEditor.ts +++ b/packages/agent/src/tools/io/textEditor.ts @@ -160,13 +160,6 @@ export const textEditorTool: Tool = { } case 'create': { - // Check if file already exists - if (fsSync.existsSync(absolutePath)) { - throw new Error( - `File already exists: ${filePath}. Use str_replace to modify it.`, - ); - } - if (!file_text) { throw new Error('file_text parameter is required for create command'); } @@ -174,15 +167,29 @@ export const textEditorTool: Tool = { // Create parent directories if they don't exist await fs.mkdir(path.dirname(absolutePath), { recursive: true }); - // Create the file - await fs.writeFile(absolutePath, file_text, 'utf8'); + // Check if file already exists + const fileExists = fsSync.existsSync(absolutePath); - // Store initial state for undo - fileStateHistory[absolutePath] = [file_text]; + if (fileExists) { + // Save current state for undo if file exists + const currentContent = await fs.readFile(absolutePath, 'utf8'); + if (!fileStateHistory[absolutePath]) { + fileStateHistory[absolutePath] = []; + } + fileStateHistory[absolutePath].push(currentContent); + } else { + // Initialize history for new files + fileStateHistory[absolutePath] = []; + } + + // Create or overwrite the file + await fs.writeFile(absolutePath, file_text, 'utf8'); return { success: true, - message: `File created: ${filePath}`, + message: fileExists + ? `File overwritten: ${filePath}` + : `File created: ${filePath}`, }; }