using System.IO; using ClaudeDo.Data.Environment; using ClaudeDo.Installer.Core; namespace ClaudeDo.Installer.Checks; /// Without the claude CLI, no task can ever run — blocking. public sealed class ClaudeCliCheck : IEnvironmentCheck { public const string CheckId = "claude-cli"; private readonly ClaudeCliLookup _lookup; public ClaudeCliCheck(ClaudeCliLookup lookup) { _lookup = lookup; } public string Id => CheckId; public CheckSeverity Severity => CheckSeverity.Error; public async Task RunAsync(InstallContext ctx, CancellationToken ct) { var lookup = await _lookup.ResolveAsync(ctx, ct); if (lookup.Resolved is null) { var searched = string.Join(", ", SearchedLocations()); return CheckResult.Fail(Id, Severity, "checks.claudeCli.title", $"'{ctx.ClaudeBin}' was not found on PATH. Searched: {searched}.", "checks.claudeCli.hint"); } if (lookup.ExitCode != 0) { return CheckResult.Fail(Id, Severity, "checks.claudeCli.title", $"'{lookup.Resolved.Path}' exited with code {lookup.ExitCode}.", "checks.claudeCli.hint", detail: lookup.Output); } var version = lookup.ParsedVersion?.ToString() ?? lookup.Output.Trim(); var detail = lookup.Resolved.IsShim ? "Resolved via a shim (.cmd/.bat/.ps1), not a native .exe." : null; return CheckResult.Ok(Id, Severity, "checks.claudeCli.title", $"{lookup.Resolved.Path} — {version}", detail); } private IEnumerable SearchedLocations() { var pathVar = _lookup.PathOverride ?? System.Environment.GetEnvironmentVariable("PATH") ?? ""; var pathEntries = pathVar.Split(Path.PathSeparator, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); return pathEntries.Concat(ExecutableResolver.FallbackDirectories()); } }