using ClaudeDo.Releases; namespace ClaudeDo.Installer.Core; public sealed record DetectedState( InstallerMode Mode, InstallManifest? Existing, GiteaRelease? LatestRelease, string? LatestVersion) { /// True when a release was returned but its tag isn't a parseable /// System.Version (e.g. "0.2.0-beta") — so we couldn't decide if it's newer. public bool LatestTagUnparseable { get; init; } } public sealed class InstallModeDetector { private readonly IReleaseClient _releases; public InstallModeDetector(IReleaseClient releases) { _releases = releases; } public async Task DetectAsync(string installDir, CancellationToken ct) { var manifest = InstallManifestStore.TryRead(installDir); if (manifest is null) return new DetectedState(InstallerMode.FreshInstall, null, null, null); var release = await _releases.GetLatestReleaseAsync(ct); if (release is null) return new DetectedState(InstallerMode.Config, manifest, null, null); var latestVersion = release.TagName.TrimStart('v', 'V'); var cmp = VersionComparer.Compare(latestVersion, manifest.Version); var newer = cmp.IsNewer; var unparseable = cmp.Unparseable; if (newer) return new DetectedState(InstallerMode.Update, manifest, release, latestVersion); return new DetectedState(InstallerMode.Config, manifest, release, latestVersion) { LatestTagUnparseable = unparseable, }; } }