#!/bin/sh # rclaude [dir] # # Durable Claude Code session, local or remote. Two layers of resilience: # # 1. tmux on survives terminal/transport drops (network, lid close, # ssh kill, terminal crash) — works even when is the local box # because the local terminal can also die independently. # 2. `claude --continue` resumes the per-directory session from disk after # anything kills the host itself (reboot, crash, OOM). # # Re-running with the same + always lands you back in the same # conversation: tmux reattaches if alive, claude --continue picks up from # ~/.claude/projects// otherwise. # # can be: # - any ssh-reachable target (Host alias, user@hostname, IP) # - "local", "localhost", or the local short/long hostname → no ssh, # just a local tmux session (still detachable with Ctrl-b d) # # Permission mode: --dangerously-skip-permissions is on by default — these # are sessions on hosts you own. Override with RCLAUDE_PERMS=default (or any # other --permission-mode value) if you want prompts back. # # Usage: # rclaude apricot # remote home dir on apricot # rclaude apricot ~/Code/@projects/foo # remote, specific dir # rclaude local ~/Code/@projects/foo # local tmux-wrapped session # rclaude $(hostname) ~ # same — detected as local set -eu if [ $# -lt 1 ]; then echo "usage: $0 [dir] (dir defaults to remote/local \$HOME)" >&2 exit 2 fi host=$1 dir=${2:-\~} slug=$(printf %s "$dir" | sed -e 's|^[~/]*||' -e 's|[^A-Za-z0-9]|-|g') [ -z "$slug" ] && slug=home session="claude-$(whoami)-${slug}" perms=${RCLAUDE_PERMS:-bypass} case $perms in bypass) flag="--dangerously-skip-permissions" ;; *) flag="--permission-mode $perms" ;; esac is_local() { case $1 in local|localhost|127.0.0.1|::1) return 0 ;; esac [ "$1" = "$(hostname)" ] && return 0 [ "$1" = "$(hostname -s 2>/dev/null)" ] && return 0 return 1 } if is_local "$host"; then # No ssh hop — just local tmux. eval expands ~ and env vars in dir. eval "cd ${dir}" exec tmux new-session -A -s "$session" "exec claude --continue ${flag}" fi # Remote: tmux on the other side of an ssh -t. exec replaces the shell so # the tmux pane dies cleanly when claude exits. inner="cd ${dir} && exec claude --continue ${flag}" exec ssh -t "$host" "tmux new-session -A -s '${session}' \"${inner}\""