feat(read_file): enhance file reading capabilities with multi-file support and improved parameter handling (#2886)

* feat(read_file): enhance file reading capabilities with multi-file support and improved parameter handling

fix(read_file): change return to continue on approval rejection in readFileTool

Enhance readFileTool with improved error handling and validation

- Introduced a FileEntry interface for better type management.
- Added validation for start_line and end_line to ensure proper ranges.
- Implemented RooIgnore validation before processing files.
- Enhanced error handling with dedicated functions for file and global errors.
- Streamlined file reading logic to handle binary files, definitions-only mode, and line thresholds more effectively.
- Improved user feedback for empty files and read limits.

chore: update Jest snapshot for system prompt tool usage

Refactor read-file tool to support XML input format and multiple line ranges

- Updated the `getReadFileDescription` function to reflect new XML structure for file reading requests.
- Modified `readFileTool` to parse XML input, allowing multiple line ranges for each file.
- Removed old parsing logic that handled line ranges as separate parameters.
- Implemented validation for line ranges and ensured proper error handling for file access.
- Adjusted approval messaging to accommodate new line range format.
- Enhanced error handling to provide consistent feedback for file read errors.

update from KJ7LNW  comment

feat: add maxConcurrentFileReads setting to enhance read_file tool performance

feat: enhance readFileTool with XML parsing and file processing state tracking

feat: enhance readFileTool to include user feedback handling and processing state tracking

chore: clean up read_file tool documentation by removing extra newlines

feat: update read_file tool tests to handle user feedback and approval states

feat: add tests for feedback message formatting and XML special character handling in read_file tool

Implement code changes to enhance functionality and improve performance

feat: increase max concurrent file reads and adjust slider range in settings

feat: increase default max concurrent file reads from 5 to 15 across settings and context management

fix(read_file): enhance legacy path handling and remove duplicate parameters

feat(read_file): enhance file description handling and add support for multiple files in messages

done poc for new ux

idea 1

* fix the test

* fix: normalize locale file formatting to use tabs

- Applied prettier formatting to all locale JSON files
- Fixed mixed indentation (spaces/tabs) to use consistent tabs
- Aligns with project's prettier configuration

* fix(settings): improve checkbox handling and slider configuration in ConcurrentFileReadsExperiment

* fix(read_file): enhance description to include partial reads support

* fix(read_file): update description for partial reads and improve example clarity

* fix: suggestions

* fix: translations

* test: update system prompt snapshots for multi-file read tool

* fix: remove batch permission question from readFile tool translations

---------

Co-authored-by: Daniel Riccio <ricciodaniel98@gmail.com>
This commit is contained in:
Sam Hoang Van 2025-05-31 04:09:42 +07:00 committed by GitHub
parent 177e7a8eb9
commit 9ba0cd5c7a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
85 changed files with 2780 additions and 789 deletions

View file

@ -6,7 +6,7 @@ import type { Keys, Equals, AssertEqual } from "./type-fu.js"
* ExperimentId
*/
export const experimentIds = ["powerSteering"] as const
export const experimentIds = ["powerSteering", "concurrentFileReads"] as const
export const experimentIdsSchema = z.enum(experimentIds)
@ -18,6 +18,7 @@ export type ExperimentId = z.infer<typeof experimentIdsSchema>
export const experimentsSchema = z.object({
powerSteering: z.boolean(),
concurrentFileReads: z.boolean(),
})
export type Experiments = z.infer<typeof experimentsSchema>

View file

@ -48,6 +48,7 @@ export const globalSettingsSchema = z.object({
allowedMaxRequests: z.number().nullish(),
autoCondenseContext: z.boolean().optional(),
autoCondenseContextPercent: z.number().optional(),
maxConcurrentFileReads: z.number().optional(),
browserToolEnabled: z.boolean().optional(),
browserViewportSize: z.string().optional(),
@ -134,6 +135,7 @@ export const GLOBAL_SETTINGS_KEYS = keysOf<GlobalSettings>()([
"allowedMaxRequests",
"autoCondenseContext",
"autoCondenseContextPercent",
"maxConcurrentFileReads",
"browserToolEnabled",
"browserViewportSize",

View file

@ -9,7 +9,7 @@ import type { ToolParamName, ToolResponse } from "../../shared/tools"
import { fetchInstructionsTool } from "../tools/fetchInstructionsTool"
import { listFilesTool } from "../tools/listFilesTool"
import { readFileTool } from "../tools/readFileTool"
import { getReadFileToolDescription, readFileTool } from "../tools/readFileTool"
import { writeToFileTool } from "../tools/writeToFileTool"
import { applyDiffTool } from "../tools/applyDiffTool"
import { insertContentTool } from "../tools/insertContentTool"
@ -153,7 +153,7 @@ export async function presentAssistantMessage(cline: Task) {
case "execute_command":
return `[${block.name} for '${block.params.command}']`
case "read_file":
return `[${block.name} for '${block.params.path}']`
return getReadFileToolDescription(block.name, block.params)
case "fetch_instructions":
return `[${block.name} for '${block.params.task}']`
case "write_to_file":

File diff suppressed because it is too large Load diff

View file

@ -17,9 +17,10 @@ Tool uses are formatted using XML-style tags. The tool name itself becomes the X
For example, to use the read_file tool:
<read_file>
<path>src/main.js</path>
</read_file>
<new_task>
<mode>code</mode>
<message>Implement a new feature for the application.</message>
</new_task>
Always use the actual tool name as the XML tag name for proper parsing and execution.`
}

View file

@ -43,6 +43,7 @@ async function generatePrompt(
language?: string,
rooIgnoreInstructions?: string,
partialReadsEnabled?: boolean,
settings?: Record<string, any>,
): Promise<string> {
if (!context) {
throw new Error("Extension context is required for generating system prompt")
@ -81,6 +82,7 @@ ${getToolDescriptionsForMode(
customModeConfigs,
experiments,
partialReadsEnabled,
settings,
)}
${getToolUseGuidelinesSection()}
@ -119,6 +121,7 @@ export const SYSTEM_PROMPT = async (
language?: string,
rooIgnoreInstructions?: string,
partialReadsEnabled?: boolean,
settings?: Record<string, any>,
): Promise<string> => {
if (!context) {
throw new Error("Extension context is required for generating system prompt")
@ -191,5 +194,6 @@ ${customInstructions}`
language,
rooIgnoreInstructions,
partialReadsEnabled,
settings,
)
}

View file

@ -58,6 +58,7 @@ export function getToolDescriptionsForMode(
customModes?: ModeConfig[],
experiments?: Record<string, boolean>,
partialReadsEnabled?: boolean,
settings?: Record<string, any>,
): string {
const config = getModeConfig(mode, customModes)
const args: ToolArgs = {
@ -67,6 +68,7 @@ export function getToolDescriptionsForMode(
browserViewportSize,
mcpHub,
partialReadsEnabled,
settings,
}
const tools = new Set<string>()

View file

@ -1,74 +1,85 @@
import { ToolArgs } from "./types"
export function getReadFileDescription(args: ToolArgs): string {
// Base description without partial read instructions
let description = `## read_file
Description: Request to read the contents of a file at the specified path. Use this when you need to examine the contents of an existing file you do not know the contents of, for example to analyze code, review text files, or extract information from configuration files. The output includes line numbers prefixed to each line (e.g. "1 | const x = 1"), making it easier to reference specific lines when creating diffs or discussing code.`
const maxConcurrentReads = args.settings?.maxConcurrentFileReads ?? 15
const isMultipleReadsEnabled = maxConcurrentReads > 1
// Add partial read instructions only when partial reads are active
if (args.partialReadsEnabled) {
description += ` By specifying start_line and end_line parameters, you can efficiently read specific portions of large files without loading the entire file into memory.`
}
return `## read_file
Description: Request to read the contents of ${isMultipleReadsEnabled ? "one or more files" : "a file"}. The tool outputs line-numbered content (e.g. "1 | const x = 1") for easy reference when creating diffs or discussing code.${args.partialReadsEnabled ? " Use line ranges to efficiently read specific portions of large files." : ""} Supports text extraction from PDF and DOCX files, but may not handle other binary files properly.
description += ` Automatically extracts raw text from PDF and DOCX files. May not be suitable for other types of binary files, as it returns the raw content as a string.
${isMultipleReadsEnabled ? `**IMPORTANT: You can read a maximum of ${maxConcurrentReads} files in a single request.** If you need to read more files, use multiple sequential read_file requests.` : "**IMPORTANT: Multiple file reads are currently disabled. You can only read one file at a time.**"}
${args.partialReadsEnabled ? `By specifying line ranges, you can efficiently read specific portions of large files without loading the entire file into memory.` : ""}
Parameters:
- path: (required) The path of the file to read (relative to the current workspace directory ${args.cwd})`
- args: Contains one or more file elements, where each file contains:
- path: (required) File path (relative to workspace directory ${args.cwd})
${args.partialReadsEnabled ? `- line_range: (optional) One or more line range elements in format "start-end" (1-based, inclusive)` : ""}
// Add start_line and end_line parameters only when partial reads are active
if (args.partialReadsEnabled) {
description += `
- start_line: (optional) The starting line number to read from (1-based). If not provided, it starts from the beginning of the file.
- end_line: (optional) The ending line number to read to (1-based, inclusive). If not provided, it reads to the end of the file.`
}
description += `
Usage:
<read_file>
<path>File path here</path>`
// Add start_line and end_line in usage only when partial reads are active
if (args.partialReadsEnabled) {
description += `
<start_line>Starting line number (optional)</start_line>
<end_line>Ending line number (optional)</end_line>`
}
description += `
<args>
<file>
<path>path/to/file</path>
${args.partialReadsEnabled ? `<line_range>start-end</line_range>` : ""}
</file>
</args>
</read_file>
Examples:
1. Reading an entire file:
1. Reading a single file:
<read_file>
<path>frontend-config.json</path>
<args>
<file>
<path>src/app.ts</path>
${args.partialReadsEnabled ? `<line_range>1-1000</line_range>` : ""}
</file>
</args>
</read_file>
${isMultipleReadsEnabled ? `2. Reading multiple files (within the ${maxConcurrentReads}-file limit):` : ""}${
isMultipleReadsEnabled
? `
<read_file>
<args>
<file>
<path>src/app.ts</path>
${
args.partialReadsEnabled
? `<line_range>1-50</line_range>
<line_range>100-150</line_range>`
: ""
}
</file>
<file>
<path>src/utils.ts</path>
${args.partialReadsEnabled ? `<line_range>10-20</line_range>` : ""}
</file>
</args>
</read_file>`
// Add partial read examples only when partial reads are active
if (args.partialReadsEnabled) {
description += `
2. Reading the first 1000 lines of a large log file:
<read_file>
<path>logs/application.log</path>
<end_line>1000</end_line>
</read_file>
3. Reading lines 500-1000 of a CSV file:
<read_file>
<path>data/large-dataset.csv</path>
<start_line>500</start_line>
<end_line>1000</end_line>
</read_file>
4. Reading a specific function in a source file:
<read_file>
<path>src/app.ts</path>
<start_line>46</start_line>
<end_line>68</end_line>
</read_file>
Note: When both start_line and end_line are provided, this tool efficiently streams only the requested lines, making it suitable for processing large files like logs, CSV files, and other large datasets without memory issues.`
: ""
}
return description
${isMultipleReadsEnabled ? "3. " : "2. "}Reading an entire file:
<read_file>
<args>
<file>
<path>config.json</path>
</file>
</args>
</read_file>
IMPORTANT: You MUST use this Efficient Reading Strategy:
- ${isMultipleReadsEnabled ? `You MUST read all related files and implementations together in a single operation (up to ${maxConcurrentReads} files at once)` : "You MUST read files one at a time, as multiple file reads are currently disabled"}
- You MUST obtain all necessary context before proceeding with changes
${
args.partialReadsEnabled
? `- You MUST use line ranges to read specific portions of large files, rather than reading entire files when not needed
- You MUST combine adjacent line ranges (<10 lines apart)
- You MUST use multiple ranges for content separated by >10 lines
- You MUST include sufficient line context for planned modifications while keeping ranges minimal
`
: ""
}
${isMultipleReadsEnabled ? `- When you need to read more than ${maxConcurrentReads} files, prioritize the most critical files first, then use subsequent read_file requests for additional files` : ""}`
}

View file

@ -9,4 +9,5 @@ export type ToolArgs = {
mcpHub?: McpHub
toolOptions?: any
partialReadsEnabled?: boolean
settings?: Record<string, any>
}

View file

@ -1513,6 +1513,7 @@ export class Task extends EventEmitter<ClineEvents> {
enableMcpServerCreation,
browserToolEnabled,
language,
maxConcurrentFileReads,
maxReadFileLine,
} = state ?? {}
@ -1540,6 +1541,9 @@ export class Task extends EventEmitter<ClineEvents> {
language,
rooIgnoreInstructions,
maxReadFileLine !== -1,
{
maxConcurrentFileReads,
},
)
})()
}

File diff suppressed because it is too large Load diff

View file

@ -13,6 +13,62 @@ import { countFileLines } from "../../integrations/misc/line-counter"
import { readLines } from "../../integrations/misc/read-lines"
import { extractTextFromFile, addLineNumbers } from "../../integrations/misc/extract-text"
import { parseSourceCodeDefinitionsForFile } from "../../services/tree-sitter"
import { parseXml } from "../../utils/xml"
export function getReadFileToolDescription(blockName: string, blockParams: any): string {
// Handle both single path and multiple files via args
if (blockParams.args) {
try {
const parsed = parseXml(blockParams.args) as any
const files = Array.isArray(parsed.file) ? parsed.file : [parsed.file].filter(Boolean)
const paths = files.map((f: any) => f?.path).filter(Boolean) as string[]
if (paths.length === 0) {
return `[${blockName} with no valid paths]`
} else if (paths.length === 1) {
// Modified part for single file
return `[${blockName} for '${paths[0]}'. Reading multiple files at once is more efficient for the LLM. If other files are relevant to your current task, please read them simultaneously.]`
} else if (paths.length <= 3) {
const pathList = paths.map((p) => `'${p}'`).join(", ")
return `[${blockName} for ${pathList}]`
} else {
return `[${blockName} for ${paths.length} files]`
}
} catch (error) {
console.error("Failed to parse read_file args XML for description:", error)
return `[${blockName} with unparseable args]`
}
} else if (blockParams.path) {
// Fallback for legacy single-path usage
// Modified part for single file (legacy)
return `[${blockName} for '${blockParams.path}'. Reading multiple files at once is more efficient for the LLM. If other files are relevant to your current task, please read them simultaneously.]`
} else {
return `[${blockName} with missing path/args]`
}
}
// Types
interface LineRange {
start: number
end: number
}
interface FileEntry {
path?: string
lineRanges?: LineRange[]
}
// New interface to track file processing state
interface FileResult {
path: string
status: "approved" | "denied" | "blocked" | "error" | "pending"
content?: string
error?: string
notice?: string
lineRanges?: LineRange[]
xmlContent?: string // Final XML content for this file
feedbackText?: string // User feedback text from approval/denial
feedbackImages?: any[] // User feedback images from approval/denial
}
export async function readFileTool(
cline: Task,
@ -20,241 +76,532 @@ export async function readFileTool(
askApproval: AskApproval,
handleError: HandleError,
pushToolResult: PushToolResult,
removeClosingTag: RemoveClosingTag,
_removeClosingTag: RemoveClosingTag,
) {
const relPath: string | undefined = block.params.path
const startLineStr: string | undefined = block.params.start_line
const endLineStr: string | undefined = block.params.end_line
const argsXmlTag: string | undefined = block.params.args
const legacyPath: string | undefined = block.params.path
const legacyStartLineStr: string | undefined = block.params.start_line
const legacyEndLineStr: string | undefined = block.params.end_line
// Get the full path and determine if it's outside the workspace
const fullPath = relPath ? path.resolve(cline.cwd, removeClosingTag("path", relPath)) : ""
const isOutsideWorkspace = isPathOutsideWorkspace(fullPath)
// Handle partial message first
if (block.partial) {
let filePath = ""
// Prioritize args for partial, then legacy path
if (argsXmlTag) {
const match = argsXmlTag.match(/<file>.*?<path>([^<]+)<\/path>/s)
if (match) filePath = match[1]
}
if (!filePath && legacyPath) {
// If args didn't yield a path, try legacy
filePath = legacyPath
}
const sharedMessageProps: ClineSayTool = {
tool: "readFile",
path: getReadablePath(cline.cwd, removeClosingTag("path", relPath)),
isOutsideWorkspace,
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
}
try {
if (block.partial) {
const partialMessage = JSON.stringify({ ...sharedMessageProps, content: undefined } satisfies ClineSayTool)
await cline.ask("tool", partialMessage, block.partial).catch(() => {})
const fileEntries: FileEntry[] = []
if (argsXmlTag) {
// Parse file entries from XML (new multi-file format)
try {
const parsed = parseXml(argsXmlTag) as any
const files = Array.isArray(parsed.file) ? parsed.file : [parsed.file].filter(Boolean)
for (const file of files) {
if (!file.path) continue // Skip if no path in a file entry
const fileEntry: FileEntry = {
path: file.path,
lineRanges: [],
}
if (file.line_range) {
const ranges = Array.isArray(file.line_range) ? file.line_range : [file.line_range]
for (const range of ranges) {
const match = String(range).match(/(\d+)-(\d+)/) // Ensure range is treated as string
if (match) {
const [, start, end] = match.map(Number)
if (!isNaN(start) && !isNaN(end)) {
fileEntry.lineRanges?.push({ start, end })
}
}
}
}
fileEntries.push(fileEntry)
}
} catch (error) {
const errorMessage = `Failed to parse read_file XML args: ${error instanceof Error ? error.message : String(error)}`
await handleError("parsing read_file args", new Error(errorMessage))
pushToolResult(`<files><error>${errorMessage}</error></files>`)
return
} else {
if (!relPath) {
cline.consecutiveMistakeCount++
cline.recordToolError("read_file")
const errorMsg = await cline.sayAndCreateMissingParamError("read_file", "path")
pushToolResult(`<file><path></path><error>${errorMsg}</error></file>`)
return
}
} else if (legacyPath) {
// Handle legacy single file path as a fallback
console.warn("[readFileTool] Received legacy 'path' parameter. Consider updating to use 'args' structure.")
const fileEntry: FileEntry = {
path: legacyPath,
lineRanges: [],
}
if (legacyStartLineStr && legacyEndLineStr) {
const start = parseInt(legacyStartLineStr, 10)
const end = parseInt(legacyEndLineStr, 10)
if (!isNaN(start) && !isNaN(end) && start > 0 && end > 0) {
fileEntry.lineRanges?.push({ start, end })
} else {
console.warn(
`[readFileTool] Invalid legacy line range for ${legacyPath}: start='${legacyStartLineStr}', end='${legacyEndLineStr}'`,
)
}
}
fileEntries.push(fileEntry)
}
// If, after trying both new and legacy, no valid file entries are found.
if (fileEntries.length === 0) {
cline.consecutiveMistakeCount++
cline.recordToolError("read_file")
const errorMsg = await cline.sayAndCreateMissingParamError("read_file", "args (containing valid file paths)")
pushToolResult(`<files><error>${errorMsg}</error></files>`)
return
}
// Create an array to track the state of each file
const fileResults: FileResult[] = fileEntries.map((entry) => ({
path: entry.path || "",
status: "pending",
lineRanges: entry.lineRanges,
}))
// Function to update file result status
const updateFileResult = (path: string, updates: Partial<FileResult>) => {
const index = fileResults.findIndex((result) => result.path === path)
if (index !== -1) {
fileResults[index] = { ...fileResults[index], ...updates }
}
}
try {
// First validate all files and prepare for batch approval
const filesToApprove: FileResult[] = []
for (let i = 0; i < fileResults.length; i++) {
const fileResult = fileResults[i]
const relPath = fileResult.path
const fullPath = path.resolve(cline.cwd, relPath)
// Validate line ranges first
if (fileResult.lineRanges) {
let hasRangeError = false
for (const range of fileResult.lineRanges) {
if (range.start > range.end) {
const errorMsg = "Invalid line range: end line cannot be less than start line"
updateFileResult(relPath, {
status: "blocked",
error: errorMsg,
xmlContent: `<file><path>${relPath}</path><error>Error reading file: ${errorMsg}</error></file>`,
})
await handleError(`reading file ${relPath}`, new Error(errorMsg))
hasRangeError = true
break
}
if (isNaN(range.start) || isNaN(range.end)) {
const errorMsg = "Invalid line range values"
updateFileResult(relPath, {
status: "blocked",
error: errorMsg,
xmlContent: `<file><path>${relPath}</path><error>Error reading file: ${errorMsg}</error></file>`,
})
await handleError(`reading file ${relPath}`, new Error(errorMsg))
hasRangeError = true
break
}
}
if (hasRangeError) continue
}
// Then check RooIgnore validation
if (fileResult.status === "pending") {
const accessAllowed = cline.rooIgnoreController?.validateAccess(relPath)
if (!accessAllowed) {
await cline.say("rooignore_error", relPath)
const errorMsg = formatResponse.rooIgnoreError(relPath)
updateFileResult(relPath, {
status: "blocked",
error: errorMsg,
xmlContent: `<file><path>${relPath}</path><error>${errorMsg}</error></file>`,
})
continue
}
// Add to files that need approval
filesToApprove.push(fileResult)
}
}
// Handle batch approval if there are multiple files to approve
if (filesToApprove.length > 1) {
const { maxReadFileLine = -1 } = (await cline.providerRef.deref()?.getState()) ?? {}
const isFullRead = maxReadFileLine === -1
// Check if we're doing a line range read
let isRangeRead = false
let startLine: number | undefined = undefined
let endLine: number | undefined = undefined
// Prepare batch file data
const batchFiles = filesToApprove.map((fileResult) => {
const relPath = fileResult.path
const fullPath = path.resolve(cline.cwd, relPath)
const isOutsideWorkspace = isPathOutsideWorkspace(fullPath)
// Check if we have either range parameter and we're not doing a full read
if (!isFullRead && (startLineStr || endLineStr)) {
isRangeRead = true
}
// Parse start_line if provided
if (startLineStr) {
startLine = parseInt(startLineStr)
if (isNaN(startLine)) {
// Invalid start_line
cline.consecutiveMistakeCount++
cline.recordToolError("read_file")
await cline.say("error", `Failed to parse start_line: ${startLineStr}`)
pushToolResult(`<file><path>${relPath}</path><error>Invalid start_line value</error></file>`)
return
// Create line snippet for this file
let lineSnippet = ""
if (fileResult.lineRanges && fileResult.lineRanges.length > 0) {
const ranges = fileResult.lineRanges.map((range) =>
t("tools:readFile.linesRange", { start: range.start, end: range.end }),
)
lineSnippet = ranges.join(", ")
} else if (maxReadFileLine === 0) {
lineSnippet = t("tools:readFile.definitionsOnly")
} else if (maxReadFileLine > 0) {
lineSnippet = t("tools:readFile.maxLines", { max: maxReadFileLine })
}
startLine -= 1 // Convert to 0-based index
}
const readablePath = getReadablePath(cline.cwd, relPath)
const key = `${readablePath}${lineSnippet ? ` (${lineSnippet})` : ""}`
// Parse end_line if provided
if (endLineStr) {
endLine = parseInt(endLineStr)
if (isNaN(endLine)) {
// Invalid end_line
cline.consecutiveMistakeCount++
cline.recordToolError("read_file")
await cline.say("error", `Failed to parse end_line: ${endLineStr}`)
pushToolResult(`<file><path>${relPath}</path><error>Invalid end_line value</error></file>`)
return
return {
path: readablePath,
lineSnippet,
isOutsideWorkspace,
key,
content: fullPath, // Include full path for content
}
})
// Convert to 0-based index
endLine -= 1
const completeMessage = JSON.stringify({
tool: "readFile",
batchFiles,
} satisfies ClineSayTool)
const { response, text, images } = await cline.ask("tool", completeMessage, false)
// Process batch response
if (response === "yesButtonClicked") {
// Approve all files
if (text) {
await cline.say("user_feedback", text, images)
}
filesToApprove.forEach((fileResult) => {
updateFileResult(fileResult.path, {
status: "approved",
feedbackText: text,
feedbackImages: images,
})
})
} else if (response === "noButtonClicked") {
// Deny all files
if (text) {
await cline.say("user_feedback", text, images)
}
cline.didRejectTool = true
filesToApprove.forEach((fileResult) => {
updateFileResult(fileResult.path, {
status: "denied",
xmlContent: `<file><path>${fileResult.path}</path><status>Denied by user</status></file>`,
feedbackText: text,
feedbackImages: images,
})
})
} else {
// Handle individual permissions from objectResponse
// if (text) {
// await cline.say("user_feedback", text, images)
// }
try {
const individualPermissions = JSON.parse(text || "{}")
let hasAnyDenial = false
batchFiles.forEach((batchFile, index) => {
const fileResult = filesToApprove[index]
const approved = individualPermissions[batchFile.key] === true
if (approved) {
updateFileResult(fileResult.path, {
status: "approved",
})
} else {
hasAnyDenial = true
updateFileResult(fileResult.path, {
status: "denied",
xmlContent: `<file><path>${fileResult.path}</path><status>Denied by user</status></file>`,
})
}
})
if (hasAnyDenial) {
cline.didRejectTool = true
}
} catch (error) {
// Fallback: if JSON parsing fails, deny all files
console.error("Failed to parse individual permissions:", error)
cline.didRejectTool = true
filesToApprove.forEach((fileResult) => {
updateFileResult(fileResult.path, {
status: "denied",
xmlContent: `<file><path>${fileResult.path}</path><status>Denied by user</status></file>`,
})
})
}
}
} else if (filesToApprove.length === 1) {
// Handle single file approval (existing logic)
const fileResult = filesToApprove[0]
const relPath = fileResult.path
const fullPath = path.resolve(cline.cwd, relPath)
const isOutsideWorkspace = isPathOutsideWorkspace(fullPath)
const { maxReadFileLine = -1 } = (await cline.providerRef.deref()?.getState()) ?? {}
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
}
// Create line snippet description for approval message
// Create line snippet for approval message
let lineSnippet = ""
if (isFullRead) {
// No snippet for full read
} else if (startLine !== undefined && endLine !== undefined) {
lineSnippet = t("tools:readFile.linesRange", { start: startLine + 1, end: endLine + 1 })
} else if (startLine !== undefined) {
lineSnippet = t("tools:readFile.linesFromToEnd", { start: startLine + 1 })
} else if (endLine !== undefined) {
lineSnippet = t("tools:readFile.linesFromStartTo", { end: endLine + 1 })
if (fileResult.lineRanges && fileResult.lineRanges.length > 0) {
const ranges = fileResult.lineRanges.map((range) =>
t("tools:readFile.linesRange", { start: range.start, end: range.end }),
)
lineSnippet = ranges.join(", ")
} else if (maxReadFileLine === 0) {
lineSnippet = t("tools:readFile.definitionsOnly")
} else if (maxReadFileLine > 0) {
lineSnippet = t("tools:readFile.maxLines", { max: maxReadFileLine })
}
cline.consecutiveMistakeCount = 0
const absolutePath = path.resolve(cline.cwd, relPath)
const completeMessage = JSON.stringify({
...sharedMessageProps,
content: absolutePath,
tool: "readFile",
path: getReadablePath(cline.cwd, relPath),
isOutsideWorkspace,
content: fullPath,
reason: lineSnippet,
} satisfies ClineSayTool)
const didApprove = await askApproval("tool", completeMessage)
const { response, text, images } = await cline.ask("tool", completeMessage, false)
if (!didApprove) {
return
}
// Count total lines in the file
let totalLines = 0
try {
totalLines = await countFileLines(absolutePath)
} catch (error) {
console.error(`Error counting lines in file ${absolutePath}:`, error)
}
// now execute the tool like normal
let content: string
let isFileTruncated = false
let sourceCodeDef = ""
const isBinary = await isBinaryFile(absolutePath).catch(() => false)
if (isRangeRead) {
if (startLine === undefined) {
content = addLineNumbers(await readLines(absolutePath, endLine, startLine))
} else {
content = addLineNumbers(await readLines(absolutePath, endLine, startLine), startLine + 1)
if (response !== "yesButtonClicked") {
// Handle both messageResponse and noButtonClicked with text
if (text) {
await cline.say("user_feedback", text, images)
}
} else if (!isBinary && maxReadFileLine >= 0 && totalLines > maxReadFileLine) {
// If file is too large, only read the first maxReadFileLine lines
isFileTruncated = true
cline.didRejectTool = true
const res = await Promise.all([
maxReadFileLine > 0 ? readLines(absolutePath, maxReadFileLine - 1, 0) : "",
(async () => {
try {
return await parseSourceCodeDefinitionsForFile(absolutePath, cline.rooIgnoreController)
} catch (error) {
if (error instanceof Error && error.message.startsWith("Unsupported language:")) {
console.warn(`[read_file] Warning: ${error.message}`)
return undefined
} else {
console.error(
`[read_file] Unhandled error: ${error instanceof Error ? error.message : String(error)}`,
)
return undefined
}
}
})(),
])
content = res[0].length > 0 ? addLineNumbers(res[0]) : ""
const result = res[1]
if (result) {
sourceCodeDef = `${result}`
}
updateFileResult(relPath, {
status: "denied",
xmlContent: `<file><path>${relPath}</path><status>Denied by user</status></file>`,
feedbackText: text,
feedbackImages: images,
})
} else {
// Read entire file
content = await extractTextFromFile(absolutePath)
}
// Create variables to store XML components
let xmlInfo = ""
let contentTag = ""
// Add truncation notice if applicable
if (isFileTruncated) {
xmlInfo += `<notice>Showing only ${maxReadFileLine} of ${totalLines} total lines. Use start_line and end_line if you need to read more</notice>\n`
// Add source code definitions if available
if (sourceCodeDef) {
xmlInfo += `<list_code_definition_names>${sourceCodeDef}</list_code_definition_names>\n`
}
}
// Empty files (zero lines)
if (content === "" && totalLines === 0) {
// Always add self-closing content tag and notice for empty files
contentTag = `<content/>`
xmlInfo += `<notice>File is empty</notice>\n`
}
// Range reads should always show content regardless of maxReadFileLine
else if (isRangeRead) {
// Create content tag with line range information
let lineRangeAttr = ""
const displayStartLine = startLine !== undefined ? startLine + 1 : 1
const displayEndLine = endLine !== undefined ? endLine + 1 : totalLines
lineRangeAttr = ` lines="${displayStartLine}-${displayEndLine}"`
// Maintain exact format expected by tests
contentTag = `<content${lineRangeAttr}>\n${content}</content>\n`
}
// maxReadFileLine=0 for non-range reads
else if (maxReadFileLine === 0) {
// Skip content tag for maxReadFileLine=0 (definitions only mode)
contentTag = ""
}
// Normal case: non-empty files with content (non-range reads)
else {
// For non-range reads, always show line range
let lines = totalLines
if (maxReadFileLine >= 0 && totalLines > maxReadFileLine) {
lines = maxReadFileLine
// Handle yesButtonClicked with text
if (text) {
await cline.say("user_feedback", text, images)
}
const lineRangeAttr = ` lines="1-${lines}"`
updateFileResult(relPath, {
status: "approved",
feedbackText: text,
feedbackImages: images,
})
}
}
// Maintain exact format expected by tests
contentTag = `<content${lineRangeAttr}>\n${content}</content>\n`
// Then process only approved files
for (const fileResult of fileResults) {
// Skip files that weren't approved
if (fileResult.status !== "approved") {
continue
}
// Track file read operation
if (relPath) {
const relPath = fileResult.path
const fullPath = path.resolve(cline.cwd, relPath)
const { maxReadFileLine = 500 } = (await cline.providerRef.deref()?.getState()) ?? {}
// Process approved files
try {
const [totalLines, isBinary] = await Promise.all([countFileLines(fullPath), isBinaryFile(fullPath)])
// Handle binary files
if (isBinary) {
updateFileResult(relPath, {
notice: "Binary file",
xmlContent: `<file><path>${relPath}</path>\n<notice>Binary file</notice>\n</file>`,
})
continue
}
// Handle range reads (bypass maxReadFileLine)
if (fileResult.lineRanges && fileResult.lineRanges.length > 0) {
const rangeResults: string[] = []
for (const range of fileResult.lineRanges) {
const content = addLineNumbers(
await readLines(fullPath, range.end - 1, range.start - 1),
range.start,
)
const lineRangeAttr = ` lines="${range.start}-${range.end}"`
rangeResults.push(`<content${lineRangeAttr}>\n${content}</content>`)
}
updateFileResult(relPath, {
xmlContent: `<file><path>${relPath}</path>\n${rangeResults.join("\n")}\n</file>`,
})
continue
}
// Handle definitions-only mode
if (maxReadFileLine === 0) {
try {
const defResult = await parseSourceCodeDefinitionsForFile(fullPath, cline.rooIgnoreController)
if (defResult) {
let xmlInfo = `<notice>Showing only ${maxReadFileLine} of ${totalLines} total lines. Use line_range if you need to read more lines</notice>\n`
updateFileResult(relPath, {
xmlContent: `<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(`[read_file] Warning: ${error.message}`)
} else {
console.error(
`[read_file] Unhandled error: ${error instanceof Error ? error.message : String(error)}`,
)
}
}
continue
}
// 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. Use line_range if you need to read more lines</notice>\n`
updateFileResult(relPath, {
xmlContent: `<file><path>${relPath}</path>\n${xmlInfo}</file>`,
})
} catch (error) {
if (error instanceof Error && error.message.startsWith("Unsupported language:")) {
console.warn(`[read_file] Warning: ${error.message}`)
} else {
console.error(
`[read_file] Unhandled error: ${error instanceof Error ? error.message : String(error)}`,
)
}
}
continue
}
// 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)
}
// Format the result into the required XML structure
const xmlResult = `<file><path>${relPath}</path>\n${contentTag}${xmlInfo}</file>`
pushToolResult(xmlResult)
updateFileResult(relPath, {
xmlContent: `<file><path>${relPath}</path>\n${xmlInfo}</file>`,
})
} catch (error) {
const errorMsg = error instanceof Error ? error.message : String(error)
updateFileResult(relPath, {
status: "error",
error: `Error reading file: ${errorMsg}`,
xmlContent: `<file><path>${relPath}</path><error>Error reading file: ${errorMsg}</error></file>`,
})
await handleError(`reading file ${relPath}`, error instanceof Error ? error : new Error(errorMsg))
}
}
// Generate final XML result from all file results
const xmlResults = fileResults.filter((result) => result.xmlContent).map((result) => result.xmlContent)
const filesXml = `<files>\n${xmlResults.join("\n")}\n</files>`
// Process all feedback in a unified way without branching
let statusMessage = ""
let feedbackImages: any[] = []
// Handle denial with feedback (highest priority)
const deniedWithFeedback = fileResults.find((result) => result.status === "denied" && result.feedbackText)
if (deniedWithFeedback && deniedWithFeedback.feedbackText) {
statusMessage = formatResponse.toolDeniedWithFeedback(deniedWithFeedback.feedbackText)
feedbackImages = deniedWithFeedback.feedbackImages || []
}
// Handle generic denial
else if (cline.didRejectTool) {
statusMessage = formatResponse.toolDenied()
}
// Handle approval with feedback
else {
const approvedWithFeedback = fileResults.find(
(result) => result.status === "approved" && result.feedbackText,
)
if (approvedWithFeedback && approvedWithFeedback.feedbackText) {
statusMessage = formatResponse.toolApprovedWithFeedback(approvedWithFeedback.feedbackText)
feedbackImages = approvedWithFeedback.feedbackImages || []
}
}
// Push the result with appropriate formatting
if (statusMessage) {
const result = formatResponse.toolResult(statusMessage, feedbackImages)
// Handle different return types from toolResult
if (typeof result === "string") {
pushToolResult(`${result}\n${filesXml}`)
} else {
// For block-based results, we need to convert the filesXml to a text block and append it
const textBlock = { type: "text" as const, text: filesXml }
pushToolResult([...result, textBlock])
}
} else {
// No status message, just push the files XML
pushToolResult(filesXml)
}
} catch (error) {
// Handle all errors using per-file format for consistency
const relPath = fileEntries[0]?.path || "unknown"
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", error)
// If we have file results, update the first one with the error
if (fileResults.length > 0) {
updateFileResult(relPath, {
status: "error",
error: `Error reading file: ${errorMsg}`,
xmlContent: `<file><path>${relPath}</path><error>Error reading file: ${errorMsg}</error></file>`,
})
}
await handleError(`reading file ${relPath}`, error instanceof Error ? error : new Error(errorMsg))
// Generate final XML result from all file results
const xmlResults = fileResults.filter((result) => result.xmlContent).map((result) => result.xmlContent)
pushToolResult(`<files>\n${xmlResults.join("\n")}\n</files>`)
}
}

View file

@ -37,7 +37,7 @@ import { supportPrompt } from "../../shared/support-prompt"
import { GlobalFileNames } from "../../shared/globalFileNames"
import { ExtensionMessage } from "../../shared/ExtensionMessage"
import { Mode, defaultModeSlug } from "../../shared/modes"
import { experimentDefault } from "../../shared/experiments"
import { experimentDefault, experiments, EXPERIMENT_IDS } from "../../shared/experiments"
import { formatLanguage } from "../../shared/language"
import { Terminal } from "../../integrations/terminal/Terminal"
import { downloadTask } from "../../integrations/misc/export-markdown"
@ -1299,6 +1299,7 @@ export class ClineProvider
historyPreviewCollapsed,
cloudUserInfo,
organizationAllowList,
maxConcurrentFileReads,
condensingApiConfigId,
customCondensingPrompt,
codebaseIndexConfig,
@ -1389,6 +1390,7 @@ export class ClineProvider
language: language ?? formatLanguage(vscode.env.language),
renderContext: this.renderContext,
maxReadFileLine: maxReadFileLine ?? -1,
maxConcurrentFileReads: maxConcurrentFileReads ?? 15,
settingsImportedAt: this.settingsImportedAt,
terminalCompressProgressBar: terminalCompressProgressBar ?? true,
hasSystemPromptOverride,
@ -1516,6 +1518,10 @@ export class ClineProvider
telemetrySetting: stateValues.telemetrySetting || "unset",
showRooIgnoredFiles: stateValues.showRooIgnoredFiles ?? true,
maxReadFileLine: stateValues.maxReadFileLine ?? -1,
maxConcurrentFileReads: experiments.isEnabled(
stateValues.experiments ?? experimentDefault,
EXPERIMENT_IDS.CONCURRENT_FILE_READS
) ? (stateValues.maxConcurrentFileReads ?? 15) : 1,
historyPreviewCollapsed: stateValues.historyPreviewCollapsed ?? false,
cloudUserInfo,
organizationAllowList,

View file

@ -21,6 +21,7 @@ export const generateSystemPrompt = async (provider: ClineProvider, message: Web
browserToolEnabled,
language,
maxReadFileLine,
maxConcurrentFileReads,
} = await provider.getState()
const diffStrategy = new MultiSearchReplaceDiffStrategy(fuzzyMatchThreshold)
@ -69,6 +70,9 @@ export const generateSystemPrompt = async (provider: ClineProvider, message: Web
language,
rooIgnoreInstructions,
maxReadFileLine !== -1,
{
maxConcurrentFileReads,
},
)
return systemPrompt

View file

@ -959,6 +959,11 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We
await updateGlobalState("maxReadFileLine", message.value)
await provider.postStateToWebview()
break
case "maxConcurrentFileReads":
const valueToSave = message.value // Capture the value intended for saving
await updateGlobalState("maxConcurrentFileReads", valueToSave)
await provider.postStateToWebview()
break
case "setHistoryPreviewCollapsed": // Add the new case handler
await updateGlobalState("historyPreviewCollapsed", message.bool ?? false)
// No need to call postStateToWebview here as the UI already updated optimistically
@ -1429,4 +1434,4 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We
break
}
}
}
}

View file

@ -1,8 +1,6 @@
{
"readFile": {
"linesRange": " (línies {{start}}-{{end}})",
"linesFromToEnd": " (línies {{start}}-final)",
"linesFromStartTo": " (línies 1-{{end}})",
"definitionsOnly": " (només definicions)",
"maxLines": " (màxim {{max}} línies)"
},

View file

@ -1,8 +1,6 @@
{
"readFile": {
"linesRange": " (Zeilen {{start}}-{{end}})",
"linesFromToEnd": " (Zeilen {{start}}-Ende)",
"linesFromStartTo": " (Zeilen 1-{{end}})",
"definitionsOnly": " (nur Definitionen)",
"maxLines": " (maximal {{max}} Zeilen)"
},

View file

@ -1,8 +1,6 @@
{
"readFile": {
"linesRange": " (lines {{start}}-{{end}})",
"linesFromToEnd": " (lines {{start}}-end)",
"linesFromStartTo": " (lines 1-{{end}})",
"definitionsOnly": " (definitions only)",
"maxLines": " (max {{max}} lines)"
},

View file

@ -1,8 +1,6 @@
{
"readFile": {
"linesRange": " (líneas {{start}}-{{end}})",
"linesFromToEnd": " (líneas {{start}}-final)",
"linesFromStartTo": " (líneas 1-{{end}})",
"definitionsOnly": " (solo definiciones)",
"maxLines": " (máximo {{max}} líneas)"
},

View file

@ -1,8 +1,6 @@
{
"readFile": {
"linesRange": " (lignes {{start}}-{{end}})",
"linesFromToEnd": " (lignes {{start}}-fin)",
"linesFromStartTo": " (lignes 1-{{end}})",
"definitionsOnly": " (définitions uniquement)",
"maxLines": " (max {{max}} lignes)"
},

View file

@ -1,8 +1,6 @@
{
"readFile": {
"linesRange": " (पंक्तियाँ {{start}}-{{end}})",
"linesFromToEnd": " (पंक्तियाँ {{start}}-अंत)",
"linesFromStartTo": " (पंक्तियाँ 1-{{end}})",
"definitionsOnly": " (केवल परिभाषाएँ)",
"maxLines": " (अधिकतम {{max}} पंक्तियाँ)"
},

View file

@ -1,8 +1,6 @@
{
"readFile": {
"linesRange": " (righe {{start}}-{{end}})",
"linesFromToEnd": " (righe {{start}}-fine)",
"linesFromStartTo": " (righe 1-{{end}})",
"definitionsOnly": " (solo definizioni)",
"maxLines": " (max {{max}} righe)"
},

View file

@ -1,8 +1,6 @@
{
"readFile": {
"linesRange": " ({{start}}-{{end}}行目)",
"linesFromToEnd": " ({{start}}行目-最後まで)",
"linesFromStartTo": " (1-{{end}}行目)",
"definitionsOnly": " (定義のみ)",
"maxLines": " (最大{{max}}行)"
},

View file

@ -1,8 +1,6 @@
{
"readFile": {
"linesRange": " ({{start}}-{{end}}행)",
"linesFromToEnd": " ({{start}}행-끝)",
"linesFromStartTo": " (1-{{end}}행)",
"definitionsOnly": " (정의만)",
"maxLines": " (최대 {{max}}행)"
},

View file

@ -1,8 +1,6 @@
{
"readFile": {
"linesRange": " (regels {{start}}-{{end}})",
"linesFromToEnd": " (regels {{start}}-einde)",
"linesFromStartTo": " (regels 1-{{end}})",
"definitionsOnly": " (alleen definities)",
"maxLines": " (max {{max}} regels)"
},

View file

@ -1,8 +1,6 @@
{
"readFile": {
"linesRange": " (linie {{start}}-{{end}})",
"linesFromToEnd": " (linie {{start}}-koniec)",
"linesFromStartTo": " (linie 1-{{end}})",
"definitionsOnly": " (tylko definicje)",
"maxLines": " (maks. {{max}} linii)"
},

View file

@ -1,8 +1,6 @@
{
"readFile": {
"linesRange": " (linhas {{start}}-{{end}})",
"linesFromToEnd": " (linhas {{start}}-fim)",
"linesFromStartTo": " (linhas 1-{{end}})",
"definitionsOnly": " (apenas definições)",
"maxLines": " (máx. {{max}} linhas)"
},

View file

@ -1,8 +1,6 @@
{
"readFile": {
"linesRange": " (строки {{start}}-{{end}})",
"linesFromToEnd": " (строки {{start}}-конец)",
"linesFromStartTo": " (строки 1-{{end}})",
"definitionsOnly": " (только определения)",
"maxLines": " (макс. {{max}} строк)"
},

View file

@ -1,8 +1,6 @@
{
"readFile": {
"linesRange": " (satır {{start}}-{{end}})",
"linesFromToEnd": " (satır {{start}}-son)",
"linesFromStartTo": " (satır 1-{{end}})",
"definitionsOnly": " (sadece tanımlar)",
"maxLines": " (maks. {{max}} satır)"
},

View file

@ -1,8 +1,6 @@
{
"readFile": {
"linesRange": " (dòng {{start}}-{{end}})",
"linesFromToEnd": " (dòng {{start}}-cuối)",
"linesFromStartTo": " (dòng 1-{{end}})",
"definitionsOnly": " (chỉ định nghĩa)",
"maxLines": " (tối đa {{max}} dòng)"
},

View file

@ -1,8 +1,6 @@
{
"readFile": {
"linesRange": " (第 {{start}}-{{end}} 行)",
"linesFromToEnd": " (第 {{start}} 行至末尾)",
"linesFromStartTo": " (第 1-{{end}} 行)",
"definitionsOnly": " (仅定义)",
"maxLines": " (最多 {{max}} 行)"
},

View file

@ -1,8 +1,6 @@
{
"readFile": {
"linesRange": " (第 {{start}}-{{end}} 行)",
"linesFromToEnd": " (第 {{start}} 行至結尾)",
"linesFromStartTo": " (第 1-{{end}} 行)",
"definitionsOnly": " (僅定義)",
"maxLines": " (最多 {{max}} 行)"
},

View file

@ -137,4 +137,4 @@ describe("read_file tool with maxReadFileLine setting", () => {
expect(readLines).toHaveBeenCalledWith(filePath, maxReadFileLine - 1, 0)
expect(addLineNumbers).toHaveBeenCalled()
})
})
})

View file

@ -153,6 +153,7 @@ export type ExtensionState = Pick<
// | "maxWorkspaceFiles" // Optional in GlobalSettings, required here.
// | "showRooIgnoredFiles" // Optional in GlobalSettings, required here.
// | "maxReadFileLine" // Optional in GlobalSettings, required here.
| "maxConcurrentFileReads" // Optional in GlobalSettings, required here.
| "terminalOutputLineLimit"
| "terminalShellIntegrationTimeout"
| "terminalShellIntegrationDisabled"
@ -249,6 +250,7 @@ export interface ClineSayTool {
mode?: string
reason?: string
isOutsideWorkspace?: boolean
additionalFileCount?: number // Number of additional files in the same read_file request
search?: string
replace?: string
useRegex?: boolean
@ -257,6 +259,13 @@ export interface ClineSayTool {
endLine?: number
lineNumber?: number
query?: string
batchFiles?: Array<{
path: string
lineSnippet: string
isOutsideWorkspace?: boolean
key: string
}>
question?: string
}
// Must keep in sync with system prompt.

View file

@ -4,7 +4,7 @@ import type { ProviderSettings, PromptComponent, ModeConfig } from "@roo-code/ty
import { Mode } from "./modes"
export type ClineAskResponse = "yesButtonClicked" | "noButtonClicked" | "messageResponse"
export type ClineAskResponse = "yesButtonClicked" | "noButtonClicked" | "messageResponse" | "objectResponse"
export type PromptMode = Mode | "enhance"
@ -133,6 +133,7 @@ export interface WebviewMessage {
| "remoteBrowserEnabled"
| "language"
| "maxReadFileLine"
| "maxConcurrentFileReads"
| "searchFiles"
| "toggleApiConfigPin"
| "setHistoryPreviewCollapsed"

View file

@ -18,6 +18,8 @@ describe("experiments", () => {
it("returns false when POWER_STEERING experiment is not enabled", () => {
const experiments: Record<ExperimentId, boolean> = {
powerSteering: false,
concurrentFileReads: false,
}
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(false)
})
@ -25,6 +27,7 @@ describe("experiments", () => {
it("returns true when experiment POWER_STEERING is enabled", () => {
const experiments: Record<ExperimentId, boolean> = {
powerSteering: true,
concurrentFileReads: false,
}
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(true)
})
@ -32,6 +35,7 @@ describe("experiments", () => {
it("returns false when experiment is not present", () => {
const experiments: Record<ExperimentId, boolean> = {
powerSteering: false,
concurrentFileReads: false,
}
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(false)
})

View file

@ -2,6 +2,7 @@ import type { AssertEqual, Equals, Keys, Values, ExperimentId } from "@roo-code/
export const EXPERIMENT_IDS = {
POWER_STEERING: "powerSteering",
CONCURRENT_FILE_READS: "concurrentFileReads",
} as const satisfies Record<string, ExperimentId>
type _AssertExperimentIds = AssertEqual<Equals<ExperimentId, Values<typeof EXPERIMENT_IDS>>>
@ -14,6 +15,7 @@ interface ExperimentConfig {
export const experimentConfigsMap: Record<ExperimentKey, ExperimentConfig> = {
POWER_STEERING: { enabled: false },
CONCURRENT_FILE_READS: { enabled: false },
}
export const experimentDefault = Object.fromEntries(

View file

@ -45,11 +45,8 @@ export const toolParamNames = [
"question",
"result",
"diff",
"start_line",
"end_line",
"mode_slug",
"reason",
"operations",
"line",
"mode",
"message",
@ -61,6 +58,7 @@ export const toolParamNames = [
"replace",
"use_regex",
"ignore_case",
"args",
"start_line",
"end_line",
"query",
@ -84,7 +82,7 @@ export interface ExecuteCommandToolUse extends ToolUse {
export interface ReadFileToolUse extends ToolUse {
name: "read_file"
params: Partial<Pick<Record<ToolParamName, string>, "path" | "start_line" | "end_line">>
params: Partial<Pick<Record<ToolParamName, string>, "args" | "path" | "start_line" | "end_line">>
}
export interface FetchInstructionsToolUse extends ToolUse {

View file

@ -20,10 +20,12 @@ module.exports = {
"^src/i18n/TranslationContext$": "<rootDir>/src/__mocks__/i18n/TranslationContext.tsx",
"^\\.\\./TranslationContext$": "<rootDir>/src/__mocks__/i18n/TranslationContext.tsx",
"^\\./TranslationContext$": "<rootDir>/src/__mocks__/i18n/TranslationContext.tsx",
"^@src/utils/highlighter$": "<rootDir>/src/__mocks__/utils/highlighter.ts",
"^shiki$": "<rootDir>/src/__mocks__/shiki.ts",
},
reporters: [["jest-simple-dot-reporter", {}]],
transformIgnorePatterns: [
"/node_modules/(?!(rehype-highlight|react-remark|unist-util-visit|unist-util-find-after|vfile|unified|bail|is-plain-obj|trough|vfile-message|unist-util-stringify-position|mdast-util-from-markdown|mdast-util-to-string|micromark|decode-named-character-reference|character-entities|markdown-table|zwitch|longest-streak|escape-string-regexp|unist-util-is|hast-util-to-text|@vscode/webview-ui-toolkit|@microsoft/fast-react-wrapper|@microsoft/fast-element|@microsoft/fast-foundation|@microsoft/fast-web-utilities|exenv-es6|vscrui)/)",
"/node_modules/(?!(shiki|rehype-highlight|react-remark|unist-util-visit|unist-util-find-after|vfile|unified|bail|is-plain-obj|trough|vfile-message|unist-util-stringify-position|mdast-util-from-markdown|mdast-util-to-string|micromark|decode-named-character-reference|character-entities|markdown-table|zwitch|longest-streak|escape-string-regexp|unist-util-is|hast-util-to-text|@vscode/webview-ui-toolkit|@microsoft/fast-react-wrapper|@microsoft/fast-element|@microsoft/fast-foundation|@microsoft/fast-web-utilities|exenv-es6|vscrui)/)",
],
roots: ["<rootDir>"],
moduleDirectories: ["node_modules", "src"],

View file

@ -0,0 +1,32 @@
export const bundledLanguages = {
javascript: jest.fn(),
typescript: jest.fn(),
python: jest.fn(),
html: jest.fn(),
css: jest.fn(),
json: jest.fn(),
// Add more as needed
}
export const bundledThemes = {}
export type BundledTheme = string
export type BundledLanguage = string
export type Highlighter = any
export type ShikiTransformer = any
export const createHighlighter = jest.fn(() =>
Promise.resolve({
codeToHtml: jest.fn((code: string) => `<pre><code>${code}</code></pre>`),
getLoadedThemes: jest.fn(() => []),
loadTheme: jest.fn(),
}),
)
export const codeToHast = jest.fn()
export const codeToHtml = jest.fn((code: string) => `<pre><code>${code}</code></pre>`)
export const codeToTokens = jest.fn()
export const codeToTokensBase = jest.fn()
export const codeToTokensWithThemes = jest.fn()
export const getLastGrammarState = jest.fn()
export const getSingletonHighlighter = jest.fn()

View file

@ -0,0 +1,24 @@
export type ExtendedLanguage = string
export const highlighter = {
codeToHtml: jest.fn((code: string) => `<pre><code>${code}</code></pre>`),
getLoadedThemes: jest.fn(() => []),
loadTheme: jest.fn(),
}
export const getHighlighter = jest.fn(() => Promise.resolve(highlighter))
export const isLanguageLoaded = jest.fn(() => true)
export const normalizeLanguage = jest.fn((lang: string): ExtendedLanguage => lang)
// Mock bundledLanguages
export const bundledLanguages = {
javascript: jest.fn(),
typescript: jest.fn(),
python: jest.fn(),
html: jest.fn(),
css: jest.fn(),
json: jest.fn(),
// Add more as needed
}

View file

@ -0,0 +1,54 @@
import { memo } from "react"
import { ToolUseBlock, ToolUseBlockHeader } from "../common/ToolUseBlock"
import { vscode } from "@src/utils/vscode"
import { removeLeadingNonAlphanumeric } from "@src/utils/removeLeadingNonAlphanumeric"
interface FilePermissionItem {
path: string
lineSnippet?: string
isOutsideWorkspace?: boolean
key: string
content?: string // full path
}
interface BatchFilePermissionProps {
files: FilePermissionItem[]
onPermissionResponse?: (response: { [key: string]: boolean }) => void
ts: number
}
export const BatchFilePermission = memo(({ files = [], onPermissionResponse, ts }: BatchFilePermissionProps) => {
// Don't render if there are no files or no response handler
if (!files?.length || !onPermissionResponse) {
return null
}
return (
<div className="pt-[5px]">
{/* Individual files */}
<div className="flex flex-col gap-0 border border-border rounded-md p-1">
{files.map((file) => {
return (
<div key={`${file.path}-${ts}`} className="flex items-center gap-2">
<ToolUseBlock className="flex-1">
<ToolUseBlockHeader
onClick={() => vscode.postMessage({ type: "openFile", text: file.content })}>
{file.path?.startsWith(".") && <span>.</span>}
<span className="whitespace-nowrap overflow-hidden text-ellipsis text-left mr-2 rtl">
{removeLeadingNonAlphanumeric(file.path ?? "") + "\u200E"}
{file.lineSnippet && ` ${file.lineSnippet}`}
</span>
<div className="flex-grow"></div>
<span className="codicon codicon-link-external text-[13.5px] my-[1px]" />
</ToolUseBlockHeader>
</ToolUseBlock>
</div>
)
})}
</div>
</div>
)
})
BatchFilePermission.displayName = "BatchFilePermission"

View file

@ -30,6 +30,7 @@ import McpToolRow from "../mcp/McpToolRow"
import { Mention } from "./Mention"
import { CheckpointSaved } from "./checkpoints/CheckpointSaved"
import { FollowUpSuggest } from "./FollowUpSuggest"
import { BatchFilePermission } from "./BatchFilePermission"
import { ProgressIndicator } from "./ProgressIndicator"
import { Markdown } from "./Markdown"
import { CommandExecution } from "./CommandExecution"
@ -47,6 +48,7 @@ interface ChatRowProps {
onToggleExpand: (ts: number) => void
onHeightChange: (isTaller: boolean) => void
onSuggestionClick?: (answer: string, event?: React.MouseEvent) => void
onBatchFileResponse?: (response: { [key: string]: boolean }) => void
}
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
@ -95,6 +97,7 @@ export const ChatRowContent = ({
isStreaming,
onToggleExpand,
onSuggestionClick,
onBatchFileResponse,
}: ChatRowContentProps) => {
const { t } = useTranslation()
const { mcpServers, alwaysAllowMcp, currentCheckpoint } = useExtensionState()
@ -399,6 +402,30 @@ export const ChatRowContent = ({
</>
)
case "readFile":
// Check if this is a batch file permission request
const isBatchRequest = message.type === "ask" && tool.batchFiles && Array.isArray(tool.batchFiles)
if (isBatchRequest) {
return (
<>
<div style={headerStyle}>
{toolIcon("files")}
<span style={{ fontWeight: "bold" }}>
{t("chat:fileOperations.wantsToReadMultiple")}
</span>
</div>
<BatchFilePermission
files={tool.batchFiles || []}
onPermissionResponse={(response) => {
onBatchFileResponse?.(response)
}}
ts={message?.ts}
/>
</>
)
}
// Regular single file read request
return (
<>
<div style={headerStyle}>
@ -407,7 +434,11 @@ export const ChatRowContent = ({
{message.type === "ask"
? tool.isOutsideWorkspace
? t("chat:fileOperations.wantsToReadOutsideWorkspace")
: t("chat:fileOperations.wantsToRead")
: tool.additionalFileCount && tool.additionalFileCount > 0
? t("chat:fileOperations.wantsToReadAndXMore", {
count: tool.additionalFileCount,
})
: t("chat:fileOperations.wantsToRead")
: t("chat:fileOperations.didRead")}
</span>
</div>

View file

@ -266,6 +266,15 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
setPrimaryButtonText(t("chat:completeSubtaskAndReturn"))
setSecondaryButtonText(undefined)
break
case "readFile":
if (tool.batchFiles && Array.isArray(tool.batchFiles)) {
setPrimaryButtonText(t("chat:read-batch.approve.title"))
setSecondaryButtonText(t("chat:read-batch.deny.title"))
} else {
setPrimaryButtonText(t("chat:approve.title"))
setSecondaryButtonText(t("chat:reject.title"))
}
break
default:
setPrimaryButtonText(t("chat:approve.title"))
setSecondaryButtonText(t("chat:reject.title"))
@ -1155,6 +1164,11 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
[handleSendMessage, setInputValue], // setInputValue is stable, handleSendMessage depends on clineAsk
)
const handleBatchFileResponse = useCallback((response: { [key: string]: boolean }) => {
// Handle batch file response, e.g., for file uploads
vscode.postMessage({ type: "askResponse", askResponse: "objectResponse", text: JSON.stringify(response) })
}, [])
const itemContent = useCallback(
(index: number, messageOrGroup: ClineMessage | ClineMessage[]) => {
// browser session group
@ -1189,19 +1203,19 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
onHeightChange={handleRowHeightChange}
isStreaming={isStreaming}
onSuggestionClick={handleSuggestionClickInRow} // This was already stabilized
onBatchFileResponse={handleBatchFileResponse}
/>
)
},
[
// Original broader dependencies
expandedRows,
groupedMessages,
toggleRowExpansion,
modifiedMessages,
groupedMessages.length,
handleRowHeightChange,
isStreaming,
toggleRowExpansion,
handleSuggestionClickInRow,
setExpandedRows, // For the inline onToggleExpand in BrowserSessionRow
handleBatchFileResponse,
],
)

View file

@ -0,0 +1,178 @@
import React from "react"
import { render, screen, fireEvent } from "@testing-library/react"
import { BatchFilePermission } from "../BatchFilePermission"
import { TranslationProvider } from "@/i18n/__mocks__/TranslationContext"
const mockVscodePostMessage = jest.fn()
// Mock vscode API
jest.mock("@src/utils/vscode", () => ({
vscode: {
postMessage: (...args: any[]) => mockVscodePostMessage(...args),
},
}))
describe("BatchFilePermission", () => {
const mockOnPermissionResponse = jest.fn()
const mockFiles = [
{
key: "file1",
path: "src/components/Button.tsx",
content: "src/components/Button.tsx",
lineSnippet: "export const Button = () => {",
isOutsideWorkspace: false,
},
{
key: "file2",
path: "../outside/config.json",
content: "/absolute/path/to/outside/config.json",
lineSnippet: '{ "apiKey": "..." }',
isOutsideWorkspace: true,
},
{
key: "file3",
path: "tests/Button.test.tsx",
content: "tests/Button.test.tsx",
lineSnippet: "describe('Button', () => {",
isOutsideWorkspace: false,
},
]
beforeEach(() => {
jest.clearAllMocks()
})
it("renders file list correctly", () => {
render(
<TranslationProvider>
<BatchFilePermission
files={mockFiles}
onPermissionResponse={mockOnPermissionResponse}
ts={Date.now()}
/>
</TranslationProvider>,
)
// Check that all files are rendered
expect(screen.getByText(/Button\.tsx/)).toBeInTheDocument()
expect(screen.getByText(/config\.json/)).toBeInTheDocument()
expect(screen.getByText(/Button\.test\.tsx/)).toBeInTheDocument()
// Check that line snippets are shown
expect(screen.getByText(/export const Button = \(\) => \{/)).toBeInTheDocument()
expect(screen.getByText(/\{ "apiKey": "\.\.\." \}/)).toBeInTheDocument()
expect(screen.getByText(/describe\('Button', \(\) => \{/)).toBeInTheDocument()
})
it("renders nothing when files array is empty", () => {
const { container } = render(
<TranslationProvider>
<BatchFilePermission files={[]} onPermissionResponse={mockOnPermissionResponse} ts={Date.now()} />
</TranslationProvider>,
)
expect(container.firstChild).toBeNull()
})
it("renders nothing when onPermissionResponse is not provided", () => {
const { container } = render(
<TranslationProvider>
<BatchFilePermission files={mockFiles} onPermissionResponse={undefined} ts={Date.now()} />
</TranslationProvider>,
)
expect(container.firstChild).toBeNull()
})
it("opens file when clicking on file item", () => {
render(
<TranslationProvider>
<BatchFilePermission
files={mockFiles}
onPermissionResponse={mockOnPermissionResponse}
ts={Date.now()}
/>
</TranslationProvider>,
)
// The onClick is on the ToolUseBlockHeader which contains the file path text
// Find the header that contains our file path and click it
const filePathElement = screen.getByText(/Button\.tsx.*export const Button/)
// The ToolUseBlockHeader is the parent div with the flex class
const headerElement = filePathElement.closest(".flex.items-center.select-none")
if (headerElement) {
fireEvent.click(headerElement)
}
expect(mockVscodePostMessage).toHaveBeenCalledWith({
type: "openFile",
text: "src/components/Button.tsx",
})
})
it("handles files with paths starting with dot correctly", () => {
const filesWithDotPath = [
{
key: "file1",
path: "./src/index.ts",
content: "./src/index.ts",
lineSnippet: "import React from 'react'",
},
]
render(
<TranslationProvider>
<BatchFilePermission
files={filesWithDotPath}
onPermissionResponse={mockOnPermissionResponse}
ts={Date.now()}
/>
</TranslationProvider>,
)
// Should render dot before the path
expect(screen.getByText(".")).toBeInTheDocument()
expect(screen.getByText(/\/src\/index\.ts/)).toBeInTheDocument()
})
it("re-renders when timestamp changes", () => {
const { rerender } = render(
<TranslationProvider>
<BatchFilePermission files={mockFiles} onPermissionResponse={mockOnPermissionResponse} ts={1000} />
</TranslationProvider>,
)
// Initial render
expect(screen.getByText(/Button\.tsx/)).toBeInTheDocument()
// Re-render with new timestamp
rerender(
<TranslationProvider>
<BatchFilePermission files={mockFiles} onPermissionResponse={mockOnPermissionResponse} ts={2000} />
</TranslationProvider>,
)
// Should still show files
expect(screen.getByText(/Button\.tsx/)).toBeInTheDocument()
})
it("displays external link icon for all files", () => {
render(
<TranslationProvider>
<BatchFilePermission
files={mockFiles}
onPermissionResponse={mockOnPermissionResponse}
ts={Date.now()}
/>
</TranslationProvider>,
)
// All files should have external link icons
const externalLinkIcons = screen.getAllByText((_content, element) => {
return element?.classList?.contains("codicon-link-external") ?? false
})
expect(externalLinkIcons).toHaveLength(mockFiles.length)
})
})

View file

@ -0,0 +1,66 @@
import { VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react"
import { useAppTranslation } from "@/i18n/TranslationContext"
import { Slider } from "@/components/ui/slider"
interface ConcurrentFileReadsExperimentProps {
enabled: boolean
onEnabledChange: (value: boolean) => void
maxConcurrentFileReads: number
onMaxConcurrentFileReadsChange: (value: number) => void
}
export const ConcurrentFileReadsExperiment = ({
enabled,
onEnabledChange,
maxConcurrentFileReads,
onMaxConcurrentFileReadsChange,
}: ConcurrentFileReadsExperimentProps) => {
const { t } = useAppTranslation()
const handleChange = (value: boolean) => {
// Set to 1 if disabling to reset the setting
if (!value) onMaxConcurrentFileReadsChange(1)
onEnabledChange(value)
}
return (
<div>
<div className="flex items-center gap-2">
<VSCodeCheckbox
checked={enabled}
onChange={(e: any) => handleChange(e.target.checked)}
data-testid="concurrent-file-reads-checkbox">
<span className="font-medium">{t("settings:experimental.CONCURRENT_FILE_READS.name")}</span>
</VSCodeCheckbox>
</div>
<p className="text-vscode-descriptionForeground text-sm mt-0">
{t("settings:experimental.CONCURRENT_FILE_READS.description")}
</p>
{enabled && (
<div className="flex flex-col gap-3 pl-3 border-l-2 border-vscode-button-background">
<div>
<span className="block text-sm mb-1">
{t("settings:contextManagement.maxConcurrentFileReads.label")}
</span>
<div className="flex items-center gap-2">
<Slider
min={2}
max={100}
step={1}
value={[
maxConcurrentFileReads && maxConcurrentFileReads > 1 ? maxConcurrentFileReads : 15,
]}
onValueChange={([value]) => onMaxConcurrentFileReadsChange(value)}
data-testid="max-concurrent-file-reads-slider"
/>
<span className="w-10 text-sm">
{maxConcurrentFileReads && maxConcurrentFileReads > 1 ? maxConcurrentFileReads : 15}
</span>
</div>
</div>
</div>
)}
</div>
)
}

View file

@ -14,11 +14,13 @@ import { SectionHeader } from "./SectionHeader"
import { Section } from "./Section"
import { ExperimentalFeature } from "./ExperimentalFeature"
import { CodeIndexSettings } from "./CodeIndexSettings"
import { ConcurrentFileReadsExperiment } from "./ConcurrentFileReadsExperiment"
type ExperimentalSettingsProps = HTMLAttributes<HTMLDivElement> & {
experiments: Record<ExperimentId, boolean>
setExperimentEnabled: SetExperimentEnabled
setCachedStateField: SetCachedStateField<"codebaseIndexConfig">
maxConcurrentFileReads?: number
setCachedStateField: SetCachedStateField<"codebaseIndexConfig" | "maxConcurrentFileReads">
// CodeIndexSettings props
codebaseIndexModels: CodebaseIndexModels | undefined
codebaseIndexConfig: CodebaseIndexConfig | undefined
@ -30,6 +32,7 @@ type ExperimentalSettingsProps = HTMLAttributes<HTMLDivElement> & {
export const ExperimentalSettings = ({
experiments,
setExperimentEnabled,
maxConcurrentFileReads,
setCachedStateField,
codebaseIndexModels,
codebaseIndexConfig,
@ -53,16 +56,33 @@ export const ExperimentalSettings = ({
<Section>
{Object.entries(experimentConfigsMap)
.filter((config) => config[0] !== "DIFF_STRATEGY" && config[0] !== "MULTI_SEARCH_AND_REPLACE")
.map((config) => (
<ExperimentalFeature
key={config[0]}
experimentKey={config[0]}
enabled={experiments[EXPERIMENT_IDS[config[0] as keyof typeof EXPERIMENT_IDS]] ?? false}
onChange={(enabled) =>
setExperimentEnabled(EXPERIMENT_IDS[config[0] as keyof typeof EXPERIMENT_IDS], enabled)
}
/>
))}
.map((config) => {
if (config[0] === "CONCURRENT_FILE_READS") {
return (
<ConcurrentFileReadsExperiment
key={config[0]}
enabled={experiments[EXPERIMENT_IDS.CONCURRENT_FILE_READS] ?? false}
onEnabledChange={(enabled) =>
setExperimentEnabled(EXPERIMENT_IDS.CONCURRENT_FILE_READS, enabled)
}
maxConcurrentFileReads={maxConcurrentFileReads ?? 15}
onMaxConcurrentFileReadsChange={(value) =>
setCachedStateField("maxConcurrentFileReads", value)
}
/>
)
}
return (
<ExperimentalFeature
key={config[0]}
experimentKey={config[0]}
enabled={experiments[EXPERIMENT_IDS[config[0] as keyof typeof EXPERIMENT_IDS]] ?? false}
onChange={(enabled) =>
setExperimentEnabled(EXPERIMENT_IDS[config[0] as keyof typeof EXPERIMENT_IDS], enabled)
}
/>
)
})}
<CodeIndexSettings
codebaseIndexModels={codebaseIndexModels}

View file

@ -165,6 +165,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
remoteBrowserEnabled,
maxReadFileLine,
terminalCompressProgressBar,
maxConcurrentFileReads,
condensingApiConfigId,
customCondensingPrompt,
codebaseIndexConfig,
@ -289,6 +290,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
vscode.postMessage({ type: "maxWorkspaceFiles", value: maxWorkspaceFiles ?? 200 })
vscode.postMessage({ type: "showRooIgnoredFiles", bool: showRooIgnoredFiles })
vscode.postMessage({ type: "maxReadFileLine", value: maxReadFileLine ?? -1 })
vscode.postMessage({ type: "maxConcurrentFileReads", value: cachedState.maxConcurrentFileReads ?? 15 })
vscode.postMessage({ type: "currentApiConfigName", text: currentApiConfigName })
vscode.postMessage({ type: "updateExperimental", values: experiments })
vscode.postMessage({ type: "alwaysAllowModeSwitch", bool: alwaysAllowModeSwitch })
@ -654,6 +656,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
<ExperimentalSettings
setExperimentEnabled={setExperimentEnabled}
experiments={experiments}
maxConcurrentFileReads={maxConcurrentFileReads}
setCachedStateField={setCachedStateField}
codebaseIndexModels={codebaseIndexModels}
codebaseIndexConfig={codebaseIndexConfig}

View file

@ -35,6 +35,7 @@ export interface ExtensionStateContextType extends ExtensionState {
filePaths: string[]
openedTabs: Array<{ label: string; isActive: boolean; path?: string }>
organizationAllowList: OrganizationAllowList
maxConcurrentFileReads?: number
condensingApiConfigId?: string
setCondensingApiConfigId: (value: string) => void
customCondensingPrompt?: string
@ -192,6 +193,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
maxReadFileLine: -1, // Default max read file line limit
pinnedApiConfigs: {}, // Empty object for pinned API configs
terminalZshOhMy: false, // Default Oh My Zsh integration setting
maxConcurrentFileReads: 15, // Default concurrent file reads
terminalZshP10k: false, // Default Powerlevel10k integration setting
terminalZdotdir: false, // Default ZDOTDIR handling setting
terminalCompressProgressBar: true, // Default to compress progress bar output

View file

@ -221,6 +221,7 @@ describe("mergeExtensionState", () => {
experiments: {
powerSteering: true,
autoCondenseContext: true,
concurrentFileReads: true,
} as Record<ExperimentId, boolean>,
}
@ -234,6 +235,7 @@ describe("mergeExtensionState", () => {
expect(result.experiments).toEqual({
powerSteering: true,
autoCondenseContext: true,
concurrentFileReads: true,
})
})
})

View file

@ -149,7 +149,9 @@
"didSearchReplace": "Roo ha realitzat cerca i substitució en aquest fitxer:",
"wantsToInsert": "Roo vol inserir contingut en aquest fitxer:",
"wantsToInsertWithLineNumber": "Roo vol inserir contingut a la línia {{lineNumber}} d'aquest fitxer:",
"wantsToInsertAtEnd": "Roo vol afegir contingut al final d'aquest fitxer:"
"wantsToInsertAtEnd": "Roo vol afegir contingut al final d'aquest fitxer:",
"wantsToReadAndXMore": "En Roo vol llegir aquest fitxer i {{count}} més:",
"wantsToReadMultiple": "Roo vol llegir diversos fitxers:"
},
"directoryOperations": {
"wantsToViewTopLevel": "Roo vol veure els fitxers de nivell superior en aquest directori:",
@ -267,5 +269,13 @@
"wantsToSearch": "Roo vol cercar a la base de codi <code>{{query}}</code>:",
"wantsToSearchWithPath": "Roo vol cercar a la base de codi <code>{{query}}</code> a <code>{{path}}</code>:",
"didSearch": "S'han trobat {{count}} resultat(s) per a <code>{{query}}</code>:"
},
"read-batch": {
"approve": {
"title": "Aprovar tot"
},
"deny": {
"title": "Denegar tot"
}
}
}

View file

@ -373,6 +373,10 @@
"description": "Roo llegeix aquest nombre de línies quan el model omet els valors d'inici/final. Si aquest nombre és menor que el total del fitxer, Roo genera un índex de números de línia de les definicions de codi. Casos especials: -1 indica a Roo que llegeixi tot el fitxer (sense indexació), i 0 indica que no llegeixi cap línia i proporcioni només índexs de línia per a un context mínim. Valors més baixos minimitzen l'ús inicial de context, permetent lectures posteriors de rangs de línies precisos. Les sol·licituds amb inici/final explícits no estan limitades per aquesta configuració.",
"lines": "línies",
"always_full_read": "Llegeix sempre el fitxer sencer"
},
"maxConcurrentFileReads": {
"label": "Límit de lectures simultànies",
"description": "Nombre màxim de fitxers que l'eina 'read_file' pot processar simultàniament. Els valors més alts poden accelerar la lectura de múltiples fitxers petits però augmenten l'ús de memòria."
}
},
"terminal": {
@ -472,6 +476,10 @@
"MULTI_SEARCH_AND_REPLACE": {
"name": "Utilitzar eina diff de blocs múltiples experimental",
"description": "Quan està activat, Roo utilitzarà l'eina diff de blocs múltiples. Això intentarà actualitzar múltiples blocs de codi a l'arxiu en una sola petició."
},
"CONCURRENT_FILE_READS": {
"name": "Habilitar lectura concurrent de fitxers",
"description": "Quan està habilitat, Roo pot llegir múltiples fitxers en una sola sol·licitud (fins a 15 fitxers). Quan està deshabilitat, Roo ha de llegir fitxers un per un. Deshabilitar-ho pot ajudar quan es treballa amb models menys capaços o quan voleu més control sobre l'accés als fitxers."
}
},
"promptCaching": {

View file

@ -140,6 +140,7 @@
},
"fileOperations": {
"wantsToRead": "Roo möchte diese Datei lesen:",
"wantsToReadAndXMore": "Roo möchte diese Datei und {{count}} weitere lesen:",
"wantsToReadOutsideWorkspace": "Roo möchte diese Datei außerhalb des Arbeitsbereichs lesen:",
"didRead": "Roo hat diese Datei gelesen:",
"wantsToEdit": "Roo möchte diese Datei bearbeiten:",
@ -149,7 +150,8 @@
"didSearchReplace": "Roo hat Suchen und Ersetzen in dieser Datei durchgeführt:",
"wantsToInsert": "Roo möchte Inhalte in diese Datei einfügen:",
"wantsToInsertWithLineNumber": "Roo möchte Inhalte in diese Datei in Zeile {{lineNumber}} einfügen:",
"wantsToInsertAtEnd": "Roo möchte Inhalte am Ende dieser Datei anhängen:"
"wantsToInsertAtEnd": "Roo möchte Inhalte am Ende dieser Datei anhängen:",
"wantsToReadMultiple": "Roo möchte mehrere Dateien lesen:"
},
"directoryOperations": {
"wantsToViewTopLevel": "Roo möchte die Dateien auf oberster Ebene in diesem Verzeichnis anzeigen:",
@ -267,5 +269,13 @@
"wantsToSearch": "Roo möchte den Codebase nach <code>{{query}}</code> durchsuchen:",
"wantsToSearchWithPath": "Roo möchte den Codebase nach <code>{{query}}</code> in <code>{{path}}</code> durchsuchen:",
"didSearch": "{{count}} Ergebnis(se) für <code>{{query}}</code> gefunden:"
},
"read-batch": {
"approve": {
"title": "Alle genehmigen"
},
"deny": {
"title": "Alle ablehnen"
}
}
}

View file

@ -368,6 +368,10 @@
"label": ".rooignore-Dateien in Listen und Suchen anzeigen",
"description": "Wenn aktiviert, werden Dateien, die mit Mustern in .rooignore übereinstimmen, in Listen mit einem Schlosssymbol angezeigt. Wenn deaktiviert, werden diese Dateien vollständig aus Dateilisten und Suchen ausgeblendet."
},
"maxConcurrentFileReads": {
"label": "Concurrent file reads limit",
"description": "Maximum number of files the 'read_file' tool can process concurrently. Higher values may speed up reading multiple small files but increase memory usage."
},
"maxReadFile": {
"label": "Schwellenwert für automatische Dateilesekürzung",
"description": "Roo liest diese Anzahl von Zeilen, wenn das Modell keine Start-/Endwerte angibt. Wenn diese Zahl kleiner als die Gesamtzahl der Zeilen ist, erstellt Roo einen Zeilennummernindex der Codedefinitionen. Spezialfälle: -1 weist Roo an, die gesamte Datei zu lesen (ohne Indexierung), und 0 weist an, keine Zeilen zu lesen und nur Zeilenindizes für minimalen Kontext bereitzustellen. Niedrigere Werte minimieren die anfängliche Kontextnutzung und ermöglichen präzise nachfolgende Zeilenbereich-Lesungen. Explizite Start-/End-Anfragen sind von dieser Einstellung nicht begrenzt.",
@ -472,6 +476,10 @@
"MULTI_SEARCH_AND_REPLACE": {
"name": "Experimentelles Multi-Block-Diff-Werkzeug verwenden",
"description": "Wenn aktiviert, verwendet Roo das Multi-Block-Diff-Werkzeug. Dies versucht, mehrere Codeblöcke in der Datei in einer Anfrage zu aktualisieren."
},
"CONCURRENT_FILE_READS": {
"name": "Gleichzeitiges Lesen von Dateien aktivieren",
"description": "Wenn aktiviert, kann Roo mehrere Dateien in einer einzigen Anfrage lesen (bis zu 15 Dateien). Wenn deaktiviert, muss Roo Dateien nacheinander lesen. Das Deaktivieren kann helfen, wenn Sie mit weniger leistungsfähigen Modellen arbeiten oder mehr Kontrolle über den Dateizugriff wünschen."
}
},
"promptCaching": {

View file

@ -45,6 +45,14 @@
"title": "Approve",
"tooltip": "Approve this action"
},
"read-batch": {
"approve": {
"title": "Approve All"
},
"deny": {
"title": "Deny All"
}
},
"runCommand": {
"title": "Run Command",
"tooltip": "Execute this command"
@ -141,6 +149,8 @@
},
"fileOperations": {
"wantsToRead": "Roo wants to read this file:",
"wantsToReadMultiple": "Roo wants to read multiple files:",
"wantsToReadAndXMore": "Roo wants to read this file and {{count}} more:",
"wantsToReadOutsideWorkspace": "Roo wants to read this file outside of the workspace:",
"didRead": "Roo read this file:",
"wantsToEdit": "Roo wants to edit this file:",

View file

@ -368,6 +368,10 @@
"label": "Show .rooignore'd files in lists and searches",
"description": "When enabled, files matching patterns in .rooignore will be shown in lists with a lock symbol. When disabled, these files will be completely hidden from file lists and searches."
},
"maxConcurrentFileReads": {
"label": "Concurrent file reads limit",
"description": "Maximum number of files the 'read_file' tool can process concurrently. Higher values may speed up reading multiple small files but increase memory usage."
},
"maxReadFile": {
"label": "File read auto-truncate threshold",
"description": "Roo reads this number of lines when the model omits start/end values. If this number is less than the file's total, Roo generates a line number index of code definitions. Special cases: -1 instructs Roo to read the entire file (without indexing), and 0 instructs it to read no lines and provides line indexes only for minimal context. Lower values minimize initial context usage, enabling precise subsequent line-range reads. Explicit start/end requests are not limited by this setting.",
@ -469,6 +473,10 @@
"name": "Use experimental \"power steering\" mode",
"description": "When enabled, Roo will remind the model about the details of its current mode definition more frequently. This will lead to stronger adherence to role definitions and custom instructions, but will use more tokens per message."
},
"CONCURRENT_FILE_READS": {
"name": "Enable concurrent file reads",
"description": "When enabled, Roo can read multiple files in a single request (up to 15 files). When disabled, Roo must read files one at a time. Disabling this can help when working with less capable models or when you want more control over file access."
},
"MULTI_SEARCH_AND_REPLACE": {
"name": "Use experimental multi block diff tool",
"description": "When enabled, Roo will use multi block diff tool. This will try to update multiple code blocks in the file in one request."

View file

@ -149,7 +149,9 @@
"didSearchReplace": "Roo realizó búsqueda y reemplazo en este archivo:",
"wantsToInsert": "Roo quiere insertar contenido en este archivo:",
"wantsToInsertWithLineNumber": "Roo quiere insertar contenido en este archivo en la línea {{lineNumber}}:",
"wantsToInsertAtEnd": "Roo quiere añadir contenido al final de este archivo:"
"wantsToInsertAtEnd": "Roo quiere añadir contenido al final de este archivo:",
"wantsToReadAndXMore": "Roo quiere leer este archivo y {{count}} más:",
"wantsToReadMultiple": "Roo quiere leer varios archivos:"
},
"directoryOperations": {
"wantsToViewTopLevel": "Roo quiere ver los archivos de nivel superior en este directorio:",
@ -267,5 +269,13 @@
"wantsToSearch": "Roo quiere buscar en la base de código <code>{{query}}</code>:",
"wantsToSearchWithPath": "Roo quiere buscar en la base de código <code>{{query}}</code> en <code>{{path}}</code>:",
"didSearch": "Se encontraron {{count}} resultado(s) para <code>{{query}}</code>:"
},
"read-batch": {
"approve": {
"title": "Aprobar todo"
},
"deny": {
"title": "Denegar todo"
}
}
}

View file

@ -373,6 +373,10 @@
"description": "Roo lee este número de líneas cuando el modelo omite valores de inicio/fin. Si este número es menor que el total del archivo, Roo genera un índice de números de línea de las definiciones de código. Casos especiales: -1 indica a Roo que lea el archivo completo (sin indexación), y 0 indica que no lea líneas y proporcione solo índices de línea para un contexto mínimo. Valores más bajos minimizan el uso inicial de contexto, permitiendo lecturas posteriores de rangos de líneas precisos. Las solicitudes con inicio/fin explícitos no están limitadas por esta configuración.",
"lines": "líneas",
"always_full_read": "Siempre leer el archivo completo"
},
"maxConcurrentFileReads": {
"label": "Límite de lecturas simultáneas",
"description": "Número máximo de archivos que la herramienta 'read_file' puede procesar simultáneamente. Valores más altos pueden acelerar la lectura de múltiples archivos pequeños pero aumentan el uso de memoria."
}
},
"terminal": {
@ -472,6 +476,10 @@
"MULTI_SEARCH_AND_REPLACE": {
"name": "Usar herramienta experimental de diff de bloques múltiples",
"description": "Cuando está habilitado, Roo usará la herramienta de diff de bloques múltiples. Esto intentará actualizar múltiples bloques de código en el archivo en una sola solicitud."
},
"CONCURRENT_FILE_READS": {
"name": "Habilitar lectura concurrente de archivos",
"description": "Cuando está habilitado, Roo puede leer múltiples archivos en una sola solicitud (hasta 15 archivos). Cuando está deshabilitado, Roo debe leer archivos uno a la vez. Deshabilitarlo puede ayudar cuando se trabaja con modelos menos capaces o cuando desea más control sobre el acceso a archivos."
}
},
"promptCaching": {

View file

@ -146,7 +146,9 @@
"didSearchReplace": "Roo a effectué une recherche et remplacement sur ce fichier :",
"wantsToInsert": "Roo veut insérer du contenu dans ce fichier :",
"wantsToInsertWithLineNumber": "Roo veut insérer du contenu dans ce fichier à la ligne {{lineNumber}} :",
"wantsToInsertAtEnd": "Roo veut ajouter du contenu à la fin de ce fichier :"
"wantsToInsertAtEnd": "Roo veut ajouter du contenu à la fin de ce fichier :",
"wantsToReadAndXMore": "Roo veut lire ce fichier et {{count}} de plus :",
"wantsToReadMultiple": "Roo souhaite lire plusieurs fichiers :"
},
"instructions": {
"wantsToFetch": "Roo veut récupérer des instructions détaillées pour aider à la tâche actuelle"
@ -267,5 +269,13 @@
"wantsToSearch": "Roo veut rechercher dans la base de code <code>{{query}}</code> :",
"wantsToSearchWithPath": "Roo veut rechercher dans la base de code <code>{{query}}</code> dans <code>{{path}}</code> :",
"didSearch": "{{count}} résultat(s) trouvé(s) pour <code>{{query}}</code> :"
},
"read-batch": {
"approve": {
"title": "Tout approuver"
},
"deny": {
"title": "Tout refuser"
}
}
}

View file

@ -373,6 +373,10 @@
"description": "Roo lit ce nombre de lignes lorsque le modèle omet les valeurs de début/fin. Si ce nombre est inférieur au total du fichier, Roo génère un index des numéros de ligne des définitions de code. Cas spéciaux : -1 indique à Roo de lire le fichier entier (sans indexation), et 0 indique de ne lire aucune ligne et de fournir uniquement les index de ligne pour un contexte minimal. Des valeurs plus basses minimisent l'utilisation initiale du contexte, permettant des lectures ultérieures de plages de lignes précises. Les requêtes avec début/fin explicites ne sont pas limitées par ce paramètre.",
"lines": "lignes",
"always_full_read": "Toujours lire le fichier entier"
},
"maxConcurrentFileReads": {
"label": "Limite de lectures simultanées",
"description": "Nombre maximum de fichiers que l'outil 'read_file' peut traiter simultanément. Des valeurs plus élevées peuvent accélérer la lecture de plusieurs petits fichiers mais augmentent l'utilisation de la mémoire."
}
},
"terminal": {
@ -472,6 +476,10 @@
"MULTI_SEARCH_AND_REPLACE": {
"name": "Utiliser l'outil diff multi-blocs expérimental",
"description": "Lorsqu'il est activé, Roo utilisera l'outil diff multi-blocs. Cela tentera de mettre à jour plusieurs blocs de code dans le fichier en une seule requête."
},
"CONCURRENT_FILE_READS": {
"name": "Activer la lecture simultanée de fichiers",
"description": "Lorsqu'activé, Roo peut lire plusieurs fichiers dans une seule requête (jusqu'à 15 fichiers). Lorsque désactivé, Roo doit lire les fichiers un par un. La désactivation peut aider lors du travail avec des modèles moins performants ou lorsque vous souhaitez plus de contrôle sur l'accès aux fichiers."
}
},
"promptCaching": {

View file

@ -149,7 +149,9 @@
"didSearchReplace": "Roo ने इस फ़ाइल में खोज और प्रतिस्थापन किया:",
"wantsToInsert": "Roo इस फ़ाइल में सामग्री डालना चाहता है:",
"wantsToInsertWithLineNumber": "Roo इस फ़ाइल की {{lineNumber}} लाइन पर सामग्री डालना चाहता है:",
"wantsToInsertAtEnd": "Roo इस फ़ाइल के अंत में सामग्री जोड़ना चाहता है:"
"wantsToInsertAtEnd": "Roo इस फ़ाइल के अंत में सामग्री जोड़ना चाहता है:",
"wantsToReadAndXMore": "रू इस फ़ाइल को और {{count}} अन्य को पढ़ना चाहता है:",
"wantsToReadMultiple": "Roo कई फ़ाइलें पढ़ना चाहता है:"
},
"directoryOperations": {
"wantsToViewTopLevel": "Roo इस निर्देशिका में शीर्ष स्तर की फ़ाइलें देखना चाहता है:",
@ -267,5 +269,13 @@
"wantsToSearch": "Roo कोडबेस में <code>{{query}}</code> खोजना चाहता है:",
"wantsToSearchWithPath": "Roo <code>{{path}}</code> में कोडबेस में <code>{{query}}</code> खोजना चाहता है:",
"didSearch": "<code>{{query}}</code> के लिए {{count}} परिणाम मिले:"
},
"read-batch": {
"approve": {
"title": "सभी स्वीकृत करें"
},
"deny": {
"title": "सभी अस्वीकार करें"
}
}
}

View file

@ -373,6 +373,10 @@
"description": "जब मॉडल प्रारंभ/अंत मान नहीं देता है, तो Roo इतनी पंक्तियाँ पढ़ता है। यदि यह संख्या फ़ाइल की कुल पंक्तियों से कम है, तो Roo कोड परिभाषाओं का पंक्ति क्रमांक इंडेक्स बनाता है। विशेष मामले: -1 Roo को पूरी फ़ाइल पढ़ने का निर्देश देता है (इंडेक्सिंग के बिना), और 0 कोई पंक्ति न पढ़ने और न्यूनतम संदर्भ के लिए केवल पंक्ति इंडेक्स प्रदान करने का निर्देश देता है। कम मान प्रारंभिक संदर्भ उपयोग को कम करते हैं, जो बाद में सटीक पंक्ति श्रेणी पढ़ने की अनुमति देता है। स्पष्ट प्रारंभ/अंत अनुरोध इस सेटिंग से सीमित नहीं हैं।",
"lines": "पंक्तियाँ",
"always_full_read": "हमेशा पूरी फ़ाइल पढ़ें"
},
"maxConcurrentFileReads": {
"label": "एक साथ फ़ाइल पढ़ने की सीमा",
"description": "'read_file' टूल द्वारा एक साथ प्रोसेस की जा सकने वाली अधिकतम फ़ाइलों की संख्या। उच्च मान कई छोटी फ़ाइलों को पढ़ने की गति बढ़ा सकते हैं लेकिन मेमोरी उपयोग बढ़ा देते हैं।"
}
},
"terminal": {
@ -472,6 +476,10 @@
"MULTI_SEARCH_AND_REPLACE": {
"name": "प्रायोगिक मल्टी ब्लॉक diff उपकरण का उपयोग करें",
"description": "जब सक्षम किया जाता है, तो Roo मल्टी ब्लॉक diff उपकरण का उपयोग करेगा। यह एक अनुरोध में फ़ाइल में कई कोड ब्लॉक अपडेट करने का प्रयास करेगा।"
},
"CONCURRENT_FILE_READS": {
"name": "समवर्ती फ़ाइल पढ़ना सक्षम करें",
"description": "सक्षम होने पर, Roo एक ही अनुरोध में कई फ़ाइलें (अधिकतम 15 फ़ाइलें) पढ़ सकता है। अक्षम होने पर, Roo को एक बार में एक फ़ाइल पढ़नी होगी। कम सक्षम मॉडल के साथ काम करते समय या जब आप फ़ाइल एक्सेस पर अधिक नियंत्रण चाहते हैं तो इसे अक्षम करना मददगार हो सकता है।"
}
},
"promptCaching": {

View file

@ -149,7 +149,9 @@
"didSearchReplace": "Roo ha eseguito ricerca e sostituzione in questo file:",
"wantsToInsert": "Roo vuole inserire contenuto in questo file:",
"wantsToInsertWithLineNumber": "Roo vuole inserire contenuto in questo file alla riga {{lineNumber}}:",
"wantsToInsertAtEnd": "Roo vuole aggiungere contenuto alla fine di questo file:"
"wantsToInsertAtEnd": "Roo vuole aggiungere contenuto alla fine di questo file:",
"wantsToReadAndXMore": "Roo vuole leggere questo file e altri {{count}}:",
"wantsToReadMultiple": "Roo vuole leggere più file:"
},
"directoryOperations": {
"wantsToViewTopLevel": "Roo vuole visualizzare i file di primo livello in questa directory:",
@ -267,5 +269,13 @@
"wantsToSearch": "Roo vuole cercare nella base di codice <code>{{query}}</code>:",
"wantsToSearchWithPath": "Roo vuole cercare nella base di codice <code>{{query}}</code> in <code>{{path}}</code>:",
"didSearch": "Trovato {{count}} risultato/i per <code>{{query}}</code>:"
},
"read-batch": {
"approve": {
"title": "Approva tutto"
},
"deny": {
"title": "Nega tutto"
}
}
}

View file

@ -373,6 +373,10 @@
"description": "Roo legge questo numero di righe quando il modello omette i valori di inizio/fine. Se questo numero è inferiore al totale del file, Roo genera un indice dei numeri di riga delle definizioni di codice. Casi speciali: -1 indica a Roo di leggere l'intero file (senza indicizzazione), e 0 indica di non leggere righe e fornire solo indici di riga per un contesto minimo. Valori più bassi minimizzano l'utilizzo iniziale del contesto, permettendo successive letture precise di intervalli di righe. Le richieste con inizio/fine espliciti non sono limitate da questa impostazione.",
"lines": "righe",
"always_full_read": "Leggi sempre l'intero file"
},
"maxConcurrentFileReads": {
"label": "Limite letture simultanee",
"description": "Numero massimo di file che lo strumento 'read_file' può elaborare contemporaneamente. Valori più alti possono velocizzare la lettura di più file piccoli ma aumentano l'utilizzo della memoria."
}
},
"terminal": {
@ -472,6 +476,10 @@
"MULTI_SEARCH_AND_REPLACE": {
"name": "Usa strumento diff multi-blocco sperimentale",
"description": "Quando abilitato, Roo utilizzerà lo strumento diff multi-blocco. Questo tenterà di aggiornare più blocchi di codice nel file in una singola richiesta."
},
"CONCURRENT_FILE_READS": {
"name": "Abilita lettura simultanea dei file",
"description": "Quando abilitato, Roo può leggere più file in una singola richiesta (fino a 15 file). Quando disabilitato, Roo deve leggere i file uno alla volta. Disabilitarlo può aiutare quando si lavora con modelli meno capaci o quando si desidera maggiore controllo sull'accesso ai file."
}
},
"promptCaching": {

View file

@ -149,7 +149,9 @@
"didSearchReplace": "Rooはこのファイルで検索と置換を実行しました:",
"wantsToInsert": "Rooはこのファイルにコンテンツを挿入したい:",
"wantsToInsertWithLineNumber": "Rooはこのファイルの{{lineNumber}}行目にコンテンツを挿入したい:",
"wantsToInsertAtEnd": "Rooはこのファイルの末尾にコンテンツを追加したい:"
"wantsToInsertAtEnd": "Rooはこのファイルの末尾にコンテンツを追加したい:",
"wantsToReadAndXMore": "Roo はこのファイルと他に {{count}} 個のファイルを読み込もうとしています:",
"wantsToReadMultiple": "Rooは複数のファイルを読み取ろうとしています"
},
"directoryOperations": {
"wantsToViewTopLevel": "Rooはこのディレクトリのトップレベルファイルを表示したい:",
@ -267,5 +269,13 @@
"wantsToSearch": "Rooはコードベースで <code>{{query}}</code> を検索したい:",
"wantsToSearchWithPath": "Rooは <code>{{path}}</code> 内のコードベースで <code>{{query}}</code> を検索したい:",
"didSearch": "<code>{{query}}</code> の検索結果: {{count}} 件"
},
"read-batch": {
"approve": {
"title": "すべて承認"
},
"deny": {
"title": "すべて拒否"
}
}
}

View file

@ -373,6 +373,10 @@
"description": "モデルが開始/終了の値を指定しない場合、Rooはこの行数を読み込みます。この数がファイルの総行数より少ない場合、Rooはコード定義の行番号インデックスを生成します。特殊なケース-1はRooにファイル全体を読み込むよう指示しインデックス作成なし、0は行を読み込まず最小限のコンテキストのために行インデックスのみを提供するよう指示します。低い値は初期コンテキスト使用量を最小限に抑え、後続の正確な行範囲の読み込みを可能にします。明示的な開始/終了の要求はこの設定による制限を受けません。",
"lines": "行",
"always_full_read": "常にファイル全体を読み込む"
},
"maxConcurrentFileReads": {
"label": "同時ファイル読み取り制限",
"description": "read_file ツールが同時に処理できるファイルの最大数。値を高くすると複数の小さなファイルの読み取りが速くなる可能性がありますが、メモリ使用量が増加します。"
}
},
"terminal": {
@ -472,6 +476,10 @@
"MULTI_SEARCH_AND_REPLACE": {
"name": "実験的なマルチブロックdiffツールを使用する",
"description": "有効にすると、Rooはマルチブロックdiffツールを使用します。これにより、1つのリクエストでファイル内の複数のコードブロックを更新しようとします。"
},
"CONCURRENT_FILE_READS": {
"name": "並行ファイル読み取りを有効にする",
"description": "有効にすると、Rooは1回のリクエストで複数のファイル最大15ファイルを読み取ることができます。無効にすると、Rooはファイルを1つずつ読み取る必要があります。能力の低いモデルで作業する場合や、ファイルアクセスをより細かく制御したい場合は、無効にすると役立ちます。"
}
},
"promptCaching": {

View file

@ -149,7 +149,9 @@
"didSearchReplace": "Roo가 이 파일에서 검색 및 바꾸기를 수행했습니다:",
"wantsToInsert": "Roo가 이 파일에 내용을 삽입하고 싶어합니다:",
"wantsToInsertWithLineNumber": "Roo가 이 파일의 {{lineNumber}}번 줄에 내용을 삽입하고 싶어합니다:",
"wantsToInsertAtEnd": "Roo가 이 파일의 끝에 내용을 추가하고 싶어합니다:"
"wantsToInsertAtEnd": "Roo가 이 파일의 끝에 내용을 추가하고 싶어합니다:",
"wantsToReadAndXMore": "Roo가 이 파일과 {{count}}개의 파일을 더 읽으려고 합니다:",
"wantsToReadMultiple": "Roo가 여러 파일을 읽으려고 합니다:"
},
"directoryOperations": {
"wantsToViewTopLevel": "Roo가 이 디렉토리의 최상위 파일을 보고 싶어합니다:",
@ -267,5 +269,13 @@
"wantsToSearch": "Roo가 코드베이스에서 <code>{{query}}</code>을(를) 검색하고 싶어합니다:",
"wantsToSearchWithPath": "Roo가 <code>{{path}}</code>에서 <code>{{query}}</code>을(를) 검색하고 싶어합니다:",
"didSearch": "<code>{{query}}</code>에 대한 검색 결과 {{count}}개 찾음:"
},
"read-batch": {
"approve": {
"title": "모두 승인"
},
"deny": {
"title": "모두 거부"
}
}
}

View file

@ -373,6 +373,10 @@
"description": "모델이 시작/끝 값을 지정하지 않을 때 Roo가 읽는 줄 수입니다. 이 수가 파일의 총 줄 수보다 적으면 Roo는 코드 정의의 줄 번호 인덱스를 생성합니다. 특수한 경우: -1은 Roo에게 전체 파일을 읽도록 지시하고(인덱싱 없이), 0은 줄을 읽지 않고 최소한의 컨텍스트를 위해 줄 인덱스만 제공하도록 지시합니다. 낮은 값은 초기 컨텍스트 사용을 최소화하고, 이후 정확한 줄 범위 읽기를 가능하게 합니다. 명시적 시작/끝 요청은 이 설정의 제한을 받지 않습니다.",
"lines": "줄",
"always_full_read": "항상 전체 파일 읽기"
},
"maxConcurrentFileReads": {
"label": "동시 파일 읽기 제한",
"description": "read_file 도구가 동시에 처리할 수 있는 최대 파일 수입니다. 높은 값은 여러 작은 파일을 읽는 속도를 높일 수 있지만 메모리 사용량이 증가합니다."
}
},
"terminal": {
@ -472,6 +476,10 @@
"MULTI_SEARCH_AND_REPLACE": {
"name": "실험적 다중 블록 diff 도구 사용",
"description": "활성화하면 Roo가 다중 블록 diff 도구를 사용합니다. 이것은 하나의 요청에서 파일의 여러 코드 블록을 업데이트하려고 시도합니다."
},
"CONCURRENT_FILE_READS": {
"name": "동시 파일 읽기 활성화",
"description": "활성화하면 Roo가 한 번의 요청으로 여러 파일(최대 15개)을 읽을 수 있습니다. 비활성화하면 Roo는 파일을 하나씩 읽어야 합니다. 성능이 낮은 모델로 작업하거나 파일 액세스를 더 제어하려는 경우 비활성화하면 도움이 될 수 있습니다."
}
},
"promptCaching": {

View file

@ -144,7 +144,9 @@
"didSearchReplace": "Roo heeft zoeken en vervangen uitgevoerd op dit bestand:",
"wantsToInsert": "Roo wil inhoud invoegen in dit bestand:",
"wantsToInsertWithLineNumber": "Roo wil inhoud invoegen in dit bestand op regel {{lineNumber}}:",
"wantsToInsertAtEnd": "Roo wil inhoud toevoegen aan het einde van dit bestand:"
"wantsToInsertAtEnd": "Roo wil inhoud toevoegen aan het einde van dit bestand:",
"wantsToReadAndXMore": "Roo wil dit bestand en nog {{count}} andere lezen:",
"wantsToReadMultiple": "Roo wil meerdere bestanden lezen:"
},
"directoryOperations": {
"wantsToViewTopLevel": "Roo wil de bovenliggende bestanden in deze map bekijken:",
@ -267,5 +269,13 @@
"wantsToSearch": "Roo wil de codebase doorzoeken op <code>{{query}}</code>:",
"wantsToSearchWithPath": "Roo wil de codebase doorzoeken op <code>{{query}}</code> in <code>{{path}}</code>:",
"didSearch": "{{count}} resultaat/resultaten gevonden voor <code>{{query}}</code>:"
},
"read-batch": {
"approve": {
"title": "Alles goedkeuren"
},
"deny": {
"title": "Alles weigeren"
}
}
}

View file

@ -373,6 +373,10 @@
"description": "Roo leest dit aantal regels wanneer het model geen begin/eindwaarden opgeeft. Als dit aantal lager is dan het totaal, genereert Roo een index van codelijnen. Speciale gevallen: -1 laat Roo het hele bestand lezen (zonder indexering), 0 leest geen regels en geeft alleen een minimale index. Lagere waarden minimaliseren het initiële contextgebruik en maken precieze vervolg-leesopdrachten mogelijk. Expliciete begin/eind-aanvragen worden niet door deze instelling beperkt.",
"lines": "regels",
"always_full_read": "Altijd volledig bestand lezen"
},
"maxConcurrentFileReads": {
"label": "Limiet gelijktijdige bestandslezingen",
"description": "Maximum aantal bestanden dat de 'read_file' tool tegelijkertijd kan verwerken. Hogere waarden kunnen het lezen van meerdere kleine bestanden versnellen maar verhogen het geheugengebruik."
}
},
"terminal": {
@ -472,6 +476,10 @@
"MULTI_SEARCH_AND_REPLACE": {
"name": "Experimentele multi-block diff-tool gebruiken",
"description": "Indien ingeschakeld, gebruikt Roo de multi-block diff-tool. Hiermee wordt geprobeerd meerdere codeblokken in het bestand in één verzoek bij te werken."
},
"CONCURRENT_FILE_READS": {
"name": "Gelijktijdig lezen van bestanden inschakelen",
"description": "Wanneer ingeschakeld, kan Roo meerdere bestanden in één verzoek lezen (tot 15 bestanden). Wanneer uitgeschakeld, moet Roo bestanden één voor één lezen. Uitschakelen kan helpen bij het werken met minder capabele modellen of wanneer u meer controle over bestandstoegang wilt."
}
},
"promptCaching": {

View file

@ -149,7 +149,9 @@
"didSearchReplace": "Roo wykonał wyszukiwanie i zamianę w tym pliku:",
"wantsToInsert": "Roo chce wstawić zawartość do tego pliku:",
"wantsToInsertWithLineNumber": "Roo chce wstawić zawartość do tego pliku w linii {{lineNumber}}:",
"wantsToInsertAtEnd": "Roo chce dodać zawartość na końcu tego pliku:"
"wantsToInsertAtEnd": "Roo chce dodać zawartość na końcu tego pliku:",
"wantsToReadAndXMore": "Roo chce przeczytać ten plik i {{count}} więcej:",
"wantsToReadMultiple": "Roo chce odczytać wiele plików:"
},
"directoryOperations": {
"wantsToViewTopLevel": "Roo chce zobaczyć pliki najwyższego poziomu w tym katalogu:",
@ -267,5 +269,13 @@
"wantsToSearch": "Roo chce przeszukać bazę kodu w poszukiwaniu <code>{{query}}</code>:",
"wantsToSearchWithPath": "Roo chce przeszukać bazę kodu w poszukiwaniu <code>{{query}}</code> w <code>{{path}}</code>:",
"didSearch": "Znaleziono {{count}} wynik(ów) dla <code>{{query}}</code>:"
},
"read-batch": {
"approve": {
"title": "Zatwierdź wszystko"
},
"deny": {
"title": "Odrzuć wszystko"
}
}
}

View file

@ -373,6 +373,10 @@
"description": "Roo odczytuje tę liczbę linii, gdy model nie określa wartości początkowej/końcowej. Jeśli ta liczba jest mniejsza niż całkowita liczba linii pliku, Roo generuje indeks numerów linii definicji kodu. Przypadki specjalne: -1 nakazuje Roo odczytać cały plik (bez indeksowania), a 0 nakazuje nie czytać żadnych linii i dostarczyć tylko indeksy linii dla minimalnego kontekstu. Niższe wartości minimalizują początkowe użycie kontekstu, umożliwiając późniejsze precyzyjne odczyty zakresów linii. Jawne żądania początku/końca nie są ograniczone tym ustawieniem.",
"lines": "linii",
"always_full_read": "Zawsze czytaj cały plik"
},
"maxConcurrentFileReads": {
"label": "Limit jednoczesnych odczytów",
"description": "Maksymalna liczba plików, które narzędzie 'read_file' może przetwarzać jednocześnie. Wyższe wartości mogą przyspieszyć odczyt wielu małych plików, ale zwiększają zużycie pamięci."
}
},
"terminal": {
@ -472,6 +476,10 @@
"MULTI_SEARCH_AND_REPLACE": {
"name": "Użyj eksperymentalnego narzędzia diff wieloblokowego",
"description": "Po włączeniu, Roo użyje narzędzia diff wieloblokowego. Spróbuje to zaktualizować wiele bloków kodu w pliku w jednym żądaniu."
},
"CONCURRENT_FILE_READS": {
"name": "Włącz jednoczesne odczytywanie plików",
"description": "Po włączeniu Roo może odczytać wiele plików w jednym żądaniu (do 15 plików). Po wyłączeniu Roo musi odczytywać pliki pojedynczo. Wyłączenie może pomóc podczas pracy z mniej wydajnymi modelami lub gdy chcesz mieć większą kontrolę nad dostępem do plików."
}
},
"promptCaching": {

View file

@ -149,7 +149,9 @@
"didSearchReplace": "Roo realizou busca e substituição neste arquivo:",
"wantsToInsert": "Roo quer inserir conteúdo neste arquivo:",
"wantsToInsertWithLineNumber": "Roo quer inserir conteúdo neste arquivo na linha {{lineNumber}}:",
"wantsToInsertAtEnd": "Roo quer adicionar conteúdo ao final deste arquivo:"
"wantsToInsertAtEnd": "Roo quer adicionar conteúdo ao final deste arquivo:",
"wantsToReadAndXMore": "Roo quer ler este arquivo e mais {{count}}:",
"wantsToReadMultiple": "Roo deseja ler múltiplos arquivos:"
},
"directoryOperations": {
"wantsToViewTopLevel": "Roo quer visualizar os arquivos de nível superior neste diretório:",
@ -267,5 +269,13 @@
"wantsToSearch": "Roo quer pesquisar na base de código por <code>{{query}}</code>:",
"wantsToSearchWithPath": "Roo quer pesquisar na base de código por <code>{{query}}</code> em <code>{{path}}</code>:",
"didSearch": "Encontrado {{count}} resultado(s) para <code>{{query}}</code>:"
},
"read-batch": {
"approve": {
"title": "Aprovar tudo"
},
"deny": {
"title": "Negar tudo"
}
}
}

View file

@ -373,6 +373,10 @@
"description": "O Roo lê este número de linhas quando o modelo omite valores de início/fim. Se este número for menor que o total do arquivo, o Roo gera um índice de números de linha das definições de código. Casos especiais: -1 instrui o Roo a ler o arquivo inteiro (sem indexação), e 0 instrui a não ler linhas e fornecer apenas índices de linha para contexto mínimo. Valores mais baixos minimizam o uso inicial de contexto, permitindo leituras posteriores precisas de intervalos de linhas. Requisições com início/fim explícitos não são limitadas por esta configuração.",
"lines": "linhas",
"always_full_read": "Sempre ler o arquivo inteiro"
},
"maxConcurrentFileReads": {
"label": "Limite de leituras simultâneas",
"description": "Número máximo de arquivos que a ferramenta 'read_file' pode processar simultaneamente. Valores mais altos podem acelerar a leitura de vários arquivos pequenos, mas aumentam o uso de memória."
}
},
"terminal": {
@ -472,6 +476,10 @@
"MULTI_SEARCH_AND_REPLACE": {
"name": "Usar ferramenta diff de múltiplos blocos experimental",
"description": "Quando ativado, o Roo usará a ferramenta diff de múltiplos blocos. Isso tentará atualizar vários blocos de código no arquivo em uma única solicitação."
},
"CONCURRENT_FILE_READS": {
"name": "Habilitar leitura simultânea de arquivos",
"description": "Quando habilitado, o Roo pode ler vários arquivos em uma única solicitação (até 15 arquivos). Quando desabilitado, o Roo deve ler arquivos um de cada vez. Desabilitar pode ajudar ao trabalhar com modelos menos capazes ou quando você deseja mais controle sobre o acesso aos arquivos."
}
},
"promptCaching": {

View file

@ -144,7 +144,9 @@
"didSearchReplace": "Roo выполнил поиск и замену в этом файле:",
"wantsToInsert": "Roo хочет вставить содержимое в этот файл:",
"wantsToInsertWithLineNumber": "Roo хочет вставить содержимое в этот файл на строку {{lineNumber}}:",
"wantsToInsertAtEnd": "Roo хочет добавить содержимое в конец этого файла:"
"wantsToInsertAtEnd": "Roo хочет добавить содержимое в конец этого файла:",
"wantsToReadAndXMore": "Roo хочет прочитать этот файл и еще {{count}}:",
"wantsToReadMultiple": "Roo хочет прочитать несколько файлов:"
},
"directoryOperations": {
"wantsToViewTopLevel": "Roo хочет просмотреть файлы верхнего уровня в этой директории:",
@ -267,5 +269,13 @@
"wantsToSearch": "Roo хочет выполнить поиск в кодовой базе по <code>{{query}}</code>:",
"wantsToSearchWithPath": "Roo хочет выполнить поиск в кодовой базе по <code>{{query}}</code> в <code>{{path}}</code>:",
"didSearch": "Найдено {{count}} результат(ов) для <code>{{query}}</code>:"
},
"read-batch": {
"approve": {
"title": "Одобрить все"
},
"deny": {
"title": "Отклонить все"
}
}
}

View file

@ -373,6 +373,10 @@
"description": "Roo читает столько строк, если модель не указала явно начало/конец. Если число меньше общего количества строк в файле, Roo создаёт индекс определений кода по строкам. Особые случаи: -1 — Roo читает весь файл (без индексации), 0 — не читает строки, а создаёт только минимальный индекс. Меньшие значения минимизируют начальный контекст, позволяя точнее читать нужные диапазоны строк. Явные запросы начала/конца не ограничиваются этим параметром.",
"lines": "строк",
"always_full_read": "Всегда читать весь файл"
},
"maxConcurrentFileReads": {
"label": "Лимит одновременного чтения",
"description": "Максимальное количество файлов, которые инструмент 'read_file' может обрабатывать одновременно. Более высокие значения могут ускорить чтение нескольких небольших файлов, но увеличивают использование памяти."
}
},
"terminal": {
@ -472,6 +476,10 @@
"MULTI_SEARCH_AND_REPLACE": {
"name": "Использовать экспериментальный мультиблочный инструмент диффа",
"description": "Если включено, Roo будет использовать мультиблочный инструмент диффа, пытаясь обновить несколько блоков кода за один запрос."
},
"CONCURRENT_FILE_READS": {
"name": "Включить одновременное чтение файлов",
"description": "При включении Roo может читать несколько файлов в одном запросе (до 15 файлов). При отключении Roo должен читать файлы по одному. Отключение может помочь при работе с менее производительными моделями или когда вы хотите больше контроля над доступом к файлам."
}
},
"promptCaching": {

View file

@ -149,7 +149,9 @@
"didSearchReplace": "Roo bu dosyada arama ve değiştirme yaptı:",
"wantsToInsert": "Roo bu dosyaya içerik eklemek istiyor:",
"wantsToInsertWithLineNumber": "Roo bu dosyanın {{lineNumber}}. satırına içerik eklemek istiyor:",
"wantsToInsertAtEnd": "Roo bu dosyanın sonuna içerik eklemek istiyor:"
"wantsToInsertAtEnd": "Roo bu dosyanın sonuna içerik eklemek istiyor:",
"wantsToReadAndXMore": "Roo bu dosyayı ve {{count}} tane daha okumak istiyor:",
"wantsToReadMultiple": "Roo birden fazla dosya okumak istiyor:"
},
"directoryOperations": {
"wantsToViewTopLevel": "Roo bu dizindeki üst düzey dosyaları görüntülemek istiyor:",
@ -267,5 +269,13 @@
"wantsToSearch": "Roo kod tabanında <code>{{query}}</code> aramak istiyor:",
"wantsToSearchWithPath": "Roo <code>{{path}}</code> içinde kod tabanında <code>{{query}}</code> aramak istiyor:",
"didSearch": "<code>{{query}}</code> için {{count}} sonuç bulundu:"
},
"read-batch": {
"approve": {
"title": "Tümünü Onayla"
},
"deny": {
"title": "Tümünü Reddet"
}
}
}

View file

@ -373,6 +373,10 @@
"description": "Model başlangıç/bitiş değerlerini belirtmediğinde Roo bu sayıda satırı okur. Bu sayı dosyanın toplam satır sayısından azsa, Roo kod tanımlamalarının satır numarası dizinini oluşturur. Özel durumlar: -1, Roo'ya tüm dosyayı okumasını (dizinleme olmadan), 0 ise hiç satır okumamasını ve minimum bağlam için yalnızca satır dizinleri sağlamasını belirtir. Düşük değerler başlangıç bağlam kullanımını en aza indirir ve sonraki hassas satır aralığı okumalarına olanak tanır. Açık başlangıç/bitiş istekleri bu ayarla sınırlı değildir.",
"lines": "satır",
"always_full_read": "Her zaman tüm dosyayı oku"
},
"maxConcurrentFileReads": {
"label": "Eşzamanlı dosya okuma sınırı",
"description": "'read_file' aracının aynı anda işleyebileceği maksimum dosya sayısı. Daha yüksek değerler birden çok küçük dosyanın okunmasını hızlandırabilir ancak bellek kullanımını artırır."
}
},
"terminal": {
@ -472,6 +476,10 @@
"MULTI_SEARCH_AND_REPLACE": {
"name": "Deneysel çoklu blok diff aracını kullan",
"description": "Etkinleştirildiğinde, Roo çoklu blok diff aracını kullanacaktır. Bu, tek bir istekte dosyadaki birden fazla kod bloğunu güncellemeye çalışacaktır."
},
"CONCURRENT_FILE_READS": {
"name": "Eşzamanlı dosya okumayı etkinleştir",
"description": "Etkinleştirildiğinde, Roo tek bir istekte birden fazla dosya okuyabilir (en fazla 15 dosya). Devre dışı bırakıldığında, Roo dosyaları birer birer okumalıdır. Daha az yetenekli modellerle çalışırken veya dosya erişimi üzerinde daha fazla kontrol istediğinizde devre dışı bırakmak yardımcı olabilir."
}
},
"promptCaching": {

View file

@ -149,7 +149,9 @@
"didSearchReplace": "Roo đã thực hiện tìm kiếm và thay thế trong tệp này:",
"wantsToInsert": "Roo muốn chèn nội dung vào tệp này:",
"wantsToInsertWithLineNumber": "Roo muốn chèn nội dung vào dòng {{lineNumber}} của tệp này:",
"wantsToInsertAtEnd": "Roo muốn thêm nội dung vào cuối tệp này:"
"wantsToInsertAtEnd": "Roo muốn thêm nội dung vào cuối tệp này:",
"wantsToReadAndXMore": "Roo muốn đọc tệp này và {{count}} tệp khác:",
"wantsToReadMultiple": "Roo muốn đọc nhiều tệp:"
},
"directoryOperations": {
"wantsToViewTopLevel": "Roo muốn xem các tệp cấp cao nhất trong thư mục này:",
@ -267,5 +269,13 @@
"wantsToSearch": "Roo muốn tìm kiếm trong cơ sở mã cho <code>{{query}}</code>:",
"wantsToSearchWithPath": "Roo muốn tìm kiếm trong cơ sở mã cho <code>{{query}}</code> trong <code>{{path}}</code>:",
"didSearch": "Đã tìm thấy {{count}} kết quả cho <code>{{query}}</code>:"
},
"read-batch": {
"approve": {
"title": "Chấp nhận tất cả"
},
"deny": {
"title": "Từ chối tất cả"
}
}
}

View file

@ -373,6 +373,10 @@
"description": "Roo đọc số dòng này khi mô hình không chỉ định giá trị bắt đầu/kết thúc. Nếu số này nhỏ hơn tổng số dòng của tệp, Roo sẽ tạo một chỉ mục số dòng của các định nghĩa mã. Trường hợp đặc biệt: -1 chỉ thị Roo đọc toàn bộ tệp (không tạo chỉ mục), và 0 chỉ thị không đọc dòng nào và chỉ cung cấp chỉ mục dòng cho ngữ cảnh tối thiểu. Giá trị thấp hơn giảm thiểu việc sử dụng ngữ cảnh ban đầu, cho phép đọc chính xác các phạm vi dòng sau này. Các yêu cầu có chỉ định bắt đầu/kết thúc rõ ràng không bị giới hạn bởi cài đặt này.",
"lines": "dòng",
"always_full_read": "Luôn đọc toàn bộ tệp"
},
"maxConcurrentFileReads": {
"label": "Giới hạn đọc file đồng thời",
"description": "Số lượng file tối đa mà công cụ 'read_file' có thể xử lý cùng lúc. Giá trị cao hơn có thể tăng tốc độ đọc nhiều file nhỏ nhưng sẽ tăng mức sử dụng bộ nhớ."
}
},
"terminal": {
@ -472,6 +476,10 @@
"MULTI_SEARCH_AND_REPLACE": {
"name": "Sử dụng công cụ diff đa khối thử nghiệm",
"description": "Khi được bật, Roo sẽ sử dụng công cụ diff đa khối. Điều này sẽ cố gắng cập nhật nhiều khối mã trong tệp trong một yêu cầu."
},
"CONCURRENT_FILE_READS": {
"name": "Bật đọc tệp đồng thời",
"description": "Khi bật, Roo có thể đọc nhiều tệp trong một yêu cầu duy nhất (tối đa 15 tệp). Khi tắt, Roo phải đọc từng tệp một. Việc tắt có thể hữu ích khi làm việc với các mô hình ít khả năng hơn hoặc khi bạn muốn kiểm soát nhiều hơn quyền truy cập tệp."
}
},
"promptCaching": {

View file

@ -149,7 +149,9 @@
"didSearchReplace": "已完成搜索和替换:",
"wantsToInsert": "需要在此文件中插入内容:",
"wantsToInsertWithLineNumber": "需要在第 {{lineNumber}} 行插入内容:",
"wantsToInsertAtEnd": "需要在文件末尾添加内容:"
"wantsToInsertAtEnd": "需要在文件末尾添加内容:",
"wantsToReadAndXMore": "Roo 想读取此文件以及另外 {{count}} 个文件:",
"wantsToReadMultiple": "Roo 想要读取多个文件:"
},
"directoryOperations": {
"wantsToViewTopLevel": "需要查看目录文件列表:",
@ -267,5 +269,13 @@
"wantsToSearch": "Roo 需要搜索代码库: <code>{{query}}</code>",
"wantsToSearchWithPath": "Roo 需要在 <code>{{path}}</code> 中搜索: <code>{{query}}</code>",
"didSearch": "找到 {{count}} 个结果: <code>{{query}}</code>"
},
"read-batch": {
"approve": {
"title": "全部批准"
},
"deny": {
"title": "全部拒绝"
}
}
}

View file

@ -373,6 +373,10 @@
"description": "自动读取文件行数设置:-1=完整读取 0=仅生成行号索引较小值可节省token支持后续使用行号进行读取。 <0>了解更多</0>",
"lines": "行",
"always_full_read": "始终读取整个文件"
},
"maxConcurrentFileReads": {
"label": "并发文件读取限制",
"description": "read_file 工具可以同时处理的最大文件数。较高的值可能会加快读取多个小文件的速度,但会增加内存使用量。"
}
},
"terminal": {
@ -472,6 +476,10 @@
"MULTI_SEARCH_AND_REPLACE": {
"name": "允许批量搜索和替换",
"description": "启用后Roo 将尝试在一个请求中进行批量搜索和替换。"
},
"CONCURRENT_FILE_READS": {
"name": "启用并发文件读取",
"description": "启用后Roo 可以在单个请求中读取多个文件(最多 15 个文件。禁用后Roo 必须逐个读取文件。在使用能力较弱的模型或希望对文件访问有更多控制时,禁用此功能可能会有所帮助。"
}
},
"promptCaching": {

View file

@ -149,7 +149,9 @@
"didSearchReplace": "Roo 已在此檔案執行搜尋和取代:",
"wantsToInsert": "Roo 想要在此檔案中插入內容:",
"wantsToInsertWithLineNumber": "Roo 想要在此檔案第 {{lineNumber}} 行插入內容:",
"wantsToInsertAtEnd": "Roo 想要在此檔案末尾新增內容:"
"wantsToInsertAtEnd": "Roo 想要在此檔案末尾新增內容:",
"wantsToReadAndXMore": "Roo 想要讀取此檔案以及另外 {{count}} 個檔案:",
"wantsToReadMultiple": "Roo 想要讀取多個檔案:"
},
"directoryOperations": {
"wantsToViewTopLevel": "Roo 想要檢視此目錄中最上層的檔案:",
@ -267,5 +269,13 @@
"wantsToSearch": "Roo 想要搜尋程式碼庫:<code>{{query}}</code>",
"wantsToSearchWithPath": "Roo 想要在 <code>{{path}}</code> 中搜尋:<code>{{query}}</code>",
"didSearch": "找到 {{count}} 個結果:<code>{{query}}</code>"
},
"read-batch": {
"approve": {
"title": "全部核准"
},
"deny": {
"title": "全部拒絕"
}
}
}

View file

@ -373,6 +373,10 @@
"description": "當模型未指定起始/結束值時Roo 讀取的行數。如果此數值小於檔案總行數Roo 將產生程式碼定義的行號索引。特殊情況:-1 指示 Roo 讀取整個檔案不建立索引0 指示不讀取任何行並僅提供行索引以取得最小上下文。較低的值可最小化初始上下文使用,允許後續精確的行範圍讀取。明確指定起始/結束的請求不受此設定限制。 <0>瞭解更多</0>",
"lines": "行",
"always_full_read": "始終讀取整個檔案"
},
"maxConcurrentFileReads": {
"label": "並行檔案讀取限制",
"description": "read_file 工具可以同時處理的最大檔案數。較高的值可能會加快讀取多個小檔案的速度,但會增加記憶體使用量。"
}
},
"terminal": {
@ -472,6 +476,10 @@
"MULTI_SEARCH_AND_REPLACE": {
"name": "使用實驗性多區塊差異比對工具",
"description": "啟用後Roo 將使用多區塊差異比對工具,嘗試在單一請求中更新檔案內的多個程式碼區塊。"
},
"CONCURRENT_FILE_READS": {
"name": "啟用並行檔案讀取",
"description": "啟用後Roo 可以在單一請求中讀取多個檔案(最多 15 個檔案。停用後Roo 必須逐一讀取檔案。在使用能力較弱的模型或希望對檔案存取有更多控制時,停用此功能可能會有所幫助。"
}
},
"promptCaching": {

View file

@ -123,6 +123,11 @@
--color-vscode-inputValidation-infoForeground: var(--vscode-inputValidation-infoForeground);
--color-vscode-inputValidation-infoBackground: var(--vscode-inputValidation-infoBackground);
--color-vscode-inputValidation-infoBorder: var(--vscode-inputValidation-infoBorder);
--color-vscode-widget-border: var(--vscode-widget-border);
--color-vscode-textLink-foreground: var(--vscode-textLink-foreground);
--color-vscode-textCodeBlock-background: var(--vscode-textCodeBlock-background);
--color-vscode-button-hoverBackground: var(--vscode-button-hoverBackground);
}
@layer base {