using System.IO; using System.Text.Json; namespace AxCopilot.Services.Agent; public sealed class EnterWorktreeTool : IAgentTool { public string Name => "enter_worktree"; public string Description => "Enter or create a nested worktree path inside current WorkFolder."; public ToolParameterSchema Parameters => new() { Properties = new() { ["path"] = new() { Type = "string", Description = "Relative worktree path." }, ["create"] = new() { Type = "boolean", Description = "Create directory if missing (default true)." } }, Required = ["path"] }; public Task ExecuteAsync(JsonElement args, AgentContext context, CancellationToken ct = default) { var relative = args.TryGetProperty("path", out var pathEl) ? (pathEl.GetString() ?? "").Trim() : ""; if (string.IsNullOrWhiteSpace(relative)) return Task.FromResult(ToolResult.Fail("path is required.")); if (string.IsNullOrWhiteSpace(context.WorkFolder) || !Directory.Exists(context.WorkFolder)) return Task.FromResult(ToolResult.Fail("valid WorkFolder is required.")); var create = !args.TryGetProperty("create", out var createEl) || createEl.ValueKind != JsonValueKind.False; var root = WorktreeStateStore.ResolveRoot(context.WorkFolder); var full = Path.GetFullPath(Path.Combine(root, relative)); if (!full.StartsWith(root, StringComparison.OrdinalIgnoreCase)) return Task.FromResult(ToolResult.Fail("worktree path must stay inside WorkFolder.")); if (!Directory.Exists(full)) { if (!create) return Task.FromResult(ToolResult.Fail("worktree path does not exist.")); Directory.CreateDirectory(full); } var state = WorktreeStateStore.Load(root); state.Active = full; WorktreeStateStore.Save(root, state); context.WorkFolder = full; return Task.FromResult(ToolResult.Ok($"Entered worktree: {full}")); } }