Summary
Opening a terminal in wmux — most visibly via a quick-launch profile — produces a broken first prompt when the shell uses oh-my-posh (PowerShell + PSReadLine). Three symptoms, all rooted in how terminal queries/responses and sizing are handled across wmux's multi-process (renderer ⇄ main ⇄ pty) architecture.
Environment
- Shell: PowerShell (
pwsh) with oh-my-posh prompt + PSReadLine
- wmux terminal: xterm.js (renderer) ⇄ Electron main ⇄ node-pty/ConPTY
- Repro: open a quick-launch profile (even one that only sets a
cwd), or any new terminal tab
Symptoms
-
Garbled characters on the prompt. A stray escape sequence appears as if typed:
Typing cls to clear it yields 62;4;9;22ccls — the junk and the typed command merge into one executed line.
-
Doubled prompt. The oh-my-posh prompt renders twice on startup.
-
Slow / stalled first prompt. The terminal takes several seconds before the prompt appears.
Root causes
-
DA1 (Device Attributes) response leak. oh-my-posh / PSReadLine probe the terminal with a DA1 query (ESC [ c). The xterm image addon answers it with ESC [ ? 62 ; 4 ; 9 ; 22 c (62=VT220, 4=Sixel, 9, 22=ANSI color). That reply travels a slow renderer→main→pty round-trip and arrives after the prompt is drawn, so PSReadLine echoes it as typed input → the [?62;4;9;22c junk.
-
Doubled prompt = two shells per tab. React StrictMode double-mounts the terminal component in dev; the renderer's pty.has() check is async, so pty.create() could fire twice for one surfaceId, spawning a second PowerShell process that also streamed to the renderer (and leaked an orphan). Separately, spawning the PTY at the default 80×24 and then resizing it made the shell redraw the prompt.
-
Slow first prompt. The shell-integration script started the PR-polling background job with Start-Job during init — Start-Job spins up a child PowerShell runspace and costs several hundred ms on the startup critical path.
Impact
Every new terminal/profile under oh-my-posh starts dirty: an executed garbage command, a duplicated prompt, and a multi-second delay. Diagnosed with an opt-in PTY I/O trace that showed the duplicate shell-init sequences and the DA round-trip timing.
Proposed fix
- Answer DA1 in-process in
PtyManager (instant) and suppress xterm's own (slow) DA1 reply.
- Make
PtyManager.create() idempotent per surfaceId; skip re-wiring a reused PTY in the IPC handler.
- Spawn PTYs at the measured size; drop redundant same-size resizes.
- Defer the PR
Start-Job to the shell's first idle.
- Bake quick-launch startup commands into shell init for PowerShell instead of injecting keystrokes.
- Guard async terminal callbacks against use-after-dispose (fixes an xterm
reading 'dimensions' crash).
Summary
Opening a terminal in wmux — most visibly via a quick-launch profile — produces a broken first prompt when the shell uses oh-my-posh (PowerShell + PSReadLine). Three symptoms, all rooted in how terminal queries/responses and sizing are handled across wmux's multi-process (renderer ⇄ main ⇄ pty) architecture.
Environment
pwsh) with oh-my-posh prompt + PSReadLinecwd), or any new terminal tabSymptoms
Garbled characters on the prompt. A stray escape sequence appears as if typed:
Typing
clsto clear it yields62;4;9;22ccls— the junk and the typed command merge into one executed line.Doubled prompt. The oh-my-posh prompt renders twice on startup.
Slow / stalled first prompt. The terminal takes several seconds before the prompt appears.
Root causes
DA1 (Device Attributes) response leak. oh-my-posh / PSReadLine probe the terminal with a DA1 query (
ESC [ c). The xterm image addon answers it withESC [ ? 62 ; 4 ; 9 ; 22 c(62=VT220, 4=Sixel, 9, 22=ANSI color). That reply travels a slow renderer→main→pty round-trip and arrives after the prompt is drawn, so PSReadLine echoes it as typed input → the[?62;4;9;22cjunk.Doubled prompt = two shells per tab. React StrictMode double-mounts the terminal component in dev; the renderer's
pty.has()check is async, sopty.create()could fire twice for onesurfaceId, spawning a second PowerShell process that also streamed to the renderer (and leaked an orphan). Separately, spawning the PTY at the default 80×24 and then resizing it made the shell redraw the prompt.Slow first prompt. The shell-integration script started the PR-polling background job with
Start-Jobduring init —Start-Jobspins up a child PowerShell runspace and costs several hundred ms on the startup critical path.Impact
Every new terminal/profile under oh-my-posh starts dirty: an executed garbage command, a duplicated prompt, and a multi-second delay. Diagnosed with an opt-in PTY I/O trace that showed the duplicate shell-init sequences and the DA round-trip timing.
Proposed fix
PtyManager(instant) and suppress xterm's own (slow) DA1 reply.PtyManager.create()idempotent persurfaceId; skip re-wiring a reused PTY in the IPC handler.Start-Jobto the shell's first idle.reading 'dimensions'crash).