--- name: windows-shell description: Run shell commands correctly on Windows for coding agents. Use when executing terminal commands, writing scripts, choosing pwsh vs cmd vs bash, fixing failed shell invocations, or when the OS is Windows. Covers PowerShell 7, Windows PowerShell 5.1, cmd.exe, Git Bash pitfalls, chaining, exit codes, and encoding. license: MIT metadata: author: javeckbila version: "1.0.0" platform: windows --- # Windows shell for coding agents ## Default shell choice | Prefer | When | |--------|------| | **PowerShell 7+ (`pwsh`)** | Default for automation, scripts, JSON, pipelines | | **Windows PowerShell 5.1 (`powershell`)** | Only if `pwsh` is missing; note syntax gaps | | **cmd.exe** | Legacy build tools that require it; keep one-liners short | | **Git Bash / WSL bash** | Only when the project docs require Unix tools, or user is in WSL | **Do not assume bash.** On Windows agent sandboxes, `&&`, `export`, `rm -rf`, and POSIX paths often fail or behave differently. ## Invocation patterns ### Detect environment (run once per session) ```powershell $PSVersionTable.PSVersion [System.Environment]::OSVersion.VersionString Get-Command pwsh, powershell, git, node, python -ErrorAction SilentlyContinue | Select-Object Name, Source ``` ### Prefer PowerShell native over Unix clones | Wrong (Unix habit) | Right (Windows) | |--------------------|-----------------| | `ls -la` as primary API | `Get-ChildItem -Force` (alias `ls` is fine, but write PS for scripts) | | `cat file \| grep x` | `Select-String -Path file -Pattern x` or `Get-Content file \| Select-String x` | | `export FOO=bar` | `$env:FOO = 'bar'` | | `which node` | `Get-Command node` | | `rm -rf build` | `Remove-Item -Recurse -Force build` (confirm path first) | | `cp -r a b` | `Copy-Item -Recurse a b` | | `mkdir -p a/b` | `New-Item -ItemType Directory -Force -Path a\b` | | `cmd1 && cmd2` in PS 5.1 | `cmd1; if ($?) { cmd2 }` or use `pwsh` where `&&` works (PS 7+) | | `$(command)` subshell | `$(command)` works in PS, but prefer assignment: `$x = command` | ### Chaining and exit codes ```powershell # PowerShell 7+ git status && git diff # Compatible with 5.1 + 7 git status; if ($LASTEXITCODE -ne 0) { throw "git status failed" } # Capture output + exit code $output = & git rev-parse --is-inside-work-tree 2>&1 $code = $LASTEXITCODE ``` **Note:** Native PowerShell cmdlets set `$?` but often leave `$LASTEXITCODE` untouched. External programs (git, npm, cargo) set `$LASTEXITCODE`. ### Quoting rules (critical) ```powershell # Paths with spaces — use -LiteralPath or quoted strings Set-Location -LiteralPath 'C:\Users\Ada Smith\project' # Arguments with spaces & 'C:\Program Files\Git\bin\git.exe' -C 'C:\work\my repo' status # Do NOT rely on single-quote escaping like bash # Inside double quotes, expand variables: "Hello $name" # Inside single quotes, literal: 'Hello $name' ``` ### Encoding and output - Prefer UTF-8 for new files. When writing files from the agent: ```powershell $utf8NoBom = New-Object System.Text.UTF8Encoding $false [System.IO.File]::WriteAllText($path, $content, $utf8NoBom) ``` - If console output is garbled (Polish/Chinese paths, etc.): ```powershell [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 $OutputEncoding = [Console]::OutputEncoding ``` ### Long-running and interactive tools - Avoid interactive prompts (`Read-Host`, `npm` questions). Pass flags (`-y`, `--yes`, `/S`). - For tools that open a pager (`git log`, `git diff`), set: ```powershell $env:GIT_PAGER = 'cat' # or git --no-pager diff ``` ## cmd.exe only when required ```cmd cmd /c "dir /b && echo ok" ``` Keep cmd strings simple. Escape carefully; prefer PowerShell wrappers. ## Safety defaults 1. Never run destructive recursive deletes without verifying the resolved path (`Resolve-Path`). 2. Prefer project-scoped commands over modifying user PATH or registry. 3. Do not disable ExecutionPolicy globally; use: ```powershell pwsh -NoProfile -ExecutionPolicy Bypass -File .\scripts\build.ps1 ``` 4. Prefer `-WhatIf` / dry-run flags when available. ## When a command fails 1. Re-run with PowerShell explicit call operator: `& tool arg1 arg2` 2. Check `Get-Command tool` — not installed vs wrong PATH 3. Check if the project expects **WSL** (Linux paths, Docker-from-WSL, bash scripts) 4. Read the error: `CommandNotFoundException` ≠ bash `command not found` but same meaning ## Quick decision tree ``` Need to run something on Windows? ├─ Project has *.sh only and user uses WSL? → wsl-interop skill / run in WSL ├─ One-off file/git/npm/dotnet? → pwsh native or tool CLI ├─ Complex pipeline / JSON / objects? → PowerShell └─ Legacy .bat/.cmd build? → cmd /c, keep minimal ```