Skip to content

Commit d1cde65

Browse files
committed
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
1 parent 3c5d8a6 commit d1cde65

File tree

2 files changed

+109
-12
lines changed

2 files changed

+109
-12
lines changed

packages/agent/src/tools/io/textEditor.test.ts

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,4 +303,94 @@ describe('textEditor', () => {
303303
);
304304
}).rejects.toThrow(/Found 2 occurrences/);
305305
});
306+
307+
it('should overwrite an existing file with create command', async () => {
308+
const initialContent = 'Initial content';
309+
const newContent = 'New content that overwrites the file';
310+
const testPath = join(testDir, `${randomUUID()}.txt`);
311+
312+
// Create initial file
313+
await textEditorTool.execute(
314+
{
315+
command: 'create',
316+
path: testPath,
317+
file_text: initialContent,
318+
description: 'test',
319+
},
320+
toolContext,
321+
);
322+
323+
// Verify initial content
324+
let content = await readFile(testPath, 'utf8');
325+
expect(content).toBe(initialContent);
326+
327+
// Overwrite the file using create command
328+
const result = await textEditorTool.execute(
329+
{
330+
command: 'create',
331+
path: testPath,
332+
file_text: newContent,
333+
description: 'test',
334+
},
335+
toolContext,
336+
);
337+
338+
// Verify return value
339+
expect(result.success).toBe(true);
340+
expect(result.message).toContain('File overwritten');
341+
342+
// Verify content has been updated
343+
content = await readFile(testPath, 'utf8');
344+
expect(content).toBe(newContent);
345+
});
346+
347+
it('should be able to undo file overwrite', async () => {
348+
const initialContent = 'Initial content that will be restored';
349+
const overwrittenContent = 'This content will be undone';
350+
const testPath = join(testDir, `${randomUUID()}.txt`);
351+
352+
// Create initial file
353+
await textEditorTool.execute(
354+
{
355+
command: 'create',
356+
path: testPath,
357+
file_text: initialContent,
358+
description: 'test',
359+
},
360+
toolContext,
361+
);
362+
363+
// Overwrite the file
364+
await textEditorTool.execute(
365+
{
366+
command: 'create',
367+
path: testPath,
368+
file_text: overwrittenContent,
369+
description: 'test',
370+
},
371+
toolContext,
372+
);
373+
374+
// Verify overwritten content
375+
let content = await readFile(testPath, 'utf8');
376+
expect(content).toBe(overwrittenContent);
377+
378+
// Undo the overwrite
379+
const result = await textEditorTool.execute(
380+
{
381+
command: 'undo_edit',
382+
path: testPath,
383+
description: 'test',
384+
},
385+
toolContext,
386+
);
387+
388+
// Verify return value
389+
expect(result.success).toBe(true);
390+
expect(result.message).toContain('Successfully reverted');
391+
392+
// Verify content is back to initial
393+
content = await readFile(testPath, 'utf8');
394+
expect(content).toBe(initialContent);
395+
});
306396
});

packages/agent/src/tools/io/textEditor.ts

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -160,29 +160,36 @@ export const textEditorTool: Tool<Parameters, ReturnType> = {
160160
}
161161

162162
case 'create': {
163-
// Check if file already exists
164-
if (fsSync.existsSync(absolutePath)) {
165-
throw new Error(
166-
`File already exists: ${filePath}. Use str_replace to modify it.`,
167-
);
168-
}
169-
170163
if (!file_text) {
171164
throw new Error('file_text parameter is required for create command');
172165
}
173166

174167
// Create parent directories if they don't exist
175168
await fs.mkdir(path.dirname(absolutePath), { recursive: true });
176169

177-
// Create the file
178-
await fs.writeFile(absolutePath, file_text, 'utf8');
170+
// Check if file already exists
171+
const fileExists = fsSync.existsSync(absolutePath);
179172

180-
// Store initial state for undo
181-
fileStateHistory[absolutePath] = [file_text];
173+
if (fileExists) {
174+
// Save current state for undo if file exists
175+
const currentContent = await fs.readFile(absolutePath, 'utf8');
176+
if (!fileStateHistory[absolutePath]) {
177+
fileStateHistory[absolutePath] = [];
178+
}
179+
fileStateHistory[absolutePath].push(currentContent);
180+
} else {
181+
// Initialize history for new files
182+
fileStateHistory[absolutePath] = [];
183+
}
184+
185+
// Create or overwrite the file
186+
await fs.writeFile(absolutePath, file_text, 'utf8');
182187

183188
return {
184189
success: true,
185-
message: `File created: ${filePath}`,
190+
message: fileExists
191+
? `File overwritten: ${filePath}`
192+
: `File created: ${filePath}`,
186193
};
187194
}
188195

0 commit comments

Comments
 (0)