using System.Text.Json; namespace ClaudeDo.Worker.Runner; public sealed record ClaudeRunConfig( string? Model, string? SystemPrompt, string? AgentPath, string? ResumeSessionId ); 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 string Build(ClaudeRunConfig config) { var args = new List { "-p", "--output-format stream-json", "--verbose", "--dangerously-skip-permissions", }; if (config.Model is not null) args.Add($"--model {config.Model}"); if (config.SystemPrompt is not null) args.Add($"--append-system-prompt {Escape(config.SystemPrompt)}"); if (config.AgentPath is not null) { var agentJson = JsonSerializer.Serialize(new[] { new { file = config.AgentPath } }); args.Add($"--agents {Escape(agentJson)}"); } args.Add($"--json-schema {Escape(ResultSchema)}"); if (config.ResumeSessionId is not null) args.Add($"--resume {config.ResumeSessionId}"); return string.Join(" ", args); } private static string Escape(string value) { if (value.Contains(' ') || value.Contains('"') || value.Contains('\'') || value.Contains('\t') || value.Contains('\n') || value.Contains('\r')) { var escaped = value .Replace("\\", "\\\\") .Replace("\"", "\\\"") .Replace("\n", "\\n") .Replace("\r", "\\r") .Replace("\t", "\\t"); return $"\"{escaped}\""; } return value; } }