yoyo-agent 0.1.7

A coding agent that evolves itself. Born as 200 lines of Rust, growing up in public.
#Requires -Version 5.1
$ErrorActionPreference = "Stop"

$Repo = "yologdev/yoyo-evolve"
$InstallDir = Join-Path $env:USERPROFILE ".yoyo\bin"

function Main {
    # Detect architecture (with fallback for older .NET Framework)
    try {
        $Arch = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString()
    } catch {
        $Arch = $env:PROCESSOR_ARCHITECTURE
    }
    switch ($Arch) {
        { $_ -in "X64", "AMD64" } { $Target = "x86_64-pc-windows-msvc" }
        default {
            Write-Host "Unsupported architecture: $Arch. Falling back to cargo install."
            Invoke-CargoFallback
            return
        }
    }

    Write-Host "Detected platform: $Target"

    # Get latest release tag
    try {
        $Release = Invoke-RestMethod -Uri "https://api.github.com/repos/$Repo/releases/latest"
        $Version = $Release.tag_name
    } catch {
        Write-Host "Error: failed to fetch release info from GitHub API."
        Write-Host "You may be rate-limited. Try: cargo install yoyo-agent"
        exit 1
    }

    if (-not $Version) {
        Write-Host "Error: could not determine latest release version."
        Write-Host "Try: cargo install yoyo-agent"
        exit 1
    }

    Write-Host "Installing yoyo $Version..."

    $Archive = "yoyo-$Version-$Target.zip"
    $Url = "https://github.com/$Repo/releases/download/$Version/$Archive"
    $ChecksumUrl = "$Url.sha256"

    # Download to temp directory
    $TmpDir = Join-Path ([System.IO.Path]::GetTempPath()) ([System.IO.Path]::GetRandomFileName())
    New-Item -ItemType Directory -Path $TmpDir -Force | Out-Null

    try {
        Write-Host "Downloading $Url..."
        try {
            Invoke-WebRequest -Uri $Url -OutFile (Join-Path $TmpDir $Archive) -UseBasicParsing
        } catch {
            Write-Host "Error: failed to download $Archive"
            Write-Host "The release may not exist yet. Try: cargo install yoyo-agent"
            exit 1
        }

        # Download checksum (optional)
        $ChecksumFile = Join-Path $TmpDir "$Archive.sha256"
        $ChecksumAvailable = $false
        try {
            Invoke-WebRequest -Uri $ChecksumUrl -OutFile $ChecksumFile -UseBasicParsing
            $ChecksumAvailable = $true
        } catch {
            Write-Host "Warning: checksum file not available, skipping verification."
        }

        # Verify checksum (if downloaded, verification MUST pass)
        if ($ChecksumAvailable) {
            $ExpectedLine = Get-Content $ChecksumFile -Raw
            if (-not $ExpectedLine -or $ExpectedLine.Trim().Length -eq 0) {
                Write-Host "Error: checksum file is empty."
                exit 1
            }
            $ExpectedHash = ($ExpectedLine -split '\s+')[0].Trim().ToLower()
            $ActualHash = (Get-FileHash -Algorithm SHA256 (Join-Path $TmpDir $Archive)).Hash.ToLower()
            if ($ExpectedHash -ne $ActualHash) {
                Write-Host "Error: checksum verification failed. The download may be corrupted."
                Write-Host "Expected: $ExpectedHash"
                Write-Host "Actual:   $ActualHash"
                exit 1
            }
            Write-Host "Checksum verified."
        }

        # Extract
        try {
            Expand-Archive -Path (Join-Path $TmpDir $Archive) -DestinationPath $TmpDir -Force
        } catch {
            Write-Host "Error: failed to extract $Archive. The download may be corrupted."
            Write-Host "Try: cargo install yoyo-agent"
            exit 1
        }

        # Find the binary
        $Binary = Get-ChildItem -Path $TmpDir -Filter "yoyo.exe" -Recurse | Select-Object -First 1
        if (-not $Binary) {
            Write-Host "Error: binary 'yoyo.exe' not found in archive."
            Write-Host "Please report this: https://github.com/$Repo/issues"
            exit 1
        }

        # Install
        New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null
        try {
            Copy-Item -Path $Binary.FullName -Destination (Join-Path $InstallDir "yoyo.exe") -Force
        } catch {
            Write-Host "Error: could not install yoyo.exe to $InstallDir"
            Write-Host "If yoyo is currently running, close it and try again."
            exit 1
        }

        Write-Host "Installed yoyo to $InstallDir\yoyo.exe"

        # Check PATH
        $UserPath = [Environment]::GetEnvironmentVariable("PATH", "User")
        if (-not $UserPath) { $UserPath = "" }
        if ($UserPath -split ';' -notcontains $InstallDir) {
            try {
                $NewPath = if ($UserPath) { "$InstallDir;$UserPath" } else { $InstallDir }
                [Environment]::SetEnvironmentVariable("PATH", $NewPath, "User")
                $env:PATH = "$InstallDir;$env:PATH"
                Write-Host "Added $InstallDir to your PATH."
                Write-Host "Restart your terminal for the change to take effect."
            } catch {
                Write-Host ""
                Write-Host "Add yoyo to your PATH manually:"
                Write-Host "  `$env:PATH = `"$InstallDir;`$env:PATH`""
                Write-Host ""
            }
        }

        Write-Host "Run 'yoyo --help' to get started."
    } finally {
        Remove-Item -Path $TmpDir -Recurse -Force -ErrorAction SilentlyContinue
    }
}

function Invoke-CargoFallback {
    if (Get-Command cargo -ErrorAction SilentlyContinue) {
        Write-Host "Installing via cargo..."
        cargo install yoyo-agent
        if ($LASTEXITCODE -ne 0) {
            Write-Host "Error: cargo install failed."
            exit 1
        }
    } else {
        Write-Host "Error: cargo is not installed. Install Rust first: https://rustup.rs"
        exit 1
    }
}

Main