Last active
February 6, 2026 13:57
-
-
Save kuhar/50940e8613a8f70ed9a2e38b888e2d04 to your computer and use it in GitHub Desktop.
Cursor IDE <-> tmux integration for Claude Code CLI
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # cursor-tmux.sh -- Cursor IDE <-> tmux integration for Claude Code | |
| # | |
| # When sourced inside Cursor's terminal, snapshots IDE env vars to a file. | |
| # When sourced elsewhere (tmux, other shells), restores them. | |
| # The claude() wrapper strips vars that trigger a PID ancestry check. | |
| # | |
| # Supports multiple Cursor instances: env files are stored per-port | |
| # in ~/.cursor-env.d/. Use cursor-select to switch instances. | |
| _CURSOR_ENV_DIR="$HOME/.cursor-env.d" | |
| # --- Auto-save when inside Cursor's integrated terminal -------------------- | |
| if [[ -n "$VSCODE_IPC_HOOK_CLI" && -S "$VSCODE_IPC_HOOK_CLI" && -n "$CLAUDE_CODE_SSE_PORT" ]]; then | |
| mkdir -p "$_CURSOR_ENV_DIR" | |
| { | |
| printf 'export CLAUDE_CODE_AUTO_CONNECT_IDE=true\n' | |
| for _v in TERM_PROGRAM TERM_PROGRAM_VERSION \ | |
| VSCODE_IPC_HOOK_CLI VSCODE_GIT_IPC_HANDLE VSCODE_GIT_IPC_AUTH_TOKEN \ | |
| VSCODE_GIT_ASKPASS_NODE VSCODE_GIT_ASKPASS_MAIN VSCODE_GIT_ASKPASS_EXTRA_ARGS \ | |
| VSCODE_NONCE GIT_ASKPASS BROWSER \ | |
| CLAUDE_CODE_SSE_PORT ENABLE_IDE_INTEGRATION; do | |
| eval "_val=\${$_v}" | |
| [[ -n "$_val" ]] && printf 'export %s=%q\n' "$_v" "$_val" | |
| done | |
| } > "$_CURSOR_ENV_DIR/$CLAUDE_CODE_SSE_PORT" | |
| unset _v _val | |
| fi | |
| # --- Auto-restore for non-Cursor shells ----------------------------------- | |
| # Sources the most recently saved instance. | |
| if [[ -z "$VSCODE_IPC_HOOK_CLI" || ! -S "$VSCODE_IPC_HOOK_CLI" ]]; then | |
| _f=$(ls -t "$_CURSOR_ENV_DIR"/* 2>/dev/null | head -1) | |
| [[ -n "$_f" ]] && source "$_f" | |
| unset _f | |
| fi | |
| # --- Functions ------------------------------------------------------------- | |
| # Pick which Cursor instance to connect to. | |
| cursor-select() { | |
| local f port ws entries=() | |
| for f in "$_CURSOR_ENV_DIR"/*; do | |
| [[ -f "$f" ]] || continue | |
| port=$(basename "$f") | |
| ws=$(jq -r '.workspaceFolders[0] // empty' "$HOME/.claude/ide/${port}.lock" 2>/dev/null) | |
| entries+=("$(printf '%s\t%s' "$port" "${ws:-(stale)}")") | |
| done | |
| if [[ ${#entries[@]} -eq 0 ]]; then | |
| echo "No saved Cursor instances" >&2; return 1 | |
| fi | |
| if [[ ${#entries[@]} -eq 1 ]]; then | |
| port=$(printf '%s' "${entries[0]}" | cut -f1) | |
| source "$_CURSOR_ENV_DIR/$port" | |
| echo "Using Cursor on port $port (${entries[0]##* })" | |
| return | |
| fi | |
| local pick | |
| pick=$(printf '%s\n' "${entries[@]}" | fzf --prompt="Cursor instance: " | cut -f1) | |
| [[ -n "$pick" ]] && source "$_CURSOR_ENV_DIR/$pick" && echo "Using Cursor on port $pick" | |
| } | |
| # Strip env vars that make Claude think it's inside Cursor's terminal | |
| # (triggers a PID ancestry check that fails from tmux). | |
| claude() { | |
| VSCODE_GIT_ASKPASS_MAIN= \ | |
| VSCODE_GIT_ASKPASS_NODE= \ | |
| CURSOR_TRACE_ID= \ | |
| command claude "$@" | |
| } | |
| # Ensure `code` always resolves to the currently-selected Cursor instance's | |
| # remote CLI, even when the cursor-server path isn't in PATH. | |
| # Derives the binary location from BROWSER (set by the env restore above). | |
| # BROWSER = <server-root>/bin/helpers/browser.sh | |
| # code = <server-root>/bin/remote-cli/code | |
| code() { | |
| local cli="${BROWSER%/helpers/browser.sh}/remote-cli/code" | |
| if [[ -n "$BROWSER" && -x "$cli" ]]; then | |
| "$cli" "$@" | |
| else | |
| echo "code: Cursor remote CLI not found. Run cursor-select or open a Cursor terminal." >&2 | |
| return 1 | |
| fi | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment