#Requires -Version 5.1 <# .SYNOPSIS AI Terminal Kickstart - One script to prep any Windows PC for AI-powered terminal work. .DESCRIPTION Interactive, menu-driven installer that bootstraps a Windows PC with everything needed for the top 3 AI terminal solutions: 1. Claude Code (Anthropic) - Terminal AI coding assistant 2. ChatGPT CLI (OpenAI) - Interactive GPT in your terminal 3. GitHub Copilot CLI (GitHub) - AI-powered shell suggestions The script: - Self-upgrades to PowerShell 7 if running from PS 5.x - Installs all prerequisites (Git, Node.js, Python, etc.) - Runs your existing installer scripts as sub-modules - Provides a guided, color-coded experience any junior tech can follow - Validates every install step with logic tests - Generates a final health-check report .PARAMETER AutoMode Skip all menus and install everything. Great for unattended setup. .PARAMETER SkipPowerShell7 Don't attempt to upgrade PowerShell (stay on 5.x). .PARAMETER ScriptsDir Directory containing your helper PS1 scripts. Defaults to the same folder as this script. .EXAMPLE .\Start-AITerminalKickstart.ps1 # Interactive menu - pick what you want .EXAMPLE .\Start-AITerminalKickstart.ps1 -AutoMode # Install everything, no prompts (except API keys) .NOTES Author : Endpoint Security / AI Terminal ops Date : 2026-03-25 Tested : Windows Server 2022, Windows 10/11, PowerShell 5.1+ #> [CmdletBinding()] param( [switch]$AutoMode, [switch]$SkipPowerShell7, [string]$ScriptsDir = "" ) # ═══════════════════════════════════════════════════════════════════════════════ # CONFIGURATION # ═══════════════════════════════════════════════════════════════════════════════ $ErrorActionPreference = 'Continue' Set-StrictMode -Version Latest if (-not $ScriptsDir) { $ScriptsDir = Split-Path -Parent $MyInvocation.MyCommand.Definition if (-not $ScriptsDir) { $ScriptsDir = Get-Location } } $script:TempDir = Join-Path $env:TEMP "ai-kickstart-$(Get-Date -Format 'yyyyMMdd')" $script:LogFile = Join-Path $script:TempDir "kickstart.log" $script:Results = [System.Collections.ArrayList]::new() $script:StartTime = Get-Date # ═══════════════════════════════════════════════════════════════════════════════ # HELPER FUNCTIONS # ═══════════════════════════════════════════════════════════════════════════════ function Write-Banner { $banner = @" ╔═══════════════════════════════════════════════════════════════════╗ ║ ║ ║ A I T E R M I N A L K I C K S T A R T ║ ║ ───────────────────────────────────────── ║ ║ One Script To Prep Them All ║ ║ ║ ║ Claude Code | ChatGPT CLI | GitHub Copilot CLI ║ ║ ║ ╚═══════════════════════════════════════════════════════════════════╝ "@ Write-Host $banner -ForegroundColor Magenta } function Write-Section { param([string]$Title) $line = "=" * 65 Write-Host "" Write-Host " $line" -ForegroundColor DarkCyan Write-Host " $Title" -ForegroundColor Cyan Write-Host " $line" -ForegroundColor DarkCyan } function Write-Step { param([string]$Msg) Write-Host "`n >> $Msg" -ForegroundColor Cyan Add-Content -Path $script:LogFile -Value "[$(Get-Date -Format 'HH:mm:ss')] STEP: $Msg" -ErrorAction SilentlyContinue } function Write-OK { param([string]$Msg) Write-Host " [OK] $Msg" -ForegroundColor Green Add-Content -Path $script:LogFile -Value "[$(Get-Date -Format 'HH:mm:ss')] OK: $Msg" -ErrorAction SilentlyContinue } function Write-Warn { param([string]$Msg) Write-Host " [WARN] $Msg" -ForegroundColor Yellow Add-Content -Path $script:LogFile -Value "[$(Get-Date -Format 'HH:mm:ss')] WARN: $Msg" -ErrorAction SilentlyContinue } function Write-Fail { param([string]$Msg) Write-Host " [FAIL] $Msg" -ForegroundColor Red Add-Content -Path $script:LogFile -Value "[$(Get-Date -Format 'HH:mm:ss')] FAIL: $Msg" -ErrorAction SilentlyContinue } function Write-Info { param([string]$Msg) Write-Host " $Msg" -ForegroundColor Gray } function Write-Tip { param([string]$Msg) Write-Host " [TIP] $Msg" -ForegroundColor DarkYellow } function Add-Result { param([string]$Component, [string]$Status, [string]$Detail = "") [void]$script:Results.Add([PSCustomObject]@{ Component = $Component Status = $Status Detail = $Detail Time = Get-Date -Format 'HH:mm:ss' }) } function Test-CmdExists { param([string]$Name) $null -ne (Get-Command $Name -ErrorAction SilentlyContinue) } function Refresh-Path { # Rebuild from registry but PRESERVE any paths added during this session $machine = [Environment]::GetEnvironmentVariable('Path', 'Machine') $user = [Environment]::GetEnvironmentVariable('Path', 'User') $basePath = "$machine;$user" # Preserve session-added paths that aren't in the registry yet $currentParts = $env:Path -split ';' | Where-Object { $_ -ne '' } $baseParts = $basePath -split ';' | Where-Object { $_ -ne '' } $sessionOnly = $currentParts | Where-Object { $_ -notin $baseParts } $env:Path = $basePath foreach ($sp in $sessionOnly) { if ($env:Path -notlike "*$sp*") { $env:Path += ";$sp" } } # Also add common tool paths that might not be registered yet $extras = @( "$env:ProgramFiles\Git\cmd", "$env:ProgramFiles\Git\bin", "$env:ProgramFiles\nodejs", "$env:USERPROFILE\.local\bin", "$env:USERPROFILE\.cargo\bin", "$env:APPDATA\npm", "$env:LOCALAPPDATA\Programs\Python\Python312", "$env:LOCALAPPDATA\Programs\Python\Python312\Scripts", "$env:LOCALAPPDATA\Programs\Python\Python313", "$env:LOCALAPPDATA\Programs\Python\Python313\Scripts" ) foreach ($p in $extras) { if ((Test-Path $p) -and $env:Path -notlike "*$p*") { $env:Path += ";$p" } } } function Test-IsAdmin { ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent() ).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) } function Show-Progress { param( [string]$Activity, [string]$Status, [int]$Step, [int]$TotalSteps ) $pct = [math]::Round(($Step / $TotalSteps) * 100) $barLen = 30 $filled = [math]::Round($barLen * $Step / $TotalSteps) $empty = $barLen - $filled $bar = "$([char]0x2588)" * $filled + "$([char]0x2591)" * $empty Write-Host "`r [$bar] $pct% - $Status " -NoNewline -ForegroundColor Cyan Write-Progress -Activity $Activity -Status "$Status ($pct%)" -PercentComplete $pct } function Complete-Progress { param([string]$Activity) Write-Progress -Activity $Activity -Completed Write-Host "" # newline after progress bar } function Invoke-Download { # Download with progress display, retry on failure param( [string]$Uri, [string]$OutFile, [string]$Description = "Downloading", [int]$MaxRetries = 2 ) $attempt = 0 while ($attempt -le $MaxRetries) { try { Write-Info "$Description (attempt $($attempt + 1))..." $ProgressPreference = 'SilentlyContinue' # Invoke-WebRequest's built-in progress is slow Invoke-WebRequest -Uri $Uri -OutFile $OutFile -UseBasicParsing -TimeoutSec 120 $ProgressPreference = 'Continue' $sizeKB = [math]::Round((Get-Item $OutFile).Length / 1KB) Write-OK "$Description complete (${sizeKB} KB)" return $true } catch { $attempt++ if ($attempt -gt $MaxRetries) { Write-Fail "$Description failed after $($MaxRetries + 1) attempts: $_" return $false } Write-Warn "Download failed, retrying in 3 seconds... ($_)" Start-Sleep -Seconds 3 } } return $false } # Version-flexible package names (auto-detect latest where possible) $script:PythonVersion = "3.12" # Change this single value to update Python target $script:PythonWingetId = "Python.Python.$($script:PythonVersion)" $script:PythonChocoId = "python$($script:PythonVersion -replace '\.','')" $script:NodeMajor = "22" # Change this to update Node.js LTS target # Phase tracking for progress bar $script:TotalPhases = 6 $script:CurrentPhase = 0 function Enter-Phase { param([string]$Name) $script:CurrentPhase++ Show-Progress -Activity "AI Terminal Kickstart" -Status "Phase $($script:CurrentPhase)/$($script:TotalPhases): $Name" -Step $script:CurrentPhase -TotalSteps $script:TotalPhases } function Get-UserChoice { param( [string]$Prompt, [string[]]$Options, [int]$Default = 0 ) if ($AutoMode) { return $Default } Write-Host "" for ($i = 0; $i -lt $Options.Count; $i++) { $marker = if ($i -eq $Default) { "*" } else { " " } $color = if ($i -eq $Default) { "White" } else { "Gray" } Write-Host " [$($i+1)]$marker $($Options[$i])" -ForegroundColor $color } Write-Host "" Write-Host " $Prompt [$($Default+1)]: " -NoNewline -ForegroundColor Yellow $input = Read-Host if ($input -match '^\d+$') { $idx = [int]$input - 1 if ($idx -ge 0 -and $idx -lt $Options.Count) { return $idx } } return $Default } function Get-YesNo { param([string]$Prompt, [bool]$Default = $true) if ($AutoMode) { return $Default } $hint = if ($Default) { "Y/n" } else { "y/N" } Write-Host " $Prompt ($hint): " -NoNewline -ForegroundColor Yellow $answer = Read-Host if ([string]::IsNullOrWhiteSpace($answer)) { return $Default } return ($answer -match '^[Yy]') } function Invoke-SubScript { param( [string]$Name, [string]$FileName, [hashtable]$Params = @{} ) $path = Join-Path $ScriptsDir $FileName if (-not (Test-Path $path)) { Write-Warn "Script not found: $path" Write-Info "Skipping $Name - file missing from scripts directory" Add-Result $Name "SKIPPED" "Script file not found" return $false } Write-Info "Running: $FileName" try { & $path @Params Add-Result $Name "RAN" "Sub-script executed" return $true } catch { Write-Fail "$Name script error: $_" Add-Result $Name "ERROR" "$_" return $false } } # ═══════════════════════════════════════════════════════════════════════════════ # PHASE 0 - INITIALIZATION # ═══════════════════════════════════════════════════════════════════════════════ if (-not (Test-Path $script:TempDir)) { New-Item -Path $script:TempDir -ItemType Directory -Force | Out-Null } Write-Banner # Show system info $osInfo = Get-CimInstance Win32_OperatingSystem $psVer = $PSVersionTable.PSVersion $isAdmin = Test-IsAdmin Write-Host " System Info:" -ForegroundColor White Write-Host " OS : $($osInfo.Caption)" -ForegroundColor Gray Write-Host " Build : $($osInfo.BuildNumber)" -ForegroundColor Gray Write-Host " PowerShell : $psVer" -ForegroundColor Gray Write-Host " Admin : $isAdmin" -ForegroundColor $(if ($isAdmin) { 'Green' } else { 'Yellow' }) Write-Host " Scripts Dir : $ScriptsDir" -ForegroundColor Gray Write-Host " Log File : $($script:LogFile)" -ForegroundColor Gray Write-Host "" if (-not $isAdmin) { Write-Host " ┌──────────────────────────────────────────────────────────────┐" -ForegroundColor Yellow Write-Host " │ WARNING: Not running as Administrator. │" -ForegroundColor Yellow Write-Host " │ Some installations may fail or prompt UAC. │" -ForegroundColor Yellow Write-Host " │ Right-click PowerShell > Run as Administrator for best │" -ForegroundColor Yellow Write-Host " │ results. │" -ForegroundColor Yellow Write-Host " └──────────────────────────────────────────────────────────────┘" -ForegroundColor Yellow Write-Host "" } # ─── Pre-flight: TLS, Network, Disk, Admin, Repos ───────────────────────────── Enter-Phase "Pre-flight Checks" Write-Section "PRE-FLIGHT CHECKS" # Force TLS 1.2+ for all web requests in this session Write-Step "Enforcing TLS 1.2+ for all downloads" [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12 -bor [System.Net.SecurityProtocolType]::Tls13 Write-OK "TLS 1.2/1.3 enforced for all Invoke-WebRequest/RestMethod calls" Add-Result "TLS Security" "OK" "TLS 1.2+" # Admin access validation Write-Step "Checking administrator privileges" if ($isAdmin) { Write-OK "Running as Administrator - full install capability" Add-Result "Admin Access" "OK" "Elevated" } else { Write-Warn "NOT running as Administrator" Write-Info "The following will fail without admin: Chocolatey, execution policy," Write-Info "system PATH changes, some MSI installers." Write-Info "Recommendation: Close and re-run as Administrator." Add-Result "Admin Access" "WARN" "Not elevated" } # Network connectivity - test multiple download sources Write-Step "Testing internet connectivity to download sources" $script:NetworkOK = $true $endpoints = @( @{ Name = "GitHub API"; URL = "https://api.github.com" }, @{ Name = "Node.js"; URL = "https://nodejs.org/dist/index.json" }, @{ Name = "Claude installer"; URL = "https://claude.ai" }, @{ Name = "Chocolatey"; URL = "https://community.chocolatey.org" }, @{ Name = "npm registry"; URL = "https://registry.npmjs.org" }, @{ Name = "PyPI"; URL = "https://pypi.org" } ) $reachable = 0 $unreachable = 0 foreach ($ep in $endpoints) { try { $null = Invoke-WebRequest -Uri $ep.URL -UseBasicParsing -TimeoutSec 8 -Method Head -ErrorAction Stop Write-OK "$($ep.Name) reachable" $reachable++ } catch { Write-Warn "$($ep.Name) ($($ep.URL)) - UNREACHABLE" $unreachable++ } } if ($unreachable -gt 0) { Write-Warn "$unreachable of $($endpoints.Count) download sources unreachable." Write-Info "Check your network, proxy, or firewall settings." if ($unreachable -ge 4) { Write-Fail "Most download sources are unreachable. Script will likely fail." $script:NetworkOK = $false } } else { Write-OK "All $reachable download sources reachable" } Add-Result "Network" $(if ($unreachable -eq 0) { "OK" } elseif ($unreachable -lt 4) { "PARTIAL" } else { "FAILED" }) "$reachable/$($endpoints.Count) reachable" # Disk space check Write-Step "Checking available disk space" try { $sysDrive = (Get-Item $env:SystemRoot).PSDrive $freeGB = [math]::Round(($sysDrive.Free / 1GB), 1) $totalGB = [math]::Round(($sysDrive.Used + $sysDrive.Free) / 1GB, 1) if ($freeGB -lt 2) { Write-Fail "CRITICAL: Only $freeGB GB free on $($sysDrive.Name): ($totalGB GB total). Need at least 2 GB." Write-Info "Free up disk space before continuing." Add-Result "Disk Space" "FAILED" "$freeGB GB free" } elseif ($freeGB -lt 5) { Write-Warn "Low: $freeGB GB free on $($sysDrive.Name): ($totalGB GB total). Recommend 5+ GB." Add-Result "Disk Space" "WARN" "$freeGB GB free" } else { Write-OK "Disk space: $freeGB GB free on $($sysDrive.Name): ($totalGB GB total)" Add-Result "Disk Space" "OK" "$freeGB GB free" } } catch { # Fallback for non-standard PSDrive Write-Warn "Could not determine disk space: $_" Add-Result "Disk Space" "UNKNOWN" "" } # Log session separator Add-Content -Path $script:LogFile -Value "`n$('=' * 70)" -ErrorAction SilentlyContinue Add-Content -Path $script:LogFile -Value "Session started: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -ErrorAction SilentlyContinue Add-Content -Path $script:LogFile -Value "OS: $($osInfo.Caption) Build $($osInfo.BuildNumber) | PS: $psVer | Admin: $isAdmin" -ErrorAction SilentlyContinue Add-Content -Path $script:LogFile -Value "$('=' * 70)" -ErrorAction SilentlyContinue # ═══════════════════════════════════════════════════════════════════════════════ # PHASE 1 - POWERSHELL 7 CHECK / UPGRADE # ═══════════════════════════════════════════════════════════════════════════════ Enter-Phase "PowerShell & Core Prerequisites" Write-Section "PHASE 1: PowerShell Version Check" if ($psVer.Major -ge 7) { Write-OK "Already running PowerShell $psVer - no upgrade needed" Add-Result "PowerShell 7" "PRESENT" "v$psVer" } elseif ($SkipPowerShell7) { Write-Warn "PowerShell 7 upgrade skipped (--SkipPowerShell7 flag)" Add-Result "PowerShell 7" "SKIPPED" "User opted out" } else { Write-Step "You are on PowerShell $psVer (Windows built-in)" Write-Info "PowerShell 7 is recommended for AI terminals. It offers:" Write-Info " - Better performance and modern language features" Write-Info " - Native JSON/REST support improvements" Write-Info " - Cross-platform compatibility" Write-Info " - Required by some advanced Claude Code features" Write-Host "" if (Get-YesNo "Install PowerShell 7?") { Write-Step "Installing PowerShell 7..." $installed = $false # Try winget first if (Test-CmdExists 'winget') { Write-Info "Using winget to install PowerShell 7..." try { $result = winget install --id Microsoft.PowerShell --accept-source-agreements --accept-package-agreements --silent 2>&1 if ($LASTEXITCODE -eq 0 -or ($result -match 'already installed')) { $installed = $true Write-OK "PowerShell 7 installed via winget" } } catch { Write-Info "winget method failed, trying MSI..." } } # Fallback to MSI download if (-not $installed) { Write-Info "Downloading PowerShell 7 MSI from GitHub..." try { $releases = Invoke-RestMethod 'https://api.github.com/repos/PowerShell/PowerShell/releases/latest' $msiAsset = $releases.assets | Where-Object { $_.name -match 'win-x64\.msi$' } | Select-Object -First 1 if ($msiAsset) { $msiPath = Join-Path $script:TempDir $msiAsset.name Invoke-WebRequest -Uri $msiAsset.browser_download_url -OutFile $msiPath -UseBasicParsing Write-OK "Downloaded: $($msiAsset.name)" Write-Info "Installing silently..." $msiArgs = "/i `"$msiPath`" /qn /norestart ADD_EXPLORER_CONTEXT_MENU_OPENPOWERSHELL=1 ADD_FILE_CONTEXT_MENU_RUNPOWERSHELL=1 ENABLE_PSREMOTING=0 REGISTER_MANIFEST=1 USE_MU=1 ENABLE_MU=1" Start-Process msiexec.exe -ArgumentList $msiArgs -Wait -NoNewWindow $installed = $true Write-OK "PowerShell 7 installed via MSI" } } catch { Write-Fail "MSI download/install failed: $_" } } if ($installed) { Add-Result "PowerShell 7" "INSTALLED" "Restart terminal to use pwsh.exe" Write-Host "" Write-Tip "After this script finishes, open a NEW terminal and type 'pwsh' to use PS7." Write-Tip "You can also right-click > 'Open in Terminal' for Windows Terminal with PS7." } else { Add-Result "PowerShell 7" "FAILED" "Manual install needed" Write-Warn "Install manually from: https://aka.ms/powershell-release?tag=stable" } } else { Write-Info "Staying on PowerShell $psVer" Add-Result "PowerShell 7" "SKIPPED" "User declined" } } # ═══════════════════════════════════════════════════════════════════════════════ # PHASE 2 - CORE PREREQUISITES # ═══════════════════════════════════════════════════════════════════════════════ Write-Section "PHASE 2: Core Prerequisites" # ─── 2a. Execution Policy ───────────────────────────────────────────────────── Write-Step "Checking execution policy" $policy = Get-ExecutionPolicy -Scope LocalMachine if ($policy -in 'Restricted', 'AllSigned') { if ($isAdmin) { Set-ExecutionPolicy RemoteSigned -Scope LocalMachine -Force Write-OK "Execution policy set to RemoteSigned" } else { Write-Warn "Execution policy is '$policy' - run as Admin to change it" } } else { Write-OK "Execution policy is '$policy'" } Add-Result "Execution Policy" "OK" $policy # ─── 2b. Package Manager (Chocolatey) ───────────────────────────────────────── Write-Step "Checking Chocolatey package manager" if (Test-CmdExists 'choco') { $chocoVer = choco --version 2>$null Write-OK "Chocolatey already installed ($chocoVer)" Add-Result "Chocolatey" "PRESENT" "v$chocoVer" } else { Write-Info "Chocolatey is used to install many developer tools automatically." if (Get-YesNo "Install Chocolatey?") { try { [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12 Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1')) Refresh-Path choco feature enable -n allowGlobalConfirmation 2>$null | Out-Null Write-OK "Chocolatey installed" Add-Result "Chocolatey" "INSTALLED" "" } catch { Write-Fail "Chocolatey install failed: $_" Add-Result "Chocolatey" "FAILED" "$_" } } else { Add-Result "Chocolatey" "SKIPPED" "User declined" } } # ─── 2c. Git for Windows ────────────────────────────────────────────────────── Write-Step "Checking Git for Windows" Refresh-Path if (Test-CmdExists 'git') { $gitVer = (git --version) -replace 'git version ', '' Write-OK "Git already installed ($gitVer)" Write-Info "Git Bash: $env:ProgramFiles\Git\bin\bash.exe" Add-Result "Git" "PRESENT" "v$gitVer" } else { Write-Info "Git is REQUIRED for Claude Code and most AI terminal tools." Write-Info "It also provides Git Bash which Claude Code uses on Windows." if (Get-YesNo "Install Git for Windows?" $true) { $gitInstalled = $false # Try winget first if (Test-CmdExists 'winget') { Write-Info "Installing via winget..." try { winget install --id Git.Git --accept-source-agreements --accept-package-agreements --silent 2>$null if ($LASTEXITCODE -eq 0) { $gitInstalled = $true } } catch { Write-Info "winget install failed: $_ - trying next method..." } } # Try Chocolatey if (-not $gitInstalled -and (Test-CmdExists 'choco')) { Write-Info "Installing via Chocolatey..." try { choco install git --yes --no-progress $gitInstalled = $true } catch { Write-Info "Chocolatey install failed: $_ - trying direct download..." } } # Direct download fallback (auto-detect latest version from GitHub API) if (-not $gitInstalled) { Write-Info "Downloading Git from GitHub (auto-detecting latest release)..." try { $gitRelease = Invoke-RestMethod 'https://api.github.com/repos/git-for-windows/git/releases/latest' $gitAsset = $gitRelease.assets | Where-Object { $_.name -match '64-bit\.exe$' -and $_.name -notmatch 'portable' } | Select-Object -First 1 if ($gitAsset) { $gitInstaller = Join-Path $script:TempDir $gitAsset.name $dlOk = Invoke-Download -Uri $gitAsset.browser_download_url -OutFile $gitInstaller -Description "Git for Windows ($($gitAsset.name))" if (-not $dlOk) { throw "Download failed" } $gitArgs = '/VERYSILENT', '/NORESTART', '/SP-', '/CLOSEAPPLICATIONS' Start-Process -FilePath $gitInstaller -ArgumentList $gitArgs -Wait -NoNewWindow $gitInstalled = $true } else { Write-Fail "Could not find Git installer asset in release" } } catch { Write-Fail "Git download failed: $_" } } Refresh-Path if (Test-CmdExists 'git') { Write-OK "Git installed: $(git --version)" Add-Result "Git" "INSTALLED" (git --version) } else { Write-Fail "Git not found after install attempt" Add-Result "Git" "FAILED" "Not on PATH" } } else { Write-Warn "Skipping Git - Claude Code WILL NOT WORK without it!" Add-Result "Git" "SKIPPED" "User declined (WARNING)" } } # ─── 2d. Node.js ────────────────────────────────────────────────────────────── Write-Step "Checking Node.js" Refresh-Path if (Test-CmdExists 'node') { $nodeVer = node --version 2>$null Write-OK "Node.js already installed ($nodeVer)" if (Test-CmdExists 'npm') { Write-OK "npm $(npm --version 2>$null)" } if (Test-CmdExists 'npx') { Write-OK "npx available" } Add-Result "Node.js" "PRESENT" $nodeVer } else { Write-Info "Node.js is required for Claude Code, npm packages, and MCP servers." if (Get-YesNo "Install Node.js LTS?" $true) { $nodeInstalled = $false if (Test-CmdExists 'winget') { Write-Info "Installing via winget..." try { winget install --id OpenJS.NodeJS.LTS --accept-source-agreements --accept-package-agreements --silent 2>$null if ($LASTEXITCODE -eq 0) { $nodeInstalled = $true } } catch { Write-Info "winget install failed: $_ - trying next method..." } } if (-not $nodeInstalled -and (Test-CmdExists 'choco')) { Write-Info "Installing via Chocolatey..." try { choco install nodejs-lts --yes --no-progress $nodeInstalled = $true } catch { Write-Info "Chocolatey install failed: $_ - trying direct download..." } } if (-not $nodeInstalled) { Write-Info "Downloading from nodejs.org..." try { $nodeVersions = Invoke-RestMethod 'https://nodejs.org/dist/index.json' $latestLts = $nodeVersions | Where-Object { $_.lts -ne $false } | Select-Object -First 1 $ver = $latestLts.version $msi = "node-$ver-x64.msi" $url = "https://nodejs.org/dist/$ver/$msi" $installer = Join-Path $script:TempDir $msi Invoke-WebRequest -Uri $url -OutFile $installer -UseBasicParsing Start-Process msiexec.exe -ArgumentList "/i `"$installer`" /qn /norestart" -Wait -NoNewWindow } catch { Write-Fail "Node.js download failed: $_" } } Refresh-Path if (Test-CmdExists 'node') { Write-OK "Node.js $(node --version 2>$null) installed" Add-Result "Node.js" "INSTALLED" (node --version 2>$null) } else { Write-Fail "Node.js not found after install" Add-Result "Node.js" "FAILED" "Not on PATH" } } else { Add-Result "Node.js" "SKIPPED" "User declined" } } # ─── 2e. Python ─────────────────────────────────────────────────────────────── Write-Step "Checking Python" Refresh-Path if (Test-CmdExists 'python') { $pyVer = python --version 2>&1 Write-OK "Python already installed ($pyVer)" Add-Result "Python" "PRESENT" "$pyVer" } else { Write-Info "Python is used for MCP server development, data processing," Write-Info "security tools, and many Claude Code plugins." if (Get-YesNo "Install Python $($script:PythonVersion)?" $true) { $pyInstalled = $false if (Test-CmdExists 'winget') { Write-Info "Installing Python $($script:PythonVersion) via winget..." try { winget install --id $script:PythonWingetId --accept-source-agreements --accept-package-agreements --silent 2>$null if ($LASTEXITCODE -eq 0) { $pyInstalled = $true } } catch { Write-Info "winget install failed: $_ - trying next method..." } } if (-not $pyInstalled -and (Test-CmdExists 'choco')) { Write-Info "Installing Python $($script:PythonVersion) via Chocolatey..." try { choco install $script:PythonChocoId --yes --no-progress $pyInstalled = $true } catch { Write-Info "Chocolatey install failed: $_" } } Refresh-Path if (Test-CmdExists 'python') { Write-OK "Python $(python --version 2>&1) installed" python -m pip install --upgrade pip --quiet 2>$null Write-OK "pip upgraded" Add-Result "Python" "INSTALLED" (python --version 2>&1) } else { Write-Fail "Python not found after install" Add-Result "Python" "FAILED" "Not on PATH" } } else { Add-Result "Python" "SKIPPED" "User declined" } } # ─── 2f. Additional CLI tools ───────────────────────────────────────────────── Write-Step "Checking additional CLI tools" $cliTools = @( @{ Name = "ripgrep"; Cmd = "rg"; Desc = "Ultra-fast code search (used by Claude Code)" }, @{ Name = "jq"; Cmd = "jq"; Desc = "JSON processor for API responses" }, @{ Name = "fd"; Cmd = "fd"; Desc = "Fast file finder" }, @{ Name = "7zip"; Cmd = "7z"; Desc = "Archive utility" }, @{ Name = "imagemagick"; Cmd = "magick"; Desc = "Image processing (convert, resize, etc.)" }, @{ Name = "ghostscript"; Cmd = "gswin64c"; Desc = "PDF/PostScript engine (required by ImageMagick for PDFs)" }, @{ Name = "ffmpeg"; Cmd = "ffmpeg"; Desc = "Video/audio processing and media conversion" }, @{ Name = "bat"; Cmd = "bat"; Desc = "Syntax-highlighted file viewer (better cat)" }, @{ Name = "fzf"; Cmd = "fzf"; Desc = "Fuzzy finder for files, history, and commands" }, @{ Name = "yq"; Cmd = "yq"; Desc = "YAML processor (like jq but for YAML)" }, @{ Name = "tree"; Cmd = "tree"; Desc = "Directory structure viewer" }, @{ Name = "delta"; Cmd = "delta"; Desc = "Enhanced git diff viewer with syntax highlighting" }, @{ Name = "shellcheck"; Cmd = "shellcheck"; Desc = "Shell script linter and validator" } ) foreach ($tool in $cliTools) { Refresh-Path if (Test-CmdExists $tool.Cmd) { Write-OK "$($tool.Name) is installed" Add-Result $tool.Name "PRESENT" "" } else { Write-Info "$($tool.Name): $($tool.Desc)" if (Get-YesNo "Install $($tool.Name)?") { if (Test-CmdExists 'choco') { try { choco install $tool.Name --yes --no-progress 2>$null Refresh-Path if (Test-CmdExists $tool.Cmd) { Write-OK "$($tool.Name) installed" Add-Result $tool.Name "INSTALLED" "" } else { Write-Warn "$($tool.Name) installed but not yet on PATH" Add-Result $tool.Name "INSTALLED" "Restart terminal for PATH" } } catch { Write-Fail "$($tool.Name) install failed: $_" Add-Result $tool.Name "FAILED" "$_" } } elseif (Test-CmdExists 'winget') { try { $wingetIds = @{ "ripgrep" = "BurntSushi.ripgrep.MSVC" "jq" = "jqlang.jq" "fd" = "sharkdp.fd" "7zip" = "7zip.7zip" "imagemagick" = "ImageMagick.ImageMagick" "ghostscript" = "ArtifexSoftware.GhostScript" "ffmpeg" = "Gyan.FFmpeg" "bat" = "sharkdp.bat" "fzf" = "junegunn.fzf" "yq" = "MikeFarah.yq" "tree" = "" "delta" = "dandavison.delta" "shellcheck" = "koalaman.shellcheck" } $id = $wingetIds[$tool.Name] if ($id) { winget install --id $id --accept-source-agreements --accept-package-agreements --silent 2>$null } Refresh-Path Add-Result $tool.Name "INSTALLED" "via winget" } catch { Add-Result $tool.Name "FAILED" "$_" } } else { Write-Warn "No package manager available. Install $($tool.Name) manually." Add-Result $tool.Name "SKIPPED" "No package manager" } } else { Add-Result $tool.Name "SKIPPED" "User declined" } } } # ─── 2g. Python packages ────────────────────────────────────────────────────── Write-Step "Python package libraries" if (Test-CmdExists 'python') { Write-Info "These packages enhance what Claude Code and AI tools can do:" Write-Info " Data: numpy, pandas, polars, matplotlib, openpyxl" Write-Info " Web: requests, httpx, beautifulsoup4" Write-Info " AI: anthropic, openai, fastmcp, mcp" Write-Info " Dev: pydantic, rich, pyyaml, python-dotenv" if (Get-YesNo "Install recommended Python packages?") { $packages = @( 'numpy', 'pandas', 'polars', 'matplotlib', 'openpyxl', 'requests', 'httpx', 'beautifulsoup4', 'lxml', 'anthropic', 'openai', 'fastmcp', 'mcp', 'pydantic', 'rich', 'pyyaml', 'python-dotenv', 'Pillow', 'chardet', 'tabulate', 'jsonlines' ) try { $pkgList = $packages -join ' ' python -m pip install $pkgList --quiet 2>&1 | Out-Null Write-OK "Installed $($packages.Count) Python packages" Add-Result "Python Packages" "INSTALLED" "$($packages.Count) packages" } catch { Write-Warn "Some packages may have failed: $_" Add-Result "Python Packages" "PARTIAL" "$_" } } else { Add-Result "Python Packages" "SKIPPED" "" } # uv package manager if (-not (Test-CmdExists 'uv')) { Write-Info "uv is a fast Python package manager used by MCP servers." if (Get-YesNo "Install uv?") { try { Invoke-RestMethod https://astral.sh/uv/install.ps1 | Invoke-Expression Refresh-Path Write-OK "uv installed" Add-Result "uv" "INSTALLED" "" } catch { Write-Fail "uv install failed: $_" Add-Result "uv" "FAILED" "$_" } } } else { Write-OK "uv already installed" Add-Result "uv" "PRESENT" "" } } # ═══════════════════════════════════════════════════════════════════════════════ # PHASE 3 - AI TERMINAL INSTALLATIONS # ═══════════════════════════════════════════════════════════════════════════════ Enter-Phase "AI Terminal Solutions" Write-Section "PHASE 3: AI Terminal Solutions" Write-Host "" Write-Host " Which AI terminal solutions would you like to set up?" -ForegroundColor White Write-Host "" Write-Host " ┌──────────────────────────────────────────────────────────────┐" -ForegroundColor DarkCyan Write-Host " │ 1. Claude Code (Anthropic) │" -ForegroundColor DarkCyan Write-Host " │ Best for: Deep code understanding, multi-file edits, │" -ForegroundColor Gray Write-Host " │ agentic coding, MCP servers, security research │" -ForegroundColor Gray Write-Host " │ Needs: Git Bash, Node.js, Anthropic API key or login │" -ForegroundColor Gray Write-Host " │ │" -ForegroundColor DarkCyan Write-Host " │ 2. ChatGPT Terminal (OpenAI) │" -ForegroundColor DarkCyan Write-Host " │ Best for: Quick questions, brainstorming, general AI │" -ForegroundColor Gray Write-Host " │ chat, streaming responses, session history │" -ForegroundColor Gray Write-Host " │ Needs: OpenAI API key │" -ForegroundColor Gray Write-Host " │ │" -ForegroundColor DarkCyan Write-Host " │ 3. GitHub Copilot CLI (GitHub) │" -ForegroundColor DarkCyan Write-Host " │ Best for: Shell command suggestions, git operations, │" -ForegroundColor Gray Write-Host " │ explaining commands, GitHub integration │" -ForegroundColor Gray Write-Host " │ Needs: GitHub account with Copilot subscription │" -ForegroundColor Gray Write-Host " └──────────────────────────────────────────────────────────────┘" -ForegroundColor DarkCyan Write-Host "" $installClaude = Get-YesNo "Install Claude Code?" $true $installChatGPT = Get-YesNo "Install ChatGPT Terminal?" $true $installCopilot = Get-YesNo "Install GitHub Copilot CLI?" $true # ─── 3a. Claude Code ────────────────────────────────────────────────────────── if ($installClaude) { Write-Section "Installing Claude Code" # Check for existing install Refresh-Path $claudeExists = (Test-CmdExists 'claude') -or (Test-Path "$env:USERPROFILE\.local\bin\claude.exe") if ($claudeExists) { Write-OK "Claude Code already installed" try { $ccVer = claude --version 2>$null Write-OK "Version: $ccVer" } catch { Write-Info "Could not retrieve version" } Add-Result "Claude Code" "PRESENT" "" } else { Write-Step "Installing Claude Code..." # Method 1: Use existing Install-ClaudeCode.ps1 if available $ccScript = Join-Path $ScriptsDir "Install-ClaudeCode.ps1" if (Test-Path $ccScript) { Write-Info "Found Install-ClaudeCode.ps1 - using your custom installer" try { & $ccScript Refresh-Path Add-Result "Claude Code" "INSTALLED" "via Install-ClaudeCode.ps1" } catch { Write-Warn "Custom installer had an issue: $_" Write-Info "Trying the official method..." } } # Method 2: Official native installer if (-not (Test-CmdExists 'claude')) { Write-Info "Using official native installer..." try { $installScript = Invoke-RestMethod -Uri "https://claude.ai/install.ps1" -UseBasicParsing Invoke-Expression $installScript Refresh-Path } catch { Write-Warn "Native installer issue: $_" } } # Method 3: npm fallback if (-not (Test-CmdExists 'claude') -and (Test-CmdExists 'npm')) { Write-Info "Trying npm install as fallback..." try { npm install -g @anthropic-ai/claude-code 2>&1 | Out-Host Refresh-Path } catch { Write-Info "npm fallback also failed: $_" } } # Final check $env:Path += ";$env:USERPROFILE\.local\bin" if (Test-CmdExists 'claude') { Write-OK "Claude Code installed successfully!" try { Write-OK "Version: $(claude --version 2>$null)" } catch {} Add-Result "Claude Code" "INSTALLED" "" } else { Write-Fail "Claude Code could not be installed automatically" Write-Tip "Try manually: irm https://claude.ai/install.ps1 | iex" Add-Result "Claude Code" "FAILED" "Manual install needed" } } # Run Setup-ClaudeCodeEnv.ps1 if available $envScript = Join-Path $ScriptsDir "Setup-ClaudeCodeEnv.ps1" if (Test-Path $envScript) { Write-Host "" if (Get-YesNo "Run Setup-ClaudeCodeEnv.ps1 (Python libs, CLI tools, Git config)?") { Write-Step "Running Claude Code environment setup..." try { & $envScript Add-Result "Claude Env Setup" "RAN" "" } catch { Write-Warn "Env setup error: $_" Add-Result "Claude Env Setup" "ERROR" "$_" } } } # Run Install-ClaudePlugins.ps1 if available $pluginScript = Join-Path $ScriptsDir "Install-ClaudePlugins.ps1" if (Test-Path $pluginScript) { Write-Host "" if (Get-YesNo "Run Install-ClaudePlugins.ps1 (security, frontend, playground, MCP)?") { Write-Step "Installing Claude Code plugins..." try { & $pluginScript Add-Result "Claude Plugins" "RAN" "" } catch { Write-Warn "Plugin install error: $_" Add-Result "Claude Plugins" "ERROR" "$_" } } } # Run Install-PlaygroundPlugin.ps1 if available $pgScript = Join-Path $ScriptsDir "Install-PlaygroundPlugin.ps1" if (Test-Path $pgScript) { Write-Host "" if (Get-YesNo "Run Install-PlaygroundPlugin.ps1?") { Write-Step "Installing Playground plugin..." try { & $pgScript Add-Result "Playground Plugin" "RAN" "" } catch { Write-Warn "Playground plugin error: $_" Add-Result "Playground Plugin" "ERROR" "$_" } } } # Configure Git Bash path for Claude Code Write-Step "Configuring Claude Code settings..." $gitBashExe = "$env:ProgramFiles\Git\bin\bash.exe" $claudeSettingsDir = Join-Path $env:USERPROFILE ".claude" $claudeSettingsFile = Join-Path $claudeSettingsDir "settings.json" if (Test-Path $gitBashExe) { if (-not (Test-Path $claudeSettingsDir)) { New-Item -Path $claudeSettingsDir -ItemType Directory -Force | Out-Null } $settings = @{} if (Test-Path $claudeSettingsFile) { try { $settings = Get-Content $claudeSettingsFile -Raw | ConvertFrom-Json -AsHashtable } catch { $settings = @{} } } if (-not $settings.ContainsKey('env')) { $settings['env'] = @{} } $settings['env']['CLAUDE_CODE_GIT_BASH_PATH'] = $gitBashExe $settings | ConvertTo-Json -Depth 10 | Set-Content $claudeSettingsFile -Encoding UTF8 Write-OK "Git Bash path configured in Claude settings" } # Git performance tweaks if (Test-CmdExists 'git') { git config --global core.fscache true 2>$null git config --global core.preloadindex true 2>$null git config --global gc.auto 256 2>$null Write-OK "Git performance settings applied" # Check for git user config $gitUser = git config --global user.name 2>$null $gitEmail = git config --global user.email 2>$null if (-not $gitUser -or -not $gitEmail) { Write-Warn "Git user.name or user.email not set. Git commits will fail without these." Write-Tip "Run: git config --global user.name 'Your Name'" Write-Tip "Run: git config --global user.email 'you@example.com'" } else { Write-OK "Git user: $gitUser <$gitEmail>" } git config --global init.defaultBranch main 2>$null } } # ─── 3b. ChatGPT Terminal ───────────────────────────────────────────────────── if ($installChatGPT) { Write-Section "Installing ChatGPT Terminal" $chatGPTScript = Join-Path $ScriptsDir "Setup-ChatGPT-Terminal.ps1" if (Test-Path $chatGPTScript) { Write-Info "Found Setup-ChatGPT-Terminal.ps1" Write-Info "This sets up an interactive ChatGPT session right in PowerShell." Write-Info "Commands: chatgpt (interactive), cgpt 'question' (one-shot), gpt (alias)" Write-Host "" Write-Step "Running ChatGPT Terminal setup..." try { & $chatGPTScript -InstallOnly Add-Result "ChatGPT Terminal" "INSTALLED" "Use: chatgpt, cgpt, gpt" } catch { Write-Warn "ChatGPT setup error: $_" Add-Result "ChatGPT Terminal" "ERROR" "$_" } } else { Write-Info "Setup-ChatGPT-Terminal.ps1 not found in $ScriptsDir" Write-Info "Setting up ChatGPT terminal manually..." # Check for OpenAI API key if (-not $env:OPENAI_API_KEY) { Write-Host "" Write-Host " An OpenAI API key is needed. Get one at: https://platform.openai.com/api-keys" -ForegroundColor Yellow Write-Host " Enter your OpenAI API key (or press Enter to skip): " -NoNewline -ForegroundColor Yellow $openaiKey = Read-Host if ($openaiKey -and $openaiKey.StartsWith('sk-')) { [System.Environment]::SetEnvironmentVariable('OPENAI_API_KEY', $openaiKey, 'User') $env:OPENAI_API_KEY = $openaiKey Write-OK "OPENAI_API_KEY set" } } else { Write-OK "OPENAI_API_KEY already set" } # Install openai-chatgpt-cli if npm is available if (Test-CmdExists 'npm') { Write-Info "Installing chatgpt CLI via npm..." try { npm install -g chatgpt-cli 2>&1 | Out-Null Refresh-Path Write-OK "chatgpt-cli installed" Add-Result "ChatGPT Terminal" "INSTALLED" "npm chatgpt-cli" } catch { Write-Warn "npm install failed. Use Setup-ChatGPT-Terminal.ps1 instead." Add-Result "ChatGPT Terminal" "PARTIAL" "Script-based only" } } else { Add-Result "ChatGPT Terminal" "SKIPPED" "Need npm or setup script" } } } # ─── 3c. GitHub Copilot CLI ─────────────────────────────────────────────────── if ($installCopilot) { Write-Section "Installing GitHub Copilot CLI" Refresh-Path $ghExists = Test-CmdExists 'gh' if (-not $ghExists) { Write-Info "GitHub CLI (gh) is required for Copilot CLI." if (Get-YesNo "Install GitHub CLI?" $true) { if (Test-CmdExists 'winget') { winget install --id GitHub.cli --accept-source-agreements --accept-package-agreements --silent 2>$null } elseif (Test-CmdExists 'choco') { choco install gh --yes --no-progress 2>$null } else { Write-Info "Downloading from GitHub..." try { $ghRelease = Invoke-RestMethod 'https://api.github.com/repos/cli/cli/releases/latest' $ghAsset = $ghRelease.assets | Where-Object { $_.name -match 'windows_amd64\.msi$' } | Select-Object -First 1 if ($ghAsset) { $ghMsi = Join-Path $script:TempDir $ghAsset.name Invoke-WebRequest -Uri $ghAsset.browser_download_url -OutFile $ghMsi -UseBasicParsing Start-Process msiexec.exe -ArgumentList "/i `"$ghMsi`" /qn /norestart" -Wait -NoNewWindow } } catch { Write-Fail "GitHub CLI download failed: $_" } } Refresh-Path } } if (Test-CmdExists 'gh') { Write-OK "GitHub CLI installed: $(gh --version 2>$null | Select-Object -First 1)" # Install Copilot extension Write-Step "Installing GitHub Copilot CLI extension..." try { gh extension install github/gh-copilot 2>&1 | Out-Host Write-OK "Copilot CLI extension installed" Write-Info "" Write-Info "Usage examples:" Write-Info " gh copilot suggest 'find large files over 100MB'" Write-Info " gh copilot explain 'git rebase -i HEAD~3'" Write-Info "" Write-Tip "You need a GitHub Copilot subscription. Log in with: gh auth login" Add-Result "GitHub Copilot CLI" "INSTALLED" "gh copilot suggest/explain" } catch { Write-Warn "Copilot extension install error: $_" Write-Tip "You may need to log in first: gh auth login" Add-Result "GitHub Copilot CLI" "PARTIAL" "Login may be needed" } } else { Write-Fail "GitHub CLI not available - Copilot CLI cannot be installed" Add-Result "GitHub Copilot CLI" "FAILED" "gh CLI missing" } } # ═══════════════════════════════════════════════════════════════════════════════ # PHASE 3d - NETLIFY CLI & DEPLOYMENT # ═══════════════════════════════════════════════════════════════════════════════ Enter-Phase "Deployment & Extras" Write-Section "Netlify CLI & Deployment Tools" Write-Step "Checking Netlify CLI" Refresh-Path if (Test-CmdExists 'netlify') { $netlifyVer = netlify --version 2>$null Write-OK "Netlify CLI already installed ($netlifyVer)" Add-Result "Netlify CLI" "PRESENT" "$netlifyVer" } else { Write-Info "Netlify CLI lets you deploy websites directly from your terminal." Write-Info "Free tier: 100GB bandwidth, 300 build minutes/month, custom domains." Write-Info "Works beautifully with Claude Code - AI edits your site, then deploys." if (Get-YesNo "Install Netlify CLI?" $true) { if (Test-CmdExists 'npm') { Write-Info "Installing netlify-cli globally via npm..." try { npm install -g netlify-cli 2>&1 | Out-Host Refresh-Path if (Test-CmdExists 'netlify') { Write-OK "Netlify CLI installed ($(netlify --version 2>$null))" Add-Result "Netlify CLI" "INSTALLED" "" } else { Write-Warn "Netlify CLI installed but restart terminal for PATH" Add-Result "Netlify CLI" "INSTALLED" "Restart terminal" } } catch { Write-Fail "Netlify CLI install failed: $_" Add-Result "Netlify CLI" "FAILED" "$_" } } else { Write-Fail "npm not available - install Node.js first" Add-Result "Netlify CLI" "FAILED" "npm missing" } } else { Add-Result "Netlify CLI" "SKIPPED" "" } } # Netlify authentication guide Write-Host "" Write-Host " ┌──────────────────────────────────────────────────────────────┐" -ForegroundColor DarkCyan Write-Host " │ NETLIFY AUTHENTICATION & DEPLOYMENT │" -ForegroundColor DarkCyan Write-Host " ├──────────────────────────────────────────────────────────────┤" -ForegroundColor DarkCyan Write-Host " │ │" -ForegroundColor DarkCyan Write-Host " │ LOGIN: │" -ForegroundColor DarkCyan Write-Host " │ netlify login │" -ForegroundColor White Write-Host " │ (browser opens > click Authorize > return to terminal) │" -ForegroundColor Gray Write-Host " │ │" -ForegroundColor DarkCyan Write-Host " │ DEPLOY FROM ANY AI TERMINAL: │" -ForegroundColor DarkCyan Write-Host " │ cd C:\path\to\my-website │" -ForegroundColor White Write-Host " │ netlify init # link folder to Netlify site │" -ForegroundColor White Write-Host " │ netlify deploy # preview draft │" -ForegroundColor White Write-Host " │ netlify deploy --prod # push live │" -ForegroundColor White Write-Host " │ │" -ForegroundColor DarkCyan Write-Host " │ AI + DEPLOY (Claude Code): │" -ForegroundColor DarkCyan Write-Host " │ cd C:\my-site && claude │" -ForegroundColor White Write-Host " │ > fix the nav menu and deploy a draft to Netlify │" -ForegroundColor White Write-Host " │ │" -ForegroundColor DarkCyan Write-Host " │ AI + DEPLOY (ChatGPT > manual): │" -ForegroundColor DarkCyan Write-Host " │ gpt │" -ForegroundColor White Write-Host " │ You > write me the CSS for a responsive hero section │" -ForegroundColor White Write-Host " │ (copy output to your file, then: netlify deploy --prod) │" -ForegroundColor Gray Write-Host " │ │" -ForegroundColor DarkCyan Write-Host " │ AI + DEPLOY (Copilot CLI): │" -ForegroundColor DarkCyan Write-Host " │ gh copilot suggest 'deploy this folder to Netlify' │" -ForegroundColor White Write-Host " └──────────────────────────────────────────────────────────────┘" -ForegroundColor DarkCyan if (Test-CmdExists 'netlify') { Write-Host "" if (Get-YesNo "Log in to Netlify now?" $false) { Write-Step "Opening Netlify login..." netlify login } } # ─── Additional: Windows Terminal & Fonts ────────────────────────────────────── Write-Step "Checking Windows Terminal" $wtInstalled = Get-AppxPackage -Name "Microsoft.WindowsTerminal" -ErrorAction SilentlyContinue if ($wtInstalled) { Write-OK "Windows Terminal installed" Add-Result "Windows Terminal" "PRESENT" "" } else { Write-Info "Windows Terminal provides tabs, GPU rendering, and Nerd Font support." if (Get-YesNo "Install Windows Terminal?") { if (Test-CmdExists 'winget') { winget install --id Microsoft.WindowsTerminal --accept-source-agreements --accept-package-agreements --silent 2>$null Write-OK "Windows Terminal installed" Add-Result "Windows Terminal" "INSTALLED" "" } else { Write-Info "Install from Microsoft Store: search 'Windows Terminal'" Add-Result "Windows Terminal" "SKIPPED" "Install from Store" } } else { Add-Result "Windows Terminal" "SKIPPED" "" } } # UTF-8 console Write-Step "Setting console to UTF-8" $currentCP = chcp 2>$null if ($currentCP -notmatch '65001') { chcp 65001 > $null 2>&1 Write-OK "Console code page set to UTF-8 (65001)" } else { Write-OK "Console already UTF-8" } # Vercel CLI Write-Step "Checking Vercel CLI" Refresh-Path if (Test-CmdExists 'vercel') { Write-OK "Vercel CLI already installed" Add-Result "Vercel CLI" "PRESENT" "" } else { Write-Info "Vercel is another popular hosting platform (like Netlify)." if (Get-YesNo "Install Vercel CLI?" $false) { if (Test-CmdExists 'npm') { npm install -g vercel 2>&1 | Out-Null Refresh-Path Write-OK "Vercel CLI installed" Add-Result "Vercel CLI" "INSTALLED" "" } } else { Add-Result "Vercel CLI" "SKIPPED" "" } } # ═══════════════════════════════════════════════════════════════════════════════ # PHASE 4 - VALIDATION & HEALTH CHECK # ═══════════════════════════════════════════════════════════════════════════════ Enter-Phase "Validation & Health Check" Write-Section "PHASE 4: Environment Health Check" Refresh-Path $checks = @( @{ Name = "PowerShell"; Cmd = "pwsh --version"; Alt = "`$PSVersionTable.PSVersion" }, @{ Name = "Git"; Cmd = "git --version"; Alt = $null }, @{ Name = "Git Bash"; Cmd = $null; Path = "$env:ProgramFiles\Git\bin\bash.exe" }, @{ Name = "Node.js"; Cmd = "node --version"; Alt = $null }, @{ Name = "npm"; Cmd = "npm --version"; Alt = $null }, @{ Name = "npx"; Cmd = "npx --version"; Alt = $null }, @{ Name = "Python"; Cmd = "python --version"; Alt = $null }, @{ Name = "pip"; Cmd = "pip --version"; Alt = $null }, @{ Name = "uv"; Cmd = "uv --version"; Alt = $null }, @{ Name = "Claude Code"; Cmd = "claude --version"; Alt = $null; Path2 = "$env:USERPROFILE\.local\bin\claude.exe" }, @{ Name = "GitHub CLI"; Cmd = "gh --version"; Alt = $null }, @{ Name = "ripgrep"; Cmd = "rg --version"; Alt = $null }, @{ Name = "jq"; Cmd = "jq --version"; Alt = $null }, @{ Name = "ImageMagick"; Cmd = "magick --version"; Alt = $null }, @{ Name = "GhostScript"; Cmd = "gswin64c --version"; Alt = $null }, @{ Name = "FFmpeg"; Cmd = "ffmpeg -version"; Alt = $null }, @{ Name = "bat"; Cmd = "bat --version"; Alt = $null }, @{ Name = "fzf"; Cmd = "fzf --version"; Alt = $null }, @{ Name = "delta"; Cmd = "delta --version"; Alt = $null }, @{ Name = "shellcheck"; Cmd = "shellcheck --version"; Alt = $null }, @{ Name = "yq"; Cmd = "yq --version"; Alt = $null }, @{ Name = "Netlify CLI"; Cmd = "netlify --version"; Alt = $null }, @{ Name = "Vercel CLI"; Cmd = "vercel --version"; Alt = $null }, @{ Name = "Chocolatey"; Cmd = "choco --version"; Alt = $null } ) # ─── Functional Validation (not just version checks) ────────────────────────── Write-Step "Running functional validation tests" $funcTests = @() # Test Node.js can actually execute JavaScript if (Test-CmdExists 'node') { try { $nodeTest = node -e "console.log('node-ok')" 2>&1 if ($nodeTest -match 'node-ok') { $funcTests += @{ Name = "Node.js exec"; Pass = $true } } else { $funcTests += @{ Name = "Node.js exec"; Pass = $false } } } catch { $funcTests += @{ Name = "Node.js exec"; Pass = $false } } } # Test Python can import key AI packages if (Test-CmdExists 'python') { try { $pyTest = python -c "import ssl; print('py-ssl-ok:', ssl.OPENSSL_VERSION)" 2>&1 if ($pyTest -match 'py-ssl-ok') { $funcTests += @{ Name = "Python SSL"; Pass = $true } } else { $funcTests += @{ Name = "Python SSL"; Pass = $false } } } catch { $funcTests += @{ Name = "Python SSL"; Pass = $false } } try { $pyImport = python -c "import requests, pydantic; print('imports-ok')" 2>&1 if ($pyImport -match 'imports-ok') { $funcTests += @{ Name = "Python packages"; Pass = $true } } else { $funcTests += @{ Name = "Python packages"; Pass = $false } } } catch { $funcTests += @{ Name = "Python packages"; Pass = $false } } } # Test npm registry connectivity if (Test-CmdExists 'npm') { try { $npmPing = npm ping 2>&1 if ($LASTEXITCODE -eq 0) { $funcTests += @{ Name = "npm registry"; Pass = $true } } else { $funcTests += @{ Name = "npm registry"; Pass = $false } } } catch { $funcTests += @{ Name = "npm registry"; Pass = $false } } } # Test Git can actually connect (credential check) if (Test-CmdExists 'git') { try { $gitLs = git ls-remote --heads https://github.com/anthropics/claude-code.git 2>&1 if ($LASTEXITCODE -eq 0) { $funcTests += @{ Name = "Git HTTPS"; Pass = $true } } else { $funcTests += @{ Name = "Git HTTPS"; Pass = $false } } } catch { $funcTests += @{ Name = "Git HTTPS"; Pass = $false } } } # Test ImageMagick delegates (GhostScript for PDF) if (Test-CmdExists 'magick') { try { $delegates = magick identify -list format 2>&1 if ($delegates -match 'PDF') { $funcTests += @{ Name = "ImageMagick PDF"; Pass = $true } } else { $funcTests += @{ Name = "ImageMagick PDF"; Pass = $false } } } catch { $funcTests += @{ Name = "ImageMagick PDF"; Pass = $false } } } foreach ($ft in $funcTests) { if ($ft.Pass) { Write-Host " $($ft.Name.PadRight(18)) " -NoNewline Write-Host "PASS" -ForegroundColor Green } else { Write-Host " $($ft.Name.PadRight(18)) " -NoNewline Write-Host "FAIL" -ForegroundColor Red } } Write-Host "" $colName = "Component".PadRight(18) $colStatus = "Status".PadRight(10) $colVer = "Version / Details" Write-Host " $colName $colStatus $colVer" -ForegroundColor White Write-Host " $('-' * 18) $('-' * 10) $('-' * 35)" -ForegroundColor DarkGray $passCount = 0 $failCount = 0 foreach ($check in $checks) { $name = $check.Name.PadRight(18) $ver = "" $found = $false # Check via command if ($check.Cmd) { try { $ver = Invoke-Expression $check.Cmd 2>&1 | Select-Object -First 1 if ($LASTEXITCODE -eq 0 -or $ver) { $found = $true } } catch {} } # Check via path if (-not $found -and $check.Path) { if (Test-Path $check.Path) { $found = $true $ver = $check.Path } } if (-not $found -and $check.Path2) { if (Test-Path $check.Path2) { $found = $true $ver = $check.Path2 } } # Check via alt expression if (-not $found -and $check.Alt) { try { $ver = Invoke-Expression $check.Alt 2>&1 if ($ver) { $found = $true } } catch {} } if ($found) { $status = "PASS".PadRight(10) $verStr = "$ver".Trim() if ($verStr.Length -gt 35) { $verStr = $verStr.Substring(0, 35) } Write-Host " $name " -NoNewline Write-Host $status -NoNewline -ForegroundColor Green Write-Host $verStr -ForegroundColor Gray $passCount++ } else { $status = "MISSING".PadRight(10) Write-Host " $name " -NoNewline Write-Host $status -NoNewline -ForegroundColor Red Write-Host "Not installed" -ForegroundColor DarkGray $failCount++ } } Write-Host "" Write-Host " $('-' * 65)" -ForegroundColor DarkGray Write-Host " Total: $passCount passed, $failCount missing" -ForegroundColor $(if ($failCount -eq 0) { 'Green' } else { 'Yellow' }) # ═══════════════════════════════════════════════════════════════════════════════ # PHASE 5 - QUICK REFERENCE GUIDE # ═══════════════════════════════════════════════════════════════════════════════ Complete-Progress "AI Terminal Kickstart" Write-Section "PHASE 5: Quick Reference Guide" $elapsed = (Get-Date) - $script:StartTime Write-Host @" ┌─────────────────────────────────────────────────────────────────┐ │ HOW TO USE YOUR AI TERMINALS │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ CLAUDE CODE (Best for coding, files, agents) │ │ ───────────────────────────────────────────── │ │ claude Start interactive session │ │ claude "fix this bug" One-shot command │ │ claude --help See all options │ │ claude doctor Diagnose configuration │ │ claude mcp list Show MCP servers │ │ /help In-session help │ │ │ │ First-time auth: run 'claude' > browser opens > log in at │ │ claude.ai > click Authorize > return to terminal > done! │ │ │ │ Keyboard: Enter = send | Ctrl+C = cancel | Escape = exit │ │ Power moves: Ask it to edit files, run tests, create PRs │ │ │ │ CHATGPT TERMINAL (Best for quick Q&A, brainstorming) │ │ ───────────────────────────────────────────────── │ │ chatgpt Start interactive chat │ │ gpt Alias for chatgpt │ │ cgpt "explain X" Quick one-shot question │ │ /model gpt-4o Switch models mid-chat │ │ /save Export session to JSON │ │ /clear Reset conversation │ │ │ │ GITHUB COPILOT CLI (Best for shell commands) │ │ ───────────────────────────────────────────── │ │ gh copilot suggest "..." Get command suggestions │ │ gh copilot explain "..." Explain a command │ │ gh auth login Log in to GitHub first │ │ │ ├─────────────────────────────────────────────────────────────────┤ │ NETLIFY DEPLOYMENT (direct deploy from any AI terminal) │ │ ────────────────────────────────────────────────── │ │ netlify login Authenticate (one-time) │ │ cd C:\path\to\site Go to your site folder │ │ netlify init Link to Netlify site (one-time) │ │ netlify deploy Preview deploy (draft URL) │ │ netlify deploy --prod Push to production (live URL) │ │ netlify open Open live site in browser │ │ netlify env:set KEY val Set environment variables │ │ │ │ AI + DEPLOY WORKFLOWS: │ │ Claude: cd site && claude │ │ > fix the footer and deploy a draft to Netlify │ │ ChatGPT: gpt > ask for code > paste into file > netlify deploy│ │ Copilot: gh copilot suggest "deploy to Netlify" │ │ │ ├─────────────────────────────────────────────────────────────────┤ │ GUI & WEB INTERFACES │ │ ──────────────────── │ │ Claude: https://claude.ai/code (web app) │ │ VS Code extension: "Claude Code" in marketplace │ │ JetBrains extension: search "Claude" in plugins │ │ ChatGPT: https://chatgpt.com (web interface) │ │ Desktop app: https://openai.com/chatgpt/download │ │ Copilot: Built into VS Code (GitHub Copilot extension) │ │ Built into github.com (Copilot Chat in repos) │ │ Netlify: https://app.netlify.com (dashboard) │ │ │ ├─────────────────────────────────────────────────────────────────┤ │ GOOGLE DRIVE & APP CONNECTORS (via MCP Servers) │ │ ─────────────────────────────────────────────── │ │ Claude Code can connect to external apps via MCP: │ │ │ │ Google Drive: │ │ claude mcp add gdrive -- npx @anthropic/gdrive-mcp │ │ (requires Google OAuth - follow the prompts) │ │ Then: > list my Google Drive files │ │ > read the doc called "Project Plan" │ │ │ │ Slack: │ │ claude mcp add slack -- npx @anthropic/slack-mcp │ │ Then: > check #general for recent messages │ │ │ │ GitHub: │ │ claude mcp add github -- npx @anthropic/github-mcp │ │ Then: > list open PRs in my repo │ │ │ │ Filesystem (already built-in): │ │ Claude Code reads/writes files in your current directory │ │ by default - no extra setup needed. │ │ │ │ Playwright (browser automation): │ │ claude mcp add playwright -- npx @playwright/mcp@latest │ │ Then: > take a screenshot of my site at localhost:8080 │ │ │ │ COMMON TIPS FOR ALL TERMINALS │ │ ───────────────────────────── │ │ - Be specific in your prompts for better results │ │ - Use quotes around multi-word arguments │ │ - Start simple, then add detail if output is wrong │ │ - Check 'claude doctor' or '/help' if something breaks │ │ - Restart terminal after fresh installs for PATH updates │ │ │ └─────────────────────────────────────────────────────────────────┘ "@ -ForegroundColor Cyan # ═══════════════════════════════════════════════════════════════════════════════ # FINAL SUMMARY # ═══════════════════════════════════════════════════════════════════════════════ Write-Host "" Write-Host " ╔═══════════════════════════════════════════════════════════════════╗" -ForegroundColor Green Write-Host " ║ KICKSTART COMPLETE ║" -ForegroundColor Green Write-Host " ╠═══════════════════════════════════════════════════════════════════╣" -ForegroundColor Green Write-Host " ║ ║" -ForegroundColor Green # Display results table $colComp = "Component".PadRight(22) $colStat = "Status".PadRight(12) $colDetail = "Detail" Write-Host " ║ $colComp $colStat $colDetail" -ForegroundColor Green Write-Host " ║ $('-' * 22) $('-' * 12) $('-' * 25)" -ForegroundColor DarkGreen foreach ($r in $script:Results) { $comp = $r.Component.PadRight(22) $stat = $r.Status.PadRight(12) $det = if ($r.Detail.Length -gt 25) { $r.Detail.Substring(0, 25) } else { $r.Detail } $color = switch -Regex ($r.Status) { 'INSTALL|PRESENT|RAN|OK' { 'Green' } 'SKIPPED|PARTIAL' { 'Yellow' } 'FAILED|ERROR' { 'Red' } default { 'Gray' } } Write-Host " ║ " -NoNewline -ForegroundColor Green Write-Host "$comp " -NoNewline -ForegroundColor $color Write-Host "$stat " -NoNewline -ForegroundColor $color Write-Host "$det" -ForegroundColor Gray } Write-Host " ║ ║" -ForegroundColor Green Write-Host " ║ Time elapsed: $($elapsed.ToString('mm\:ss')) ║" -ForegroundColor Green Write-Host " ║ Log file : $($script:LogFile.PadRight(45)) ║" -ForegroundColor Green Write-Host " ║ ║" -ForegroundColor Green Write-Host " ║ NEXT STEPS: ║" -ForegroundColor Green Write-Host " ║ 1. CLOSE this terminal and open a NEW one (PATH refresh) ║" -ForegroundColor Green Write-Host " ║ 2. Run 'claude' to start Claude Code ║" -ForegroundColor Green Write-Host " ║ 3. Run 'chatgpt' or 'gpt' for ChatGPT ║" -ForegroundColor Green Write-Host " ║ 4. Run 'gh auth login' then 'gh copilot suggest ...' ║" -ForegroundColor Green Write-Host " ║ ║" -ForegroundColor Green Write-Host " ╚═══════════════════════════════════════════════════════════════════╝" -ForegroundColor Green Write-Host "" # Cleanup temp if empty if ((Get-ChildItem $script:TempDir -File -ErrorAction SilentlyContinue).Count -le 1) { # Keep the log file, remove installers Get-ChildItem $script:TempDir -File -Exclude "kickstart.log" -ErrorAction SilentlyContinue | Remove-Item -Force -ErrorAction SilentlyContinue } Write-Host " Press any key to exit..." -ForegroundColor Gray try { $null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown') } catch {}