using ClaudeDo.Data; using ClaudeDo.Data.Models; using ClaudeDo.Data.Repositories; using Microsoft.EntityFrameworkCore; namespace ClaudeDo.Worker.Lifecycle; /// /// Startup-only sweep: warns when a list's working directory already contains a /// .claudedo-worktrees folder inside its own tree — the symptom of the sibling-worktree /// path bug (see ) from before it was /// fixed. Detection only; moving or ignoring existing folders is left to the user. /// public sealed class LegacyWorktreeFolderRecovery : IHostedService { private const string LegacyFolderName = ".claudedo-worktrees"; private readonly IDbContextFactory _dbFactory; private readonly ILogger _logger; public LegacyWorktreeFolderRecovery( IDbContextFactory dbFactory, ILogger logger) { _dbFactory = dbFactory; _logger = logger; } public async Task StartAsync(CancellationToken cancellationToken) { await using var ctx = await _dbFactory.CreateDbContextAsync(cancellationToken); var lists = await new ListRepository(ctx).GetAllAsync(cancellationToken); var affected = FindAffectedListNames(lists); if (affected.Count > 0) _logger.LogWarning( "Legacy worktree folder recovery: found '{Folder}' inside the working tree of {Count} list(s): {Lists}. " + "This was previously caused by a trailing separator on the list's working directory; existing worktrees were not moved.", LegacyFolderName, affected.Count, string.Join(", ", affected)); else _logger.LogInformation("Legacy worktree folder recovery: no lists affected"); } public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; /// Names of lists whose working dir already contains a .claudedo-worktrees folder. public static List FindAffectedListNames(IEnumerable lists) => lists .Where(l => !string.IsNullOrWhiteSpace(l.WorkingDir)) .Where(l => Directory.Exists(Path.Combine(l.WorkingDir!, LegacyFolderName))) .Select(l => l.Name) .ToList(); }