Removes SourceDirectory field (no longer in InstallContext), adds
dynamic Heading/Subheading/InstallDirEditable for FreshInstall vs Update
mode, and updates XAML to match sibling page style.
Placeholder edit to App.xaml.cs to keep the project building until Task 11
wires the new async detector.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
17-task TDD plan for rewriting the installer to fetch binaries from
releases/ClaudeDo on git.kuns.dev. Covers InstallManifest, ReleaseClient,
InstallModeDetector, DownloadAndExtractStep, Config/Repair/Uninstall,
and the publish-time single-file self-contained settings.
Workflow file is out of scope (handled by VPS Claude).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- App + Worker now self-contained (zero .NET runtime dep on target)
- Collapse Manage mode into "update check -> Config view" on every
subsequent launch; Repair + Uninstall become buttons in Config
- Uninstall removes {InstallDir} and ~/.todo-app in full (no prompt
to keep data) — matches user's stated intent
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
VPS confirmed the releases/ org is world-readable without auth; the
ClaudeDo source already lives at git.kuns.dev/releases/ClaudeDo, so the
workflow uses the built-in gitea.token (no cross-org PAT needed).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Design for rewriting the installer to fetch prebuilt binaries from Gitea
Releases on git.kuns.dev instead of building from source. Covers the
Actions workflow, release artifact layout, install.json marker file,
Install/Update/Manage mode detection, and the new DownloadAndExtractStep.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove wrapping ScrollViewer from live output TextBox — Avalonia
TextBox with AcceptsReturn handles its own scrolling; nested
ScrollViewer caused layout collapse
- Auto-scroll via CaretIndex instead of removed ScrollViewer
- Add OnWindowClosed to both editor ViewModels, ensuring the
TaskCompletionSource always resolves when the dialog closes
(including via X button), preventing RelayCommand from staying
permanently disabled
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix event handler leak in TaskDetailView.OnDataContextChanged
(unsubscribe from previous ViewModel before subscribing to new one)
- Move FormatFile call off UI thread with Task.Run to prevent freeze
on large log files
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds Model, SystemPrompt, and AgentPath override fields to TaskEditorViewModel and TaskEditorView. Wires agent loading from WorkerClient in TaskListViewModel before opening the editor dialog.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace LiveLines with formatted LiveText + StreamLineFormatter
- Add log reload from disk for completed tasks
- Add start feedback (starting.../running) on detail and list views
- Apply dark theme (WindowBgBrush, AccentBrush) to editor modals
- Add model/system-prompt/agent-path config to ListEditorView
- Wire config loading/saving in MainWindowViewModel
- Fix duplicate AgentInfo DTO (use canonical Data.Models version)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Parses Claude CLI stream-json output into human-readable text.
Handles text deltas, tool use, results, API retries, and trims large files.
All 13 tests pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Covers four issues from first real test: raw NDJSON display,
missing start feedback, lost live output, and config editor gaps.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace flat columns with rounded island panels on a dark base.
Remove GridSplitters, add gap-based spacing between islands.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Subscribe to WorkerClient.PropertyChanged in TaskListViewModel and call
NotifyCanExecuteChanged on every TaskItemViewModel.RunNowCommand when
IsConnected flips, so the button enables immediately on reconnect without
waiting for the next task DB refresh.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add Background="Transparent" and HorizontalAlignment="Stretch" to the
DataTemplate roots in MainWindow.axaml and TaskListView.axaml so that
DoubleTapped, PointerPressed and ContextFlyout fire reliably across the
entire row surface.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add PointerPressed handlers on list/task item templates that set SelectedList/SelectedTask on right-click before the ContextFlyout opens
- Add CanAddTask guard and NotifyCanExecuteChangedFor on CurrentListId so Add Task menu item is disabled when no list is selected
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove redundant SelectedList/SelectedTask assignments (single-tap already
updated selection via binding) and CanExecute guards (always true for
unconditional [RelayCommand]). Set e.Handled = true to stop DoubleTapped
from bubbling to the ListBox.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix _startCts lifecycle leak: old CTS is cancelled and disposed before replacement
- Guard StartAsync re-entrancy: early-return if retry loop is still running (lock + IsCompleted check)
- Await _retryLoopTask in both StopAsync and DisposeAsync before stopping/disposing the hub
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add _startCts?.Cancel() at the top of DisposeAsync to prevent
ConnectWithRetryAsync from calling _hub.StartAsync on an already-disposed
HubConnection when the caller disposes without first calling StopAsync.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add IndefiniteRetryPolicy for WithAutomaticReconnect, wrap initial
StartAsync in a retry loop so the UI keeps retrying when Worker is
offline at startup, and expose IsReconnecting to StatusBar
("Connecting..." / "Online" / "Offline").
App: build a ServiceProvider in Program.cs (AppSettings, SqliteConnectionFactory,
all repositories, GitService, WorkerClient, all view-models), apply schema, then
hand control to Avalonia. App.OnFrameworkInitializationCompleted resolves
MainWindowViewModel from the container.
Ui:
- AppSettings POCO loaded from ~/.todo-app/ui.config.json (db path, hub url).
- WorkerClient wraps HubConnection with auto-reconnect, exposes IsConnected and
ActiveTasks plus C# events for TaskStarted/Finished/Message/Updated and
WorktreeUpdated; all inbound events are marshalled to the UI thread.
- ViewModels: MainWindow (lists CRUD via ListEditor dialog), TaskList (load by
list, add/edit/delete, auto WakeQueue on agent+queued create), TaskItem
(RunNow gated on connection + status), TaskDetail (description, result, live
ndjson rolling buffer of 500 lines, worktree branch/diff with merge/keep/
discard via GitService), StatusBar, ListEditor, TaskEditor.
- Views: 3-pane MainWindow (lists | tasks | detail) with GridSplitters, status
bar, dialog windows for the editors. Status badges via StatusColorConverter.
- Markdown rendering, folder picker, delete-confirmation, settings dialog and
scroll-to-bottom on the live log are intentionally TODO -- functional
scaffold only.
Tests: also debounce the FIFO queue test (poll instead of Task.Delay(200)) so
the assertion isn't racy when the suite runs alongside the slower git tests.
38 tests pass.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
GitService (in ClaudeDo.Data so the UI can reuse it) wraps the git CLI:
IsGitRepo, RevParseHead, WorktreeAdd/Remove, HasChanges, AddAll, Commit
(multi-line via -F -), DiffStat, BranchDelete, MergeFfOnly. Throws with
stderr on failure.
WorktreeManager owns the per-task lifecycle: validate working_dir is a
git repo (throws if not, no DB row written), create the worktree at
<repo>/../.claudedo-worktrees/<slug>/<id>/ (or central root per config),
insert the worktrees row. CommitIfChangedAsync skips when there are no
changes, otherwise commits and updates head_commit + diff_stat.
CommitMessageBuilder produces "{type}({list-slug}): {title<=60}" with a
blank-line-separated description (truncated to 400) and a permanent
"ClaudeDo-Task: <id>" trailer. Slug normalises whitespace + strips
non-alphanumerics. Newlines hard-coded to \n so git on Windows doesn't
choke on \r\n.
TaskRunner branches on list.WorkingDir: worktree path runs Claude in the
worktree, commits on success, broadcasts WorktreeUpdated; failure leaves
the worktree row active for inspection. Sandbox path unchanged.
Tests: 38 pass (12 new). GitRepoFixture spins up a real temp repo with a
seed commit; tests skip gracefully if `git` isn't on PATH.
CommitMessageBuilder fully unit-tested. WorktreeManager covers create,
no-change skip, real-commit, and non-repo failure.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Runner stack (non-worktree path): IClaudeProcess + ClaudeProcess spawning the
CLI with --output-format stream-json, prompt via stdin, parses the final
type:"result" line into RunResult. LogWriter appends ndjson to
~/.todo-app/logs/<taskId>.ndjson. TaskRunner orchestrates DB transitions
(MarkRunning -> MarkDone/Failed) and pushes TaskStarted/Message/Finished/
Updated via HubBroadcaster. Worktree-backed lists short-circuit with a
"Slice E" failure message until git support lands.
QueueService (BackgroundService) holds two in-memory slots (_queueSlot +
_overrideSlot) guarded by a lock. Uses PeriodicTimer + SemaphoreSlim wake
signal so WakeQueue() triggers an instant pickup. RunNow throws
InvalidOperationException when override busy; CancelTask cancels the linked
CTS which kills the child process tree.
WorkerHub extended with GetActive, RunNow (translated to HubException
variants), CancelTask, WakeQueue. HubBroadcaster exposes typed push methods.
Tests: 26 pass (12 new). QueueServiceTests cover override-busy,
schedule-filter, FIFO sequentiality, cancellation, plus a FakeClaudeProcess
that blocks on a TCS for deterministic slot-state assertions.
MessageParserTests cover result extraction + malformed/non-result lines.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Data: TagRepository, ListRepository, TaskRepository (incl. queue selection via
effective agent tag + scheduled_for filter), WorktreeRepository. All CRUD via
parameterized SqliteCommand; enums roundtrip as lowercase strings matching the
schema CHECK constraints.
Worker: StaleTaskRecovery IHostedService flips running -> failed on startup and
marks the result column with a [stale] reason. All four repositories registered
as singletons.
Tests: DbFixture with temp-file SQLite + schema bootstrap, covering
TaskRepository (queue pick via list-tag and task-tag, schedule filter,
transitions, stale flip), ListRepository CRUD + junctions, and
StaleTaskRecovery. 14 tests pass.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Data layer: Paths helper with ~/%USERPROFILE% expansion, SqliteConnectionFactory
(WAL + foreign keys), SchemaInitializer that applies the embedded schema.sql, and
POCO entities for lists/tasks/tags/worktrees.
Worker: WorkerConfig loader (~/.todo-app/worker.config.json with defaults),
WorkerHub exposing Ping(), and Program.cs wiring Kestrel to 127.0.0.1:<port>,
SignalR at /hub, schema applied on startup.
Pins Microsoft.Data.Sqlite and Microsoft.Extensions.Hosting to 8.x for net8.0
compatibility.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>