From 1024c373201c718509ecec7345ca6761897ed41c Mon Sep 17 00:00:00 2001 From: Murilo Date: Fri, 27 Jun 2025 00:33:08 -0300 Subject: [PATCH] The `list_files` tool with `recursive: true` was returning empty results when targeting directories that start with a dot (e.g., `.roo-memory`). This happened because the recursive mode was applying a blanket exclusion pattern `!**/.*//**` that excluded all files inside any hidden directory, even when the user explicitly requested to list that directory. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `src/services/glob/list-files.ts` - **Modified `buildRecursiveArgs()`**: Removed the problematic `!**/.*//**` exclusion pattern for hidden directories in recursive mode - **Enhanced `listFilteredDirectories()`**: Added `isTargetDir` parameter to distinguish between explicitly targeted directories and discovered subdirectories - **Updated `shouldIncludeDirectory()`**: Always include explicitly targeted directories (even if hidden), while still applying ignore rules to subdirectories found during traversal - **Before**: `list_files` with `path: ".roo-memory"` and `recursive: true` → Empty results - **After**: `list_files` with `path: ".roo-memory"` and `recursive: true` → Returns directory contents - **Preserved**: Hidden subdirectories discovered during traversal are still filtered out This maintains consistency with `.gitignore` and `.rooignore` mechanisms while ensuring explicitly targeted directories are always processed. Fixes #2992 --- src/services/glob/list-files.ts | 35 ++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/src/services/glob/list-files.ts b/src/services/glob/list-files.ts index 05fa8a1d7b..e7e78511d2 100644 --- a/src/services/glob/list-files.ts +++ b/src/services/glob/list-files.ts @@ -209,6 +209,14 @@ function buildRecursiveArgs(): string[] { // Apply directory exclusions for recursive searches for (const dir of DIRS_TO_IGNORE) { + // Special handling for hidden directories pattern + if (dir === ".*") { + // Don't exclude hidden directories at the ripgrep level for recursive mode + // This allows explicitly targeted hidden directories to be processed + // Hidden subdirectories will be filtered during directory traversal instead + continue + } + args.push("-g", `!**/${dir}/**`) } @@ -310,9 +318,10 @@ async function listFilteredDirectories( ignoreInstance: ReturnType, ): Promise { const absolutePath = path.resolve(dirPath) + const targetDirPath = absolutePath // Store the explicitly targeted directory const directories: string[] = [] - async function scanDirectory(currentPath: string): Promise { + async function scanDirectory(currentPath: string, isTargetDir: boolean = false): Promise { try { // List all entries in the current directory const entries = await fs.promises.readdir(currentPath, { withFileTypes: true }) @@ -324,14 +333,14 @@ async function listFilteredDirectories( const fullDirPath = path.join(currentPath, dirName) // Check if this directory should be included - if (shouldIncludeDirectory(dirName, fullDirPath, dirPath, ignoreInstance)) { + if (shouldIncludeDirectory(dirName, fullDirPath, dirPath, ignoreInstance, isTargetDir)) { // Add the directory to our results (with trailing slash) const formattedPath = fullDirPath.endsWith("/") ? fullDirPath : `${fullDirPath}/` directories.push(formattedPath) // If recursive mode and not a ignored directory, scan subdirectories if (recursive && !isDirectoryExplicitlyIgnored(dirName)) { - await scanDirectory(fullDirPath) + await scanDirectory(fullDirPath, false) // Subdirectories are not target dirs } } } @@ -342,8 +351,8 @@ async function listFilteredDirectories( } } - // Start scanning from the root directory - await scanDirectory(absolutePath) + // Start scanning from the root directory - this is the explicitly targeted directory + await scanDirectory(absolutePath, true) return directories } @@ -356,7 +365,23 @@ function shouldIncludeDirectory( fullDirPath: string, basePath: string, ignoreInstance: ReturnType, + isTargetDir: boolean = false, ): boolean { + // If this is the explicitly targeted directory, always include it + // (unless it's explicitly ignored by name, not by the .* pattern) + if (isTargetDir) { + // Only apply non-hidden-directory ignore rules to target directories + const nonHiddenIgnorePatterns = DIRS_TO_IGNORE.filter((pattern) => pattern !== ".*") + for (const pattern of nonHiddenIgnorePatterns) { + if (pattern === dirName || (pattern.includes("/") && pattern.split("/")[0] === dirName)) { + return false + } + } + return true + } + + // For non-target directories (subdirectories found during traversal), apply all ignore rules + // Skip hidden directories if configured to ignore them if (dirName.startsWith(".") && DIRS_TO_IGNORE.includes(".*")) { return false