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( 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 + + ` } 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 diff --git a/src/core/tools/writeToFileTool.ts b/src/core/tools/writeToFileTool.ts index a23aea9714..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" @@ -69,6 +70,21 @@ export async function writeToFileTool( const fullPath = relPath ? path.resolve(cline.cwd, removeClosingTag("path", relPath)) : "" const isOutsideWorkspace = isPathOutsideWorkspace(fullPath) + if (fileExists) { + // 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 = { tool: fileExists ? "editedExistingFile" : "newFileCreated", path: getReadablePath(cline.cwd, removeClosingTag("path", relPath)), 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