session-tools/bin/rclaude

69 lines
2.4 KiB
Bash
Executable file

#!/bin/sh
# rclaude <host> [dir]
#
# Durable Claude Code session, local or remote. Two layers of resilience:
#
# 1. tmux on <host> survives terminal/transport drops (network, lid close,
# ssh kill, terminal crash) — works even when <host> 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 <host> + <dir> always lands you back in the same
# conversation: tmux reattaches if alive, claude --continue picks up from
# ~/.claude/projects/<encoded-cwd>/ otherwise.
#
# <host> 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 <host> [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}\""