| name | remote |
| description | Session Teleport for Claude Code. Launch the active Claude session on a remote machine, check it, pull it back locally, and troubleshoot the handoff over SSH, SCP, and tmux.
|
| disable-model-invocation | false |
| user-invocable | true |
| allowed-tools | Bash, Read, Write, Glob, Grep, Edit |
| argument-hint | <launch|pull|list|check|doctor> [cluster] [args] |
Remote Session Teleport
Move an active Claude Code workflow between machines without restarting from scratch.
This skill treats a Claude session as a bundle:
~/.claude/projects/<project-slug>/<session-id>.jsonl
- Optional sibling directory
~/.claude/projects/<project-slug>/<session-id>/
Teleport works by copying that bundle to another machine, resuming it there with
claude --resume <session-id> --fork-session, and recording enough metadata to
pull the resulting fork back later.
Configuration
Resolve the cluster registry in this order:
.claude/remote-clusters.json in the current project
~/.config/session-teleport/clusters.json
Write launch history to .claude/remote-sessions.json in the current project.
Create the file on first use if it does not exist.
Cluster schema
Each cluster entry should look like:
{
"my-gpu-server": {
"ssh_alias": "gpu-server",
"repo_base": "/home/username/repos",
"claude_projects": "/home/username/.claude/projects",
"project_slug": "auto",
"default_cwd": "/home/username/repos/my-project"
}
}
Fields:
ssh_alias: required SSH host alias
claude_projects: required remote ~/.claude/projects root
default_cwd: required remote repo path to run Claude from
project_slug: optional. Use "auto" to derive the slug from default_cwd
repo_base: optional documentation/convenience field for human readers
notes: optional freeform notes
Shared conventions
Before any subcommand, compute:
LOCAL_CWD: pwd -P
LOCAL_PROJECT_SLUG: resolved local cwd with / replaced by -
LOCAL_PROJECTS: $HOME/.claude/projects/<LOCAL_PROJECT_SLUG>
STATE_FILE: .claude/remote-sessions.json in the current project
For the selected cluster, compute:
REMOTE_PROJECT_SLUG: explicit project_slug, or if it is "auto", derive it from default_cwd using the same / to - rule
REMOTE_DEST: <claude_projects>/<REMOTE_PROJECT_SLUG>
Use Python for slug generation and JSON reads/writes when practical. A portable slug helper is:
python3 - <<'PY'
import pathlib
print(str(pathlib.Path.cwd().resolve()).replace("/", "-"))
PY
When reading cluster config, fail with a clear error if the registry is missing, the cluster is not found, or required fields are absent.
Subcommands
Parse the first argument as one of:
launch <cluster> "<task>" - teleport the current local session to the remote machine and start it in tmux
pull <cluster> [session-id] - pull a remote forked session back to local storage
list [cluster] - inspect configured machines and recent session artifacts
check <cluster> [session-id] - inspect one launched session
doctor [cluster] - run health checks and surface portability issues
/remote list [cluster]
If no cluster is provided, iterate over every configured cluster.
For each cluster:
- Load the cluster record and compute
REMOTE_PROJECT_SLUG plus REMOTE_DEST.
- Test SSH connectivity:
ssh -o ConnectTimeout=5 <ssh_alias> echo ok 2>&1
- If reachable, run:
ssh <ssh_alias> "tmux list-sessions 2>/dev/null"
ssh <ssh_alias> "ls -lt <REMOTE_DEST>/*.jsonl 2>/dev/null | head -10"
ssh <ssh_alias> "pgrep -a -f 'claude.*resume' 2>/dev/null || echo 'No active Claude processes'"
- Present a compact summary showing connectivity, recent tmux sessions, recent Claude bundles, and active Claude processes.
/remote launch <cluster> "<task>"
Teleport the current local session to a remote machine and continue it there.
Step 1: Load config and compute paths
Read the cluster registry, select the requested cluster, and compute:
LOCAL_CWD
LOCAL_PROJECT_SLUG
LOCAL_PROJECTS
STATE_FILE
REMOTE_PROJECT_SLUG
REMOTE_DEST
REMOTE_CWD from default_cwd
Step 2: Preflight checks
Fail fast on missing prerequisites:
ssh -o ConnectTimeout=5 <ssh_alias> echo ok
ssh <ssh_alias> "claude --version 2>&1"
CLAUDECODE=1 claude --version 2>&1
ssh <ssh_alias> "claude --help 2>&1 | grep -q -- '--fork-session'"
ssh <ssh_alias> "which tmux"
ssh <ssh_alias> "test -d <REMOTE_CWD> && echo ok || echo MISSING"
ssh <ssh_alias> "mkdir -p '<REMOTE_DEST>'"
Compare local and remote Claude versions. Warn on mismatches, especially if the remote version is older.
Detect whether the remote CLI advertises --rc:
ssh <ssh_alias> "claude --help 2>&1 | grep -q -- '--rc'"
Only append --rc if that check passes. Otherwise tell the user to run /rc manually after attaching.
Step 3: Identify the current local session
Find the newest session bundle in LOCAL_PROJECTS:
ls -t "<LOCAL_PROJECTS>"/*.jsonl | head -1
Extract:
SID: filename stem
BUNDLE_FILE: <LOCAL_PROJECTS>/<SID>.jsonl
BUNDLE_DIR: <LOCAL_PROJECTS>/<SID>/
If no local session exists for the current project, stop and explain that the user needs an existing Claude session in this repo first.
Step 4: Inspect the bundle
Report what will be copied:
ls -lh "<BUNDLE_FILE>"
du -sh "<BUNDLE_DIR>" 2>/dev/null || echo "No sibling directory"
Step 5: Transfer the bundle
Copy both the JSONL log and the sibling directory when present:
scp "<BUNDLE_FILE>" <ssh_alias>:"<REMOTE_DEST>/<SID>.jsonl"
if [ -d "<BUNDLE_DIR>" ]; then
scp -r "<BUNDLE_DIR>" <ssh_alias>:"<REMOTE_DEST>/"
fi
Step 6: Start Claude in remote tmux
Use a short, stable tmux session name:
SHORT_SID="<first 8 chars of SID>"
TMUX_NAME="teleport-${SHORT_SID}"
Launch:
ssh <ssh_alias> "tmux new-session -d -s <TMUX_NAME> \
'cd <REMOTE_CWD> && claude --resume <SID> --fork-session --dangerously-skip-permissions <OPTIONAL_RC_FLAG>'"
Then verify:
sleep 3
ssh <ssh_alias> "tmux has-session -t <TMUX_NAME> 2>&1 && echo RUNNING || echo FAILED"
Step 7: Record the launch
Update .claude/remote-sessions.json with Python. Create the file if needed and append a launch entry like:
{
"launches": [
{
"timestamp": "<ISO datetime>",
"source_session": "<SID>",
"cluster": "<cluster>",
"ssh_alias": "<ssh_alias>",
"tmux_session": "<TMUX_NAME>",
"local_project_slug": "<LOCAL_PROJECT_SLUG>",
"remote_project_slug": "<REMOTE_PROJECT_SLUG>",
"remote_project_path": "<REMOTE_DEST>",
"remote_cwd": "<REMOTE_CWD>",
"status": "launched",
"task": "<task description>"
}
]
}
Step 8: Report back
Include:
- remote cluster name
- tmux session name
- whether
--rc was enabled automatically
- how to attach directly:
ssh <ssh_alias> && tmux attach -t <TMUX_NAME>
- how to check later:
/remote check <cluster>
- how to pull back:
/remote pull <cluster>
/remote pull <cluster> [session-id]
Pull a remote forked session back to local storage.
Step 1: Determine the remote session ID
If the user passed a session ID, use it.
Otherwise:
- Read
.claude/remote-sessions.json
- Find the most recent
"launched" record for the cluster
- List recent remote bundles:
ssh <ssh_alias> "ls -t <REMOTE_DEST>/*.jsonl | head -5"
- Prefer the newest bundle whose session ID is different from the original
source_session
- If there are multiple plausible candidates, show them and explain the selection
Step 2: Transfer the bundle back
Ensure the local project directory exists, then copy the remote JSONL and sibling directory:
mkdir -p "<LOCAL_PROJECTS>"
scp <ssh_alias>:"<REMOTE_DEST>/<REMOTE_SID>.jsonl" "<LOCAL_PROJECTS>/<REMOTE_SID>.jsonl"
if ssh <ssh_alias> "test -d '<REMOTE_DEST>/<REMOTE_SID>'"; then
scp -r <ssh_alias>:"<REMOTE_DEST>/<REMOTE_SID>" "<LOCAL_PROJECTS>/"
fi
Step 3: Update launch history
Update the most relevant launch record:
- set
status to "pulled"
- set
pulled_session_id to the remote session ID
- set
pulled_at to the current timestamp
Step 4: Report back
Include:
/remote check <cluster> [session-id]
Inspect the status of one launched remote session.
- Read
.claude/remote-sessions.json
- Pick the matching launch record, or the latest launched record for the cluster
- Check tmux status:
ssh <ssh_alias> "tmux has-session -t <TMUX_NAME> 2>&1"
- Check active Claude process:
ssh <ssh_alias> "pgrep -a -f 'claude.*resume' 2>/dev/null"
- Show recent remote commits:
ssh <ssh_alias> "cd <REMOTE_CWD> && git log --oneline -5 2>/dev/null"
- Report whether the session appears
running, completed, or crashed, and cite the evidence used.
/remote doctor [cluster]
Run health checks for one cluster or all configured clusters.
For each target:
- SSH connectivity:
ssh -o ConnectTimeout=5 <ssh_alias> echo ok
- Claude CLI availability and version comparison:
CLAUDECODE=1 claude --version 2>&1
ssh <ssh_alias> "claude --version 2>&1"
ssh <ssh_alias> "claude --help 2>&1 | grep -q -- '--fork-session'"
- tmux availability:
ssh <ssh_alias> "which tmux"
- Remote repo exists:
ssh <ssh_alias> "test -d <REMOTE_CWD> && echo ok || echo MISSING"
- Remote disk space:
ssh <ssh_alias> "df -h <REMOTE_CWD> | tail -1"
- Teleport tmux sessions:
ssh <ssh_alias> "tmux list-sessions 2>/dev/null | grep 'teleport-' || true"
- Stale launch records: launch entries older than 7 days still marked
"launched"
- Optional JSONL integrity check for a specific session: verify the last line parses as JSON
Report pass, warn, and fail items clearly.
Operating notes
- This skill does not migrate live process state, shell history, venv state, or open MCP connections.
- It only moves Claude session artifacts plus whatever repository state already exists on both machines.
- Always remind the user that source code sync is separate from session teleport.
- Prefer explicit evidence over assumptions when selecting a session to pull.