--- name: powershell-for-agents description: Write correct, safe PowerShell for AI coding agents on Windows. Use when authoring .ps1 scripts, one-liners, installers, CI steps, modules, error handling, JSON parsing, REST calls, or replacing bash scripts with PowerShell. license: MIT metadata: author: javeckbila version: "1.0.0" platform: windows --- # PowerShell for coding agents ## Target edition - **Primary:** PowerShell 7+ (`pwsh`) — cross-platform, modern syntax - **Fallback:** Windows PowerShell 5.1 — still default on many corporate images At the top of scripts: ```powershell #Requires -Version 5.1 $ErrorActionPreference = 'Stop' ``` ## Script skeleton ```powershell #Requires -Version 5.1 [CmdletBinding()] param( [Parameter(Mandatory = $false)] [string]$Path = '.' ) $ErrorActionPreference = 'Stop' Set-StrictMode -Version Latest function Write-Info([string]$Message) { Write-Host "[info] $Message" } try { $resolved = Resolve-Path -LiteralPath $Path Write-Info "Working in $resolved" # ... } catch { Write-Error $_ exit 1 } ``` ## Objects > text PowerShell pipelines pass **objects**, not lines of text. ```powershell # Good Get-Process | Where-Object CPU -gt 100 | Select-Object Name, Id, CPU # Fragile Get-Process | Out-String | Select-String '...' ``` ## Common agent tasks ### JSON ```powershell $data = Get-Content -Raw -Path config.json | ConvertFrom-Json $data | ConvertTo-Json -Depth 10 | Set-Content -Path out.json -Encoding utf8 ``` ### HTTP ```powershell # PS 7+ Invoke-RestMethod -Uri 'https://api.example.com/v1' -Method Get # With headers $headers = @{ Authorization = "Bearer $env:API_TOKEN" } Invoke-RestMethod -Uri $url -Headers $headers ``` ### Find files ```powershell Get-ChildItem -Path . -Recurse -Filter *.cs -File -ErrorAction SilentlyContinue | Select-Object -ExpandProperty FullName ``` ### Search file contents ```powershell Select-String -Path '.\src\**\*.ts' -Pattern 'TODO' -SimpleMatch -ErrorAction SilentlyContinue # Or recursive: Get-ChildItem -Recurse -Include *.ts,*.tsx | Select-String -Pattern 'TODO' ``` ### Environment variables ```powershell $env:PATH -split ';' $env:MY_VAR = 'value' # process-scoped [Environment]::SetEnvironmentVariable('MY_VAR', 'value', 'User') # persistent — ask first ``` ## Parameters and splatting ```powershell $params = @{ LiteralPath = $path Recurse = $true Force = $true } Get-ChildItem @params ``` ## Error handling patterns ```powershell $ErrorActionPreference = 'Stop' try { & npm test if ($LASTEXITCODE -ne 0) { throw "npm test failed with $LASTEXITCODE" } } catch { Write-Host "FAILED: $($_.Exception.Message)" -ForegroundColor Red exit 1 } ``` ## Arrays, hashtables, null ```powershell $list = @('a', 'b') $list += 'c' # creates new array — fine for small lists $map = @{ Name = 'app'; Port = 3000 } if ($null -eq $value) { ... } # put $null on the left ``` ## String pitfalls ```powershell # Expansion "Build $version" # Literal 'Build $version' # Here-string @' line1 line2 '@ ``` ## Execution policy (do not fight the machine) Prefer bypassing for a **single** invocation: ```powershell pwsh -NoProfile -ExecutionPolicy Bypass -File .\scripts\install.ps1 ``` Avoid changing user/machine ExecutionPolicy unless the user asks. ## Replace bash idioms | Bash | PowerShell | |------|------------| | `set -e` | `$ErrorActionPreference = 'Stop'` | | `$@` | `$args` / declared `param()` | | `$(pwd)` | `Get-Location` / `$PWD` | | `dirname $0` | `$PSScriptRoot` | | `grep -R` | `Select-String -Recurse` / `Get-ChildItem \| Select-String` | | `sed` | `-replace` operator | | `curl` | `Invoke-WebRequest` / `Invoke-RestMethod` (or real `curl.exe`) | | `jq` | `ConvertFrom-Json` / `ConvertTo-Json` | **Note:** On Windows, `curl` may be an alias to `Invoke-WebRequest`. Use `curl.exe` for real curl. ## Testing a script quickly ```powershell pwsh -NoProfile -File .\scripts\tool.ps1 -WhatIf pwsh -NoProfile -Command "& { . .\scripts\tool.ps1 }" ``` ## Style for agent-generated scripts 1. Explicit param blocks 2. `$ErrorActionPreference = 'Stop'` 3. `-LiteralPath` for user-provided paths 4. ASCII-first unless the repo is clearly Unicode-heavy 5. No interactive prompts 6. Idempotent installers (`Test-Path` then create)