# Sundy Handbook ยท RTK installer for Windows # # Recommended usage: # try { $env:RTK_SUNDY_NO_PAUSE='1'; $u='https://sundy.tumpai.site/install-rtk.ps1'; iex ([System.Text.Encoding]::UTF8.GetString((New-Object Net.WebClient).DownloadData($u))) } catch { Write-Host ('RTK installer failed to start: ' + $_.Exception.Message) -ForegroundColor Red } finally { if (-not [Console]::IsInputRedirected) { Read-Host 'Press Enter to exit' } } # # Optional: # $env:RTK_INIT='0' # install only, skip Claude Code / Codex integration # $env:RTK_SUNDY_YES='1' param( [switch]$NoInit, [switch]$ClaudeOnly, [switch]$CodexOnly, [switch]$Yes ) $ErrorActionPreference = "Stop" try { [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false) $OutputEncoding = [Console]::OutputEncoding if ($PSVersionTable.PSEdition -eq "Desktop") { chcp 65001 > $null } } catch {} $InstallDir = Join-Path $env:LOCALAPPDATA "rtk\bin" $RtkExe = Join-Path $InstallDir "rtk.exe" $RunInit = -not $NoInit -and ($env:RTK_INIT -ne "0") $InitClaude = -not $CodexOnly $InitCodex = -not $ClaudeOnly $AssumeYes = $Yes -or ($env:RTK_SUNDY_YES -eq "1") -or ($env:CI -eq "true") $LogFile = Join-Path $env:TEMP "rtk-install-last.log" function Add-Log { param([string]$Message) try { Add-Content -Encoding UTF8 -Path $LogFile -Value $Message } catch {} } try { "RTK installer started at $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" | Set-Content -Encoding UTF8 -Path $LogFile } catch {} function Write-Title { param([string]$Message) Write-Host "" Write-Host "==> $Message" -ForegroundColor Cyan Add-Log "==> $Message" } function Write-Ok { param([string]$Message) Write-Host "OK: $Message" -ForegroundColor Green Add-Log "OK: $Message" } function Write-Info { param([string]$Message) Write-Host "INFO: $Message" -ForegroundColor Cyan Add-Log "INFO: $Message" } function Write-Warn2 { param([string]$Message) Write-Host "WARN: $Message" -ForegroundColor Yellow Add-Log "WARN: $Message" } function Wait-BeforeExit { if ($env:RTK_SUNDY_NO_PAUSE -eq "1" -or $env:CI -eq "true") { return } try { Write-Host "" Read-Host "Press Enter to exit" | Out-Null } catch { try { cmd.exe /c pause | Out-Null } catch {} } } function Read-Line { param([string]$Prompt, [string]$Default = "") try { $value = Read-Host $Prompt if ([string]::IsNullOrWhiteSpace($value)) { return $Default } return $value } catch { return $Default } } function Get-RtkCommand { $cmd = Get-Command rtk -ErrorAction SilentlyContinue if ($cmd) { return $cmd.Source } if (Test-Path $RtkExe) { return $RtkExe } return $null } function Test-RtkValid { $cmd = Get-RtkCommand if (-not $cmd) { return $false } try { & $cmd gain *> $null return ($LASTEXITCODE -eq 0) } catch { return $false } } function Add-UserPath { param([string]$PathToAdd) $userPath = [System.Environment]::GetEnvironmentVariable("Path", "User") $parts = @() if ($userPath) { $parts = $userPath -split ";" | Where-Object { $_ } } $already = $false foreach ($part in $parts) { if ($part.TrimEnd("\") -ieq $PathToAdd.TrimEnd("\")) { $already = $true } } if (-not $already) { $newPath = if ($userPath) { "$userPath;$PathToAdd" } else { $PathToAdd } [System.Environment]::SetEnvironmentVariable("Path", $newPath, "User") Write-Ok "Added RTK to user PATH: $PathToAdd" } if (($env:Path -split ";") -notcontains $PathToAdd) { $env:Path = "$PathToAdd;$env:Path" } } function Download-RtkZip { param([string]$Destination) $assetUrl = $null try { $release = Invoke-RestMethod -Uri "https://api.github.com/repos/rtk-ai/rtk/releases/latest" -Headers @{ "User-Agent" = "Sundy-RTK-Installer" } -TimeoutSec 30 $asset = $release.assets | Where-Object { $_.name -match "x86_64-pc-windows-msvc\.zip$" } | Select-Object -First 1 if ($asset) { $assetUrl = $asset.browser_download_url } } catch { Write-Warn2 ("Could not query GitHub release API: " + $_.Exception.Message) } if (-not $assetUrl) { $assetUrl = "https://github.com/rtk-ai/rtk/releases/latest/download/rtk-x86_64-pc-windows-msvc.zip" } Write-Info "Downloading RTK Windows binary" Invoke-WebRequest -Uri $assetUrl -OutFile $Destination -UseBasicParsing -MaximumRedirection 5 -TimeoutSec 120 } try { Write-Host "" Write-Host "================================================" -ForegroundColor Cyan Write-Host " Sundy RTK token-saver setup - Windows" -ForegroundColor Cyan Write-Host "================================================" -ForegroundColor Cyan Write-Host "" Write-Host "This script will:" Write-Host " 1. Install Rust Token Killer (rtk-ai/rtk)" Write-Host " 2. Add RTK to user PATH" Write-Host " 3. Initialize Claude Code hooks and Codex instructions" Write-Host " 4. Show RTK token savings" Write-Host "" Write-Host "Note: do not install with cargo install rtk; that name can resolve to a different package." Write-Host "" if (-not $AssumeYes) { $go = Read-Line -Prompt "Continue? [Y/n]" -Default "Y" if ($go -and $go -notmatch '^[Yy]') { Write-Info "Cancelled." Wait-BeforeExit return } } Write-Title "1/4 Installing RTK" New-Item -ItemType Directory -Force -Path $InstallDir | Out-Null Add-UserPath $InstallDir if (Test-RtkValid) { $cmd = Get-RtkCommand Write-Ok ("RTK is installed: " + (& $cmd --version)) } else { $existing = Get-Command rtk -ErrorAction SilentlyContinue if ($existing) { Write-Warn2 "A command named rtk exists but failed RTK validation. Installing Rust Token Killer into $InstallDir and preferring it in PATH." } $zipFile = Join-Path $env:TEMP "rtk-windows.zip" $extractDir = Join-Path $env:TEMP ("rtk-windows-" + [Guid]::NewGuid().ToString("N")) Remove-Item $zipFile -Force -ErrorAction SilentlyContinue Download-RtkZip -Destination $zipFile Expand-Archive -LiteralPath $zipFile -DestinationPath $extractDir -Force $downloadedExe = Get-ChildItem -LiteralPath $extractDir -Filter "rtk.exe" -Recurse -File | Select-Object -First 1 if (-not $downloadedExe) { throw "Downloaded RTK archive did not contain rtk.exe" } Copy-Item -LiteralPath $downloadedExe.FullName -Destination $RtkExe -Force Remove-Item $zipFile -Force -ErrorAction SilentlyContinue Remove-Item $extractDir -Recurse -Force -ErrorAction SilentlyContinue if (-not (Test-RtkValid)) { throw "RTK installed but rtk gain validation failed" } Write-Ok ("RTK installed: " + (& $RtkExe --version)) } $Rtk = Get-RtkCommand Write-Title "2/4 Initializing AI tools" if ($RunInit) { if ($InitClaude) { & $Rtk init -g --auto-patch if ($LASTEXITCODE -eq 0) { Write-Ok "Initialized Claude Code global hook" } else { Write-Warn2 "Claude Code initialization exited with $LASTEXITCODE" } } if ($InitCodex) { & $Rtk init -g --codex if ($LASTEXITCODE -eq 0) { Write-Ok "Initialized Codex global instructions" } else { Write-Warn2 "Codex initialization exited with $LASTEXITCODE" } } } else { Write-Info "Skipped initialization" } Write-Title "3/4 Verifying" & $Rtk verify if ($LASTEXITCODE -ne 0) { Write-Warn2 "rtk verify did not fully pass; RTK commands may still work." } Write-Title "4/4 Token savings" & $Rtk gain Write-Host "" Write-Host "================================================" -ForegroundColor Green Write-Host " RTK install complete" -ForegroundColor Green Write-Host "================================================" -ForegroundColor Green Write-Host "" Write-Host "Close this PowerShell window, open a new one, then restart Claude Code / Codex." Write-Host "Try: rtk git status" Write-Host "Log file: $LogFile" Write-Host "" Wait-BeforeExit } catch { Write-Host "" Write-Host ("ERROR: RTK installation stopped: " + $_.Exception.Message) -ForegroundColor Red Add-Log ("ERROR: " + $_.Exception.Message) Write-Host ("Log file: " + $LogFile) -ForegroundColor Yellow Wait-BeforeExit return }