feat: remove simpleReadFileTool completely (#10254)

- Delete simpleReadFileTool.ts file
- Delete simple-read-file.ts prompt description file
- Delete single-file-read-models.ts types file
- Remove imports and usage from presentAssistantMessage.ts
- Remove imports and usage from prompts/tools/index.ts
- Remove export from packages/types/src/index.ts

This removes all traces of the legacy single-file read tool implementation that was used for specific models. All models now use the standard read_file tool.

Co-authored-by: Roo Code <roomote@roocode.com>
This commit is contained in:
roomote[bot] 2025-12-25 09:57:46 -08:00 committed by GitHub
parent a07d28c7e6
commit a510223a09
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 15 additions and 386 deletions

View file

@ -17,7 +17,6 @@ export * from "./message.js"
export * from "./mode.js"
export * from "./model.js"
export * from "./provider-settings.js"
export * from "./single-file-read-models.js"
export * from "./task.js"
export * from "./todo.js"
export * from "./telemetry.js"

View file

@ -1,14 +0,0 @@
/**
* Configuration for models that should use simplified single-file read_file tool
* These models will use the simpler <read_file><path>...</path></read_file> format
* instead of the more complex multi-file args format
*/
/**
* Check if a model should use single file read format
* @param modelId The model ID to check
* @returns true if the model should use single file reads
*/
export function shouldUseSingleFileRead(modelId: string): boolean {
return modelId.includes("grok-code-fast-1") || modelId.includes("code-supernova")
}

View file

@ -19,8 +19,7 @@ import { Task } from "../task/Task"
import { fetchInstructionsTool } from "../tools/FetchInstructionsTool"
import { listFilesTool } from "../tools/ListFilesTool"
import { readFileTool } from "../tools/ReadFileTool"
import { getSimpleReadFileToolDescription, simpleReadFileTool } from "../tools/simpleReadFileTool"
import { shouldUseSingleFileRead, TOOL_PROTOCOL } from "@roo-code/types"
import { TOOL_PROTOCOL } from "@roo-code/types"
import { writeToFileTool } from "../tools/WriteToFileTool"
import { applyDiffTool } from "../tools/MultiApplyDiffTool"
import { searchAndReplaceTool } from "../tools/SearchAndReplaceTool"
@ -363,18 +362,12 @@ export async function presentAssistantMessage(cline: Task) {
case "execute_command":
return `[${block.name} for '${block.params.command}']`
case "read_file":
// Check if this model should use the simplified description
const modelId = cline.api.getModel().id
if (shouldUseSingleFileRead(modelId)) {
return getSimpleReadFileToolDescription(block.name, block.params)
} else {
// Prefer native typed args when available; fall back to legacy params
// Check if nativeArgs exists (native protocol)
if (block.nativeArgs) {
return readFileTool.getReadFileToolDescription(block.name, block.nativeArgs)
}
return readFileTool.getReadFileToolDescription(block.name, block.params)
// Prefer native typed args when available; fall back to legacy params
// Check if nativeArgs exists (native protocol)
if (block.nativeArgs) {
return readFileTool.getReadFileToolDescription(block.name, block.nativeArgs)
}
return readFileTool.getReadFileToolDescription(block.name, block.params)
case "fetch_instructions":
return `[${block.name} for '${block.params.task}']`
case "write_to_file":
@ -909,29 +902,14 @@ export async function presentAssistantMessage(cline: Task) {
})
break
case "read_file":
// Check if this model should use the simplified single-file read tool
// Only use simplified tool for XML protocol - native protocol works with standard tool
const modelId = cline.api.getModel().id
if (shouldUseSingleFileRead(modelId) && toolProtocol !== TOOL_PROTOCOL.NATIVE) {
await simpleReadFileTool(
cline,
block,
askApproval,
handleError,
pushToolResult,
removeClosingTag,
toolProtocol,
)
} else {
// Type assertion is safe here because we're in the "read_file" case
await readFileTool.handle(cline, block as ToolUse<"read_file">, {
askApproval,
handleError,
pushToolResult,
removeClosingTag,
toolProtocol,
})
}
// Type assertion is safe here because we're in the "read_file" case
await readFileTool.handle(cline, block as ToolUse<"read_file">, {
askApproval,
handleError,
pushToolResult,
removeClosingTag,
toolProtocol,
})
break
case "fetch_instructions":
await fetchInstructionsTool.handle(cline, block as ToolUse<"fetch_instructions">, {

View file

@ -1,5 +1,4 @@
import type { ToolName, ModeConfig } from "@roo-code/types"
import { shouldUseSingleFileRead } from "@roo-code/types"
import { TOOL_GROUPS, ALWAYS_AVAILABLE_TOOLS, DiffStrategy } from "../../../shared/tools"
import { Mode, getModeConfig, getGroupName } from "../../../shared/modes"
@ -12,7 +11,6 @@ import { CodeIndexManager } from "../../../services/code-index/manager"
import { ToolArgs } from "./types"
import { getExecuteCommandDescription } from "./execute-command"
import { getReadFileDescription } from "./read-file"
import { getSimpleReadFileDescription } from "./simple-read-file"
import { getFetchInstructionsDescription } from "./fetch-instructions"
import { getWriteToFileDescription } from "./write-to-file"
import { getSearchFilesDescription } from "./search-files"
@ -32,14 +30,7 @@ import { getGenerateImageDescription } from "./generate-image"
// Map of tool names to their description functions
const toolDescriptionMap: Record<string, (args: ToolArgs) => string | undefined> = {
execute_command: (args) => getExecuteCommandDescription(args),
read_file: (args) => {
// Check if the current model should use the simplified read_file tool
const modelId = args.settings?.modelId
if (modelId && shouldUseSingleFileRead(modelId)) {
return getSimpleReadFileDescription(args)
}
return getReadFileDescription(args)
},
read_file: (args) => getReadFileDescription(args),
fetch_instructions: (args) => getFetchInstructionsDescription(args.settings?.enableMcpServerCreation),
write_to_file: (args) => getWriteToFileDescription(args),
search_files: (args) => getSearchFilesDescription(args),
@ -162,7 +153,6 @@ export function getToolDescriptionsForMode(
export {
getExecuteCommandDescription,
getReadFileDescription,
getSimpleReadFileDescription,
getFetchInstructionsDescription,
getWriteToFileDescription,
getSearchFilesDescription,

View file

@ -1,35 +0,0 @@
import { ToolArgs } from "./types"
/**
* Generate a simplified read_file tool description for models that only support single file reads
* Uses the simpler format: <read_file><path>file/path.ext</path></read_file>
*/
export function getSimpleReadFileDescription(args: ToolArgs): string {
return `## read_file
Description: Request to read the contents of a file. The tool outputs line-numbered content (e.g. "1 | const x = 1") for easy reference when discussing code.
Parameters:
- path: (required) File path (relative to workspace directory ${args.cwd})
Usage:
<read_file>
<path>path/to/file</path>
</read_file>
Examples:
1. Reading a TypeScript file:
<read_file>
<path>src/app.ts</path>
</read_file>
2. Reading a configuration file:
<read_file>
<path>config.json</path>
</read_file>
3. Reading a markdown file:
<read_file>
<path>README.md</path>
</read_file>`
}

View file

@ -1,289 +0,0 @@
import path from "path"
import { isBinaryFile } from "isbinaryfile"
import { Task } from "../task/Task"
import { ClineSayTool } from "../../shared/ExtensionMessage"
import { formatResponse } from "../prompts/responses"
import { t } from "../../i18n"
import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
import { RecordSource } from "../context-tracking/FileContextTrackerTypes"
import { isPathOutsideWorkspace } from "../../utils/pathUtils"
import { getReadablePath } from "../../utils/path"
import { countFileLines } from "../../integrations/misc/line-counter"
import { readLines } from "../../integrations/misc/read-lines"
import { extractTextFromFile, addLineNumbers, getSupportedBinaryFormats } from "../../integrations/misc/extract-text"
import { parseSourceCodeDefinitionsForFile } from "../../services/tree-sitter"
import { ToolProtocol, isNativeProtocol } from "@roo-code/types"
import {
DEFAULT_MAX_IMAGE_FILE_SIZE_MB,
DEFAULT_MAX_TOTAL_IMAGE_SIZE_MB,
isSupportedImageFormat,
validateImageForProcessing,
processImageFile,
} from "./helpers/imageHelpers"
/**
* Simplified read file tool for models that only support single file reads
* Uses the format: <read_file><path>file/path.ext</path></read_file>
*
* This is a streamlined version of readFileTool that:
* - Only accepts a single path parameter
* - Does not support multiple files
* - Does not support line ranges
* - Has simpler XML parsing
*/
export async function simpleReadFileTool(
cline: Task,
block: ToolUse,
askApproval: AskApproval,
handleError: HandleError,
pushToolResult: PushToolResult,
_removeClosingTag: RemoveClosingTag,
toolProtocol?: ToolProtocol,
) {
const filePath: string | undefined = block.params.path
// Check if the current model supports images
const modelInfo = cline.api.getModel().info
const supportsImages = modelInfo.supportsImages ?? false
// Handle partial message
if (block.partial) {
const fullPath = filePath ? path.resolve(cline.cwd, filePath) : ""
const sharedMessageProps: ClineSayTool = {
tool: "readFile",
path: getReadablePath(cline.cwd, filePath || ""),
isOutsideWorkspace: filePath ? isPathOutsideWorkspace(fullPath) : false,
}
const partialMessage = JSON.stringify({
...sharedMessageProps,
content: undefined,
} satisfies ClineSayTool)
await cline.ask("tool", partialMessage, block.partial).catch(() => {})
return
}
// Validate path parameter
if (!filePath) {
cline.consecutiveMistakeCount++
cline.recordToolError("read_file")
const errorMsg = await cline.sayAndCreateMissingParamError("read_file", "path")
pushToolResult(`<file><error>${errorMsg}</error></file>`)
return
}
const relPath = filePath
const fullPath = path.resolve(cline.cwd, relPath)
try {
// Check RooIgnore validation
const accessAllowed = cline.rooIgnoreController?.validateAccess(relPath)
if (!accessAllowed) {
await cline.say("rooignore_error", relPath)
const errorMsg = formatResponse.rooIgnoreError(relPath)
pushToolResult(`<file><path>${relPath}</path><error>${errorMsg}</error></file>`)
return
}
// Get max read file line setting
const { maxReadFileLine = -1 } = (await cline.providerRef.deref()?.getState()) ?? {}
// Create approval message
const isOutsideWorkspace = isPathOutsideWorkspace(fullPath)
let lineSnippet = ""
if (maxReadFileLine === 0) {
lineSnippet = t("tools:readFile.definitionsOnly")
} else if (maxReadFileLine > 0) {
lineSnippet = t("tools:readFile.maxLines", { max: maxReadFileLine })
}
const completeMessage = JSON.stringify({
tool: "readFile",
path: getReadablePath(cline.cwd, relPath),
isOutsideWorkspace,
content: fullPath,
reason: lineSnippet,
} satisfies ClineSayTool)
const { response, text, images } = await cline.ask("tool", completeMessage, false)
if (response !== "yesButtonClicked") {
// Handle denial
if (text) {
await cline.say("user_feedback", text, images)
}
cline.didRejectTool = true
const statusMessage = text ? formatResponse.toolDeniedWithFeedback(text) : formatResponse.toolDenied()
pushToolResult(`${statusMessage}\n<file><path>${relPath}</path><status>Denied by user</status></file>`)
return
}
// Handle approval with feedback
if (text) {
await cline.say("user_feedback", text, images)
}
// Process the file
const [totalLines, isBinary] = await Promise.all([countFileLines(fullPath), isBinaryFile(fullPath)])
// Handle binary files
if (isBinary) {
const fileExtension = path.extname(relPath).toLowerCase()
const supportedBinaryFormats = getSupportedBinaryFormats()
// Check if it's a supported image format
if (isSupportedImageFormat(fileExtension)) {
try {
const {
maxImageFileSize = DEFAULT_MAX_IMAGE_FILE_SIZE_MB,
maxTotalImageSize = DEFAULT_MAX_TOTAL_IMAGE_SIZE_MB,
} = (await cline.providerRef.deref()?.getState()) ?? {}
// Validate image for processing
const validationResult = await validateImageForProcessing(
fullPath,
supportsImages,
maxImageFileSize,
maxTotalImageSize,
0, // No cumulative memory for single file
)
if (!validationResult.isValid) {
await cline.fileContextTracker.trackFileContext(relPath, "read_tool" as RecordSource)
pushToolResult(
`<file><path>${relPath}</path>\n<notice>${validationResult.notice}</notice>\n</file>`,
)
return
}
// Process the image
const imageResult = await processImageFile(fullPath)
await cline.fileContextTracker.trackFileContext(relPath, "read_tool" as RecordSource)
// Return result with image data
const result = formatResponse.toolResult(
`<file><path>${relPath}</path>\n<notice>${imageResult.notice}</notice>\n</file>`,
supportsImages ? [imageResult.dataUrl] : undefined,
)
if (typeof result === "string") {
pushToolResult(result)
} else {
pushToolResult(result)
}
return
} catch (error) {
const errorMsg = error instanceof Error ? error.message : String(error)
pushToolResult(
`<file><path>${relPath}</path><error>Error reading image file: ${errorMsg}</error></file>`,
)
await handleError(
`reading image file ${relPath}`,
error instanceof Error ? error : new Error(errorMsg),
)
return
}
}
// Check if it's a supported binary format that can be processed
if (supportedBinaryFormats && supportedBinaryFormats.includes(fileExtension)) {
// For supported binary formats (.pdf, .docx, .ipynb), continue to extractTextFromFile
// Fall through to the normal extractTextFromFile processing below
} else {
// Handle unknown binary format
const fileFormat = fileExtension.slice(1) || "bin"
pushToolResult(
`<file><path>${relPath}</path>\n<binary_file format="${fileFormat}">Binary file - content not displayed</binary_file>\n</file>`,
)
return
}
}
// Handle definitions-only mode
if (maxReadFileLine === 0) {
try {
const defResult = await parseSourceCodeDefinitionsForFile(fullPath, cline.rooIgnoreController)
if (defResult) {
let xmlInfo = `<notice>Showing only definitions. Use standard read_file if you need to read actual content</notice>\n`
pushToolResult(
`<file><path>${relPath}</path>\n<list_code_definition_names>${defResult}</list_code_definition_names>\n${xmlInfo}</file>`,
)
}
} catch (error) {
if (error instanceof Error && error.message.startsWith("Unsupported language:")) {
console.warn(`[simple_read_file] Warning: ${error.message}`)
} else {
console.error(
`[simple_read_file] Unhandled error: ${error instanceof Error ? error.message : String(error)}`,
)
}
}
return
}
// Handle files exceeding line threshold
if (maxReadFileLine > 0 && totalLines > maxReadFileLine) {
const content = addLineNumbers(await readLines(fullPath, maxReadFileLine - 1, 0))
const lineRangeAttr = ` lines="1-${maxReadFileLine}"`
let xmlInfo = `<content${lineRangeAttr}>\n${content}</content>\n`
try {
const defResult = await parseSourceCodeDefinitionsForFile(fullPath, cline.rooIgnoreController)
if (defResult) {
xmlInfo += `<list_code_definition_names>${defResult}</list_code_definition_names>\n`
}
xmlInfo += `<notice>Showing only ${maxReadFileLine} of ${totalLines} total lines. File is too large for complete display</notice>\n`
pushToolResult(`<file><path>${relPath}</path>\n${xmlInfo}</file>`)
} catch (error) {
if (error instanceof Error && error.message.startsWith("Unsupported language:")) {
console.warn(`[simple_read_file] Warning: ${error.message}`)
} else {
console.error(
`[simple_read_file] Unhandled error: ${error instanceof Error ? error.message : String(error)}`,
)
}
}
return
}
// Handle normal file read
const content = await extractTextFromFile(fullPath)
const lineRangeAttr = ` lines="1-${totalLines}"`
let xmlInfo = totalLines > 0 ? `<content${lineRangeAttr}>\n${content}</content>\n` : `<content/>`
if (totalLines === 0) {
xmlInfo += `<notice>File is empty</notice>\n`
}
// Track file read
await cline.fileContextTracker.trackFileContext(relPath, "read_tool" as RecordSource)
// Return the result
if (text) {
const statusMessage = formatResponse.toolApprovedWithFeedback(text)
pushToolResult(`${statusMessage}\n<file><path>${relPath}</path>\n${xmlInfo}</file>`)
} else {
pushToolResult(`<file><path>${relPath}</path>\n${xmlInfo}</file>`)
}
} catch (error) {
const errorMsg = error instanceof Error ? error.message : String(error)
pushToolResult(`<file><path>${relPath}</path><error>Error reading file: ${errorMsg}</error></file>`)
await handleError(`reading file ${relPath}`, error instanceof Error ? error : new Error(errorMsg))
}
}
/**
* Get description for the simple read file tool
* @param blockName The name of the tool block
* @param blockParams The parameters passed to the tool
* @returns A description string for the tool use
*/
export function getSimpleReadFileToolDescription(blockName: string, blockParams: any): string {
if (blockParams.path) {
return `[${blockName} for '${blockParams.path}']`
} else {
return `[${blockName} with missing path]`
}
}