using System.IO; using System.Text; using System.Text.Json; namespace AxCopilot.Services.Agent; /// 파일/폴더 메타 정보(크기, 수정일, 줄 수 등) 조회 도구. public class FileInfoTool : IAgentTool { public string Name => "file_info"; public string Description => "Get file or directory metadata without reading contents. " + "Returns: size, created/modified dates, line count (files), item count (directories), encoding hint."; public ToolParameterSchema Parameters => new() { Properties = new() { ["path"] = new() { Type = "string", Description = "File or directory path", }, }, Required = ["path"], }; public Task ExecuteAsync(JsonElement args, AgentContext context, CancellationToken ct = default) { var rawPath = args.GetProperty("path").GetString() ?? ""; var path = Path.IsPathRooted(rawPath) ? rawPath : Path.Combine(context.WorkFolder, rawPath); if (!context.IsPathAllowed(path)) return Task.FromResult(ToolResult.Fail($"경로 접근 차단: {path}")); try { if (File.Exists(path)) { var fi = new FileInfo(path); var sb = new StringBuilder(); sb.AppendLine($"Type: File"); sb.AppendLine($"Path: {fi.FullName}"); sb.AppendLine($"Size: {FormatSize(fi.Length)} ({fi.Length:N0} bytes)"); sb.AppendLine($"Extension: {fi.Extension}"); sb.AppendLine($"Created: {fi.CreationTime:yyyy-MM-dd HH:mm:ss}"); sb.AppendLine($"Modified: {fi.LastWriteTime:yyyy-MM-dd HH:mm:ss}"); sb.AppendLine($"ReadOnly: {fi.IsReadOnly}"); // 텍스트 파일이면 줄 수 카운트 (최대 100만 줄까지) var textExts = new HashSet(StringComparer.OrdinalIgnoreCase) { ".cs", ".py", ".js", ".ts", ".java", ".cpp", ".c", ".h", ".xml", ".json", ".yaml", ".yml", ".md", ".txt", ".csv", ".html", ".htm", ".css", ".sql", ".sh", ".bat", ".ps1", ".config", ".ini", ".log", ".xaml" }; if (textExts.Contains(fi.Extension) && fi.Length < 50 * 1024 * 1024) { var lineCount = File.ReadLines(path).Take(1_000_000).Count(); sb.AppendLine($"Lines: {lineCount:N0}{(lineCount >= 1_000_000 ? "+" : "")}"); } return Task.FromResult(ToolResult.Ok(sb.ToString())); } if (Directory.Exists(path)) { var di = new DirectoryInfo(path); var files = di.GetFiles("*", SearchOption.TopDirectoryOnly); var dirs = di.GetDirectories(); var totalSize = files.Sum(f => f.Length); var sb = new StringBuilder(); sb.AppendLine($"Type: Directory"); sb.AppendLine($"Path: {di.FullName}"); sb.AppendLine($"Files: {files.Length}"); sb.AppendLine($"Subdirectories: {dirs.Length}"); sb.AppendLine($"Total Size (top-level files): {FormatSize(totalSize)}"); sb.AppendLine($"Created: {di.CreationTime:yyyy-MM-dd HH:mm:ss}"); sb.AppendLine($"Modified: {di.LastWriteTime:yyyy-MM-dd HH:mm:ss}"); return Task.FromResult(ToolResult.Ok(sb.ToString())); } return Task.FromResult(ToolResult.Fail($"경로를 찾을 수 없습니다: {path}")); } catch (Exception ex) { return Task.FromResult(ToolResult.Fail($"정보 조회 오류: {ex.Message}")); } } private static string FormatSize(long bytes) => bytes switch { < 1024 => $"{bytes}B", < 1024 * 1024 => $"{bytes / 1024.0:F1}KB", < 1024L * 1024 * 1024 => $"{bytes / (1024.0 * 1024):F1}MB", _ => $"{bytes / (1024.0 * 1024 * 1024):F2}GB", }; }