mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
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:
parent
177e7a8eb9
commit
9ba0cd5c7a
85 changed files with 2780 additions and 789 deletions
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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.`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>()
|
||||
|
|
|
|||
|
|
@ -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` : ""}`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,4 +9,5 @@ export type ToolArgs = {
|
|||
mcpHub?: McpHub
|
||||
toolOptions?: any
|
||||
partialReadsEnabled?: boolean
|
||||
settings?: Record<string, any>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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>`)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
{
|
||||
"readFile": {
|
||||
"linesRange": " (Zeilen {{start}}-{{end}})",
|
||||
"linesFromToEnd": " (Zeilen {{start}}-Ende)",
|
||||
"linesFromStartTo": " (Zeilen 1-{{end}})",
|
||||
"definitionsOnly": " (nur Definitionen)",
|
||||
"maxLines": " (maximal {{max}} Zeilen)"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
{
|
||||
"readFile": {
|
||||
"linesRange": " (lines {{start}}-{{end}})",
|
||||
"linesFromToEnd": " (lines {{start}}-end)",
|
||||
"linesFromStartTo": " (lines 1-{{end}})",
|
||||
"definitionsOnly": " (definitions only)",
|
||||
"maxLines": " (max {{max}} lines)"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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)"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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)"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
{
|
||||
"readFile": {
|
||||
"linesRange": " (पंक्तियाँ {{start}}-{{end}})",
|
||||
"linesFromToEnd": " (पंक्तियाँ {{start}}-अंत)",
|
||||
"linesFromStartTo": " (पंक्तियाँ 1-{{end}})",
|
||||
"definitionsOnly": " (केवल परिभाषाएँ)",
|
||||
"maxLines": " (अधिकतम {{max}} पंक्तियाँ)"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
{
|
||||
"readFile": {
|
||||
"linesRange": " (righe {{start}}-{{end}})",
|
||||
"linesFromToEnd": " (righe {{start}}-fine)",
|
||||
"linesFromStartTo": " (righe 1-{{end}})",
|
||||
"definitionsOnly": " (solo definizioni)",
|
||||
"maxLines": " (max {{max}} righe)"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
{
|
||||
"readFile": {
|
||||
"linesRange": " ({{start}}-{{end}}行目)",
|
||||
"linesFromToEnd": " ({{start}}行目-最後まで)",
|
||||
"linesFromStartTo": " (1-{{end}}行目)",
|
||||
"definitionsOnly": " (定義のみ)",
|
||||
"maxLines": " (最大{{max}}行)"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
{
|
||||
"readFile": {
|
||||
"linesRange": " ({{start}}-{{end}}행)",
|
||||
"linesFromToEnd": " ({{start}}행-끝)",
|
||||
"linesFromStartTo": " (1-{{end}}행)",
|
||||
"definitionsOnly": " (정의만)",
|
||||
"maxLines": " (최대 {{max}}행)"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
{
|
||||
"readFile": {
|
||||
"linesRange": " (regels {{start}}-{{end}})",
|
||||
"linesFromToEnd": " (regels {{start}}-einde)",
|
||||
"linesFromStartTo": " (regels 1-{{end}})",
|
||||
"definitionsOnly": " (alleen definities)",
|
||||
"maxLines": " (max {{max}} regels)"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
{
|
||||
"readFile": {
|
||||
"linesRange": " (linie {{start}}-{{end}})",
|
||||
"linesFromToEnd": " (linie {{start}}-koniec)",
|
||||
"linesFromStartTo": " (linie 1-{{end}})",
|
||||
"definitionsOnly": " (tylko definicje)",
|
||||
"maxLines": " (maks. {{max}} linii)"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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)"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
{
|
||||
"readFile": {
|
||||
"linesRange": " (строки {{start}}-{{end}})",
|
||||
"linesFromToEnd": " (строки {{start}}-конец)",
|
||||
"linesFromStartTo": " (строки 1-{{end}})",
|
||||
"definitionsOnly": " (только определения)",
|
||||
"maxLines": " (макс. {{max}} строк)"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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)"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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)"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
{
|
||||
"readFile": {
|
||||
"linesRange": " (第 {{start}}-{{end}} 行)",
|
||||
"linesFromToEnd": " (第 {{start}} 行至末尾)",
|
||||
"linesFromStartTo": " (第 1-{{end}} 行)",
|
||||
"definitionsOnly": " (仅定义)",
|
||||
"maxLines": " (最多 {{max}} 行)"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
{
|
||||
"readFile": {
|
||||
"linesRange": " (第 {{start}}-{{end}} 行)",
|
||||
"linesFromToEnd": " (第 {{start}} 行至結尾)",
|
||||
"linesFromStartTo": " (第 1-{{end}} 行)",
|
||||
"definitionsOnly": " (僅定義)",
|
||||
"maxLines": " (最多 {{max}} 行)"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -137,4 +137,4 @@ describe("read_file tool with maxReadFileLine setting", () => {
|
|||
expect(readLines).toHaveBeenCalledWith(filePath, maxReadFileLine - 1, 0)
|
||||
expect(addLineNumbers).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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"],
|
||||
|
|
|
|||
32
webview-ui/src/__mocks__/shiki.ts
Normal file
32
webview-ui/src/__mocks__/shiki.ts
Normal 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()
|
||||
24
webview-ui/src/__mocks__/utils/highlighter.ts
Normal file
24
webview-ui/src/__mocks__/utils/highlighter.ts
Normal 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
|
||||
}
|
||||
54
webview-ui/src/components/chat/BatchFilePermission.tsx
Normal file
54
webview-ui/src/components/chat/BatchFilePermission.tsx
Normal 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"
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
})
|
||||
})
|
||||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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:",
|
||||
|
|
|
|||
|
|
@ -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."
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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": "सभी अस्वीकार करें"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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": "すべて拒否"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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": "모두 거부"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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": "Отклонить все"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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ả"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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": "全部拒绝"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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": "全部拒絕"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue