Skip to content

Commit b71c161

Browse files
grichaclaude
andauthored
Add workspace cloning functionality (#46)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 1969206 commit b71c161

File tree

21 files changed

+890
-22
lines changed

21 files changed

+890
-22
lines changed

docs/docs/cli.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,22 @@ perry shell myproject
147147

148148
Uses direct Docker exec for local agents, WebSocket for remote agents.
149149

150+
### `perry clone <source> <clone-name>`
151+
152+
Clone an existing workspace with all its data.
153+
154+
```bash
155+
perry clone myproject myproject-copy
156+
```
157+
158+
This creates a new workspace by:
159+
- Copying the home volume (all files in `/home/workspace`)
160+
- Copying the Docker-in-Docker volume
161+
- Creating a new container with copied volumes
162+
- Assigning a new SSH port
163+
164+
The source workspace is temporarily stopped during cloning to ensure data consistency.
165+
150166
### `perry sync <name>`
151167

152168
Re-sync credentials and files to a running workspace.

docs/docs/workflows.md

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
---
2+
sidebar_position: 6
3+
---
4+
5+
# Common Workflows
6+
7+
This guide covers common workflows and use cases for Perry workspaces.
8+
9+
## Cloning Workspaces
10+
11+
When you need to experiment with changes without affecting your main workspace, or want to create a backup before making risky modifications, you can clone a workspace.
12+
13+
### Via CLI
14+
15+
```bash
16+
perry clone myproject myproject-experiment
17+
```
18+
19+
### Via Web UI
20+
21+
1. Open the workspace you want to clone
22+
2. Click the clone icon (next to the settings gear) in the navbar, or
23+
3. Go to Settings and click "Clone" in the Clone Workspace section
24+
4. Enter a name for the new workspace
25+
5. Click "Clone Workspace"
26+
27+
### Via Mobile App
28+
29+
1. Open the workspace details
30+
2. Go to Settings
31+
3. Tap "Clone Workspace" in the Clone section
32+
4. Enter a name for the new workspace
33+
5. Tap "Clone"
34+
35+
### What Gets Cloned
36+
37+
- All files in `/home/workspace` (your code, configurations, etc.)
38+
- Docker-in-Docker state (containers, images, volumes)
39+
- A new SSH port is assigned automatically
40+
41+
### Notes
42+
43+
- The source workspace is temporarily stopped during cloning to ensure data consistency
44+
- After cloning, both workspaces can run independently
45+
- Credentials are synced to the new workspace automatically
46+
47+
## Setting Up Development Environment
48+
49+
### Quick Start for a New Project
50+
51+
```bash
52+
# Create workspace with a git repo
53+
perry start my-app --clone https://github.com/user/repo.git
54+
55+
# Open a shell
56+
perry shell my-app
57+
58+
# Or use an AI agent
59+
claude
60+
```
61+
62+
### Syncing After Credential Changes
63+
64+
If you update your credentials or SSH keys on the host:
65+
66+
```bash
67+
perry sync my-app
68+
```
69+
70+
Or use the "Sync Credentials" button in the Web UI settings.
71+
72+
## Port Forwarding
73+
74+
When running web servers or services in your workspace:
75+
76+
### Configure Persistent Ports
77+
78+
```bash
79+
# Set ports that auto-forward with perry proxy
80+
perry ports my-app 3000 5173
81+
82+
# Forward them
83+
perry proxy my-app
84+
```
85+
86+
### One-Time Port Forward
87+
88+
```bash
89+
# Forward specific ports without saving
90+
perry proxy my-app 8080:3000 # Local 8080 -> workspace 3000
91+
```
92+
93+
## Multi-Workspace Development
94+
95+
### Working on Related Projects
96+
97+
```bash
98+
# Create workspaces for different services
99+
perry start frontend --clone https://github.com/user/frontend.git
100+
perry start backend --clone https://github.com/user/backend.git
101+
perry start shared-lib --clone https://github.com/user/shared.git
102+
103+
# List all workspaces
104+
perry list
105+
```
106+
107+
### Branching Strategy with Clones
108+
109+
When working on a feature that might break things:
110+
111+
```bash
112+
# Clone your stable workspace
113+
perry clone main-project feature-experiment
114+
115+
# Work on the feature in the clone
116+
perry shell feature-experiment
117+
118+
# If it works, you can delete the original and rename
119+
# Or just continue working in the clone
120+
```
121+
122+
## Using AI Agents
123+
124+
### Claude Code
125+
126+
```bash
127+
perry shell my-app
128+
claude
129+
```
130+
131+
Or use the Web UI's chat interface directly.
132+
133+
### OpenCode
134+
135+
```bash
136+
perry shell my-app
137+
opencode
138+
```
139+
140+
### Codex
141+
142+
```bash
143+
perry shell my-app
144+
codex
145+
```
146+
147+
## Troubleshooting Common Issues
148+
149+
### Workspace Won't Start
150+
151+
```bash
152+
# Check logs
153+
perry logs my-app
154+
155+
# If container was deleted externally, starting will recreate it
156+
perry start my-app
157+
```
158+
159+
### Credentials Not Working
160+
161+
```bash
162+
# Re-sync credentials
163+
perry sync my-app
164+
165+
# Check what files are configured
166+
perry config agent
167+
```
168+
169+
### SSH Connection Issues
170+
171+
```bash
172+
# Check SSH configuration
173+
perry ssh show
174+
175+
# Verify authorized keys
176+
perry ssh list
177+
```

docs/sidebars.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const sidebars: SidebarsConfig = {
1818
],
1919
},
2020
'cli',
21+
'workflows',
2122
'web-ui',
2223
'troubleshooting',
2324
],

mobile/src/lib/api.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ function createClient() {
156156
logs: (input: { name: string; tail?: number }) => Promise<string>
157157
sync: (input: { name: string }) => Promise<{ success: boolean }>
158158
syncAll: () => Promise<{ synced: number; failed: number; results: { name: string; success: boolean; error?: string }[] }>
159+
clone: (input: { sourceName: string; cloneName: string }) => Promise<WorkspaceInfo>
159160
}
160161
sessions: {
161162
list: (input: {
@@ -236,6 +237,8 @@ export const api = {
236237
getLogs: (name: string, tail = 100) => client.workspaces.logs({ name, tail }),
237238
syncWorkspace: (name: string) => client.workspaces.sync({ name }),
238239
syncAllWorkspaces: () => client.workspaces.syncAll(),
240+
cloneWorkspace: (sourceName: string, cloneName: string) =>
241+
client.workspaces.clone({ sourceName, cloneName }),
239242
listSessions: (workspaceName: string, agentType?: AgentType, limit?: number, offset?: number) =>
240243
client.sessions.list({ workspaceName, agentType, limit, offset }),
241244
listAllSessions: (agentType?: AgentType, limit?: number, offset?: number) =>

0 commit comments

Comments
 (0)