---
name: windows-dev-setup
description: PowerShell-based Windows dev environment setup. Use when installing dev tools via winget/scoop/choco, configuring PATH/env vars, setting up Git/SSH/GPG, installing fonts, configuring Windows Terminal, enabling long paths, or bootstrapping a fresh Windows machine for development. Triggers on requests like "cài đặt môi trường dev", "setup windows", "winget install", "config PATH", "Windows Terminal", "set up git on windows".
---

# Windows Dev Environment Setup (PowerShell)

Mọi lệnh dùng PowerShell 7+ (`pwsh`). Nếu user vẫn dùng Windows PowerShell 5.1, khuyến khích nâng cấp: `winget install Microsoft.PowerShell`.

## Pre-flight

```powershell
$PSVersionTable.PSVersion              # >= 7.0 ưu tiên
Get-ExecutionPolicy -List              # CurrentUser nên là RemoteSigned
Set-ExecutionPolicy -Scope CurrentUser RemoteSigned -Force
```

Bật long path (>260 ký tự) — bắt buộc cho Node, Rust, Go monorepo:

```powershell
New-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem' `
  -Name 'LongPathsEnabled' -Value 1 -PropertyType DWORD -Force
git config --system core.longpaths true
```

## Package managers

Ưu tiên thứ tự: **winget** (Microsoft, mặc định) → **scoop** (user-scope, không cần admin) → **choco** (rộng nhất, cần admin).

### winget

```powershell
winget search <term>
winget install --id <Publisher.App> -e --accept-source-agreements --accept-package-agreements
winget upgrade --all --include-unknown
winget list                            # liệt kê đã cài
```

Bộ tool dev tối thiểu:

```powershell
$apps = @(
  'Git.Git',
  'GitHub.cli',
  'Microsoft.PowerShell',
  'Microsoft.WindowsTerminal',
  'Microsoft.VisualStudioCode',
  'OpenJS.NodeJS.LTS',
  'Python.Python.3.12',
  'astral-sh.uv',
  'Docker.DockerDesktop',
  'Microsoft.OpenSSH.Beta'
)
$apps | ForEach-Object { winget install --id $_ -e --accept-source-agreements --accept-package-agreements }
```

### scoop (user-scope, không cần admin)

```powershell
Invoke-RestMethod -Uri https://get.scoop.sh | Invoke-Expression
scoop bucket add extras
scoop bucket add nerd-fonts
scoop install fzf ripgrep bat fd jq gh lazygit delta starship
```

### choco (cần admin)

```powershell
Set-ExecutionPolicy Bypass -Scope Process -Force
[System.Net.ServicePointManager]::SecurityProtocol = 3072
Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
choco install <pkg> -y
```

## PATH và biến môi trường

Đọc/ghi đúng scope — sai scope là lỗi phổ biến nhất:

```powershell
# Đọc
[Environment]::GetEnvironmentVariable('PATH','User')
[Environment]::GetEnvironmentVariable('PATH','Machine')

# Ghi (User-scope, không cần admin)
$old = [Environment]::GetEnvironmentVariable('PATH','User')
[Environment]::SetEnvironmentVariable('PATH', "$old;C:\tools\bin", 'User')

# Ghi biến mới
[Environment]::SetEnvironmentVariable('GOPATH', "$env:USERPROFILE\go", 'User')
```

**Không** dùng `$env:PATH = ...` cho thay đổi vĩnh viễn — chỉ tồn tại trong session hiện tại. Sau khi đổi, mở terminal mới (process cũ không tự refresh).

Refresh PATH trong session hiện tại mà không đóng terminal:

```powershell
$env:PATH = [Environment]::GetEnvironmentVariable('PATH','Machine') + ';' + `
            [Environment]::GetEnvironmentVariable('PATH','User')
```

## Profile PowerShell

```powershell
if (-not (Test-Path $PROFILE)) { New-Item -ItemType File -Path $PROFILE -Force }
code $PROFILE
```

Mẫu profile dev:

```powershell
# Aliases
Set-Alias ll Get-ChildItem
Set-Alias which Get-Command
Set-Alias g git

# History tốt hơn
Set-PSReadLineOption -PredictionSource HistoryAndPlugin -PredictionViewStyle ListView
Set-PSReadLineKeyHandler -Key Tab -Function MenuComplete

# Starship prompt (sau khi `winget install Starship.Starship`)
Invoke-Expression (&starship init powershell)

# fnm (Node version manager)
fnm env --use-on-cd | Out-String | Invoke-Expression
```

## Git / SSH / GPG

```powershell
git config --global user.name "Tên"
git config --global user.email "email@example.com"
git config --global init.defaultBranch main
git config --global core.autocrlf input        # giữ LF trong repo
git config --global core.editor "code --wait"
git config --global pull.rebase true

# SSH key
ssh-keygen -t ed25519 -C "email@example.com"
Get-Service ssh-agent | Set-Service -StartupType Automatic
Start-Service ssh-agent
ssh-add $env:USERPROFILE\.ssh\id_ed25519
Get-Content $env:USERPROFILE\.ssh\id_ed25519.pub | Set-Clipboard

# GitHub CLI auth (đăng ký SSH key luôn)
gh auth login -p ssh -w
```

## Windows Terminal

Settings JSON: `$env:LOCALAPPDATA\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbcw\LocalState\settings.json`

Cài Nerd Font cho icon prompt:

```powershell
scoop install JetBrainsMono-NF
```

Đặt làm default profile, font `JetBrainsMono Nerd Font`, default shell `pwsh.exe`.

## Node / Python / một số runtime phổ biến

```powershell
# Node qua fnm (đa version, nhanh hơn nvm-windows)
winget install Schniz.fnm
fnm install --lts
fnm default lts-latest

# Python qua uv (nhanh, thay pip/venv)
winget install astral-sh.uv
uv python install 3.12
uv venv
.venv\Scripts\Activate.ps1

# Rust
winget install Rustlang.Rustup
rustup default stable
```

## Defender exclusions (tăng tốc build/test rõ rệt)

Cần admin. Cẩn trọng — chỉ exclude thư mục dev cụ thể:

```powershell
Add-MpPreference -ExclusionPath "$env:USERPROFILE\code"
Add-MpPreference -ExclusionPath "$env:USERPROFILE\.cargo"
Add-MpPreference -ExclusionPath "$env:USERPROFILE\AppData\Local\pnpm"
Add-MpPreference -ExclusionProcess "node.exe","cargo.exe","pwsh.exe"
Get-MpPreference | Select-Object -ExpandProperty ExclusionPath
```

## Verify

```powershell
git --version; node --version; python --version; gh --version; pwsh --version
where.exe git node python                # kiểm tra resolve đúng PATH
```

## Common gotchas

- **`curl` mặc định là alias cho `Invoke-WebRequest`** — dùng `curl.exe` để chạy curl thật.
- **`sudo` không có sẵn** — dùng `Start-Process pwsh -Verb RunAs` hoặc `gsudo` (`scoop install gsudo`).
- **Path có dấu cách** — luôn quote `"C:\Program Files\..."`.
- **CRLF** — Git Windows mặc định convert; với repo cross-platform set `core.autocrlf input` và thêm `.gitattributes` với `* text=auto eol=lf`.
- **Execution Policy chặn script** — `Unblock-File <script.ps1>` cho file tải về, hoặc `-ExecutionPolicy Bypass` cho lần chạy đơn lẻ.
- **Env vars không "ngấm" sang process con** — chỉ session đang chạy thấy `$env:VAR`; mở terminal mới để dùng giá trị từ User/Machine scope vừa set.
