From 979fa4f93ea868376556f9178bf12620ef579763 Mon Sep 17 00:00:00 2001 From: can1357 Date: Sat, 27 Jun 2026 01:23:19 +0200 Subject: [PATCH] fix(advisor): avoid ancestor walks for child repos --- .../src/utils/active-repo-context.ts | 28 +++++++++++++++---- .../test/git-active-context.test.ts | 4 ++- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/packages/coding-agent/src/utils/active-repo-context.ts b/packages/coding-agent/src/utils/active-repo-context.ts index 41226281b..670db16e4 100644 --- a/packages/coding-agent/src/utils/active-repo-context.ts +++ b/packages/coding-agent/src/utils/active-repo-context.ts @@ -88,15 +88,32 @@ function resolveDirectChildDirectorySync(cwd: string, entry: fs.Dirent): string } } +async function hasGitMarker(childPath: string): Promise { + try { + const stat = await fsPromises.stat(path.join(childPath, ".git")); + return stat.isDirectory() || stat.isFile(); + } catch { + return false; + } +} + +function hasGitMarkerSync(childPath: string): boolean { + try { + const stat = fs.statSync(path.join(childPath, ".git")); + return stat.isDirectory() || stat.isFile(); + } catch { + return false; + } +} + async function findSingleDirectChildRepo(cwd: string): Promise { let context: ActiveRepoContext | null = null; for (const entry of await readDirectChildren(cwd)) { const childPath = await resolveDirectChildDirectory(cwd, entry); if (!childPath) continue; - const repository = await resolveRepository(childPath); - if (!repository || path.resolve(repository.repoRoot) !== path.resolve(childPath)) continue; + if (!(await hasGitMarker(childPath))) continue; if (context) return null; - context = buildContext(cwd, repository.repoRoot); + context = buildContext(cwd, childPath); } return context; } @@ -106,10 +123,9 @@ function findSingleDirectChildRepoSync(cwd: string): ActiveRepoContext | null { for (const entry of readDirectChildrenSync(cwd)) { const childPath = resolveDirectChildDirectorySync(cwd, entry); if (!childPath) continue; - const repository = resolveRepositorySync(childPath); - if (!repository || path.resolve(repository.repoRoot) !== path.resolve(childPath)) continue; + if (!hasGitMarkerSync(childPath)) continue; if (context) return null; - context = buildContext(cwd, repository.repoRoot); + context = buildContext(cwd, childPath); } return context; } diff --git a/packages/coding-agent/test/git-active-context.test.ts b/packages/coding-agent/test/git-active-context.test.ts index 4a5c9b06f..7f6b109a4 100644 --- a/packages/coding-agent/test/git-active-context.test.ts +++ b/packages/coding-agent/test/git-active-context.test.ts @@ -9,6 +9,8 @@ import { resolveActiveRepoContextSync, } from "@oh-my-pi/pi-coding-agent/utils/active-repo-context"; +const itWithSymlinkPrivilege = process.platform === "win32" ? it.skip : it; + function createGitDirectory(repoRoot: string): void { const gitDir = path.join(repoRoot, ".git"); fs.mkdirSync(gitDir, { recursive: true }); @@ -74,7 +76,7 @@ describe("resolveActiveRepoContext", () => { await expectResolvers(cwd, expected); }); - it("treats a direct child symlink to a repository directory as that child", async () => { + itWithSymlinkPrivilege("treats a direct child symlink to a repository directory as that child", async () => { const cwd = path.join(tempRoot, "workspace"); const targetRoot = path.join(tempRoot, "target-repo"); const repoRoot = path.join(cwd, "linked-repo");