UNPKG

@agentforce/adk

Version:

AgentForce Agent Development Kit - A powerful framework for building AI agents and servers

140 lines (127 loc) 5.84 kB
import { readdirSync, statSync } from "fs"; import { join, relative } from "path"; import type { ToolImplementation } from "../types"; import { parseGitignore, shouldExclude } from "../utils/gitignore_parser"; /** * File system find files by extension tool * Recursively searches for files with specified extensions */ export const fs_find_files: ToolImplementation = { definition: { type: "function", function: { name: "fs_find_files", description: "Recursively find files by extension(s) in a directory, with automatic .gitignore exclusions", parameters: { type: "object", properties: { path: { type: "string", description: "The directory path to search in (defaults to current working directory)", }, extensions: { type: "array", description: "Array of file extensions to search for (e.g., ['.md', '.ts', '.js'])", }, use_gitignore: { type: "boolean", description: "Whether to use .gitignore patterns for exclusions (default: true)", }, additional_excludes: { type: "array", description: "Additional directories to exclude from search", }, max_depth: { type: "number", description: "Maximum depth to search (default: 10)", }, }, required: ["extensions"], }, }, }, execute: async (args: Record<string, any>) => { const searchPath = args.path || process.cwd(); const extensions = args.extensions || []; const useGitignore = args.use_gitignore !== false; // Default true const additionalExcludes = args.additional_excludes || []; const maxDepth = args.max_depth || 10; try { // Get excludes from .gitignore if enabled let excludes: string[] = [...additionalExcludes]; if (useGitignore) { const gitignoreExcludes = parseGitignore(searchPath); excludes = [...excludes, ...gitignoreExcludes]; } const foundFiles: any[] = []; const searchDirectory = (dirPath: string, depth = 0): void => { if (depth > maxDepth) return; // Check if this directory should be excluded const relativePath = relative(searchPath, dirPath); if (relativePath && shouldExclude(relativePath, excludes)) { return; } try { const items = readdirSync(dirPath); for (const item of items) { const fullPath = join(dirPath, item); const relPath = relative(searchPath, fullPath); try { const stats = statSync(fullPath); if (stats.isDirectory()) { // Skip if directory should be excluded if (shouldExclude(relPath, excludes, false)) { continue; } // Recursively search subdirectories searchDirectory(fullPath, depth + 1); } else if (stats.isFile()) { // Skip if file should be excluded if (shouldExclude(relPath, excludes, true)) { continue; } // Check if file matches any of the extensions const hasMatchingExtension = extensions.some((ext: string) => { // Handle extensions with or without leading dot const normalizedExt = ext.startsWith(".") ? ext : `.${ext}`; return item.endsWith(normalizedExt); }); if (hasMatchingExtension) { foundFiles.push({ name: item, path: relPath, fullPath: fullPath, size: stats.size, modified: stats.mtime, }); } } } catch (error) { // Skip files/directories we can't access continue; } } } catch (error) { // Skip directories we can't read } }; searchDirectory(searchPath); // Sort files by path for consistent output foundFiles.sort((a, b) => a.path.localeCompare(b.path)); return { success: true, searchPath: searchPath, extensions: extensions, excludes: excludes, count: foundFiles.length, files: foundFiles, }; } catch (error: any) { return { success: false, error: error.message, searchPath: searchPath, }; } }, };