Skip to content
Development
Skill

/remote-claude-code

Run Claude Code on a remote host over SSH — a persistent expect-driven login session, headless claude -p with the stdin fix, the interactive TUI inside a remote tmux driven by send-keys/capture-pane (one keystroke at a time, capture-verified; relayed user messages go through

From plugin
penguin-harness
2.3k26 skills
Install
$ npx -y skills add Prism-Shadow/penguin-harness --skill remote-claude-code --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/remote-claude-code

Context preview

The summary Claude sees to decide when to auto-load this skill.

Run Claude Code on a remote host over SSH — a persistent expect-driven login session, headless claude -p with the stdin fix, the interactive TUI inside a remote tmux driven by send-keys/capture-pane (one keystroke at a time, capture-verified; relayed user messages go through

SKILL.md

remote-claude-code.SKILL.md
name: remote-claude-code
description: Run Claude Code on a remote host over SSH — a persistent expect-driven login session, headless claude -p with the stdin fix, the interactive TUI inside a remote tmux driven by send-keys/capture-pane (one keystroke at a time, capture-verified; relayed user messages go through verbatim), and multi-turn continuity via --session-id/--resume or stream-json; hosts and credentials are placeholders resolved at runtime from the user or the vault, never hardcoded.

Remote Claude Code

Drive Claude Code on a remote Linux host over SSH. Three modes, in increasing interactivity:

1. **Persistent SSH session** — one long-lived expect-driven connection you keep feeding commands across turns. 2. **Headless (`claude -p`)** — one-shot or scripted calls, with the stdin fix that naive invocations need. 3. **Interactive TUI** — the real Claude Code UI inside a remote `tmux` session, driven with `tmux send-keys` / `tmux capture-pane`. tmux is the way to do interactive use.

Reach for this skill when the user wants to run or drive Claude Code on a server, keep an SSH connection open across turns, or hold a continuous conversation with Claude Code on a remote box. In a relayed conversation you are a pure message pipe — the user's words go to Claude Code verbatim (section 4).

Before you start

If the user's message only invokes this skill without a concrete task, ask what they want — at minimum the remote host, the SSH user, and what Claude Code should do there.

Credential rules — non-negotiable:

  • This document contains **no real credentials**: `<ssh-user>`, `<remote-host>`, `<target-user>`, `<sess>` are placeholders you substitute at runtime from what the user provides or from this agent's **key vault** (suggested keys: `REMOTE_SSH_HOST`, `REMOTE_SSH_USER`, `REMOTE_SSH_PASSWORD`). Vault values reach your shell environment on the next task — check with `[ -n "$REMOTE_SSH_PASSWORD" ] && echo ok || echo missing`, and if missing ask the user to add the keys to the key vault (gear icon on the agent card → settings → key vault tab) or to provide the values in chat.
  • **Never hardcode a password** into scripts left on disk, deliverables, or your final answer. Have expect read it from the environment (`$env(...)`); if the user pasted it in chat, export it only into the running process's environment, scrub it from logs and replies, and delete any temp file that embeds it as soon as the session is done.
  • Prefer SSH keys when they are already set up — then no password tooling is needed at all.
  • Back up remote config before modifying it, and never copy remote credential stores (`~/.claude/.credentials.json`, OAuth fields of `~/.claude.json`, private keys) anywhere.

1. Persistent SSH session (expect)

One long-lived connection with password auto-login and an optional user switch, held open by `interact`. `expect` ships with macOS and is a one-command install on Linux; `sshpass` is usually absent, so expect is the password-interactive tool of choice.

The full expect template plus its operating notes live in [`reference/persistent-session.md`](reference/persistent-session.md) — read that file when setting up this mode. In short: write the template to your scratchpad, fill the placeholders at runtime (the password comes from `$env(REMOTE_SSH_PASSWORD)`, never hardcoded), start it with `exec_command`, and drive the held-open connection through its `process_id` with `input_command`. Reuse this one session for most remote commands rather than opening a new connection each time.

2. Headless Claude Code (`claude -p`) — the stdin gotcha

`claude -p "<prompt>"` hangs forever when its stdin is an open pipe that never closes — which is exactly what agent harnesses and the expect session above provide. The process idles with no output and no network connections. Fix: redirect stdin from `/dev/null`:

claude -p "Reply with exactly: all good" < /dev/null

Same for `--resume`, `--session-id`, `--output-format stream-json`, and the rest. Exception: when input is _meant_ to come from stdin (`--input-format stream-json`), feed it through a pipe that closes (`echo '<json>' | claude ...`) and do **not** add `< /dev/null` — that would override the pipe.

Symptom checklist: process running + no output + no TCP connections → stdin starvation; add `< /dev/null`.

3. Interactive TUI — tmux is the way

Launching `claude` interactively inside the expect/pipe session renders the welcome screen, but pipe input is treated as a **paste**: text lands in the input box and Enter (`\r`) is inserted literally, so the message is never submitted (the remote transcript `~/.claude/projects/<dir>/*.jsonl` gains no user entry). Run Claude Code inside a remote `tmux` session instead and drive it with `tmux send-keys`, which delivers discrete keypresses:

# start detached, then read the screen
TERM=xterm-256color tmux new-session -d -s <sess> "claude"
sleep 8
tmux capture-pane -t <sess> -p | tail -25

# converse: type the message, confirm it landed, submit — text and Enter as separate calls (§3.3)
tmux send-keys -t <sess> -l "Introduce yourself in one sentence"
tmux capture-pane -t <sess> -p | tail -5     # exactly your message on the input line?
tmux send-keys -t <sess> Enter
sleep 20
tmux capture-pane -t <sess> -p -S -200       # scrollback: a long reply overflows the visible pane
  • Workspace trust dialog: it shows on the first launch in each project directory (`-p` mode skips it; `--dangerously-skip-permissions` does **not**). Answer it once via `tmux send-keys -t <sess> Enter` and capture to confirm it closed, or pre-accept it by setting `hasTrustDialogAccepted: true` for that project path in the remote `~/.claude.json` — back the file up first (`cp ~/.claude.json ~/.claude.json.bak-$(date +%s)`) and rewrite it with a JSON-aware tool (python), not sed.
  • Tool-permission prompts, and any other question Claude Code puts to **the user** (clarifying questions, plan approvals)
Read more
Ships withpenguin-harness

🐧 Harness for RSI. Let AI Build AI. Multi-Agent Auto-Dev Platform. Everything is Transparent.

Get the whole plugin
Stats
2,322
Stars
246
Forks
Active
Maintenance
TypeScript
Language
Apache-2.0
License
56m ago
Last commit
2mo ago
Created

Repo: Prism-Shadow/penguin-harness

Other skills on penguin-harness.