diff --git a/src/web/chat/utils/tool-utils.ts b/src/web/chat/utils/tool-utils.ts index 4cbb324a..4f0640d4 100644 --- a/src/web/chat/utils/tool-utils.ts +++ b/src/web/chat/utils/tool-utils.ts @@ -6,18 +6,21 @@ * Convert absolute path to relative path if it's a subpath of working directory */ export function formatFilePath(path: string, workingDirectory?: string): string { - if (!path) { return path; } - - // If we have a working directory and the path starts with it, make it relative - if (workingDirectory && path.startsWith(workingDirectory)) { - const relativePath = path.slice(workingDirectory.length); - // Remove leading slash if present - const cleaned = relativePath.startsWith('/') ? relativePath.slice(1) : relativePath; - const result = cleaned ? `./${cleaned}` : './'; - return result; + + if (workingDirectory) { + const normalizedWorkingDir = workingDirectory.replace(/[\\/]+$/, ''); + const comparisonPath = path.replace(/[\\/]+$/, ''); + + const isSamePath = comparisonPath === normalizedWorkingDir; + const isSubPath = comparisonPath.startsWith(`${normalizedWorkingDir}/`) || comparisonPath.startsWith(`${normalizedWorkingDir}\\`); + + if (isSamePath || isSubPath) { + const relativePath = path.slice(normalizedWorkingDir.length).replace(/^[\\/]+/, ''); + return relativePath ? `./${relativePath}` : './'; + } } // If it starts with user home directory, show as ~ @@ -126,4 +129,4 @@ export function extractDomain(url: string): string { } catch (e) { return url; } -} \ No newline at end of file +} diff --git a/tests/unit/web/utils/tool-utils.test.ts b/tests/unit/web/utils/tool-utils.test.ts new file mode 100644 index 00000000..d56da8bf --- /dev/null +++ b/tests/unit/web/utils/tool-utils.test.ts @@ -0,0 +1,20 @@ +import { formatFilePath } from '@/web/chat/utils/tool-utils'; + +describe('formatFilePath', () => { + it('returns relative path when inside working directory', () => { + expect(formatFilePath('/home/user/project/src/index.ts', '/home/user/project')) + .toBe('./src/index.ts'); + }); + + it('does not collapse sibling directories that only share a prefix', () => { + expect(formatFilePath('/home/user/project-other/file.ts', '/home/user/project')) + .toBe('~/user/project-other/file.ts'); + }); + + it('handles windows-style paths with trailing slashes', () => { + expect(formatFilePath('C:/workspace/app/src/main.ts', 'C:/workspace/app/')) + .toBe('./src/main.ts'); + expect(formatFilePath('C:/workspace/app-logs/output.log', 'C:/workspace/app/')) + .toBe('C:/workspace/app-logs/output.log'); + }); +});