feat(@scripts): add resume uuid column to tmux session listing

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Natalie 2026-05-22 00:31:07 -07:00
parent ece58a21d6
commit 8997920dc3

View file

@ -90,22 +90,66 @@ is_local() {
} }
# List claude-* tmux sessions on a host. Output one row per session: # List claude-* tmux sessions on a host. Output one row per session:
# <host>\ttmux\t<session_name>\t<detail_from_tmux_ls> # <host>\ttmux\t<session_name>\t<detail_from_tmux_ls>\t<resumed_uuid>
# The 5th column is the Claude session UUID a `claude --resume <uuid>` pane was
# started with — empty for fresh-spawned (no --resume) sessions. It lets a
# consumer map a UUID back to a live tmux session name. The column is purely
# additive: consumers reading fixed columns 1-4 are unaffected.
#
# Both `tmux ls` and `tmux list-panes` run inside ONE shell (one ssh round-trip
# for remote hosts). `pane_start_command` is the command tmux launched the pane
# with — for rclaude-spawned resumes it embeds `claude --resume <uuid>`. We do
# NOT use `tmux capture-pane` here: it heap-corrupts apricot's tmux. `list-panes
# -F` reads server-side metadata only and is safe.
list_tmux_on() { list_tmux_on() {
_host=$1 _host=$1
# One shell does both `tmux ls` and `tmux list-panes`; a marker line splits
# the two blocks in the combined output. The list-panes format uses a
# literal TAB between session_name and pane_start_command.
_ltcmd='tmux ls 2>/dev/null; echo "@@RCLAUDE-PANES@@"; tmux list-panes -a -F "#{session_name} #{pane_start_command}" 2>/dev/null'
if is_local "$_host"; then if is_local "$_host"; then
command -v tmux >/dev/null 2>&1 || return 0 command -v tmux >/dev/null 2>&1 || return 0
_raw=$(tmux ls 2>/dev/null || true) _raw=$(sh -c "$_ltcmd" 2>/dev/null || true)
else else
_raw=$(ssh -o BatchMode=yes -o ConnectTimeout=3 "$_host" 'tmux ls 2>/dev/null' || true) _raw=$(ssh -o BatchMode=yes -o ConnectTimeout=3 "$_host" "$_ltcmd" 2>/dev/null || true)
fi fi
# tmux ls lines look like: claude-foo: 1 windows (created ...) [80x24] # First block: `tmux ls` lines — claude-foo: 1 windows (created ...) [80x24]
printf %s "$_raw" | awk -v host="$_host" ' # Second block (after the marker): `<session_name>\t<pane_start_command>`.
# The `tmux ls` block precedes the pane block, so a single pass can't look
# up resume[] while emitting tmux rows — the map isn't built yet. Stash the
# tmux rows, build the map from the pane block, then emit at END.
printf %s "$_raw" | awk -F'\t' -v host="$_host" '
/^@@RCLAUDE-PANES@@$/ { panes = 1; next }
panes {
# Pane row: $1 = session_name, $2.. = pane_start_command (which may
# itself contain tabs, so rejoin fields 2..NF before matching).
cmd = ""
for (i = 2; i <= NF; i++) cmd = cmd (i > 2 ? "\t" : "") $i
if (match(cmd, /--resume[ =][0-9a-fA-F][0-9a-fA-F-]+/)) {
uuid = substr(cmd, RSTART, RLENGTH)
sub(/^--resume[ =]/, "", uuid)
# First pane wins per session (panes share the session start cmd).
if (!($1 in resume)) resume[$1] = uuid
}
next
}
/^claude-/ { /^claude-/ {
name=$1; sub(/:$/, "", name); # `tmux ls` line, space-delimited (the -F TAB above only applies to
$1=""; # the list-panes block). Stash name + detail; emit in END once the
sub(/^[[:space:]]+/, ""); # resume[] map is fully built.
printf "%s\ttmux\t%s\t%s\n", host, name, $0 line = $0
name = line
sub(/[ \t].*$/, "", name)
sub(/:$/, "", name)
detail = line
sub(/^[^ \t]*[ \t]+/, "", detail)
n++
names[n] = name
details[n] = detail
}
END {
for (i = 1; i <= n; i++)
printf "%s\ttmux\t%s\t%s\t%s\n", host, names[i], details[i], resume[names[i]]
} }
' '
} }