From ba2affae7ffa098b3ed2d4152f72e1d202694f66 Mon Sep 17 00:00:00 2001 From: can1357 Date: Wed, 6 May 2026 17:23:33 +0200 Subject: [PATCH] fix(coding-agent): restore path alias equivalence Fixes #935 --- packages/coding-agent/src/main.ts | 20 +++++++++---------- .../src/task/isolation-backend.ts | 4 ++-- packages/utils/src/dirs.ts | 7 ++++++- packages/utils/test/issue-935-repro.test.ts | 14 ++++++------- 4 files changed, 25 insertions(+), 20 deletions(-) diff --git a/packages/coding-agent/src/main.ts b/packages/coding-agent/src/main.ts index 2c51de11f..80b603ff6 100644 --- a/packages/coding-agent/src/main.ts +++ b/packages/coding-agent/src/main.ts @@ -10,7 +10,15 @@ import * as os from "node:os"; import * as path from "node:path"; import { createInterface } from "node:readline/promises"; import type { ImageContent } from "@oh-my-pi/pi-ai"; -import { $env, getProjectDir, logger, postmortem, setProjectDir, VERSION } from "@oh-my-pi/pi-utils"; +import { + $env, + getProjectDir, + logger, + normalizePathForComparison, + postmortem, + setProjectDir, + VERSION, +} from "@oh-my-pi/pi-utils"; import chalk from "chalk"; import type { Args } from "./cli/args"; import { processFileArguments } from "./cli/file-processor"; @@ -215,11 +223,6 @@ async function runInteractiveMode( } } -function normalizePathForComparison(value: string): string { - const resolved = path.resolve(value); - return process.platform === "win32" ? resolved.toLowerCase() : resolved; -} - async function promptForkSession(session: SessionInfo): Promise { if (!process.stdin.isTTY) { return false; @@ -348,10 +351,7 @@ async function maybeAutoChdir(parsed: Args): Promise { return; } - const normalizePath = (value: string) => { - const resolved = path.resolve(value); - return process.platform === "win32" ? resolved.toLowerCase() : resolved; - }; + const normalizePath = normalizePathForComparison; const cwd = normalizePath(getProjectDir()); const normalizedHome = normalizePath(home); diff --git a/packages/coding-agent/src/task/isolation-backend.ts b/packages/coding-agent/src/task/isolation-backend.ts index b0bac736f..e27164139 100644 --- a/packages/coding-agent/src/task/isolation-backend.ts +++ b/packages/coding-agent/src/task/isolation-backend.ts @@ -9,7 +9,7 @@ export interface IsolationBackendResolution { warning: string; } -type ProcessorEnv = Pick; +type ProcessorEnv = Partial>; function isWindowsArm64HostUnderX64Emulation( platform: NodeJS.Platform, @@ -28,7 +28,7 @@ export async function resolveIsolationBackendForTaskExecution( repoRoot: string | null, platform: NodeJS.Platform = process.platform, arch: NodeJS.Architecture = process.arch, - env: ProcessorEnv = process.env, + env: ProcessorEnv = process.env as ProcessorEnv, ): Promise { let effectiveIsolationMode = requestedMode; let warning = ""; diff --git a/packages/utils/src/dirs.ts b/packages/utils/src/dirs.ts index 0e4bd35a6..f39c5f5f7 100644 --- a/packages/utils/src/dirs.ts +++ b/packages/utils/src/dirs.ts @@ -48,7 +48,12 @@ function standardizeMacOSPath(p: string): string { } export function resolveEquivalentPath(inputPath: string): string { - return path.resolve(inputPath); + const resolvedPath = path.resolve(inputPath); + try { + return fs.realpathSync(resolvedPath); + } catch { + return resolvedPath; + } } export function normalizePathForComparison(inputPath: string): string { diff --git a/packages/utils/test/issue-935-repro.test.ts b/packages/utils/test/issue-935-repro.test.ts index 9f7259944..e56132974 100644 --- a/packages/utils/test/issue-935-repro.test.ts +++ b/packages/utils/test/issue-935-repro.test.ts @@ -8,15 +8,15 @@ describe("issue #935 path equivalence", () => { vi.restoreAllMocks(); }); - it("preserves the lexical project path instead of canonicalizing symlink or junction targets", () => { + it("falls back to the lexical project path when realpath fails", () => { const inputPath = path.resolve("/sessions/link-project"); - const targetPath = path.resolve("/sessions/real-project"); - const realpathSpy = vi.spyOn(fs, "realpathSync").mockImplementation(((p: fs.PathLike) => { - if (path.resolve(String(p)) === inputPath) return targetPath; - return path.resolve(String(p)); - }) as typeof fs.realpathSync); + const realpathSpy = vi.spyOn(fs, "realpathSync").mockImplementation((() => { + const error = new Error("ENOENT: no such file or directory, realpath"); + (error as NodeJS.ErrnoException).code = "ENOENT"; + throw error; + }) as unknown as typeof fs.realpathSync); expect(resolveEquivalentPath(inputPath)).toBe(inputPath); - expect(realpathSpy).not.toHaveBeenCalled(); + expect(realpathSpy).toHaveBeenCalledWith(inputPath); }); });