From 5a0e4c474f82761d1b9edfebb71520955cd2f1e5 Mon Sep 17 00:00:00 2001 From: matt Date: Wed, 11 Feb 2026 20:12:25 +0900 Subject: [PATCH] Refactor extractProjectName to accept cwdHint for improved project name extraction. Update ErrorTriggerChecker to utilize new parameter, ensuring accurate project name retrieval in various contexts. Enhance tests to cover new functionality and edge cases. --- src/main/services/error/ErrorTriggerChecker.ts | 8 ++++---- src/main/utils/pathDecoder.ts | 9 ++++++++- .../components/notifications/NotificationRow.tsx | 1 + test/main/utils/pathDecoder.test.ts | 13 +++++++++++++ 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/main/services/error/ErrorTriggerChecker.ts b/src/main/services/error/ErrorTriggerChecker.ts index 9ddbf3de..4887ee6d 100644 --- a/src/main/services/error/ErrorTriggerChecker.ts +++ b/src/main/services/error/ErrorTriggerChecker.ts @@ -185,7 +185,7 @@ export function checkToolResultTrigger( sessionId, projectId, filePath, - projectName: extractProjectName(projectId), + projectName: extractProjectName(projectId, message.cwd), lineNumber, source: result.toolName ?? 'tool_result', message: errorMessage, @@ -220,7 +220,7 @@ export function checkToolResultTrigger( sessionId, projectId, filePath, - projectName: extractProjectName(projectId), + projectName: extractProjectName(projectId, message.cwd), lineNumber, source: trigger.toolName, message: `Tool result matched: ${content.slice(0, 200)}`, @@ -296,7 +296,7 @@ export function checkToolUseTrigger( sessionId, projectId, filePath, - projectName: extractProjectName(projectId), + projectName: extractProjectName(projectId, message.cwd), lineNumber, source: toolUse.name, message: `${trigger.matchField ?? 'tool_use'}: ${fieldValue.slice(0, 200)}`, @@ -443,7 +443,7 @@ export function checkTokenThresholdTrigger( sessionId, projectId, filePath, - projectName: extractProjectName(projectId), + projectName: extractProjectName(projectId, message.cwd), lineNumber, source: toolUse.name, message: tokenMessage, diff --git a/src/main/utils/pathDecoder.ts b/src/main/utils/pathDecoder.ts index fea1038b..7faebb2b 100644 --- a/src/main/utils/pathDecoder.ts +++ b/src/main/utils/pathDecoder.ts @@ -68,7 +68,14 @@ export function decodePath(encodedName: string): string { * @param encodedName - The encoded directory name * @returns The project name */ -export function extractProjectName(encodedName: string): string { +export function extractProjectName(encodedName: string, cwdHint?: string): string { + // Prefer cwdHint (actual filesystem path) since decodePath is lossy for + // paths containing dashes (e.g., "claude-code-context" → "claude/code/context"). + if (cwdHint) { + const segments = cwdHint.split(/[/\\]/).filter(Boolean); + const last = segments[segments.length - 1]; + if (last) return last; + } const decoded = decodePath(encodedName); const segments = decoded.split('/').filter(Boolean); return segments[segments.length - 1] || encodedName; diff --git a/src/renderer/components/notifications/NotificationRow.tsx b/src/renderer/components/notifications/NotificationRow.tsx index 69bf8a4e..950d96f1 100644 --- a/src/renderer/components/notifications/NotificationRow.tsx +++ b/src/renderer/components/notifications/NotificationRow.tsx @@ -74,6 +74,7 @@ export const NotificationRow = ({ style={{ borderColor: 'var(--color-border)', backgroundColor: isHovered ? 'var(--color-surface-raised)' : undefined, + opacity: isUnread ? 1 : 0.5, }} > {/* Color Dot — always visible, opacity indicates read state */} diff --git a/test/main/utils/pathDecoder.test.ts b/test/main/utils/pathDecoder.test.ts index d3e4d178..c7e7d293 100644 --- a/test/main/utils/pathDecoder.test.ts +++ b/test/main/utils/pathDecoder.test.ts @@ -96,6 +96,19 @@ describe('pathDecoder', () => { it('should handle path with underscore in project name', () => { expect(extractProjectName('-Users-username-my_cool_projectname')).toBe('my_cool_projectname'); }); + + it('should prefer cwdHint over lossy decode for dashed project names', () => { + // Without cwdHint, dashes are decoded as slashes (lossy) + expect(extractProjectName('-Users-name-claude-code-context')).toBe('context'); + // With cwdHint, the actual project name is preserved + expect( + extractProjectName('-Users-name-claude-code-context', '/Users/name/claude-code-context') + ).toBe('claude-code-context'); + }); + + it('should fall back to decoded name when cwdHint is undefined', () => { + expect(extractProjectName('-Users-username-projectname', undefined)).toBe('projectname'); + }); }); describe('isValidEncodedPath', () => {