Claude Code on WSL

Image Paste on WSL2

wsl-screenshot-cli — a Go daemon that saves screenshots to disk and puts the file path in the clipboard. Works with both Claude Code and Codex CLI.

   
Claude Code ✓ (Alt+V or Ctrl+Shift+V, paste path)
Codex CLI ✓
Requirements WSL2, Windows interoperability, systemd user services; pre-built binary needs no Go toolchain
Poll interval 250ms

Install

Install a pinned release after checking its checksum. Use arch=arm64 on ARM:

version=1.3.2
arch=amd64
archive="wsl-screenshot-cli_${version}_linux_${arch}.tar.gz"
base="https://github.com/Nailuu/wsl-screenshot-cli/releases/download/v${version}"
tmp=$(mktemp -d)
curl -fsSLo "$tmp/$archive" "$base/$archive"
curl -fsSLo "$tmp/checksums.txt" "$base/checksums.txt"
(cd "$tmp" && grep " $archive\$" checksums.txt | sha256sum -c -) &&
  tar -xzf "$tmp/$archive" -C "$tmp" wsl-screenshot-cli &&
  install -Dm755 "$tmp/wsl-screenshot-cli" ~/.local/bin/wsl-screenshot-cli
rm -rf "$tmp"

Check the releases page for a newer version. This installs only the binary; no Go toolchain is needed and no startup file changes.

The upstream one-line installer (curl -fsSL https://nailu.dev/wscli/install.sh | bash) also works, but on a fresh install it offers to start the monitor from ~/.bashrc, from Claude Code SessionStart/SessionEnd hooks, or both. Choose Skip: either would race with the systemd service below, and the SessionEnd hook stops the monitor. The installer may also append a ~/.local/bin PATH entry to ~/.bashrc.


Run the monitor with systemd

Use the service below as the only process owner. Do not run start --daemon alongside it or put start/stop commands in shell startup files or Claude hooks. If migrating from a manually started daemon, stop that old daemon once with wsl-screenshot-cli stop before enabling the service. If a service already owns the monitor, use systemctl --user stop wsl-screenshot-cli.service instead.

This requires a WSL distribution with systemd enabled. Confirm systemctl --user is-system-running can reach the user manager before continuing. A degraded result means some units failed; inspect them with systemctl --user --failed.

Create the directory, then create ~/.config/systemd/user/wsl-screenshot-cli.service. Back up an existing unit before editing it:

mkdir -p ~/.config/systemd/user
[Unit]
Description=WSL screenshot clipboard monitor
After=default.target

[Service]
Type=simple
Environment=WSL_INTEROP=/run/WSL/1_interop
Environment=PATH=%h/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
ExecStart=%h/.local/bin/wsl-screenshot-cli start --quiet
Restart=always
RestartSec=2

[Install]
WantedBy=default.target

This unit preserves the WSL_INTEROP path used by the previous setup. Confirm test -S /run/WSL/1_interop succeeds on your WSL instance; if it does not, inspect printf '%s\n' "$WSL_INTEROP" in a working WSL shell and use that socket path. The socket may change after a WSL restart, so recheck it if interoperability fails. The explicit environment entries are needed because the systemd user manager does not inherit them from an interactive WSL shell. Run start in the foreground here: systemd must own the process so it can restart it if the clipboard client exits. Do not also start the daemon from shell startup files or Claude hooks.

Then enable it:

systemctl --user daemon-reload
systemctl --user enable --now wsl-screenshot-cli.service
systemctl --user status wsl-screenshot-cli.service

Inspect failures with:

journalctl --user -u wsl-screenshot-cli.service -n 50 --no-pager

Keybinding (optional)

To use Alt+V instead of Ctrl+Shift+V, merge this binding into ~/.claude/keybindings.json, preserving other entries:

{
  "bindings": [
    {
      "context": "Chat",
      "bindings": {
        "alt+v": "chat:imagePaste"
      }
    }
  ]
}

The top level must be an object with a bindings array — not a bare JSON array (bare array silently fails to load).

Verify

Use the service for routine control:

systemctl --user start wsl-screenshot-cli.service
systemctl --user status wsl-screenshot-cli.service
# To stop it:
systemctl --user stop wsl-screenshot-cli.service
# Start it again before testing clipboard paste:
systemctl --user start wsl-screenshot-cli.service

Take a screenshot on Windows and paste into your terminal with Ctrl+Shift+V. Expect a path under /tmp/.wsl-screenshot-cli/; confirm that the file exists and Claude Code can open it. After restarting WSL, repeat the test to check autostart.

For an update, stop the service, run wsl-screenshot-cli update (or rerun the install block with a new version), then start the service again and repeat the paste test.

bash scripts/doctor.sh from the cloned repository also checks the service and flags shell startup files or Claude hooks that start a second monitor.

Remove

systemctl --user disable --now wsl-screenshot-cli.service
rm -f ~/.config/systemd/user/wsl-screenshot-cli.service
systemctl --user daemon-reload

Remove only the optional alt+v binding you added from ~/.claude/keybindings.json. Keep other bindings. If no other workflow needs it, remove the installed ~/.local/bin/wsl-screenshot-cli binary. Restore an earlier service file if you had one, reload systemd, and restore its previous enabled state.