99 lines
4.0 KiB
C#
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",
|
|
};
|
|
}
|