Commit Graph
1782 Commits
Author SHA1 Message Date
mika kuns 074c0e4dbd feat(ui): show cost columns and the TokenTracker hint card in the usage monitor 2026-08-24 16:16:39 +02:00
mika kuns bac0d69a49 feat(ui): surface TokenTracker status and cost in the usage monitor 2026-08-24 16:15:02 +02:00
mika kuns 66f743dd43 feat(i18n): add TokenTracker analytics strings in en and de 2026-08-24 13:51:55 +02:00
mika kuns 3c3bb30696 feat(ui): add TokenTracker status, refresh and install to the worker client 2026-08-24 13:50:51 +02:00
mika kuns 40879705ac refactor(usage): drop the transcript aggregation path in favour of TokenTracker 2026-08-24 13:49:49 +02:00
mika kuns 44337d4b94 feat(usage): serve model and task usage from the TokenTracker export 2026-08-24 13:41:21 +02:00
mika kuns 3f3638c1ce feat(usage): cache the TokenTracker export with single-flight refresh 2026-08-24 13:39:20 +02:00
mika kuns a92180456d feat(usage): run the TokenTracker CLI through the shared executable resolver 2026-08-24 13:37:57 +02:00
mika kuns d6b22ea528 feat(usage): pin the TokenTracker CLI invocations 2026-08-24 13:36:59 +02:00
mika kuns 580d4e0717 feat(usage): hold the last good TokenTracker export fail-open 2026-08-24 13:36:59 +02:00
mika kuns 2fca0cc4d3 feat(usage): aggregate TokenTracker sessions by model and per task 2026-08-24 13:35:36 +02:00
mika kuns 106f9a374a feat(usage): parse the TokenTracker sessions export defensively 2026-08-24 13:34:01 +02:00
mika kuns 16952b6432 feat(usage): add TokenTracker export models and test fixture 2026-08-24 13:32:46 +02:00
mika kuns 1cb574bd96 feat(usage): reproduce TokenTracker session_hash for task attribution 2026-08-24 13:31:29 +02:00
mika kuns 0e74650d70 docs(usage): spec for TokenTracker as the token-analytics backend 2026-08-24 10:29:56 +02:00
Mika Kuns 664816881a refactor(claude-do): merge TaskRowViewModel: CanX und DisabledReason aus einer Gate-Met
ClaudeDo-Task: 6c4b27aa-1b5e-4d73-98f5-dcafa3935247
2026-08-24 10:08:55 +02:00
Mika Kuns 67083e1a4a refactor(claude-do): merge Dedupe: FormatRemaining aus UsagePill und UsageMonitorModal
ClaudeDo-Task: 94ea71ad-22a1-4612-8743-425d5fb43d75
2026-08-24 10:02:22 +02:00
Mika Kuns 9c59c77a28 refactor(claude-do): merge Dedupe: ShellOpen-Helper für "Pfad im Explorer öffnen" (5 Co
ClaudeDo-Task: 4084c060-3cf0-4474-930a-ea79695b9ba6
2026-08-24 09:54:37 +02:00
Mika Kuns b2a1b79029 refactor(ui): dedupe path-open helper across five view models
ListsIslandViewModel.OpenInExplorer, MergeSectionViewModel.OpenWorktree,
WorktreesOverviewModalViewModel.OpenInExplorer, AboutModalViewModel.OpenPath and
TasksIslandViewModel.OpenTaskWorktree each reimplemented "open this path in the
shell" with their own existence check, launch mechanism and error handling.
Replace all five with the new ShellOpen.Path helper and route failures through
the existing ErrorReported -> footer-strip convention instead of bare catch {}.
2026-08-24 09:38:41 +02:00
Mika Kuns d309302043 refactor(claude-do): merge Dedupe: "Liste hat ein verlinktes Repo?" — ein Prädikat stat
ClaudeDo-Task: 594ee78e-7112-427a-9cde-76f6610e7fd8
2026-08-24 09:38:36 +02:00
Mika Kuns baba921696 refactor(ui): derive TaskRowViewModel CanX/DisabledReason pairs from one gate method
SendToQueue, Cancel, Refine, Planning and OpenWorktree each had a CanX property
and a hand-written negation of it for the DisabledReason tooltip, kept in sync
only by a comment's promise. Replace each pair with a private gate method
returning (Can, Reason) so there is one source of truth per condition, and add
a state-matrix test pinning down the CanX == (Reason == null) invariant that
the old comment only asserted.
2026-08-24 09:36:00 +02:00
Mika Kuns 9578c95074 refactor(ui): unify list-linked-repo predicate into RepoLinkage
ListsIslandViewModel and SettingsModalViewModel each re-implemented "does
this list have a linked WorkingDir" with different whitespace handling.
RepoLinkage.IsLinked/IsLinkedInDb is now the single definition; both
callers derive from it, closing the whitespace-only WorkingDir gap where
the Settings modal disagreed with the ListsIsland banner.
2026-08-24 09:28:20 +02:00
Mika Kuns e164f1e33f refactor(ui): dedupe FormatRemaining between UsagePill and UsageMonitorModal
Both viewmodels carried byte-identical copies of the reset-countdown
formatter. Moved it to UsageTimeFormat.FormatRemaining and pointed
both call sites at it; locale keys and output unchanged.
2026-08-24 09:27:15 +02:00
Mika Kuns 79438c00d2 feat(Data): Improve App Prompts 2026-08-24 09:25:43 +02:00
mika kuns 29171b104b fix(ui): failureReason "usage_limit" nachziehen + Worktree-Gate prüft die Platte
Zwei liegengebliebene Consumer aus den letzten beiden Commits:

- TaskRunner klassifiziert seit 07dd7570 "usage_limit", aber weder
  TaskRowViewModel.FailureReasonLabel noch vm.failureReason (de/en) noch
  die get_task-Tool-Beschreibung kannten den Wert — die UI zeigte
  "Grund unbekannt", das MCP-Doc listete weiterhin max_turns|timeout|error.
- TaskRowViewModel.CanOpenWorktree prüfte nur auf einen nicht-leeren
  String. Die Zeile behält den Path eines gemergten/verworfenen Worktrees,
  also war der Menüpunkt aktiv und Process.Start warf in den Footer.
  Jetzt zusätzlich Directory.Exists — dieselbe Prüfung, die
  WorktreesOverviewModalViewModel und MergeSectionViewModel schon machen.
2026-08-24 09:05:31 +02:00
mika kuns 3dfae30fff fix(worker): Usage-Limit-Auto-Continue verbrennt nicht mehr alle Kandidaten außer dem ersten
OverrideSlotService hält genau einen Slot, und ein Usage-Limit stoppt
naturgemäß alle laufenden Tasks gleichzeitig — "mehr Kandidaten als
Kapazität" ist also der Normalfall. Der Coordinator stempelte
UsageLimitAutoContinuedAt aber VOR dem ContinueTask (der Marker ist die
Dedupe-Guard), sodass Kandidat 2..n den Marker bekamen, deren
ContinueTask mit "override slot busy" in den catch flog und sie danach
dauerhaft aus GetUsageLimitAutoContinueCandidatesAsync ausgeschlossen
waren — ein Continue, der nie lief.

- Vorab-Check auf CurrentSlot: bei belegtem Slot bricht der Tick ab,
  statt die restlichen Kandidaten zu verbrennen.
- Neuer TaskRepository.ReleaseUsageLimitAutoContinueClaimAsync gibt den
  Claim zurück, wenn ContinueTask wirft; danach return, der 30s-Backstop
  holt den Rest im nächsten Tick.
- Regressionstest mit zwei Kandidaten, deterministisch über eine
  TaskCompletionSource im FakeClaudeProcess (StartInSlot setzt _slot
  synchron unter dem Lock, bevor die Arbeit startet).
2026-08-24 09:05:20 +02:00
mika kuns 55bfb765e6 feat(claude-do): merge feat(worker): Toggle "Continue on session limit reset" — lim
ClaudeDo-Task: 6a8a7151695d4b93bd6d13e66d9307c5
2026-08-21 19:16:20 +02:00
mika kuns 07dd75700d feat(worker): Toggle "Continue on session limit reset" für Usage-Limit-Abbrüche
Klassifiziert einen echten Usage-Limit-Abbruch als eigene FailureReason
"usage_limit" (TaskRunner.ClassifyFailureReason: nur bei terminal_reason
"api_error" plus einem Limit-Muster im gerenderten Fehlertext, nicht an
Status==Failed allein). Neuer Toggle AutoContinueOnUsageLimit (app_settings,
Default aus) unter Settings → General → "Usage limit stop":

- UsageLimitAutoContinueCoordinator feuert pro Task genau einmal ContinueTask
  über OverrideSlotService, sobald das 5h-Fenster (UsageState.Snapshot.FiveHour
  .ResetsAt) tatsächlich zurückgesetzt ist; ein persistenter Marker
  (TaskEntity.UsageLimitAutoContinuedAt) verhindert einen zweiten Anlauf bei
  einem erneuten Limit-Treffer.
- QueueService schedult zusätzlich einen exakten Wake-Timer auf den
  Reset-Zeitpunkt, statt nur auf den 30s-Backstop zu warten.
- Fail-open durchgängig: kein Snapshot/keine Reset-Zeit → kein Timer, kein
  Continue, kein Throw. Toggle aus ändert das heutige Verhalten nicht.

Migration AddUsageLimitAutoContinue fügt beide Spalten hinzu; die von
`dotnet ef migrations add` mitgescaffoldete leere UpdateData auf app_settings
(columns/values: []) erzeugte ungültiges SQL ("near WHERE") und wurde entfernt
— TaskNumberMigrationTests deckte das über den vollen Migrate()-Pfad auf.
2026-08-21 18:43:02 +02:00
mika kuns 290dd1b614 feat(claude-do): merge feat(ui): Hauptfenster-Close mit offenen Mission-Control-Ses
ClaudeDo-Task: a963f289b38449a3a8d0c52fc9bfd9e7
2026-08-21 18:20:44 +02:00
mika kuns 0892883a82 feat(ui): confirm main-window close when Mission Control sessions are open
Closing the main window used to silently kill every open Mission Control
ConPTY session (App's ShutdownMode.OnMainWindowClose tears the process down
without warning). Intercept Window.Closing, and when at least one pane is
open, ask via the existing ConfirmAsync pattern before proceeding; cancelling
leaves the window and sessions alive, confirming force-closes as before.
2026-08-21 18:14:48 +02:00
mika kuns 89d30d1463 feat(claude-do): merge feat(ui): "Worktree öffnen" im Kontextmenü der Task-Zeile
ClaudeDo-Task: 9be17e29e0b54f42ba9385b0cebe89d5
2026-08-21 18:14:45 +02:00
mika kuns eb2160dfc2 fix(claude-do): merge fix(worker): List-Handler-Session kennt ihre eigene Handler-
ClaudeDo-Task: 997d0ffa-48ae-42d7-bea0-3aed4cf81768
2026-08-21 18:09:02 +02:00
mika kuns b598ec2fac fix(claude-do): merge fix(ui): Usage-Pill bleibt nach App-Start leer — Initial-Loa
ClaudeDo-Task: cdb71ff5aefd4ae9819352a845e0be89
2026-08-21 18:03:03 +02:00
mika kuns 9c4a63d2c5 fix(worker): thread handler task id into list-handler kickoff prompts
Neither the initial nor the handoff kickoff ever told a list-handler
session its own handler task id, so handoff_list_handler(taskId, ...)
was unrenderable -- the handoff chain broke exactly where it was needed
(#200/#201 on 2026-08-21).

Add {handlerTaskId} to both MergeHelperInitialDefault and
MergeHelperHandoffDefault, thread a handlerTaskId parameter through
BuildForMergeHelperAsync (interface, WorkerHub.GetMergeHelperLaunchSpec,
IWorkerClient/WorkerClient, and the MissionControlViewModel call site,
which already had the id from CreateMergeHelperTaskAsync but never
passed it on), and render it in BuildForMergeHelperHandoffAsync from the
taskId parameter it already receives.

RenderTemplate leaves unknown/missing tokens untouched, so a
user-edited override without the new token still renders fine -- no
forced migration for override users.
2026-08-21 18:00:16 +02:00
mika kuns ac9b0dab29 feat(ui): "Worktree öffnen" im Kontextmenü der Task-Zeile
Group 1 of the task-row context menu gets a fourth gated entry, right
after "Open interactive session": it opens the task's worktree folder
via Process.Start/UseShellExecute, mirroring ListsIslandViewModel's
OpenInExplorer. Missing worktree greys the item out with a tooltip
reason instead of hiding it, keeping Group 1's fixed length. Failures
report through TasksIslandViewModel.ErrorReported (footer strip), not
a silent catch.
2026-08-21 17:58:31 +02:00
mika kuns 7f94bf4aed fix(ui): UsagePillViewModel loads snapshot on connection restore
The ctor-time LoadAsync races WorkerClient.StartAsync, so the SignalR
call fails silently and the pill only fills on the next worker poll
(up to 900s). Subscribe to ConnectionRestoredEvent, which also fires
on the first successful connect, to retry the cached snapshot.
2026-08-21 17:47:49 +02:00
mika kuns 93be76a144 feat(claude-do): merge feat(mcp): update_task kann isManual setzen + List-Handler s
ClaudeDo-Task: 0af82db9dd3e43eb9071f98f807ef452
2026-08-21 16:56:48 +02:00
mika kuns ea2271cc3e feat(mcp): update_task kann isManual setzen + List-Handler stellt seinen Task auf Review
Teil A: update_task bekommt einen optionalen isManual-Parameter (null =
unveraendert); TaskDto/TaskRefDto spiegeln IsManual zurueck.

Teil B: neuer submit_task_for_review MCP-Tool (LifecycleMcpTools) laesst
einen Handler-Task selbst auf WaitingForReview gehen -- fuer einen
worktree-losen Task wird HandlerHeadCommit vom aktuellen HEAD gestempelt,
sonst werden offene Aenderungen committet. Die Submit-Logik ist aus
WorkerHub.SubmitTaskForReview in InteractiveReviewSubmissionService
extrahiert; der Hub ruft sie jetzt nur noch auf. Der Merge-Prompt-Default
weist die Endzweige (merge ohne Rerun, merge_final) an, den eigenen
Handler-Task nach der Summary einzureichen.
2026-08-21 16:51:18 +02:00
mika kuns 36720d33ae fix(claude-do): merge fix(worker): Interaktive Session auf einem Nicht-Repo-Workin
ClaudeDo-Task: 15e126d564874973be2a5bbe7d796646
2026-08-21 16:51:15 +02:00
mika kuns 302d657644 feat(claude-do): merge feat(ui): Usage-Monitor zeigt hinter der Restzeit die Reset-
ClaudeDo-Task: 533f0a42c08e4044a8eaa9235f960dbe
2026-08-21 16:45:30 +02:00
mika kuns ff37a97ade fix(claude-do): merge fix(ui): Description-Editbox nutzt die volle Höhe der Descri
ClaudeDo-Task: 2e98a45690e842b7a498034dc9483a15
2026-08-21 16:39:47 +02:00
mika kuns 0d1dd169ac fix(claude-do): fix(worker): Interaktive Session auf einem Nicht-Repo-Workin
Eine interaktive ("Quick") Session soll auf Pfaden, die kein Git-Repo sind, keinen Worktree erzwingen, sondern direkt im ausgewählten Ordner starten. Der User hat entschieden: BEIDE Einstiegspunkte prüfen — den task-gebundenen Pfad fixen, den Ad-hoc-Pfad verifizieren.

## Pfad 1 (der eigentliche Fix) — task-gebundene Session

- Einstieg: Kontextmenü der Task-Zeile "Open interactive session" — src/

ClaudeDo-Task: 15e126d564874973be2a5bbe7d796646
2026-08-21 16:36:11 +02:00
mika kuns a6d165a177 fix(ui): Description-Editbox nutzt volle Kartenhoehe statt 320px-Deckel
Die Edit-TextBox war per festem MaxHeight auf 320px begrenzt, auch wenn die
Description-Card per Splitter groesser gezogen wurde. Die Card begrenzt die
Hoehe bereits selbst (2/3-Cap in UpdateRowLimits), daher stretcht die TextBox
jetzt ohne eigenen Deckel bis zur verfuegbaren Card-Hoehe.
2026-08-21 16:28:35 +02:00
mika kuns 8f983851b4 feat(ui): Usage-Monitor zeigt Reset-Uhrzeit hinter der Restzeit 2026-08-21 16:22:26 +02:00
mika kuns 8dce2d0898 chore(claude-do): merge feat(ui): Erststart-Banner und Textkette Repo→Task→Queue→Rev
ClaudeDo-Task: 60ad0392-ef5e-4204-9445-415788ccd45b
2026-08-21 15:54:21 +02:00
mika kuns 111dd0fb0d feat(ui): Erststart-Banner und Textkette Repo→Task→Queue→Review (UX-Struktur C)
HasNoLinkedRepo on ListsIslandViewModel drives a dismiss-less banner under the
Lists-Island header when no User list has a linked repo; recomputed on load
and after list CRUD / list-settings save. Sharpens the #192 empty-state texts
into one readable chain (banner → no-repo list → repo-linked list) and adds a
line to the WorkConsole review gate spelling out that Approve also merges the
whole unit.
2026-08-21 15:25:04 +02:00
mika kuns c47cdc1c7c chore(claude-do): merge feat(ui): Settings-Modal auf Sidebar-Kategorien umbauen (UX-
ClaudeDo-Task: 314cf7a5-ed81-4b55-895c-abc30301d02e
2026-08-21 15:08:32 +02:00
mika kuns 56c3719e98 feat(ui): Settings-Modal auf Sidebar-Kategorien umbauen
TabControl bleibt Content-Host, TabStrip wird retempliert (nur
PART_SelectedContentHost) und durch eine zweigruppige Sidebar
(BASIS/ERWEITERT) ersetzt. General wird in Allgemein/Ausfuehrung/Berichte
gesplittet, Session Skills (Checkbox-Liste aus General + Skills-Tab)
zu einer Liste zusammengelegt, und ein Repo-Hinweisstreifen erscheint
auf Worktrees/Prime Claude/Session Skills/Berichte solange keine Liste
ein WorkingDir hat. Fensterbreite 580 -> 700.
2026-08-21 15:04:15 +02:00
mika kuns a80a31eeac chore(claude-do): merge feat(ui): Task-Zeile entdichten und Kontextmenü gruppieren (
ClaudeDo-Task: a43e22e7-ba96-4ee0-8aa6-482f7c567db6
2026-08-21 14:39:10 +02:00
mika kuns 51dc7bd61b feat(ui): Task-Zeile entdichten und Kontextmenü gruppieren (UX-Struktur A)
Gruppe 1 (Send to queue, Remove from queue, Cancel execution, Open quick
session, Refine task) ist jetzt fix sichtbar und gated per IsEnabled + Grund
statt komplett auszublenden. Planning/Schedule wandern in Untermenüs (Mark as
existierte bereits); der Planning-Kopf zeigt einen Grund, wenn er sonst leer
wäre. Refine-Button, ungesetzter Star und Dequeue-X erscheinen nur bei
Hover-oder-Selektion (TaskRowViewModel.ShowRowActions); ein gesetzter Star und
der Refining-Spinner bleiben immer sichtbar. Der Branch-Chip wandert aus der
Zeile in eine Meta-Zeile in TaskHeaderBar.

A3 headless verifiziert: Avalonia 12 zeigt ToolTips auf IsEnabled=false
Controls nur mit ToolTip.ShowOnDisabled="True" (Default ist false) — dieses
Attached Property existiert bereits und wird für die neuen Disabled-Reason-
Tooltips genutzt.
2026-08-21 14:34:14 +02:00