refactor(claude-do): refactor(worker): move the merge and worktree MCP tools out
`src/ClaudeDo.Worker/External/ExternalMcpService.cs` ist **1897 Zeilen** (nachgezählt 2026-09-02; die Datei ist seit dem Anlegen dieses Tasks von 1696 weiter gewachsen): ~30 DTO-Records oben plus ~25 Tool-Methoden — während **13** Geschwisterdateien `*McpTools.cs` im selben Ordner das Topic-Split-Muster schon tragen (`ListMcpTools`, `ConfigMcpTools`, `BatchMcpTools`, `NotesMcpTools`, `QueueStateMc ClaudeDo-Task: 1862e160-ffde-4a50-aa03-bb7251f56088
This commit is contained in:
+136
@@ -0,0 +1,136 @@
|
||||
import re, sys, json
|
||||
|
||||
path = r"C:\Private\.claudedo-worktrees\claude-do\1862e160-ffde-4a50-aa03-bb7251f56088\tests\ClaudeDo.Worker.Tests\External\ExternalMcpServiceTests.cs"
|
||||
with open(path, encoding='utf-8') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
# Find all [Fact] blocks: (start_idx, end_idx, name) where start_idx is the [Fact] line (0-based)
|
||||
# and end_idx is the line with the closing brace (inclusive), 0-based.
|
||||
blocks = {}
|
||||
i = 0
|
||||
n = len(lines)
|
||||
while i < n:
|
||||
if lines[i].strip() == '[Fact]':
|
||||
fact_start = i
|
||||
# next line should be method signature
|
||||
sig_line = lines[i+1]
|
||||
m = re.search(r'public async Task (\w+)\(', sig_line)
|
||||
assert m, f"no method name at line {i+2}: {sig_line}"
|
||||
name = m.group(1)
|
||||
# find opening brace - it's on the line after signature (possibly signature spans multiple lines)
|
||||
j = i + 1
|
||||
# advance until we find a line that is just '{' (with indentation)
|
||||
while lines[j].strip() != '{':
|
||||
j += 1
|
||||
brace_line = j
|
||||
depth = 0
|
||||
k = brace_line
|
||||
while True:
|
||||
depth += lines[k].count('{') - lines[k].count('}')
|
||||
if depth == 0:
|
||||
break
|
||||
k += 1
|
||||
end_idx = k
|
||||
blocks[name] = (fact_start, end_idx)
|
||||
i = end_idx + 1
|
||||
else:
|
||||
i += 1
|
||||
|
||||
print(f"Found {len(blocks)} [Fact] blocks", file=sys.stderr)
|
||||
|
||||
merge_names = """MergeTask_NotDone_Throws
|
||||
MergeTask_DryRun_DoesNotMerge
|
||||
MergeTask_WaitingForReview_WithoutFlag_Throws
|
||||
MergeTask_WaitingForReview_WithFlag_DryRun_Succeeds
|
||||
MergeTask_WaitingForReview_WithFlag_MarksTaskDone
|
||||
MergeTask_LeaveConflictsInTree_LeavesMarkersAndKeepsRepoMidMerge
|
||||
MergeTask_ConflictWithoutLeaveFlag_AbortsCleanly
|
||||
ContinueMerge_AfterResolvingMarkers_MergesAndSetsDone
|
||||
ContinueMerge_SlowVerifyCommand_ReportsProgressWellBeforeIdleTimeout
|
||||
ContinueMerge_MarkersStillPresent_ReportsConflicts
|
||||
AbortMerge_RestoresCleanTreeAndKeepsWaitingForReview
|
||||
ContinueMerge_NoMergeInProgress_Throws
|
||||
AbortMerge_NoMergeInProgress_Throws
|
||||
GetMergeConflicts_ReportsHunksWithStartLineAndDiff3Base
|
||||
GetMergeConflicts_NoMergeInProgress_ReturnsEmpty
|
||||
ResolveConflictHunk_OursTheirsAndText_ResolveEachHunkAndTrackRemainingCount
|
||||
ResolveConflictHunk_LeavesOtherHunksUntouchedUntilContinueMerge
|
||||
ResolveConflictHunk_UnknownFileOrOutOfRangeIndex_Throws
|
||||
RevertMerge_NotMerged_Throws
|
||||
RevertMerge_AfterCleanMerge_RevertsAndReturnsTaskToWaitingForReview
|
||||
RevertMerge_ConflictingRevert_ReturnsRevertedFalseWithConflictsAndAbortsCleanly
|
||||
ContinueMerge_ParentUnitMergeConflict_RoutesToOrchestratorAndCompletes
|
||||
PreviewMerge_CleanMerge_ReturnsCleanStatusAndDoesNotChangeRepo
|
||||
PreviewMerge_EmptyWorktree_ReturnsIsEmptyTrue
|
||||
PreviewMerge_WorktreeLessHandlerTask_EmptyRange_ReturnsIsEmptyTrue
|
||||
PreviewMerge_WorktreeLessHandlerTask_NonEmptyRange_ReturnsIsEmptyFalse
|
||||
PreviewMerge_ConflictingBranches_ReturnsConflictStatusAndDoesNotChangeRepo
|
||||
PreviewMerge_NoWorktree_Throws
|
||||
PreviewMerge_MissingWorkingDir_ThrowsClearError
|
||||
PreviewMerge_VerifyCommandConfigured_RunsInScratchWorktreeAndDoesNotTouchTarget
|
||||
PreviewMerge_TargetChangedSameFileSinceFork_ReportsStaleFiles
|
||||
PreviewMerge_TargetBehindButUnrelatedFile_ReportsNoStaleFiles
|
||||
PreviewMerge_WorktreeLessHandlerTask_ReportsNoStaleFiles
|
||||
PreviewMergeSet_TwoTasksSharedFile_ReportsOverlap
|
||||
PreviewMergeSet_TaskFilesAreProperSubsetOfAnother_ReportsSubset
|
||||
PreviewMergeSet_SingleTaskId_ReturnsEmptyOverlap
|
||||
PreviewMergeSet_ReportsProgressPerTask
|
||||
PreviewMergeSet_OneTaskWithoutWorktree_ReportsErrorWithoutAbortingOthers
|
||||
PreviewMergeSet_MixedEmptyAndNonEmpty_ReportsIsEmptyPerEntry
|
||||
PreviewMergeSet_DefaultRunVerifyFalse_NeverInvokesRunnerEvenWithCommandConfigured
|
||||
PreviewMergeSet_RunVerifyTrue_InvokesRunnerPerTask""".split()
|
||||
|
||||
worktree_names = """GetTaskWorktree_NoWorktree_Throws
|
||||
GetTaskWorktree_ReturnsBranchAndBaseCommit
|
||||
GetTaskWorktree_BeforeMerge_MergeCommitIsNull
|
||||
GetTaskWorktree_AfterMerge_ExposesMergeCommit
|
||||
GetTaskDiff_NoWorktree_Throws
|
||||
GetTaskDiff_AfterCommit_ListsChangedFile
|
||||
GetTaskDiff_WithPathsFilter_ReturnsOnlyRequestedFile
|
||||
GetTaskDiff_StatMode_WithPathsFilter_ReturnsOnlyRequestedFile
|
||||
GetTaskDiff_WorktreeLessHandlerTask_UsesHandlerCommitRangeOverListWorkingDir
|
||||
GetTaskDiff_WorktreeLessHandlerTask_StatMode_UsesHandlerCommitRange
|
||||
GetTaskDiff_ReportsProgressWhileDiffing
|
||||
ListWorktrees_Empty_ReturnsEmpty
|
||||
ListWorktrees_ReturnsCreatedWorktree
|
||||
ListWorktrees_ReportsProgressWhileSurveying
|
||||
CleanupTaskWorktree_NotFound_Throws
|
||||
CleanupTaskWorktree_Running_Throws
|
||||
CleanupTaskWorktree_CleanWorktree_Removes
|
||||
CleanupTaskWorktree_ReportsProgressWhileRemoving""".split()
|
||||
|
||||
missing_merge = [n for n in merge_names if n not in blocks]
|
||||
missing_worktree = [n for n in worktree_names if n not in blocks]
|
||||
print("missing merge:", missing_merge, file=sys.stderr)
|
||||
print("missing worktree:", missing_worktree, file=sys.stderr)
|
||||
overlap = set(merge_names) & set(worktree_names)
|
||||
print("overlap:", overlap, file=sys.stderr)
|
||||
|
||||
def extract(names):
|
||||
out = []
|
||||
for name in names:
|
||||
s, e = blocks[name]
|
||||
out.append(''.join(lines[s:e+1]))
|
||||
return out
|
||||
|
||||
with open(r"C:\Private\.claudedo-worktrees\claude-do\1862e160-ffde-4a50-aa03-bb7251f56088\tests\ClaudeDo.Worker.Tests\External\_merge_bodies.txt", 'w', encoding='utf-8') as f:
|
||||
f.write('\n'.join(extract(merge_names)))
|
||||
|
||||
with open(r"C:\Private\.claudedo-worktrees\claude-do\1862e160-ffde-4a50-aa03-bb7251f56088\tests\ClaudeDo.Worker.Tests\External\_worktree_bodies.txt", 'w', encoding='utf-8') as f:
|
||||
f.write('\n'.join(extract(worktree_names)))
|
||||
|
||||
# Build set of all line ranges to remove (sorted), then write remainder file for inspection
|
||||
all_names = merge_names + worktree_names
|
||||
ranges = sorted([blocks[n] for n in all_names])
|
||||
# merge overlapping/adjacent not needed; just mark lines
|
||||
remove_lines = set()
|
||||
for s, e in ranges:
|
||||
for x in range(s, e+1):
|
||||
remove_lines.add(x)
|
||||
|
||||
remainder = [lines[i] for i in range(n) if i not in remove_lines]
|
||||
with open(r"C:\Private\.claudedo-worktrees\claude-do\1862e160-ffde-4a50-aa03-bb7251f56088\tests\ClaudeDo.Worker.Tests\External\_remainder.cs", 'w', encoding='utf-8') as f:
|
||||
f.writelines(remainder)
|
||||
|
||||
print(f"Total lines removed: {len(remove_lines)}", file=sys.stderr)
|
||||
print(f"Remainder line count: {len(remainder)}", file=sys.stderr)
|
||||
Reference in New Issue
Block a user