using System.IO; using System.Text.Json; using System.Text.Json.Serialization; using ClaudeDo.Data; namespace ClaudeDo.Installer.Core; internal static class JsonConfigFile { public static T LoadOrDefault(string fileName, JsonSerializerOptions readOpts) where T : new() { var path = Path.Combine(Paths.AppDataRoot(), fileName); if (!File.Exists(path)) return new(); return JsonSerializer.Deserialize(File.ReadAllText(path), readOpts) ?? new(); } public static void Save(string fileName, T value, JsonSerializerOptions writeOpts) { var dir = Paths.AppDataRoot(); Directory.CreateDirectory(dir); File.WriteAllText(Path.Combine(dir, fileName), JsonSerializer.Serialize(value, writeOpts)); } } /// /// Mirrors ClaudeDo.Worker.Config.WorkerConfig JSON shape. /// Keep in sync with src/ClaudeDo.Worker/Config/WorkerConfig.cs. /// public sealed class InstallerWorkerConfig { [JsonPropertyName("db_path")] public string DbPath { get; set; } = "~/.todo-app/todo.db"; [JsonPropertyName("sandbox_root")] public string SandboxRoot { get; set; } = "~/.todo-app/sandbox"; [JsonPropertyName("log_root")] public string LogRoot { get; set; } = "~/.todo-app/logs"; [JsonPropertyName("worktree_root_strategy")] public string WorktreeRootStrategy { get; set; } = "sibling"; [JsonPropertyName("central_worktree_root")] public string CentralWorktreeRoot { get; set; } = "~/.todo-app/worktrees"; [JsonPropertyName("queue_backstop_interval_ms")] public int QueueBackstopIntervalMs { get; set; } = 30_000; [JsonPropertyName("signalr_port")] public int SignalRPort { get; set; } = 47_821; [JsonPropertyName("claude_bin")] public string ClaudeBin { get; set; } = "claude"; private static readonly JsonSerializerOptions ReadOpts = new() { ReadCommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = true, }; private static readonly JsonSerializerOptions WriteOpts = new() { WriteIndented = true, }; public static InstallerWorkerConfig Load() => JsonConfigFile.LoadOrDefault("worker.config.json", ReadOpts); public void Save() => JsonConfigFile.Save("worker.config.json", this, WriteOpts); } /// /// Mirrors ClaudeDo.Ui.AppSettings JSON shape. /// Keep in sync with src/ClaudeDo.Ui/AppSettings.cs. /// public sealed class InstallerAppSettings { public string DbPath { get; set; } = "~/.todo-app/todo.db"; public string SignalRUrl { get; set; } = "http://127.0.0.1:47821/hub"; public string Language { get; set; } = ""; private static readonly JsonSerializerOptions ReadOpts = new() { PropertyNameCaseInsensitive = true, }; private static readonly JsonSerializerOptions WriteOpts = new() { WriteIndented = true, }; public static InstallerAppSettings Load() { try { return JsonConfigFile.LoadOrDefault("ui.config.json", ReadOpts); } catch { return new(); } } public void Save() => JsonConfigFile.Save("ui.config.json", this, WriteOpts); }