🛠️ Guide: Fix Cursor Agent Terminal Hangs Caused by .zshrc

Finally I test this method works, and will test for a while to double confirm it works for long time or not.

ref: Manual installation

enable:
"terminal.integrated.shellIntegration.enabled": true
and
cat /Users/lqiao/.config/fish/conf.d/cursor.fish
string match -q "$TERM_PROGRAM" "vscode"
and . (cursor --locate-shell-integration-path fish)

Now both local macOS and remote macOS, fish shell, after run terminal in cursor chat, the LLM will detect the finish status and reply/summarize to me.

run `ls -l; env|grep -iE 'COMPOSER_NO_INTERACTION|PAGER|VSCODE_INJECTION'` in terminal, then put the original env list to me.

total 8
...
PAGER=head -n 10000 | cat
COMPOSER_NO_INTERACTION=1

I guess maybe zsh user can try to use (replace official code to cursor to test, or other shell user to test according to official doc.

[[ "$TERM_PROGRAM" == "vscode" ]] && . "$(cursor --locate-shell-integration-path zsh)"

Finally I found Kiro did it with right way: in config.fish
string match -q "$TERM_PROGRAM" "kiro" and . (kiro --locate-shell-integration-path fish)

cat ~/.config/fish/conf.d/vsc-term-shell-integration.fish 
# string match -q "$TERM_PROGRAM" "vscode" and . (cursor --locate-shell-integration-path fish)
# string match -q "$TERM_PROGRAM" "kiro"   and . (kiro   --locate-shell-integration-path fish)

# List of IDE commands to check for
set -l ide_cmds "cursor" "kiro" "windsurf" "cascade"
# Corresponding TERM_PROGRAM patterns for each command (space-separated)
set -l term_program_env_names "vscode" "cursor" "kiro" "windsurf" "cascade"

for i in (seq (count $ide_cmds))
    set -l cmd $ide_cmds[$i]
    if command -v $cmd >/dev/null
        # Get the patterns for the current command and split them
        set -l term_name (string split ' ' $term_program_env_names[$i])

        # Check if TERM_PROGRAM matches any of the patterns for the command
        string match -q -- "$TERM_PROGRAM" $term_name
        and . ($cmd --locate-shell-integration-path fish)
        and break
    end
end

for zsh users:

[[ “$CURSOR_AGENT” == “1” ]] && . “$(cursor --locate-shell-integration-path zsh)”

works great. thanks for the pointer.

I’ve tried to add the fix to ~/.zprofile as well now and it has not helped. I am not sure what you mean when you say nothing overrides $PAGER further down the config, which config are you referring to?

what to do on WSL?
Also, you said “Run this inside Cursor’s Agent terminal”
for the life of me, i have no idea how to do that.

try vscode official doc: ref: Manual installation

according to your shell, like bash/zsh/fish/pwsh etc. vscode provide solution.

Hi @Gulzar the suggestions in this thread focus on Mac.

We will release an update soon that should resolve your issue as well.

I really don’t like this answer.

Please be aware that if you do this, all of your custom environment from .zshrc won’t be loaded after the return. So no aliases, custom environment exports etc.

This goes more to opinion territory, but it’s much better to have your Cursor agent have as close to the same environment as you have in your interactive shell, and these days most people just dump all of their shell setup into the .zshrc file rather than splitting it out (see below pedantic approach). This means you don’t need to waste time asking Cursor to figure out why you’re on a different asdf version or a tool or anything like that.

Alternative solution for the most common issue (interactive OMZ update prompt):

  • If you have Oh My ZSH set up then you’re likely getting the terminal hanging on the update prompt for that. What happens is that you .zshrc fully loads, and then the “Please update OMZ” prompt runs at the end.
    • Alternatively, any other interactive prompting in your .zshrc could do this too.
  • Since OMZ is so rarely updated with anything you care about, just disable it with this anywhere in you .zshrc:
DISABLE_UPDATE_PROMPT=true
  • Note that the update prompt runs in the same environment, after .zshrc was sourced, so you don’t even need to export it.
  • You can update it by hand whenever you want with omz update.

More comprehensive, pedantic solution:

Technically, following z-shell semantics, .zshrc shouldn’t be sourced by Cursor because it’s intended for interactive shells, whereas Cursor is trying to use the shell in a non-interactive manner.

There are multiple initialization files you can use:

  • .zshenv: Move any common environment setup here for all shells, login or otherwise. This is sourced for all shells.
  • .zprofile: Move your user’s login setup here. E.g. any $PATH adjustments. This is sourced for all login shells and Cursor should source it.
  • .zshrc: Add the early return logic here from the above answer. “RC” = “Run Commands” — this file should be sourced only by interactive shells. Cursor should not source it but it does, so you need to force it not to load.
  • .zshlogin: Listed for the pedantic sake of completion, this gets sourced after the login process is complete, so if you care about specific timing, it’s an option. Typically, just use .zprofile though.
  • .zshlogout: Listed for the pedantic sake of completion, this gets sourced when you exit a login shell. I guess you could put a cleanup task here.
2 Likes