namespace ClaudeDo.Ui.Services; public sealed class InstallerLocator : InstallArtifactLocator { protected override string Subdir => "uninstaller"; protected override string ExeName => "ClaudeDo.Installer.exe"; } public sealed class WorkerLocator : InstallArtifactLocator { protected override string Subdir => "worker"; protected override string ExeName => "ClaudeDo.Worker.exe"; } /// /// Locates an executable inside a ClaudeDo install: walk up from the running /// directory to the folder containing install.json, otherwise read the /// uninstall registry key. Subclasses supply the subdirectory and exe name. /// public abstract class InstallArtifactLocator { private const string InstallJson = "install.json"; protected abstract string Subdir { get; } protected abstract string ExeName { get; } public string? Find() => FindByWalkingUp(AppContext.BaseDirectory) ?? (OperatingSystem.IsWindows() ? FindByRegistry() : null); public string? FindByWalkingUp(string startDir) { var dir = new DirectoryInfo(startDir); while (dir is not null) { if (File.Exists(Path.Combine(dir.FullName, InstallJson))) { var candidate = Path.Combine(dir.FullName, Subdir, ExeName); return File.Exists(candidate) ? candidate : null; } dir = dir.Parent; } return null; } [System.Runtime.Versioning.SupportedOSPlatform("windows")] public string? FindByRegistry() { if (!OperatingSystem.IsWindows()) return null; try { using var key = Microsoft.Win32.Registry.LocalMachine .OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Uninstall\ClaudeDo"); var location = key?.GetValue("InstallLocation") as string; if (string.IsNullOrEmpty(location)) return null; var candidate = Path.Combine(location, Subdir, ExeName); return File.Exists(candidate) ? candidate : null; } catch { return null; } } }