Add remote SSH sessions: remote projects, indexing/search/resume, IDE over SSH - #78
Open
HAN-oQo wants to merge 3 commits into
Open
Add remote SSH sessions: remote projects, indexing/search/resume, IDE over SSH#78HAN-oQo wants to merge 3 commits into
HAN-oQo wants to merge 3 commits into
Conversation
Run Claude Code and shells on remote hosts over SSH. Remote hosts and directories are first-class projects alongside local ones — no local-only assumption anymore. - shell-profiles + remote-hosts: SSH shell profiles (mirrors the existing WSL wrapped-command pattern), ~/.ssh/config parsing, remote command assembly (single-quoted to survive ssh's argv re-parse), ControlMaster multiplexing. - main.js: open-terminal remote branch (skips local-path checks, local cwd=$HOME, no MCP/shim), plus IPC for remote projects, interactive connect, remote dir browse, host save/test, and write-to-~/.ssh/config. - session-cache: persisted remote projects injected into the sidebar; live remote sessions group under them (remote Claude reads as a Claude session). - renderer: Add Project (Local | Remote SSH) with "+ Add new host", inline Connect with structured password / host-key popups (no raw terminal), remote directory browser, host management in Settings, SSH badges. - 30 unit tests (node --test). Auth stays with the user: passwords/passphrases/passkeys/host-key confirmations are entered by the user; nothing is stored. Remote session indexing/search (Phase 2), fork/resume on the remote (Phase 3), and IDE-over-SSH (Phase 4) are deferred — see the PR description. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Builds on Phase 1 (doctly#76). Remote sessions previously appeared in the sidebar only while live; this indexes a connected host's PAST sessions so they persist, are searchable, and resume in a terminal — with no transcript copied to local disk. - read-session-file: extract pure parseSessionContent(content, meta), shared by local and remote indexing (identical summary/title/search behavior). - remote-index (new): metadata-over-SSH sync over the live ControlMaster socket. Lists a host's ~/.claude/projects/*.jsonl, auto-discovers a remote project per session from its cwd (ssh://<label>/<dir>), fetches only new/changed transcripts (length-framed stream), parses in memory, upserts cache + FTS tagged source=host, prunes vanished sessions. Pure helpers are unit-tested. - db: add `source` column to session_cache + search_map (NULL = local; additive migration, no cache wipe). Source-scoped queries isolate local vs remote rows. DB path overridable via env for tests. - main: sync-remote-host IPC + runRemoteSync; triggers on connect, manual refresh, remote-session exit, and a background sweep at startup (status surfaced to the status bar). read-session-jsonl streams remote transcripts live over SSH (View messages) with a needsConnect fallback. remove-project drops a remote project's indexed rows. - Click-to-resume parity: a remote session resumes in a terminal via `cd <dir> && claude --resume "<id>"` on the host (fork supported); the resume dir is derived from the ssh:// projectPath. Remote Control reconnects automatically on resume when the session had it. - sidebar/app: past remote sessions show the SSH badge + Claude logo; a refresh (⟳) control on remote project headers. - test: run under Electron's node (matches the app's better-sqlite3 ABI). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Builds on Phase 2 (doctly#77). Opt-in: a remote Claude session can push proposed diffs / file-opens into Switchboard's local side panel, exactly like a local session. Off by default — it reverse-forwards the local IDE port to the remote host (token-protected). IDE over SSH: - remote-ide.js (new): pure command builders — reverse/cancel `ssh -O forward -R` over the shared control master, the remote IDE lock-file script (writes ~/.claude/ide/<port>.lock with $PWD + authToken after cd), remote lock cleanup, remote `cat`, and a candidate-port picker. Unit-tested. - mcp-bridge.js: startMcpServer takes opts.readOldFile; openDiff/openFile read the "old" content through it (new readOldContent seam). Remote sessions inject an ssh-cat reader so the diff's old side reflects the remote file (new side comes from the CLI). Local unchanged. - remote-hosts.js: buildRemoteCommand gains a preExec slot (cd && <preExec> && exec) so the IDE lock is written with the project dir as $PWD. - main.js: when the remoteIde setting is on for a remote claude session, bring up the control master, start the local MCP server, reverse-forward a free remote port (retry candidates), write the remote lock + export CLAUDE_CODE_SSE_PORT, add --ide. On exit: cancel the forward + rm the remote lock + shutdown the server. cleanStaleLockFiles runs at startup. If the tunnel can't be set up the session still runs (no --ide). - Settings: "IDE integration over SSH" toggle (global, default off; per-project override honored). Unify remote session UI with local (no reason to differ): - The remote New Session popover now uses the same labels/icons/order as local (Claude / Claude (Configure...) / Terminal) — the SSH badge + host:path already mark it remote. Handlers branch on project.remote. - The remote Configure dialog matches local: same "Cancel / Start" buttons (no more "Open shell"/"Start Claude"). Justified differences remain — a Remote Directory picker, and Worktree/Chrome hidden (local-machine features). - Pre-launch Command now applies to remote too (prefixes the remote claude command), since it is meaningful over SSH unlike Worktree/Chrome. Renderer diff panel is unchanged — remote diffs flow through the same IPC. Tests run under Electron's node (matches the app's better-sqlite3 ABI). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This was referenced Jul 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds remote SSH session support to Switchboard. Today Switchboard is local-only — it reads
~/.claude/projectsoff the local disk and spawns terminals against local shells. This PR lets you run Claude Code (and shells) on remote hosts over SSH, with remote hosts + directories treated as first-class projects, their past sessions indexed & searchable, click-to-resume on the host, and (opt-in) the remote CLI's diffs/file-opens rendered in Switchboard's local side panel.Delivered in three phases (each a self-contained commit); this single PR combines them.
Phase 1 — Remote sessions & host management
Add Projectgains a Local folder | Remote (SSH) choice. Pick a host, choose a remote directory, and it appears in the sidebar with anSSHbadge; its+launches Claude/shell on that host. Connect verifies/authenticates in place (spinner → green; popup only when the host needs a password/passphrase or first-time host-key confirmation — never a raw terminal). A remote directory Browse mirrors the local folder picker.~/.ssh/confighosts import automatically; you can also add hosts manually (identity file + extra-ooptions) and optionally write them back to~/.ssh/config. Connections are multiplexed (ControlMaster) so you authenticate once per host.Phase 2 — Indexing, search & resume
A connected host's past sessions are indexed (metadata over the existing SSH connection — no transcript copied to disk) so they persist in the sidebar, are searchable, and resume/fork in a terminal on the host. Auto-discovered by each session's
cwd, grouped by remote directory, with the SSH badge + Claude logo + a ⟳ refresh. Full-text search covers remote transcripts; "View messages" streams a remote transcript live over SSH.Phase 3 — IDE emulation over SSH
Opt-in: a remote Claude session pushes proposed diffs / file-opens into Switchboard's local side panel, exactly like a local session (review, accept, edit-then-accept, reject). It reverse-forwards the local IDE WebSocket port to the remote host (
ssh -O forward -R), writes an IDE lock file there so the remoteclaude --ideconnects back through the tunnel, and sources the diff's old side from the remote viassh cat(the new side comes from the CLI). Off by default — the tunnel exposes the local IDE port on the remote host (protected by a per-session token), so it's enabled per your trust.This phase also unifies the remote New Session popover and Configure dialog with the local ones (same labels/buttons; only justified differences remain — a Remote Directory picker, and Worktree/Chrome hidden as local-machine features; Pre-launch Command now applies to remote too).
How it works
bash, single-quoted to survivessh's argv re-join + remote-shell re-parse. OneControlMastersocket per host is reused by Browse, indexing, resume, and the IDE tunnel.ssh://<host>/<dir>path.Data / settings
sourcecolumn tosession_cache+search_map(NULL= local; no cache wipe). All folder-scoped queries are source-isolated.remoteIde(default off).Testing
npm test, run under Electron's node to match the app'sbetter-sqlite3ABI): SSH arg assembly +~/.ssh/configparsing + quoting/transport (Phase 1); JSONL parsing, listing/framing/diff/cwd parsers, thesourcemigration + isolation, sync orchestration, sidebar grouping (Phase 2); reverse-forward/cancel arg builders, the IDE lock script, the injected old-file reader (Phase 3). Full suite green.sourcebackfilled to local).Roadmap
sourcecolumn.(Remote Control —
claude --remote-controlfor live cross-device mirroring — is orthogonal to local-vs-remote and is tracked separately, not part of this PR.)🤖 Generated with Claude Code