feat(mcp): add get_task_config, continue_task; fix status enum, branchDeleted, merge-from-review
- ConfigMcpTools: add get_task_config read-back (was write-only) - ExternalMcpService: add WaitingForChildren to ListTasks filter and GetTaskStatusValues - ExternalMcpService: add continue_task tool wrapping QueueService.ContinueTask - ExternalMcpService: add allowWaitingForReview param to merge_task (default false) - ExternalMcpService: fix CleanupTaskWorktree branchDeleted — now uses real branch-delete outcome - WorktreeMaintenanceService: TryRemoveAsync returns (Removed, BranchDeleted) tuple; ForceRemoveResult gains BranchDeleted field - Tests: 9 new cases covering all five changes Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -9,7 +9,7 @@ public sealed class WorktreeMaintenanceService
|
||||
{
|
||||
public sealed record CleanupResult(int Removed, IReadOnlyList<string> RemovedTaskIds);
|
||||
public sealed record ResetResult(int Removed, int TasksAffected, bool Blocked, int RunningTasks, IReadOnlyList<string> RemovedTaskIds);
|
||||
public sealed record ForceRemoveResult(bool Removed, string? Reason);
|
||||
public sealed record ForceRemoveResult(bool Removed, string? Reason, bool BranchDeleted);
|
||||
|
||||
private readonly IDbContextFactory<ClaudeDoDbContext> _dbFactory;
|
||||
private readonly GitService _git;
|
||||
@@ -43,7 +43,8 @@ public sealed class WorktreeMaintenanceService
|
||||
var removedTaskIds = new List<string>();
|
||||
foreach (var row in rows)
|
||||
{
|
||||
if (await TryRemoveAsync(row, force: false, ct))
|
||||
var (rowRemoved, _) = await TryRemoveAsync(row, force: false, ct);
|
||||
if (rowRemoved)
|
||||
{
|
||||
removed++;
|
||||
removedTaskIds.Add(row.TaskId);
|
||||
@@ -71,7 +72,8 @@ public sealed class WorktreeMaintenanceService
|
||||
var removedTaskIds = new List<string>();
|
||||
foreach (var row in rows)
|
||||
{
|
||||
if (await TryRemoveAsync(row, force: true, ct))
|
||||
var (rowRemoved, _) = await TryRemoveAsync(row, force: true, ct);
|
||||
if (rowRemoved)
|
||||
{
|
||||
removed++;
|
||||
removedTaskIds.Add(row.TaskId);
|
||||
@@ -118,16 +120,16 @@ public sealed class WorktreeMaintenanceService
|
||||
.FirstOrDefaultAsync(ct);
|
||||
|
||||
if (row is null)
|
||||
return new ForceRemoveResult(false, "worktree not found");
|
||||
return new ForceRemoveResult(false, "worktree not found", false);
|
||||
|
||||
if (row.Status == ClaudeDo.Data.Models.TaskStatus.Running)
|
||||
return new ForceRemoveResult(false, "task is currently running");
|
||||
return new ForceRemoveResult(false, "task is currently running", false);
|
||||
|
||||
var ok = await TryRemoveAsync(row.Row, force: true, ct);
|
||||
return new ForceRemoveResult(ok, ok ? null : "remove failed");
|
||||
var (ok, branchDeleted) = await TryRemoveAsync(row.Row, force: true, ct);
|
||||
return new ForceRemoveResult(ok, ok ? null : "remove failed", branchDeleted);
|
||||
}
|
||||
|
||||
private async Task<bool> TryRemoveAsync(WorktreeRow row, bool force, CancellationToken ct)
|
||||
private async Task<(bool Removed, bool BranchDeleted)> TryRemoveAsync(WorktreeRow row, bool force, CancellationToken ct)
|
||||
{
|
||||
var repoDirExists = !string.IsNullOrWhiteSpace(row.WorkingDir) && Directory.Exists(row.WorkingDir);
|
||||
bool dirRemoved;
|
||||
@@ -163,8 +165,13 @@ public sealed class WorktreeMaintenanceService
|
||||
}
|
||||
}
|
||||
|
||||
// Drop the DB row only when the on-disk worktree is gone; otherwise we'd silently
|
||||
// strand a directory while reporting success.
|
||||
if (!dirRemoved) return (false, false);
|
||||
|
||||
// Branch cleanup: otherwise rerunning the task hits "branch already exists".
|
||||
// Prune first so git no longer thinks the branch is checked out by a phantom worktree.
|
||||
bool branchDeleted = false;
|
||||
if (repoDirExists)
|
||||
{
|
||||
try { await _git.WorktreePruneAsync(row.WorkingDir!, ct); }
|
||||
@@ -175,6 +182,7 @@ public sealed class WorktreeMaintenanceService
|
||||
try
|
||||
{
|
||||
await _git.BranchDeleteAsync(row.WorkingDir!, row.BranchName, force: true, ct);
|
||||
branchDeleted = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -184,13 +192,9 @@ public sealed class WorktreeMaintenanceService
|
||||
}
|
||||
}
|
||||
|
||||
// Drop the DB row only when the on-disk worktree is gone; otherwise we'd silently
|
||||
// strand a directory while reporting success.
|
||||
if (!dirRemoved) return false;
|
||||
|
||||
using var context = _dbFactory.CreateDbContext();
|
||||
await context.Worktrees.Where(w => w.TaskId == row.TaskId).ExecuteDeleteAsync(ct);
|
||||
return true;
|
||||
return (true, branchDeleted);
|
||||
}
|
||||
|
||||
private sealed record WorktreeRow(string TaskId, string Path, string BranchName, string? WorkingDir);
|
||||
|
||||
Reference in New Issue
Block a user