From 2ba92c168147f133e0249231c30f31e4704afed1 Mon Sep 17 00:00:00 2001 From: breken-ai <312387581+breken-ai@users.noreply.github.com> Date: Fri, 25 Sep 2026 03:37:38 -0700 Subject: [PATCH] fix(compiler): leave tail -n +N to the VFS shell instead of printing the last N lines tail -n +N prints from line N onward. The compiled fast path read +N as N and returned the last N lines, so an agent paging a memory file with tail -n +3 got the wrong part of it. Decline the +N form so the VFS shell, whose tail implements it, answers instead. --- src/hooks/bash-command-compiler.ts | 3 +++ .../claude-code/bash-command-compiler.test.ts | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/hooks/bash-command-compiler.ts b/src/hooks/bash-command-compiler.ts index 68e153423..12c2f4f75 100644 --- a/src/hooks/bash-command-compiler.ts +++ b/src/hooks/bash-command-compiler.ts @@ -177,6 +177,9 @@ function parseHeadTailStage(stage: string): { lineLimit: number; fromEnd: boolea return { lineLimit: Math.abs(count), fromEnd: cmd === "tail" }; } if (rest.length === 3 && rest[0] === "-n") { + // `tail -n +N` prints from line N onward, not the last N lines. Leave it to + // the VFS shell, whose tail implements the +N form. + if (cmd === "tail" && rest[1].startsWith("+")) return null; const count = Number(rest[1]); if (!Number.isFinite(count)) return null; return { lineLimit: Math.abs(count), fromEnd: cmd === "tail" }; diff --git a/tests/claude-code/bash-command-compiler.test.ts b/tests/claude-code/bash-command-compiler.test.ts index 3bb90a733..0337249df 100644 --- a/tests/claude-code/bash-command-compiler.test.ts +++ b/tests/claude-code/bash-command-compiler.test.ts @@ -454,4 +454,22 @@ describe("bash-command-compiler execution", () => { ); expect(output).toBe("/summaries/a.md"); }); + + it("does not compile `tail -n +N`, which starts at line N rather than keeping the last N lines", async () => { + expect(parseCompiledSegment("tail -n +3 /a.md")).toBeNull(); + + const readVirtualPathContentsFn = vi.fn(async () => new Map([["/a.md", "l1\nl2\nl3\nl4\nl5\nl6\nl7\nl8"]])); + const output = await executeCompiledBashCommand( + { query: vi.fn() } as any, + "memory", + "sessions", + "tail -n +3 /a.md", + { readVirtualPathContentsFn: readVirtualPathContentsFn as any }, + ); + // GNU tail prints l3..l8 here; the compiled fast path used to answer with the + // last three lines (l6..l8). Returning null hands the command to the VFS shell, + // whose tail implements the +N form. + expect(output).toBeNull(); + expect(readVirtualPathContentsFn).not.toHaveBeenCalled(); + }); });