From e9242711f4fe10ef7b0048cf7a40bae918eab1f2 Mon Sep 17 00:00:00 2001 From: Eric Wheeler Date: Fri, 21 Mar 2025 14:27:41 -0700 Subject: [PATCH 1/8] hack: removed line numbers --- src/integrations/misc/extract-text.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/integrations/misc/extract-text.ts b/src/integrations/misc/extract-text.ts index 5bbbbf8514..924badc1d1 100644 --- a/src/integrations/misc/extract-text.ts +++ b/src/integrations/misc/extract-text.ts @@ -55,6 +55,8 @@ async function extractTextFromIPYNB(filePath: string): Promise { } export function addLineNumbers(content: string, startLine: number = 1): string { + return content + // If content is empty, return empty string - empty files should not have line numbers // If content is empty but startLine > 1, return "startLine | " because we know the file is not empty // but the content is empty at that line offset From 4619a26d55076c72cfd9e6e01c9e9daf9dc14af5 Mon Sep 17 00:00:00 2001 From: Eric Wheeler Date: Fri, 21 Mar 2025 14:41:12 -0700 Subject: [PATCH 2/8] hack: turn off diff line number validation in apply_diff --- src/core/diff/strategies/multi-search-replace.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/diff/strategies/multi-search-replace.ts b/src/core/diff/strategies/multi-search-replace.ts index 00cebd2bea..f8f4e656e8 100644 --- a/src/core/diff/strategies/multi-search-replace.ts +++ b/src/core/diff/strategies/multi-search-replace.ts @@ -423,8 +423,10 @@ Only use a single line of '=======' between search and replacement content, beca let searchStartIndex = 0 let searchEndIndex = resultLines.length + startLine = endLine = 0 + // Validate and handle line range if provided - if (startLine) { + if (startLine && endLine) { // Convert to 0-based index const exactStartIndex = startLine - 1 const searchLen = searchLines.length @@ -483,7 +485,7 @@ Only use a single line of '=======' between search and replacement content, beca } else { // No match found with either method const originalContentSection = - startLine !== undefined && endLine !== undefined + startLine && endLine ? `\n\nOriginal Content:\n${addLineNumbers( resultLines .slice( From d6fe15b19e2fc61c56b81f25c6a2bef724273ffa Mon Sep 17 00:00:00 2001 From: Eric Wheeler Date: Fri, 21 Mar 2025 15:03:56 -0700 Subject: [PATCH 3/8] hack: force apply_diff for existing files --- src/core/tools/writeToFileTool.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/core/tools/writeToFileTool.ts b/src/core/tools/writeToFileTool.ts index a23aea9714..21bb9d2381 100644 --- a/src/core/tools/writeToFileTool.ts +++ b/src/core/tools/writeToFileTool.ts @@ -69,6 +69,15 @@ export async function writeToFileTool( const fullPath = relPath ? path.resolve(cline.cwd, removeClosingTag("path", relPath)) : "" const isOutsideWorkspace = isPathOutsideWorkspace(fullPath) + if (fileExists) { + pushToolResult( + formatResponse.toolError( + `File '${relPath}' already exists, write_to_file failed: You must use the 'apply_diff' tool to change an existing file.`, + ), + ) + return + } + const sharedMessageProps: ClineSayTool = { tool: fileExists ? "editedExistingFile" : "newFileCreated", path: getReadablePath(cline.cwd, removeClosingTag("path", relPath)), From 41c871df05fdddc4f0e8f85e3c1fa29e89353fd6 Mon Sep 17 00:00:00 2001 From: Eric Wheeler Date: Thu, 24 Apr 2025 16:50:59 -0700 Subject: [PATCH 4/8] disable insert_content instructions this tool does not work without line numbers --- src/core/prompts/tools/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/prompts/tools/index.ts b/src/core/prompts/tools/index.ts index d3e75d7b09..0df062f82d 100644 --- a/src/core/prompts/tools/index.ts +++ b/src/core/prompts/tools/index.ts @@ -37,7 +37,7 @@ const toolDescriptionMap: Record string | undefined> access_mcp_resource: (args) => getAccessMcpResourceDescription(args), switch_mode: () => getSwitchModeDescription(), new_task: (args) => getNewTaskDescription(args), - insert_content: (args) => getInsertContentDescription(args), + // insert_content: (args) => getInsertContentDescription(args), search_and_replace: (args) => getSearchAndReplaceDescription(args), apply_diff: (args) => args.diffStrategy ? args.diffStrategy.getToolDescription({ cwd: args.cwd, toolOptions: args.toolOptions }) : "", From 0ecc56f8b3c6b125a58023a625ed5b52a1c16589 Mon Sep 17 00:00:00 2001 From: Eric Wheeler Date: Fri, 25 Apr 2025 17:14:44 -0700 Subject: [PATCH 5/8] Revert "disable insert_content instructions" This reverts commit 41c871df05fdddc4f0e8f85e3c1fa29e89353fd6. --- src/core/prompts/tools/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/prompts/tools/index.ts b/src/core/prompts/tools/index.ts index 0df062f82d..d3e75d7b09 100644 --- a/src/core/prompts/tools/index.ts +++ b/src/core/prompts/tools/index.ts @@ -37,7 +37,7 @@ const toolDescriptionMap: Record string | undefined> access_mcp_resource: (args) => getAccessMcpResourceDescription(args), switch_mode: () => getSwitchModeDescription(), new_task: (args) => getNewTaskDescription(args), - // insert_content: (args) => getInsertContentDescription(args), + insert_content: (args) => getInsertContentDescription(args), search_and_replace: (args) => getSearchAndReplaceDescription(args), apply_diff: (args) => args.diffStrategy ? args.diffStrategy.getToolDescription({ cwd: args.cwd, toolOptions: args.toolOptions }) : "", From d9827b9e85a5f73fe67bc318e4d83f8bb9f90d5c Mon Sep 17 00:00:00 2001 From: Eric Wheeler Date: Fri, 25 Apr 2025 17:18:09 -0700 Subject: [PATCH 6/8] fix: restrict insert_content to append-only The insert_content tool was intended for appending content only, but the code allowed arbitrary line numbers which could corrupt files. This change enforces that line_number must be 0 (append-only mode) to prevent file corruption. Signed-off-by: Eric Wheeler --- src/core/tools/insertContentTool.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/core/tools/insertContentTool.ts b/src/core/tools/insertContentTool.ts index 8e6c5fc89e..d85f6c35ed 100644 --- a/src/core/tools/insertContentTool.ts +++ b/src/core/tools/insertContentTool.ts @@ -78,6 +78,13 @@ export async function insertContentTool( return } + if (lineNumber !== 0) { + cline.consecutiveMistakeCount++ + cline.recordToolError("insert_content") + pushToolResult(formatResponse.toolError("Invalid line number: only append is supported so line must be 0")) + return + } + cline.consecutiveMistakeCount = 0 // Read the file From 02e3dce216c9b319d36e6fe3b671eb4c16220283 Mon Sep 17 00:00:00 2001 From: Eric Wheeler Date: Fri, 25 Apr 2025 17:22:39 -0700 Subject: [PATCH 7/8] docs: add example for handling large files with insert_content Add documentation showing how to use insert_content in conjunction with write_to_file to handle files that exceed output limits by breaking them into multiple append operations. Signed-off-by: Eric Wheeler --- src/core/prompts/tools/insert-content.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/core/prompts/tools/insert-content.ts b/src/core/prompts/tools/insert-content.ts index 7e339513d5..c575c0eb5c 100644 --- a/src/core/prompts/tools/insert-content.ts +++ b/src/core/prompts/tools/insert-content.ts @@ -29,5 +29,23 @@ Example for appending to the end of file: // This is the end of the file + +Example for creating very large files that exceed output limits: + +src/large_file.txt + +// This is the beginning of a very large file but you must terminate prematurely in order for line_count to be produced: + +100 + + +Then use insert_content to append the rest of the content starting immediately where you left off; repeat as many times as necessary: + +src/large_file.txt +0 + +// This is a continuation of very large file + + ` } From 5ea1198fec388b6d6270aaacc87108317330d959 Mon Sep 17 00:00:00 2001 From: Eric Wheeler Date: Fri, 25 Apr 2025 17:51:59 -0700 Subject: [PATCH 8/8] feat: only require apply_diff for files >25 lines Add line count check to writeToFileTool to only require apply_diff for files that are more than 25 lines long. This allows write_to_file to work on small existing files while still protecting larger files that need more careful modification. Signed-off-by: Eric Wheeler --- src/core/tools/writeToFileTool.ts | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/core/tools/writeToFileTool.ts b/src/core/tools/writeToFileTool.ts index 21bb9d2381..e0a302e1a1 100644 --- a/src/core/tools/writeToFileTool.ts +++ b/src/core/tools/writeToFileTool.ts @@ -1,6 +1,7 @@ import path from "path" import delay from "delay" import * as vscode from "vscode" +import { countFileLines } from "../../integrations/misc/line-counter" import { Cline } from "../Cline" import { ClineSayTool } from "../../shared/ExtensionMessage" @@ -70,12 +71,18 @@ export async function writeToFileTool( const isOutsideWorkspace = isPathOutsideWorkspace(fullPath) if (fileExists) { - pushToolResult( - formatResponse.toolError( - `File '${relPath}' already exists, write_to_file failed: You must use the 'apply_diff' tool to change an existing file.`, - ), - ) - return + // Count the lines in the file + const absolutePath = path.resolve(cline.cwd, relPath) + const lineCount = await countFileLines(absolutePath) + // Only show error if file has more than 25 lines + if (lineCount > 25) { + pushToolResult( + formatResponse.toolError( + `File '${relPath}' already exists and is >25 lines long, write_to_file failed: You must use the '' or '' tool to change an existing file.`, + ), + ) + return + } } const sharedMessageProps: ClineSayTool = {