using System.Diagnostics; using System.IO; using ClaudeDo.Installer.Core; namespace ClaudeDo.Installer.Steps; public sealed class StopWorkerStep : IInstallStep { public const string TaskName = "ClaudeDoWorker"; public const string ProcessName = "ClaudeDo.Worker"; public string Name => "Stop Worker"; public async Task ExecuteAsync(InstallContext ctx, IProgress progress, CancellationToken ct) { progress.Report("Stopping worker task (if running)..."); await ProcessRunner.RunAsync("schtasks.exe", $"/End /TN \"{TaskName}\"", null, progress, ct); progress.Report("Stopping worker process (if running)..."); var installDir = ctx.InstallDirectory; foreach (var p in Process.GetProcessesByName(ProcessName)) { try { var path = p.MainModule?.FileName; if (path is not null && !IsUnder(path, installDir)) continue; p.Kill(entireProcessTree: true); p.WaitForExit(10000); } catch { /* process may have exited or be inaccessible */ } finally { p.Dispose(); } } await Task.CompletedTask; return StepResult.Ok(); } private static bool IsUnder(string filePath, string dir) { try { if (string.IsNullOrWhiteSpace(dir)) return true; // can't scope — be permissive var full = Path.GetFullPath(filePath); var root = Path.GetFullPath(dir).TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar; return full.StartsWith(root, StringComparison.OrdinalIgnoreCase); } catch { return false; } } }