diff --git a/bin/rclaude b/bin/rclaude index 9ebeeab..d0a54ff 100755 --- a/bin/rclaude +++ b/bin/rclaude @@ -114,6 +114,24 @@ list_all_on() { list_disk_on "$1" } +# Push the canonical session-tools tmux fragment to and ensure +# ~/.tmux.conf sources it. Idempotent; runs on every launch so config changes +# in the repo propagate without re-running install.sh on each host. Silent +# no-op if the repo fragment can't be located. +sync_tmux_conf() { + _host=$1 + _self=$(resolve_self) + _repo=$(cd "$(dirname "$_self")/.." 2>/dev/null && pwd) + _frag="$_repo/tmux.conf" + [ -f "$_frag" ] || return 0 + _remote_cmd='mkdir -p ~/.tmux.d && cat > ~/.tmux.d/session-tools.conf && { grep -q "session-tools.conf" ~/.tmux.conf 2>/dev/null || printf "source-file ~/.tmux.d/session-tools.conf\n" >> ~/.tmux.conf; } && tmux source-file ~/.tmux.conf 2>/dev/null || true' + if is_local "$_host"; then + sh -c "$_remote_cmd" < "$_frag" 2>/dev/null || true + else + ssh -o BatchMode=yes -o ConnectTimeout=5 "$_host" "$_remote_cmd" < "$_frag" 2>/dev/null || true + fi +} + # All hosts to scan for list/resume. scan_hosts() { printf "local\n" @@ -139,9 +157,11 @@ cmd_list() { } # Resume strategy: -# - matches a tmux row → ssh+tmux attach (preserves the live conversation) -# - matches a disk row → re-exec self with ` ` so the normal -# launch path spins up a fresh tmux + claude --continue in that dir +# - 1 match → attach directly +# - 2+ matches → single-key picker (1-9 then a-z, max 35) +# - matches a tmux row → ssh+tmux attach (preserves the live conversation) +# - matches a disk row → re-exec self with ` ` so the normal +# launch path spins up a fresh tmux + claude --continue cmd_resume() { _pattern=${1:-} _matches=$(scan_hosts | while IFS= read -r h; do list_all_on "$h"; done) @@ -155,9 +175,43 @@ cmd_resume() { exit 1 fi if [ "$_count" -gt 1 ]; then - echo "multiple matches; refine pattern:" >&2 - printf '%s\n' "$_matches" | awk -F'\t' '{printf " %-10s %-6s %s\n", $1, $2, $3}' >&2 - exit 1 + _keys="123456789abcdefghijklmnopqrstuvwxyz" + if [ "$_count" -gt 35 ]; then + echo "too many matches ($_count); refine pattern" >&2 + exit 1 + fi + if [ ! -t 0 ] || [ ! -t 2 ]; then + echo "multiple matches and no tty for picker; refine pattern:" >&2 + printf '%s\n' "$_matches" | awk -F'\t' '{printf " %-10s %-6s %s\n", $1, $2, $3}' >&2 + exit 1 + fi + _i=0 + printf '%s\n' "$_matches" | while IFS= read -r _line; do + _i=$((_i + 1)) + _k=$(printf %s "$_keys" | cut -c"$_i") + printf ' [%s] %s\n' "$_k" \ + "$(printf %s "$_line" | awk -F'\t' '{printf "%-10s %-4s %s", $1, $2, $3}')" >&2 + done + _last_key=$(printf %s "$_keys" | cut -c"$_count") + printf 'select [1-%s]: ' "$_last_key" >&2 + _old=$(stty -g 2>/dev/null || true) + stty -icanon -echo min 1 time 0 2>/dev/null || true + _key=$(dd bs=1 count=1 2>/dev/null /dev/null || true + printf '%s\n' "$_key" >&2 + _idx=0 + _c=1 + while [ "$_c" -le "$_count" ]; do + if [ "$(printf %s "$_keys" | cut -c"$_c")" = "$_key" ]; then + _idx=$_c; break + fi + _c=$((_c + 1)) + done + if [ "$_idx" -eq 0 ]; then + echo "rclaude: invalid selection: '$_key'" >&2 + exit 1 + fi + _matches=$(printf '%s\n' "$_matches" | sed -n "${_idx}p") fi _host=$(printf '%s\n' "$_matches" | awk -F'\t' 'NR==1{print $1}') _kind=$(printf '%s\n' "$_matches" | awk -F'\t' 'NR==1{print $2}') @@ -281,6 +335,7 @@ if is_local "$host"; then echo "rclaude: local directory not found: $dir" >&2 exit 1 fi + sync_tmux_conf local exec tmux new-session -s "$session" "$(build_inner "$dir")" fi @@ -298,5 +353,6 @@ if ! ssh -o BatchMode=yes -o ConnectTimeout=5 "$host" "test -d ${dir}" 2>/dev/nu exit 1 fi +sync_tmux_conf "$host" inner=$(build_inner "$dir") exec ssh -t "$host" "tmux new-session -s '${session}' \"${inner}\"" diff --git a/tmux.conf b/tmux.conf index efd5ee6..cbcaa69 100644 --- a/tmux.conf +++ b/tmux.conf @@ -10,3 +10,8 @@ set -ga terminal-overrides ",*256col*:Tc" # Larger scrollback for when you need tmux copy mode set -g history-limit 50000 + +# Mouse wheel scrolls tmux scrollback (auto-enters copy mode) instead of being +# passed as arrow keys to the inner app (which would scroll Claude history etc.) +# Hold Option on macOS to bypass tmux and use native terminal selection. +set -g mouse on