fix(@scripts/session-tools): 🐛 prevent tmux session crashes on claude exits

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Natalie 2026-05-20 18:25:37 -07:00
parent 0ed5118953
commit f50a90869a

View file

@ -1569,8 +1569,16 @@ build_inner() {
_mcp_esc=$(printf %s "$RCLAUDE_MCP_CONFIG" | sed "s/'/'\\\\''/g")
_mcp_flag="--mcp-config '${_mcp_esc}'"
fi
# Strip rclaude's one-shot directive vars from the spawned session's
# environment. Without this they leak into `claude` and everything it
# later spawns (Bash tool shells, nested `rclaude` calls): a session
# started via `rclaude resume` would carry RCLAUDE_RESUME_ID forever,
# so every `rclaude` invoked from inside it re-resumes that stale
# session instead of starting fresh. Persistent prefs (RCLAUDE_TRIAGE*,
# RCLAUDE_HOSTS, RCLAUDE_PERMS, ...) are deliberately left intact.
_unset_directives='unset RCLAUDE_RESUME_ID RCLAUDE_RESUME RCLAUDE_RESUME_NAME RCLAUDE_DETACHED RCLAUDE_MCP_CONFIG RCLAUDE_MIGRATE_FROM RCLAUDE_MIGRATE_FROM_CWD RCLAUDE_MIGRATE_SYNC; '
printf '%s' \
"${_back_env}cd ${1} && rc_t=\$(date +%s); claude ${_resume_flag} ${_name_flag} ${_mcp_flag} ${flag}; rc_ec=\$?; " \
"${_unset_directives}${_back_env}cd ${1} && rc_t=\$(date +%s); claude ${_resume_flag} ${_name_flag} ${_mcp_flag} ${flag}; rc_ec=\$?; " \
"rc_e=\$(date +%s); rc_d=\$((rc_e - rc_t)); " \
"if [ \$rc_ec -ne 0 ] || [ \$rc_d -lt 2 ]; then " \
"printf '\\n[rclaude] claude exited in %ds with code %d\\n' \$rc_d \$rc_ec; " \
@ -1657,6 +1665,21 @@ fi
setup_host "$host"
sync_tmux_conf "$host"
inner=$(build_inner "$dir")
# Transport the inner command to the remote as opaque base64. `ssh host
# "<cmd>"` concatenates its args and the remote login shell RE-PARSES the
# result — so an inline-quoted "${inner}" gets a second round of expansion
# on the remote: $(date +%s), $?, $((...)) and $rc_* all evaluate at
# tmux-launch time instead of pane-runtime. That silently broke
# build_inner's safety net — rc_t==rc_e (so rc_d is always 0), and the
# guard `if [ $rc_ec -ne 0 ] || [ $rc_d -lt 2 ]` collapsed to
# `if [ -ne 0 ] || [ -lt 2 ]` (a no-op test error → false), so `read rc_`
# never ran. A fast claude exit then closed the pane with nothing to hold
# it (and, if it was the last session, took the tmux server down too).
# base64 has no shell metacharacters, so it survives the remote re-parse
# intact; the remote decodes it once inside a command substitution and
# hands the result to tmux as a single argument — tmux's own `sh -c` then
# evaluates the $(...) at pane-runtime, as intended.
inner_b64=$(printf %s "$inner" | base64 | tr -d '\n')
# RCLAUDE_DETACHED=1 → spawn the tmux session on the remote in the
# background and print the session name. Symmetric with the local-host
# detached branch above; used by supervisor processes (e.g. clare web)
@ -1672,7 +1695,7 @@ inner=$(build_inner "$dir")
# detached spawn survives; the systemd-run variant did not.
# Mosh is interactive-only — always go through ssh for detached spawn.
if [ -n "${RCLAUDE_DETACHED:-}" ]; then
ssh $_SSH_LIVE_OPTS "$host" "tmux new-session -d -s '${session}' \"${inner}\""
ssh $_SSH_LIVE_OPTS "$host" "tmux new-session -d -s '${session}' \"\$(echo '${inner_b64}' | base64 -d)\""
printf '%s\n' "$session"
exit 0
fi
@ -1685,4 +1708,4 @@ fi
if [ "$(pick_transport "$host")" = "mosh" ]; then
exec mosh "$host" -- tmux new-session -A -s "${session}" "${inner}"
fi
exec ssh -t $_SSH_LIVE_OPTS "$host" "tmux new-session -A -s '${session}' \"${inner}\""
exec ssh -t $_SSH_LIVE_OPTS "$host" "tmux new-session -A -s '${session}' \"\$(echo '${inner_b64}' | base64 -d)\""