greppy-cli 1.0.0

Sub-millisecond semantic code search with AI reranking (Claude/Gemini)
Documentation
#Requires -Version 5.1
<#
.SYNOPSIS
    Greppy Installer for Windows
.DESCRIPTION
    Downloads and installs the latest version of Greppy.
    Automatically stops any running daemon before installation.
.LINK
    https://github.com/KBLCode/greppy
.EXAMPLE
    irm https://raw.githubusercontent.com/KBLCode/greppy/main/install.ps1 | iex
#>

$ErrorActionPreference = "Stop"

$Repo = "KBLCode/greppy"
$InstallDir = if ($env:GREPPY_INSTALL_DIR) { $env:GREPPY_INSTALL_DIR } else { "$env:LOCALAPPDATA\greppy\bin" }
$GreppyHome = if ($env:GREPPY_HOME) { $env:GREPPY_HOME } else { "$env:USERPROFILE\.greppy" }

# ─────────────────────────────────────────────────────────────────────────────
# Output helpers
# ─────────────────────────────────────────────────────────────────────────────

function Write-Info  { Write-Host "[INFO] " -ForegroundColor Green -NoNewline; Write-Host $args }
function Write-Warn  { Write-Host "[WARN] " -ForegroundColor Yellow -NoNewline; Write-Host $args }
function Write-Err   { Write-Host "[ERROR] " -ForegroundColor Red -NoNewline; Write-Host $args; exit 1 }
function Write-Step  { Write-Host "[STEP] " -ForegroundColor Blue -NoNewline; Write-Host $args }

# ─────────────────────────────────────────────────────────────────────────────
# Stop existing daemon (CRITICAL for clean upgrades)
# ─────────────────────────────────────────────────────────────────────────────

function Stop-ExistingDaemon {
    Write-Step "Checking for running greppy daemon..."

    # Method 1: Use existing greppy binary if available
    $greppy = Get-Command greppy -ErrorAction SilentlyContinue
    if ($greppy) {
        try {
            $status = & greppy status 2>$null
            if ($status -match "running") {
                Write-Warn "Stopping existing greppy daemon..."
                & greppy stop 2>$null
                Start-Sleep -Seconds 1
            }
        } catch {
            # Ignore errors
        }
    }

    # Method 2: Check PID file directly
    $pidFile = Join-Path $GreppyHome "daemon.pid"
    if (Test-Path $pidFile) {
        $pid = Get-Content $pidFile -ErrorAction SilentlyContinue
        if ($pid) {
            $proc = Get-Process -Id $pid -ErrorAction SilentlyContinue
            if ($proc) {
                Write-Warn "Killing daemon process $pid..."
                Stop-Process -Id $pid -Force -ErrorAction SilentlyContinue
                Start-Sleep -Seconds 1
            }
        }
        Remove-Item $pidFile -Force -ErrorAction SilentlyContinue
    }

    # Method 3: Clean up port file (Windows uses TCP)
    $portFile = Join-Path $GreppyHome "daemon.port"
    Remove-Item $portFile -Force -ErrorAction SilentlyContinue

    Write-Info "Daemon cleanup complete"
}

# ─────────────────────────────────────────────────────────────────────────────
# Download and install
# ─────────────────────────────────────────────────────────────────────────────

function Install-Greppy {
    # Detect architecture
    $arch = if ([Environment]::Is64BitOperatingSystem) { "x86_64" } else { 
        Write-Err "32-bit Windows is not supported"
    }
    $platform = "windows-$arch"
    Write-Info "Detected platform: $platform"

    # Get latest release
    Write-Step "Fetching latest release..."
    try {
        $release = Invoke-RestMethod "https://api.github.com/repos/$Repo/releases/latest"
        $version = $release.tag_name
    } catch {
        Write-Err "Failed to fetch latest release. Check your internet connection."
    }
    Write-Info "Latest version: $version"

    # Download
    $zipName = "greppy-$version-$platform.zip"
    $url = "https://github.com/$Repo/releases/download/$version/$zipName"
    
    $tmpDir = New-TemporaryFile | ForEach-Object { 
        Remove-Item $_
        New-Item -ItemType Directory -Path $_
    }
    $zipPath = Join-Path $tmpDir $zipName

    Write-Step "Downloading $zipName..."
    try {
        Invoke-WebRequest -Uri $url -OutFile $zipPath -UseBasicParsing
    } catch {
        Write-Err "Download failed. URL: $url"
    }

    # Extract
    Write-Step "Extracting..."
    Expand-Archive -Path $zipPath -DestinationPath $tmpDir -Force

    # Install
    Write-Step "Installing to $InstallDir..."
    New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null
    Move-Item (Join-Path $tmpDir "greppy.exe") (Join-Path $InstallDir "greppy.exe") -Force

    # Verify
    $greppyPath = Join-Path $InstallDir "greppy.exe"
    try {
        $versionOutput = & $greppyPath --version 2>$null
        Write-Info "Installation successful!"
    } catch {
        Write-Err "Installation verification failed"
    }

    # Clean up
    Remove-Item $tmpDir -Recurse -Force -ErrorAction SilentlyContinue
}

# ─────────────────────────────────────────────────────────────────────────────
# Post-install setup
# ─────────────────────────────────────────────────────────────────────────────

function Show-PostInstall {
    $greppyPath = Join-Path $InstallDir "greppy.exe"
    $version = & $greppyPath --version 2>$null | Select-Object -First 1

    Write-Host ""
    Write-Host "Installation complete!" -ForegroundColor Green
    Write-Host "  Version:  $version"
    Write-Host "  Location: $greppyPath"
    Write-Host ""

    # Check if install dir is in PATH
    if ($env:PATH -notlike "*$InstallDir*") {
        Write-Host "Add greppy to your PATH:" -ForegroundColor Yellow
        Write-Host ""
        Write-Host "  # Run this command (or add to your profile):"
        Write-Host "  `$env:PATH += `";$InstallDir`""
        Write-Host ""
        Write-Host "  # Or permanently add via System Properties > Environment Variables"
        Write-Host ""
    }

    Write-Host "Quick start:" -ForegroundColor Blue
    Write-Host "  cd your-project"
    Write-Host "  greppy index              # Index your codebase"
    Write-Host "  greppy login              # (Optional) Enable AI reranking"
    Write-Host "  greppy search `"query`"     # Search!"
    Write-Host ""
    Write-Host "Commands:" -ForegroundColor Blue
    Write-Host "  greppy search <query>     # Semantic search (AI-powered)"
    Write-Host "  greppy search -d <query>  # Direct BM25 search (no AI)"
    Write-Host "  greppy start              # Start background daemon"
    Write-Host "  greppy logout             # Remove credentials"
    Write-Host ""
    Write-Host "Documentation: https://github.com/$Repo"
}

# ─────────────────────────────────────────────────────────────────────────────
# Logo
# ─────────────────────────────────────────────────────────────────────────────

function Show-Logo {
    $logo = @"
┌──────────────────────────────────────────────────┐
│ ██████╗ ██████╗ ███████╗██████╗ ██████╗ ██╗   ██╗│
│██╔════╝ ██╔══██╗██╔════╝██╔══██╗██╔══██╗╚██╗ ██╔╝│
│██║  ███╗██████╔╝█████╗  ██████╔╝██████╔╝ ╚████╔╝ │
│██║   ██║██╔══██╗██╔══╝  ██╔═══╝ ██╔═══╝   ╚██╔╝  │
│╚██████╔╝██║  ██║███████╗██║     ██║        ██║   │
│ ╚═════╝ ╚═╝  ╚═╝╚══════╝╚═╝     ╚═╝        ╚═╝   │
└──────────────────────────────────────────────────┘
"@
    Write-Host $logo
    Write-Host ""
    Write-Host "Sub-millisecond local semantic code search"
    Write-Host ""
}

# ─────────────────────────────────────────────────────────────────────────────
# Main
# ─────────────────────────────────────────────────────────────────────────────

Show-Logo

# CRITICAL: Stop daemon before replacing binary
Stop-ExistingDaemon

# Install new version
Install-Greppy

# Show post-install info
Show-PostInstall