8000 WSL panes: Claude session restore doesn't work (hooks, WSLENV /u, bash integration CRLF, cwd reports not delivered) · Issue #428 · amirlehmam/wmux · GitHub
[go: up one dir, main page]

Skip to content

WSL panes: Claude session restore doesn't work (hooks, WSLENV /u, bash integration CRLF, cwd reports not delivered) #428

Closed

Description

@ZvikaZ

What happens

When Claude Code runs inside WSL in a wsl.exe pane, wmux doesn't restore the session correctly. Four separate problems combine:

  1. The Claude hooks don't reach WSL. wmux writes them only to the Windows %USERPROFILE%\.claude\settings.json. Claude Code in WSL reads the distro's ~/.claude/settings.json, so no hook fires and the pane never gets a claudeSessionId. Copying the hooks over isn't enough on its own: wmux-hook.js calls net.connect on \\.\pipe\wmux, and Linux node can't open that pipe. The hook has no npiperelay route; only wmux.js has one.
  2. WSL_ENV_PASSTHROUGH uses /u. It's set to WMUX/u:WMUX_SURFACE_ID/u:WMUX_CLI/up:WMUX_PIPE/u:WMUX_PIPE_TOKEN/u:WMUX_INTEGRATION/u, and /u passes values only from Windows into WSL. A Windows helper started inside the pane, such as node.exe running the hook, never gets WMUX_SURFACE_ID, WMUX_PIPE or WMUX_PIPE_TOKEN.
  3. shell-integration/wmux-bash-integration.sh has CRLF line endings. WSL bash can't source it (error below). Nothing loads it in WSL panes either.
  4. In WSL, _wmux_report writes to a file wmux never reads. Without WMUX_REMOTE, it appends to /mnt/c/Users/$USER/AppData/Local/Temp/wmux/messages. That file only grows, and report_pwd never reaches wmux. So the workspace never gets a posixCwd, and workspaceFallbackCwd() returns undefined for a WSL shell when the workspace cwd is a Windows path. wslCdArg then falls back to --cd ~, so the restored pane opens in $HOME. There, claude --resume <id> can't find the session, because Claude finds sessions by project directory.

Steps to reproduce

  1. Install Claude Code inside WSL2 (Ubuntu) and enable wmux's agent integration (hooks).
  2. Create a workspace on a Windows folder, e.g. C:\Projects\foo, and open a wsl.exe pane in it. The pane opens in $HOME, not /mnt/c/Projects/foo.
  3. cd /mnt/c/Projects/foo, run claude, and send one message.
  4. Check %APPDATA%\wmux\sessions\session.json. The pane has no claudeSessionId, and the workspace has no posixCwd.
  5. Run . "/mnt/c/Program Files/wmux/resources/shell-integration/wmux-bash-integration.sh". It fails with a syntax error.
  6. Restart wmux. Claude is not resumed, and the pane opens in $HOME.

wmux version

2.54.0

Windows version

Windows 11 25H2 26200.9711

Anything else

Version and environment: wmux 2.54.0, Windows 11, WSL2 Ubuntu (systemd on), Claude Code installed inside WSL.

Shell: C:\Windows\System32\wsl.exe. The same thing happens when WSL is started from a Git Bash pane, which never gets the WMUX_* variables at all.

Error when sourcing the bash integration:

: command not found
-bash: /mnt/c/Program Files/wmux/resources/shell-integration/wmux-bash-integration.sh: line 14: syntax error near unexpected token `$'{\r''

Workarounds that make it work (confirmed)

The hooks in WSL ~/.claude/settings.json run Windows node and pass the variables back out of WSL:

WSLENV=WMUX:WMUX_SURFACE_ID:WMUX_PIPE:WMUX_PIPE_TOKEN:WMUX_INSTANCE:WMUX_REMOTE:WMUX_REMOTE_TOKEN "/mnt/c/Program Files/nodejs/node.exe" "C:/Program Files/wmux/resources/cli/wmux-hook.js" --event SessionStart

With this, claudeSessionId appears in session.json.

~/.bashrc loads the integration with the CRs removed and sends reports over the pipe:

if [ "$WMUX_INTEGRATION" = "1" ] && [ -n "$WMUX_SURFACE_ID" ]; then
    . <(tr -d '\r' < "/mnt/c/Program Files/wmux/resources/shell-integration/wmux-bash-integration.sh")
    _wmux_report() {
        ( WSLENV="WMUX:WMUX_PIPE:WMUX_PIPE_TOKEN" "/mnt/c/Program Files/nodejs/node.exe" \
            "C:/Program Files/wmux/resources/cli/wmux.js" raw-v1 "$1" >/dev/null 2>&1 & )
    }
fi

raw-v1 report_pwd <surface> /mnt/c/... returns ok in about 0.1 s, and posixCwd then appears on the workspace.

Suggested fixes

  • Ship the .sh files with LF line endings (.gitattributes: *.sh text eol=lf).
  • Drop /u from the WMUX_* entries in WSL_ENV_PASSTHROUGH.
  • Install the hooks into the WSL Claude config too, run through Windows node.exe.
  • In WSL, have _wmux_report send over the pipe (raw-v1 through Windows node or npiperelay) instead of the temp file.
  • Load the bash integration in WSL panes automatically.

This report was investigated and written by Claude (Claude Code, Anthropic's AI assistant) at my request. I confirmed the workarounds on my machine.

Activity

  1. amirlehmam commented on Nov 3, 2026

    @amirlehmam
    Owner

    Thanks for the precise breakdown. 2.55.0 fixes three of the four points:

    1. CRLF: .sh files are now checked out with LF (.gitattributes), so the shipped script sources in WSL bash.
    2. WSLENV: WMUX, WMUX_SURFACE_ID, WMUX_PIPE and WMUX_PIPE_TOKEN now cross both ways, and WMUX_CLI is /p. A Windows node.exe started inside the pane, such as your hook, gets them without the inline WSLENV= prefix.
    3. Reports never delivered: in WSL, _wmux_report now sends over the pipe through Windows node running wmux.js raw-v1, like your workaround, so report_pwd reaches wmux and the workspace gets its posixCwd.

    wmux still does not load the integration in a WSL pane by itself. wsl.exe starts the distro's own login shell, and replacing it would break zsh and fish users. Instead every WSL pane now gets WMUX_BASH_SCRIPT, so one line in the distro's ~/.bashrc is enough:

    [ -n "$WMUX_BASH_SCRIPT" ] && . "$WMUX_BASH_SCRIPT"

    (documented in docs/wsl-explorer.md).

    The remaining point is installing the Claude hooks into the distro's ~/.claude/settings.json. That has to go through wmux's consent gate with a matching removal path. It also has to find each distro's home without waking stopped WSL VMs on every launch, so it isn't in this release. Until then, your manual hook works without its WSLENV prefix. I'm keeping this issue open for that part.

  2. amirlehmam commented on Nov 4, 2026

    @amirlehmam
    Owner

    2.56.0 covers the last part: installing the Claude Code hooks inside the distro.

    With agent hooks enabled (the same consent as the Windows hooks), wmux now adds its hooks to ~/.claude/settings.json in each running WSL distro. Your own hooks and settings are left alone, and turning hooks off removes wmux's entries again. It never starts a stopped distro for this. It only looks at distros wsl.exe --list --running reports, a few seconds after a WSL pane opens, and updates a stopped one the next time it is running. A distro without ~/.claude/settings.json is left alone.

    Each hook command looks for wmux's helper next to WMUX_BASH_SCRIPT. Outside a wmux pane it does nothing and exits 0, so Claude in another terminal is unaffected. Inside a pane, the helper runs the Windows wmux-hook.js through interop over the same pipe route _wmux_report uses since 2.55.0. The session id is recorded with its origin, so restoring wmux no longer drops a WSL Claude session because its transcript isn't in the Windows ~/.claude/projects.

    Two things to know:

    • The ~/.bashrc line from 2.55.0 is still needed ([ -n "$WMUX_BASH_SCRIPT" ] && . "$WMUX_BASH_SCRIPT").
    • Your manual hook calls wmux-hook.js too, so wmux treats it as its own. It is replaced by the managed ones, and removed if you turn hooks off. Details are in docs/wsl-explorer.md.

    I tested the install, update and removal against a distro's settings file, and ran the hook command end to end through a POSIX shell into a fake pipe. I could not run Claude in a real Ubuntu pane here. Could you confirm on 2.56.0 that Claude in a WSL pane shows its state in the sidebar and comes back after a wmux restart? I'll close the issue once you do.

  3. ZvikaZ commented on Nov 4, 2026

    @ZvikaZ
    Author

    Confirmed on 2.57.0 (Windows 11, WSL2 Ubuntu, Claude Code inside WSL):

    • wmux replaced my manual hooks in the distro's ~/.claude/settings.json with the managed wmux-hook-wsl.sh ones.
    • Claude in a WSL pane shows its state in the sidebar.
    • After a wmux restart, both WSL panes reopened in the project directory (currentCwd saved per pane) and resumed their Claude sessions (claudeSessionHost: "wsl" in session.json).
    • Only the ~/.bashrc line from docs/wsl-explorer.md is still needed on my side.

    Thanks for the quick fixes! Fine to close.

    Verified with Claude (Claude Code), which checked the settings and session files.

  4. amirlehmam commented on Nov 6, 2026

    @amirlehmam
    Owner

    Thanks for confirming on 2.57.0, @ZvikaZ. Closing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      0