iTranslated by AI
Fixing Claude Code Execution Hangs in Cursor: A Wrapper Script Solution
For those who just want the conclusion
- Running
claude -pvia Cursor Agent from the chat hangs indefinitely. - It works with a pipe (
echo ... | claude) → Behavior changes depending on whether stdout/stderr is a TTY. - Solved with a wrapper that uses
nohup+ output redirection to point the process output to a file. - Wrapper script code (same as the sample in this article).
Background: What I wanted to achieve
Cursor Agent and Claude Code operate with different models and contexts. If so, by having the Agent in a Cursor chat thread run claude -p when told "ask Claude Code about this," I should be able to create a pseudo-multi-agent configuration.
For example:
- Assigning code implementation to Cursor Agent while asking Claude Code for reviews or second opinions.
- Persisting context on the Claude Code side with
-c(session continuation) and submitting tasks incrementally.
Of course, it works if you manually open a terminal pane in Cursor and run claude -p. However, that doesn't allow for seamless collaboration within the conversation flow with the Cursor Agent. My intention was to have the Agent execute it via the terminal to get a second opinion or a review.
While trying to verify this, I ran into an issue where I couldn't receive a response from claude -p at all when executed via the Agent.
Environment
| Item | Version |
|---|---|
| macOS | 15.7.3 (24G419) |
| Cursor | 2.4.28 (arm64) |
| Claude CLI | 2.1.31 (Claude Code) |
| Node.js | 18.17.1 |
What Happens
When you have Cursor Agent execute claude -p in the terminal, it hangs without returning any output. The same behavior has been confirmed when running it directly from the cursor-agent command (Cursor CLI).
# When running via terminal execution from Cursor Agent in chat
$ claude -p "Hello, respond with OK"
# → No output after waiting more than 60 seconds. Only option is to kill.
# Similar behavior when running directly via cursor-agent command
$ cursor-agent claude -p "Hello, respond with OK"
# → Hangs in the same way
On the other hand, it works normally when passed from standard input via a pipe.
$ echo "Hello, respond with OK" | claude --output-format text
OK
# → Responds in about 5 seconds
If you run it directly from an external terminal (such as WezTerm), -p works without any problems.
Why it works with a pipe
It is likely that the input detection logic on the CLI side differs between the -p flag and a pipe.
-
Pipe: stdin is detected as a pipe via
isatty(0), etc. → Reads purely as a stream. -
-pflag: stdin remains a TTY, and it switches to non-interactive mode via the flag → TTY control setup is executed.
The terminal operated by Cursor Agent (and the cursor-agent command) runs in a PTY environment that differs from a normal one, leading to the assumption that a TTY control conflict is occurring in the latter path.
Solution: nohup wrapper
Redirect stdout/stderr of the process to a file using output redirection to bypass TTY detection on the CLI side.
Script
#!/bin/bash
# claude-safe - A wrapper to safely execute Claude CLI via Cursor Agent
#
# Usage:
# claude-safe -p "prompt" --output-format text
# claude-safe -c -p "session continuation"
# DEBUG=1 claude-safe -p "with debug output"
set -euo pipefail
# Debug mode
DEBUG=${DEBUG:-0}
debug_log() {
if [ "$DEBUG" = "1" ]; then
echo "[DEBUG] $*" >&2
fi
d}
# Output file
OUTPUT_DIR="${HOME}/.claude_wrapper"
mkdir -p "$OUTPUT_DIR"
OUTPUT_FILE="${OUTPUT_DIR}/output_$$.txt"
ERROR_FILE="${OUTPUT_DIR}/error_$$.txt"
# Cleanup
cleanup() {
debug_log "Cleaning up..."
rm -f "$OUTPUT_FILE" "$ERROR_FILE"
}
trap cleanup EXIT
debug_log "Running: claude $*"
debug_log "Output file: $OUTPUT_FILE"
# Run Claude (process isolation - macOS compatible)
# Process isolation via nohup and background execution
nohup claude "$@" > "$OUTPUT_FILE" 2> "$ERROR_FILE" &
CLAUDE_PID=$!
# Wait for process completion (capture exit code while avoiding immediate termination with set -e)
wait $CLAUDE_PID && EXIT_CODE=0 || EXIT_CODE=$?
debug_log "Exit code: $EXIT_CODE"
# Output
cat "$OUTPUT_FILE"
if [ -s "$ERROR_FILE" ]; then
cat "$ERROR_FILE" >&2
fi
exit $EXIT_CODE
Setup
# Place the script (anywhere in your PATH)
cp claude-safe ~/.local/bin/claude-safe
chmod +x ~/.local/bin/claude-safe
Why this works
Cursor Agent terminal (unique PTY environment)
└─ claude-safe (wrapper)
└─ nohup claude ... > file 2> file & (stdout/stderr to file → non-TTY determination)
└─ API call → Write to temporary file
└─ wait → cat → Display to standard output
The key point here is the output redirection using > "$OUTPUT_FILE" 2> "$ERROR_FILE". By directing Claude CLI's stdout/stderr to a file instead of a TTY, the CLI's TTY detection (e.g., isatty()) determines it is "non-TTY," skipping the TTY control setup. This is the same logic as why it works with a pipe.
While nohup itself is a command to make the process ignore SIGHUP signals and isn't primarily for TTY detachment, the core effective factor here is making it non-TTY through redirection. Strictly speaking, it might work with just redirection + background execution without nohup, but I've included it for safety, including HUP resistance.
Note that it hasn't been confirmed which file descriptor (stdin/stdout/stderr) the CLI's internal TTY check is performed against. The workaround in this article is a practical one that proved effective in this environment.
Operation Verification
Here are the comparison results via the terminal running within a Cursor thread (verified for reproducibility across multiple runs).
❌ claude -p (Hangs)
$ claude -p "Hello! This is a connectivity test. Please respond with OK."
# → No output after 90 seconds. Forced termination with kill.
✅ Via claude-safe (Normal operation)
$ claude-safe -p "Hello! This is a connectivity test. Please respond with OK."
OK - Claude Opus 4.5
# → Responds in about 6 seconds, exit code 0
Comparison
| Execution Method | Execution Environment | Result | Time Taken |
|---|---|---|---|
claude -p "..." |
Via Cursor Agent | ❌ Hangs | Over 60 sec (no timeout) |
echo "..." | claude --output-format text |
Via Cursor Agent | ✅ Works | Approx. 5 sec |
claude-safe -p "..." |
Via Cursor Agent | ✅ Works | Approx. 6 sec |
claude -p "..." |
External Terminal (WezTerm) | ✅ Works | Approx. 5 sec |
While it also works via a pipe, I believe the wrapper approach is more practical as it allows you to use the -p suite of options (such as session continuation with -c, --allowedTools, etc.) as they are.
Conclusion
- The reason
claude -phangs via Cursor Agent is likely due to TTY/PTY control conflicts (speculative). - It can be solved by using a wrapper that redirects output to a file, thereby bypassing the CLI's TTY detection.
- The essence of the workaround is the non-TTY status achieved through redirection, not
nohupitself. - The fact that it works via a pipe supports the hypothesis that TTY control is the underlying cause.
I hope this serves as a helpful reference for anyone looking to try something similar.
Discussion