diff --git a/README.md b/README.md index 28def7d..77488b6 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,14 @@ a detached tmux session on the remote so the work survives the SSH drop. creates) a per-user tmux session on `` named `claude-$(whoami)`. Detach with `Ctrl-b d`; transport drops don't kill the shell. +- **`bin/rclaude [dir]`** — Remote, durable Claude Code session. + Stacks two resilience layers: tmux survives transport drops, and + `claude --continue` resumes the per-directory session from + `~/.claude/projects/` after anything kills the host itself. Re-running + with the same `` + `` always lands back in the same + conversation. Defaults to `--dangerously-skip-permissions`; override with + `RCLAUDE_PERMS=default` (or any other `--permission-mode` value). + ## Install On every host that should have these on `$PATH`: @@ -37,6 +45,7 @@ via plain `git pull` — symlinks track the repo automatically. |--------------------------------------------|----------------------------------------------| | Interactive shell on a remote | `tssh ` | | One-off command (build, test, query) | `remote-run ""` | +| Claude Code session on a remote | `rclaude [dir]` | | Long-running job (>1h, must survive reboot)| `systemd --user` unit on the remote, not ssh | ## Per-host shims (optional) diff --git a/bin/rclaude b/bin/rclaude new file mode 100755 index 0000000..50e04f9 --- /dev/null +++ b/bin/rclaude @@ -0,0 +1,50 @@ +#!/bin/sh +# rclaude [dir] +# +# Remote, durable Claude Code session. Two layers of resilience stacked: +# +# 1. tmux on survives transport drops (network, lid close, ssh kill) +# 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 where you +# were: tmux reattaches if alive, claude --continue picks up the conversation +# from ~/.claude/projects// otherwise. +# +# Permission mode: --dangerously-skip-permissions is on by default — these +# are remote 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 +# rclaude apricot ~/Code/@projects/foo # specific dir +# rclaude apricot @proj/lilith # alias from project-paths.md +# # works if remote shell expands it + +set -eu + +if [ $# -lt 1 ]; then + echo "usage: $0 [dir] (dir defaults to remote \$HOME)" >&2 + exit 2 +fi + +host=$1 +dir=${2:-\~} + +# tmux session name unique per (user, dir) so multiple Claude sessions on the +# same host don't collide. Slug is the path with non-alnum collapsed. +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 + +# cd then exec claude. exec replaces the shell so the tmux pane dies cleanly +# when claude exits (instead of leaving a stray shell behind). +inner="cd ${dir} && exec claude --continue ${flag}" + +exec ssh -t "$host" "tmux new-session -A -s '${session}' \"${inner}\""