summaryrefslogtreecommitdiff
path: root/packages/cli/src/tools/glob.tool.ts
diff options
context:
space:
mode:
authorEvan Senter <[email protected]>2025-04-19 19:45:42 +0100
committerGitHub <[email protected]>2025-04-19 19:45:42 +0100
commit3fce6cea27d3e6129d6c06e528b62e1b11bf7094 (patch)
tree244b8e9ab94f902d65d4bda8739a6538e377ed17 /packages/cli/src/tools/glob.tool.ts
parent0c9e1ef61be7db53e6e73b7208b649cd8cbed6c3 (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.ts251
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.
}