From 8916cd742e8af2ff9b2373e1effe7eaa575edb29 Mon Sep 17 00:00:00 2001 From: Akhilesh Kumar Date: Thu, 21 May 2026 16:55:19 +0000 Subject: [PATCH] fix(core): resolve symlinks when normalizing project paths Project registry identity was based on path.resolve() which does not follow symlinks. This caused different symlink paths to the same physical directory to be treated as different projects, leading to separate session stores. Fixed by using resolveToRealPath() which correctly resolves symbolic links. Closes #27278 --- packages/core/src/config/projectRegistry.test.ts | 16 ++++++++++++++++ packages/core/src/config/projectRegistry.ts | 3 ++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/packages/core/src/config/projectRegistry.test.ts b/packages/core/src/config/projectRegistry.test.ts index 84d266b278..d3201133a6 100644 --- a/packages/core/src/config/projectRegistry.test.ts +++ b/packages/core/src/config/projectRegistry.test.ts @@ -435,4 +435,20 @@ describe('ProjectRegistry', () => { expect(data.projects[normalizePath(projectPath)]).toBe('my-project'); expect(Object.values(data.projects)).not.toContain('../../etc/passwd'); }); + + it('resolves symlinks to the same short ID', async () => { + const registry = new ProjectRegistry(registryPath); + await registry.initialize(); + + const realDir = path.join(tempDir, 'real-project'); + fs.mkdirSync(realDir); + + const symlinkDir = path.join(tempDir, 'symlink-project'); + fs.symlinkSync(realDir, symlinkDir, 'dir'); + + const id1 = await registry.getShortId(realDir); + const id2 = await registry.getShortId(symlinkDir); + + expect(id1).toBe(id2); + }); }); diff --git a/packages/core/src/config/projectRegistry.ts b/packages/core/src/config/projectRegistry.ts index 88c49ac3af..b50ee61cc2 100644 --- a/packages/core/src/config/projectRegistry.ts +++ b/packages/core/src/config/projectRegistry.ts @@ -12,6 +12,7 @@ import { lock } from 'proper-lockfile'; import { z } from 'zod'; import { debugLogger } from '../utils/debugLogger.js'; import { isNodeError } from '../utils/errors.js'; +import { resolveToRealPath } from '../utils/paths.js'; export interface RegistryData { projects: Record; @@ -93,7 +94,7 @@ export class ProjectRegistry { } private normalizePath(projectPath: string): string { - let resolved = path.resolve(projectPath); + let resolved = resolveToRealPath(projectPath); if (os.platform() === 'win32') { resolved = resolved.toLowerCase(); }