# Sundy Handbook ยท Codex / Claude Code / Gemini CLI restore script for Windows # # Recommended usage: # try { $env:SUNDY_UNINSTALL_NO_PAUSE='1'; $u='https://sundy.tumpai.site/uninstall.ps1'; iex ([System.Text.Encoding]::UTF8.GetString((New-Object Net.WebClient).DownloadData($u))) } catch { Write-Host ('Uninstaller failed to start: ' + $_.Exception.Message) -ForegroundColor Red } finally { if (-not [Console]::IsInputRedirected) { Read-Host 'Press Enter to exit' } } # # Optional: # $env:SUNDY_UNINSTALL_TARGET='codex' # all | codex | claude | gemini # $env:SUNDY_REMOVE_CLI='1' # also try npm uninstall param( [ValidateSet("all", "codex", "claude", "gemini")] [string]$Target = "", [switch]$RemoveCli, [switch]$Yes ) $ErrorActionPreference = "Stop" try { [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false) $OutputEncoding = [Console]::OutputEncoding if ($PSVersionTable.PSEdition -eq "Desktop") { chcp 65001 > $null } } catch {} if (-not $Target) { if ($env:SUNDY_UNINSTALL_TARGET) { $Target = $env:SUNDY_UNINSTALL_TARGET } else { $Target = "all" } } if ($Target -notin @("all", "codex", "claude", "gemini")) { throw "Invalid target: $Target" } $RemoveCli = $RemoveCli -or ($env:SUNDY_REMOVE_CLI -eq "1") $AssumeYes = $Yes -or ($env:SUNDY_UNINSTALL_YES -eq "1") -or ($env:CI -eq "true") $LogFile = Join-Path $env:TEMP "sundy-uninstall-last.log" function Add-Log { param([string]$Message) try { Add-Content -Encoding UTF8 -Path $LogFile -Value $Message } catch {} } try { "Sundy restore 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:SUNDY_UNINSTALL_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-NpmCommand { $cmd = Get-Command npm.cmd -ErrorAction SilentlyContinue if ($cmd) { return $cmd.Source } $cmd = Get-Command npm -CommandType Application -ErrorAction SilentlyContinue if ($cmd) { return $cmd.Source } return $null } function Write-Utf8NoBom { param([string]$Path, [string]$Content) $utf8NoBom = [System.Text.UTF8Encoding]::new($false) [System.IO.File]::WriteAllText($Path, $Content, $utf8NoBom) } function Backup-File { param([string]$Path) if (-not (Test-Path $Path)) { return $null } $backup = "$Path.sundy-uninstall-bak-" + (Get-Date -Format "yyyyMMdd-HHmmss") Copy-Item -LiteralPath $Path -Destination $backup -Force return $backup } function Clear-ProcessEnv { param([string]$Name) Remove-Item "Env:$Name" -ErrorAction SilentlyContinue } function Get-PowerShellProfilePath { try { if ($PROFILE -and $PROFILE.PSObject.Properties["CurrentUserAllHosts"] -and $PROFILE.CurrentUserAllHosts) { return [string]$PROFILE.CurrentUserAllHosts } if ($PROFILE) { return [string]$PROFILE } } catch {} $documents = [Environment]::GetFolderPath("MyDocuments") if ([string]::IsNullOrWhiteSpace($documents)) { $documents = $HOME } return (Join-Path $documents "WindowsPowerShell\profile.ps1") } function Remove-GeminiYoloProfile { $profilePath = Get-PowerShellProfilePath if ([string]::IsNullOrWhiteSpace($profilePath) -or -not (Test-Path $profilePath)) { Write-Info "Gemini PowerShell YOLO profile wrapper was not found" return } $oldProfile = Get-Content -Raw -Path $profilePath $newProfile = [regex]::Replace($oldProfile, "(?ms)\r?\n?# >>> gemini-sundy-yolo >>>.*?# <<< gemini-sundy-yolo <<<\r?\n?", "`n") if ($newProfile -ne $oldProfile) { $backup = Backup-File $profilePath Write-Utf8NoBom -Path $profilePath -Content ($newProfile.TrimEnd() + "`n") Write-Ok "Removed Gemini PowerShell YOLO wrapper" if ($backup) { Write-Ok "Backed up previous profile to $backup" } } else { Write-Info "Gemini PowerShell YOLO profile wrapper was not found" } } function Test-CodexUsesSundy { $configFile = Join-Path $HOME ".codex\config.toml" if (-not (Test-Path $configFile)) { return $false } try { $raw = Get-Content -Raw -Path $configFile return ($raw -match "model_providers\.sundy|sundy-api\.tumpai\.site:2053|Sundy Codex CLI") } catch { return $false } } function Restore-Codex { Write-Title "Restoring Codex" [System.Environment]::SetEnvironmentVariable("SUNDY_API_KEY", $null, "User") Clear-ProcessEnv "SUNDY_API_KEY" Write-Ok "Removed user environment variable SUNDY_API_KEY" $configDir = Join-Path $HOME ".codex" $configFile = Join-Path $configDir "config.toml" if (Test-Path $configFile) { $raw = Get-Content -Raw -Path $configFile if ($raw -match "model_providers\.sundy|sundy-api\.tumpai\.site:2053|Sundy Codex CLI") { $removedBackup = Backup-File $configFile $backup = Get-ChildItem -LiteralPath $configDir -Filter "config.toml.bak-*" -File -ErrorAction SilentlyContinue | Sort-Object Name -Descending | Select-Object -First 1 if ($backup) { Copy-Item -LiteralPath $backup.FullName -Destination $configFile -Force Write-Ok "Restored Codex config from $($backup.FullName)" } else { Remove-Item -LiteralPath $configFile -Force Write-Ok "Removed Sundy-generated Codex config" } if ($removedBackup) { Write-Ok "Backed up current Codex config to $removedBackup" } } else { Write-Info "Codex config does not look Sundy-generated; keeping $configFile" } } else { Write-Info "Codex config was not found" } if ($RemoveCli) { $npm = Get-NpmCommand if ($npm) { & $npm uninstall -g "@openai/codex" if ($LASTEXITCODE -eq 0) { Write-Ok "Tried to uninstall @openai/codex" } else { Write-Warn2 "npm uninstall @openai/codex exited with $LASTEXITCODE" } } else { Write-Warn2 "npm was not found; skipped Codex CLI uninstall" } } } function Restore-Claude { Write-Title "Restoring Claude Code" $envNames = @( "ANTHROPIC_AUTH_TOKEN", "ANTHROPIC_BASE_URL", "ANTHROPIC_DEFAULT_OPUS_MODEL", "ANTHROPIC_DEFAULT_SONNET_MODEL", "ANTHROPIC_DEFAULT_HAIKU_MODEL", "ANTHROPIC_MODEL", "ANTHROPIC_SMALL_FAST_MODEL", "CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY" ) foreach ($name in $envNames) { [System.Environment]::SetEnvironmentVariable($name, $null, "User") Clear-ProcessEnv $name } if (-not (Test-CodexUsesSundy)) { [System.Environment]::SetEnvironmentVariable("SUNDY_API_KEY", $null, "User") Clear-ProcessEnv "SUNDY_API_KEY" } Write-Ok "Removed Sundy/Anthropic user environment variables" $cacheFile = Join-Path $HOME ".claude\cache\gateway-models.json" if (Test-Path $cacheFile) { Remove-Item -LiteralPath $cacheFile -Force Write-Ok "Cleared Claude Code gateway model cache" } $settingsFile = Join-Path $HOME ".claude\settings.json" if (Test-Path $settingsFile) { $backup = Backup-File $settingsFile try { $rawSettings = Get-Content -Raw -Path $settingsFile if ([string]::IsNullOrWhiteSpace($rawSettings)) { $settings = [pscustomobject]@{} } else { $settings = $rawSettings | ConvertFrom-Json } if ($settings.PSObject.Properties["permissions"] -and $null -ne $settings.permissions) { if ($settings.permissions.PSObject.Properties["defaultMode"] -and $settings.permissions.defaultMode -eq "bypassPermissions") { $settings.permissions.PSObject.Properties.Remove("defaultMode") } if ($settings.permissions.PSObject.Properties["skipDangerousModePermissionPrompt"] -and $settings.permissions.skipDangerousModePermissionPrompt -eq $true) { $settings.permissions.PSObject.Properties.Remove("skipDangerousModePermissionPrompt") } if ($settings.permissions.PSObject.Properties.Count -eq 0) { $settings.PSObject.Properties.Remove("permissions") } } $settingsJson = $settings | ConvertTo-Json -Depth 20 Write-Utf8NoBom -Path $settingsFile -Content ($settingsJson + "`n") Write-Ok "Removed Claude Code Sundy YOLO settings" if ($backup) { Write-Ok "Backed up previous settings to $backup" } } catch { Write-Warn2 "Could not edit settings.json: $($_.Exception.Message). Backup: $backup" } } else { Write-Info "Claude Code settings.json was not found" } if ($RemoveCli) { $npm = Get-NpmCommand if ($npm) { & $npm uninstall -g "@anthropic-ai/claude-code" if ($LASTEXITCODE -eq 0) { Write-Ok "Tried to uninstall @anthropic-ai/claude-code" } else { Write-Warn2 "npm uninstall @anthropic-ai/claude-code exited with $LASTEXITCODE" } } else { Write-Warn2 "npm was not found; skipped Claude Code npm package uninstall" } Write-Warn2 "Official Claude Code installers differ by version; this script does not delete the entire .claude folder." } } function Restore-Gemini { Write-Title "Restoring Gemini CLI" $envNames = @( "GEMINI_API_KEY", "GOOGLE_GEMINI_BASE_URL", "GOOGLE_GENAI_API_VERSION", "GEMINI_API_KEY_AUTH_MECHANISM", "GEMINI_MODEL", "GEMINI_SUNDY_MODE" ) foreach ($name in $envNames) { [System.Environment]::SetEnvironmentVariable($name, $null, "User") Clear-ProcessEnv $name } if (-not (Test-CodexUsesSundy)) { [System.Environment]::SetEnvironmentVariable("SUNDY_API_KEY", $null, "User") Clear-ProcessEnv "SUNDY_API_KEY" } Write-Ok "Removed Gemini/Sundy user environment variables" Remove-GeminiYoloProfile $settingsFile = Join-Path $HOME ".gemini\settings.json" if (Test-Path $settingsFile) { $backup = Backup-File $settingsFile try { $rawSettings = Get-Content -Raw -Path $settingsFile if ([string]::IsNullOrWhiteSpace($rawSettings)) { $settings = [pscustomobject]@{} } else { $settings = $rawSettings | ConvertFrom-Json } if ($settings.PSObject.Properties["security"] -and $null -ne $settings.security) { if ($settings.security.PSObject.Properties["auth"] -and $null -ne $settings.security.auth) { if ($settings.security.auth.PSObject.Properties["selectedType"] -and $settings.security.auth.selectedType -eq "gemini-api-key") { $settings.security.auth.PSObject.Properties.Remove("selectedType") } if ($settings.security.auth.PSObject.Properties["enforcedType"] -and $settings.security.auth.enforcedType -eq "gemini-api-key") { $settings.security.auth.PSObject.Properties.Remove("enforcedType") } if ($settings.security.auth.PSObject.Properties.Count -eq 0) { $settings.security.PSObject.Properties.Remove("auth") } } if ($settings.security.PSObject.Properties.Count -eq 0) { $settings.PSObject.Properties.Remove("security") } } $settingsJson = $settings | ConvertTo-Json -Depth 20 Write-Utf8NoBom -Path $settingsFile -Content ($settingsJson + "`n") Write-Ok "Removed Gemini CLI Sundy auth settings" if ($backup) { Write-Ok "Backed up previous settings to $backup" } } catch { Write-Warn2 "Could not edit settings.json: $($_.Exception.Message). Backup: $backup" } } else { Write-Info "Gemini CLI settings.json was not found" } if ($RemoveCli) { $npm = Get-NpmCommand if ($npm) { & $npm uninstall -g "@google/gemini-cli" if ($LASTEXITCODE -eq 0) { Write-Ok "Tried to uninstall @google/gemini-cli" } else { Write-Warn2 "npm uninstall @google/gemini-cli exited with $LASTEXITCODE" } } else { Write-Warn2 "npm was not found; skipped Gemini CLI npm package uninstall" } } } try { Write-Host "" Write-Host "================================================" -ForegroundColor Cyan Write-Host " Sundy restore / uninstall" -ForegroundColor Cyan Write-Host "================================================" -ForegroundColor Cyan Write-Host "" Write-Host "Target: $Target" Write-Host "Remove CLI packages: $RemoveCli" Write-Host "" Write-Host "This script removes Sundy integration and keeps official CLIs by default." 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 } } if ($Target -eq "all" -or $Target -eq "codex") { Restore-Codex } if ($Target -eq "all" -or $Target -eq "claude") { Restore-Claude } if ($Target -eq "all" -or $Target -eq "gemini") { Restore-Gemini } Write-Host "" Write-Host "================================================" -ForegroundColor Green Write-Host " Restore complete" -ForegroundColor Green Write-Host "================================================" -ForegroundColor Green Write-Host "" Write-Host "Close this PowerShell window, open a new one, then run codex, claude, or gemini." Write-Host "Log file: $LogFile" Write-Host "" Wait-BeforeExit } catch { Write-Host "" Write-Host ("ERROR: Restore stopped: " + $_.Exception.Message) -ForegroundColor Red Add-Log ("ERROR: " + $_.Exception.Message) Write-Host ("Log file: " + $LogFile) -ForegroundColor Yellow Wait-BeforeExit return }