diff --git a/src/services/code-index/processors/__tests__/file-watcher.spec.ts b/src/services/code-index/processors/__tests__/file-watcher.spec.ts index 2a3b7e11677..41e050da1ef 100644 --- a/src/services/code-index/processors/__tests__/file-watcher.spec.ts +++ b/src/services/code-index/processors/__tests__/file-watcher.spec.ts @@ -275,6 +275,46 @@ describe("FileWatcher", () => { expect(processedFiles).not.toContain(".hidden/src/components/Button.tsx") expect(processedFiles).not.toContain("src/components/.hidden/Button.tsx") }) + + it("should process files when workspace is under hidden parent directory", async () => { + // Mock parseFile to return blocks for this test + const { codeParser } = await import("../parser") + vi.mocked(codeParser.parseFile).mockResolvedValue([ + { + file_path: "/Users/test/.config/project/src/file.ts", + content: "test content", + start_line: 1, + end_line: 5, + identifier: "test", + type: "function", + fileHash: "hash", + segmentHash: "segment-hash", + } as any, + ]) + + // Create a new watcher instance with workspace under hidden parent + const watcherUnderHidden = new FileWatcher( + "/Users/test/.config/project", + mockContext, + mockCacheManager, + mockEmbedder, + mockVectorStore, + ) + + await watcherUnderHidden.initialize() + + // Capture the event handler for the new watcher instance + const createHandler = mockWatcher.onDidCreate.mock.calls[mockWatcher.onDidCreate.mock.calls.length - 1][0] + + // Simulate file creation using the captured handler + await createHandler({ fsPath: "/Users/test/.config/project/src/file.ts" }) + + // Wait for batch processing + await new Promise((resolve) => setTimeout(resolve, 600)) + + // File should be processed, not filtered out + expect(mockVectorStore.upsertPoints).toHaveBeenCalled() + }) }) describe("dispose", () => { diff --git a/src/services/code-index/processors/__tests__/scanner.spec.ts b/src/services/code-index/processors/__tests__/scanner.spec.ts index 4d4150b4439..bd60273edd2 100644 --- a/src/services/code-index/processors/__tests__/scanner.spec.ts +++ b/src/services/code-index/processors/__tests__/scanner.spec.ts @@ -259,6 +259,32 @@ describe("DirectoryScanner", () => { expect(mockCodeParser.parseFile).toHaveBeenCalledTimes(2) }) + it("should process files when workspace is under hidden parent directory", async () => { + const { listFiles } = await import("../../../glob/list-files") + // Simulate workspace at /Users/test/.root-project + vi.mocked(listFiles).mockResolvedValue([["src/file1.js"], false]) + + const mockBlocks: any[] = [ + { + file_path: "src/file1.js", + content: "test content", + start_line: 1, + end_line: 5, + identifier: "test", + type: "function", + fileHash: "hash", + segmentHash: "segment-hash", + }, + ] + ;(mockCodeParser.parseFile as any).mockResolvedValue(mockBlocks) + + const result = await scanner.scanDirectory("/Users/test/.root-project") + + // File should be processed, not filtered out + expect(result.stats.processed).toBe(1) + expect(mockCodeParser.parseFile).toHaveBeenCalledWith("src/file1.js", expect.any(Object)) + }) + it("should process markdown files alongside code files", async () => { // Create scanner without embedder to test the non-embedding path const scannerNoEmbeddings = new DirectoryScanner( diff --git a/src/services/code-index/processors/file-watcher.ts b/src/services/code-index/processors/file-watcher.ts index 1e5ebcbcebc..cf2c5f6df2a 100644 --- a/src/services/code-index/processors/file-watcher.ts +++ b/src/services/code-index/processors/file-watcher.ts @@ -508,8 +508,12 @@ export class FileWatcher implements IFileWatcher { */ async processFile(filePath: string): Promise { try { + // Calculate relative path for ignore checks + const relativeFilePath = generateRelativeFilePath(filePath, this.workspacePath) + // Check if file is in an ignored directory - if (isPathInIgnoredDirectory(filePath)) { + // Use relative path to avoid filtering out workspaces under hidden parent directories + if (isPathInIgnoredDirectory(relativeFilePath)) { return { path: filePath, status: "skipped" as const, @@ -518,7 +522,6 @@ export class FileWatcher implements IFileWatcher { } // Check if file should be ignored - const relativeFilePath = generateRelativeFilePath(filePath, this.workspacePath) if ( !this.ignoreController.validateAccess(filePath) || (this.ignoreInstance && this.ignoreInstance.ignores(relativeFilePath)) diff --git a/src/services/code-index/processors/scanner.ts b/src/services/code-index/processors/scanner.ts index 92a7d77c272..97deb8362d3 100644 --- a/src/services/code-index/processors/scanner.ts +++ b/src/services/code-index/processors/scanner.ts @@ -96,7 +96,8 @@ export class DirectoryScanner implements IDirectoryScanner { const relativeFilePath = generateRelativeFilePath(filePath, scanWorkspace) // Check if file is in an ignored directory using the shared helper - if (isPathInIgnoredDirectory(filePath)) { + // Use relative path to avoid filtering out workspaces under hidden parent directories + if (isPathInIgnoredDirectory(relativeFilePath)) { return false }