From f512d99614556ed7fca80afd7f8a38134d183e60 Mon Sep 17 00:00:00 2001 From: roboomp Date: Wed, 15 Jul 2026 22:25:44 +0000 Subject: [PATCH] fix(tool): strip leading colon before Windows path shapes expandPath's leading-colon strip only fired before POSIX prefixes (`/`, `~/`, `./`, `../`), so Windows-mangled inputs like `:C:\repo\file` or `:.\src` slipped through and path.resolve treated them as relative children of cwd. The omp grep subcommand is the Windows grep path, so it hit the common Windows form of the same bug. Broaden the lookahead to admit `\` separators, `.\`/`..\` relatives, and drive-letter absolutes (`[A-Za-z]:`). Add expandPath regression tests for the Windows forms and the bare-token non-strip cases. Fixes #5624 --- packages/coding-agent/CHANGELOG.md | 2 +- packages/coding-agent/src/tools/path-utils.ts | 13 ++++++++----- .../tools/path-literal-colon-selector.test.ts | 18 ++++++++++++++++++ 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index d427add2e..998deff3d 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -4,7 +4,7 @@ ### Fixed -- Fixed the `omp grep` CLI subcommand failing on paths with a stray leading colon (e.g. `:/abs/path`); it now routes the path argument through `expandPath` like `read`/`edit`/in-agent `grep` ([#5624](https://github.com/can1357/oh-my-pi/issues/5624)). +- Fixed the `omp grep` CLI subcommand failing on paths with a stray leading colon (e.g. `:/abs/path`); it now routes the path argument through `expandPath` like `read`/`edit`/in-agent `grep`. Broadened `expandPath`'s leading-colon strip to also recover Windows-style shapes (`:C:\repo\file`, `:.\src`, `:..\rel`, `:\\server\share`) ([#5624](https://github.com/can1357/oh-my-pi/issues/5624)). ## [17.0.0] - 2026-07-15 diff --git a/packages/coding-agent/src/tools/path-utils.ts b/packages/coding-agent/src/tools/path-utils.ts index 9da7262c0..5602b3c85 100644 --- a/packages/coding-agent/src/tools/path-utils.ts +++ b/packages/coding-agent/src/tools/path-utils.ts @@ -148,11 +148,14 @@ export function expandTilde(filePath: string, home?: string): string { export function expandPath(filePath: string): string { // Some models intermittently prefix an otherwise-valid path with a stray - // `:` (e.g. `:/abs/path`, `:../rel`). No real path starts with `:` and it - // never begins a selector against an absolute/relative path, so strip it - // before resolution — mirroring the `@`-prefix normalization above and the - // implicit stripping `write` already tolerates (issue #5508). - const deColoned = /^:(?=[/~]|\.\.?\/)/.test(filePath) ? filePath.slice(1) : filePath; + // `:` (e.g. `:/abs/path`, `:../rel`, or the Windows forms `:C:\repo\file` + // and `:.\src`). No real path starts with `:` and it never begins a + // selector against an absolute/relative path, so strip it before + // resolution — mirroring the `@`-prefix normalization above and the + // implicit stripping `write` already tolerates (issues #5508, #5624). The + // lookahead admits POSIX (`/`, `~`, `./`, `../`) and Windows (`\`, `.\`, + // `..\`, drive-letter `C:`) path shapes. + const deColoned = /^:(?=[/\\~]|\.\.?[/\\]|[A-Za-z]:)/.test(filePath) ? filePath.slice(1) : filePath; const normalized = stripWindowsExtendedLengthPathPrefix( stripFileUrl(normalizeUnicodeSpaces(normalizeAtPrefix(deColoned))), ); diff --git a/packages/coding-agent/test/tools/path-literal-colon-selector.test.ts b/packages/coding-agent/test/tools/path-literal-colon-selector.test.ts index c11cc0bd9..e79abfbde 100644 --- a/packages/coding-agent/test/tools/path-literal-colon-selector.test.ts +++ b/packages/coding-agent/test/tools/path-literal-colon-selector.test.ts @@ -6,6 +6,7 @@ import { resetSettingsForTest, Settings } from "@oh-my-pi/pi-coding-agent/config import { EditTool } from "@oh-my-pi/pi-coding-agent/edit"; import type { ToolSession } from "@oh-my-pi/pi-coding-agent/tools"; import { + expandPath, probeLiteralPathExists, resolveToCwd, splitPathAndSel, @@ -363,6 +364,23 @@ describe("leading-colon path recovery (issue #5508)", () => { expect(resolveToCwd(":name.txt", tmpDir)).toBe(path.join(tmpDir, ":name.txt")); }); + it("strips a leading colon before Windows path shapes in expandPath (issue #5624)", () => { + // Windows native paths mangled with a stray leading colon: drive-letter + // absolutes and `\`/`.\`/`..\` relative forms. expandPath runs before any + // path.resolve, so the strip is platform-independent. + expect(expandPath(":C:\\repo\\file.ts")).toBe("C:\\repo\\file.ts"); + expect(expandPath(":.\\src")).toBe(".\\src"); + expect(expandPath(":..\\sibling")).toBe("..\\sibling"); + expect(expandPath(":\\\\server\\share")).toBe("\\\\server\\share"); + }); + + it("does not strip a colon before a bare drive letter without a path (expandPath)", () => { + // `:selector` shapes still round-trip; the drive-letter branch requires + // the `:` colon to follow, distinguishing `:C:\x` from `:cache`. + expect(expandPath(":raw")).toBe(":raw"); + expect(expandPath(":cache")).toBe(":cache"); + }); + it("read opens a file addressed with a leading colon", async () => { const abs = path.join(tmpDir, "colon-read.txt"); await Bun.write(abs, "test line A\ntest line B\n");