--- name: execution-pattern description: Always write PowerShell to .ps1 files and run with -NoProfile -File, never -Command inline applies-to: powershell, all --- # PowerShell Execution Pattern (Windows) ## Rule: Always use -NoProfile -File Never use inline PowerShell commands (`-Command` or `-c`). Always write scripts to `.ps1` files and execute with `-NoProfile -File`. ## Rationale - **Prevents font/codepage changes**: PowerShell profile scripts often set `chcp 65001` or modify `[Console]::OutputEncoding`, which changes the Claude Code CLI font and breaks rendering. `-NoProfile` skips all profile scripts. - **Avoids Git Bash quoting issues**: Inline commands have unpredictable quote escaping and variable expansion (`$_`, `$foo`) before PowerShell sees them. What you write is not what PowerShell receives. - **Enforced by hooks**: `.claude/hooks/pre-bash-pwsh-script.sh` blocks inline execution and rejects the command before it runs. ## Correct pattern ```bash # Write script to file using the Write tool # (Write tool creates the file; Bash tool executes it) # Execute with -NoProfile -File pwsh -NoProfile -File /tmp/script.ps1 ``` Or using a temp file in the claudetools tmp directory (safe from /tmp path mismatch): ```bash # Write to .claude/tmp/ which both Write tool and Bash agree on pwsh -NoProfile -File D:/claudetools/.claude/tmp/script.ps1 ``` ## Incorrect (BLOCKED BY HOOKS) ```bash # These will be rejected by the pre-bash-pwsh-script.sh hook powershell -Command "Get-Process" pwsh -c "Get-Date" powershell.exe -Command '$x = 5; Write-Host $x' powershell.exe -Command "Get-Service GuruRMMAgent" ``` ## Hook enforcement The hook at `.claude/hooks/pre-bash-pwsh-script.sh` intercepts any `Bash` tool call and checks for the patterns `powershell.*-Command`, `powershell.*-c`, `pwsh.*-c`, `pwsh.*-Command`. If matched, the hook returns a non-zero exit code and the command is rejected. The hook extracts only the `tool_input.command` field via `jq` before grepping — this prevents false positives from grep matching echo arguments or heredoc content. ## Note on /tmp path ambiguity on Windows On Windows, `/tmp` resolves differently in Git Bash vs. the Write tool. Git Bash maps `/tmp` to `%LOCALAPPDATA%\Temp\`, while the Write tool may create files in `C:\tmp\`. This mismatch has caused wrong-content file reads in past sessions. Use `D:/claudetools/.claude/tmp/` as the temp directory for any file that needs to be read back by a Bash command. ## Reference See `.claude/hooks/pre-bash-pwsh-script.sh` for enforcement details.