diff --git a/src/ClaudeDo.Installer/App.xaml.cs b/src/ClaudeDo.Installer/App.xaml.cs index fb4ff2f..cd57389 100644 --- a/src/ClaudeDo.Installer/App.xaml.cs +++ b/src/ClaudeDo.Installer/App.xaml.cs @@ -121,6 +121,9 @@ public partial class App : Application sc.AddSingleton(); sc.AddSingleton(); + // Runners + sc.AddSingleton(); + // ViewModels sc.AddSingleton(); sc.AddSingleton(); diff --git a/src/ClaudeDo.Installer/Core/UninstallRunner.cs b/src/ClaudeDo.Installer/Core/UninstallRunner.cs new file mode 100644 index 0000000..fb31569 --- /dev/null +++ b/src/ClaudeDo.Installer/Core/UninstallRunner.cs @@ -0,0 +1,68 @@ +using System.IO; +using ClaudeDo.Data; +using ClaudeDo.Installer.Steps; + +namespace ClaudeDo.Installer.Core; + +public sealed class UninstallRunner +{ + private readonly InstallContext _context; + private readonly StopServiceStep _stopService; + + public UninstallRunner(InstallContext context, StopServiceStep stopService) + { + _context = context; + _stopService = stopService; + } + + public async Task RunAsync(IProgress progress, CancellationToken ct) + { + // 1) Stop + delete service. + progress.Report("Stopping worker service..."); + var stopResult = await _stopService.ExecuteAsync(_context, progress, ct); + if (!stopResult.Success) + { + progress.Report($"(Ignored) {stopResult.ErrorMessage}"); + } + + progress.Report("Unregistering service..."); + await ProcessRunner.RunAsync("sc.exe", $"delete {StopServiceStep.ServiceName}", null, progress, ct); + + // 2) Remove shortcuts. + progress.Report("Removing shortcuts..."); + TryDeleteFile(Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.CommonDesktopDirectory), + "ClaudeDo.lnk")); + TryDeleteFile(Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.CommonStartMenu), + "Programs", "ClaudeDo.lnk")); + + // 3) Delete install directory. + if (Directory.Exists(_context.InstallDirectory)) + { + progress.Report($"Deleting {_context.InstallDirectory}..."); + TryDeleteDir(_context.InstallDirectory); + } + + // 4) Delete ~/.todo-app (config + DB + logs) — user opted into full removal. + var appData = Paths.AppDataRoot(); + if (Directory.Exists(appData)) + { + progress.Report($"Deleting {appData}..."); + TryDeleteDir(appData); + } + + progress.Report("Uninstall complete."); + return StepResult.Ok(); + } + + private static void TryDeleteFile(string path) + { + try { if (File.Exists(path)) File.Delete(path); } catch { /* best effort */ } + } + + private static void TryDeleteDir(string path) + { + try { Directory.Delete(path, recursive: true); } catch { /* best effort */ } + } +}