From claude-code-tunnels
Connects a single workspace on a remote machine to the Claude Code Orchestrator via SSH or kubectl. Use for adding individual remote workspaces to existing local projects.
npx claudepluginhub matteblack9/claude-code-clone --plugin claude-tunnelsThis skill uses the workspace's default tool permissions.
Connects a single workspace on a remote machine to the Orchestrator.
Deploys HTTP listener to remote machine via SSH or kubectl, enabling Orchestrator to use remote project as workspace without query timeouts. Useful for long-running tasks on servers or K8s pods.
Creates and manages isolated Docker workspaces on Tailscale tailnet with pre-installed Claude Code and OpenCode. Use for Perry workspaces, coding agents, or remote development environments.
Manages graft for local-first remote dev: runs shimming commands remotely, syncs files bidirectionally with mutagen, forwards ports, sets up connections via flags or graft.yaml, reads status, diagnoses with graft doctor.
Share bugs, ideas, or general feedback.
Connects a single workspace on a remote machine to the Orchestrator.
Uses the same listener as /claude-code-tunnels:setup-remote-project, but registers at the individual workspace level.
| setup-remote-project | setup-remote-workspace | |
|---|---|---|
| Target | Entire project (including sub-workspaces) | A single specific workspace |
| Registration | PO discovers the project structure | Workspace is specified and registered directly |
| When to use | Moving an entire project to a remote machine | Adding a remote workspace to an existing project |
Connecting a single workspace to a remote listener requires the orchestrator to be installed and an access method to be available. If any check fails, do not proceed to the next step until it is resolved.
Why it is needed: a new workspace must be added to the remote_workspaces array.
Why it is needed: deploy.py and listener.py must be present for remote deployment.
Why it is needed: prevents duplicate registration on the same host:port, and allows recommending a port that does not conflict with existing ones.
Currently registered remote workspaces:
my-project/backend → 10.0.0.5:9100
my-project/frontend → 10.0.0.5:9101
Adding a new workspace.
Auto-detect available tools:
Select the remote access method.
[1] ssh <- detected
[2] kubectl <- detected
Number:
Auto-detect local project directories and present as choices:
# List directories under the root in orchestrator.yaml
root=$(python3 -c "import yaml; print(yaml.safe_load(open('orchestrator.yaml')).get('root','.'))")
ls "$root" # exclude: orchestrator, ARCHIVE, .tasks, .claude, .git, hidden folders
Select the project this workspace belongs to.
A project directory with the same name must exist locally for the PO to recognize it.
[1] my-project
[2] another-project
[3] Enter manually
Number:
Enter the workspace name.
This name will appear in the execution plan.
It will be registered in orchestrator.yaml as "$PROJECT/$WORKSPACE".
Enter (e.g. data-pipeline):
Name to be registered: my-project/data-pipeline
Is this correct? (yes/no)
─────────────────────────────────────────────────────────────────
1. host (required)
The remote machine to connect to via HTTP for the listener.
Enter:
2. user
[1] $USER <- current user
[2] Enter manually
Number:
3. key_file
[1] ~/.ssh/id_rsa <- exists
[2] ~/.ssh/id_ed25519 <- exists
[3] Use default key
[4] Enter manually
Number:
─────────────────────────────────────────────────────────────────
SSH key candidates auto-detected by scanning ~/.ssh/.
After input, immediately run a connection test: ssh $USER@$HOST "echo OK"
Present auto-detected choices in the order: namespace → pod → container (same as setup-remote-project).
Enter the absolute path where the workspace is located on the remote machine.
Enter (e.g. /home/user/my-project/data-pipeline):
Validation: verify existence on the remote machine with test -d.
Listener port — auto-calculate candidates that do not conflict with already-registered ports:
# Check already-used ports
used_ports=(9100 9101) # ports for the same host in orchestrator.yaml
next_port=9102 # next available port
Select the listener port.
Ports 9100 and 9101 are already registered for the same host (10.0.0.5).
[1] 9102 <- next in sequence, available
[2] 9103 <- available
[3] Enter manually
Number:
Auth token:
[1] No authentication
[2] Enter a token
Number:
Why it is needed: the listener uses Python + claude-agent-sdk + aiohttp on the remote machine.
Remote environment check results:
Python: 3.11.5 ✓
claude-agent-sdk: 0.3.0 ✓
aiohttp: 3.9.1 ✓
[1] Continue
— or if any items are missing —
[1] Install on remote now
[2] Install manually and continue
Number:
Deployment summary:
Deployment summary:
workspace: my-project/data-pipeline
Target: irteam@10.0.0.5
Path: /home/user/my-project/data-pipeline
Port: 9102
Token: (none)
Proceed? (yes/no)
After confirmation, deploy + update orchestrator.yaml:
remote_workspaces:
- name: my-project/backend
host: 10.0.0.5
port: 9100
- name: my-project/frontend
host: 10.0.0.5
port: 9101
- name: my-project/data-pipeline # <- newly added
host: 10.0.0.5
port: 9102
token: ""
curl http://$HOST:$LISTENER_PORT/health
# Expected: {"status": "ok", "cwd": "...", "port": 9102}
ssh $USER@$HOST cat /tmp/claude-listener-$LISTENER_PORT.log
kubectl exec $POD -n $NAMESPACE -- cat /tmp/claude-listener-$LISTENER_PORT.log