Created
January 9, 2026 20:47
-
-
Save davidvasandani/382fe536f85cb7e874ed143b24883c6d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #Requires -RunAsAdministrator | |
| <# | |
| .SYNOPSIS | |
| Complete Development Environment Setup for Windows | |
| .DESCRIPTION | |
| Installs and configures all development tools needed for Claude Code development: | |
| - Scoop package manager | |
| - Node.js LTS | |
| - Git with git-bash | |
| - VS Code | |
| - Claude Code CLI | |
| All tools are installed to user space and configured automatically. | |
| .PARAMETER TargetUser | |
| Username for installation (default: Administrator) | |
| Useful when running remotely via incus exec where env vars aren't set | |
| .NOTES | |
| Author: Generated with Claude Code | |
| Version: 2.0 | |
| - Fixed Unicode encoding issues for remote execution | |
| - Added -RunAsAdmin support for elevated sessions | |
| - Handles missing USERNAME/USERPROFILE environment variables | |
| #> | |
| param( | |
| [string]$TargetUser = "Administrator", | |
| [switch]$SkipVSCode, | |
| [switch]$SkipContextMenu | |
| ) | |
| $ErrorActionPreference = "Stop" | |
| $ProgressPreference = "SilentlyContinue" | |
| # Color output functions | |
| function Write-Success { param($Message) Write-Host "[OK] $Message" -ForegroundColor Green } | |
| function Write-Info { param($Message) Write-Host "[INFO] $Message" -ForegroundColor Cyan } | |
| function Write-ScriptWarning { param($Message) Write-Host "[WARN] $Message" -ForegroundColor Yellow } | |
| function Write-ScriptError { param($Message) Write-Host "[ERROR] $Message" -ForegroundColor Red } | |
| # Banner | |
| Write-Host "" | |
| Write-Host "=======================================================" -ForegroundColor Magenta | |
| Write-Host " Development Environment Setup for Windows" -ForegroundColor Magenta | |
| Write-Host "=======================================================" -ForegroundColor Magenta | |
| Write-Host "" | |
| # Check if running as admin | |
| $isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) | |
| if (-not $isAdmin) { | |
| Write-ScriptWarning "This script should be run as Administrator for best results" | |
| Write-Info "Continuing anyway, but some features may not work..." | |
| } | |
| # Set up working directory | |
| # When running via incus exec, env vars may not be set, so use TargetUser parameter | |
| $WorkDir = "C:\Users\$TargetUser\code" | |
| Write-Info "Setting up working directory for user: $TargetUser" | |
| Write-Info "Working directory: $WorkDir" | |
| if (-not (Test-Path $WorkDir)) { | |
| New-Item -ItemType Directory -Path $WorkDir -Force | Out-Null | |
| Write-Success "Created working directory" | |
| } else { | |
| Write-Success "Working directory exists" | |
| } | |
| Set-Location $WorkDir | |
| # Fix PATH permanently for Scoop prerequisites (Robocopy) | |
| Write-Info "Configuring system PATH for development tools..." | |
| $currentUserPath = [Environment]::GetEnvironmentVariable('Path', 'User') | |
| $systemPaths = "C:\Windows\System32;C:\Windows\System32\WindowsPowerShell\v1.0" | |
| # Only add System32 to User PATH if not already present | |
| if ($currentUserPath -notlike "*Windows\System32*") { | |
| [Environment]::SetEnvironmentVariable('Path', "$systemPaths;$currentUserPath", 'User') | |
| Write-Success "Added Windows System32 to PATH" | |
| } | |
| # Reload PATH for current session | |
| $env:PATH = [Environment]::GetEnvironmentVariable('Path', 'User') + ';' + [Environment]::GetEnvironmentVariable('Path', 'Machine') | |
| Write-Host "" | |
| Write-Host "-------------------------------------------------------" -ForegroundColor Gray | |
| Write-Host "STEP 1: Installing Scoop Package Manager" -ForegroundColor Yellow | |
| Write-Host "-------------------------------------------------------" -ForegroundColor Gray | |
| if (Get-Command scoop -ErrorAction SilentlyContinue) { | |
| Write-Success "Scoop is already installed" | |
| scoop update | |
| } else { | |
| Write-Info "Setting PowerShell execution policy..." | |
| try { | |
| Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser -Force -ErrorAction Stop | |
| Write-Success "Execution policy set" | |
| } catch { | |
| Write-Info "Execution policy already configured (this is fine)" | |
| } | |
| Write-Info "Downloading Scoop installer..." | |
| try { | |
| # Download installer as recommended by Scoop docs | |
| # https://github.com/ScoopInstaller/Install#for-admin | |
| Invoke-RestMethod get.scoop.sh -outfile "$WorkDir\install.ps1" | |
| Write-Info "Installing Scoop..." | |
| # Use -RunAsAdmin flag if running as Administrator | |
| if ($isAdmin) { | |
| Write-Info "Running as Administrator - using -RunAsAdmin flag" | |
| & "$WorkDir\install.ps1" -RunAsAdmin | |
| } else { | |
| Write-Info "Running as regular user" | |
| & "$WorkDir\install.ps1" | |
| } | |
| # Reload PATH after installation | |
| $env:PATH = [Environment]::GetEnvironmentVariable('Path', 'User') + ';' + [Environment]::GetEnvironmentVariable('Path', 'Machine') | |
| # Verify Scoop installed | |
| if (Get-Command scoop -ErrorAction SilentlyContinue) { | |
| Write-Success "Scoop installed successfully" | |
| } else { | |
| Write-ScriptError "Scoop installation failed - command not found" | |
| Write-Info "Check the Scoop installation log above for details" | |
| exit 1 | |
| } | |
| } catch { | |
| Write-ScriptError "Failed to install Scoop: $_" | |
| exit 1 | |
| } | |
| } | |
| # Reload PATH to include Scoop | |
| $env:PATH = [Environment]::GetEnvironmentVariable('Path', 'User') + ';' + [Environment]::GetEnvironmentVariable('Path', 'Machine') | |
| Write-Host "" | |
| Write-Host "-------------------------------------------------------" -ForegroundColor Gray | |
| Write-Host "STEP 2: Installing Node.js LTS" -ForegroundColor Yellow | |
| Write-Host "-------------------------------------------------------" -ForegroundColor Gray | |
| if (Get-Command node -ErrorAction SilentlyContinue) { | |
| $nodeVersion = node --version | |
| Write-Success "Node.js is already installed: $nodeVersion" | |
| } else { | |
| Write-Info "Installing Node.js via Scoop..." | |
| scoop install nodejs-lts | |
| Write-Success "Node.js installed successfully" | |
| } | |
| # Reload PATH | |
| $env:PATH = [Environment]::GetEnvironmentVariable('Path', 'User') + ';' + [Environment]::GetEnvironmentVariable('Path', 'Machine') | |
| Write-Host "" | |
| Write-Host "-------------------------------------------------------" -ForegroundColor Gray | |
| Write-Host "STEP 3: Installing Git with git-bash" -ForegroundColor Yellow | |
| Write-Host "-------------------------------------------------------" -ForegroundColor Gray | |
| if (Get-Command git -ErrorAction SilentlyContinue) { | |
| $gitVersion = git --version | |
| Write-Success "Git is already installed: $gitVersion" | |
| } else { | |
| Write-Info "Installing Git via Scoop..." | |
| scoop install git | |
| Write-Success "Git installed successfully" | |
| } | |
| # Reload PATH | |
| $env:PATH = [Environment]::GetEnvironmentVariable('Path', 'User') + ';' + [Environment]::GetEnvironmentVariable('Path', 'Machine') | |
| Write-Host "" | |
| Write-Host "-------------------------------------------------------" -ForegroundColor Gray | |
| Write-Host "STEP 4: Installing Claude Code CLI" -ForegroundColor Yellow | |
| Write-Host "-------------------------------------------------------" -ForegroundColor Gray | |
| if (Get-Command claude -ErrorAction SilentlyContinue) { | |
| Write-Success "Claude Code is already installed" | |
| } else { | |
| Write-Info "Installing Claude Code via npm..." | |
| npm install -g @anthropic-ai/claude-code | |
| Write-Success "Claude Code installed successfully" | |
| } | |
| # Set Claude Code Git Bash path | |
| Write-Info "Configuring Claude Code environment..." | |
| # Detect git-bash location dynamically (supports both user and admin Scoop installations) | |
| $userProfilePath = "C:\Users\$TargetUser" | |
| $possiblePaths = @( | |
| "$userProfilePath\scoop\apps\git\current\bin\bash.exe", | |
| "$userProfilePath\scoop\apps\git\current\usr\bin\bash.exe", | |
| "C:\scoop\apps\git\current\bin\bash.exe", | |
| "C:\scoop\apps\git\current\usr\bin\bash.exe" | |
| ) | |
| $gitBashPath = $possiblePaths | Where-Object { Test-Path $_ } | Select-Object -First 1 | |
| if ($gitBashPath) { | |
| [Environment]::SetEnvironmentVariable('CLAUDE_CODE_GIT_BASH_PATH', $gitBashPath, 'User') | |
| $env:CLAUDE_CODE_GIT_BASH_PATH = $gitBashPath | |
| Write-Success "CLAUDE_CODE_GIT_BASH_PATH configured: $gitBashPath" | |
| } else { | |
| Write-ScriptWarning "Could not find git-bash. Claude Code may not work correctly." | |
| Write-Info "You can manually set CLAUDE_CODE_GIT_BASH_PATH environment variable later" | |
| } | |
| if (-not $SkipVSCode) { | |
| Write-Host "" | |
| Write-Host "-------------------------------------------------------" -ForegroundColor Gray | |
| Write-Host "STEP 5: Installing VS Code" -ForegroundColor Yellow | |
| Write-Host "-------------------------------------------------------" -ForegroundColor Gray | |
| if (Get-Command code -ErrorAction SilentlyContinue) { | |
| Write-Success "VS Code is already installed" | |
| } else { | |
| Write-Info "Adding extras bucket..." | |
| try { | |
| scoop bucket add extras 2>&1 | Out-Null | |
| Write-Success "Extras bucket added" | |
| } catch { | |
| # Bucket already exists, continue | |
| Write-Info "Extras bucket already exists" | |
| } | |
| Write-Info "Installing VS Code via Scoop..." | |
| scoop install vscode | |
| Write-Success "VS Code installed successfully" | |
| } | |
| # Reload PATH | |
| $env:PATH = [Environment]::GetEnvironmentVariable('Path', 'User') + ';' + [Environment]::GetEnvironmentVariable('Path', 'Machine') | |
| if (-not $SkipContextMenu) { | |
| Write-Info "Adding VS Code context menu integration..." | |
| # Detect VS Code installation path (user or system scope) | |
| $vscodeRegPaths = @( | |
| "$userProfilePath\scoop\apps\vscode\current", | |
| "C:\scoop\apps\vscode\current" | |
| ) | |
| $vscodeRegPath = $vscodeRegPaths | Where-Object { Test-Path $_ } | Select-Object -First 1 | |
| if ($vscodeRegPath) { | |
| try { | |
| reg import "$vscodeRegPath\install-context.reg" 2>&1 | Out-Null | |
| Write-Success "Context menu integration added" | |
| reg import "$vscodeRegPath\install-associations.reg" 2>&1 | Out-Null | |
| Write-Success "File associations added" | |
| reg import "$vscodeRegPath\install-github-integration.reg" 2>&1 | Out-Null | |
| Write-Success "GitHub integration added" | |
| } catch { | |
| Write-ScriptWarning "Could not add VS Code integrations: $_" | |
| } | |
| } else { | |
| Write-ScriptWarning "VS Code registry files not found, skipping context menu integration" | |
| } | |
| } | |
| } | |
| Write-Host "" | |
| Write-Host "-------------------------------------------------------" -ForegroundColor Gray | |
| Write-Host "STEP 6: Creating PowerShell Profile" -ForegroundColor Yellow | |
| Write-Host "-------------------------------------------------------" -ForegroundColor Gray | |
| # Construct target user's PowerShell profile path | |
| $targetProfilePath = "$userProfilePath\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1" | |
| $profileDir = Split-Path $targetProfilePath -Parent | |
| if (-not (Test-Path $profileDir)) { | |
| New-Item -ItemType Directory -Path $profileDir -Force | Out-Null | |
| } | |
| $profileContent = @' | |
| # PowerShell Profile - Auto-generated by setup-dev-environment.ps1 | |
| # Load Scoop environment | |
| $env:PATH = [Environment]::GetEnvironmentVariable('Path', 'User') + ';' + [Environment]::GetEnvironmentVariable('Path', 'Machine') | |
| # Load Claude Code Git Bash path | |
| $env:CLAUDE_CODE_GIT_BASH_PATH = [Environment]::GetEnvironmentVariable('CLAUDE_CODE_GIT_BASH_PATH', 'User') | |
| Write-Host "Development environment loaded!" -ForegroundColor Green | |
| Write-Host "" | |
| Write-Host "Available tools:" -ForegroundColor Cyan | |
| Write-Host " scoop - package manager" | |
| Write-Host " node - $(node --version 2>$null)" | |
| Write-Host " npm - v$(npm --version 2>$null)" | |
| Write-Host " git - $(git --version 2>$null)" | |
| Write-Host " claude - $(claude --version 2>$null | Select-Object -First 1)" | |
| if (Get-Command code -ErrorAction SilentlyContinue) { | |
| Write-Host " code - VS Code $(code --version 2>$null | Select-Object -First 1)" | |
| } | |
| Write-Host "" | |
| '@ | |
| Set-Content -Path $targetProfilePath -Value $profileContent -Force | |
| Write-Success "PowerShell profile created at: $targetProfilePath" | |
| Write-Host "" | |
| Write-Host "-------------------------------------------------------" -ForegroundColor Gray | |
| Write-Host "STEP 7: Creating Desktop Shortcut" -ForegroundColor Yellow | |
| Write-Host "-------------------------------------------------------" -ForegroundColor Gray | |
| $desktopShortcut = @" | |
| @echo off | |
| echo ============================================ | |
| echo Starting Development Environment | |
| echo ============================================ | |
| echo. | |
| cd $WorkDir | |
| powershell.exe -NoExit -ExecutionPolicy Bypass -File "$targetProfilePath" | |
| "@ | |
| # Construct target user's desktop path | |
| $desktopPath = "$userProfilePath\Desktop" | |
| Set-Content -Path "$desktopPath\Start-DevTools.bat" -Value $desktopShortcut -Force | |
| Write-Success "Desktop shortcut created at: $desktopPath\Start-DevTools.bat" | |
| Write-Host "" | |
| Write-Host "-------------------------------------------------------" -ForegroundColor Gray | |
| Write-Host "STEP 8: Verification" -ForegroundColor Yellow | |
| Write-Host "-------------------------------------------------------" -ForegroundColor Gray | |
| # Reload environment one more time | |
| $env:PATH = [Environment]::GetEnvironmentVariable('Path', 'User') + ';' + [Environment]::GetEnvironmentVariable('Path', 'Machine') | |
| $env:CLAUDE_CODE_GIT_BASH_PATH = [Environment]::GetEnvironmentVariable('CLAUDE_CODE_GIT_BASH_PATH', 'User') | |
| Write-Host "" | |
| Write-Host "Installed Versions:" -ForegroundColor Cyan | |
| try { | |
| $scoopVer = (scoop --version | Select-Object -First 1) | |
| Write-Host " Scoop: $scoopVer" -ForegroundColor White | |
| } catch { Write-ScriptWarning " Scoop: Not found" } | |
| try { | |
| $nodeVer = node --version | |
| Write-Host " Node: $nodeVer" -ForegroundColor White | |
| } catch { Write-ScriptWarning " Node: Not found" } | |
| try { | |
| $npmVer = npm --version | |
| Write-Host " npm: v$npmVer" -ForegroundColor White | |
| } catch { Write-ScriptWarning " npm: Not found" } | |
| try { | |
| $gitVer = git --version | |
| Write-Host " Git: $gitVer" -ForegroundColor White | |
| } catch { Write-ScriptWarning " Git: Not found" } | |
| try { | |
| $claudeVer = claude --version 2>&1 | Select-Object -First 1 | |
| Write-Host " Claude: $claudeVer" -ForegroundColor White | |
| } catch { Write-ScriptWarning " Claude: Not found" } | |
| if (-not $SkipVSCode) { | |
| try { | |
| $codeVer = code --version | Select-Object -First 1 | |
| Write-Host " VS Code: $codeVer" -ForegroundColor White | |
| } catch { Write-ScriptWarning " VS Code: Not found" } | |
| } | |
| Write-Host "" | |
| Write-Host "=======================================================" -ForegroundColor Green | |
| Write-Host " Installation Complete!" -ForegroundColor Green | |
| Write-Host "=======================================================" -ForegroundColor Green | |
| Write-Host "" | |
| Write-Host "Next steps:" -ForegroundColor Yellow | |
| Write-Host " 1. Close and reopen PowerShell (or use the Desktop shortcut)" | |
| Write-Host " 2. Run 'claude' to configure your Anthropic API key" | |
| Write-Host " 3. Start coding with AI assistance!" | |
| Write-Host "" | |
| Write-Host "Documentation: $WorkDir\README.md" -ForegroundColor Cyan | |
| Write-Host "" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment