`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
137 lines
6.1 KiB
Python
137 lines
6.1 KiB
Python
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)
|