Files
AX-Copilot-Codex/src/AxCopilot/Services/Agent/FileInfoTool.cs

99 lines
4.0 KiB
C#

using System.IO;
using System.Text;
using System.Text.Json;
namespace AxCopilot.Services.Agent;
/// <summary>파일/폴더 메타 정보(크기, 수정일, 줄 수 등) 조회 도구.</summary>
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<ToolResult> 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<string>(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",
};
}