Skip to content

Instantly share code, notes, and snippets.

@bgr
Created February 19, 2026 13:50
Show Gist options
  • Select an option

  • Save bgr/31a25707f026abd34b2deb587ee74466 to your computer and use it in GitHub Desktop.

Select an option

Save bgr/31a25707f026abd34b2deb587ee74466 to your computer and use it in GitHub Desktop.
Make claude --chrome work from WSL2 with Windows Chrome
#!/bin/bash
# Enables `claude --chrome` to work from WSL2 with Windows-installed Chrome
# Run this from inside your WSL2 distro
#
# This script was written by Claude, see the following link for more info:
# https://github.com/anthropics/claude-code/issues/14367#issuecomment-3927349991
#
# The script will:
# - Auto-detect Windows username, WSL distro, Chrome profile, and claude binary
# - Create the real directory + Extensions symlink for detection
# - Patch the .bat file (with backup) to bridge to WSL
# - Create the native host script and manifest if missing
# - Check the Windows registry
# Idempotent — safe to re-run (skips steps already done)
set -euo pipefail
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
info() { echo -e "${GREEN}[+]${NC} $*"; }
warn() { echo -e "${YELLOW}[!]${NC} $*"; }
error() { echo -e "${RED}[x]${NC} $*"; exit 1; }
# Check we're in WSL
grep -qi microsoft /proc/version 2>/dev/null || error "This script must be run inside WSL"
# Detect Windows username
WIN_USER=$(cmd.exe /c "echo %USERNAME%" 2>/dev/null | tr -d '\r') || error "Could not detect Windows username"
WIN_HOME="/mnt/c/Users/$WIN_USER"
[ -d "$WIN_HOME" ] || error "Windows home not found at $WIN_HOME"
info "Windows user: $WIN_USER"
# Detect WSL distro name
WSL_DISTRO="${WSL_DISTRO_NAME:-}"
if [ -z "$WSL_DISTRO" ]; then
# Fallback: parse from wsl.exe
WSL_DISTRO=$(wsl.exe -l -q 2>/dev/null | tr -d '\0\r' | head -1) || true
fi
[ -n "$WSL_DISTRO" ] || error "Could not detect WSL distro name. Set WSL_DISTRO_NAME env var."
info "WSL distro: $WSL_DISTRO"
# Find claude binary in WSL
CLAUDE_BIN=$(which claude 2>/dev/null) || error "claude not found in PATH. Install Claude Code in WSL first."
# Don't resolve symlinks — keep the stable path so it survives version updates
info "Claude binary: $CLAUDE_BIN"
# Find Windows Chrome user data
CHROME_DATA="$WIN_HOME/AppData/Local/Google/Chrome/User Data"
[ -d "$CHROME_DATA" ] || error "Windows Chrome not found at $CHROME_DATA"
info "Windows Chrome data: $CHROME_DATA"
# Check extension is installed in Windows Chrome
EXT_ID="fcoeoabgfenejglbffodgkkbkcdhcgfn"
EXT_FOUND=false
for profile_dir in "$CHROME_DATA"/Default "$CHROME_DATA"/Profile\ *; do
[ -d "$profile_dir" ] || continue
if [ -d "$profile_dir/Extensions/$EXT_ID" ]; then
PROFILE_NAME=$(basename "$profile_dir")
info "Claude in Chrome extension found in profile: $PROFILE_NAME"
EXT_FOUND=true
break
fi
done
$EXT_FOUND || error "Claude in Chrome extension not found in any Chrome profile. Install it from https://claude.ai/chrome"
# --- Step 1: Fix extension detection ---
info "Setting up extension detection..."
LINUX_CHROME_DIR="$HOME/.config/google-chrome"
LINUX_PROFILE_DIR="$LINUX_CHROME_DIR/$PROFILE_NAME"
LINUX_EXT_LINK="$LINUX_PROFILE_DIR/Extensions"
WIN_EXT_DIR="$profile_dir/Extensions"
# Create real directory (not symlink) so Node's isDirectory() works
if [ -L "$LINUX_PROFILE_DIR" ]; then
info "Removing existing symlink at $LINUX_PROFILE_DIR (won't work with Node's readdir)"
rm "$LINUX_PROFILE_DIR"
fi
mkdir -p "$LINUX_PROFILE_DIR"
# Symlink Extensions inside the real directory
if [ -L "$LINUX_EXT_LINK" ]; then
CURRENT_TARGET=$(readlink "$LINUX_EXT_LINK")
if [ "$CURRENT_TARGET" = "$WIN_EXT_DIR" ]; then
info "Extensions symlink already correct"
else
warn "Updating Extensions symlink (was: $CURRENT_TARGET)"
rm "$LINUX_EXT_LINK"
ln -s "$WIN_EXT_DIR" "$LINUX_EXT_LINK"
fi
elif [ -d "$LINUX_EXT_LINK" ]; then
warn "Extensions is a real directory, replacing with symlink"
rmdir "$LINUX_EXT_LINK" 2>/dev/null || error "Cannot replace $LINUX_EXT_LINK - it's not empty"
ln -s "$WIN_EXT_DIR" "$LINUX_EXT_LINK"
else
ln -s "$WIN_EXT_DIR" "$LINUX_EXT_LINK"
fi
info "Extension detection: $LINUX_PROFILE_DIR/Extensions -> $WIN_EXT_DIR"
# --- Step 2: Bridge native host to WSL ---
info "Setting up native host bridge..."
BAT_FILE="$WIN_HOME/.claude/chrome/chrome-native-host.bat"
if [ ! -f "$BAT_FILE" ]; then
warn "Native host .bat not found at $BAT_FILE"
warn "Run 'claude --chrome' once on Windows to create it, then re-run this script"
warn "Or, run 'claude --chrome' from WSL first (it will fail but creates the Linux-side files)"
# Create the directory and bat file ourselves
mkdir -p "$(dirname "$BAT_FILE")"
cat > "$BAT_FILE" << BATEOF
@echo off
REM Chrome native host wrapper script - bridges to WSL
REM Generated by setup-claude-chrome-wsl.sh
wsl.exe -d ${WSL_DISTRO} -- ${CLAUDE_BIN} --chrome-native-host
BATEOF
info "Created $BAT_FILE"
else
# Check if already patched
if grep -q "wsl.exe" "$BAT_FILE" 2>/dev/null; then
info "Native host .bat already bridges to WSL"
else
# Back up and patch
cp "$BAT_FILE" "${BAT_FILE}.bak"
info "Backed up original to ${BAT_FILE}.bak"
cat > "$BAT_FILE" << BATEOF
@echo off
REM Chrome native host wrapper script - bridges to WSL
REM Modified by setup-claude-chrome-wsl.sh
wsl.exe -d ${WSL_DISTRO} -- ${CLAUDE_BIN} --chrome-native-host
$(sed 's/^/REM [original] /' "${BAT_FILE}.bak")
BATEOF
info "Patched $BAT_FILE to bridge to WSL"
fi
fi
# --- Step 3: Ensure native messaging host manifest exists ---
info "Checking native messaging host manifest..."
NMH_DIR="$LINUX_CHROME_DIR/NativeMessagingHosts"
NMH_FILE="$NMH_DIR/com.anthropic.claude_code_browser_extension.json"
NATIVE_HOST="$HOME/.claude/chrome/chrome-native-host"
mkdir -p "$NMH_DIR"
# Also ensure the Linux-side native host script exists
if [ ! -f "$NATIVE_HOST" ]; then
mkdir -p "$(dirname "$NATIVE_HOST")"
cat > "$NATIVE_HOST" << SHEOF
#!/bin/sh
# Chrome native host wrapper script
# Generated by setup-claude-chrome-wsl.sh
exec "$CLAUDE_BIN" --chrome-native-host
SHEOF
chmod +x "$NATIVE_HOST"
info "Created $NATIVE_HOST"
fi
# Write the manifest
cat > "$NMH_FILE" << JSONEOF
{
"name": "com.anthropic.claude_code_browser_extension",
"description": "Claude Code Browser Extension Native Host",
"path": "$NATIVE_HOST",
"type": "stdio",
"allowed_origins": [
"chrome-extension://$EXT_ID/"
]
}
JSONEOF
info "Native messaging manifest: $NMH_FILE"
# --- Step 4: Check Windows registry ---
REG_KEY="HKCU\\Software\\Google\\Chrome\\NativeMessagingHosts\\com.anthropic.claude_code_browser_extension"
REG_VALUE=$(reg.exe query "$REG_KEY" /ve 2>/dev/null | tr -d '\r' | grep REG_SZ | awk '{print $NF}') || true
if [ -z "$REG_VALUE" ]; then
warn "Windows registry key not found for native messaging host"
warn "The Claude in Chrome extension should create this automatically."
warn "If it doesn't work, you may need to run 'claude --chrome' on Windows once first."
else
info "Windows registry: $REG_KEY -> $REG_VALUE"
fi
echo ""
info "Setup complete! Run 'claude --chrome' in WSL to connect to Windows Chrome."
warn "Note: If you update Claude Code on Windows, it may overwrite chrome-native-host.bat — re-run this script if that happens."
@apictapx
Copy link
Copy Markdown

apictapx commented Mar 6, 2026

Oh, man! Thank you! Mine started to work too.

@potto007
Copy link
Copy Markdown

You are a saint! This worked for me, too! No more jumping over to Claude Code in PowerHell when I need to use Claude in Chrome! :)

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