فهرست منبع

Refactor: Remove line_count parameter from write_to_file tool (#9667)

Hannes Rudolph 1 ماه پیش
والد
کامیت
be69ef901e
25فایلهای تغییر یافته به همراه61 افزوده شده و 275 حذف شده
  1. 2 12
      src/core/assistant-message/NativeToolCallParser.ts
  2. 3 5
      src/core/assistant-message/__tests__/AssistantMessageParser.spec.ts
  3. 3 5
      src/core/assistant-message/__tests__/parseAssistantMessage.spec.ts
  4. 3 3
      src/core/assistant-message/__tests__/parseAssistantMessageBenchmark.ts
  5. 0 3
      src/core/prompts/__tests__/__snapshots__/add-custom-instructions/architect-mode-prompt.snap
  6. 0 3
      src/core/prompts/__tests__/__snapshots__/add-custom-instructions/mcp-server-creation-disabled.snap
  7. 0 3
      src/core/prompts/__tests__/__snapshots__/add-custom-instructions/mcp-server-creation-enabled.snap
  8. 0 3
      src/core/prompts/__tests__/__snapshots__/add-custom-instructions/partial-reads-enabled.snap
  9. 0 3
      src/core/prompts/__tests__/__snapshots__/system-prompt/consistent-system-prompt.snap
  10. 0 3
      src/core/prompts/__tests__/__snapshots__/system-prompt/with-computer-use-support.snap
  11. 0 3
      src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-false.snap
  12. 0 3
      src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-true.snap
  13. 0 3
      src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-undefined.snap
  14. 0 3
      src/core/prompts/__tests__/__snapshots__/system-prompt/with-different-viewport-size.snap
  15. 0 3
      src/core/prompts/__tests__/__snapshots__/system-prompt/with-mcp-hub-provided.snap
  16. 0 3
      src/core/prompts/__tests__/__snapshots__/system-prompt/with-undefined-mcp-hub.snap
  17. 0 39
      src/core/prompts/responses.ts
  18. 2 9
      src/core/prompts/tools/native-tools/write_to_file.ts
  19. 0 3
      src/core/prompts/tools/write-to-file.ts
  20. 1 2
      src/core/task/Task.ts
  21. 12 52
      src/core/tools/WriteToFileTool.ts
  22. 3 6
      src/core/tools/__tests__/writeToFileTool.spec.ts
  23. 23 85
      src/integrations/editor/__tests__/detect-omission.spec.ts
  24. 7 15
      src/integrations/editor/detect-omission.ts
  25. 2 3
      src/shared/tools.ts

+ 2 - 12
src/core/assistant-message/NativeToolCallParser.ts

@@ -394,16 +394,10 @@ export class NativeToolCallParser {
 				break
 
 			case "write_to_file":
-				if (partialArgs.path || partialArgs.content || partialArgs.line_count !== undefined) {
+				if (partialArgs.path || partialArgs.content) {
 					nativeArgs = {
 						path: partialArgs.path,
 						content: partialArgs.content,
-						line_count:
-							typeof partialArgs.line_count === "number"
-								? partialArgs.line_count
-								: partialArgs.line_count
-									? parseInt(String(partialArgs.line_count), 10)
-									: undefined,
 					}
 				}
 				break
@@ -745,14 +739,10 @@ export class NativeToolCallParser {
 					break
 
 				case "write_to_file":
-					if (args.path !== undefined && args.content !== undefined && args.line_count !== undefined) {
+					if (args.path !== undefined && args.content !== undefined) {
 						nativeArgs = {
 							path: args.path,
 							content: args.content,
-							line_count:
-								typeof args.line_count === "number"
-									? args.line_count
-									: parseInt(String(args.line_count), 10),
 						} as NativeArgsFor<TName>
 					}
 					break

+ 3 - 5
src/core/assistant-message/__tests__/AssistantMessageParser.spec.ts

@@ -179,7 +179,7 @@ describe("AssistantMessageParser (streaming)", () => {
 	// This has XML-like content: </content>
 	return true;
 	}
-	</content><line_count>5</line_count></write_to_file>`
+	</content></write_to_file>`
 
 			const result = streamChunks(parser, message).filter((block) => !isEmptyTextContent(block))
 
@@ -188,7 +188,6 @@ describe("AssistantMessageParser (streaming)", () => {
 			expect(toolUse.type).toBe("tool_use")
 			expect(toolUse.name).toBe("write_to_file")
 			expect(toolUse.params.path).toBe("src/file.ts")
-			expect(toolUse.params.line_count).toBe("5")
 			expect(toolUse.params.content).toContain("function example()")
 			expect(toolUse.params.content).toContain("// This has XML-like content: </content>")
 			expect(toolUse.params.content).toContain("return true;")
@@ -263,7 +262,7 @@ describe("AssistantMessageParser (streaming)", () => {
 	line 1
 	line 2
 	line 3
-	</content><line_count>3</line_count></write_to_file>`
+	</content></write_to_file>`
 			const result = streamChunks(parser, message).filter((block) => !isEmptyTextContent(block))
 
 			expect(result).toHaveLength(1)
@@ -274,7 +273,6 @@ describe("AssistantMessageParser (streaming)", () => {
 			expect(toolUse.params.content).toContain("line 1")
 			expect(toolUse.params.content).toContain("line 2")
 			expect(toolUse.params.content).toContain("line 3")
-			expect(toolUse.params.line_count).toBe("3")
 			expect(toolUse.partial).toBe(false)
 		})
 		it("should handle a complex message with multiple content types", () => {
@@ -287,7 +285,7 @@ describe("AssistantMessageParser (streaming)", () => {
 	<write_to_file><path>src/index.ts</path><content>
 	// Updated content
 	console.log("Hello world");
-	</content><line_count>2</line_count></write_to_file>
+	</content></write_to_file>
 
 	Let's run the code:
 

+ 3 - 5
src/core/assistant-message/__tests__/parseAssistantMessage.spec.ts

@@ -168,7 +168,7 @@ const isEmptyTextContent = (block: AssistantMessageContent) =>
 	// This has XML-like content: </content>
 	return true;
 	}
-	</content><line_count>5</line_count></write_to_file>`
+	</content></write_to_file>`
 
 				const result = parser(message).filter((block) => !isEmptyTextContent(block))
 
@@ -177,7 +177,6 @@ const isEmptyTextContent = (block: AssistantMessageContent) =>
 				expect(toolUse.type).toBe("tool_use")
 				expect(toolUse.name).toBe("write_to_file")
 				expect(toolUse.params.path).toBe("src/file.ts")
-				expect(toolUse.params.line_count).toBe("5")
 				expect(toolUse.params.content).toContain("function example()")
 				expect(toolUse.params.content).toContain("// This has XML-like content: </content>")
 				expect(toolUse.params.content).toContain("return true;")
@@ -276,7 +275,7 @@ const isEmptyTextContent = (block: AssistantMessageContent) =>
 	line 1
 	line 2
 	line 3
-	</content><line_count>3</line_count></write_to_file>`
+	</content></write_to_file>`
 				const result = parser(message).filter((block) => !isEmptyTextContent(block))
 
 				expect(result).toHaveLength(1)
@@ -287,7 +286,6 @@ const isEmptyTextContent = (block: AssistantMessageContent) =>
 				expect(toolUse.params.content).toContain("line 1")
 				expect(toolUse.params.content).toContain("line 2")
 				expect(toolUse.params.content).toContain("line 3")
-				expect(toolUse.params.line_count).toBe("3")
 				expect(toolUse.partial).toBe(false)
 			})
 
@@ -301,7 +299,7 @@ const isEmptyTextContent = (block: AssistantMessageContent) =>
 	<write_to_file><path>src/index.ts</path><content>
 	// Updated content
 	console.log("Hello world");
-	</content><line_count>2</line_count></write_to_file>
+	</content></write_to_file>
 
 	Let's run the code:
 

+ 3 - 3
src/core/assistant-message/__tests__/parseAssistantMessageBenchmark.ts

@@ -62,17 +62,17 @@ const testCases = [
 	},
 	{
 		name: "Message with a complex tool use (write_to_file)",
-		input: "<write_to_file><path>src/file.ts</path><content>\nfunction example() {\n  // This has XML-like content: </content>\n  return true;\n}\n</content><line_count>5</line_count></write_to_file>",
+		input: "<write_to_file><path>src/file.ts</path><content>\nfunction example() {\n  // This has XML-like content: </content>\n  return true;\n}\n</content></write_to_file>",
 	},
 	{
 		name: "Message with multiple tool uses",
-		input: "First file: <read_file><path>src/file1.ts</path></read_file>\nSecond file: <read_file><path>src/file2.ts</path></read_file>\nLet's write a new file: <write_to_file><path>src/file3.ts</path><content>\nexport function newFunction() {\n  return 'Hello world';\n}\n</content><line_count>3</line_count></write_to_file>",
+		input: "First file: <read_file><path>src/file1.ts</path></read_file>\nSecond file: <read_file><path>src/file2.ts</path></read_file>\nLet's write a new file: <write_to_file><path>src/file3.ts</path><content>\nexport function newFunction() {\n  return 'Hello world';\n}\n</content></write_to_file>",
 	},
 	{
 		name: "Large message with repeated tool uses",
 		input: Array(50)
 			.fill(
-				'<read_file><path>src/file.ts</path></read_file>\n<write_to_file><path>output.ts</path><content>console.log("hello");</content><line_count>1</line_count></write_to_file>',
+				'<read_file><path>src/file.ts</path></read_file>\n<write_to_file><path>output.ts</path><content>console.log("hello");</content></write_to_file>',
 			)
 			.join("\n"),
 	},

+ 0 - 3
src/core/prompts/__tests__/__snapshots__/add-custom-instructions/architect-mode-prompt.snap

@@ -164,14 +164,12 @@ Description: Request to write content to a file. This tool is primarily used for
 Parameters:
 - path: (required) The path of the file to write to (relative to the current workspace directory /test/path)
 - content: (required) The content to write to the file. When performing a full rewrite of an existing file or creating a new one, ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file.
-- line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing.
 Usage:
 <write_to_file>
 <path>File path here</path>
 <content>
 Your file content here
 </content>
-<line_count>total number of lines in the file, including empty lines</line_count>
 </write_to_file>
 
 Example: Requesting to write to frontend-config.json
@@ -193,7 +191,6 @@ Example: Requesting to write to frontend-config.json
   "version": "1.0.0"
 }
 </content>
-<line_count>14</line_count>
 </write_to_file>
 
 ## insert_content

+ 0 - 3
src/core/prompts/__tests__/__snapshots__/add-custom-instructions/mcp-server-creation-disabled.snap

@@ -163,14 +163,12 @@ Description: Request to write content to a file. This tool is primarily used for
 Parameters:
 - path: (required) The path of the file to write to (relative to the current workspace directory /test/path)
 - content: (required) The content to write to the file. When performing a full rewrite of an existing file or creating a new one, ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file.
-- line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing.
 Usage:
 <write_to_file>
 <path>File path here</path>
 <content>
 Your file content here
 </content>
-<line_count>total number of lines in the file, including empty lines</line_count>
 </write_to_file>
 
 Example: Requesting to write to frontend-config.json
@@ -192,7 +190,6 @@ Example: Requesting to write to frontend-config.json
   "version": "1.0.0"
 }
 </content>
-<line_count>14</line_count>
 </write_to_file>
 
 ## insert_content

+ 0 - 3
src/core/prompts/__tests__/__snapshots__/add-custom-instructions/mcp-server-creation-enabled.snap

@@ -164,14 +164,12 @@ Description: Request to write content to a file. This tool is primarily used for
 Parameters:
 - path: (required) The path of the file to write to (relative to the current workspace directory /test/path)
 - content: (required) The content to write to the file. When performing a full rewrite of an existing file or creating a new one, ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file.
-- line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing.
 Usage:
 <write_to_file>
 <path>File path here</path>
 <content>
 Your file content here
 </content>
-<line_count>total number of lines in the file, including empty lines</line_count>
 </write_to_file>
 
 Example: Requesting to write to frontend-config.json
@@ -193,7 +191,6 @@ Example: Requesting to write to frontend-config.json
   "version": "1.0.0"
 }
 </content>
-<line_count>14</line_count>
 </write_to_file>
 
 ## insert_content

+ 0 - 3
src/core/prompts/__tests__/__snapshots__/add-custom-instructions/partial-reads-enabled.snap

@@ -169,14 +169,12 @@ Description: Request to write content to a file. This tool is primarily used for
 Parameters:
 - path: (required) The path of the file to write to (relative to the current workspace directory /test/path)
 - content: (required) The content to write to the file. When performing a full rewrite of an existing file or creating a new one, ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file.
-- line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing.
 Usage:
 <write_to_file>
 <path>File path here</path>
 <content>
 Your file content here
 </content>
-<line_count>total number of lines in the file, including empty lines</line_count>
 </write_to_file>
 
 Example: Requesting to write to frontend-config.json
@@ -198,7 +196,6 @@ Example: Requesting to write to frontend-config.json
   "version": "1.0.0"
 }
 </content>
-<line_count>14</line_count>
 </write_to_file>
 
 ## insert_content

+ 0 - 3
src/core/prompts/__tests__/__snapshots__/system-prompt/consistent-system-prompt.snap

@@ -164,14 +164,12 @@ Description: Request to write content to a file. This tool is primarily used for
 Parameters:
 - path: (required) The path of the file to write to (relative to the current workspace directory /test/path)
 - content: (required) The content to write to the file. When performing a full rewrite of an existing file or creating a new one, ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file.
-- line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing.
 Usage:
 <write_to_file>
 <path>File path here</path>
 <content>
 Your file content here
 </content>
-<line_count>total number of lines in the file, including empty lines</line_count>
 </write_to_file>
 
 Example: Requesting to write to frontend-config.json
@@ -193,7 +191,6 @@ Example: Requesting to write to frontend-config.json
   "version": "1.0.0"
 }
 </content>
-<line_count>14</line_count>
 </write_to_file>
 
 ## insert_content

+ 0 - 3
src/core/prompts/__tests__/__snapshots__/system-prompt/with-computer-use-support.snap

@@ -164,14 +164,12 @@ Description: Request to write content to a file. This tool is primarily used for
 Parameters:
 - path: (required) The path of the file to write to (relative to the current workspace directory /test/path)
 - content: (required) The content to write to the file. When performing a full rewrite of an existing file or creating a new one, ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file.
-- line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing.
 Usage:
 <write_to_file>
 <path>File path here</path>
 <content>
 Your file content here
 </content>
-<line_count>total number of lines in the file, including empty lines</line_count>
 </write_to_file>
 
 Example: Requesting to write to frontend-config.json
@@ -193,7 +191,6 @@ Example: Requesting to write to frontend-config.json
   "version": "1.0.0"
 }
 </content>
-<line_count>14</line_count>
 </write_to_file>
 
 ## insert_content

+ 0 - 3
src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-false.snap

@@ -164,14 +164,12 @@ Description: Request to write content to a file. This tool is primarily used for
 Parameters:
 - path: (required) The path of the file to write to (relative to the current workspace directory /test/path)
 - content: (required) The content to write to the file. When performing a full rewrite of an existing file or creating a new one, ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file.
-- line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing.
 Usage:
 <write_to_file>
 <path>File path here</path>
 <content>
 Your file content here
 </content>
-<line_count>total number of lines in the file, including empty lines</line_count>
 </write_to_file>
 
 Example: Requesting to write to frontend-config.json
@@ -193,7 +191,6 @@ Example: Requesting to write to frontend-config.json
   "version": "1.0.0"
 }
 </content>
-<line_count>14</line_count>
 </write_to_file>
 
 ## insert_content

+ 0 - 3
src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-true.snap

@@ -252,14 +252,12 @@ Description: Request to write content to a file. This tool is primarily used for
 Parameters:
 - path: (required) The path of the file to write to (relative to the current workspace directory /test/path)
 - content: (required) The content to write to the file. When performing a full rewrite of an existing file or creating a new one, ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file.
-- line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing.
 Usage:
 <write_to_file>
 <path>File path here</path>
 <content>
 Your file content here
 </content>
-<line_count>total number of lines in the file, including empty lines</line_count>
 </write_to_file>
 
 Example: Requesting to write to frontend-config.json
@@ -281,7 +279,6 @@ Example: Requesting to write to frontend-config.json
   "version": "1.0.0"
 }
 </content>
-<line_count>14</line_count>
 </write_to_file>
 
 ## insert_content

+ 0 - 3
src/core/prompts/__tests__/__snapshots__/system-prompt/with-diff-enabled-undefined.snap

@@ -164,14 +164,12 @@ Description: Request to write content to a file. This tool is primarily used for
 Parameters:
 - path: (required) The path of the file to write to (relative to the current workspace directory /test/path)
 - content: (required) The content to write to the file. When performing a full rewrite of an existing file or creating a new one, ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file.
-- line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing.
 Usage:
 <write_to_file>
 <path>File path here</path>
 <content>
 Your file content here
 </content>
-<line_count>total number of lines in the file, including empty lines</line_count>
 </write_to_file>
 
 Example: Requesting to write to frontend-config.json
@@ -193,7 +191,6 @@ Example: Requesting to write to frontend-config.json
   "version": "1.0.0"
 }
 </content>
-<line_count>14</line_count>
 </write_to_file>
 
 ## insert_content

+ 0 - 3
src/core/prompts/__tests__/__snapshots__/system-prompt/with-different-viewport-size.snap

@@ -164,14 +164,12 @@ Description: Request to write content to a file. This tool is primarily used for
 Parameters:
 - path: (required) The path of the file to write to (relative to the current workspace directory /test/path)
 - content: (required) The content to write to the file. When performing a full rewrite of an existing file or creating a new one, ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file.
-- line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing.
 Usage:
 <write_to_file>
 <path>File path here</path>
 <content>
 Your file content here
 </content>
-<line_count>total number of lines in the file, including empty lines</line_count>
 </write_to_file>
 
 Example: Requesting to write to frontend-config.json
@@ -193,7 +191,6 @@ Example: Requesting to write to frontend-config.json
   "version": "1.0.0"
 }
 </content>
-<line_count>14</line_count>
 </write_to_file>
 
 ## insert_content

+ 0 - 3
src/core/prompts/__tests__/__snapshots__/system-prompt/with-mcp-hub-provided.snap

@@ -164,14 +164,12 @@ Description: Request to write content to a file. This tool is primarily used for
 Parameters:
 - path: (required) The path of the file to write to (relative to the current workspace directory /test/path)
 - content: (required) The content to write to the file. When performing a full rewrite of an existing file or creating a new one, ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file.
-- line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing.
 Usage:
 <write_to_file>
 <path>File path here</path>
 <content>
 Your file content here
 </content>
-<line_count>total number of lines in the file, including empty lines</line_count>
 </write_to_file>
 
 Example: Requesting to write to frontend-config.json
@@ -193,7 +191,6 @@ Example: Requesting to write to frontend-config.json
   "version": "1.0.0"
 }
 </content>
-<line_count>14</line_count>
 </write_to_file>
 
 ## insert_content

+ 0 - 3
src/core/prompts/__tests__/__snapshots__/system-prompt/with-undefined-mcp-hub.snap

@@ -164,14 +164,12 @@ Description: Request to write content to a file. This tool is primarily used for
 Parameters:
 - path: (required) The path of the file to write to (relative to the current workspace directory /test/path)
 - content: (required) The content to write to the file. When performing a full rewrite of an existing file or creating a new one, ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file.
-- line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing.
 Usage:
 <write_to_file>
 <path>File path here</path>
 <content>
 Your file content here
 </content>
-<line_count>total number of lines in the file, including empty lines</line_count>
 </write_to_file>
 
 Example: Requesting to write to frontend-config.json
@@ -193,7 +191,6 @@ Example: Requesting to write to frontend-config.json
   "version": "1.0.0"
 }
 </content>
-<line_count>14</line_count>
 </write_to_file>
 
 ## insert_content

+ 0 - 39
src/core/prompts/responses.ts

@@ -94,45 +94,6 @@ Otherwise, if you have not completed the task and do not need additional informa
 		return `Missing value for required parameter '${paramName}'. Please retry with complete response.\n\n${instructions}`
 	},
 
-	lineCountTruncationError: (
-		actualLineCount: number,
-		isNewFile: boolean,
-		diffStrategyEnabled: boolean = false,
-		protocol?: ToolProtocol,
-	) => {
-		const truncationMessage = `Note: Your response may have been truncated because it exceeded your output limit. You wrote ${actualLineCount} lines of content, but the line_count parameter was either missing or not included in your response.`
-
-		const newFileGuidance =
-			`This appears to be a new file.\n` +
-			`${truncationMessage}\n\n` +
-			`RECOMMENDED APPROACH:\n` +
-			`1. Try again with the line_count parameter in your response if you forgot to include it\n` +
-			`2. Or break your content into smaller chunks - first use write_to_file with the initial chunk\n` +
-			`3. Then use insert_content to append additional chunks\n`
-
-		let existingFileApproaches = [
-			`1. Try again with the line_count parameter in your response if you forgot to include it`,
-		]
-
-		if (diffStrategyEnabled) {
-			existingFileApproaches.push(`2. Or try using apply_diff instead of write_to_file for targeted changes`)
-		}
-
-		existingFileApproaches.push(
-			`${diffStrategyEnabled ? "3" : "2"}. Or use insert_content to add specific content at particular lines`,
-		)
-
-		const existingFileGuidance =
-			`This appears to be content for an existing file.\n` +
-			`${truncationMessage}\n\n` +
-			`RECOMMENDED APPROACH:\n` +
-			`${existingFileApproaches.join("\n")}\n`
-
-		const instructions = getToolInstructionsReminder(protocol)
-
-		return `${isNewFile ? newFileGuidance : existingFileGuidance}\n${instructions}`
-	},
-
 	invalidMcpToolArgumentError: (serverName: string, toolName: string, protocol?: ToolProtocol) => {
 		if (isNativeProtocol(protocol ?? TOOL_PROTOCOL.XML)) {
 			return JSON.stringify({

+ 2 - 9
src/core/prompts/tools/native-tools/write_to_file.ts

@@ -5,17 +5,14 @@ const WRITE_TO_FILE_DESCRIPTION = `Request to write content to a file. This tool
 Parameters:
 - path: (required) The path of the file to write to (relative to the current workspace directory)
 - content: (required) The content to write to the file. When performing a full rewrite of an existing file or creating a new one, ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file.
-- line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing.
 
 Example: Writing a configuration file
-{ "path": "frontend-config.json", "content": "{\\n  \\"apiEndpoint\\": \\"https://api.example.com\\",\\n  \\"theme\\": {\\n    \\"primaryColor\\": \\"#007bff\\"\\n  }\\n}", "line_count": 5 }`
+{ "path": "frontend-config.json", "content": "{\\n  \\"apiEndpoint\\": \\"https://api.example.com\\",\\n  \\"theme\\": {\\n    \\"primaryColor\\": \\"#007bff\\"\\n  }\\n}" }`
 
 const PATH_PARAMETER_DESCRIPTION = `Path to the file to write, relative to the workspace`
 
 const CONTENT_PARAMETER_DESCRIPTION = `Full contents that the file should contain with no omissions or line numbers`
 
-const LINE_COUNT_PARAMETER_DESCRIPTION = `Total number of lines in the written file, counting blank lines`
-
 export default {
 	type: "function",
 	function: {
@@ -33,12 +30,8 @@ export default {
 					type: "string",
 					description: CONTENT_PARAMETER_DESCRIPTION,
 				},
-				line_count: {
-					type: "integer",
-					description: LINE_COUNT_PARAMETER_DESCRIPTION,
-				},
 			},
-			required: ["path", "content", "line_count"],
+			required: ["path", "content"],
 			additionalProperties: false,
 		},
 	},

+ 0 - 3
src/core/prompts/tools/write-to-file.ts

@@ -6,14 +6,12 @@ Description: Request to write content to a file. This tool is primarily used for
 Parameters:
 - path: (required) The path of the file to write to (relative to the current workspace directory ${args.cwd})
 - content: (required) The content to write to the file. When performing a full rewrite of an existing file or creating a new one, ALWAYS provide the COMPLETE intended content of the file, without any truncation or omissions. You MUST include ALL parts of the file, even if they haven't been modified. Do NOT include the line numbers in the content though, just the actual content of the file.
-- line_count: (required) The number of lines in the file. Make sure to compute this based on the actual content of the file, not the number of lines in the content you're providing.
 Usage:
 <write_to_file>
 <path>File path here</path>
 <content>
 Your file content here
 </content>
-<line_count>total number of lines in the file, including empty lines</line_count>
 </write_to_file>
 
 Example: Requesting to write to frontend-config.json
@@ -35,6 +33,5 @@ Example: Requesting to write to frontend-config.json
   "version": "1.0.0"
 }
 </content>
-<line_count>14</line_count>
 </write_to_file>`
 }

+ 1 - 2
src/core/task/Task.ts

@@ -2192,8 +2192,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
 
 			// Add environment details as its own text block, separate from tool
 			// results.
-			const finalUserContent = [...contentWithoutEnvDetails, { type: "text" as const, text: environmentDetails }]
-
+			let finalUserContent = [...contentWithoutEnvDetails, { type: "text" as const, text: environmentDetails }]
 			// Only add user message to conversation history if:
 			// 1. This is the first attempt (retryAttempt === 0), AND
 			// 2. The original userContent was not empty (empty signals delegation resume where

+ 12 - 52
src/core/tools/WriteToFileTool.ts

@@ -18,12 +18,10 @@ import { EXPERIMENT_IDS, experiments } from "../../shared/experiments"
 import { convertNewFileToUnifiedDiff, computeDiffStats, sanitizeUnifiedDiff } from "../diff/stats"
 import { BaseTool, ToolCallbacks } from "./BaseTool"
 import type { ToolUse } from "../../shared/tools"
-import { resolveToolProtocol } from "../../utils/resolveToolProtocol"
 
 interface WriteToFileParams {
 	path: string
 	content: string
-	line_count: number
 }
 
 export class WriteToFileTool extends BaseTool<"write_to_file"> {
@@ -33,15 +31,13 @@ export class WriteToFileTool extends BaseTool<"write_to_file"> {
 		return {
 			path: params.path || "",
 			content: params.content || "",
-			line_count: parseInt(params.line_count ?? "0", 10),
 		}
 	}
 
 	async execute(params: WriteToFileParams, task: Task, callbacks: ToolCallbacks): Promise<void> {
-		const { pushToolResult, handleError, askApproval, removeClosingTag, toolProtocol } = callbacks
+		const { pushToolResult, handleError, askApproval, removeClosingTag } = callbacks
 		const relPath = params.path
 		let newContent = params.content
-		const predictedLineCount = params.line_count
 
 		if (!relPath) {
 			task.consecutiveMistakeCount++
@@ -63,7 +59,7 @@ export class WriteToFileTool extends BaseTool<"write_to_file"> {
 
 		if (!accessAllowed) {
 			await task.say("rooignore_error", relPath)
-			pushToolResult(formatResponse.rooIgnoreError(relPath, toolProtocol))
+			pushToolResult(formatResponse.rooIgnoreError(relPath))
 			return
 		}
 
@@ -109,38 +105,6 @@ export class WriteToFileTool extends BaseTool<"write_to_file"> {
 		}
 
 		try {
-			if (predictedLineCount === undefined || predictedLineCount === 0) {
-				task.consecutiveMistakeCount++
-				task.recordToolError("write_to_file")
-				task.didToolFailInCurrentTurn = true
-
-				const actualLineCount = newContent.split("\n").length
-				const isNewFile = !fileExists
-				const diffStrategyEnabled = !!task.diffStrategy
-				const modelInfo = task.api.getModel().info
-				const toolProtocol = resolveToolProtocol(task.apiConfiguration, modelInfo)
-
-				await task.say(
-					"error",
-					`Roo tried to use write_to_file${
-						relPath ? ` for '${relPath.toPosix()}'` : ""
-					} but the required parameter 'line_count' was missing or truncated after ${actualLineCount} lines of content were written. Retrying...`,
-				)
-
-				pushToolResult(
-					formatResponse.toolError(
-						formatResponse.lineCountTruncationError(
-							actualLineCount,
-							isNewFile,
-							diffStrategyEnabled,
-							toolProtocol,
-						),
-					),
-				)
-				await task.diffViewProvider.revertChanges()
-				return
-			}
-
 			task.consecutiveMistakeCount = 0
 
 			const provider = task.providerRef.deref()
@@ -161,24 +125,22 @@ export class WriteToFileTool extends BaseTool<"write_to_file"> {
 					task.diffViewProvider.originalContent = ""
 				}
 
-				if (detectCodeOmission(task.diffViewProvider.originalContent || "", newContent, predictedLineCount)) {
+				if (detectCodeOmission(task.diffViewProvider.originalContent || "", newContent)) {
 					if (task.diffStrategy) {
 						pushToolResult(
 							formatResponse.toolError(
-								`Content appears to be truncated (file has ${
-									newContent.split("\n").length
-								} lines but was predicted to have ${predictedLineCount} lines), and found comments indicating omitted code (e.g., '// rest of code unchanged', '/* previous code */'). Please provide the complete file content without any omissions if possible, or otherwise use the 'apply_diff' tool to apply the diff to the original file.`,
+								`Content appears to contain comments indicating omitted code (e.g., '// rest of code unchanged', '/* previous code */'). Please provide the complete file content without any omissions if possible, or otherwise use the 'apply_diff' tool to apply the diff to the original file.`,
 							),
 						)
 						return
 					} else {
 						vscode.window
 							.showWarningMessage(
-								"Potential code truncation detected. cline happens when the AI reaches its max output limit.",
-								"Follow cline guide to fix the issue",
+								"Potential code truncation detected. This happens when the AI reaches its max output limit.",
+								"Follow guide to fix the issue",
 							)
 							.then((selection) => {
-								if (selection === "Follow cline guide to fix the issue") {
+								if (selection === "Follow guide to fix the issue") {
 									vscode.env.openExternal(
 										vscode.Uri.parse(
 											"https://github.com/cline/cline/wiki/Troubleshooting-%E2%80%90-Cline-Deleting-Code-with-%22Rest-of-Code-Here%22-Comments",
@@ -221,26 +183,24 @@ export class WriteToFileTool extends BaseTool<"write_to_file"> {
 				await delay(300)
 				task.diffViewProvider.scrollToFirstDiff()
 
-				if (detectCodeOmission(task.diffViewProvider.originalContent || "", newContent, predictedLineCount)) {
+				if (detectCodeOmission(task.diffViewProvider.originalContent || "", newContent)) {
 					if (task.diffStrategy) {
 						await task.diffViewProvider.revertChanges()
 
 						pushToolResult(
 							formatResponse.toolError(
-								`Content appears to be truncated (file has ${
-									newContent.split("\n").length
-								} lines but was predicted to have ${predictedLineCount} lines), and found comments indicating omitted code (e.g., '// rest of code unchanged', '/* previous code */'). Please provide the complete file content without any omissions if possible, or otherwise use the 'apply_diff' tool to apply the diff to the original file.`,
+								`Content appears to contain comments indicating omitted code (e.g., '// rest of code unchanged', '/* previous code */'). Please provide the complete file content without any omissions if possible, or otherwise use the 'apply_diff' tool to apply the diff to the original file.`,
 							),
 						)
 						return
 					} else {
 						vscode.window
 							.showWarningMessage(
-								"Potential code truncation detected. cline happens when the AI reaches its max output limit.",
-								"Follow cline guide to fix the issue",
+								"Potential code truncation detected. This happens when the AI reaches its max output limit.",
+								"Follow guide to fix the issue",
 							)
 							.then((selection) => {
-								if (selection === "Follow cline guide to fix the issue") {
+								if (selection === "Follow guide to fix the issue") {
 									vscode.env.openExternal(
 										vscode.Uri.parse(
 											"https://github.com/cline/cline/wiki/Troubleshooting-%E2%80%90-Cline-Deleting-Code-with-%22Rest-of-Code-Here%22-Comments",

+ 3 - 6
src/core/tools/__tests__/writeToFileTool.spec.ts

@@ -36,9 +36,6 @@ vi.mock("../../prompts/responses", () => ({
 	formatResponse: {
 		toolError: vi.fn((msg) => `Error: ${msg}`),
 		rooIgnoreError: vi.fn((path) => `Access denied: ${path}`),
-		lineCountTruncationError: vi.fn(
-			(count, isNew, diffEnabled) => `Line count error: ${count}, new: ${isNew}, diff: ${diffEnabled}`,
-		),
 		createPrettyPatch: vi.fn(() => "mock-diff"),
 	},
 }))
@@ -224,7 +221,6 @@ describe("writeToFileTool", () => {
 			params: {
 				path: testFilePath,
 				content: testContent,
-				line_count: "3",
 				...params,
 			},
 			partial: isPartial,
@@ -383,8 +379,9 @@ describe("writeToFileTool", () => {
 			expect(mockedIsPathOutsideWorkspace).toHaveBeenCalled()
 		})
 
-		it("processes files with very large line counts", async () => {
-			await executeWriteFileTool({ line_count: "999999" })
+		it("processes files with large content", async () => {
+			const largeContent = "Line\n".repeat(10000)
+			await executeWriteFileTool({ content: largeContent })
 
 			// Should process normally without issues
 			expect(mockCline.consecutiveMistakeCount).toBe(0)

+ 23 - 85
src/integrations/editor/__tests__/detect-omission.spec.ts

@@ -8,7 +8,8 @@ describe("detectCodeOmission", () => {
   return x + y;
 }`
 
-	const generateLongContent = (commentLine: string, length: number = 90) => {
+	// Generate content with a specified number of lines (100+ lines triggers detection)
+	const generateLongContent = (commentLine: string, length: number = 110) => {
 		return `${commentLine}
 	${Array.from({ length }, (_, i) => `const x${i} = ${i};`).join("\n")}
 	const y = 2;`
@@ -17,126 +18,63 @@ describe("detectCodeOmission", () => {
 	it("should skip comment checks for files under 100 lines", () => {
 		const newContent = `// Lines 1-50 remain unchanged
 const z = 3;`
-		const predictedLineCount = 50
-		expect(detectCodeOmission(originalContent, newContent, predictedLineCount)).toBe(false)
+		expect(detectCodeOmission(originalContent, newContent)).toBe(false)
 	})
 
 	it("should not detect regular comments without omission keywords", () => {
 		const newContent = generateLongContent("// Adding new functionality")
-		const predictedLineCount = 150
-		expect(detectCodeOmission(originalContent, newContent, predictedLineCount)).toBe(false)
+		expect(detectCodeOmission(originalContent, newContent)).toBe(false)
 	})
 
 	it("should not detect when comment is part of original content", () => {
 		const originalWithComment = `// Content remains unchanged
 ${originalContent}`
 		const newContent = generateLongContent("// Content remains unchanged")
-		const predictedLineCount = 150
-		expect(detectCodeOmission(originalWithComment, newContent, predictedLineCount)).toBe(false)
+		expect(detectCodeOmission(originalWithComment, newContent)).toBe(false)
 	})
 
 	it("should not detect code that happens to contain omission keywords", () => {
 		const newContent = generateLongContent(`const remains = 'some value';
 const unchanged = true;`)
-		const predictedLineCount = 150
-		expect(detectCodeOmission(originalContent, newContent, predictedLineCount)).toBe(false)
+		expect(detectCodeOmission(originalContent, newContent)).toBe(false)
 	})
 
-	it("should detect suspicious single-line comment when content is more than 20% shorter", () => {
+	it("should detect suspicious single-line comment for files with 100+ lines", () => {
 		const newContent = generateLongContent("// Previous content remains here\nconst x = 1;")
-		const predictedLineCount = 150
-		expect(detectCodeOmission(originalContent, newContent, predictedLineCount)).toBe(true)
+		expect(detectCodeOmission(originalContent, newContent)).toBe(true)
 	})
 
-	it("should not flag suspicious single-line comment when content is less than 20% shorter", () => {
-		const newContent = generateLongContent("// Previous content remains here", 130)
-		const predictedLineCount = 150
-		expect(detectCodeOmission(originalContent, newContent, predictedLineCount)).toBe(false)
-	})
-
-	it("should detect suspicious Python-style comment when content is more than 20% shorter", () => {
+	it("should detect suspicious Python-style comment for files with 100+ lines", () => {
 		const newContent = generateLongContent("# Previous content remains here\nconst x = 1;")
-		const predictedLineCount = 150
-		expect(detectCodeOmission(originalContent, newContent, predictedLineCount)).toBe(true)
+		expect(detectCodeOmission(originalContent, newContent)).toBe(true)
 	})
 
-	it("should not flag suspicious Python-style comment when content is less than 20% shorter", () => {
-		const newContent = generateLongContent("# Previous content remains here", 130)
-		const predictedLineCount = 150
-		expect(detectCodeOmission(originalContent, newContent, predictedLineCount)).toBe(false)
-	})
-
-	it("should detect suspicious multi-line comment when content is more than 20% shorter", () => {
+	it("should detect suspicious multi-line comment for files with 100+ lines", () => {
 		const newContent = generateLongContent("/* Previous content remains the same */\nconst x = 1;")
-		const predictedLineCount = 150
-		expect(detectCodeOmission(originalContent, newContent, predictedLineCount)).toBe(true)
-	})
-
-	it("should not flag suspicious multi-line comment when content is less than 20% shorter", () => {
-		const newContent = generateLongContent("/* Previous content remains the same */", 130)
-		const predictedLineCount = 150
-		expect(detectCodeOmission(originalContent, newContent, predictedLineCount)).toBe(false)
+		expect(detectCodeOmission(originalContent, newContent)).toBe(true)
 	})
 
-	it("should detect suspicious JSX comment when content is more than 20% shorter", () => {
+	it("should detect suspicious JSX comment for files with 100+ lines", () => {
 		const newContent = generateLongContent("{/* Rest of the code remains the same */}\nconst x = 1;")
-		const predictedLineCount = 150
-		expect(detectCodeOmission(originalContent, newContent, predictedLineCount)).toBe(true)
-	})
-
-	it("should not flag suspicious JSX comment when content is less than 20% shorter", () => {
-		const newContent = generateLongContent("{/* Rest of the code remains the same */}", 130)
-		const predictedLineCount = 150
-		expect(detectCodeOmission(originalContent, newContent, predictedLineCount)).toBe(false)
+		expect(detectCodeOmission(originalContent, newContent)).toBe(true)
 	})
 
-	it("should detect suspicious HTML comment when content is more than 20% shorter", () => {
+	it("should detect suspicious HTML comment for files with 100+ lines", () => {
 		const newContent = generateLongContent("<!-- Existing content unchanged -->\nconst x = 1;")
-		const predictedLineCount = 150
-		expect(detectCodeOmission(originalContent, newContent, predictedLineCount)).toBe(true)
-	})
-
-	it("should not flag suspicious HTML comment when content is less than 20% shorter", () => {
-		const newContent = generateLongContent("<!-- Existing content unchanged -->", 130)
-		const predictedLineCount = 150
-		expect(detectCodeOmission(originalContent, newContent, predictedLineCount)).toBe(false)
+		expect(detectCodeOmission(originalContent, newContent)).toBe(true)
 	})
 
-	it("should detect suspicious square bracket notation when content is more than 20% shorter", () => {
+	it("should detect suspicious square bracket notation for files with 100+ lines", () => {
 		const newContent = generateLongContent(
 			"[Previous content from line 1-305 remains exactly the same]\nconst x = 1;",
 		)
-		const predictedLineCount = 150
-		expect(detectCodeOmission(originalContent, newContent, predictedLineCount)).toBe(true)
+		expect(detectCodeOmission(originalContent, newContent)).toBe(true)
 	})
 
-	it("should not flag suspicious square bracket notation when content is less than 20% shorter", () => {
-		const newContent = generateLongContent("[Previous content from line 1-305 remains exactly the same]", 130)
-		const predictedLineCount = 150
-		expect(detectCodeOmission(originalContent, newContent, predictedLineCount)).toBe(false)
-	})
-
-	it("should not flag content very close to predicted length", () => {
-		const newContent = generateLongContent(
-			`const x = 1;
-const y = 2;
-// This is a legitimate comment that remains here`,
-			130,
-		)
-		const predictedLineCount = 150
-		expect(detectCodeOmission(originalContent, newContent, predictedLineCount)).toBe(false)
-	})
-
-	it("should not flag when content is longer than predicted", () => {
-		const newContent = generateLongContent(
-			`const x = 1;
-const y = 2;
-// Previous content remains here but we added more
-const z = 3;
-const w = 4;`,
-			160,
-		)
-		const predictedLineCount = 150
-		expect(detectCodeOmission(originalContent, newContent, predictedLineCount)).toBe(false)
+	it("should not flag legitimate comments in files with 100+ lines when in original", () => {
+		const originalWithComment = `// This is a legitimate comment that remains here
+${originalContent}`
+		const newContent = generateLongContent("// This is a legitimate comment that remains here")
+		expect(detectCodeOmission(originalWithComment, newContent)).toBe(false)
 	})
 })

+ 7 - 15
src/integrations/editor/detect-omission.ts

@@ -1,23 +1,18 @@
 /**
  * Detects potential AI-generated code omissions in the given file content.
+ * Looks for comments containing omission keywords that weren't in the original file.
  * @param originalFileContent The original content of the file.
  * @param newFileContent The new content of the file to check.
- * @param predictedLineCount The predicted number of lines in the new content.
  * @returns True if a potential omission is detected, false otherwise.
  */
-export function detectCodeOmission(
-	originalFileContent: string,
-	newFileContent: string,
-	predictedLineCount: number,
-): boolean {
-	// Skip all checks if predictedLineCount is less than 100
-	if (!predictedLineCount || predictedLineCount < 100) {
+export function detectCodeOmission(originalFileContent: string, newFileContent: string): boolean {
+	const actualLineCount = newFileContent.split("\n").length
+
+	// Skip checks for small files (less than 100 lines)
+	if (actualLineCount < 100) {
 		return false
 	}
 
-	const actualLineCount = newFileContent.split("\n").length
-	const lengthRatio = actualLineCount / predictedLineCount
-
 	const originalLines = originalFileContent.split("\n")
 	const newLines = newFileContent.split("\n")
 	const omissionKeywords = [
@@ -48,10 +43,7 @@ export function detectCodeOmission(
 			const words = line.toLowerCase().split(/\s+/)
 			if (omissionKeywords.some((keyword) => words.includes(keyword))) {
 				if (!originalLines.includes(line)) {
-					// For files with 100+ lines, only flag if content is more than 20% shorter
-					if (lengthRatio <= 0.8) {
-						return true
-					}
+					return true
 				}
 			}
 		}

+ 2 - 3
src/shared/tools.ts

@@ -39,7 +39,6 @@ export const toolParamNames = [
 	"command",
 	"path",
 	"content",
-	"line_count",
 	"regex",
 	"file_pattern",
 	"recursive",
@@ -106,7 +105,7 @@ export type NativeToolArgs = {
 	switch_mode: { mode_slug: string; reason: string }
 	update_todo_list: { todos: string }
 	use_mcp_tool: { server_name: string; tool_name: string; arguments?: Record<string, unknown> }
-	write_to_file: { path: string; content: string; line_count: number }
+	write_to_file: { path: string; content: string }
 	// Add more tools as they are migrated to native protocol
 }
 
@@ -164,7 +163,7 @@ export interface FetchInstructionsToolUse extends ToolUse<"fetch_instructions">
 
 export interface WriteToFileToolUse extends ToolUse<"write_to_file"> {
 	name: "write_to_file"
-	params: Partial<Pick<Record<ToolParamName, string>, "path" | "content" | "line_count">>
+	params: Partial<Pick<Record<ToolParamName, string>, "path" | "content">>
 }
 
 export interface InsertCodeBlockToolUse extends ToolUse<"insert_content"> {