From cb2087762099098f74180d4f29d21df814e8fa5e Mon Sep 17 00:00:00 2001 From: mika kuns Date: Fri, 5 Jun 2026 10:50:04 +0200 Subject: [PATCH] feat(hub): expose conflict-resolution merge methods --- src/ClaudeDo.Worker/Hub/WorkerHub.cs | 46 ++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/ClaudeDo.Worker/Hub/WorkerHub.cs b/src/ClaudeDo.Worker/Hub/WorkerHub.cs index 96c4003..525379c 100644 --- a/src/ClaudeDo.Worker/Hub/WorkerHub.cs +++ b/src/ClaudeDo.Worker/Hub/WorkerHub.cs @@ -56,6 +56,9 @@ public record ForceRemoveResultDto(bool Removed, string? Reason); public record MergeResultDto(string Status, IReadOnlyList ConflictFiles, string? ErrorMessage); public record MergePreviewDto(string Status, IReadOnlyList ConflictFiles, int ChangedFileCount); public record MergeTargetsDto(string DefaultBranch, IReadOnlyList LocalBranches); +public record MergeConflictsDto(string TaskId, IReadOnlyList Files); +public record ConflictFileDto(string Path, IReadOnlyList Hunks); +public record ConflictHunkDto(string Ours, string Theirs, string? Base); public record UpdateListDto(string Id, string Name, string? WorkingDir, string DefaultCommitType); public record UpdateListConfigDto(string ListId, string? Model, string? SystemPrompt, string? AgentPath, int? MaxTurns = null); public record UpdateTaskAgentSettingsDto(string TaskId, string? Model, string? SystemPrompt, string? AgentPath, int? MaxTurns = null); @@ -328,6 +331,49 @@ public sealed class WorkerHub : Microsoft.AspNetCore.SignalR.Hub return new MergePreviewDto(p.Status, p.ConflictFiles, p.ChangedFileCount); }); + public Task StartConflictMerge(string taskId, string targetBranch) + => HubGuard(async () => + { + var r = await _mergeService.MergeAsync( + taskId, targetBranch ?? "", removeWorktree: false, "Merge task", + leaveConflictsInTree: true, CancellationToken.None); + if (r.Status == TaskMergeService.StatusBlocked) + throw new HubException(r.ErrorMessage ?? "merge blocked"); + return new MergeResultDto(r.Status, r.ConflictFiles, r.ErrorMessage); + }); + + public Task GetMergeConflicts(string taskId) + => HubGuard(async () => + { + var c = await _mergeService.GetConflictsAsync(taskId, CancellationToken.None); + return new MergeConflictsDto( + c.TaskId, + c.Files.Select(f => new ConflictFileDto( + f.Path, + new[] { new ConflictHunkDto(f.Ours, f.Theirs, f.Base) })).ToList()); + }); + + public Task WriteConflictResolution(string taskId, string path, string resolvedContent) + => HubGuard(() => _mergeService.WriteResolutionAsync( + taskId, path, resolvedContent ?? "", CancellationToken.None)); + + public Task ContinueMerge(string taskId) + => HubGuard(async () => + { + var r = await _mergeService.ContinueMergeAsync(taskId, CancellationToken.None); + if (r.Status == TaskMergeService.StatusBlocked) + throw new HubException(r.ErrorMessage ?? "continue failed"); + return new MergeResultDto(r.Status, r.ConflictFiles, r.ErrorMessage); + }); + + public Task AbortMerge(string taskId) + => HubGuard(async () => + { + var r = await _mergeService.AbortMergeAsync(taskId, CancellationToken.None); + if (r.Status == TaskMergeService.StatusBlocked) + throw new HubException(r.ErrorMessage ?? "abort failed"); + }); + public async Task UpdateList(UpdateListDto dto) { using var ctx = _dbFactory.CreateDbContext();