diff options
| author | DeWitt Clinton <[email protected]> | 2025-05-21 12:22:18 -0700 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-05-21 12:22:18 -0700 |
| commit | 01c28df8b2bcdb34d5a4ee5f330bbb8d1df0b334 (patch) | |
| tree | 299bee70aa5f1575dbb408a6be08595b65c68baf /packages/cli/src/ui/hooks/atCommandProcessor.ts | |
| parent | e1a64b41e854182991cac265ba3c0335e500918c (diff) | |
Add globbing support to @-command file suggestions and resolution. (#462)
Implements recursive glob-based file search for both suggestions and execution of the `@` command.
- When typing `@filename`, suggestions will now include files matching `filename` in nested directories.
- Suggestions are sorted by path depth (shallowest first), then directories before files, then alphabetically.
- The maximum recursion depth for suggestions is set to 10.
- When executing an `@filename` command, if the file is not found directly, a recursive search (using the glob tool) is performed to locate the file.
This addresses the first request in issue #461 by allowing users to quickly reference deeply nested files without typing the full path. Also addresses b/416292478.
Diffstat (limited to 'packages/cli/src/ui/hooks/atCommandProcessor.ts')
| -rw-r--r-- | packages/cli/src/ui/hooks/atCommandProcessor.ts | 63 |
1 files changed, 60 insertions, 3 deletions
diff --git a/packages/cli/src/ui/hooks/atCommandProcessor.ts b/packages/cli/src/ui/hooks/atCommandProcessor.ts index a5b602ad..dd97a0d6 100644 --- a/packages/cli/src/ui/hooks/atCommandProcessor.ts +++ b/packages/cli/src/ui/hooks/atCommandProcessor.ts @@ -150,10 +150,67 @@ export async function handleAtCommand({ onDebugMessage(`Path resolved to file: ${pathSpec}`); } } catch (error) { - // If stat fails (e.g., not found), proceed with original path. - // The tool itself will handle the error during execution. if (isNodeError(error) && error.code === 'ENOENT') { - onDebugMessage(`Path not found, proceeding with original: ${pathSpec}`); + onDebugMessage( + `Path ${pathPart} not found directly, attempting glob search.`, + ); + const globTool = toolRegistry.getTool('glob'); + if (globTool) { + try { + const globResult = await globTool.execute( + { + pattern: `**/*${pathPart}*`, + path: config.getTargetDir(), // Ensure glob searches from the root + }, + signal, + ); + // Assuming llmContent contains the list of files or a "no files found" message. + // And that paths are absolute. + if ( + globResult.llmContent && + typeof globResult.llmContent === 'string' && + !globResult.llmContent.startsWith('No files found') && + !globResult.llmContent.startsWith('Error:') + ) { + // Extract the first line after the header + const lines = globResult.llmContent.split('\n'); + if (lines.length > 1 && lines[1]) { + const firstMatchAbsolute = lines[1].trim(); + // Convert absolute path from glob to relative path for read_many_files + pathSpec = path.relative( + config.getTargetDir(), + firstMatchAbsolute, + ); + onDebugMessage( + `Glob search found ${firstMatchAbsolute}, using relative path: ${pathSpec}`, + ); + } else { + onDebugMessage( + `Glob search for '**/*${pathPart}*' did not return a usable path. Proceeding with original: ${pathPart}`, + ); + // pathSpec remains pathPart + } + } else { + onDebugMessage( + `Glob search for '**/*${pathPart}*' found no files or an error occurred. Proceeding with original: ${pathPart}`, + ); + // pathSpec remains pathPart + } + } catch (globError) { + console.error( + `Error during glob search: ${getErrorMessage(globError)}`, + ); + onDebugMessage( + `Error during glob search. Proceeding with original: ${pathPart}`, + ); + // pathSpec remains pathPart + } + } else { + onDebugMessage( + 'Glob tool not found. Proceeding with original path: ${pathPart}', + ); + // pathSpec remains pathPart + } } else { console.error( `Error stating path ${pathPart}: ${getErrorMessage(error)}`, |
