From 121f5866bef9f980d559429d3d7e45b19520065b Mon Sep 17 00:00:00 2001 From: ZanzyTHEbar Date: Sat, 21 Mar 2026 15:25:32 +0000 Subject: [PATCH 1/4] fix: keep AppImage terminals resolving to the cursor shim Cursor-launched shells can put AppImage runtime mounts ahead of ~/.local/bin, which bypasses the shim and breaks agent and installer flows. Manage shell PATH setup and tighten shim detection so installs remain idempotent regardless of install order. --- README.md | 28 +++++--- cursor.sh | 6 ++ install.sh | 9 ++- lib.sh | 65 ++++++++++++++++- scripts/ensure-shell-path.sh | 132 +++++++++++++++++++++++++++++++++++ scripts/ensure-shim.sh | 10 ++- shell-path.sh | 23 ++++++ shim.sh | 98 +++++++++++++++++++++++--- uninstall.sh | 10 ++- 9 files changed, 355 insertions(+), 26 deletions(-) create mode 100644 scripts/ensure-shell-path.sh create mode 100644 shell-path.sh diff --git a/README.md b/README.md index 367d9fe..bc40b7e 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,8 @@ The one‑liner script will: 2. Download `lib.sh` to `~/.local/share/cursor-installer/lib.sh` 3. Make the script executable 4. Install a `cursor` shim at `~/.local/bin/cursor` (see [The `cursor` Shim](#the-cursor-shim)) -5. Download and install the latest version of Cursor +5. Install a managed shell startup hook for supported shells so `~/.local/bin` stays ahead of transient AppImage runtime paths +6. Download and install the latest version of Cursor **Note:** If you're installing via the piped bash method and don't have FUSE2 installed, the script will warn you but continue. You'll need to either: @@ -125,7 +126,8 @@ The uninstall script will: 1. Remove the `cursor-installer` script from `~/.local/bin/` 2. Remove the shared `lib.sh` from `~/.local/share/cursor-installer/` 3. Remove the Cursor AppImage -4. Ask if you want to remove the Cursor configuration files +4. Remove the managed shell PATH hook from supported shell startup files +5. Ask if you want to remove the Cursor configuration files **Note:** The `cursor` shim at `~/.local/bin/cursor` is not removed by the uninstall script. See [Removing the Shim](#removing-the-shim) for manual cleanup. @@ -211,12 +213,19 @@ The shim bridges that gap. It installs a lightweight script at `~/.local/bin/cur When you type `cursor`, the shim (`~/.local/bin/cursor`) follows a short resolution chain: -1. **Real Cursor binary found in PATH?** -- Forward all arguments to it (e.g. Cursor's official `cursor` CLI). -2. **`cursor agent` subcommand?** -- Delegate to `~/.local/bin/agent` if it exists. -3. **`cursor-installer` found?** -- Delegate to the installer CLI so commands like `cursor --update` still work. -4. **Nothing found** -- Print a helpful error with install instructions. +1. **`cursor agent` subcommand?** -- Delegate to `~/.local/bin/agent` if it exists. +2. **Installer-only flag?** -- Delegate to `cursor-installer` for commands like `cursor --update`, `cursor --check`, or `cursor --extract`. +3. **Stable Cursor binary found in PATH?** -- Forward all other arguments to it (e.g. Cursor's official `cursor` CLI). +4. **`cursor-installer` found?** -- Delegate to the installer CLI as a general fallback. +5. **Nothing found** -- Print a helpful error with install instructions. -The shim never hides a real Cursor binary; it only acts as a fallback. +The shim does not override a stable Cursor CLI, but it deliberately ignores transient AppImage mount paths under `/tmp/.mount_*` and normalizes duplicate path aliases so it cannot recurse back into itself. + +### AppImage Terminals + +When Cursor is launched from an AppImage, terminals opened inside Cursor may inherit a `PATH` where Cursor's transient runtime mount (`/tmp/.mount_*`) appears before `~/.local/bin`. That can bypass the shim entirely. + +To keep `cursor` resolving to the shim in supported shells, the installer manages a small startup hook that prepends `~/.local/bin` in interactive `bash` and `zsh` sessions. This keeps the shim available while still allowing it to delegate to a real Cursor CLI when appropriate. ### How It Works @@ -241,6 +250,7 @@ The shim is synced automatically during normal installer operations: - **`install.sh`** -- Copies `shim.sh` and `ensure-shim.sh` into `~/.local/share/cursor-installer/`, then runs `ensure-shim.sh`. - **`cursor-installer --update`** -- Re-downloads the latest shim assets from GitHub, then re-runs `ensure-shim.sh`. - **`cursor-installer` (install paths)** -- Runs `ensure-shim.sh` before each install to keep the shim current. +- **Shell PATH setup** -- Syncs `shell-path.sh` and `ensure-shell-path.sh`, then ensures supported shell startup files source the PATH helper. ### File Locations @@ -249,6 +259,8 @@ The shim is synced automatically during normal installer operations: | `~/.local/bin/cursor` | The shim (what you invoke). | | `~/.local/share/cursor-installer/shim.sh` | Cached copy of the shim source. | | `~/.local/share/cursor-installer/ensure-shim.sh` | Cached copy of the installer helper. | +| `~/.local/share/cursor-installer/shell-path.sh` | Shell snippet that prepends `~/.local/bin`. | +| `~/.local/share/cursor-installer/ensure-shell-path.sh` | Helper that updates supported shell startup files. | ### Removing the Shim @@ -264,7 +276,7 @@ If you only want to disable the shim without uninstalling the rest of the projec ## Note -If you encounter a warning that `~/.local/bin` is not in your PATH, you can add it by running: +If you encounter a warning that `~/.local/bin` is not in your PATH, or if `cursor` resolves to Cursor's transient AppImage runtime instead of the shim, prepend it by running: ```bash export PATH="$HOME/.local/bin:$PATH" diff --git a/cursor.sh b/cursor.sh index 3c7c12e..8ffdac9 100755 --- a/cursor.sh +++ b/cursor.sh @@ -290,6 +290,7 @@ EOF function install_cursor_extracted() { run_ensure_shim + run_ensure_shell_path local install_dir="$1" local release_track=${2:-stable} local temp_file @@ -440,6 +441,7 @@ function install_cursor_extracted() { function install_cursor() { run_ensure_shim + run_ensure_shell_path local install_dir="$1" local release_track=${2:-stable} # Default to stable if not specified @@ -666,6 +668,10 @@ EOF function update_cursor() { log_step "Updating Cursor..." refresh_shim_assets + refresh_shell_path_assets + run_ensure_shim + run_ensure_shell_path + warn_if_cursor_shadowed_by_appimage_runtime local current_appimage current_appimage=$(find_cursor_appimage || true) local install_dir diff --git a/install.sh b/install.sh index 707b5d0..3513cd1 100755 --- a/install.sh +++ b/install.sh @@ -94,8 +94,12 @@ chmod +x "$CLI_PATH" log_ok "Cursor installer script has been placed in $CLI_PATH" -log_step "Ensuring cursor shim..." -LOCAL_SHIM_PATH="$SCRIPT_DIR/shim.sh" LOCAL_SHIM_HELPER_PATH="$SCRIPT_DIR/scripts/ensure-shim.sh" sync_shim_assets && run_ensure_shim || log_warn "Shim update skipped or failed; continuing." +log_step "Ensuring cursor shim and shell PATH setup..." +LOCAL_SHIM_PATH="$SCRIPT_DIR/shim.sh" +LOCAL_SHIM_HELPER_PATH="$SCRIPT_DIR/scripts/ensure-shim.sh" +LOCAL_SHELL_PATH_SCRIPT="$SCRIPT_DIR/shell-path.sh" +LOCAL_SHELL_PATH_HELPER_PATH="$SCRIPT_DIR/scripts/ensure-shell-path.sh" +sync_shim_assets && sync_shell_path_assets && run_ensure_shim && run_ensure_shell_path || log_warn "Shim or shell PATH setup skipped or failed; continuing." # Check if ~/.local/bin is in PATH if [[ ":$PATH:" != *":$LOCAL_BIN:"* ]]; then @@ -103,6 +107,7 @@ if [[ ":$PATH:" != *":$LOCAL_BIN:"* ]]; then log_info "To add it, run this or add it to your shell profile:" log_info "export PATH=\"\$HOME/.local/bin:\$PATH\"" fi +warn_if_cursor_shadowed_by_appimage_runtime # Run cursor --update to download and install Cursor log_step "Downloading and installing Cursor ($INSTALL_MODE mode) from ${REPO_OWNER}/${REPO_NAME}@${REPO_BRANCH}..." diff --git a/lib.sh b/lib.sh index 81aed43..55abee4 100644 --- a/lib.sh +++ b/lib.sh @@ -114,8 +114,12 @@ BASE_RAW_URL="https://raw.githubusercontent.com/${REPO_OWNER}/${REPO_NAME}/${REP SHIM_TARGET="${SHIM_TARGET:-$HOME/.local/bin/cursor}" SHARED_SHIM="${LIB_DIR}/shim.sh" SHIM_HELPER="${LIB_DIR}/ensure-shim.sh" +SHELL_PATH_SCRIPT="${LIB_DIR}/shell-path.sh" +SHELL_PATH_HELPER="${LIB_DIR}/ensure-shell-path.sh" SHIM_URL="${BASE_RAW_URL}/shim.sh" SHIM_HELPER_URL="${BASE_RAW_URL}/scripts/ensure-shim.sh" +SHELL_PATH_SCRIPT_URL="${BASE_RAW_URL}/shell-path.sh" +SHELL_PATH_HELPER_URL="${BASE_RAW_URL}/scripts/ensure-shell-path.sh" LIB_URL="${BASE_RAW_URL}/lib.sh" CURSOR_SCRIPT_URL="${BASE_RAW_URL}/cursor.sh" @@ -137,6 +141,22 @@ function sync_shim_assets() { return 0 } +function sync_shell_path_assets() { + mkdir -p "$LIB_DIR" + if [ -n "${LOCAL_SHELL_PATH_SCRIPT:-}" ] && [ -f "$LOCAL_SHELL_PATH_SCRIPT" ]; then + cp "$LOCAL_SHELL_PATH_SCRIPT" "$SHELL_PATH_SCRIPT" + elif [ ! -f "$SHELL_PATH_SCRIPT" ]; then + curl -fsSL "$SHELL_PATH_SCRIPT_URL" -o "$SHELL_PATH_SCRIPT" || { log_warn "Failed to download shell-path.sh"; return 1; } + fi + if [ -n "${LOCAL_SHELL_PATH_HELPER_PATH:-}" ] && [ -f "$LOCAL_SHELL_PATH_HELPER_PATH" ]; then + cp "$LOCAL_SHELL_PATH_HELPER_PATH" "$SHELL_PATH_HELPER" + elif [ ! -f "$SHELL_PATH_HELPER" ]; then + curl -fsSL "$SHELL_PATH_HELPER_URL" -o "$SHELL_PATH_HELPER" || { log_warn "Failed to download ensure-shell-path.sh"; return 1; } + fi + chmod +x "$SHELL_PATH_HELPER" "$SHELL_PATH_SCRIPT" 2>/dev/null || true + return 0 +} + # Refresh shim assets from GitHub (used on cursor-installer --update). function refresh_shim_assets() { log_step "Refreshing cursor shim assets..." @@ -152,11 +172,52 @@ function refresh_shim_assets() { chmod +x "$SHIM_HELPER" "$SHARED_SHIM" 2>/dev/null || true } +function refresh_shell_path_assets() { + log_step "Refreshing shell PATH assets..." + mkdir -p "$LIB_DIR" + if ! curl -fsSL "$SHELL_PATH_SCRIPT_URL" -o "$SHELL_PATH_SCRIPT"; then + log_warn "Failed to download shell-path.sh; continuing." + return 0 + fi + if ! curl -fsSL "$SHELL_PATH_HELPER_URL" -o "$SHELL_PATH_HELPER"; then + log_warn "Failed to download ensure-shell-path.sh; continuing." + return 0 + fi + chmod +x "$SHELL_PATH_HELPER" "$SHELL_PATH_SCRIPT" 2>/dev/null || true +} + # Run ensure-shim.sh with canonical SOURCE_SHIM and TARGET_SHIM. function run_ensure_shim() { - if [ ! -x "$SHIM_HELPER" ] && [ ! -f "$SHIM_HELPER" ]; then + if [ ! -f "$SHIM_HELPER" ]; then log_info "Shim helper not found; skipping shim update." return 0 fi - SOURCE_SHIM="$SHARED_SHIM" TARGET_SHIM="$SHIM_TARGET" "$SHIM_HELPER" || { log_warn "Shim update failed; continuing."; return 0; } + SOURCE_SHIM="$SHARED_SHIM" TARGET_SHIM="$SHIM_TARGET" sh "$SHIM_HELPER" || { log_warn "Shim update failed; continuing."; return 0; } +} + +function run_ensure_shell_path() { + if [ ! -f "$SHELL_PATH_HELPER" ] || [ ! -f "$SHELL_PATH_SCRIPT" ]; then + log_info "Shell PATH helper not found; skipping shell PATH setup." + return 0 + fi + SHELL_PATH_SCRIPT="$SHELL_PATH_SCRIPT" sh "$SHELL_PATH_HELPER" || { log_warn "Shell PATH setup failed; continuing."; return 0; } +} + +function run_remove_shell_path() { + if [ ! -f "$SHELL_PATH_HELPER" ]; then + return 0 + fi + SHELL_PATH_SCRIPT="$SHELL_PATH_SCRIPT" sh "$SHELL_PATH_HELPER" --remove || { log_warn "Shell PATH cleanup failed; continuing."; return 0; } +} + +function warn_if_cursor_shadowed_by_appimage_runtime() { + local resolved_cursor + resolved_cursor=$(command -v cursor 2>/dev/null || true) + + case "$resolved_cursor" in + /tmp/.mount_*) + log_warn "The current shell resolves 'cursor' to Cursor's AppImage runtime path." + log_info "Open a new terminal or source your shell startup file so ~/.local/bin takes precedence." + ;; + esac } diff --git a/scripts/ensure-shell-path.sh b/scripts/ensure-shell-path.sh new file mode 100644 index 0000000..388af6d --- /dev/null +++ b/scripts/ensure-shell-path.sh @@ -0,0 +1,132 @@ +#!/bin/sh +# Ensure supported interactive shells source cursor-installer's PATH helper. +set -eu + +ACTION="${1:-ensure}" +LIB_DIR="${HOME}/.local/share/cursor-installer" +SHELL_PATH_SCRIPT="${SHELL_PATH_SCRIPT:-$LIB_DIR/shell-path.sh}" +START_MARKER="# >>> cursor-installer path >>>" +END_MARKER="# <<< cursor-installer path <<<" + +build_source_block() { + cat <&2 + return 1 + ;; + esac +} + +strip_managed_block() { + file="$1" + tmp="$2" + + if [ -f "$file" ]; then + awk -v start="$START_MARKER" -v end="$END_MARKER" ' + $0 == start { skip = 1; next } + skip && $0 == end { skip = 0; next } + !skip { print } + ' "$file" > "$tmp" + else + : > "$tmp" + fi +} + +ensure_block() { + file="$1" + tmp=$(mktemp) + mkdir -p "$(dirname "$file")" + strip_managed_block "$file" "$tmp" + + if [ -s "$tmp" ]; then + printf '\n' >> "$tmp" + fi + + build_source_block >> "$tmp" + + if [ -f "$file" ] && cmp -s "$tmp" "$file"; then + rm -f "$tmp" + echo "Shell PATH setup already present in $file" + return 0 + fi + + mv "$tmp" "$file" + echo "Ensured shell PATH setup in $file" +} + +remove_block() { + file="$1" + [ -f "$file" ] || return 0 + + tmp=$(mktemp) + strip_managed_block "$file" "$tmp" + + if cmp -s "$tmp" "$file"; then + rm -f "$tmp" + return 0 + fi + + mv "$tmp" "$file" + echo "Removed shell PATH setup from $file" +} + +if [ "$ACTION" = "ensure" ] && [ ! -f "$SHELL_PATH_SCRIPT" ]; then + echo "Error: shell-path.sh source not found at $SHELL_PATH_SCRIPT" >&2 + exit 1 +fi + +if ! target_files=$(print_target_files); then + exit 0 +fi + +printf '%s\n' "$target_files" | while IFS= read -r file; do + [ -n "$file" ] || continue + + case "$ACTION" in + ensure) + ensure_block "$file" + ;; + --remove|remove) + remove_block "$file" + ;; + *) + echo "Unknown action: $ACTION" >&2 + exit 1 + ;; + esac +done diff --git a/scripts/ensure-shim.sh b/scripts/ensure-shim.sh index 9d805f7..fc1ebf6 100644 --- a/scripts/ensure-shim.sh +++ b/scripts/ensure-shim.sh @@ -5,6 +5,7 @@ set -eu TARGET_SHIM="${TARGET_SHIM:-$HOME/.local/bin/cursor}" SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) LIB_DIR="${HOME}/.local/share/cursor-installer" +SHIM_MARKER="cursor-linux-installer-shim" SOURCE_SHIM="${SOURCE_SHIM:-}" if [ -z "$SOURCE_SHIM" ]; then @@ -36,12 +37,17 @@ is_shim() { return 1 ;; esac - if grep -q "Find cursor executable in PATH" "$file" 2>/dev/null; then + + if grep -Fq "$SHIM_MARKER" "$file" 2>/dev/null; then return 0 fi - if grep -q "cursor-installer" "$file" 2>/dev/null; then + + if grep -Fq "Find cursor executable in PATH" "$file" 2>/dev/null && + grep -Fq 'AGENT_BIN="$HOME/.local/bin/agent"' "$file" 2>/dev/null && + grep -Fq 'Install/update with: cursor-installer --update [stable|latest]' "$file" 2>/dev/null; then return 0 fi + return 1 } diff --git a/shell-path.sh b/shell-path.sh new file mode 100644 index 0000000..e8ef79c --- /dev/null +++ b/shell-path.sh @@ -0,0 +1,23 @@ +#!/bin/sh +# cursor-linux-installer-path + +cursor_installer_local_bin="$HOME/.local/bin" + +if [ -d "$cursor_installer_local_bin" ]; then + cursor_installer_filtered_path=$( + printf '%s' "${PATH:-}" | + awk -v RS=: -v ORS=: -v skip="$cursor_installer_local_bin" '$0 != skip { print }' | + sed 's/:$//' + ) + + if [ -n "$cursor_installer_filtered_path" ]; then + PATH="$cursor_installer_local_bin:$cursor_installer_filtered_path" + else + PATH="$cursor_installer_local_bin" + fi + + export PATH +fi + +unset cursor_installer_local_bin +unset cursor_installer_filtered_path diff --git a/shim.sh b/shim.sh index 001bf49..af82171 100644 --- a/shim.sh +++ b/shim.sh @@ -1,33 +1,95 @@ #!/bin/sh set -eu +# cursor-linux-installer-shim # Find cursor executable in PATH, excluding the current shim +canonicalize_path() { + path="$1" + + if command -v realpath >/dev/null 2>&1; then + realpath "$path" 2>/dev/null && return 0 + fi + + if command -v readlink >/dev/null 2>&1; then + readlink -f "$path" 2>/dev/null && return 0 + fi + + case "$path" in + */*) + dir_part=${path%/*} + base_part=${path##*/} + ;; + *) + dir_part=. + base_part=$path + ;; + esac + + old_pwd=$(pwd) + if cd "$dir_part" 2>/dev/null; then + resolved_dir=$(pwd -P) + cd "$old_pwd" || exit 1 + printf '%s/%s\n' "$resolved_dir" "$base_part" + return 0 + fi + + cd "$old_pwd" || exit 1 + printf '%s\n' "$path" +} + +same_path() { + left=$(canonicalize_path "$1" || printf '%s\n' "$1") + right=$(canonicalize_path "$2" || printf '%s\n' "$2") + [ "$left" = "$right" ] +} + +is_ignored_cursor_path() { + case "$1" in + # Ignore transient AppImage runtime mounts; they are not stable CLI installs + # and can shadow the shim inside terminals launched from Cursor itself. + /tmp/.mount_*) + return 0 + ;; + esac + + return 1 +} + +SHIM_PATH=$(canonicalize_path "$HOME/.local/bin/cursor" || printf '%s\n' "$HOME/.local/bin/cursor") +case "${0:-}" in + */*) + SHIM_PATH=$(canonicalize_path "$0" || printf '%s\n' "$0") + ;; +esac + find_cursor() { old_IFS="$IFS" IFS=: for dir in $PATH; do [ -n "$dir" ] || continue cursor_path="$dir/cursor" - if [ "$cursor_path" != "$HOME/.local/bin/cursor" ] && [ -x "$cursor_path" ]; then - IFS="$old_IFS" - echo "$cursor_path" - return 0 + [ -x "$cursor_path" ] || continue + + if is_ignored_cursor_path "$cursor_path"; then + continue fi + + if same_path "$cursor_path" "$SHIM_PATH"; then + continue + fi + + IFS="$old_IFS" + echo "$cursor_path" + return 0 done IFS="$old_IFS" return 1 } -OTHER_CURSOR=$(find_cursor || true) +first_arg="${1:-}" CURSOR_INSTALLER=$(command -v cursor-installer 2>/dev/null || true) AGENT_BIN="$HOME/.local/bin/agent" -if [ -n "${OTHER_CURSOR:-}" ]; then - exec "$OTHER_CURSOR" "$@" -fi - -first_arg="${1:-}" - if [ "$first_arg" = "agent" ]; then if [ -x "$AGENT_BIN" ]; then exec "$AGENT_BIN" "$@" @@ -36,6 +98,20 @@ if [ "$first_arg" = "agent" ]; then exit 1 fi +case "$first_arg" in + --update|-u|--check|-c|--extract|--no-fuse|--reinstall-desktop) + if [ -n "${CURSOR_INSTALLER:-}" ]; then + exec "$CURSOR_INSTALLER" "$@" + fi + ;; +esac + +OTHER_CURSOR=$(find_cursor || true) + +if [ -n "${OTHER_CURSOR:-}" ]; then + exec "$OTHER_CURSOR" "$@" +fi + if [ -n "${CURSOR_INSTALLER:-}" ]; then exec "$CURSOR_INSTALLER" "$@" fi diff --git a/uninstall.sh b/uninstall.sh index 859c21b..9213a73 100755 --- a/uninstall.sh +++ b/uninstall.sh @@ -48,7 +48,15 @@ fi log_step "Removing cursor-installer script..." safe_remove "$CLI_PATH" "cursor-installer script" -# Remove shared lib (installed by installer) +# Remove managed shell PATH setup before deleting helper assets +log_step "Removing managed shell PATH setup..." +run_remove_shell_path + +# Remove shared support assets (installed by installer) +safe_remove "$SHARED_SHIM" "cursor shim source" +safe_remove "$SHIM_HELPER" "cursor shim helper" +safe_remove "$SHELL_PATH_SCRIPT" "shell PATH helper script" +safe_remove "$SHELL_PATH_HELPER" "shell PATH helper" safe_remove "$SHARED_LIB" "cursor-installer lib" if [ -d "$LIB_DIR" ] && [ -z "$(ls -A "$LIB_DIR")" ]; then rmdir "$LIB_DIR" 2>/dev/null || true From 89455866f2f29d2691fcbf032a87789fcf8278de Mon Sep 17 00:00:00 2001 From: ZanzyTHEbar Date: Sat, 21 Mar 2026 16:18:38 +0000 Subject: [PATCH 2/4] fix: preserve symlinked shell rc files Update the PATH hook helper in place when an rc file already exists so dotfile-managed symlinks keep pointing at their original targets during install and removal. --- scripts/ensure-shell-path.sh | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/scripts/ensure-shell-path.sh b/scripts/ensure-shell-path.sh index 388af6d..0d03d38 100644 --- a/scripts/ensure-shell-path.sh +++ b/scripts/ensure-shell-path.sh @@ -67,6 +67,19 @@ strip_managed_block() { fi } +write_updated_file() { + tmp="$1" + file="$2" + + if [ -e "$file" ] || [ -L "$file" ]; then + cat "$tmp" > "$file" + rm -f "$tmp" + return 0 + fi + + mv "$tmp" "$file" +} + ensure_block() { file="$1" tmp=$(mktemp) @@ -85,7 +98,7 @@ ensure_block() { return 0 fi - mv "$tmp" "$file" + write_updated_file "$tmp" "$file" echo "Ensured shell PATH setup in $file" } @@ -101,7 +114,7 @@ remove_block() { return 0 fi - mv "$tmp" "$file" + write_updated_file "$tmp" "$file" echo "Removed shell PATH setup from $file" } From 70840f4aa8a899519ac8e5e3da0a056e0edac3ea Mon Sep 17 00:00:00 2001 From: ZanzyTHEbar Date: Sat, 21 Mar 2026 17:14:50 +0000 Subject: [PATCH 3/4] fix: strip the routed agent subcommand The shim should route `cursor agent ...` into the agent binary without forwarding the `agent` dispatcher token itself. Shift the subcommand before exec so the agent CLI receives only its own arguments. --- shim.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/shim.sh b/shim.sh index af82171..f9c39b8 100644 --- a/shim.sh +++ b/shim.sh @@ -92,6 +92,7 @@ AGENT_BIN="$HOME/.local/bin/agent" if [ "$first_arg" = "agent" ]; then if [ -x "$AGENT_BIN" ]; then + shift exec "$AGENT_BIN" "$@" fi echo "Error: Cursor agent not found at $AGENT_BIN" 1>&2 From 4fc839499705934b812eff6586e88db012e433b2 Mon Sep 17 00:00:00 2001 From: ZanzyTHEbar Date: Sat, 21 Mar 2026 17:15:01 +0000 Subject: [PATCH 4/4] fix: keep shell PATH setup idempotent Repeated installs should not rewrite shell rc files with extra blank lines or lose track of the intended destination variables. Trim trailing separator whitespace before re-appending the managed block and avoid leaking helper-local variable names across functions. --- scripts/ensure-shell-path.sh | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/scripts/ensure-shell-path.sh b/scripts/ensure-shell-path.sh index 0d03d38..aacafba 100644 --- a/scripts/ensure-shell-path.sh +++ b/scripts/ensure-shell-path.sh @@ -67,17 +67,37 @@ strip_managed_block() { fi } +trim_trailing_blank_lines() { + trim_file="$1" + trimmed_file=$(mktemp) + + awk ' + { lines[NR] = $0 } + END { + last = NR + while (last > 0 && lines[last] ~ /^[[:space:]]*$/) { + last-- + } + for (i = 1; i <= last; i++) { + print lines[i] + } + } + ' "$trim_file" > "$trimmed_file" + + mv "$trimmed_file" "$trim_file" +} + write_updated_file() { - tmp="$1" - file="$2" + source_tmp="$1" + destination_file="$2" - if [ -e "$file" ] || [ -L "$file" ]; then - cat "$tmp" > "$file" - rm -f "$tmp" + if [ -e "$destination_file" ] || [ -L "$destination_file" ]; then + cat "$source_tmp" > "$destination_file" + rm -f "$source_tmp" return 0 fi - mv "$tmp" "$file" + mv "$source_tmp" "$destination_file" } ensure_block() { @@ -85,6 +105,7 @@ ensure_block() { tmp=$(mktemp) mkdir -p "$(dirname "$file")" strip_managed_block "$file" "$tmp" + trim_trailing_blank_lines "$tmp" if [ -s "$tmp" ]; then printf '\n' >> "$tmp"