smart-tree 8.0.1

Smart Tree - An intelligent, AI-friendly directory visualization tool
Documentation
# Smart Tree Windows Installer Script
# 
# This script installs the 'st' binary on Windows
#
# Usage:
#   iwr -useb https://raw.githubusercontent.com/8b-is/smart-tree/main/scripts/install.ps1 | iex
#
# You can customize the installation by setting environment variables:
#   - $env:INSTALL_DIR: The directory to install the binary to (default: $env:LOCALAPPDATA\Programs\st)
#   - $env:VERSION: The version to install (default: latest)

param(
    [string]$InstallDir = "$env:LOCALAPPDATA\Programs\st",
    [string]$Version = ""
)

# Configuration
$GitHubRepo = "8b-is/smart-tree"
$BinaryName = "st.exe"

# Terminal colors
function Write-Info {
    param([string]$Message)
    Write-Host "[INFO] $Message" -ForegroundColor Blue
}

function Write-Success {
    param([string]$Message)
    Write-Host "[SUCCESS] $Message" -ForegroundColor Green
}

function Write-Error {
    param([string]$Message)
    Write-Host "[ERROR] $Message" -ForegroundColor Red
    throw $Message
}

function Write-Warning {
    param([string]$Message)
    Write-Host "[WARNING] $Message" -ForegroundColor Yellow
}

# Function to check if a release has assets
function Test-ReleaseAssets {
    param([string]$VersionTag)
    
    try {
        $releaseInfo = Invoke-RestMethod -Uri "https://api.github.com/repos/$GitHubRepo/releases/tags/$VersionTag"
        return $releaseInfo.assets.Count -gt 0
    } catch {
        return $false
    }
}

# Function to get releases with assets
function Get-ReleasesWithAssets {
    try {
        # Get first 10 releases with pagination for efficiency
        $releases = Invoke-RestMethod -Uri "https://api.github.com/repos/$GitHubRepo/releases?per_page=10"
        return $releases | Where-Object { $_.assets.Count -gt 0 } | Select-Object -ExpandProperty tag_name
    } catch {
        Write-Error "Failed to fetch releases from GitHub API"
    }
}

# Function to select version interactively
function Select-Version {
    param([string]$LatestVersion)
    
    Write-Warning "The latest version ($LatestVersion) doesn't have any release binaries yet."
    Write-Info "Fetching other available versions with binaries..."
    
    $versions = Get-ReleasesWithAssets
    
    if ($versions.Count -eq 0) {
        Write-Error "No releases with binaries found!"
    }
    
    Write-Info "Available versions with binaries:"
    for ($i = 0; $i -lt $versions.Count; $i++) {
        Write-Host "  $($i + 1). $($versions[$i])"
    }
    
    while ($true) {
        $selection = Read-Host "Select a version (1-$($versions.Count)) or 'q' to quit"
        
        if ($selection -eq 'q') {
            Write-Info "Installation cancelled."
            exit 0
        }
        
        # Safe integer parsing
        $selectionNum = 0
        if ([System.Int32]::TryParse($selection, [ref]$selectionNum) -and 
            $selectionNum -ge 1 -and $selectionNum -le $versions.Count) {
            return $versions[$selectionNum - 1]
        } else {
            Write-Warning "Invalid selection. Please try again."
        }
    }
}

# Main Installation Logic

Write-Info "Smart Tree Windows Installer"
Write-Info "Repository: $GitHubRepo"
Write-Info ""

# 1. Detect Architecture
$arch = $env:PROCESSOR_ARCHITECTURE
switch ($arch) {
    "AMD64" { $targetArch = "x86_64" }
    "ARM64" { $targetArch = "aarch64" }
    default {
        Write-Error "Unsupported architecture: $arch"
    }
}

Write-Info "Detected architecture: $targetArch"

# 2. Determine Version to Install
if ([string]::IsNullOrEmpty($Version)) {
    Write-Info "Fetching the latest version number..."
    try {
        $latestRelease = Invoke-RestMethod -Uri "https://api.github.com/repos/$GitHubRepo/releases/latest"
        $latestVersion = $latestRelease.tag_name
        
        if ([string]::IsNullOrEmpty($latestVersion)) {
            Write-Error "Could not fetch the latest version. Please check the repository path and your network connection."
        }
        
        # Check if latest version has assets
        if (-not (Test-ReleaseAssets -VersionTag $latestVersion)) {
            $Version = Select-Version -LatestVersion $latestVersion
        } else {
            $Version = $latestVersion
            Write-Info "Latest version is $Version"
        }
    } catch {
        Write-Error "Failed to fetch the latest version: $_"
    }
} else {
    Write-Info "Installing specified version: $Version"
    
    # Check if specified version has assets
    if (-not (Test-ReleaseAssets -VersionTag $Version)) {
        Write-Warning "Version $Version doesn't have any release binaries."
        $response = Read-Host "Would you like to select another version? (y/n)"
        if ($response -eq 'y' -or $response -eq 'Y') {
            $Version = Select-Version -LatestVersion $Version
        } else {
            Write-Error "Cannot install version without binaries."
        }
    }
}

# 3. Construct Download URL
$archiveName = "st-$Version-$targetArch-pc-windows-msvc.zip"
$downloadUrl = "https://github.com/$GitHubRepo/releases/download/$Version/$archiveName"

# 4. Download and Extract
$tempDir = New-Item -ItemType Directory -Path "$env:TEMP\st-install-$(Get-Random)" -Force
$archivePath = Join-Path $tempDir $archiveName

Write-Info "Downloading from $downloadUrl"
try {
    # Use WebRequest for better progress display
    $ProgressPreference = 'SilentlyContinue'
    Invoke-WebRequest -Uri $downloadUrl -OutFile $archivePath -UseBasicParsing
    $ProgressPreference = 'Continue'
} catch {
    Write-Error "Download failed: $_"
}

Write-Info "Extracting the binary..."
try {
    Expand-Archive -Path $archivePath -DestinationPath $tempDir -Force
} catch {
    Write-Error "Extraction failed: $_"
}

# 5. Install the binary
Write-Info "Installing to $InstallDir..."

# Create install directory if it doesn't exist
if (-not (Test-Path $InstallDir)) {
    New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null
}

$binaryPath = Join-Path $tempDir $BinaryName
if (-not (Test-Path $binaryPath)) {
    # Sometimes the binary is in a subdirectory
    $binaryPath = Get-ChildItem -Path $tempDir -Filter $BinaryName -Recurse | Select-Object -First 1 -ExpandProperty FullName
    if ([string]::IsNullOrEmpty($binaryPath)) {
        Write-Error "Could not find the '$BinaryName' binary in the downloaded archive."
    }
}

$destinationPath = Join-Path $InstallDir $BinaryName
Copy-Item -Path $binaryPath -Destination $destinationPath -Force

# 6. Update PATH if necessary
$userPath = [Environment]::GetEnvironmentVariable("Path", "User")
if ($userPath -notlike "*$InstallDir*") {
    Write-Info "Adding $InstallDir to your PATH..."
    
    # Handle trailing semicolon properly
    $newPath = if ($userPath.TrimEnd().EndsWith(';')) {
        "$userPath$InstallDir"
    } else {
        "$userPath;$InstallDir"
    }
    
    [Environment]::SetEnvironmentVariable("Path", $newPath, "User")
    Write-Success "Added to PATH. Please restart your terminal for changes to take effect."
    
    # Update current session PATH (check if not already present)
    if ($env:Path -notlike "*$InstallDir*") {
        $env:Path = "$env:Path;$InstallDir"
    }
} else {
    Write-Info "$InstallDir is already in your PATH"
}

# 7. Verify Installation
Write-Info "Verifying installation..."
try {
    $versionOutput = & $destinationPath --version
    Write-Success "Successfully installed st to $destinationPath"
    Write-Info "Version: $versionOutput"
    Write-Info "You can now use the 'st' command!"
} catch {
    Write-Warning "Installation completed but verification failed. You may need to restart your terminal."
}

# Clean up
Remove-Item -Path $tempDir -Recurse -Force

# Show next steps
Write-Host ""
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "  Smart Tree installed successfully! 🌳" -ForegroundColor Green
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
Write-Info "Next steps:"
Write-Host "  1. Restart your terminal (or open a new one) to use 'st'" -ForegroundColor Yellow
Write-Host "  2. Try: st --help" -ForegroundColor Yellow
Write-Host "  3. Try: st --version" -ForegroundColor Yellow
Write-Host "  4. Try: st . (analyze current directory)" -ForegroundColor Yellow
Write-Host ""
Write-Host "For MCP integration with Claude Desktop:" -ForegroundColor Cyan
Write-Host "  Run: st --mcp-config" -ForegroundColor Yellow
Write-Host ""
Write-Host "Thank you for installing Smart Tree! 🎸" -ForegroundColor Green