using System.Text.Json; namespace ClaudeDo.Worker.Runner; public sealed record ClaudeRunConfig( string? Model, string? SystemPrompt, string? AgentPath, string? ResumeSessionId, int? MaxTurns = null, string? PermissionMode = null, string? McpConfigPath = null, string? AllowedTools = null, IReadOnlyList? SkillNames = null ) { public IReadOnlyList SkillNames { get; init; } = SkillNames ?? Array.Empty(); } public sealed class ClaudeArgsBuilder { private static readonly string ResultSchema = JsonSerializer.Serialize(new { type = "object", properties = new { summary = new { type = "string" }, files_changed = new { type = "array", items = new { type = "string" } }, commit_type = new { type = "string" }, }, required = new[] { "summary" }, }); public IReadOnlyList Build(ClaudeRunConfig config) { var args = new List { "-p", "--output-format", "stream-json", "--verbose", }; var permissionMode = string.IsNullOrWhiteSpace(config.PermissionMode) || config.PermissionMode.Equals("bypassPermissions", StringComparison.OrdinalIgnoreCase) ? "auto" : config.PermissionMode; args.Add("--permission-mode"); args.Add(permissionMode); if (config.Model is not null) { args.Add("--model"); args.Add(config.Model); } if (config.MaxTurns is int turns && turns > 0) { args.Add("--max-turns"); args.Add(turns.ToString()); } if (config.SystemPrompt is not null) { args.Add("--append-system-prompt"); args.Add(config.SystemPrompt); } if (config.AgentPath is not null) { var agentJson = JsonSerializer.Serialize(new[] { new { file = config.AgentPath } }); args.Add("--agents"); args.Add(agentJson); } args.Add("--json-schema"); args.Add(ResultSchema); if (config.McpConfigPath is not null) { args.Add("--mcp-config"); args.Add(config.McpConfigPath); } if (config.AllowedTools is not null) { args.Add("--allowedTools"); args.Add(config.AllowedTools); } if (config.ResumeSessionId is not null) { args.Add("--resume"); args.Add(config.ResumeSessionId); } return args; } }