diff --git a/src/main/utils/pathDecoder.ts b/src/main/utils/pathDecoder.ts index 077d5cde..5257b748 100644 --- a/src/main/utils/pathDecoder.ts +++ b/src/main/utils/pathDecoder.ts @@ -47,6 +47,16 @@ export function decodePath(encodedName: string): string { return ''; } + // Legacy Windows format observed in some Claude installs: "C--Users-name-project" + // (no leading dash, drive separator encoded as "--"). + const legacyWindowsRegex = /^([a-zA-Z])--(.+)$/; + const legacyWindowsMatch = legacyWindowsRegex.exec(encodedName); + if (legacyWindowsMatch) { + const drive = legacyWindowsMatch[1].toUpperCase(); + const rest = legacyWindowsMatch[2].replace(/-/g, '/'); + return `${drive}:/${rest}`; + } + // Remove leading dash if present (indicates absolute path) const withoutLeadingDash = encodedName.startsWith('-') ? encodedName.slice(1) : encodedName; @@ -96,6 +106,12 @@ export function isValidEncodedPath(encodedName: string): boolean { return false; } + // Support legacy Windows format: "C--Users-name-project" + // (no leading dash, drive separator encoded as "--"). + if (/^[a-zA-Z]--[a-zA-Z0-9_.\s-]+$/.test(encodedName)) { + return true; + } + // Must start with a dash (indicates absolute path) if (!encodedName.startsWith('-')) { return false; diff --git a/test/main/ipc/guards.test.ts b/test/main/ipc/guards.test.ts index 6af43df5..e6b264dd 100644 --- a/test/main/ipc/guards.test.ts +++ b/test/main/ipc/guards.test.ts @@ -21,6 +21,12 @@ describe('ipc guards', () => { expect(result.value).toBe('-C:-Users-test-project'); }); + it('accepts legacy Windows-style encoded project IDs', () => { + const result = validateProjectId('C--Users-test-project'); + expect(result.valid).toBe(true); + expect(result.value).toBe('C--Users-test-project'); + }); + it('rejects invalid project IDs', () => { const result = validateProjectId('../escape'); expect(result.valid).toBe(false); diff --git a/test/main/utils/pathDecoder.test.ts b/test/main/utils/pathDecoder.test.ts index c0a4e50f..aba00925 100644 --- a/test/main/utils/pathDecoder.test.ts +++ b/test/main/utils/pathDecoder.test.ts @@ -75,6 +75,10 @@ describe('pathDecoder', () => { it('should decode Windows-style encoded path without adding leading slash', () => { expect(decodePath('-C:-Users-username-projectname')).toBe('C:/Users/username/projectname'); }); + + it('should decode legacy Windows-style encoded path without leading dash', () => { + expect(decodePath('C--Users-username-projectname')).toBe('C:/Users/username/projectname'); + }); }); describe('extractProjectName', () => { @@ -145,6 +149,10 @@ describe('pathDecoder', () => { expect(isValidEncodedPath('-C:-Users-username-projectname')).toBe(true); }); + it('should return true for legacy Windows-style encoded path', () => { + expect(isValidEncodedPath('C--Users-username-projectname')).toBe(true); + }); + it('should return false for misplaced colons', () => { expect(isValidEncodedPath('-Users-username:project')).toBe(false); expect(isValidEncodedPath('-C:-Users-name-project:extra')).toBe(false);