diff --git a/packages/coding-agent/src/core/tools/grep.ts b/packages/coding-agent/src/core/tools/grep.ts index a9562c69f..77d830cd1 100644 --- a/packages/coding-agent/src/core/tools/grep.ts +++ b/packages/coding-agent/src/core/tools/grep.ts @@ -150,7 +150,7 @@ export function createGrepToolDefinition( if (!lines) { try { const content = await ops.readFile(filePath); - lines = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n"); + lines = content.replace(/\r\n/g, "\n").split("\n"); } catch { lines = []; } diff --git a/packages/coding-agent/test/tools.test.ts b/packages/coding-agent/test/tools.test.ts index 91432623f..a096c2f91 100644 --- a/packages/coding-agent/test/tools.test.ts +++ b/packages/coding-agent/test/tools.test.ts @@ -908,6 +908,23 @@ describe("Coding Agent Tools", () => { expect(output).not.toContain("match two"); }); + // https://github.com/earendil-works/pi/issues/11398 + it("should keep context line numbers aligned with ripgrep when a file contains a lone CR", async () => { + const testFile = join(testDir, "lone-cr.txt"); + writeFileSync(testFile, "before\rprogress\nneedle\nafter"); + + const result = await grepTool.execute("test-call-lone-cr", { + pattern: "needle", + path: testFile, + context: 1, + }); + + const output = getTextOutput(result); + expect(output).toContain("lone-cr.txt-1- beforeprogress"); + expect(output).toContain("lone-cr.txt:2: needle"); + expect(output).toContain("lone-cr.txt-3- after"); + }); + // https://github.com/earendil-works/pi/issues/11203 it("should ignore RIPGREP_CONFIG_PATH", async () => { const configFile = join(testDir, "rg-config");