Skip to content

Instantly share code, notes, and snippets.

@kuhar
Last active February 6, 2026 13:57
Show Gist options
  • Select an option

  • Save kuhar/50940e8613a8f70ed9a2e38b888e2d04 to your computer and use it in GitHub Desktop.

Select an option

Save kuhar/50940e8613a8f70ed9a2e38b888e2d04 to your computer and use it in GitHub Desktop.
Cursor IDE <-> tmux integration for Claude Code CLI
# 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