diff --git a/src/ClaudeDo.Ui/Services/Interfaces/IWorkerClient.cs b/src/ClaudeDo.Ui/Services/Interfaces/IWorkerClient.cs index dffe671..78a7ace 100644 --- a/src/ClaudeDo.Ui/Services/Interfaces/IWorkerClient.cs +++ b/src/ClaudeDo.Ui/Services/Interfaces/IWorkerClient.cs @@ -33,6 +33,10 @@ public interface IWorkerClient : INotifyPropertyChanged Task GetListConfigAsync(string listId); Task UpdateTaskAgentSettingsAsync(UpdateTaskAgentSettingsDto dto); Task SetTaskStatusAsync(string taskId, TaskStatus status); + Task ApproveReviewAsync(string taskId); + Task RejectReviewToQueueAsync(string taskId, string feedback); + Task RejectReviewToIdleAsync(string taskId); + Task CancelReviewAsync(string taskId); Task StartPlanningSessionAsync(string taskId, CancellationToken ct = default); Task OpenInteractiveTerminalAsync(string taskId, CancellationToken ct = default); Task ResumePlanningSessionAsync(string taskId, CancellationToken ct = default); diff --git a/src/ClaudeDo.Ui/Services/WorkerClient.cs b/src/ClaudeDo.Ui/Services/WorkerClient.cs index 04d91a0..a0e0d76 100644 --- a/src/ClaudeDo.Ui/Services/WorkerClient.cs +++ b/src/ClaudeDo.Ui/Services/WorkerClient.cs @@ -349,6 +349,26 @@ public partial class WorkerClient : ObservableObject, IAsyncDisposable, IWorkerC await _hub.InvokeAsync("SetTaskStatus", taskId, status.ToString()); } + public async Task ApproveReviewAsync(string taskId) + { + await _hub.InvokeAsync("ApproveReview", taskId); + } + + public async Task RejectReviewToQueueAsync(string taskId, string feedback) + { + await _hub.InvokeAsync("RejectReviewToQueue", taskId, feedback); + } + + public async Task RejectReviewToIdleAsync(string taskId) + { + await _hub.InvokeAsync("RejectReviewToIdle", taskId); + } + + public async Task CancelReviewAsync(string taskId) + { + await _hub.InvokeAsync("CancelReview", taskId); + } + public Task CleanupFinishedWorktreesAsync(string? listId = null) => TryInvokeAsync("CleanupFinishedWorktrees", listId); diff --git a/src/ClaudeDo.Worker/Hub/WorkerHub.cs b/src/ClaudeDo.Worker/Hub/WorkerHub.cs index c5b14db..f8b44b0 100644 --- a/src/ClaudeDo.Worker/Hub/WorkerHub.cs +++ b/src/ClaudeDo.Worker/Hub/WorkerHub.cs @@ -361,6 +361,30 @@ public sealed class WorkerHub : Microsoft.AspNetCore.SignalR.Hub if (!result.Ok) throw new HubException(result.Reason ?? "set status failed"); } + public async Task ApproveReview(string taskId) + { + var result = await _state.ApproveReviewAsync(taskId, Context.ConnectionAborted); + if (!result.Ok) throw new HubException(result.Reason ?? "approve failed"); + } + + public async Task RejectReviewToQueue(string taskId, string feedback) + { + var result = await _state.RejectToQueueAsync(taskId, feedback, Context.ConnectionAborted); + if (!result.Ok) throw new HubException(result.Reason ?? "reject failed"); + } + + public async Task RejectReviewToIdle(string taskId) + { + var result = await _state.RejectToIdleAsync(taskId, Context.ConnectionAborted); + if (!result.Ok) throw new HubException(result.Reason ?? "park failed"); + } + + public async Task CancelReview(string taskId) + { + var result = await _state.CancelAsync(taskId, DateTime.UtcNow, Context.ConnectionAborted); + if (!result.Ok) throw new HubException(result.Reason ?? "cancel failed"); + } + public async Task UpdateTaskAgentSettings(UpdateTaskAgentSettingsDto dto) { using var ctx = _dbFactory.CreateDbContext(); diff --git a/tests/ClaudeDo.Ui.Tests/StubWorkerClient.cs b/tests/ClaudeDo.Ui.Tests/StubWorkerClient.cs index 22498dd..2c2c914 100644 --- a/tests/ClaudeDo.Ui.Tests/StubWorkerClient.cs +++ b/tests/ClaudeDo.Ui.Tests/StubWorkerClient.cs @@ -40,6 +40,10 @@ public abstract class StubWorkerClient : IWorkerClient public virtual Task GetListConfigAsync(string listId) => Task.FromResult(null); public virtual Task UpdateTaskAgentSettingsAsync(UpdateTaskAgentSettingsDto dto) => Task.CompletedTask; public virtual Task SetTaskStatusAsync(string taskId, TaskStatus status) => Task.CompletedTask; + public virtual Task ApproveReviewAsync(string taskId) => Task.CompletedTask; + public virtual Task RejectReviewToQueueAsync(string taskId, string feedback) => Task.CompletedTask; + public virtual Task RejectReviewToIdleAsync(string taskId) => Task.CompletedTask; + public virtual Task CancelReviewAsync(string taskId) => Task.CompletedTask; public virtual Task StartPlanningSessionAsync(string taskId, CancellationToken ct = default) => Task.CompletedTask; public virtual Task OpenInteractiveTerminalAsync(string taskId, CancellationToken ct = default) => Task.CompletedTask; public virtual Task ResumePlanningSessionAsync(string taskId, CancellationToken ct = default) => Task.CompletedTask; diff --git a/tests/ClaudeDo.Worker.Tests/UiVm/TasksIslandViewModelPlanningTests.cs b/tests/ClaudeDo.Worker.Tests/UiVm/TasksIslandViewModelPlanningTests.cs index 0e2d26e..c74df15 100644 --- a/tests/ClaudeDo.Worker.Tests/UiVm/TasksIslandViewModelPlanningTests.cs +++ b/tests/ClaudeDo.Worker.Tests/UiVm/TasksIslandViewModelPlanningTests.cs @@ -42,6 +42,10 @@ sealed class FakeWorkerClient : IWorkerClient public Task GetListConfigAsync(string listId) => Task.FromResult(null); public Task UpdateTaskAgentSettingsAsync(UpdateTaskAgentSettingsDto dto) => Task.CompletedTask; public Task SetTaskStatusAsync(string taskId, TaskStatus status) => Task.CompletedTask; + public Task ApproveReviewAsync(string taskId) => Task.CompletedTask; + public Task RejectReviewToQueueAsync(string taskId, string feedback) => Task.CompletedTask; + public Task RejectReviewToIdleAsync(string taskId) => Task.CompletedTask; + public Task CancelReviewAsync(string taskId) => Task.CompletedTask; public Task WakeQueueAsync() { WakeQueueCalls++; return Task.CompletedTask; } public Task StartPlanningSessionAsync(string taskId, CancellationToken ct = default) { StartPlanningCalls++; return Task.CompletedTask; } public Task OpenInteractiveTerminalAsync(string taskId, CancellationToken ct = default) => Task.CompletedTask;