Skip to content

Commit 0b4df8e

Browse files
committed
fix: support line range regardless of max line read
1 parent 97f9686 commit 0b4df8e

File tree

3 files changed

+82
-5
lines changed

3 files changed

+82
-5
lines changed

src/core/prompts/tools/read-file.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ Description: Request to read the contents of ${isMultipleReadsEnabled ? "one or
99
1010
${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.**"}
1111
12-
${args.partialReadsEnabled ? `By specifying line ranges, you can efficiently read specific portions of large files without loading the entire file into memory.` : ""}
12+
${args.partialReadsEnabled ? `**Line ranges bypass the "Always read entire file" setting** - when you specify explicit line ranges, you can read any lines from any file regardless of the maxReadFileLine configuration. This is useful for targeted work based on tool outputs that reference specific line numbers (e.g., linters, search results, diffs).` : ""}
1313
Parameters:
1414
- args: Contains one or more file elements, where each file contains:
1515
- path: (required) File path (relative to workspace directory ${args.cwd})
16-
${args.partialReadsEnabled ? `- line_range: (optional) One or more line range elements in format "start-end" (1-based, inclusive)` : ""}
16+
${args.partialReadsEnabled ? `- line_range: (optional) One or more line range elements in format "start-end" (1-based, inclusive). Line ranges always bypass maxReadFileLine restrictions.` : ""}
1717
1818
Usage:
1919
<read_file>

src/core/task/Task.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2434,7 +2434,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
24342434
enableMcpServerCreation,
24352435
language,
24362436
rooIgnoreInstructions,
2437-
maxReadFileLine !== -1,
2437+
true, // Always enable partial reads - line ranges bypass maxReadFileLine
24382438
{
24392439
maxConcurrentFileReads: maxConcurrentFileReads ?? 5,
24402440
todoListEnabled: apiConfiguration?.todoListEnabled ?? true,

src/core/tools/__tests__/readFileTool.spec.ts

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ import { parseSourceCodeDefinitionsForFile } from "../../../services/tree-sitter
99
import { isBinaryFile } from "isbinaryfile"
1010
import { ReadFileToolUse, ToolParamName, ToolResponse } from "../../../shared/tools"
1111
import { readFileTool } from "../readFileTool"
12-
import { formatResponse } from "../../prompts/responses"
13-
import { DEFAULT_MAX_IMAGE_FILE_SIZE_MB, DEFAULT_MAX_TOTAL_IMAGE_SIZE_MB } from "../helpers/imageHelpers"
1412

1513
vi.mock("path", async () => {
1614
const originalPath = await vi.importActual("path")
@@ -475,6 +473,85 @@ describe("read_file tool with maxReadFileLine setting", () => {
475473
expect(rangeResult).toContain(`<file><path>${testFilePath}</path>`)
476474
expect(rangeResult).toContain(`<content lines="2-4">`)
477475
})
476+
477+
it("should bypass maxReadFileLine when line ranges are specified", async () => {
478+
// Setup - file has 1000 lines, maxReadFileLine is 100, but we request lines 500-600
479+
mockedCountFileLines.mockResolvedValue(1000)
480+
mockedReadLines.mockResolvedValue("Line 500\nLine 501\n...Line 600")
481+
addLineNumbersMock.mockReturnValue("500 | Line 500\n501 | Line 501\n...600 | Line 600")
482+
483+
// Execute with maxReadFileLine=100 but requesting lines 500-600
484+
const result = await executeReadFileTool(
485+
{},
486+
{
487+
maxReadFileLine: 100,
488+
totalLines: 1000,
489+
start_line: "500",
490+
end_line: "600",
491+
},
492+
)
493+
494+
// Verify that we got the requested range, not limited by maxReadFileLine
495+
expect(result).toContain(`<file><path>${testFilePath}</path>`)
496+
expect(result).toContain(`<content lines="500-600">`)
497+
expect(result).not.toContain("Showing only 100 of 1000 total lines")
498+
499+
// Verify readLines was called with the correct range
500+
expect(mockedReadLines).toHaveBeenCalledWith(absoluteFilePath, 599, 499) // end-1, start-1
501+
})
502+
503+
it("should bypass maxReadFileLine=0 (definitions only) when line ranges are specified", async () => {
504+
// Setup - maxReadFileLine is 0 (definitions only), but we request specific lines
505+
mockedCountFileLines.mockResolvedValue(100)
506+
mockedReadLines.mockResolvedValue("Line 10\nLine 11\nLine 12")
507+
addLineNumbersMock.mockReturnValue("10 | Line 10\n11 | Line 11\n12 | Line 12")
508+
509+
// Execute with maxReadFileLine=0 but requesting lines 10-12
510+
const result = await executeReadFileTool(
511+
{},
512+
{
513+
maxReadFileLine: 0,
514+
totalLines: 100,
515+
start_line: "10",
516+
end_line: "12",
517+
skipAddLineNumbersCheck: true,
518+
},
519+
)
520+
521+
// Verify that we got the actual content, not just definitions
522+
expect(result).toContain(`<file><path>${testFilePath}</path>`)
523+
expect(result).toContain(`<content lines="10-12">`)
524+
expect(result).not.toContain("<list_code_definition_names>")
525+
526+
// Verify readLines was called
527+
expect(mockedReadLines).toHaveBeenCalledWith(absoluteFilePath, 11, 9) // end-1, start-1
528+
})
529+
530+
it("should bypass maxReadFileLine=-1 (always read entire file) when line ranges are specified", async () => {
531+
// Setup - maxReadFileLine is -1 (always read entire file), but we request specific lines
532+
mockedCountFileLines.mockResolvedValue(1000)
533+
mockedReadLines.mockResolvedValue("Line 250\nLine 251\nLine 252")
534+
addLineNumbersMock.mockReturnValue("250 | Line 250\n251 | Line 251\n252 | Line 252")
535+
536+
// Execute with maxReadFileLine=-1 but requesting lines 250-252
537+
const result = await executeReadFileTool(
538+
{},
539+
{
540+
maxReadFileLine: -1,
541+
totalLines: 1000,
542+
start_line: "250",
543+
end_line: "252",
544+
},
545+
)
546+
547+
// Verify that we got the requested range, not the entire file
548+
expect(result).toContain(`<file><path>${testFilePath}</path>`)
549+
expect(result).toContain(`<content lines="250-252">`)
550+
expect(result).not.toContain("lines=\"1-1000\"") // Should not read entire file
551+
552+
// Verify readLines was called with the correct range
553+
expect(mockedReadLines).toHaveBeenCalledWith(absoluteFilePath, 251, 249) // end-1, start-1
554+
})
478555
})
479556
})
480557

0 commit comments

Comments
 (0)