using System; using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; namespace AxCopilot.Services.Agent; public class MarkdownSkill : IAgentTool { public string Name => "markdown_create"; public string Description => "Create a Markdown (.md) document file. Provide the content in Markdown format."; public ToolParameterSchema Parameters { get { ToolParameterSchema obj = new ToolParameterSchema { Properties = new Dictionary { ["path"] = new ToolProperty { Type = "string", Description = "Output file path (.md). Relative to work folder." }, ["content"] = new ToolProperty { Type = "string", Description = "Markdown content to write" }, ["title"] = new ToolProperty { Type = "string", Description = "Optional document title. If provided, prepends '# title' at the top." } } }; int num = 2; List list = new List(num); CollectionsMarshal.SetCount(list, num); Span span = CollectionsMarshal.AsSpan(list); span[0] = "path"; span[1] = "content"; obj.Required = list; return obj; } } public async Task ExecuteAsync(JsonElement args, AgentContext context, CancellationToken ct) { string path = args.GetProperty("path").GetString() ?? ""; string content = args.GetProperty("content").GetString() ?? ""; JsonElement t; string title = (args.TryGetProperty("title", out t) ? t.GetString() : null); string fullPath = FileReadTool.ResolvePath(path, context.WorkFolder); if (context.ActiveTab == "Cowork") { fullPath = AgentContext.EnsureTimestampedPath(fullPath); } if (!fullPath.EndsWith(".md", StringComparison.OrdinalIgnoreCase)) { fullPath += ".md"; } if (!context.IsPathAllowed(fullPath)) { return ToolResult.Fail("경로 접근 차단: " + fullPath); } if (!(await context.CheckWritePermissionAsync(Name, fullPath))) { return ToolResult.Fail("쓰기 권한 거부: " + fullPath); } try { string dir = Path.GetDirectoryName(fullPath); if (!string.IsNullOrEmpty(dir)) { Directory.CreateDirectory(dir); } StringBuilder sb = new StringBuilder(); if (!string.IsNullOrEmpty(title)) { StringBuilder stringBuilder = sb; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(2, 1, stringBuilder); handler.AppendLiteral("# "); handler.AppendFormatted(title); stringBuilder.AppendLine(ref handler); sb.AppendLine(); } sb.Append(content); await File.WriteAllTextAsync(fullPath, sb.ToString(), Encoding.UTF8, ct); int lines = sb.ToString().Split('\n').Length; return ToolResult.Ok($"Markdown 문서 저장 완료: {fullPath} ({lines} lines)", fullPath); } catch (Exception ex) { return ToolResult.Fail("Markdown 저장 실패: " + ex.Message); } } }