From c4928b4def10c5edbc67c60bfafd5fd4b67c40e2 Mon Sep 17 00:00:00 2001 From: Mika Kuns Date: Wed, 26 Aug 2026 14:26:19 +0200 Subject: [PATCH] chore: move appdata folder to .claudeDo --- CLAUDE.md | 8 +-- README.md | 2 +- docs/explore-notes/conpty-sessions.md | 2 +- src/ClaudeDo.Data/AttachmentStore.cs | 2 +- src/ClaudeDo.Data/CLAUDE.md | 6 +-- src/ClaudeDo.Data/Paths.cs | 49 +++++++++++++++++-- src/ClaudeDo.Installer/CLAUDE.md | 14 +++--- src/ClaudeDo.Installer/Core/ConfigModels.cs | 10 ++-- src/ClaudeDo.Installer/Core/InstallContext.cs | 10 ++-- .../Core/UninstallRunner.cs | 2 +- .../Pages/PathsPage/PathsPageViewModel.cs | 8 +-- .../UiSettingsPage/UiSettingsPageViewModel.cs | 2 +- .../Views/SettingsViewModel.cs | 2 +- src/ClaudeDo.Localization/locales/de.json | 2 +- src/ClaudeDo.Localization/locales/en.json | 2 +- src/ClaudeDo.Ui/AppSettings.cs | 4 +- src/ClaudeDo.Ui/CLAUDE.md | 2 +- src/ClaudeDo.Worker/CLAUDE.md | 6 +-- src/ClaudeDo.Worker/Config/WorkerConfig.cs | 8 +-- .../Online/OnlineTokenStore.cs | 2 +- .../Planning/PlanningSessionManager.cs | 2 +- .../Planning/WindowsTerminalLauncher.cs | 2 +- src/ClaudeDo.Worker/Program.cs | 6 +-- .../Runner/InteractiveLaunchSpecService.cs | 2 +- 24 files changed, 99 insertions(+), 56 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 7f24ea61..13fb7252 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -29,10 +29,10 @@ have none; this file plus the code is all there is for them. ## Key Paths -- DB: `~/.todo-app/todo.db` -- UI config: `~/.todo-app/ui.config.json` -- Worker config: `~/.todo-app/worker.config.json` -- Logs: `~/.todo-app/logs/` +- DB: `~/.claudeDo/todo.db` +- UI config: `~/.claudeDo/ui.config.json` +- Worker config: `~/.claudeDo/worker.config.json` +- Logs: `~/.claudeDo/logs/` - Worktrees: configured per worker (sibling or central strategy) ## Conventions diff --git a/README.md b/README.md index 17e3be6e..c74ddc69 100644 --- a/README.md +++ b/README.md @@ -374,7 +374,7 @@ updates`. ## Where your data lives -Everything is under `%USERPROFILE%\.todo-app`: +Everything is under `%USERPROFILE%\.claudeDo`: | Path | What | |---|---| diff --git a/docs/explore-notes/conpty-sessions.md b/docs/explore-notes/conpty-sessions.md index 5a0c2821..cc500e55 100644 --- a/docs/explore-notes/conpty-sessions.md +++ b/docs/explore-notes/conpty-sessions.md @@ -33,7 +33,7 @@ Two independent reasons: `--abort`) is then misread as an unknown option. 2. A raw multi-line positional prompt truncates at its **first newline** regardless. -A fresh task session's brief lives at `~/.todo-app/task-sessions//brief.md` +A fresh task session's brief lives at `~/.claudeDo/task-sessions//brief.md` (`InteractiveLaunchSpecService.BuildFreshTaskArgsAsync`). A task with neither title nor description skips the file **and** the positional arg entirely — it still gets `--session-id`. diff --git a/src/ClaudeDo.Data/AttachmentStore.cs b/src/ClaudeDo.Data/AttachmentStore.cs index c758f10d..41c889a5 100644 --- a/src/ClaudeDo.Data/AttachmentStore.cs +++ b/src/ClaudeDo.Data/AttachmentStore.cs @@ -7,7 +7,7 @@ public sealed class AttachmentStore private readonly string _root; public AttachmentStore(string? root = null) - => _root = root ?? Paths.Expand("~/.todo-app/attachments"); + => _root = root ?? Path.Combine(Paths.AppDataRoot(), "attachments"); public string Root => _root; diff --git a/src/ClaudeDo.Data/CLAUDE.md b/src/ClaudeDo.Data/CLAUDE.md index 34e7dfe3..fa68110a 100644 --- a/src/ClaudeDo.Data/CLAUDE.md +++ b/src/ClaudeDo.Data/CLAUDE.md @@ -63,9 +63,9 @@ dir(s) via an optional `AttachmentStore` ctor param (defaults to the production - **ClaudeDoDbContext** — EF Core DbContext; WAL mode + foreign keys via `UseSqlite` options - **IDbContextFactory\** — registered in DI; used by singleton consumers (e.g. the Worker hosted service) -- **Paths** — expands `~` and `%USERPROFILE%`, resolves relative paths. App root `~/.todo-app` -- **AppSettings** — loads `~/.todo-app/ui.config.json` (DbPath, SignalRUrl) -- **AttachmentStore** — dependency-free file store, default root `~/.todo-app/attachments//`. `SaveAsync` enforces a 5 MB cap and a path-traversal/containment guard. Also `DeleteFile`, `DeleteTaskDir`, `TaskDir`, `Root`, `EnumerateTaskIds` (used by the worker orphan sweep). Attachment files live **outside** git worktrees intentionally. +- **Paths** — expands `~` and `%USERPROFILE%`, resolves relative paths. App root `~/.claudeDo` +- **AppSettings** — loads `~/.claudeDo/ui.config.json` (DbPath, SignalRUrl) +- **AttachmentStore** — dependency-free file store, default root `~/.claudeDo/attachments//`. `SaveAsync` enforces a 5 MB cap and a path-traversal/containment guard. Also `DeleteFile`, `DeleteTaskDir`, `TaskDir`, `Root`, `EnumerateTaskIds` (used by the worker orphan sweep). Attachment files live **outside** git worktrees intentionally. ## Git diff --git a/src/ClaudeDo.Data/Paths.cs b/src/ClaudeDo.Data/Paths.cs index 1c656834..ddf9c617 100644 --- a/src/ClaudeDo.Data/Paths.cs +++ b/src/ClaudeDo.Data/Paths.cs @@ -41,7 +41,50 @@ public static class Paths return trimmed.Length == 0 || trimmed.EndsWith(':') ? path : trimmed; } - /// ~/.todo-app — parent directory for db, logs, config, sandbox, worktrees. - public static string AppDataRoot() => - Expand("~/.todo-app"); + private const string DirName = ".claudeDo"; + private const string LegacyDirName = ".todo-app"; + + /// + /// ~/.claudeDo — parent directory for db, logs, config, sandbox, worktrees. + /// Falls back to the legacy ~/.todo-app while that one still exists and the new one + /// doesn't, so a failed degrades to "keep using + /// the old folder" instead of silently starting over on an empty one. + /// + public static string AppDataRoot() + { + var root = Expand("~/" + DirName); + if (!Directory.Exists(root)) + { + var legacy = Expand("~/" + LegacyDirName); + if (Directory.Exists(legacy)) return legacy; + } + return root; + } + + /// + /// One-time rename of ~/.todo-app to ~/.claudeDo, including the path literals stored + /// inside worker.config.json / ui.config.json. No-op once the new folder exists. + /// Called from the Worker at startup before anything opens a file under the root — + /// the single mover, so the UI never races it. + /// + public static void MigrateLegacyAppDataRoot() => MigrateLegacyAppDataRoot(Expand("~")); + + internal static void MigrateLegacyAppDataRoot(string home) + { + var root = Path.Combine(home, DirName); + var legacy = Path.Combine(home, LegacyDirName); + if (Directory.Exists(root) || !Directory.Exists(legacy)) return; + + // A locked file (e.g. an old worker still holding todo.db) fails the move; AppDataRoot + // then keeps returning the legacy path and the next start retries. + try { Directory.Move(legacy, root); } + catch (IOException) { return; } + catch (UnauthorizedAccessException) { return; } + + foreach (var cfg in Directory.EnumerateFiles(root, "*.config.json")) + { + try { File.WriteAllText(cfg, File.ReadAllText(cfg).Replace(LegacyDirName, DirName)); } + catch (IOException) { /* best effort — the default already points at the new root */ } + } + } } diff --git a/src/ClaudeDo.Installer/CLAUDE.md b/src/ClaudeDo.Installer/CLAUDE.md index 3e0d96e6..a9654229 100644 --- a/src/ClaudeDo.Installer/CLAUDE.md +++ b/src/ClaudeDo.Installer/CLAUDE.md @@ -49,7 +49,7 @@ Each step implements `IInstallStep`; `InstallerService` runs them sequentially, `StopWorkerStep` → `DownloadAndExtractStep` → `RegisterAutostartStep` → `StartWorkerStep` **Uninstall** (`UninstallRunner`): -Stop worker → remove legacy task/service → delete HKLM uninstall key + shortcuts → delete install dir (cmd.exe trampoline if uninstaller exe is inside it) → optionally delete `~/.todo-app` +Stop worker → remove legacy task/service → delete HKLM uninstall key + shortcuts → delete install dir (cmd.exe trampoline if uninstaller exe is inside it) → optionally delete `~/.claudeDo` ## Folder Layout @@ -97,9 +97,9 @@ and restored if it fails. | Property | Default | |---|---| | `InstallDirectory` | `C:\Program Files\ClaudeDo` | -| `DbPath` | `~/.todo-app/todo.db` | -| `LogRoot` | `~/.todo-app/logs` | -| `SandboxRoot` | `~/.todo-app/sandbox` | +| `DbPath` | `~/.claudeDo/todo.db` | +| `LogRoot` | `~/.claudeDo/logs` | +| `SandboxRoot` | `~/.claudeDo/sandbox` | | `WorktreeRootStrategy` | `sibling` | | `SignalRPort` | `47821` | | `ExternalMcpPort` | `47822` | @@ -112,9 +112,9 @@ and restored if it fails. | Path | Content | |---|---| -| `~/.todo-app/worker.config.json` | Worker config | -| `~/.todo-app/ui.config.json` | UI config | -| `~/.todo-app/todo.db` | SQLite DB (EF migrations) | +| `~/.claudeDo/worker.config.json` | Worker config | +| `~/.claudeDo/ui.config.json` | UI config | +| `~/.claudeDo/todo.db` | SQLite DB (EF migrations) | | `\install.json` | Install manifest | | `\app\` | UI binaries | | `\worker\` | Worker binaries | diff --git a/src/ClaudeDo.Installer/Core/ConfigModels.cs b/src/ClaudeDo.Installer/Core/ConfigModels.cs index 664b6299..9e3f2a12 100644 --- a/src/ClaudeDo.Installer/Core/ConfigModels.cs +++ b/src/ClaudeDo.Installer/Core/ConfigModels.cs @@ -29,19 +29,19 @@ internal static class JsonConfigFile public sealed class InstallerWorkerConfig { [JsonPropertyName("db_path")] - public string DbPath { get; set; } = "~/.todo-app/todo.db"; + public string DbPath { get; set; } = "~/.claudeDo/todo.db"; [JsonPropertyName("sandbox_root")] - public string SandboxRoot { get; set; } = "~/.todo-app/sandbox"; + public string SandboxRoot { get; set; } = "~/.claudeDo/sandbox"; [JsonPropertyName("log_root")] - public string LogRoot { get; set; } = "~/.todo-app/logs"; + public string LogRoot { get; set; } = "~/.claudeDo/logs"; [JsonPropertyName("worktree_root_strategy")] public string WorktreeRootStrategy { get; set; } = "sibling"; [JsonPropertyName("central_worktree_root")] - public string CentralWorktreeRoot { get; set; } = "~/.todo-app/worktrees"; + public string CentralWorktreeRoot { get; set; } = "~/.claudeDo/worktrees"; [JsonPropertyName("queue_backstop_interval_ms")] public int QueueBackstopIntervalMs { get; set; } = 30_000; @@ -78,7 +78,7 @@ public sealed class InstallerWorkerConfig /// public sealed class InstallerAppSettings { - public string DbPath { get; set; } = "~/.todo-app/todo.db"; + public string DbPath { get; set; } = "~/.claudeDo/todo.db"; public string SignalRUrl { get; set; } = "http://127.0.0.1:47821/hub"; public string Language { get; set; } = ""; diff --git a/src/ClaudeDo.Installer/Core/InstallContext.cs b/src/ClaudeDo.Installer/Core/InstallContext.cs index 1640b44c..11d10d33 100644 --- a/src/ClaudeDo.Installer/Core/InstallContext.cs +++ b/src/ClaudeDo.Installer/Core/InstallContext.cs @@ -13,11 +13,11 @@ public sealed class InstallContext public bool LatestTagUnparseable { get; set; } // true if latest tag isn't a System.Version // PathsPage - public string DbPath { get; set; } = "~/.todo-app/todo.db"; - public string LogRoot { get; set; } = "~/.todo-app/logs"; - public string SandboxRoot { get; set; } = "~/.todo-app/sandbox"; + public string DbPath { get; set; } = "~/.claudeDo/todo.db"; + public string LogRoot { get; set; } = "~/.claudeDo/logs"; + public string SandboxRoot { get; set; } = "~/.claudeDo/sandbox"; public string WorktreeRootStrategy { get; set; } = "sibling"; - public string CentralWorktreeRoot { get; set; } = "~/.todo-app/worktrees"; + public string CentralWorktreeRoot { get; set; } = "~/.claudeDo/worktrees"; // ServicePage public int SignalRPort { get; set; } = 47_821; @@ -28,7 +28,7 @@ public sealed class InstallContext // UiSettingsPage public string SignalRUrl { get; set; } = "http://127.0.0.1:47821/hub"; - public string UiDbPath { get; set; } = "~/.todo-app/todo.db"; + public string UiDbPath { get; set; } = "~/.claudeDo/todo.db"; // InstallPage public bool CreateDesktopShortcut { get; set; } = true; diff --git a/src/ClaudeDo.Installer/Core/UninstallRunner.cs b/src/ClaudeDo.Installer/Core/UninstallRunner.cs index f651553b..06b6240d 100644 --- a/src/ClaudeDo.Installer/Core/UninstallRunner.cs +++ b/src/ClaudeDo.Installer/Core/UninstallRunner.cs @@ -72,7 +72,7 @@ public sealed class UninstallRunner // 6) Delete data dir (config + DB + logs) — only if user opted in. // Prefer the manifest-recorded DataDir so a customised DbPath is honoured; - // fall back to the default ~/.todo-app for older manifests. + // fall back to the default app data root for older manifests. if (removeAppData) { var manifest = InstallManifestStore.TryRead(_context.InstallDirectory); diff --git a/src/ClaudeDo.Installer/Pages/PathsPage/PathsPageViewModel.cs b/src/ClaudeDo.Installer/Pages/PathsPage/PathsPageViewModel.cs index 16b64e8e..8caeeb15 100644 --- a/src/ClaudeDo.Installer/Pages/PathsPage/PathsPageViewModel.cs +++ b/src/ClaudeDo.Installer/Pages/PathsPage/PathsPageViewModel.cs @@ -17,11 +17,11 @@ public partial class PathsPageViewModel : ObservableObject, IInstallerPage public bool ShowInSettings => true; public UserControl View => _view ??= new PathsPageView { DataContext = this }; - [ObservableProperty] private string _dbPath = "~/.todo-app/todo.db"; - [ObservableProperty] private string _logRoot = "~/.todo-app/logs"; - [ObservableProperty] private string _sandboxRoot = "~/.todo-app/sandbox"; + [ObservableProperty] private string _dbPath = "~/.claudeDo/todo.db"; + [ObservableProperty] private string _logRoot = "~/.claudeDo/logs"; + [ObservableProperty] private string _sandboxRoot = "~/.claudeDo/sandbox"; [ObservableProperty] private string _worktreeRootStrategy = "sibling"; - [ObservableProperty] private string _centralWorktreeRoot = "~/.todo-app/worktrees"; + [ObservableProperty] private string _centralWorktreeRoot = "~/.claudeDo/worktrees"; [ObservableProperty] private string? _validationError; public bool IsCentralVisible => WorktreeRootStrategy == "central"; diff --git a/src/ClaudeDo.Installer/Pages/UiSettingsPage/UiSettingsPageViewModel.cs b/src/ClaudeDo.Installer/Pages/UiSettingsPage/UiSettingsPageViewModel.cs index 290a9655..58fa7c49 100644 --- a/src/ClaudeDo.Installer/Pages/UiSettingsPage/UiSettingsPageViewModel.cs +++ b/src/ClaudeDo.Installer/Pages/UiSettingsPage/UiSettingsPageViewModel.cs @@ -18,7 +18,7 @@ public partial class UiSettingsPageViewModel : ObservableObject, IInstallerPage public UserControl View => _view ??= new UiSettingsPageView { DataContext = this }; [ObservableProperty] private string _signalRUrl = "http://127.0.0.1:47821/hub"; - [ObservableProperty] private string _uiDbPath = "~/.todo-app/todo.db"; + [ObservableProperty] private string _uiDbPath = "~/.claudeDo/todo.db"; [ObservableProperty] private bool _isSynced = true; [ObservableProperty] private string? _validationError; diff --git a/src/ClaudeDo.Installer/Views/SettingsViewModel.cs b/src/ClaudeDo.Installer/Views/SettingsViewModel.cs index 50a7ea03..5a06325c 100644 --- a/src/ClaudeDo.Installer/Views/SettingsViewModel.cs +++ b/src/ClaudeDo.Installer/Views/SettingsViewModel.cs @@ -183,7 +183,7 @@ public partial class SettingsViewModel : ObservableObject { var dataNote = RemoveAppData ? "This will remove ClaudeDo AND delete all of your tasks, configuration, and database.\n\nContinue?" - : "This will remove ClaudeDo. Your tasks, configuration, and database in ~/.todo-app will be kept.\n\nContinue?"; + : "This will remove ClaudeDo. Your tasks, configuration, and database in ~/.claudeDo will be kept.\n\nContinue?"; var confirm = MessageBox.Show( dataNote, diff --git a/src/ClaudeDo.Localization/locales/de.json b/src/ClaudeDo.Localization/locales/de.json index 617e165e..4cbd50b4 100644 --- a/src/ClaudeDo.Localization/locales/de.json +++ b/src/ClaudeDo.Localization/locales/de.json @@ -593,7 +593,7 @@ "subtitle": "Führt die Systemprüfungen erneut aus, gegen deine installierte Konfiguration." }, "settings": { - "removeUserData": "Benutzerdaten entfernen (Aufgaben, Logs, Konfigurationen in ~/.todo-app)", + "removeUserData": "Benutzerdaten entfernen (Aufgaben, Logs, Konfigurationen in ~/.claudeDo)", "uninstall": "Deinstallieren", "repair": "Reparieren", "save": "Speichern", diff --git a/src/ClaudeDo.Localization/locales/en.json b/src/ClaudeDo.Localization/locales/en.json index c39e5a46..5893d074 100644 --- a/src/ClaudeDo.Localization/locales/en.json +++ b/src/ClaudeDo.Localization/locales/en.json @@ -593,7 +593,7 @@ "subtitle": "Re-run the environment checks against your installed configuration." }, "settings": { - "removeUserData": "Remove user data (tasks, logs, configs in ~/.todo-app)", + "removeUserData": "Remove user data (tasks, logs, configs in ~/.claudeDo)", "uninstall": "Uninstall", "repair": "Repair", "save": "Save", diff --git a/src/ClaudeDo.Ui/AppSettings.cs b/src/ClaudeDo.Ui/AppSettings.cs index fad7fe9d..863b5cd7 100644 --- a/src/ClaudeDo.Ui/AppSettings.cs +++ b/src/ClaudeDo.Ui/AppSettings.cs @@ -6,7 +6,7 @@ namespace ClaudeDo.Ui; public sealed class AppSettings { - public string DbPath { get; set; } = "~/.todo-app/todo.db"; + public string DbPath { get; set; } = "~/.claudeDo/todo.db"; public string SignalRUrl { get; set; } = "http://127.0.0.1:47821/hub"; public string Language { get; set; } = ""; public string AccentPreset { get; set; } = ""; @@ -16,7 +16,7 @@ public sealed class AppSettings public string DiffViewMode { get; set; } = "unified"; public bool DiffWrapLines { get; set; } - private static readonly string DefaultConfigPath = Paths.Expand("~/.todo-app/ui.config.json"); + private static readonly string DefaultConfigPath = Paths.Expand("~/.claudeDo/ui.config.json"); /// Where this instance persists. Instance-level (not static) so tests can redirect it — /// the diff-viewer toggles call Save() on every flip. diff --git a/src/ClaudeDo.Ui/CLAUDE.md b/src/ClaudeDo.Ui/CLAUDE.md index 48ec46fe..82fe2a7e 100644 --- a/src/ClaudeDo.Ui/CLAUDE.md +++ b/src/ClaudeDo.Ui/CLAUDE.md @@ -89,7 +89,7 @@ new editor boilerplate there rather than copying it a third time. - **IPrimeScheduleApi** — prime-schedule CRUD. - **UpdateCheckService** — polls releases; `LastCheckStatus`/`LatestVersion`/`CheckNowAsync` feed the shell's update banner. - **InheritanceResolver** — resolves the task → list → global override chain to `(value, source)` for the inherited badges. -- **OperationTiming** — NDJSON sink (`~/.todo-app/logs/operation-timing.ndjson`, one rolled `.1` at 4 MB) behind every hub invoke and bulk DB path; each line carries `pid` because app restarts interleave in one file. Off by default — `Shared` only writes when the environment variable `CLAUDEDO_OP_TIMING=1` is set at process start (read once statically, no per-call overhead). Successful calls under 25 ms are dropped, **failed/cancelled ones always land** — a 1 ms cancelled `BindAsync` is churn signal, not noise. `DetailsIsland.BindAsync:` carries the selection trigger (`TasksIslandViewModel.SelectionSource`, set via `SelectFrom` — never assign `SelectedTask` directly). `Shared` is a settable static singleton **on purpose** — `Ui.Tests`/`Worker.Tests` each carry a `TestSetup` module initializer that redirects it to a temp file before any test runs (still writing — the kill switch only gates `Shared`'s default, not the constructor), because both projects construct real ViewModels (`DetailsIslandViewModel`, `TasksIslandViewModel`) that call `Shared.Record` directly; without the redirect a single test run appends 50-100 lines straight into the live app's log (a day of ~1900 `BindAsync` lines, 91-96% cancelled, turned out to be 13 such test runs plus pre-fix-binary artifacts — real user-driven binds that day: 4). +- **OperationTiming** — NDJSON sink (`~/.claudeDo/logs/operation-timing.ndjson`, one rolled `.1` at 4 MB) behind every hub invoke and bulk DB path; each line carries `pid` because app restarts interleave in one file. Off by default — `Shared` only writes when the environment variable `CLAUDEDO_OP_TIMING=1` is set at process start (read once statically, no per-call overhead). Successful calls under 25 ms are dropped, **failed/cancelled ones always land** — a 1 ms cancelled `BindAsync` is churn signal, not noise. `DetailsIsland.BindAsync:` carries the selection trigger (`TasksIslandViewModel.SelectionSource`, set via `SelectFrom` — never assign `SelectedTask` directly). `Shared` is a settable static singleton **on purpose** — `Ui.Tests`/`Worker.Tests` each carry a `TestSetup` module initializer that redirects it to a temp file before any test runs (still writing — the kill switch only gates `Shared`'s default, not the constructor), because both projects construct real ViewModels (`DetailsIslandViewModel`, `TasksIslandViewModel`) that call `Shared.Record` directly; without the redirect a single test run appends 50-100 lines straight into the live app's log (a day of ~1900 `BindAsync` lines, 91-96% cancelled, turned out to be 13 such test runs plus pre-fix-binary artifacts — real user-driven binds that day: 4). - **RepoScanner**, **InstallArtifactLocator**/**InstallerLocator**/**WorkerLocator**, **ForegroundHelper** (Win32 foreground before launching a terminal), **FocusClearing**. ## Converters diff --git a/src/ClaudeDo.Worker/CLAUDE.md b/src/ClaudeDo.Worker/CLAUDE.md index 2fa0ef39..adbe7f43 100644 --- a/src/ClaudeDo.Worker/CLAUDE.md +++ b/src/ClaudeDo.Worker/CLAUDE.md @@ -129,7 +129,7 @@ Full flow, invariants, and model/effort/max-turns resolution (including the low- - **WorktreeManager** — worktrees on `claudedo/{taskId[:8]}` branches; commits with semantic messages, updates DB with head commit + diff stats - **CommitMessageBuilder** — `{commitType}(slug): title\n\ndescription\n\nClaudeDo-Task: taskId`; `BuildMerge` is the merge-commit variant (`{commitType}(slug): merge title` + trailer). **Every merge caller passes a blank commit message on purpose** — `TaskMergeService` fills in `BuildMerge` from the task's commit type and its list's name, which is the only place that knows both. Don't reintroduce a caller-side literal. - **TaskResetService** — discards a failed task's worktree, resets the row to Idle, preserves run history -- **AgentFileService** — manages `~/.todo-app/agents/*.md`; list/refresh via SignalR +- **AgentFileService** — manages `~/.claudeDo/agents/*.md`; list/refresh via SignalR - **LogWriter** — async StreamWriter wrapper, auto-creates parent dirs Each CLI invocation is recorded in `task_runs` via `TaskRunRepository`. ⚠️ Token fields come from @@ -240,14 +240,14 @@ non-obvious, behaviour-changing. A fixed bug is git history, not a finding. ## Config -`~/.todo-app/worker.config.json`: +`~/.claudeDo/worker.config.json`: - `db_path`, `sandbox_root`, `log_root` - `worktree_root_strategy` (`sibling` | `central`), `central_worktree_root` - `queue_backstop_interval_ms` (30000) — also the gate/throttle recovery timer - `signalr_port` (47821), `claude_bin` - `usage_poll_interval_seconds` (60, clamped to min 15 on load) -- `online_inbox` — `enabled` (false by default; when false the entire `Online/` stack is not registered), `api_base_url` (must be HTTPS or loopback, validated at startup), `poll_interval_seconds` (60), `zitadel.authority`/`client_id`/`scopes`. The refresh token is **not** in this file — DPAPI-encrypted at `~/.todo-app/online-inbox.token`. +- `online_inbox` — `enabled` (false by default; when false the entire `Online/` stack is not registered), `api_base_url` (must be HTTPS or loopback, validated at startup), `poll_interval_seconds` (60), `zitadel.authority`/`client_id`/`scopes`. The refresh token is **not** in this file — DPAPI-encrypted at `~/.claudeDo/online-inbox.token`. Per-list config (`list_config` in DB) provides defaults for `model`, `system_prompt`, `agent_path`, `max_turns`, `session_skills`; tasks override each individually. `verify_command` is diff --git a/src/ClaudeDo.Worker/Config/WorkerConfig.cs b/src/ClaudeDo.Worker/Config/WorkerConfig.cs index 2d19b319..3045bdd8 100644 --- a/src/ClaudeDo.Worker/Config/WorkerConfig.cs +++ b/src/ClaudeDo.Worker/Config/WorkerConfig.cs @@ -9,20 +9,20 @@ namespace ClaudeDo.Worker.Config; public sealed class WorkerConfig { [JsonPropertyName("db_path")] - public string DbPath { get; set; } = "~/.todo-app/todo.db"; + public string DbPath { get; set; } = "~/.claudeDo/todo.db"; [JsonPropertyName("sandbox_root")] - public string SandboxRoot { get; set; } = "~/.todo-app/sandbox"; + public string SandboxRoot { get; set; } = "~/.claudeDo/sandbox"; [JsonPropertyName("log_root")] - public string LogRoot { get; set; } = "~/.todo-app/logs"; + public string LogRoot { get; set; } = "~/.claudeDo/logs"; /// "sibling" → place worktrees next to the target repo; "central" → under . [JsonPropertyName("worktree_root_strategy")] public string WorktreeRootStrategy { get; set; } = "sibling"; [JsonPropertyName("central_worktree_root")] - public string CentralWorktreeRoot { get; set; } = "~/.todo-app/worktrees"; + public string CentralWorktreeRoot { get; set; } = "~/.claudeDo/worktrees"; [JsonPropertyName("queue_backstop_interval_ms")] public int QueueBackstopIntervalMs { get; set; } = 30_000; diff --git a/src/ClaudeDo.Worker/Online/OnlineTokenStore.cs b/src/ClaudeDo.Worker/Online/OnlineTokenStore.cs index 29a75100..b07d9697 100644 --- a/src/ClaudeDo.Worker/Online/OnlineTokenStore.cs +++ b/src/ClaudeDo.Worker/Online/OnlineTokenStore.cs @@ -7,7 +7,7 @@ namespace ClaudeDo.Worker.Online; /// /// Persists the Zitadel refresh token encrypted with DPAPI (CurrentUser scope). -/// Windows-only; the file lives at ~/.todo-app/online-inbox.token. +/// Windows-only; the file lives at ~/.claudeDo/online-inbox.token. /// [SupportedOSPlatform("windows")] public sealed class OnlineTokenStore diff --git a/src/ClaudeDo.Worker/Planning/PlanningSessionManager.cs b/src/ClaudeDo.Worker/Planning/PlanningSessionManager.cs index dd51f714..a3e1b554 100644 --- a/src/ClaudeDo.Worker/Planning/PlanningSessionManager.cs +++ b/src/ClaudeDo.Worker/Planning/PlanningSessionManager.cs @@ -382,7 +382,7 @@ public sealed class PlanningSessionManager await File.WriteAllTextAsync(path, token, ct); // Best-effort current-user-only ACL on Windows. On non-Windows the inherited // perms from the parent dir apply; acceptable because sessionDir is already - // under the user's home (~/.todo-app/sessions/). + // under the user's home (~/.claudeDo/sessions/). if (OperatingSystem.IsWindows()) { try diff --git a/src/ClaudeDo.Worker/Planning/WindowsTerminalLauncher.cs b/src/ClaudeDo.Worker/Planning/WindowsTerminalLauncher.cs index a4a848bf..1d52badd 100644 --- a/src/ClaudeDo.Worker/Planning/WindowsTerminalLauncher.cs +++ b/src/ClaudeDo.Worker/Planning/WindowsTerminalLauncher.cs @@ -8,7 +8,7 @@ // wt.exe treats ';' as a tab/command delimiter in EVERY argument, regardless of // quoting (microsoft/terminal#13264), so nothing containing ';' may appear on the wt // command line. Every token we place there is a controlled constant or a filesystem -// path under ~/.todo-app/sessions/ — none can contain ';' — and each is +// path under ~/.claudeDo/sessions/ — none can contain ';' — and each is // single-quoted for PowerShell. // // The free-text task brief is NEVER passed as an argument. An interactive `claude` diff --git a/src/ClaudeDo.Worker/Program.cs b/src/ClaudeDo.Worker/Program.cs index 77330665..64850d0b 100644 --- a/src/ClaudeDo.Worker/Program.cs +++ b/src/ClaudeDo.Worker/Program.cs @@ -36,6 +36,8 @@ var mutex = new Mutex(true, @"Local\ClaudeDoWorker", out var createdNew); if (!createdNew) return; // another instance already owns the port; exit 0 +Paths.MigrateLegacyAppDataRoot(); + var cfg = WorkerConfig.Load(); var builder = WebApplication.CreateBuilder(args); @@ -171,9 +173,7 @@ builder.Services.AddSingleton(); builder.Services.AddHostedService(sp => sp.GetRequiredService()); // Planning session services. -var planningSessionsDir = Path.Combine( - Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), - ".todo-app", "planning-sessions"); +var planningSessionsDir = Path.Combine(Paths.AppDataRoot(), "planning-sessions"); builder.Services.AddSingleton(sp => new PlanningSessionManager( sp.GetRequiredService>(), diff --git a/src/ClaudeDo.Worker/Runner/InteractiveLaunchSpecService.cs b/src/ClaudeDo.Worker/Runner/InteractiveLaunchSpecService.cs index d9548dec..9b7134b8 100644 --- a/src/ClaudeDo.Worker/Runner/InteractiveLaunchSpecService.cs +++ b/src/ClaudeDo.Worker/Runner/InteractiveLaunchSpecService.cs @@ -261,7 +261,7 @@ public sealed class InteractiveLaunchSpecService /// Builds a LaunchSpec for an embedded ConPTY "merge helper" session that drives the /// given tasks to a merged/Done state via the mcp__claudedo__* tools. Writes a per-session - /// system prompt + task brief under ~/.todo-app/merge-helper-sessions/<guid> and exposes + /// system prompt + task brief under ~/.claudeDo/merge-helper-sessions/<guid> and exposes /// that dir plus the list's repo dir via --add-dir. cwd is the list's working directory. /// handlerTaskId (the id returned by CreateMergeHelperTaskAsync) is rendered into the brief so /// the session can call handoff_list_handler/submit_task_for_review on its own handler task.