Skip to content

Commit 8ff0f5b

Browse files
committed
Merge branch 'main' into cte/benchmark-monorepo
2 parents 073d82a + e7e5511 commit 8ff0f5b

File tree

13 files changed

+715
-546
lines changed

13 files changed

+715
-546
lines changed

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -187,19 +187,19 @@
187187
],
188188
"roo-code.contextMenu": [
189189
{
190-
"command": "roo-cline.explainCode",
190+
"command": "roo-cline.addToContext",
191191
"group": "1_actions@1"
192192
},
193193
{
194-
"command": "roo-cline.fixCode",
194+
"command": "roo-cline.explainCode",
195195
"group": "1_actions@2"
196196
},
197197
{
198-
"command": "roo-cline.improveCode",
198+
"command": "roo-cline.fixCode",
199199
"group": "1_actions@3"
200200
},
201201
{
202-
"command": "roo-cline.addToContext",
202+
"command": "roo-cline.improveCode",
203203
"group": "1_actions@4"
204204
}
205205
],

src/activate/registerCodeActions.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,20 +53,24 @@ const registerCodeAction = (
5353
// Handle both code action and direct command cases.
5454
let filePath: string
5555
let selectedText: string
56+
let startLine: number | undefined
57+
let endLine: number | undefined
5658
let diagnostics: any[] | undefined
5759

5860
if (args.length > 1) {
5961
// Called from code action.
60-
;[filePath, selectedText, diagnostics] = args
62+
;[filePath, selectedText, startLine, endLine, diagnostics] = args
6163
} else {
6264
// Called directly from command palette.
6365
const context = EditorUtils.getEditorContext()
6466
if (!context) return
65-
;({ filePath, selectedText, diagnostics } = context)
67+
;({ filePath, selectedText, startLine, endLine, diagnostics } = context)
6668
}
6769

6870
const params = {
6971
...{ filePath, selectedText },
72+
...(startLine !== undefined ? { startLine: startLine.toString() } : {}),
73+
...(endLine !== undefined ? { endLine: endLine.toString() } : {}),
7074
...(diagnostics ? { diagnostics } : {}),
7175
...(userInput ? { userInput } : {}),
7276
}

src/core/Cline.ts

Lines changed: 35 additions & 212 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ import {
2929
everyLineHasLineNumbers,
3030
} from "../integrations/misc/extract-text"
3131
import { countFileLines } from "../integrations/misc/line-counter"
32-
import { fetchInstructions } from "./prompts/instructions/instructions"
32+
import { fetchInstructionsTool } from "./tools/fetchInstructionsTool"
33+
import { readFileTool } from "./tools/readFileTool"
3334
import { ExitCodeDetails } from "../integrations/terminal/TerminalProcess"
3435
import { Terminal } from "../integrations/terminal/Terminal"
3536
import { TerminalRegistry } from "../integrations/terminal/TerminalRegistry"
@@ -82,11 +83,9 @@ import { insertGroups } from "./diff/insert-groups"
8283
import { telemetryService } from "../services/telemetry/TelemetryService"
8384
import { validateToolUse, isToolAllowedForMode, ToolName } from "./mode-validator"
8485
import { parseXml } from "../utils/xml"
85-
import { readLines } from "../integrations/misc/read-lines"
8686
import { getWorkspacePath } from "../utils/path"
87-
import { isBinaryFile } from "isbinaryfile"
8887

89-
type ToolResponse = string | Array<Anthropic.TextBlockParam | Anthropic.ImageBlockParam>
88+
export type ToolResponse = string | Array<Anthropic.TextBlockParam | Anthropic.ImageBlockParam>
9089
type UserContent = Array<Anthropic.Messages.ContentBlockParam>
9190

9291
export type ClineEvents = {
@@ -148,9 +147,11 @@ export class Cline extends EventEmitter<ClineEvents> {
148147
private askResponseText?: string
149148
private askResponseImages?: string[]
150149
private lastMessageTs?: number
151-
private consecutiveMistakeCount: number = 0
150+
// Not private since it needs to be accessible by tools
151+
consecutiveMistakeCount: number = 0
152152
private consecutiveMistakeCountForApplyDiff: Map<string, number> = new Map()
153-
private providerRef: WeakRef<ClineProvider>
153+
// Not private since it needs to be accessible by tools
154+
providerRef: WeakRef<ClineProvider>
154155
private abort: boolean = false
155156
didFinishAbortingStream = false
156157
abandoned = false
@@ -2254,207 +2255,13 @@ export class Cline extends EventEmitter<ClineEvents> {
22542255
}
22552256

22562257
case "read_file": {
2257-
const relPath: string | undefined = block.params.path
2258-
const startLineStr: string | undefined = block.params.start_line
2259-
const endLineStr: string | undefined = block.params.end_line
2260-
2261-
// Get the full path and determine if it's outside the workspace
2262-
const fullPath = relPath ? path.resolve(this.cwd, removeClosingTag("path", relPath)) : ""
2263-
const isOutsideWorkspace = isPathOutsideWorkspace(fullPath)
2264-
2265-
const sharedMessageProps: ClineSayTool = {
2266-
tool: "readFile",
2267-
path: getReadablePath(this.cwd, removeClosingTag("path", relPath)),
2268-
isOutsideWorkspace,
2269-
}
2270-
try {
2271-
if (block.partial) {
2272-
const partialMessage = JSON.stringify({
2273-
...sharedMessageProps,
2274-
content: undefined,
2275-
} satisfies ClineSayTool)
2276-
await this.ask("tool", partialMessage, block.partial).catch(() => {})
2277-
break
2278-
} else {
2279-
if (!relPath) {
2280-
this.consecutiveMistakeCount++
2281-
pushToolResult(await this.sayAndCreateMissingParamError("read_file", "path"))
2282-
break
2283-
}
2284-
2285-
// Check if we're doing a line range read
2286-
let isRangeRead = false
2287-
let startLine: number | undefined = undefined
2288-
let endLine: number | undefined = undefined
2289-
2290-
// Check if we have either range parameter
2291-
if (startLineStr || endLineStr) {
2292-
isRangeRead = true
2293-
}
2294-
2295-
// Parse start_line if provided
2296-
if (startLineStr) {
2297-
startLine = parseInt(startLineStr)
2298-
if (isNaN(startLine)) {
2299-
// Invalid start_line
2300-
this.consecutiveMistakeCount++
2301-
await this.say("error", `Failed to parse start_line: ${startLineStr}`)
2302-
pushToolResult(formatResponse.toolError("Invalid start_line value"))
2303-
break
2304-
}
2305-
startLine -= 1 // Convert to 0-based index
2306-
}
2307-
2308-
// Parse end_line if provided
2309-
if (endLineStr) {
2310-
endLine = parseInt(endLineStr)
2311-
2312-
if (isNaN(endLine)) {
2313-
// Invalid end_line
2314-
this.consecutiveMistakeCount++
2315-
await this.say("error", `Failed to parse end_line: ${endLineStr}`)
2316-
pushToolResult(formatResponse.toolError("Invalid end_line value"))
2317-
break
2318-
}
2319-
2320-
// Convert to 0-based index
2321-
endLine -= 1
2322-
}
2323-
2324-
const accessAllowed = this.rooIgnoreController?.validateAccess(relPath)
2325-
if (!accessAllowed) {
2326-
await this.say("rooignore_error", relPath)
2327-
pushToolResult(formatResponse.toolError(formatResponse.rooIgnoreError(relPath)))
2328-
2329-
break
2330-
}
2331-
2332-
this.consecutiveMistakeCount = 0
2333-
const absolutePath = path.resolve(this.cwd, relPath)
2334-
const completeMessage = JSON.stringify({
2335-
...sharedMessageProps,
2336-
content: absolutePath,
2337-
} satisfies ClineSayTool)
2338-
2339-
const didApprove = await askApproval("tool", completeMessage)
2340-
if (!didApprove) {
2341-
break
2342-
}
2343-
2344-
// Get the maxReadFileLine setting
2345-
const { maxReadFileLine = 500 } = (await this.providerRef.deref()?.getState()) ?? {}
2346-
2347-
// Count total lines in the file
2348-
let totalLines = 0
2349-
try {
2350-
totalLines = await countFileLines(absolutePath)
2351-
} catch (error) {
2352-
console.error(`Error counting lines in file ${absolutePath}:`, error)
2353-
}
2354-
2355-
// now execute the tool like normal
2356-
let content: string
2357-
let isFileTruncated = false
2358-
let sourceCodeDef = ""
2359-
2360-
const isBinary = await isBinaryFile(absolutePath).catch(() => false)
2361-
2362-
if (isRangeRead) {
2363-
if (startLine === undefined) {
2364-
content = addLineNumbers(await readLines(absolutePath, endLine, startLine))
2365-
} else {
2366-
content = addLineNumbers(
2367-
await readLines(absolutePath, endLine, startLine),
2368-
startLine + 1,
2369-
)
2370-
}
2371-
} else if (!isBinary && maxReadFileLine >= 0 && totalLines > maxReadFileLine) {
2372-
// If file is too large, only read the first maxReadFileLine lines
2373-
isFileTruncated = true
2374-
2375-
const res = await Promise.all([
2376-
maxReadFileLine > 0 ? readLines(absolutePath, maxReadFileLine - 1, 0) : "",
2377-
parseSourceCodeDefinitionsForFile(absolutePath, this.rooIgnoreController),
2378-
])
2379-
2380-
content = res[0].length > 0 ? addLineNumbers(res[0]) : ""
2381-
const result = res[1]
2382-
if (result) {
2383-
sourceCodeDef = `\n\n${result}`
2384-
}
2385-
} else {
2386-
// Read entire file
2387-
content = await extractTextFromFile(absolutePath)
2388-
}
2389-
2390-
// Add truncation notice if applicable
2391-
if (isFileTruncated) {
2392-
content += `\n\n[Showing only ${maxReadFileLine} of ${totalLines} total lines. Use start_line and end_line if you need to read more]${sourceCodeDef}`
2393-
}
2394-
2395-
pushToolResult(content)
2396-
break
2397-
}
2398-
} catch (error) {
2399-
await handleError("reading file", error)
2400-
break
2401-
}
2258+
readFileTool(this, block, askApproval, handleError, pushToolResult, removeClosingTag)
2259+
break
24022260
}
24032261

24042262
case "fetch_instructions": {
2405-
const task: string | undefined = block.params.task
2406-
const sharedMessageProps: ClineSayTool = {
2407-
tool: "fetchInstructions",
2408-
content: task,
2409-
}
2410-
try {
2411-
if (block.partial) {
2412-
const partialMessage = JSON.stringify({
2413-
...sharedMessageProps,
2414-
content: undefined,
2415-
} satisfies ClineSayTool)
2416-
await this.ask("tool", partialMessage, block.partial).catch(() => {})
2417-
break
2418-
} else {
2419-
if (!task) {
2420-
this.consecutiveMistakeCount++
2421-
pushToolResult(
2422-
await this.sayAndCreateMissingParamError("fetch_instructions", "task"),
2423-
)
2424-
break
2425-
}
2426-
2427-
this.consecutiveMistakeCount = 0
2428-
const completeMessage = JSON.stringify({
2429-
...sharedMessageProps,
2430-
content: task,
2431-
} satisfies ClineSayTool)
2432-
2433-
const didApprove = await askApproval("tool", completeMessage)
2434-
if (!didApprove) {
2435-
break
2436-
}
2437-
2438-
// now fetch the content and provide it to the agent.
2439-
const provider = this.providerRef.deref()
2440-
const mcpHub = provider?.getMcpHub()
2441-
if (!mcpHub) {
2442-
throw new Error("MCP hub not available")
2443-
}
2444-
const diffStrategy = this.diffStrategy
2445-
const context = provider?.context
2446-
const content = await fetchInstructions(task, { mcpHub, diffStrategy, context })
2447-
if (!content) {
2448-
pushToolResult(formatResponse.toolError(`Invalid instructions request: ${task}`))
2449-
break
2450-
}
2451-
pushToolResult(content)
2452-
break
2453-
}
2454-
} catch (error) {
2455-
await handleError("fetch instructions", error)
2456-
break
2457-
}
2263+
fetchInstructionsTool(this, block, askApproval, handleError, pushToolResult)
2264+
break
24582265
}
24592266

24602267
case "list_files": {
@@ -2508,10 +2315,10 @@ export class Cline extends EventEmitter<ClineEvents> {
25082315
}
25092316
}
25102317
case "list_code_definition_names": {
2511-
const relDirPath: string | undefined = block.params.path
2318+
const relPath: string | undefined = block.params.path
25122319
const sharedMessageProps: ClineSayTool = {
25132320
tool: "listCodeDefinitionNames",
2514-
path: getReadablePath(this.cwd, removeClosingTag("path", relDirPath)),
2321+
path: getReadablePath(this.cwd, removeClosingTag("path", relPath)),
25152322
}
25162323
try {
25172324
if (block.partial) {
@@ -2522,19 +2329,35 @@ export class Cline extends EventEmitter<ClineEvents> {
25222329
await this.ask("tool", partialMessage, block.partial).catch(() => {})
25232330
break
25242331
} else {
2525-
if (!relDirPath) {
2332+
if (!relPath) {
25262333
this.consecutiveMistakeCount++
25272334
pushToolResult(
25282335
await this.sayAndCreateMissingParamError("list_code_definition_names", "path"),
25292336
)
25302337
break
25312338
}
25322339
this.consecutiveMistakeCount = 0
2533-
const absolutePath = path.resolve(this.cwd, relDirPath)
2534-
const result = await parseSourceCodeForDefinitionsTopLevel(
2535-
absolutePath,
2536-
this.rooIgnoreController,
2537-
)
2340+
const absolutePath = path.resolve(this.cwd, relPath)
2341+
let result: string
2342+
try {
2343+
const stats = await fs.stat(absolutePath)
2344+
if (stats.isFile()) {
2345+
const fileResult = await parseSourceCodeDefinitionsForFile(
2346+
absolutePath,
2347+
this.rooIgnoreController,
2348+
)
2349+
result = fileResult ?? "No source code definitions found in this file."
2350+
} else if (stats.isDirectory()) {
2351+
result = await parseSourceCodeForDefinitionsTopLevel(
2352+
absolutePath,
2353+
this.rooIgnoreController,
2354+
)
2355+
} else {
2356+
result = "The specified path is neither a file nor a directory."
2357+
}
2358+
} catch {
2359+
result = `${absolutePath}: does not exist or cannot be accessed.`
2360+
}
25382361
const completeMessage = JSON.stringify({
25392362
...sharedMessageProps,
25402363
content: result,

0 commit comments

Comments
 (0)