Skip to content

Commit d9595a5

Browse files
Merge branch 'main' into jbbrown/bedrock_caching
* main: Require exactly node v20.18.1 (#2065) Handle null [x]ModelInfo types, recover from settings schema parse errors (#2064) feat: prioritize "Add to Context" and add line number tracking (#2063) Extract code for read_file from Cline (#2059)
2 parents 38e7dfc + 105bc3c commit d9595a5

20 files changed

+529
-1022
lines changed

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
},
1212
"engines": {
1313
"vscode": "^1.84.0",
14-
"node": ">=20.18.1"
14+
"node": "20.18.1"
1515
},
1616
"author": {
1717
"name": "Roo Code"
@@ -185,19 +185,19 @@
185185
],
186186
"roo-code.contextMenu": [
187187
{
188-
"command": "roo-cline.explainCode",
188+
"command": "roo-cline.addToContext",
189189
"group": "1_actions@1"
190190
},
191191
{
192-
"command": "roo-cline.fixCode",
192+
"command": "roo-cline.explainCode",
193193
"group": "1_actions@2"
194194
},
195195
{
196-
"command": "roo-cline.improveCode",
196+
"command": "roo-cline.fixCode",
197197
"group": "1_actions@3"
198198
},
199199
{
200-
"command": "roo-cline.addToContext",
200+
"command": "roo-cline.improveCode",
201201
"group": "1_actions@4"
202202
}
203203
],

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: 3 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
} from "../integrations/misc/extract-text"
3131
import { countFileLines } from "../integrations/misc/line-counter"
3232
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,9 +83,7 @@ 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

8988
export type ToolResponse = string | Array<Anthropic.TextBlockParam | Anthropic.ImageBlockParam>
9089
type UserContent = Array<Anthropic.Messages.ContentBlockParam>
@@ -2256,151 +2255,8 @@ export class Cline extends EventEmitter<ClineEvents> {
22562255
}
22572256

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

24062262
case "fetch_instructions": {

src/core/CodeActionProvider.ts

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,26 @@ export class CodeActionProvider implements vscode.CodeActionProvider {
5656
const filePath = EditorUtils.getFilePath(document)
5757
const actions: vscode.CodeAction[] = []
5858

59+
actions.push(
60+
this.createAction(
61+
ACTION_NAMES.ADD_TO_CONTEXT,
62+
vscode.CodeActionKind.QuickFix,
63+
COMMAND_IDS.ADD_TO_CONTEXT,
64+
[
65+
filePath,
66+
effectiveRange.text,
67+
effectiveRange.range.start.line + 1,
68+
effectiveRange.range.end.line + 1,
69+
],
70+
),
71+
)
72+
5973
actions.push(
6074
...this.createActionPair(ACTION_NAMES.EXPLAIN, vscode.CodeActionKind.QuickFix, COMMAND_IDS.EXPLAIN, [
6175
filePath,
6276
effectiveRange.text,
77+
effectiveRange.range.start.line + 1,
78+
effectiveRange.range.end.line + 1,
6379
]),
6480
)
6581

@@ -74,6 +90,8 @@ export class CodeActionProvider implements vscode.CodeActionProvider {
7490
...this.createActionPair(ACTION_NAMES.FIX, vscode.CodeActionKind.QuickFix, COMMAND_IDS.FIX, [
7591
filePath,
7692
effectiveRange.text,
93+
effectiveRange.range.start.line + 1,
94+
effectiveRange.range.end.line + 1,
7795
diagnosticMessages,
7896
]),
7997
)
@@ -83,6 +101,8 @@ export class CodeActionProvider implements vscode.CodeActionProvider {
83101
...this.createActionPair(ACTION_NAMES.FIX_LOGIC, vscode.CodeActionKind.QuickFix, COMMAND_IDS.FIX, [
84102
filePath,
85103
effectiveRange.text,
104+
effectiveRange.range.start.line + 1,
105+
effectiveRange.range.end.line + 1,
86106
]),
87107
)
88108
}
@@ -92,16 +112,12 @@ export class CodeActionProvider implements vscode.CodeActionProvider {
92112
ACTION_NAMES.IMPROVE,
93113
vscode.CodeActionKind.RefactorRewrite,
94114
COMMAND_IDS.IMPROVE,
95-
[filePath, effectiveRange.text],
96-
),
97-
)
98-
99-
actions.push(
100-
this.createAction(
101-
ACTION_NAMES.ADD_TO_CONTEXT,
102-
vscode.CodeActionKind.QuickFix,
103-
COMMAND_IDS.ADD_TO_CONTEXT,
104-
[filePath, effectiveRange.text],
115+
[
116+
filePath,
117+
effectiveRange.text,
118+
effectiveRange.range.start.line + 1,
119+
effectiveRange.range.end.line + 1,
120+
],
105121
),
106122
)
107123

src/core/EditorUtils.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ export interface EditorContext {
3838
filePath: string
3939
/** The effective text selected or derived from the document. */
4040
selectedText: string
41+
/** The starting line number of the selected text (1-based). */
42+
startLine: number
43+
/** The ending line number of the selected text (1-based). */
44+
endLine: number
4145
/** Optional list of diagnostics associated with the effective range. */
4246
diagnostics?: DiagnosticData[]
4347
}
@@ -194,6 +198,8 @@ export class EditorUtils {
194198
return {
195199
filePath,
196200
selectedText: effectiveRange.text,
201+
startLine: effectiveRange.range.start.line + 1, // Convert to 1-based line numbers
202+
endLine: effectiveRange.range.end.line + 1, // Convert to 1-based line numbers
197203
...(diagnostics.length > 0 ? { diagnostics } : {}),
198204
}
199205
} catch (error) {

src/core/__tests__/CodeActionProvider.test.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,13 @@ describe("CodeActionProvider", () => {
7575
const actions = provider.provideCodeActions(mockDocument, mockRange, mockContext)
7676

7777
expect(actions).toHaveLength(7) // 2 explain + 2 fix logic + 2 improve + 1 add to context
78-
expect((actions as any)[0].title).toBe(`${ACTION_NAMES.EXPLAIN} in New Task`)
79-
expect((actions as any)[1].title).toBe(`${ACTION_NAMES.EXPLAIN} in Current Task`)
80-
expect((actions as any)[2].title).toBe(`${ACTION_NAMES.FIX_LOGIC} in New Task`)
81-
expect((actions as any)[3].title).toBe(`${ACTION_NAMES.FIX_LOGIC} in Current Task`)
82-
expect((actions as any)[4].title).toBe(`${ACTION_NAMES.IMPROVE} in New Task`)
83-
expect((actions as any)[5].title).toBe(`${ACTION_NAMES.IMPROVE} in Current Task`)
84-
expect((actions as any)[6].title).toBe(ACTION_NAMES.ADD_TO_CONTEXT)
78+
expect((actions as any)[0].title).toBe(ACTION_NAMES.ADD_TO_CONTEXT)
79+
expect((actions as any)[1].title).toBe(`${ACTION_NAMES.EXPLAIN} in New Task`)
80+
expect((actions as any)[2].title).toBe(`${ACTION_NAMES.EXPLAIN} in Current Task`)
81+
expect((actions as any)[3].title).toBe(`${ACTION_NAMES.FIX_LOGIC} in New Task`)
82+
expect((actions as any)[4].title).toBe(`${ACTION_NAMES.FIX_LOGIC} in Current Task`)
83+
expect((actions as any)[5].title).toBe(`${ACTION_NAMES.IMPROVE} in New Task`)
84+
expect((actions as any)[6].title).toBe(`${ACTION_NAMES.IMPROVE} in Current Task`)
8585
})
8686

8787
it("should provide fix action instead of fix logic when diagnostics exist", () => {

0 commit comments

Comments
 (0)