From 8997920dc36d8ec61825fb49fffcbe8ceca330ce Mon Sep 17 00:00:00 2001 From: Natalie Date: Fri, 22 May 2026 00:31:07 -0700 Subject: [PATCH] =?UTF-8?q?feat(@scripts):=20=E2=9C=A8=20add=20resume=20uu?= =?UTF-8?q?id=20column=20to=20tmux=20session=20listing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Lilith Autocommit --- bin/rclaude | 62 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 53 insertions(+), 9 deletions(-) diff --git a/bin/rclaude b/bin/rclaude index 2b7a1bf..6ce1427 100755 --- a/bin/rclaude +++ b/bin/rclaude @@ -90,22 +90,66 @@ is_local() { } # List claude-* tmux sessions on a host. Output one row per session: -# \ttmux\t\t +# \ttmux\t\t\t +# The 5th column is the Claude session UUID a `claude --resume ` 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 `. 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() { _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 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 - _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 - # tmux ls lines look like: claude-foo: 1 windows (created ...) [80x24] - printf %s "$_raw" | awk -v host="$_host" ' + # First block: `tmux ls` lines — claude-foo: 1 windows (created ...) [80x24] + # Second block (after the marker): `\t`. + # 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-/ { - name=$1; sub(/:$/, "", name); - $1=""; - sub(/^[[:space:]]+/, ""); - printf "%s\ttmux\t%s\t%s\n", host, name, $0 + # `tmux ls` line, space-delimited (the -F TAB above only applies to + # the list-panes block). Stash name + detail; emit in END once the + # resume[] map is fully built. + 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]] } ' }