createGlobTool function

Tool<JsonMap, String> createGlobTool(
  1. Genkit ai, {
  2. required ToolRuntime runtime,
})

Builds the tool that finds files by glob pattern.

Implementation

Tool<JsonMap, String> createGlobTool(
  Genkit ai, {
  required ToolRuntime runtime,
}) {
  return defineTextTool(
    ai,
    runtime: runtime,
    name: 'glob',
    description: _description,
    properties: <String, Object?>{
      'pattern': stringProperty('The glob pattern to match, e.g. "**/*.dart".'),
      'path': stringProperty('Directory to search. Defaults to the current working directory.'),
    },
    required: <String>['pattern'],
    run: (input) async {
      final pattern = requireString(input, 'pattern').trim();
      final pathInput = readString(input, 'path');
      final searchPath = pathInput == null ? runtime.currentWorkingDirectory : runtime.resolvePath(pathInput);

      if (!await Directory(searchPath).exists()) {
        throw ToolFailure('Directory does not exist: $searchPath');
      }

      runtime.startTool('glob', '$pattern in ${runtime.displayPath(searchPath)}');
      final ProcessResult result;
      try {
        result = await Process.run(
          'rg',
          <String>['--files', '--glob', pattern, '--color=never'],
          workingDirectory: searchPath,
        );
      } on ProcessException {
        throw const ToolFailure('ripgrep (`rg`) is not installed. Ask the user to install it.');
      }
      if (result.exitCode > 1) {
        throw ToolFailure('rg exited with code ${result.exitCode}: ${'${result.stderr}'.trim()}');
      }

      final relativePaths = '${result.stdout}'.split('\n').where((line) => line.isNotEmpty);
      if (relativePaths.isEmpty) {
        return 'No files found.';
      }

      final files = <(String, DateTime)>[];
      for (final relative in relativePaths) {
        final path = runtime.resolvePath(relative, fromDirectory: searchPath);
        files.add((path, (await FileStat.stat(path)).modified));
      }
      files.sort((a, b) => b.$2.compareTo(a.$2));

      final shown = files.take(_maxResults).map((file) => file.$1).join('\n');
      if (files.length > _maxResults) {
        return '$shown\n\n(Showing $_maxResults of ${files.length} files. Use a more specific pattern or path.)';
      }
      return shown;
    },
  );
}