diff options
| author | Evan Senter <[email protected]> | 2025-04-19 19:45:42 +0100 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-04-19 19:45:42 +0100 |
| commit | 3fce6cea27d3e6129d6c06e528b62e1b11bf7094 (patch) | |
| tree | 244b8e9ab94f902d65d4bda8739a6538e377ed17 /packages/cli/src/tools/glob.tool.ts | |
| parent | 0c9e1ef61be7db53e6e73b7208b649cd8cbed6c3 (diff) | |
Starting to modularize into separate cli / server packages. (#55)
* Starting to move a lot of code into packages/server
* More of the massive refactor, builds and runs, some issues though.
* Fixing outstanding issue with double messages.
* Fixing a minor UI issue.
* Fixing the build post-merge.
* Running formatting.
* Addressing comments.
Diffstat (limited to 'packages/cli/src/tools/glob.tool.ts')
| -rw-r--r-- | packages/cli/src/tools/glob.tool.ts | 251 |
1 files changed, 38 insertions, 213 deletions
diff --git a/packages/cli/src/tools/glob.tool.ts b/packages/cli/src/tools/glob.tool.ts index b846db46..8a56d51b 100644 --- a/packages/cli/src/tools/glob.tool.ts +++ b/packages/cli/src/tools/glob.tool.ts @@ -4,247 +4,72 @@ * SPDX-License-Identifier: Apache-2.0 */ -import fs from 'fs'; -import path from 'path'; -import fg from 'fast-glob'; -import { SchemaValidator } from '../utils/schemaValidator.js'; -import { BaseTool, ToolResult } from './tools.js'; -import { shortenPath, makeRelative } from '../utils/paths.js'; +// Import core logic and types from the server package +import { GlobLogic, GlobToolParams, ToolResult } from '@gemini-code/server'; -/** - * Parameters for the GlobTool - */ -export interface GlobToolParams { - /** - * The glob pattern to match files against - */ - pattern: string; - - /** - * The directory to search in (optional, defaults to current directory) - */ - path?: string; -} +// Import CLI-specific base class and types +import { BaseTool } from './tools.js'; +import { ToolCallConfirmationDetails } from '../ui/types.js'; /** - * Implementation of the GlobTool that finds files matching patterns, - * sorted by modification time (newest first). + * CLI wrapper for the Glob tool */ export class GlobTool extends BaseTool<GlobToolParams, ToolResult> { - /** - * The root directory that this tool is grounded in. - * All file operations will be restricted to this directory. - */ - private rootDirectory: string; + static readonly Name: string = GlobLogic.Name; // Use name from logic + + // Core logic instance from the server package + private coreLogic: GlobLogic; /** - * Creates a new instance of the GlobTool - * @param rootDirectory Root directory to ground this tool in. All operations will be restricted to this directory. + * Creates a new instance of the GlobTool CLI wrapper + * @param rootDirectory Root directory to ground this tool in. */ constructor(rootDirectory: string) { + // Instantiate the core logic from the server package + const coreLogicInstance = new GlobLogic(rootDirectory); + + // Initialize the CLI BaseTool super( - 'glob', - 'FindFiles', - 'Efficiently finds files matching specific glob patterns (e.g., `src/**/*.ts`, `**/*.md`), returning absolute paths sorted by modification time (newest first). Ideal for quickly locating files based on their name or path structure, especially in large codebases.', - { - properties: { - pattern: { - description: - "The glob pattern to match against (e.g., '*.py', 'src/**/*.js', 'docs/*.md').", - type: 'string', - }, - path: { - description: - 'Optional: The absolute path to the directory to search within. If omitted, searches the root directory.', - type: 'string', - }, - }, - required: ['pattern'], - type: 'object', - }, + GlobTool.Name, + 'FindFiles', // Define display name here + 'Efficiently finds files matching specific glob patterns (e.g., `src/**/*.ts`, `**/*.md`), returning absolute paths sorted by modification time (newest first). Ideal for quickly locating files based on their name or path structure, especially in large codebases.', // Define description here + (coreLogicInstance.schema.parameters as Record<string, unknown>) ?? {}, ); - // Set the root directory - this.rootDirectory = path.resolve(rootDirectory); + this.coreLogic = coreLogicInstance; } /** - * Checks if a path is within the root directory. - * This is a security measure to prevent the tool from accessing files outside of its designated root. - * @param pathToCheck The path to check (expects an absolute path) - * @returns True if the path is within the root directory, false otherwise + * Delegates validation to the core logic */ - private isWithinRoot(pathToCheck: string): boolean { - const absolutePathToCheck = path.resolve(pathToCheck); - const normalizedPath = path.normalize(absolutePathToCheck); - const normalizedRoot = path.normalize(this.rootDirectory); - - // Ensure the normalizedRoot ends with a path separator for proper prefix comparison - const rootWithSep = normalizedRoot.endsWith(path.sep) - ? normalizedRoot - : normalizedRoot + path.sep; - - // Check if it's the root itself or starts with the root path followed by a separator. - // This ensures that we don't accidentally allow access to parent directories. - return ( - normalizedPath === normalizedRoot || - normalizedPath.startsWith(rootWithSep) - ); + validateToolParams(params: GlobToolParams): string | null { + return this.coreLogic.validateToolParams(params); } /** - * Validates the parameters for the tool. - * Ensures that the provided parameters adhere to the expected schema and that the search path is valid and within the tool's root directory. - * @param params Parameters to validate - * @returns An error message string if invalid, null otherwise + * Delegates getting description to the core logic */ - validateToolParams(params: GlobToolParams): string | null { - if ( - this.schema.parameters && - !SchemaValidator.validate( - this.schema.parameters as Record<string, unknown>, - params, - ) - ) { - return "Parameters failed schema validation. Ensure 'pattern' is a string and 'path' (if provided) is a string."; - } - - // Determine the absolute path to check - const searchDirAbsolute = params.path ?? this.rootDirectory; - - // Validate path is within root directory - if (!this.isWithinRoot(searchDirAbsolute)) { - return `Search path ("${searchDirAbsolute}") resolves outside the tool's root directory ("${this.rootDirectory}").`; - } - - // Validate path exists and is a directory using the absolute path. - // These checks prevent the tool from attempting to search in non-existent or non-directory paths, which would lead to errors. - try { - if (!fs.existsSync(searchDirAbsolute)) { - return `Search path does not exist: ${shortenPath(makeRelative(searchDirAbsolute, this.rootDirectory))} (absolute: ${searchDirAbsolute})`; - } - if (!fs.statSync(searchDirAbsolute).isDirectory()) { - return `Search path is not a directory: ${shortenPath(makeRelative(searchDirAbsolute, this.rootDirectory))} (absolute: ${searchDirAbsolute})`; - } - } catch (e: unknown) { - // Catch potential permission errors during sync checks - return `Error accessing search path: ${e}`; - } - - // Validate glob pattern (basic non-empty check) - if ( - !params.pattern || - typeof params.pattern !== 'string' || - params.pattern.trim() === '' - ) { - return "The 'pattern' parameter cannot be empty."; - } - // Could add more sophisticated glob pattern validation if needed - - return null; // Parameters are valid + getDescription(params: GlobToolParams): string { + return this.coreLogic.getDescription(params); } /** - * Gets a description of the glob operation. - * @param params Parameters for the glob operation. - * @returns A string describing the glob operation. + * Define confirmation behavior (Glob likely doesn't need confirmation) */ - getDescription(params: GlobToolParams): string { - let description = `'${params.pattern}'`; - - if (params.path) { - const searchDir = params.path || this.rootDirectory; - const relativePath = makeRelative(searchDir, this.rootDirectory); - description += ` within ${shortenPath(relativePath)}`; - } - - return description; + shouldConfirmExecute( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + params: GlobToolParams, + ): Promise<ToolCallConfirmationDetails | false> { + return Promise.resolve(false); } /** - * Executes the glob search with the given parameters - * @param params Parameters for the glob search - * @returns Result of the glob search + * Delegates execution to the core logic */ async execute(params: GlobToolParams): Promise<ToolResult> { - const validationError = this.validateToolParams(params); - if (validationError) { - return { - llmContent: `Error: Invalid parameters provided. Reason: ${validationError}`, - returnDisplay: `**Error:** Failed to execute tool.`, - }; - } - - try { - // 1. Resolve the absolute search directory. Validation ensures it exists and is a directory. - const searchDirAbsolute = params.path ?? this.rootDirectory; - - // 2. Perform Glob Search using fast-glob - // We use fast-glob because it's performant and supports glob patterns. - const entries = await fg(params.pattern, { - cwd: searchDirAbsolute, // Search within this absolute directory - absolute: true, // Return absolute paths - onlyFiles: true, // Match only files - stats: true, // Include file stats object for sorting - dot: true, // Include files starting with a dot - ignore: ['**/node_modules/**', '**/.git/**'], // Common sensible default, adjust as needed - followSymbolicLinks: false, // Avoid potential issues with symlinks unless specifically needed - suppressErrors: true, // Suppress EACCES errors for individual files (we handle dir access in validation) - }); - - // 3. Handle No Results - if (!entries || entries.length === 0) { - return { - llmContent: `No files found matching pattern "${params.pattern}" within ${searchDirAbsolute}.`, - returnDisplay: `No files found`, - }; - } - - // 4. Sort Results by Modification Time (Newest First) - // Sorting by modification time ensures that the most recently modified files are listed first. - // This can be useful for quickly identifying the files that have been recently changed. - // The stats object is guaranteed by the `stats: true` option in the fast-glob configuration. - entries.sort((a, b) => { - // Ensure stats exist before accessing mtime (though fg should provide them) - const mtimeA = a.stats?.mtime?.getTime() ?? 0; - const mtimeB = b.stats?.mtime?.getTime() ?? 0; - return mtimeB - mtimeA; // Descending order - }); - - // 5. Format Output - const sortedAbsolutePaths = entries.map((entry) => entry.path); - - // Convert absolute paths to relative paths (to rootDir) for clearer display - const sortedRelativePaths = sortedAbsolutePaths.map((absPath) => - makeRelative(absPath, this.rootDirectory), - ); - - // Construct the result message - const fileListDescription = sortedRelativePaths - .map((p) => ` - ${shortenPath(p)}`) - .join('\n'); - const fileCount = sortedRelativePaths.length; - const relativeSearchDir = makeRelative( - searchDirAbsolute, - this.rootDirectory, - ); - const displayPath = shortenPath( - relativeSearchDir === '.' ? 'root directory' : relativeSearchDir, - ); - - return { - llmContent: `Found ${fileCount} file(s) matching "${params.pattern}" within ${displayPath}, sorted by modification time (newest first):\n${fileListDescription}`, - returnDisplay: `Found ${fileCount} matching file(s)`, - }; - } catch (error) { - // Catch unexpected errors during glob execution (less likely with suppressErrors=true, but possible) - const errorMessage = - error instanceof Error ? error.message : String(error); - console.error(`GlobTool execute Error: ${errorMessage}`, error); - return { - llmContent: `Error during glob search operation: ${errorMessage}`, - returnDisplay: `**Error:** An unexpected error occurred.`, - }; - } + return this.coreLogic.execute(params); } + + // Removed private methods (isWithinRoot) + // as they are now part of GlobLogic in the server package. } |
