(
filePattern: string,
cwd: string,
{ limit, offset }: { limit: number; offset: number },
abortSignal: AbortSignal,
toolPermissionContext: ToolPermissionContext,
)
| 251 | } |
| 252 | |
| 253 | export async function glob( |
| 254 | filePattern: string, |
| 255 | cwd: string, |
| 256 | { limit, offset }: { limit: number; offset: number }, |
| 257 | abortSignal: AbortSignal, |
| 258 | toolPermissionContext: ToolPermissionContext, |
| 259 | ): Promise<{ files: string[]; truncated: boolean }> { |
| 260 | let searchDir = cwd |
| 261 | let searchPattern = filePattern |
| 262 | |
| 263 | // Narrow the search root for both absolute and relative patterns. |
| 264 | // ripgrep's --glob flag only matches paths relative to the search dir. |
| 265 | const { baseDir, relativePattern } = extractGlobBaseDirectory(filePattern) |
| 266 | if (baseDir) { |
| 267 | searchDir = isAbsolute(baseDir) ? baseDir : join(cwd, baseDir) |
| 268 | searchPattern = relativePattern |
| 269 | } else if (isAbsolute(filePattern)) { |
| 270 | searchDir = dirname(filePattern) |
| 271 | searchPattern = basename(filePattern) |
| 272 | } |
| 273 | |
| 274 | const ignorePatterns = normalizePatternsToPath( |
| 275 | getFileReadIgnorePatterns(toolPermissionContext), |
| 276 | searchDir, |
| 277 | ) |
| 278 | const pluginExclusions = await getGlobExclusionsForPluginCache(searchDir) |
| 279 | |
| 280 | let allPaths: string[] |
| 281 | if ( |
| 282 | isDirectoryGlobPattern(filePattern) || |
| 283 | (await isExactDirectoryPattern(searchPattern, searchDir)) |
| 284 | ) { |
| 285 | allPaths = await globDirectories( |
| 286 | searchPattern.endsWith('/') ? searchPattern : `${searchPattern}/`, |
| 287 | searchDir, |
| 288 | [...ignorePatterns, ...pluginExclusions], |
| 289 | abortSignal, |
| 290 | ) |
| 291 | } else { |
| 292 | if (isRecursiveLiteralDirectoryNamePattern(searchPattern)) { |
| 293 | const directoryMatches = await globDirectories( |
| 294 | `${searchPattern}/`, |
| 295 | searchDir, |
| 296 | [...ignorePatterns, ...pluginExclusions], |
| 297 | abortSignal, |
| 298 | ) |
| 299 | if (directoryMatches.length > 0) { |
| 300 | const truncated = directoryMatches.length > offset + limit |
| 301 | const files = directoryMatches.slice(offset, offset + limit) |
| 302 | return { files, truncated } |
| 303 | } |
| 304 | } |
| 305 | |
| 306 | // Use ripgrep for better memory performance |
| 307 | // --files: list files instead of searching content |
| 308 | // --sort=modified: sort by modification time (oldest first) |
| 309 | // --no-ignore: don't respect .gitignore (default false; set CLAUDE_CODE_GLOB_NO_IGNORE=true to include ignored files) |
| 310 | // --hidden: include hidden files (default true, set CLAUDE_CODE_GLOB_HIDDEN=false to exclude) |
no test coverage detected