summaryrefslogtreecommitdiff
path: root/packages/core/src/tools/grep.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/core/src/tools/grep.ts')
-rw-r--r--packages/core/src/tools/grep.ts121
1 files changed, 83 insertions, 38 deletions
diff --git a/packages/core/src/tools/grep.ts b/packages/core/src/tools/grep.ts
index 177bd1aa..027ab1b1 100644
--- a/packages/core/src/tools/grep.ts
+++ b/packages/core/src/tools/grep.ts
@@ -92,22 +92,23 @@ export class GrepTool extends BaseTool<GrepToolParams, ToolResult> {
/**
* Checks if a path is within the root directory and resolves it.
* @param relativePath Path relative to the root directory (or undefined for root).
- * @returns The absolute path if valid and exists.
+ * @returns The absolute path if valid and exists, or null if no path specified (to search all directories).
* @throws {Error} If path is outside root, doesn't exist, or isn't a directory.
*/
- private resolveAndValidatePath(relativePath?: string): string {
- const targetPath = path.resolve(
- this.config.getTargetDir(),
- relativePath || '.',
- );
+ private resolveAndValidatePath(relativePath?: string): string | null {
+ // If no path specified, return null to indicate searching all workspace directories
+ if (!relativePath) {
+ return null;
+ }
+
+ const targetPath = path.resolve(this.config.getTargetDir(), relativePath);
- // Security Check: Ensure the resolved path is still within the root directory.
- if (
- !targetPath.startsWith(this.config.getTargetDir()) &&
- targetPath !== this.config.getTargetDir()
- ) {
+ // Security Check: Ensure the resolved path is within workspace boundaries
+ const workspaceContext = this.config.getWorkspaceContext();
+ if (!workspaceContext.isPathWithinWorkspace(targetPath)) {
+ const directories = workspaceContext.getDirectories();
throw new Error(
- `Path validation failed: Attempted path "${relativePath || '.'}" resolves outside the allowed root directory "${this.config.getTargetDir()}".`,
+ `Path validation failed: Attempted path "${relativePath}" resolves outside the allowed workspace directories: ${directories.join(', ')}`,
);
}
@@ -146,10 +147,13 @@ export class GrepTool extends BaseTool<GrepToolParams, ToolResult> {
return `Invalid regular expression pattern provided: ${params.pattern}. Error: ${getErrorMessage(error)}`;
}
- try {
- this.resolveAndValidatePath(params.path);
- } catch (error) {
- return getErrorMessage(error);
+ // Only validate path if one is provided
+ if (params.path) {
+ try {
+ this.resolveAndValidatePath(params.path);
+ } catch (error) {
+ return getErrorMessage(error);
+ }
}
return null; // Parameters are valid
@@ -174,44 +178,78 @@ export class GrepTool extends BaseTool<GrepToolParams, ToolResult> {
};
}
- let searchDirAbs: string;
try {
- searchDirAbs = this.resolveAndValidatePath(params.path);
+ const workspaceContext = this.config.getWorkspaceContext();
+ const searchDirAbs = this.resolveAndValidatePath(params.path);
const searchDirDisplay = params.path || '.';
- const matches: GrepMatch[] = await this.performGrepSearch({
- pattern: params.pattern,
- path: searchDirAbs,
- include: params.include,
- signal,
- });
+ // Determine which directories to search
+ let searchDirectories: readonly string[];
+ if (searchDirAbs === null) {
+ // No path specified - search all workspace directories
+ searchDirectories = workspaceContext.getDirectories();
+ } else {
+ // Specific path provided - search only that directory
+ searchDirectories = [searchDirAbs];
+ }
- if (matches.length === 0) {
- const noMatchMsg = `No matches found for pattern "${params.pattern}" in path "${searchDirDisplay}"${params.include ? ` (filter: "${params.include}")` : ''}.`;
+ // Collect matches from all search directories
+ let allMatches: GrepMatch[] = [];
+ for (const searchDir of searchDirectories) {
+ const matches = await this.performGrepSearch({
+ pattern: params.pattern,
+ path: searchDir,
+ include: params.include,
+ signal,
+ });
+
+ // Add directory prefix if searching multiple directories
+ if (searchDirectories.length > 1) {
+ const dirName = path.basename(searchDir);
+ matches.forEach((match) => {
+ match.filePath = path.join(dirName, match.filePath);
+ });
+ }
+
+ allMatches = allMatches.concat(matches);
+ }
+
+ let searchLocationDescription: string;
+ if (searchDirAbs === null) {
+ const numDirs = workspaceContext.getDirectories().length;
+ searchLocationDescription =
+ numDirs > 1
+ ? `across ${numDirs} workspace directories`
+ : `in the workspace directory`;
+ } else {
+ searchLocationDescription = `in path "${searchDirDisplay}"`;
+ }
+
+ if (allMatches.length === 0) {
+ const noMatchMsg = `No matches found for pattern "${params.pattern}" ${searchLocationDescription}${params.include ? ` (filter: "${params.include}")` : ''}.`;
return { llmContent: noMatchMsg, returnDisplay: `No matches found` };
}
- const matchesByFile = matches.reduce(
+ // Group matches by file
+ const matchesByFile = allMatches.reduce(
(acc, match) => {
- const relativeFilePath =
- path.relative(
- searchDirAbs,
- path.resolve(searchDirAbs, match.filePath),
- ) || path.basename(match.filePath);
- if (!acc[relativeFilePath]) {
- acc[relativeFilePath] = [];
+ const fileKey = match.filePath;
+ if (!acc[fileKey]) {
+ acc[fileKey] = [];
}
- acc[relativeFilePath].push(match);
- acc[relativeFilePath].sort((a, b) => a.lineNumber - b.lineNumber);
+ acc[fileKey].push(match);
+ acc[fileKey].sort((a, b) => a.lineNumber - b.lineNumber);
return acc;
},
{} as Record<string, GrepMatch[]>,
);
- const matchCount = matches.length;
+ const matchCount = allMatches.length;
const matchTerm = matchCount === 1 ? 'match' : 'matches';
- let llmContent = `Found ${matchCount} ${matchTerm} for pattern "${params.pattern}" in path "${searchDirDisplay}"${params.include ? ` (filter: "${params.include}")` : ''}:\n---\n`;
+ let llmContent = `Found ${matchCount} ${matchTerm} for pattern "${params.pattern}" ${searchLocationDescription}${params.include ? ` (filter: "${params.include}")` : ''}:
+---
+`;
for (const filePath in matchesByFile) {
llmContent += `File: ${filePath}\n`;
@@ -334,6 +372,13 @@ export class GrepTool extends BaseTool<GrepToolParams, ToolResult> {
);
description += ` within ${shortenPath(relativePath)}`;
}
+ } else {
+ // When no path is specified, indicate searching all workspace directories
+ const workspaceContext = this.config.getWorkspaceContext();
+ const directories = workspaceContext.getDirectories();
+ if (directories.length > 1) {
+ description += ` across all workspace directories`;
+ }
}
return description;
}