From 8f891d5dd08a332aaa2f3607683417099a7a01f8 Mon Sep 17 00:00:00 2001 From: Yue_chen <1737456545@qq.com> Date: Sun, 5 Jul 2026 14:13:31 +0800 Subject: [PATCH] fix(tools): stop write_file from coaching destructive overwrite (#3150) The overwrite guard error ("Set overwrite=true to replace") steered the model to clobber files like MEMORY.md. Reword write_file's description, overwrite param, and guard error to prefer append_file/edit_file. Co-Authored-By: Claude Opus 4.8 (1M context) --- pkg/tools/fs/filesystem.go | 9 ++++++--- pkg/tools/fs/filesystem_test.go | 3 +++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/pkg/tools/fs/filesystem.go b/pkg/tools/fs/filesystem.go index adc7fa998..2823b02a7 100644 --- a/pkg/tools/fs/filesystem.go +++ b/pkg/tools/fs/filesystem.go @@ -882,7 +882,7 @@ func (t *WriteFileTool) Name() string { } func (t *WriteFileTool) Description() string { - return "Write content to a file. Content is written byte-for-byte after argument decoding. Standard JSON escaping applies: \\n for newline and \\\\n for a literal backslash-n sequence. If the file already exists, you must set overwrite=true to replace it." + return "Write content to a file, replacing any existing content. Content is written byte-for-byte after argument decoding. Standard JSON escaping applies: \\n for newline and \\\\n for a literal backslash-n sequence. If the file already exists you must set overwrite=true, which replaces the ENTIRE file. To add to or change part of an existing file without losing its current contents, use append_file or edit_file instead." } func (t *WriteFileTool) Parameters() map[string]any { @@ -899,7 +899,7 @@ func (t *WriteFileTool) Parameters() map[string]any { }, "overwrite": map[string]any{ "type": "boolean", - "description": "Must be set to true to overwrite an existing file.", + "description": "Set to true to replace an existing file in full. This discards the file's current contents — to preserve them, use append_file or edit_file instead of write_file.", "default": false, }, }, @@ -923,7 +923,10 @@ func (t *WriteFileTool) Execute(ctx context.Context, args map[string]any) *ToolR if !overwrite { if _, err := t.fs.Open(path); err == nil { return ErrorResult( - fmt.Sprintf("file: %s already exists. Set overwrite=true to replace.", path), + fmt.Sprintf( + "file: %s already exists. To add to it or change part of it without losing the current contents, use append_file or edit_file. Only set overwrite=true if you intend to replace the entire file.", + path, + ), ) } } diff --git a/pkg/tools/fs/filesystem_test.go b/pkg/tools/fs/filesystem_test.go index 4387332be..cfa9a1cec 100644 --- a/pkg/tools/fs/filesystem_test.go +++ b/pkg/tools/fs/filesystem_test.go @@ -250,6 +250,9 @@ func TestFilesystemTool_WriteFile_OverwriteDefaultBlocked(t *testing.T) { assert.True(t, result.IsError, "expected error when overwriting without overwrite=true") assert.Contains(t, result.ForLLM, "already exists") assert.Contains(t, result.ForLLM, "overwrite=true") + // The guard must steer toward non-destructive tools rather than only coaching overwrite. + assert.Contains(t, result.ForLLM, "append_file") + assert.Contains(t, result.ForLLM, "edit_file") // Original content must be untouched data, err := os.ReadFile(testFile)