---
name: windows-terminal-settings
description: Editing Windows Terminal settings.json on Windows — known gotchas and the only pattern that actually sticks. Use whenever the user wants to change Terminal settings (bell, opacity, profile, keybindings, color scheme). ALSO load when user describes a Terminal window flashing, flickering, beeping, or stealing focus — BUT 'flash' is ambiguous (render flicker vs bell flash vs focus theft), so ask what it looks like before assuming it is a Terminal setting. Includes a reusable focus-theft monitor script and diagnostic reference for identifying background processes that steal foreground window focus.
---

# Editing Windows Terminal settings.json — what actually works

## When to load this skill

Load BEFORE doing any of:
- User asks to change Terminal settings (bell, opacity, profile, keybindings, color scheme)
- User describes Terminal flashing, flickering, beeping, or making noise
- User says "my terminal window" + any visual symptom (assumes VPS first, almost always wrong)
- User says Terminal pops to foreground / steals focus / interrupts typing (focus theft — see `references/focus-theft-diagnosis.md`)
- Diagnostic on a Connie-side visual issue that didn't resolve from a server-side check

## Support files

- **`scripts/focus-theft-monitor.ps1`** — reusable PowerShell script that watches foreground window changes and new process spawns at 100ms. Supports foreground and hidden background modes. Use when diagnosing focus theft.
- **`references/focus-theft-diagnostic.md`** — detailed reference: how to confirm focus theft, how to read the monitor log, common culprits and removal, Win32 API pattern, lessons from past sessions.

## Hard rules (learned the hard way)

1. **NEVER claim an edit worked without re-reading the file and grepping for the inserted keys.** Always finish with a verification block.
2. **`Stop-Process -Force` on Windows Terminal kills every open tab and shell**, including long-running SSH sessions. **Warn the user about this UP FRONT in the very first copy-paste block**, not buried at the end. Surface side effects before shipping a destructive command — this is a recurring UX requirement.
3. **Windows Terminal rewrites `settings.json` on close AND on open.** Any edit made while Terminal is running will be silently reverted. No warning, no error — the file just snaps back.
4. **PowerShell object manipulation (`ConvertFrom-Json` + `Add-Member` + `ConvertTo-Json`) silently fails when the target object is empty** — assigning a property to an empty PSCustomObject via dot-notation no-ops in some PowerShell profiles. Use a direct text replace on the JSON file instead.
5. **The file may be a 1.5KB default template** with `"profiles": { "defaults": {}, "list": [...] }`. That's a valid state — don't assume the user's config has more than the bare minimum.
6. **Don't assume the cause is server-side.** When the user describes a Terminal/UI visual symptom, start the diagnostic on Connie (Windows) first. The VPS is unlikely to be the source of a window-flashing issue.

## The only pattern that works

```powershell
$settings = "$env:LOCALAPPDATA\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json"

# *** HEADLINE WARNING: this closes every open Terminal tab including SSH sessions ***
# Tell the user this BEFORE running the block, not after.

# 1. Close Terminal FIRST
Get-Process WindowsTerminal -ErrorAction SilentlyContinue | Stop-Process -Force
Start-Sleep -Seconds 2

# 2. Back up the current file
Copy-Item $settings "$settings.bak.$(Get-Date -Format yyyyMMdd-HHmmss)"

# 3. Direct text insert — no object manipulation
$content = Get-Content $settings -Raw
if ($content -match '"defaults"\s*:\s*\{\s*\}') {
    $content = $content -replace '"defaults"\s*:\s*\{\s*\}', '"defaults": { "bellStyle": "none", "useAcrylic": false }'
} elseif ($content -match '"defaults"\s*:\s*\{') {
    $content = $content -replace '("defaults"\s*:\s*\{)', '$1 "bellStyle": "none", "useAcrylic": false,'
}
Set-Content $settings $content -Encoding UTF8

# 4. MANDATORY verification — don't claim success without this
$verify = Get-Content $settings -Raw
if ($verify -match '"bellStyle"\s*:\s*"none"') { Write-Host "OK bellStyle=none" } else { Write-Host "FAIL bellStyle missing" }
if ($verify -match '"useAcrylic"\s*:\s*false') { Write-Host "OK useAcrylic=false" } else { Write-Host "FAIL useAcrylic missing" }
Get-Item $settings | Format-List Length, LastWriteTime

# 5. Restart Terminal
Start-Process "wt.exe"
```

## Why direct text replace beats ConvertFrom-Json/ConvertTo-Json

- `ConvertTo-Json -Depth 10` reorders keys alphabetically and may drop unknown schema fields, which Terminal then rejects on next launch.
- `Add-Member` against `[pscustomobject]@{}` works, but `$obj.newKey = value` on an object that doesn't have that property yet silently no-ops in some PowerShell profiles.
- Text replace is dumb but deterministic and reversible.

## Verification checklist (always do this)

```powershell
Get-Item $settings | Format-List Length, LastWriteTime
(Get-Content $settings -Raw) | Select-String -Pattern '"bellStyle"|"useAcrylic"' -SimpleMatch
```

If LastWriteTime didn't change, the write failed. If the keys aren't in the file, the replace didn't match (usually because the `defaults` block had a different shape than expected).

## Diagnostic for "my Terminal window flashes"

**CRITICAL: "Flash" is ambiguous. Ask the user what it looks like BEFORE assuming a cause.**

Three different symptoms get called "flashing":

1. **Render flicker** — the window content flickers/jitters but stays in place. Cause: Terminal's acrylic/repaint. Fix: `useAcrylic: false`.
2. **Bell flash** — the window border/taskbar flashes briefly. Cause: escape sequence bell (`\a`). Fix: `bellStyle: "none"`.
3. **Focus theft** — a window pops to the foreground, you can't type for a moment, then it disappears. Cause: a background process spawning a visible console window (e.g. `tasklist.exe`, `conhost.exe`). **This is NOT a Terminal settings issue.** Fix: find and remove the offending process. On Connie (July 2026), the real culprit was a **local Hermes bot gateway** (`pythonw.exe -m hermes_cli.main gateway run`) launched by `Hermes_Gateway.vbs` in the Windows Startup folder — it periodically spawned `tasklist.exe` which created a console window that stole focus. Killed the process + removed the VBS from Startup. (McAfee WebAdvisor was also uninstalled as a suspect but was NOT the actual cause.)

**Do NOT assume cause #1 or #2 without asking what the flash looks like.** The first misdiagnosis (chasing bellStyle/useAcrylic for a whole session) cost hours. The second misdiagnosis (blaming McAfee) was a lucky guess that happened to clean up bloatware but didn't fix the flash. The actual root cause was found by running a 5-minute background focus-theft monitor that logged foreground window changes + new process spawns with full paths, matching the user's "just flashed" timestamp to the log, and identifying `pythonw.exe → tasklist.exe → conhost.exe` as the focus-stealing chain.

**The reliable diagnostic for cause #3:** Run a background PowerShell monitor (100ms polling, hidden window, 15 min) that logs every FOCUS_CHANGE (with PID + process name + window title) and every NEW_PROCESS (with full executable path + command line). Match the user's flash timestamp to the log. The chain will be: a new process spawns → its console window grabs foreground → it exits → focus returns.c for a whole session) cost hours. The second misdiagnosis (blaming McAfee) was a lucky guess that happened to clean up bloatware but didn't fix the flash. The actual root cause was found by running a 5-minute background focus-theft monitor that logged foreground window changes + new process spawns with full paths, matching the user's "just flashed" timestamp to the log, and identifying `pythonw.exe → tasklist.exe → conhost.exe` as the focus-stealing chain.

**The reliable diagnostic for cause #3:** Run a background PowerShell monitor (100ms polling, hidden window, 15 min) that logs every FOCUS_CHANGE (with PID + process name + window title) and every NEW_PROCESS (with full executable path + command line). Match the user's flash timestamp to the log. The chain will be: a new process spawns → its console window grabs foreground → it exits → focus returns.

### If it's render flicker or bell flash (causes #1 or #2):

Apply the settings block above. **Do not start by checking cron jobs, systemd timers, or running processes on the VPS** unless the user explicitly says the flash is server-side. Window visual symptoms live on Connie.

## Common failure modes

| Symptom | Cause | Fix |
|---------|-------|-----|
| LastWriteTime updates but `bellStyle` not in file | Text replace didn't match — `defaults` block has a different shape | Read the file and adjust the regex |
| LastWriteTime doesn't update at all | `Stop-Process` killed the parent PowerShell that was running the script before the write completed | Run from a separate window, or split into two scripts |
| Keys appear but Terminal rewrites them on next launch | Terminal ran while the script was still executing and overwrote our edit | Make sure Terminal is fully stopped before editing (`Start-Sleep -Seconds 2` after `Stop-Process`) |
| File grows by exactly the same amount every run | Script is appending, not replacing | Make sure you `Set-Content` (overwrite), not `Add-Content` (append) |
| `bellStyle = none` set but flash persists | Not the bell — it's the Electron renderer of the app (Hermes Desktop, Agentic OS, etc.) | Minimize the offending window for 60s to confirm; apply `useAcrylic: false` too |
| 1.5KB file with no `bellStyle` key anywhere | Default fresh-install template, never customized | Edit is fine; the empty `"defaults": {}` block is the insertion point |