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)