MANDATORY for any gh CLI command with a --body flag: gh pr create, gh pr edit, gh issue create, gh issue edit. Prevents PowerShell encoding bugs that silently corrupt markdown (backticks become \^G, em-dashes garble). Always writes body to a temp file and uses --body-file instead. Triggers: create PR, open pull request, edit PR, create issue, edit issue, push and create PR, gh pr, gh issue.
Create PRs, edit PR bodies, and create issues using the gh CLI without markdown corruption.
PowerShell uses the backtick (`) as its escape character. When you pass markdown containing backticks via --body "...", PowerShell interprets them as escape sequences:
`n → newline`t → tab`a → BEL character (U+0007)`5 → literal 5 (backtick stripped)This causes commit SHAs like `58a3f49` to render as \58a3f49\ on GitHub, and code spans to break silently. The body looks correct in the terminal but renders incorrectly on GitHub.com.
The fix is simple: always write the body to a temp file and use --body-file.
Before creating a PR, verify the active account matches the repo:
gh auth status
_microsoft suffix) → for org repos (e.g., mcaps-microsoft/*)gh auth switch --user <account>Write the body as a PowerShell here-string. This preserves all markdown formatting including backticks, tables, code blocks, and special characters:
$body = @'
## Summary
Brief description of what this PR does and why.
## Changes
- First change with `inline code` preserved
- Second change referencing commit `abc1234`
## Testing
```bash
npm test
'@
**Body format guidelines:**
- Short summary sentence (what and why)
- Grouped bullet list of changes (use `###` subsections if 3+ categories)
- Footer: testing status, breaking changes, or migration notes if applicable
- Use markdown formatting freely — backticks, bold, tables, code blocks all work with `--body-file`
### 3. Write to Temp File
**PREFERRED: Use the `create` tool** to write the file. This completely bypasses PowerShell's string encoding pipeline and guarantees clean UTF-8:
create tool: path: <any temp path, e.g. pr-body.md in cwd or $env:TEMP> file_text: <the body content>
This is the safest method. Even `Out-File -Encoding utf8NoBOM` can mangle non-ASCII characters because PowerShell's pipeline processes the string before writing.
**FALLBACK: PowerShell here-string** (only if create tool is unavailable):
```powershell
$bodyFile = "$env:TEMP\gh-body-$(Get-Random).md"
$body | Out-File -FilePath $bodyFile -Encoding utf8NoBOM
When using the PowerShell fallback, avoid non-ASCII characters in the body text:
-- instead of em-dash or en-dash-> instead of Unicode arrowThis restriction does NOT apply when using the create tool (which writes clean UTF-8) or when the body text only contains ASCII.
If you must use Unicode with the PowerShell fallback, test the file content with Get-Content $bodyFile before passing it to gh.
Create a new PR:
gh pr create --title "<type>: <description>" --body-file $bodyFile --base main
Edit an existing PR body:
gh pr edit <number> --body-file $bodyFile
Create an issue:
gh issue create --title "<title>" --body-file $bodyFile
Edit an existing issue:
gh issue edit <number> --body-file $bodyFile
Remove-Item $bodyFile -ErrorAction SilentlyContinue
# 1. Compose body with a single-quoted here-string (no escaping needed)
$body = @'
## Summary
Add Redis caching layer to reduce database query latency.
## Changes
| File | Change |
|------|--------|
| `src/cache.ts` | New Redis cache wrapper with TTL support |
| `src/routes/users.ts` | Cache user profile queries (5min TTL) |
| `src/config.ts` | Add `REDIS_URL` environment variable |
## Testing
- All 47 existing tests pass
- Added 12 new cache tests (`src/__tests__/cache.test.ts`)
- Manual verification: profile page load time 250ms → 50ms
## Breaking Changes
None — caching is additive and falls back to direct DB on cache miss.
'@
# 2. Write to temp file
$bodyFile = "$env:TEMP\gh-body-$(Get-Random).md"
$body | Out-File -FilePath $bodyFile -Encoding utf8NoBOM
# 3. Create the PR
gh pr create --title "feat(cache): Add Redis caching for user profiles" --body-file $bodyFile --base main
# 4. Clean up
Remove-Item $bodyFile -ErrorAction SilentlyContinue
# ❌ NEVER — inline --body with backticks
gh pr create --title "fix: Bug" --body "Fixed the bug in `auth.js` by updating `refreshToken()`"
# Result on GitHub: "Fixed the bug in \auth.js\ by updating \refreshToken()\\"
# ❌ NEVER — double-quoted here-string (allows variable expansion + backtick escaping)
$body = @"
Fixed `$commitSha` issue
"@
# Result: backticks may still be interpreted, variables expanded unexpectedly
# ❌ NEVER — escaping backticks with more backticks
gh pr create --body "Fixed ``auth.js`` issue"
# Result: fragile, inconsistent across PowerShell versions
# ❌ NEVER — gh pr edit with inline --body
gh pr edit 42 --body "Updated `config.ts`"
# Same corruption as gh pr create
| Syntax | Backticks | Variables | Use When |
|---|---|---|---|
@'...'@ (single-quoted) | ✅ Literal | ❌ Not expanded | Body has no PowerShell variables |
@"..."@ (double-quoted) | ⚠️ Escaped | ✅ Expanded | Body needs $variable interpolation |
Prefer single-quoted @'...'@ — it treats everything as literal text, which is what you want for markdown. Only use @"..."@ when you need to embed PowerShell variables (and in that case, escape backticks as ``).
gh auth status)Out-File -Encoding utf8NoBOM--body-file, NOT --body<type>[scope]: <description>