You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When get_foreground_process_name returns None (no descendants found), the format layer substitutes the placeholder string "shell" rather than looking up the pane shell PID's own executable basename.
For an idle pwsh, pwsh is the only process attached to the TTY, so tcgetpgrp returns pwsh's pgrp, /proc/<pid>/cmdline returns pwsh, and pane_current_command is pwsh. tmux never has to substitute a placeholder.
2. Active programs report a deep descendant, not the foreground process
Reproducer:
psmux new-session-d -s repro
# Launch a long-running node child (clean process tree)
psmux send-keys-t 'repro:1''node -e "setInterval(()=>{},1000)"' Enter
Start-Sleep-Seconds 6
psmux list-panes -t 'repro:1'-F'#{pane_current_command}'
Observed for node: node ✓ (works in this clean case)
Now try a program that spawns a subprocess tree:
psmux send-keys-t 'repro:1''copilot' Enter
Start-Sleep-Seconds 10
psmux list-panes -t 'repro:1'-F'#{pane_current_command}'
Observed: cmd (or aspire, dotnet, depending on which descendant was deepest at sample time)
Expected (matching tmux): copilot
The OS process tree at the moment of measurement showed pwsh.exe with copilot.exe as its only direct child. There was no cmd.exe visible as a child of either at that instant. But pane_current_command returned cmd, suggesting the tree traversal caught a transient cmd.exe descendant.
The implementation in src/platform.rs:
/// Strategy: BFS all descendants, then pick the deepest non-system leaf./// When multiple candidates exist at the same depth, prefer the largest/// PID (heuristic for "most recently created").fnfind_foreground_child_pid(root_pid:u32) -> Option<u32>{ ...}
The strategy is deliberately "deepest non-system leaf." That works for trivial cases (node with no children, bash -c 'foo' where foo is the meaningful thing), but breaks for any program that spawns its own subprocess hierarchy as part of normal operation:
copilot spawns cmd / aspire / agency / dotnet
nvim with an LSP server has the language server as a deeper leaf
Anything with worker processes (pm2, parcel, webpack dev servers, etc.)
Worse, the result is non-deterministic across samples because the deepest leaf changes as transient subprocesses come and go.
tmux's mechanism is precise rather than heuristic: tcgetpgrp(fd) returns the single process group that owns the TTY's foreground (typically the immediate child of the shell that called tcsetpgrp on startup). Subprocesses inherit the parent's process group but do NOT become the foreground unless they explicitly call tcsetpgrp. So pane_current_command reliably returns whichever program took TTY control, regardless of how many subprocesses it spawns underneath.
There is no equivalent primitive on Windows. ConPTY does not expose foreground process group; GetConsoleProcessList is explicitly deprecated by MSDN:
This API is not recommended and does not have a virtual terminal equivalent. This decision intentionally aligns the Windows platform with other operating systems.
Microsoft has stopped extending Win32 console for this category of question precisely because they want VT-sequence-based shell↔terminal coordination going forward. The path forward, then, is the same one the modern terminal ecosystem has converged on: parse what the shell volunteers via OSC sequences, fall back to a process-tree heuristic when no integration is present.
Why this matters
pane_current_command is the canonical "what's running here" identifier across the broader psmux plugin ecosystem, and the divergence breaks features that depend on it being predictable.
Blocks psmux/psmux-plugins#22 (per-program restore strategies for psmux-resurrect, accepted in principle, prototype ready). That proposal keys strategies on pane_current_command e.g., @resurrect-strategy-copilot 'project' fires whenever a saved pane reports copilot as its command. With the current heuristic, a real copilot pane saves as cmd / aspire / dotnet / whichever descendant won the race, rarely copilot. The strategy mechanism's matching key is fundamentally non-deterministic, so no user-written strategy can ever reliably fire. Aligning pane_current_command with tmux's effective behavior is the precondition for it's good , but exit of last window(fast if not last) is quite slow #22 to be usable.
Window auto-rename picks misleading names; windows running copilot get renamed to cmd or dotnet or aspire depending on which descendant was foreground at sample time, instead of the program the user actually launched.
Status-bar #{pane_current_command} substitution displays the same misleading name in the status line, with the additional pathology that the displayed name changes spontaneously as transient subprocesses come and go.
Window titles via automatic-rename-format inherit the same problem.
How the modern terminal ecosystem solves this
I surveyed how every relevant tool in the multiplexer / terminal / IDE space handles "what command is running in this session." Three emit patterns have emerged for carrying the command name:
Pattern
Emitter
Sequence
Notes
A
Kitty (bash/zsh)
\e]133;C;cmdline=<%q-quoted cmd>\a
Extends FinalTerm OSC 133;C with a cmdline= parameter. Co-located with the state marker; backward-compatible (terminals that don't parse the param see bare 133;C).
A'
Kitty (fish)
\e]133;C;cmdline_url=<url-escaped cmd>\a
Same shape, URL-escaping for fish's quoting style.
B
WezTerm (wezterm.sh, bash/zsh)
\e]1337;SetUserVar=WEZTERM_PROG=<b64>\a
Piggybacks on iTerm2's OSC 1337 user-vars mechanism with a namespaced var.
C
VS Code (shellIntegration.ps1)
\e]633;E;<escaped cmd>;<nonce>\a
Invents a new OSC number entirely. VS-Code-only — no other emitter, no other consumer.
Pattern A is the cleanest design: no new OSC number, no namespace squatting, no separate sequence-ordering issues, smallest byte overhead. It's published in kitty's shell-integration docs and emitted verbatim by kitty's bash/zsh/fish integration scripts (see kitty.bash — printf "\e]133;C;cmdline=%q\a" "$last_cmd").
Pattern B has user-base value: wezterm.sh is widely sourced and emits WEZTERM_PROG from a preexec hook today. Parsing it gives psmux instant compat with that user base — without psmux defining its own var name (which would have the same namespace concern WezTerm's name has).
Pattern C has narrower value: VS Code-script users get the rich path, nobody else does.
Other tools in the survey:
iTerm2 emits OSC 133;C (no parameter) and OSC 1337;SetUserVar=... (generic mechanism) but does NOT emit a "current command" var — they derive command identity from tcgetpgrp (Mac-only). They invented the SetUserVar mechanism but didn't standardize a command var; WezTerm did that downstream.
Ghostty emits OSC 133;C\a (no parameter) and copies kitty's kitty-shell-cwd:// URL scheme for OSC 7. Gets command identity from its own scrollback parsing.
Starship, oh-my-posh, powerlevel10k emit state markers (OSC 7, OSC 133 A/B/C/D) when configured but don't emit command-carrying sequences today. OMP is the closest fit for pwsh — it already wires Set-PSReadLineKeyHandler -Key Enter to emit OSC 133;C when shell_integration: true is enabled. Adding cmdline_url= to that existing emission is a ~4-line patch I plan to upstream as a separate FR.
The legacy approach — tcgetpgrp + /proc/<pid>/cmdline walking — is what tmux and screen do. It works on Unix, breaks on Windows (this issue), and is brittle even on Unix once wrappers nest deeply. Every modern tool has chosen shell-integration-via-escapes instead.
Two more OSC sequences are independent of command identity but compose with it
OSC
Origin
Carries
Used by
OSC 7
dtterm/xterm
cwd as file://HOST/PATH
Universal: Windows Terminal, WezTerm, iTerm2, VS Code, Konsole, gnome-terminal, Kitty, Alacritty, ghostty (with kitty-shell-cwd:// flavor)
OSC 133 A/B/D
FinalTerm (FTCS)
prompt/output/done state markers + exit code
Broad: Windows Terminal, WezTerm, Alacritty, Kitty, iTerm2, VS Code, ghostty
OSC 7 is the right primitive for per-pane cwd. OSC 133 A/B/D are state markers — they let psmux know when the shell is idle vs. running a command. Both compose cleanly with the command-identity layer and unlock significant value beyond just pane_current_command:
psmux-resurrect cwd-aware restore comes for free once OSC 7 is parsed per-pane.
monitor-activity could finally distinguish "command is running" from "command finished," replacing the current "any byte arrived" heuristic that's nearly unusable for streaming AI tools.
Empirical verification
I wrote a minimal pwsh shell-integration emitter following kitty's pattern (~25 lines, no VS Code dependency) and captured what reaches the terminal. Annotated bytes (\e = ESC, \a = BEL):
The emission is via Set-PSReadLineKeyHandler -Key Enter wrapping AcceptLine: capture the command from PSReadLine's buffer state BEFORE AcceptLine, call AcceptLine, then emit OSC 133;C;cmdline_url=.... The same hook OMP uses for its existing OSC 133;C emission, just extended with the kitty parameter.
Proposed resolution: a layered cascade
Replace the original Option A / Option B framing with a layered cascade. Higher layers are more authoritative; psmux falls through when the higher ones produce no signal. Layers 1-3 are shell-integration sources; layers 4-5 are the existing heuristic, improved.
Command identity (what is running in this pane), ordered from most preferred to fallback:
Priority
Layer
Source
Notes
1
OSC 133;C;cmdline= / cmdline_url=
kitty's published pattern
Primary. Co-located with the state marker. No namespace concern.
Opportunistic compat. psmux consumes WezTerm's namespaced var — doesn't squat on it. Zero work for existing wezterm.sh users.
3
OSC 633 ; E ; <command>
VS Code's shellIntegration.ps1
Last-resort. Same data as 1/2 when present; recognized so VS Code-script users get the rich path.
4
Immediate-child heuristic
process tree
Replaces today's deepest-leaf strategy. Matches tmux's effective behavior in the common case (program-launched-from-shell, where the program took TTY foreground and subprocesses inherit its group).
5
Shell binary basename
pane shell PID
Fallback when no descendants exist (idle shell case from divergence #1). Replaces the literal "shell" placeholder.
Layer 4 includes a brief skip-list mechanism for known wrappers (npx, cmd /c, bash -c) where the meaningful thing is one level deeper; for those specific cases the heuristic falls through to the next child.
What the process-tree fix (layers 4 + 5) actually does
These two layers stand alone — they're the original Option A from this issue, re-cast as the fallback in the cascade. If the OSC work doesn't ship, just landing layers 4 + 5 closes the most painful symptom (the copilot case) for everyone, no shell integration required.
Concretely:
Layer 4: find_foreground_child_pid (in src/platform.rs) changes from "deepest non-system leaf" to "immediate non-system child" of the pane shell PID. This matches tmux's effective behavior in the common case: a program launched from the shell takes TTY foreground via tcsetpgrp on startup; any subprocesses it spawns inherit its process group and don't become foreground unless they themselves call tcsetpgrp. So picking the immediate child is correct for copilot, nvim (with or without LSP children), pm2, dev servers, and anything else that fans out workers under a single foreground process.
Tradeoff: for shell-style wrappers (bash -c 'foo', cmd /c 'foo', npx <tool>), the meaningful program is one level deeper than the immediate child. The skip-list above handles the named common cases; users running other wrappers fall back to layer 5 or shell-integration if enabled.
Layer 5: format-layer fix (src/format.rs line 1144). When find_foreground_child_pid returns None, instead of substituting the literal "shell" placeholder, look up the pane shell PID's own executable basename. Idle pwsh sessions report pwsh, idle bash sessions report bash, matching tmux.
Both are small, surgical changes — no new options, no new API surface — and together they close divergences #1 and #2 even without any OSC parsing. The OSC layers (1-3) are strictly additive on top.
Independent tracks (always parsed when emitted, surfaced as separate format tokens):
"command is running" state + exit code. Foundation for a better monitor-activity.
State machine for the integrated layers:
state = Idle
on OSC 133;A → state = Idle
on OSC 1337;SetUserVar=WEZTERM_PROG=<b64> → pending_cmd = b64_decode(<b64>); source = "wezterm"
on OSC 633;E;<cmd>[;<nonce>] → if pending_cmd.is_none(): pending_cmd = <cmd>; source = "vscode"
on OSC 133;C[;cmdline=<q-quoted>] → state = Running(cmd_from_param or pending_cmd, source); pending_cmd = None
on OSC 133;C[;cmdline_url=<url-escaped>] → state = Running(cmd_from_param or pending_cmd, source); pending_cmd = None
on OSC 133;C → state = Running(pending_cmd, source); pending_cmd = None
on OSC 133;D[;<exit>] → state = Idle
on OSC 7;file://HOST/<path> → cwd = <path> (independent track, always)
(Note: 133;C's optional cmdline= / cmdline_url= parameter takes precedence over any pending_cmd from a separate sequence — it's co-located with the state transition, no ordering ambiguity.)
pane_current_command resolution at query time:
Running(Some(cmd), _) → return cmd
Running(None, _) → fall through to layer 4 (heuristic)
Idle → fall through to layer 5 (shell basename)
No integration state at all → fall through to layer 4
Why this shape
No vendor protocol from psmux. Every parsed sequence already exists in the wild; psmux is strictly a consumer.
No psmux-defined SetUserVar variable name. The kitty pattern sidesteps the namespace question entirely — cmdline= is a generic parameter on a generic state marker.
Degrades gracefully. Users without any shell integration get layers 4-5 — same behavior as a process-tree-only fix.
Same parser, three pieces of metadata. Command, cwd, and state markers all flow through one OSC parser hooked into the existing PTY output handling. Small one-time cost; multiple downstream wins.
Plugins benefit transitively. psmux-resurrect (see psmux-plugins#22) gets richer save data: command literal + cwd, instead of inferred process name. The resurrect-strategy contract could expose provenance ("shell told us authoritatively" vs. "we guessed") so plugins can adapt.
Considered alternatives
GetConsoleProcessList() + heuristics about console allocation order. Same FIFO attach order we already get from the process tree, no additional information. Microsoft's docs explicitly recommend against this API for foreground detection.
A psmux-namespaced OSC 1337 SetUserVar variable (e.g., PSMUX_CMD or cmd). Considered and rejected — puts the same namespace burden on every future consumer that the WezTerm-naming concern raises. The kitty parameter pattern avoids this.
A pluggable script-based strategy system.pane_current_command is read on every status bar refresh, potentially many times per second across all panes. The latency budget is microseconds; spawning a PowerShell process per query is two orders of magnitude too slow.
Job objects. Group processes but don't have TTY semantics; doesn't tell us "which one has foreground."
Leave it alone and patch around it in plugins. Pushes the workaround into every plugin that consumes the variable, and the format-string layer (#{pane_current_command} in status bars) still produces misleading output. Better fixed at the source.
Enabling shell integration in practice (downstream docs concern)
Setups that provide the integrated data psmux would parse:
For pwsh users without OMP. Becomes unnecessary once OMP ships the FR.
OMP FR shape (will cross-link once filed): in OMP's existing New-EnterKeyHandler (which today emits bare OSC 133;C for _ompFTCSMarks), capture the command via [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState before calling AcceptLine, then emit OSC 133;C;cmdline_url=<url-escaped> instead. Backward compatible (terminals that don't parse the parameter see bare 133;C), aligned with kitty's published spec, gated by the existing shell_integration flag.
psmux itself should not ship an integration script — document the opt-in; point at kitty / OMP / WezTerm as the upstream sources.
pane_current_commandappears to give different answers from tmux for the same scenario.1. Idle shells report as
shell, not the shell binary nameReproducer (PowerShell):
Observed:
shellExpected (matching tmux):
pwshThe literal string
"shell"comes fromsrc/format.rs:When
get_foreground_process_namereturnsNone(no descendants found), the format layer substitutes the placeholder string"shell"rather than looking up the pane shell PID's own executable basename.For comparison, tmux's
osdep-linux.c:For an idle pwsh, pwsh is the only process attached to the TTY, so
tcgetpgrpreturns pwsh's pgrp,/proc/<pid>/cmdlinereturnspwsh, andpane_current_commandispwsh. tmux never has to substitute a placeholder.2. Active programs report a deep descendant, not the foreground process
Reproducer:
Observed for
node:node✓ (works in this clean case)Now try a program that spawns a subprocess tree:
Observed:
cmd(oraspire,dotnet, depending on which descendant was deepest at sample time)Expected (matching tmux):
copilotThe OS process tree at the moment of measurement showed pwsh.exe with copilot.exe as its only direct child. There was no
cmd.exevisible as a child of either at that instant. Butpane_current_commandreturnedcmd, suggesting the tree traversal caught a transient cmd.exe descendant.The implementation in
src/platform.rs:The strategy is deliberately "deepest non-system leaf." That works for trivial cases (
nodewith no children,bash -c 'foo'where foo is the meaningful thing), but breaks for any program that spawns its own subprocess hierarchy as part of normal operation:copilotspawns cmd / aspire / agency / dotnetnvimwith an LSP server has the language server as a deeper leafWorse, the result is non-deterministic across samples because the deepest leaf changes as transient subprocesses come and go.
tmux's mechanism is precise rather than heuristic:
tcgetpgrp(fd)returns the single process group that owns the TTY's foreground (typically the immediate child of the shell that calledtcsetpgrpon startup). Subprocesses inherit the parent's process group but do NOT become the foreground unless they explicitly calltcsetpgrp. Sopane_current_commandreliably returns whichever program took TTY control, regardless of how many subprocesses it spawns underneath.There is no equivalent primitive on Windows. ConPTY does not expose foreground process group;
GetConsoleProcessListis explicitly deprecated by MSDN:Microsoft has stopped extending Win32 console for this category of question precisely because they want VT-sequence-based shell↔terminal coordination going forward. The path forward, then, is the same one the modern terminal ecosystem has converged on: parse what the shell volunteers via OSC sequences, fall back to a process-tree heuristic when no integration is present.
Why this matters
pane_current_commandis the canonical "what's running here" identifier across the broader psmux plugin ecosystem, and the divergence breaks features that depend on it being predictable.psmux/psmux-plugins#22(per-program restore strategies for psmux-resurrect, accepted in principle, prototype ready). That proposal keys strategies onpane_current_commande.g.,@resurrect-strategy-copilot 'project'fires whenever a saved pane reportscopilotas its command. With the current heuristic, a real copilot pane saves ascmd/aspire/dotnet/ whichever descendant won the race, rarelycopilot. The strategy mechanism's matching key is fundamentally non-deterministic, so no user-written strategy can ever reliably fire. Aligningpane_current_commandwith tmux's effective behavior is the precondition for it's good , but exit of last window(fast if not last) is quite slow #22 to be usable.cmdordotnetoraspiredepending on which descendant was foreground at sample time, instead of the program the user actually launched.#{pane_current_command}substitution displays the same misleading name in the status line, with the additional pathology that the displayed name changes spontaneously as transient subprocesses come and go.automatic-rename-formatinherit the same problem.How the modern terminal ecosystem solves this
I surveyed how every relevant tool in the multiplexer / terminal / IDE space handles "what command is running in this session." Three emit patterns have emerged for carrying the command name:
\e]133;C;cmdline=<%q-quoted cmd>\acmdline=parameter. Co-located with the state marker; backward-compatible (terminals that don't parse the param see bare 133;C).\e]133;C;cmdline_url=<url-escaped cmd>\awezterm.sh, bash/zsh)\e]1337;SetUserVar=WEZTERM_PROG=<b64>\ashellIntegration.ps1)\e]633;E;<escaped cmd>;<nonce>\aPattern A is the cleanest design: no new OSC number, no namespace squatting, no separate sequence-ordering issues, smallest byte overhead. It's published in kitty's shell-integration docs and emitted verbatim by kitty's bash/zsh/fish integration scripts (see kitty.bash —
printf "\e]133;C;cmdline=%q\a" "$last_cmd").Pattern B has user-base value: wezterm.sh is widely sourced and emits
WEZTERM_PROGfrom a preexec hook today. Parsing it gives psmux instant compat with that user base — without psmux defining its own var name (which would have the same namespace concern WezTerm's name has).Pattern C has narrower value: VS Code-script users get the rich path, nobody else does.
Other tools in the survey:
OSC 133;C(no parameter) andOSC 1337;SetUserVar=...(generic mechanism) but does NOT emit a "current command" var — they derive command identity fromtcgetpgrp(Mac-only). They invented the SetUserVar mechanism but didn't standardize a command var; WezTerm did that downstream.OSC 133;C\a(no parameter) and copies kitty'skitty-shell-cwd://URL scheme for OSC 7. Gets command identity from its own scrollback parsing.Set-PSReadLineKeyHandler -Key Enterto emitOSC 133;Cwhenshell_integration: trueis enabled. Addingcmdline_url=to that existing emission is a ~4-line patch I plan to upstream as a separate FR.The legacy approach —
tcgetpgrp+/proc/<pid>/cmdlinewalking — is what tmux and screen do. It works on Unix, breaks on Windows (this issue), and is brittle even on Unix once wrappers nest deeply. Every modern tool has chosen shell-integration-via-escapes instead.Two more OSC sequences are independent of command identity but compose with it
file://HOST/PATHkitty-shell-cwd://flavor)OSC 7 is the right primitive for per-pane cwd. OSC 133 A/B/D are state markers — they let psmux know when the shell is idle vs. running a command. Both compose cleanly with the command-identity layer and unlock significant value beyond just
pane_current_command:Empirical verification
I wrote a minimal pwsh shell-integration emitter following kitty's pattern (~25 lines, no VS Code dependency) and captured what reaches the terminal. Annotated bytes (
\e= ESC,\a= BEL):Round-trip:
copilot%20--yolo→copilot --yolo(flags preserved)dotnet%20build%20-c%20Release→dotnet build -c ReleaseThe emission is via
Set-PSReadLineKeyHandler -Key EnterwrappingAcceptLine: capture the command from PSReadLine's buffer state BEFORE AcceptLine, call AcceptLine, then emitOSC 133;C;cmdline_url=.... The same hook OMP uses for its existing OSC 133;C emission, just extended with the kitty parameter.Proposed resolution: a layered cascade
Replace the original Option A / Option B framing with a layered cascade. Higher layers are more authoritative; psmux falls through when the higher ones produce no signal. Layers 1-3 are shell-integration sources; layers 4-5 are the existing heuristic, improved.
Command identity (what is running in this pane), ordered from most preferred to fallback:
cmdline=/cmdline_url=WEZTERM_PROG= b64(command)wezterm.sh<command>shellIntegration.ps1"shell"placeholder.Layer 4 includes a brief skip-list mechanism for known wrappers (
npx,cmd /c,bash -c) where the meaningful thing is one level deeper; for those specific cases the heuristic falls through to the next child.What the process-tree fix (layers 4 + 5) actually does
These two layers stand alone — they're the original Option A from this issue, re-cast as the fallback in the cascade. If the OSC work doesn't ship, just landing layers 4 + 5 closes the most painful symptom (the copilot case) for everyone, no shell integration required.
Concretely:
Layer 4:
find_foreground_child_pid(insrc/platform.rs) changes from "deepest non-system leaf" to "immediate non-system child" of the pane shell PID. This matches tmux's effective behavior in the common case: a program launched from the shell takes TTY foreground viatcsetpgrpon startup; any subprocesses it spawns inherit its process group and don't become foreground unless they themselves calltcsetpgrp. So picking the immediate child is correct forcopilot,nvim(with or without LSP children),pm2, dev servers, and anything else that fans out workers under a single foreground process.Tradeoff: for shell-style wrappers (
bash -c 'foo',cmd /c 'foo',npx <tool>), the meaningful program is one level deeper than the immediate child. The skip-list above handles the named common cases; users running other wrappers fall back to layer 5 or shell-integration if enabled.Layer 5: format-layer fix (
src/format.rsline 1144). Whenfind_foreground_child_pidreturnsNone, instead of substituting the literal"shell"placeholder, look up the pane shell PID's own executable basename. Idle pwsh sessions reportpwsh, idle bash sessions reportbash, matching tmux.Both are small, surgical changes — no new options, no new API surface — and together they close divergences #1 and #2 even without any OSC parsing. The OSC layers (1-3) are strictly additive on top.
Independent tracks (always parsed when emitted, surfaced as separate format tokens):
monitor-activity.State machine for the integrated layers:
(Note: 133;C's optional
cmdline=/cmdline_url=parameter takes precedence over anypending_cmdfrom a separate sequence — it's co-located with the state transition, no ordering ambiguity.)pane_current_commandresolution at query time:Running(Some(cmd), _)→ returncmdRunning(None, _)→ fall through to layer 4 (heuristic)Idle→ fall through to layer 5 (shell basename)Why this shape
cmdline=is a generic parameter on a generic state marker.Considered alternatives
GetConsoleProcessList()+ heuristics about console allocation order. Same FIFO attach order we already get from the process tree, no additional information. Microsoft's docs explicitly recommend against this API for foreground detection.PSMUX_CMDorcmd). Considered and rejected — puts the same namespace burden on every future consumer that the WezTerm-naming concern raises. The kitty parameter pattern avoids this.pane_current_commandis read on every status bar refresh, potentially many times per second across all panes. The latency budget is microseconds; spawning a PowerShell process per query is two orders of magnitude too slow.#{pane_current_command}in status bars) still produces misleading output. Better fixed at the source.Enabling shell integration in practice (downstream docs concern)
Setups that provide the integrated data psmux would parse:
kitty.bash/kitty.zsh)cmdline=/cmdline_url=pwd: osc7+shell_integration: truecmdline_url=wezterm.sh(bash/zsh)WEZTERM_PROGshellIntegration.ps1cmdline_url=OMP FR shape (will cross-link once filed): in OMP's existing
New-EnterKeyHandler(which today emits bareOSC 133;Cfor_ompFTCSMarks), capture the command via[Microsoft.PowerShell.PSConsoleReadLine]::GetBufferStatebefore callingAcceptLine, then emitOSC 133;C;cmdline_url=<url-escaped>instead. Backward compatible (terminals that don't parse the parameter see bare 133;C), aligned with kitty's published spec, gated by the existingshell_integrationflag.psmux itself should not ship an integration script — document the opt-in; point at kitty / OMP / WezTerm as the upstream sources.