using System.IO; using System.Text.Json; namespace AxCopilot.Services.Agent; /// 파일 패턴 검색 도구. glob 패턴으로 파일 목록을 반환합니다. public class GlobTool : IAgentTool { public string Name => "glob"; public string Description => "Find files matching a glob pattern (e.g. '**/*.cs', 'src/**/*.json'). Returns matching file paths."; public ToolParameterSchema Parameters => new() { Properties = new() { ["pattern"] = new() { Type = "string", Description = "Glob pattern to match files (e.g. '**/*.cs', '*.txt')" }, ["path"] = new() { Type = "string", Description = "Directory to search in. Optional, defaults to work folder." }, }, Required = ["pattern"] }; public Task ExecuteAsync(JsonElement args, AgentContext context, CancellationToken ct) { var pattern = args.GetProperty("pattern").GetString() ?? ""; var searchPath = args.TryGetProperty("path", out var p) ? p.GetString() ?? "" : ""; var baseDir = string.IsNullOrEmpty(searchPath) ? context.WorkFolder : FileReadTool.ResolvePath(searchPath, context.WorkFolder); if (string.IsNullOrEmpty(baseDir) || !Directory.Exists(baseDir)) return Task.FromResult(ToolResult.Fail($"디렉토리가 존재하지 않습니다: {baseDir}")); if (!context.IsPathAllowed(baseDir)) return Task.FromResult(ToolResult.Fail($"경로 접근 차단: {baseDir}")); try { // glob 패턴을 Directory.EnumerateFiles용으로 변환 var searchPattern = ExtractSearchPattern(pattern); var recursive = pattern.Contains("**") || pattern.Contains('/') || pattern.Contains('\\'); var option = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly; var files = Directory.EnumerateFiles(baseDir, searchPattern, option) .Where(f => context.IsPathAllowed(f)) .OrderBy(f => f) .Take(200) .ToList(); if (files.Count == 0) return Task.FromResult(ToolResult.Ok($"패턴 '{pattern}'에 일치하는 파일이 없습니다.")); var result = string.Join("\n", files.Select(f => Path.GetRelativePath(baseDir, f))); return Task.FromResult(ToolResult.Ok($"{files.Count}개 파일 발견:\n{result}")); } catch (Exception ex) { return Task.FromResult(ToolResult.Fail($"검색 실패: {ex.Message}")); } } private static string ExtractSearchPattern(string globPattern) { // **/*.cs → *.cs, src/**/*.json → *.json var parts = globPattern.Replace('/', '\\').Split('\\'); var last = parts[^1]; return string.IsNullOrEmpty(last) || last == "**" ? "*" : last; } }