From c5738423dbcd408b9812d87e74e41bfa93c81bac Mon Sep 17 00:00:00 2001 From: "Lindenau Stefan (VM/ESO2)" Date: Fri, 14 Aug 2026 13:28:20 +0000 Subject: [PATCH] fix(shell-integration): resync all three packaged copies with src MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `resources/shell-integration/` is a checked-in copy of `src/shell-integration/` that nothing keeps in step, and all three files had drifted: wmux-bash-integration.sh missing the `wmux()` function wmux-cmd-integration.cmd missing `chcp 65001` wmux-powershell-integration.ps1 19 insertions, 65 deletions behind The PowerShell one is the interesting case. Its copy predates issue #72, so `Send-WmuxMessage` sends V1 lines with no `auth ` prefix — and pipe-server.ts rejects every V1 command but `ping` without one. Anything running that copy reports no cwd, no git branch, no shell state, no PR: the pipe answers `unauthorized` and the integration has no way to notice. It also predates the UTF-8 console setup, the `wmux` function, deferring the PR-poll job off the first-prompt critical path, and `WMUX_STARTUP_COMMANDS` (issue #32). Copied from master's own `src/`, so the three files are now byte-identical to their sources; `diff -r src/shell-integration resources/shell-integration` is empty. Worth knowing before deciding how much this matters: no release path reads these files. electron-builder's extraResources maps `src/shell-integration` → `shell-integration`, step 7 of the release process copies `src/shell-integration/*` into the staging dir, and `getShellIntegrationPath()` falls back to `src/shell-integration` in dev. So a packaged wmux has always run the fresh source, and none of the breakage above ever reached a released install. What does read `resources/shell-integration/` is anything consuming wmux out of a repo checkout — which is how the drift was found; a devcontainer integration sources the bash copy from there. That makes this the same shape as `resources/cli/wmux.js`, which is 1519 diff lines behind a build of master's own `src/` for the same reason: a duplicate with no enforcement. Resyncing fixes today's drift and guarantees tomorrow's. Two ways out, both happy to send as a follow-up — say which you prefer: - a CI step that fails when `resources/` does not match a fresh copy/build, covering the shell scripts and `resources/cli/*.js` together; or - delete `resources/shell-integration/` outright and point checkout consumers at `src/shell-integration/`, since nothing wmux ships or runs reads it. The second is the smaller repo, but it breaks any external consumer already reading the old path, so it is your call rather than mine. Co-Authored-By: Claude Opus 5 --- .../wmux-bash-integration.sh | 4 + .../wmux-cmd-integration.cmd | 4 + .../wmux-powershell-integration.ps1 | 84 ++++++++++++++----- 3 files changed, 73 insertions(+), 19 deletions(-) diff --git a/resources/shell-integration/wmux-bash-integration.sh b/resources/shell-integration/wmux-bash-integration.sh index a815a713..b4207025 100644 --- a/resources/shell-integration/wmux-bash-integration.sh +++ b/resources/shell-integration/wmux-bash-integration.sh @@ -4,6 +4,10 @@ export WMUX=1 +# wmux CLI shortcut — Claude Code and users can just type: wmux browser open +wmux() { node "$WMUX_CLI" "$@"; } +export -f wmux + _wmux_report() { local msg="$1" # Write to temp file for main process to pick up diff --git a/resources/shell-integration/wmux-cmd-integration.cmd b/resources/shell-integration/wmux-cmd-integration.cmd index b950278c..764637f2 100644 --- a/resources/shell-integration/wmux-cmd-integration.cmd +++ b/resources/shell-integration/wmux-cmd-integration.cmd @@ -5,6 +5,10 @@ REM Reports CWD via OSC 9 escape sequence embedded in prompt REM Set WMUX env var set WMUX=1 +REM UTF-8 code page so multi-byte input (Korean, Japanese, Chinese, emoji) +REM round-trips through conpty correctly. +chcp 65001 >nul 2>&1 + REM Set prompt to include OSC 9 with current directory REM ESC]9;9;PATH ESC\ then normal prompt prompt $e]9;9;$P$e\$P$G diff --git a/resources/shell-integration/wmux-powershell-integration.ps1 b/resources/shell-integration/wmux-powershell-integration.ps1 index 0f981739..09326413 100644 --- a/resources/shell-integration/wmux-powershell-integration.ps1 +++ b/resources/shell-integration/wmux-powershell-integration.ps1 @@ -3,10 +3,25 @@ $env:WMUX = "1" -# Named pipe client helper +# UTF-8 I/O so multi-byte input (Korean, Japanese, Chinese, emoji, accents) +# survives the conpty round-trip cleanly. +try { + [Console]::InputEncoding = [System.Text.UTF8Encoding]::new() + [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new() + $OutputEncoding = [System.Text.UTF8Encoding]::new() + $PSDefaultParameterValues['Out-File:Encoding'] = 'utf8' +} catch {} + +# wmux CLI shortcut — Claude Code and users can just type: wmux browser open +function wmux { node "$env:WMUX_CLI" @args } + +# Named pipe client helper. State updates carry an "auth " prefix — +# wmux injects WMUX_PIPE_TOKEN into every shell it spawns, and the pipe server +# rejects unauthenticated V1 commands (issue #72). function Send-WmuxMessage { param([string]$Message) try { + if ($env:WMUX_PIPE_TOKEN) { $Message = "auth $($env:WMUX_PIPE_TOKEN) $Message" } $pipe = New-Object System.IO.Pipes.NamedPipeClientStream(".", "wmux", [System.IO.Pipes.PipeDirection]::InOut) $pipe.Connect(1000) $writer = New-Object System.IO.StreamWriter($pipe) @@ -86,22 +101,53 @@ function prompt { } } -# PR polling background job (every 45 seconds) -$_wmux_pr_job = Start-Job -ScriptBlock { - param($surfaceId, $pipeName) - while ($true) { - Start-Sleep -Seconds 45 - try { - $prJson = gh pr view --json number,state,title 2>$null - if ($LASTEXITCODE -eq 0 -and $prJson) { - $pr = $prJson | ConvertFrom-Json - $pipe = New-Object System.IO.Pipes.NamedPipeClientStream(".", $pipeName, [System.IO.Pipes.PipeDirection]::InOut) - $pipe.Connect(1000) - $writer = New-Object System.IO.StreamWriter($pipe) - $writer.AutoFlush = $true - $writer.WriteLine("report_pr $surfaceId $($pr.number) $($pr.state) $($pr.title)") - $pipe.Close() - } - } catch { } +# PR polling background job (every 45 seconds). +# DEFERRED: Start-Job spins up a whole child PowerShell runspace and costs +# several hundred ms — running it during init delayed the FIRST prompt. We +# instead start it on the shell's first idle (after the prompt is already on +# screen), so it never sits on the startup critical path. A global guard makes it +# fire exactly once; PR data isn't needed in the first 45s anyway. +$global:_wmux_pr_started = $false +$null = Register-EngineEvent -SourceIdentifier ([System.Management.Automation.PSEngineEvent]::OnIdle) -Action { + if ($global:_wmux_pr_started) { return } + $global:_wmux_pr_started = $true + $global:_wmux_pr_job = Start-Job -ScriptBlock { + param($surfaceId, $pipeName, $pipeToken) + while ($true) { + Start-Sleep -Seconds 45 + try { + $prJson = gh pr view --json number,state,title 2>$null + if ($LASTEXITCODE -eq 0 -and $prJson) { + $pr = $prJson | ConvertFrom-Json + $msg = "report_pr $surfaceId $($pr.number) $($pr.state) $($pr.title)" + if ($pipeToken) { $msg = "auth $pipeToken $msg" } + $pipe = New-Object System.IO.Pipes.NamedPipeClientStream(".", $pipeName, [System.IO.Pipes.PipeDirection]::InOut) + $pipe.Connect(1000) + $writer = New-Object System.IO.StreamWriter($pipe) + $writer.AutoFlush = $true + $writer.WriteLine($msg) + $pipe.Close() + } + } catch { } + } + } -ArgumentList $env:WMUX_SURFACE_ID, "wmux", $env:WMUX_PIPE_TOKEN +} + +# Quick-launch profile startup commands (issue #32). +# wmux passes these in WMUX_STARTUP_COMMANDS (newline-separated) so they run as +# part of init — before the first interactive prompt — rather than being injected +# as keystrokes afterward. Keystroke injection raced the shell's init-time +# Device Attributes query (ConPTY answers DA1 with "\e[?62;4;9;22c" on stdin); +# when that response landed on the prompt alongside an injected "\r" the two +# merged into a bogus executed line (e.g. "62;4;9;22ccls"). Running here avoids +# that entirely. Runs last so the prompt override / PSReadLine handlers exist. +if ($env:WMUX_STARTUP_COMMANDS) { + foreach ($_wmux_cmd in ($env:WMUX_STARTUP_COMMANDS -split "`n")) { + $_wmux_cmd = $_wmux_cmd.Trim() + if ($_wmux_cmd) { + try { Invoke-Expression $_wmux_cmd } catch { Write-Error $_ } + } } -} -ArgumentList $env:WMUX_SURFACE_ID, "wmux" + # One-shot: don't let it leak into child shells spawned from this session. + Remove-Item Env:\WMUX_STARTUP_COMMANDS -ErrorAction SilentlyContinue +}