Fix: Prevent hang in large directories by using BFS for getFolderStru… (#470)

Co-authored-by: N. Taylor Mullen <ntaylormullen@google.com>
This commit is contained in:
Allen Hutchison
2025-05-22 10:47:21 -07:00
committed by GitHub
parent 7eaf850489
commit 0c192555bb
5 changed files with 500 additions and 252 deletions

View File

@@ -130,8 +130,22 @@ async function collectDownwardGeminiFiles(
directory: string,
debugMode: boolean,
ignoreDirs: string[],
scannedDirCount: { count: number },
maxScanDirs: number,
): Promise<string[]> {
if (debugMode) logger.debug(`Recursively scanning downward in: ${directory}`);
if (scannedDirCount.count >= maxScanDirs) {
if (debugMode)
logger.debug(
`Max directory scan limit (${maxScanDirs}) reached. Stopping downward scan at: ${directory}`,
);
return [];
}
scannedDirCount.count++;
if (debugMode)
logger.debug(
`Scanning downward for ${GEMINI_MD_FILENAME} files in: ${directory} (scanned: ${scannedDirCount.count}/${maxScanDirs})`,
);
const collectedPaths: string[] = [];
try {
const entries = await fs.readdir(directory, { withFileTypes: true });
@@ -147,6 +161,8 @@ async function collectDownwardGeminiFiles(
fullPath,
debugMode,
ignoreDirs,
scannedDirCount,
maxScanDirs,
);
collectedPaths.push(...subDirPaths);
} else if (entry.isFile() && entry.name === GEMINI_MD_FILENAME) {
@@ -154,11 +170,13 @@ async function collectDownwardGeminiFiles(
await fs.access(fullPath, fsSync.constants.R_OK);
collectedPaths.push(fullPath);
if (debugMode)
logger.debug(`Found readable downward GEMINI.md: ${fullPath}`);
logger.debug(
`Found readable downward ${GEMINI_MD_FILENAME}: ${fullPath}`,
);
} catch {
if (debugMode)
logger.debug(
`Downward GEMINI.md not readable, skipping: ${fullPath}`,
`Downward ${GEMINI_MD_FILENAME} not readable, skipping: ${fullPath}`,
);
}
}
@@ -186,18 +204,22 @@ export async function getGeminiMdFilePaths(
const paths: string[] = [];
if (debugMode)
logger.debug(`Searching for GEMINI.md starting from CWD: ${resolvedCwd}`);
logger.debug(
`Searching for ${GEMINI_MD_FILENAME} starting from CWD: ${resolvedCwd}`,
);
if (debugMode) logger.debug(`User home directory: ${resolvedHome}`);
try {
await fs.access(globalMemoryPath, fsSync.constants.R_OK);
paths.push(globalMemoryPath);
if (debugMode)
logger.debug(`Found readable global GEMINI.md: ${globalMemoryPath}`);
logger.debug(
`Found readable global ${GEMINI_MD_FILENAME}: ${globalMemoryPath}`,
);
} catch {
if (debugMode)
logger.debug(
`Global GEMINI.md not found or not readable: ${globalMemoryPath}`,
`Global ${GEMINI_MD_FILENAME} not found or not readable: ${globalMemoryPath}`,
);
}
@@ -215,7 +237,9 @@ export async function getGeminiMdFilePaths(
currentDir !== path.dirname(currentDir)
) {
if (debugMode)
logger.debug(`Checking for GEMINI.md in (upward scan): ${currentDir}`);
logger.debug(
`Checking for ${GEMINI_MD_FILENAME} in (upward scan): ${currentDir}`,
);
if (currentDir === path.join(resolvedHome, GEMINI_CONFIG_DIR)) {
if (debugMode)
logger.debug(`Skipping check inside global config dir: ${currentDir}`);
@@ -226,11 +250,13 @@ export async function getGeminiMdFilePaths(
await fs.access(potentialPath, fsSync.constants.R_OK);
upwardPaths.unshift(potentialPath);
if (debugMode)
logger.debug(`Found readable upward GEMINI.md: ${potentialPath}`);
logger.debug(
`Found readable upward ${GEMINI_MD_FILENAME}: ${potentialPath}`,
);
} catch {
if (debugMode)
logger.debug(
`Upward GEMINI.md not found or not readable in: ${currentDir}`,
`Upward ${GEMINI_MD_FILENAME} not found or not readable in: ${currentDir}`,
);
}
const parentDir = path.dirname(currentDir);
@@ -245,15 +271,19 @@ export async function getGeminiMdFilePaths(
if (debugMode)
logger.debug(`Starting downward scan from CWD: ${resolvedCwd}`);
const MAX_DIRECTORIES_TO_SCAN_FOR_MEMORY = 200; // Define the cap
const scannedDirCount = { count: 0 };
const downwardPaths = await collectDownwardGeminiFiles(
resolvedCwd,
debugMode,
DEFAULT_IGNORE_DIRECTORIES,
scannedDirCount,
MAX_DIRECTORIES_TO_SCAN_FOR_MEMORY,
);
downwardPaths.sort();
if (debugMode && downwardPaths.length > 0)
logger.debug(
`Found downward GEMINI.md files (sorted): ${JSON.stringify(downwardPaths)}`,
`Found downward ${GEMINI_MD_FILENAME} files (sorted): ${JSON.stringify(downwardPaths)}`,
);
for (const dPath of downwardPaths) {
if (!paths.includes(dPath)) {
@@ -263,7 +293,7 @@ export async function getGeminiMdFilePaths(
if (debugMode)
logger.debug(
`Final ordered GEMINI.md paths to read: ${JSON.stringify(paths)}`,
`Final ordered ${GEMINI_MD_FILENAME} paths to read: ${JSON.stringify(paths)}`,
);
return paths;
}
@@ -289,7 +319,7 @@ async function readGeminiMdFiles(
} catch (error: unknown) {
const message = error instanceof Error ? error.message : String(error);
logger.warn(
`Warning: Could not read GEMINI.md file at ${filePath}. Error: ${message}`,
`Warning: Could not read ${GEMINI_MD_FILENAME} file at ${filePath}. Error: ${message}`,
);
results.push({ filePath, content: null });
if (debugMode) logger.debug(`Failed to read: ${filePath}`);