fix: poll for claude readiness before injecting prompt into tmux
Replace fixed sleep(3) + paste-buffer race with a wait_for_claude_ready() function that polls the tmux pane for the ❯ prompt (up to 120s). This fixes the bug where the initial prompt was pasted before Claude Code finished initializing, resulting in a stuck session with an empty prompt. Observed on issue #81: session sat idle for 42+ minutes because the paste arrived during Claude's startup splash screen. Changes: - Add wait_for_claude_ready() that polls tmux capture-pane for ❯ - Call it inside inject_into_session() before every paste - Use inject_into_session() for initial prompt (was inline paste-buffer) - Remove fixed sleep(3) from session creation and recovery paths - Fail hard if claude doesn't become ready within timeout Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
ee881086d3
commit
515c528e10
1 changed files with 27 additions and 14 deletions
|
|
@ -79,9 +79,25 @@ read_phase() {
|
||||||
{ cat "$PHASE_FILE" 2>/dev/null || true; } | head -1 | tr -d '[:space:]'
|
{ cat "$PHASE_FILE" 2>/dev/null || true; } | head -1 | tr -d '[:space:]'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wait_for_claude_ready() {
|
||||||
|
local timeout="${1:-120}"
|
||||||
|
local elapsed=0
|
||||||
|
while [ "$elapsed" -lt "$timeout" ]; do
|
||||||
|
# Claude Code shows ❯ when ready for input
|
||||||
|
if tmux capture-pane -t "${SESSION_NAME}" -p 2>/dev/null | grep -q '❯'; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
sleep 2
|
||||||
|
elapsed=$((elapsed + 2))
|
||||||
|
done
|
||||||
|
log "WARNING: claude not ready after ${timeout}s — proceeding anyway"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
inject_into_session() {
|
inject_into_session() {
|
||||||
local text="$1"
|
local text="$1"
|
||||||
local tmpfile
|
local tmpfile
|
||||||
|
wait_for_claude_ready 120
|
||||||
tmpfile=$(mktemp /tmp/tmux-inject-XXXXXX)
|
tmpfile=$(mktemp /tmp/tmux-inject-XXXXXX)
|
||||||
printf '%s' "$text" > "$tmpfile"
|
printf '%s' "$text" > "$tmpfile"
|
||||||
tmux load-buffer -b "inject-${ISSUE}" "$tmpfile"
|
tmux load-buffer -b "inject-${ISSUE}" "$tmpfile"
|
||||||
|
|
@ -811,9 +827,6 @@ if ! tmux has-session -t "${SESSION_NAME}" 2>/dev/null; then
|
||||||
tmux new-session -d -s "${SESSION_NAME}" -c "${WORKTREE}" \
|
tmux new-session -d -s "${SESSION_NAME}" -c "${WORKTREE}" \
|
||||||
"claude --dangerously-skip-permissions"
|
"claude --dangerously-skip-permissions"
|
||||||
|
|
||||||
# Wait for Claude to initialize
|
|
||||||
sleep 3
|
|
||||||
|
|
||||||
if ! tmux has-session -t "${SESSION_NAME}" 2>/dev/null; then
|
if ! tmux has-session -t "${SESSION_NAME}" 2>/dev/null; then
|
||||||
log "ERROR: failed to create tmux session ${SESSION_NAME}"
|
log "ERROR: failed to create tmux session ${SESSION_NAME}"
|
||||||
cleanup_labels
|
cleanup_labels
|
||||||
|
|
@ -821,20 +834,21 @@ if ! tmux has-session -t "${SESSION_NAME}" 2>/dev/null; then
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
log "tmux session created: ${SESSION_NAME}"
|
log "tmux session created: ${SESSION_NAME}"
|
||||||
|
|
||||||
|
# Wait for Claude to be ready (polls for ❯ prompt)
|
||||||
|
if ! wait_for_claude_ready 120; then
|
||||||
|
log "ERROR: claude did not become ready in ${SESSION_NAME}"
|
||||||
|
kill_tmux_session
|
||||||
|
cleanup_labels
|
||||||
|
cleanup_worktree
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
log "reusing existing tmux session: ${SESSION_NAME}"
|
log "reusing existing tmux session: ${SESSION_NAME}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Send initial prompt via paste buffer (handles long text and special chars)
|
# Send initial prompt (inject_into_session waits for claude to be ready)
|
||||||
PROMPT_TMPFILE=$(mktemp /tmp/dev-prompt-XXXXXX)
|
inject_into_session "$INITIAL_PROMPT"
|
||||||
printf '%s' "$INITIAL_PROMPT" > "$PROMPT_TMPFILE"
|
|
||||||
tmux load-buffer -b "prompt-${ISSUE}" "$PROMPT_TMPFILE"
|
|
||||||
tmux paste-buffer -t "${SESSION_NAME}" -b "prompt-${ISSUE}"
|
|
||||||
sleep 1
|
|
||||||
tmux send-keys -t "${SESSION_NAME}" "" Enter
|
|
||||||
tmux delete-buffer -b "prompt-${ISSUE}" 2>/dev/null || true
|
|
||||||
rm -f "$PROMPT_TMPFILE"
|
|
||||||
|
|
||||||
log "initial prompt sent to tmux session"
|
log "initial prompt sent to tmux session"
|
||||||
|
|
||||||
# Signal to dev-poll.sh that we're running (session is up)
|
# Signal to dev-poll.sh that we're running (session is up)
|
||||||
|
|
@ -882,7 +896,6 @@ Phase file: ${PHASE_FILE}"
|
||||||
|
|
||||||
if tmux new-session -d -s "${SESSION_NAME}" -c "${WORKTREE}" \
|
if tmux new-session -d -s "${SESSION_NAME}" -c "${WORKTREE}" \
|
||||||
"claude --dangerously-skip-permissions" 2>/dev/null; then
|
"claude --dangerously-skip-permissions" 2>/dev/null; then
|
||||||
sleep 3
|
|
||||||
inject_into_session "$RECOVERY_MSG"
|
inject_into_session "$RECOVERY_MSG"
|
||||||
log "recovery session started"
|
log "recovery session started"
|
||||||
IDLE_ELAPSED=0
|
IDLE_ELAPSED=0
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue