worktree-compose 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +118 -430
- package/package.json +9 -1
package/README.md
CHANGED
|
@@ -1,172 +1,137 @@
|
|
|
1
1
|
# worktree-compose (wtc)
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## The Problem
|
|
6
|
-
|
|
7
|
-
You're running multiple AI agents (or developers) in parallel on the same repo, each in its own [git worktree](https://git-scm.com/docs/git-worktree). Without isolation, they all share the same Docker Compose setup — same Postgres, same Redis, same backend, same frontend. This means:
|
|
3
|
+
[](https://www.npmjs.com/package/worktree-compose)
|
|
8
4
|
|
|
9
|
-
-
|
|
10
|
-
- **Shared database** — agents overwrite each other's data
|
|
11
|
-
- **Shared cache** — one agent's Redis state leaks into another's
|
|
12
|
-
- **Container collisions** — `docker compose up` in one worktree kills the other's containers
|
|
13
|
-
- **No comparison** — you can't open two frontends side by side to compare agent outputs
|
|
14
|
-
|
|
15
|
-
You need each worktree to have its own fully isolated stack: its own database, its own cache, its own ports, its own containers. But setting this up manually for every worktree is tedious and error-prone.
|
|
16
|
-
|
|
17
|
-
## The Solution
|
|
18
|
-
|
|
19
|
-
`wtc` gives every git worktree its own Docker Compose stack automatically.
|
|
20
|
-
|
|
21
|
-
It reads your `docker-compose.yml`, finds every service that exposes a port, assigns unique ports per worktree, injects them into each worktree's `.env`, and starts isolated containers. No configuration needed.
|
|
22
|
-
|
|
23
|
-
Each worktree gets:
|
|
24
|
-
- **Its own ports** — no collisions
|
|
25
|
-
- **Its own database** — no shared state
|
|
26
|
-
- **Its own cache** — no leaking
|
|
27
|
-
- **Its own containers** — independent lifecycles
|
|
28
|
-
- **Its own URL** — open them side by side and compare
|
|
5
|
+
Zero-config Docker Compose isolation for git worktrees.
|
|
29
6
|
|
|
30
|
-
|
|
7
|
+
Every worktree gets its own ports, database, cache, and containers — automatically.
|
|
31
8
|
|
|
32
9
|
```bash
|
|
33
10
|
npm install -D worktree-compose
|
|
34
|
-
# or
|
|
35
|
-
pnpm add -D worktree-compose
|
|
36
|
-
# or
|
|
37
|
-
yarn add -D worktree-compose
|
|
38
11
|
```
|
|
39
12
|
|
|
40
|
-
|
|
13
|
+
```
|
|
14
|
+
npx wtc list
|
|
15
|
+
|
|
16
|
+
┌───────┬───────────────┬────────┬────────────────────────┬─────────────────────────────────────────────────────────┐
|
|
17
|
+
│ Index │ Branch │ Status │ URL │ Ports │
|
|
18
|
+
├───────┼───────────────┼────────┼────────────────────────┼─────────────────────────────────────────────────────────┤
|
|
19
|
+
│ - │ main │ - │ - │ postgres:5434 redis:6380 backend:8000 frontend:5173 │
|
|
20
|
+
├───────┼───────────────┼────────┼────────────────────────┼─────────────────────────────────────────────────────────┤
|
|
21
|
+
│ 1 │ feature-auth │ up │ http://localhost:25174 │ postgres:25435 redis:26381 backend:28001 frontend:25174 │
|
|
22
|
+
├───────┼───────────────┼────────┼────────────────────────┼─────────────────────────────────────────────────────────┤
|
|
23
|
+
│ 2 │ fix-billing │ down │ http://localhost:25175 │ postgres:25436 redis:26382 backend:28002 frontend:25175 │
|
|
24
|
+
└───────┴───────────────┴────────┴────────────────────────┴─────────────────────────────────────────────────────────┘
|
|
25
|
+
```
|
|
41
26
|
|
|
42
|
-
##
|
|
27
|
+
## Usage
|
|
43
28
|
|
|
44
29
|
```bash
|
|
45
|
-
#
|
|
46
|
-
git worktree add ../feature-1 feature-1
|
|
47
|
-
git worktree add ../feature-2 feature-2
|
|
48
|
-
|
|
49
|
-
# 2. Start isolated stacks for all worktrees
|
|
30
|
+
# Start isolated stacks for all worktrees
|
|
50
31
|
npx wtc start
|
|
51
32
|
|
|
52
|
-
#
|
|
33
|
+
# Start specific worktrees
|
|
34
|
+
npx wtc start 1
|
|
35
|
+
npx wtc start 1 2 3
|
|
36
|
+
|
|
37
|
+
# See what's running
|
|
53
38
|
npx wtc list
|
|
54
39
|
|
|
55
|
-
#
|
|
40
|
+
# Stop worktrees
|
|
41
|
+
npx wtc stop
|
|
42
|
+
npx wtc stop 1
|
|
43
|
+
|
|
44
|
+
# Restart (re-sync files, rebuild containers)
|
|
45
|
+
npx wtc restart 1
|
|
56
46
|
|
|
57
|
-
#
|
|
47
|
+
# Pull a worktree's changes into your current branch
|
|
58
48
|
npx wtc promote 1
|
|
59
49
|
|
|
60
|
-
#
|
|
50
|
+
# Tear down everything (containers, worktrees, volumes)
|
|
61
51
|
npx wtc clean
|
|
62
52
|
```
|
|
63
53
|
|
|
64
|
-
##
|
|
65
|
-
|
|
66
|
-
### 1. Auto-Detection
|
|
54
|
+
## The Problem
|
|
67
55
|
|
|
68
|
-
|
|
56
|
+
Multiple developers or AI agents working in parallel on the same repo — each in a [git worktree](https://git-scm.com/docs/git-worktree) — share the same Docker Compose setup. This means port conflicts, shared databases, shared caches, and container collisions. You can't run two stacks side by side.
|
|
69
57
|
|
|
70
|
-
|
|
71
|
-
2. `compose.yml`
|
|
72
|
-
3. `docker-compose.yaml`
|
|
73
|
-
4. `docker-compose.yml`
|
|
58
|
+
## The Solution
|
|
74
59
|
|
|
75
|
-
|
|
60
|
+
`wtc` reads your `docker-compose.yml`, finds every service that exposes a port via `${VAR:-default}`, assigns unique ports per worktree, injects them into each worktree's `.env`, and starts isolated containers. No configuration needed.
|
|
76
61
|
|
|
77
|
-
|
|
62
|
+
## Preparing Your docker-compose.yml
|
|
78
63
|
|
|
79
|
-
|
|
64
|
+
For `wtc` to isolate a service's port, the host port must use the `${VAR:-default}` pattern:
|
|
80
65
|
|
|
81
66
|
```yaml
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
ports:
|
|
86
|
-
- "${POSTGRES_PORT:-5434}:5432"
|
|
87
|
-
|
|
88
|
-
redis:
|
|
89
|
-
image: redis:7-alpine
|
|
90
|
-
ports:
|
|
91
|
-
- "${REDIS_PORT:-6380}:6379"
|
|
92
|
-
|
|
93
|
-
backend:
|
|
94
|
-
build:
|
|
95
|
-
context: ./backend
|
|
96
|
-
dockerfile: Dockerfile.dev
|
|
97
|
-
ports:
|
|
98
|
-
- "${BACKEND_PORT:-8000}:8000"
|
|
99
|
-
|
|
100
|
-
worker:
|
|
101
|
-
build:
|
|
102
|
-
context: ./backend
|
|
103
|
-
dockerfile: Dockerfile.dev
|
|
104
|
-
# No ports — this service is ignored by wtc
|
|
105
|
-
|
|
106
|
-
frontend:
|
|
107
|
-
build:
|
|
108
|
-
context: ./frontend
|
|
109
|
-
dockerfile: Dockerfile.dev
|
|
110
|
-
ports:
|
|
111
|
-
- "${FRONTEND_PORT:-5173}:${FRONTEND_PORT:-5173}"
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
`wtc` detects 4 overridable ports (postgres, redis, backend, frontend) and ignores the worker (no port mapping).
|
|
67
|
+
# wtc CAN isolate this
|
|
68
|
+
ports:
|
|
69
|
+
- "${BACKEND_PORT:-8000}:8000"
|
|
115
70
|
|
|
116
|
-
|
|
71
|
+
# wtc CANNOT isolate this (hardcoded)
|
|
72
|
+
ports:
|
|
73
|
+
- "8080:8080"
|
|
74
|
+
```
|
|
117
75
|
|
|
118
|
-
|
|
76
|
+
If `wtc` finds hardcoded ports, it warns you and suggests the fix:
|
|
119
77
|
|
|
120
78
|
```
|
|
121
|
-
|
|
79
|
+
⚠ Service "nginx" uses a raw port mapping (8080:80).
|
|
80
|
+
To enable port isolation, change it to: "${NGINX_PORT:-8080}:80"
|
|
122
81
|
```
|
|
123
82
|
|
|
124
|
-
|
|
125
|
-
|------------|---------------|------------|------------|------------|
|
|
126
|
-
| postgres | 5434 | 25435 | 25436 | 25437 |
|
|
127
|
-
| redis | 6380 | 26381 | 26382 | 26383 |
|
|
128
|
-
| backend | 8000 | 28001 | 28002 | 28003 |
|
|
129
|
-
| frontend | 5173 | 25174 | 25175 | 25176 |
|
|
83
|
+
### Supported port formats
|
|
130
84
|
|
|
131
|
-
|
|
85
|
+
```yaml
|
|
86
|
+
# Standard
|
|
87
|
+
- "${BACKEND_PORT:-8000}:8000"
|
|
88
|
+
|
|
89
|
+
# Same var for host and container
|
|
90
|
+
- "${FRONTEND_PORT:-5173}:${FRONTEND_PORT:-5173}"
|
|
132
91
|
|
|
133
|
-
|
|
92
|
+
# IP-bound
|
|
93
|
+
- "127.0.0.1:${API_PORT:-3000}:3000"
|
|
134
94
|
|
|
135
|
-
|
|
95
|
+
# With protocol
|
|
96
|
+
- "${BACKEND_PORT:-8000}:8000/tcp"
|
|
136
97
|
|
|
137
|
-
|
|
98
|
+
# Multiple ports per service
|
|
99
|
+
- "${BACKEND_PORT:-8000}:8000"
|
|
100
|
+
- "${DEBUG_PORT:-9229}:9229"
|
|
138
101
|
|
|
102
|
+
# Long-form syntax
|
|
103
|
+
- target: 8000
|
|
104
|
+
published: "${BACKEND_PORT:-8000}"
|
|
105
|
+
protocol: tcp
|
|
139
106
|
```
|
|
140
|
-
{repo-name}-wt-{index}-{branch}
|
|
141
|
-
```
|
|
142
107
|
|
|
143
|
-
|
|
108
|
+
## How It Works
|
|
109
|
+
|
|
110
|
+
### Port Allocation
|
|
144
111
|
|
|
145
|
-
|
|
146
|
-
- **Containers** — `myapp-wt-1-feature-auth-postgres-1`, etc.
|
|
147
|
-
- **Networks** — `myapp-wt-1-feature-auth_default`
|
|
148
|
-
- **Volumes** — `myapp-wt-1-feature-auth_pg-data`
|
|
112
|
+
Each worktree N gets unique ports: `20000 + default_port + worktree_index`
|
|
149
113
|
|
|
150
|
-
|
|
114
|
+
| Service | Main (default) | Worktree 1 | Worktree 2 | Worktree 3 |
|
|
115
|
+
|----------|---------------|------------|------------|------------|
|
|
116
|
+
| postgres | 5434 | 25435 | 25436 | 25437 |
|
|
117
|
+
| redis | 6380 | 26381 | 26382 | 26383 |
|
|
118
|
+
| backend | 8000 | 28001 | 28002 | 28003 |
|
|
119
|
+
| frontend | 5173 | 25174 | 25175 | 25176 |
|
|
151
120
|
|
|
152
|
-
###
|
|
121
|
+
### Container Isolation
|
|
153
122
|
|
|
154
|
-
|
|
123
|
+
Each worktree gets its own `COMPOSE_PROJECT_NAME`: `{repo}-wt-{index}-{branch}`. This means separate containers, networks, and volumes. Nothing is shared.
|
|
155
124
|
|
|
156
|
-
|
|
157
|
-
- Every Dockerfile referenced in `build.dockerfile` fields
|
|
158
|
-
- `.env` (or `.env.example` if `.env` doesn't exist)
|
|
125
|
+
### File Sync
|
|
159
126
|
|
|
160
|
-
|
|
127
|
+
Before starting, `wtc` copies infrastructure files from main into the worktree: the compose file, every Dockerfile referenced in `build.dockerfile`, and `.env`. This ensures the worktree always has the latest Docker setup.
|
|
161
128
|
|
|
162
|
-
###
|
|
129
|
+
### Env Injection
|
|
163
130
|
|
|
164
|
-
After copying `.env`, wtc appends
|
|
131
|
+
After copying `.env`, `wtc` appends an idempotent block with allocated port overrides:
|
|
165
132
|
|
|
166
133
|
```bash
|
|
167
134
|
# existing .env content stays untouched...
|
|
168
|
-
OPENAI_API_KEY=sk-...
|
|
169
|
-
DATABASE_URL=...
|
|
170
135
|
|
|
171
136
|
# --- wtc port overrides ---
|
|
172
137
|
POSTGRES_PORT=25435
|
|
@@ -176,222 +141,62 @@ FRONTEND_PORT=25174
|
|
|
176
141
|
# --- end wtc ---
|
|
177
142
|
```
|
|
178
143
|
|
|
179
|
-
This is **idempotent** — running `wtc start` again strips the old block and writes a fresh one.
|
|
180
|
-
|
|
181
144
|
## Commands
|
|
182
145
|
|
|
183
146
|
### `wtc start [indices...]`
|
|
184
147
|
|
|
185
|
-
Start Docker Compose stacks
|
|
148
|
+
Start Docker Compose stacks. Syncs files, injects ports, runs `docker compose up -d --build`.
|
|
186
149
|
|
|
187
150
|
```bash
|
|
188
|
-
npx wtc start #
|
|
189
|
-
npx wtc start 1 #
|
|
190
|
-
npx wtc start 1 2 3 #
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
**What it does for each worktree:**
|
|
194
|
-
|
|
195
|
-
1. Syncs the compose file and Dockerfiles from main
|
|
196
|
-
2. Copies `.env` and injects port overrides
|
|
197
|
-
3. Runs `docker compose up -d --build` with the worktree's unique project name
|
|
198
|
-
|
|
199
|
-
**Output:**
|
|
200
|
-
|
|
151
|
+
npx wtc start # all worktrees
|
|
152
|
+
npx wtc start 1 # worktree 1 only
|
|
153
|
+
npx wtc start 1 2 3 # worktrees 1, 2, and 3
|
|
201
154
|
```
|
|
202
|
-
=== Worktree 1: feature-auth ===
|
|
203
|
-
ℹ Path: /Users/you/myapp-feature-auth
|
|
204
|
-
ℹ Project: myapp-wt-1-feature-auth
|
|
205
|
-
ℹ Ports: POSTGRES_PORT=25435 REDIS_PORT=26381 BACKEND_PORT=28001 FRONTEND_PORT=25174
|
|
206
|
-
✔ Synced infrastructure files
|
|
207
|
-
✔ Injected port overrides into .env
|
|
208
|
-
[+] Running 5/5
|
|
209
|
-
✔ Container myapp-wt-1-feature-auth-postgres-1 Started
|
|
210
|
-
✔ Container myapp-wt-1-feature-auth-redis-1 Started
|
|
211
|
-
✔ Container myapp-wt-1-feature-auth-backend-1 Started
|
|
212
|
-
✔ Container myapp-wt-1-feature-auth-frontend-1 Started
|
|
213
|
-
✔ Worktree 1 started
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
After starting, it prints a table of all worktrees with their URLs.
|
|
217
155
|
|
|
218
156
|
### `wtc stop [indices...]`
|
|
219
157
|
|
|
220
|
-
Stop
|
|
158
|
+
Stop stacks. Runs `docker compose down`. Volumes are preserved.
|
|
221
159
|
|
|
222
160
|
```bash
|
|
223
|
-
npx wtc stop #
|
|
224
|
-
npx wtc stop 1 #
|
|
225
|
-
npx wtc stop 1 2 # stop worktrees 1 and 2
|
|
161
|
+
npx wtc stop # all
|
|
162
|
+
npx wtc stop 1 # worktree 1 only
|
|
226
163
|
```
|
|
227
164
|
|
|
228
|
-
Runs `docker compose down` for each target worktree. Data in named volumes is preserved — only containers and networks are removed.
|
|
229
|
-
|
|
230
165
|
### `wtc restart [indices...]`
|
|
231
166
|
|
|
232
|
-
|
|
167
|
+
Full restart: stop, re-sync files, re-inject env, rebuild, start. Use after migrations, Dockerfile changes, or config updates.
|
|
233
168
|
|
|
234
169
|
```bash
|
|
235
|
-
npx wtc restart
|
|
236
|
-
npx wtc restart 1 # restart worktree 1 only
|
|
237
|
-
npx wtc restart 1 2 # restart worktrees 1 and 2
|
|
170
|
+
npx wtc restart 1
|
|
238
171
|
```
|
|
239
172
|
|
|
240
|
-
**When to use restart:**
|
|
241
|
-
- An agent wrote a database migration that needs to run on startup
|
|
242
|
-
- Dockerfiles were changed and containers need rebuilding
|
|
243
|
-
- The compose file was modified
|
|
244
|
-
- `.env` values in main changed and need to be re-synced
|
|
245
|
-
|
|
246
173
|
### `wtc list` / `wtc ls`
|
|
247
174
|
|
|
248
|
-
Show all worktrees with
|
|
175
|
+
Show all worktrees with branch, status, URL, and ports.
|
|
249
176
|
|
|
250
177
|
```bash
|
|
251
178
|
npx wtc list
|
|
252
179
|
```
|
|
253
180
|
|
|
254
|
-
**Output:**
|
|
255
|
-
|
|
256
|
-
```
|
|
257
|
-
┌───────┬───────────────┬────────┬────────────────────────┬─────────────────────────────────────────────────────────┐
|
|
258
|
-
│ Index │ Branch │ Status │ URL │ Ports │
|
|
259
|
-
├───────┼───────────────┼────────┼────────────────────────┼─────────────────────────────────────────────────────────┤
|
|
260
|
-
│ - │ main │ - │ - │ postgres:5434 redis:6380 backend:8000 frontend:5173 │
|
|
261
|
-
├───────┼───────────────┼────────┼────────────────────────┼─────────────────────────────────────────────────────────┤
|
|
262
|
-
│ 1 │ feature-auth │ up │ http://localhost:25174 │ postgres:25435 redis:26381 backend:28001 frontend:25174 │
|
|
263
|
-
├───────┼───────────────┼────────┼────────────────────────┼─────────────────────────────────────────────────────────┤
|
|
264
|
-
│ 2 │ fix-billing │ down │ http://localhost:25175 │ postgres:25436 redis:26382 backend:28002 frontend:25175 │
|
|
265
|
-
└───────┴───────────────┴────────┴────────────────────────┴─────────────────────────────────────────────────────────┘
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
- **Index** — the number you pass to other commands (`wtc start 1`, `wtc promote 2`)
|
|
269
|
-
- **Branch** — the git branch checked out in that worktree
|
|
270
|
-
- **Status** — whether Docker containers are currently running
|
|
271
|
-
- **URL** — the frontend URL (auto-detected from services named `frontend`, `web`, `app`, or `ui`)
|
|
272
|
-
- **Ports** — all allocated ports for that worktree
|
|
273
|
-
|
|
274
181
|
### `wtc promote <index>`
|
|
275
182
|
|
|
276
|
-
Copy
|
|
183
|
+
Copy changed files from a worktree into your current branch as uncommitted changes. Automatically excludes `.env` and compose files. Aborts if it would overwrite uncommitted local changes.
|
|
277
184
|
|
|
278
185
|
```bash
|
|
279
186
|
npx wtc promote 1
|
|
280
187
|
```
|
|
281
188
|
|
|
282
|
-
**What it does:**
|
|
283
|
-
|
|
284
|
-
1. Finds the divergence point between your current branch and the worktree's branch (`git merge-base`)
|
|
285
|
-
2. Collects all files that changed in the worktree (committed, uncommitted, and untracked)
|
|
286
|
-
3. **Safety check** — aborts if any of those files have uncommitted changes in your current branch (prevents overwriting your work)
|
|
287
|
-
4. Copies changed files into your repo (or deletes files that were removed in the worktree)
|
|
288
|
-
5. Leaves everything as uncommitted changes so you can review before committing
|
|
289
|
-
|
|
290
|
-
**What it excludes automatically:**
|
|
291
|
-
- `.env` — this was injected by wtc, not authored by the agent
|
|
292
|
-
- `docker-compose.yml` / `compose.yml` — synced from main, not a real change
|
|
293
|
-
|
|
294
|
-
**Output:**
|
|
295
|
-
|
|
296
|
-
```
|
|
297
|
-
ℹ Promoting worktree 1 (feature-auth) into main
|
|
298
|
-
✔ Promoted 12 file(s). Changes are uncommitted in main.
|
|
299
|
-
src/auth/login.ts
|
|
300
|
-
src/auth/session.ts
|
|
301
|
-
src/middleware/auth.ts
|
|
302
|
-
...
|
|
303
|
-
```
|
|
304
|
-
|
|
305
|
-
**If there's a conflict:**
|
|
306
|
-
|
|
307
|
-
```
|
|
308
|
-
✖ Abort: the following files have uncommitted changes and would be overwritten:
|
|
309
|
-
src/auth/login.ts
|
|
310
|
-
|
|
311
|
-
Commit or stash your local changes first, then re-run promote.
|
|
312
|
-
```
|
|
313
|
-
|
|
314
189
|
### `wtc clean`
|
|
315
190
|
|
|
316
|
-
Stop all
|
|
191
|
+
Stop all containers, remove all worktrees, prune stale Docker resources.
|
|
317
192
|
|
|
318
193
|
```bash
|
|
319
194
|
npx wtc clean
|
|
320
195
|
```
|
|
321
196
|
|
|
322
|
-
**What it does:**
|
|
323
|
-
|
|
324
|
-
1. For each non-main worktree:
|
|
325
|
-
- Stops its Docker Compose stack (`docker compose down`)
|
|
326
|
-
- Removes the git worktree (`git worktree remove --force`)
|
|
327
|
-
2. Prunes stale git worktree references (`git worktree prune`)
|
|
328
|
-
3. Removes any orphaned Docker containers, networks, and volumes matching the `*-wt-*` pattern
|
|
329
|
-
|
|
330
|
-
**Output:**
|
|
331
|
-
|
|
332
|
-
```
|
|
333
|
-
ℹ Stopping containers for myapp-wt-1-feature-auth...
|
|
334
|
-
ℹ Removing worktree: /Users/you/myapp-feature-auth
|
|
335
|
-
ℹ Stopping containers for myapp-wt-2-fix-billing...
|
|
336
|
-
ℹ Removing worktree: /Users/you/myapp-fix-billing
|
|
337
|
-
✔ Cleanup complete.
|
|
338
|
-
```
|
|
339
|
-
|
|
340
|
-
### `wtc mcp`
|
|
341
|
-
|
|
342
|
-
Start the MCP (Model Context Protocol) server. This is not meant to be run manually — it's used by AI agents. See the [MCP Server](#mcp-server) section below.
|
|
343
|
-
|
|
344
|
-
## Preparing Your docker-compose.yml
|
|
345
|
-
|
|
346
|
-
For `wtc` to isolate a service's port, the host port must use the `${VAR:-default}` pattern:
|
|
347
|
-
|
|
348
|
-
```yaml
|
|
349
|
-
# wtc CAN isolate this (env var pattern)
|
|
350
|
-
ports:
|
|
351
|
-
- "${BACKEND_PORT:-8000}:8000"
|
|
352
|
-
|
|
353
|
-
# wtc CANNOT isolate this (hardcoded number)
|
|
354
|
-
ports:
|
|
355
|
-
- "8080:8080"
|
|
356
|
-
```
|
|
357
|
-
|
|
358
|
-
If `wtc` finds hardcoded ports, it warns you and suggests the fix:
|
|
359
|
-
|
|
360
|
-
```
|
|
361
|
-
⚠ Service "nginx" uses a raw port mapping (8080:80).
|
|
362
|
-
To enable port isolation, change it to: "${NGINX_PORT:-8080}:80"
|
|
363
|
-
```
|
|
364
|
-
|
|
365
|
-
### Supported port formats
|
|
366
|
-
|
|
367
|
-
`wtc` handles all common Docker Compose port syntaxes:
|
|
368
|
-
|
|
369
|
-
```yaml
|
|
370
|
-
# Standard — most common
|
|
371
|
-
- "${BACKEND_PORT:-8000}:8000"
|
|
372
|
-
|
|
373
|
-
# Same var for host and container (when the app reads the port from env)
|
|
374
|
-
- "${FRONTEND_PORT:-5173}:${FRONTEND_PORT:-5173}"
|
|
375
|
-
|
|
376
|
-
# IP-bound
|
|
377
|
-
- "127.0.0.1:${API_PORT:-3000}:3000"
|
|
378
|
-
|
|
379
|
-
# With protocol
|
|
380
|
-
- "${BACKEND_PORT:-8000}:8000/tcp"
|
|
381
|
-
|
|
382
|
-
# Multiple ports per service
|
|
383
|
-
- "${BACKEND_PORT:-8000}:8000"
|
|
384
|
-
- "${DEBUG_PORT:-9229}:9229"
|
|
385
|
-
|
|
386
|
-
# Long-form syntax
|
|
387
|
-
- target: 8000
|
|
388
|
-
published: "${BACKEND_PORT:-8000}"
|
|
389
|
-
protocol: tcp
|
|
390
|
-
```
|
|
391
|
-
|
|
392
197
|
## Configuration (Optional)
|
|
393
198
|
|
|
394
|
-
`wtc` works zero-config
|
|
199
|
+
`wtc` works zero-config. For project-specific needs, create `.wtcrc.json` in your repo root:
|
|
395
200
|
|
|
396
201
|
```json
|
|
397
202
|
{
|
|
@@ -402,80 +207,23 @@ If `wtc` finds hardcoded ports, it warns you and suggests the fix:
|
|
|
402
207
|
}
|
|
403
208
|
```
|
|
404
209
|
|
|
405
|
-
Or
|
|
406
|
-
|
|
407
|
-
```json
|
|
408
|
-
{
|
|
409
|
-
"wtc": {
|
|
410
|
-
"sync": ["backend/alembic"],
|
|
411
|
-
"envOverrides": {
|
|
412
|
-
"VITE_API_URL": "http://localhost:${BACKEND_PORT}"
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
```
|
|
417
|
-
|
|
418
|
-
Config is looked for in this order:
|
|
419
|
-
1. `.wtcrc.json` in repo root
|
|
420
|
-
2. `"wtc"` key in `package.json`
|
|
421
|
-
3. No config (zero-config defaults)
|
|
210
|
+
Or use a `"wtc"` key in `package.json`.
|
|
422
211
|
|
|
423
212
|
### `sync`
|
|
424
213
|
|
|
425
|
-
Extra files
|
|
426
|
-
|
|
427
|
-
**Why this exists:** Git worktrees check out a branch's files, but the branch may not have the latest infrastructure files (migration configs, seed scripts, etc.). This setting ensures every worktree gets fresh copies of these files from main.
|
|
428
|
-
|
|
429
|
-
**Example:** Your backend uses Alembic for database migrations. The migration files live in `backend/alembic/` and the config in `backend/alembic.ini`. Without syncing these, a worktree on an older branch would have outdated migration files.
|
|
430
|
-
|
|
431
|
-
```json
|
|
432
|
-
{
|
|
433
|
-
"sync": [
|
|
434
|
-
"backend/alembic",
|
|
435
|
-
"backend/alembic.ini"
|
|
436
|
-
]
|
|
437
|
-
}
|
|
438
|
-
```
|
|
439
|
-
|
|
440
|
-
Both files and directories are supported. Directories are copied recursively.
|
|
214
|
+
Extra files/directories to copy from main into each worktree on start. Use for migration configs, seed scripts, or anything not on the worktree's branch.
|
|
441
215
|
|
|
442
216
|
### `envOverrides`
|
|
443
217
|
|
|
444
|
-
Additional
|
|
445
|
-
|
|
446
|
-
**Why this exists:** Some env vars depend on allocated ports. For example, a frontend might need `VITE_API_URL` pointing to the backend's allocated port. Since `wtc` assigns different backend ports per worktree, this URL must be derived dynamically.
|
|
447
|
-
|
|
448
|
-
```json
|
|
449
|
-
{
|
|
450
|
-
"envOverrides": {
|
|
451
|
-
"VITE_API_URL": "http://localhost:${BACKEND_PORT}",
|
|
452
|
-
"VITE_WS_URL": "ws://localhost:${BACKEND_PORT}/ws"
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
```
|
|
456
|
-
|
|
457
|
-
With worktree 1 getting `BACKEND_PORT=28001`, this produces:
|
|
458
|
-
|
|
459
|
-
```bash
|
|
460
|
-
VITE_API_URL=http://localhost:28001
|
|
461
|
-
VITE_WS_URL=ws://localhost:28001/ws
|
|
462
|
-
```
|
|
463
|
-
|
|
464
|
-
You can reference any env var that `wtc` allocates (any `${VAR:-default}` port pattern found in your compose file).
|
|
218
|
+
Additional env vars injected into `.env`. Supports `${VAR}` interpolation with allocated port values. Use when env vars depend on allocated ports (e.g. `VITE_API_URL`).
|
|
465
219
|
|
|
466
220
|
## MCP Server
|
|
467
221
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
### Why
|
|
471
|
-
|
|
472
|
-
When an AI agent is working in a worktree and makes changes that need a container restart (like adding a database migration), it needs a way to restart its stack. The MCP server exposes all `wtc` commands as tools that agents can call directly.
|
|
222
|
+
Built-in [MCP](https://modelcontextprotocol.io/) server so AI agents can manage their stack programmatically.
|
|
473
223
|
|
|
474
224
|
### Setup
|
|
475
225
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
**Claude Code** (`.claude/settings.json` or `.claude/settings.local.json`):
|
|
226
|
+
**Claude Code** (`.claude/settings.json`):
|
|
479
227
|
|
|
480
228
|
```json
|
|
481
229
|
{
|
|
@@ -501,117 +249,57 @@ The MCP server uses stdio transport. Configure it in your agent's MCP settings:
|
|
|
501
249
|
}
|
|
502
250
|
```
|
|
503
251
|
|
|
504
|
-
###
|
|
252
|
+
### Tools
|
|
505
253
|
|
|
506
254
|
| Tool | Parameters | Description |
|
|
507
255
|
|------|-----------|-------------|
|
|
508
|
-
| `wtc_start` | `indices?: number[]` | Start worktree stacks
|
|
509
|
-
| `wtc_stop` | `indices?: number[]` | Stop worktree stacks
|
|
510
|
-
| `wtc_restart` | `indices?: number[]` | Restart
|
|
511
|
-
| `wtc_list` | none | List
|
|
512
|
-
| `wtc_promote` | `index: number` |
|
|
513
|
-
| `wtc_clean` | none |
|
|
256
|
+
| `wtc_start` | `indices?: number[]` | Start worktree stacks |
|
|
257
|
+
| `wtc_stop` | `indices?: number[]` | Stop worktree stacks |
|
|
258
|
+
| `wtc_restart` | `indices?: number[]` | Restart after migrations/config changes |
|
|
259
|
+
| `wtc_list` | none | List worktrees (returns JSON) |
|
|
260
|
+
| `wtc_promote` | `index: number` | Pull worktree changes into current branch |
|
|
261
|
+
| `wtc_clean` | none | Tear down everything |
|
|
514
262
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
1. **Human** creates worktrees and runs `npx wtc start`
|
|
518
|
-
2. **Agents** are spawned in separate worktrees, each with its own isolated stack
|
|
519
|
-
3. An agent writes a database migration, then calls `wtc_restart` with its worktree index — containers restart, the migration runs on startup
|
|
520
|
-
4. The agent verifies the migration worked by hitting its own backend URL
|
|
521
|
-
5. **Human** opens each worktree's frontend URL, compares the results side by side
|
|
522
|
-
6. **Human** runs `npx wtc promote 1` to pull the best agent's changes into main
|
|
523
|
-
7. **Human** runs `npx wtc clean` to tear everything down
|
|
524
|
-
|
|
525
|
-
## Full Example Walkthrough
|
|
526
|
-
|
|
527
|
-
Here's a complete end-to-end example with a typical web app:
|
|
263
|
+
## Full Example
|
|
528
264
|
|
|
529
265
|
```bash
|
|
530
|
-
# You have a repo with docker-compose.yml containing postgres, redis, backend, frontend
|
|
531
266
|
cd myapp
|
|
532
|
-
|
|
533
|
-
# Install wtc
|
|
534
267
|
pnpm add -D worktree-compose
|
|
535
268
|
|
|
536
|
-
# Create branches for two AI agents to work on
|
|
537
269
|
git branch agent-1-auth
|
|
538
270
|
git branch agent-2-auth
|
|
539
|
-
|
|
540
|
-
# Create worktrees
|
|
541
271
|
git worktree add ../myapp-agent-1 agent-1-auth
|
|
542
272
|
git worktree add ../myapp-agent-2 agent-2-auth
|
|
543
273
|
|
|
544
|
-
# Start both stacks
|
|
545
274
|
npx wtc start
|
|
275
|
+
# Worktree 1: backend:28001 frontend:25174
|
|
276
|
+
# Worktree 2: backend:28002 frontend:25175
|
|
546
277
|
|
|
547
|
-
#
|
|
548
|
-
# Worktree 1 (agent-1-auth): backend:28001 frontend:25174
|
|
549
|
-
# Worktree 2 (agent-2-auth): backend:28002 frontend:25175
|
|
550
|
-
|
|
551
|
-
# Point each agent at its worktree directory
|
|
552
|
-
# Agent 1 works in ../myapp-agent-1
|
|
553
|
-
# Agent 2 works in ../myapp-agent-2
|
|
554
|
-
|
|
555
|
-
# Check status anytime
|
|
556
|
-
npx wtc list
|
|
557
|
-
|
|
558
|
-
# Compare frontends side by side
|
|
278
|
+
# Compare side by side
|
|
559
279
|
# http://localhost:25174 (agent 1)
|
|
560
280
|
# http://localhost:25175 (agent 2)
|
|
561
281
|
|
|
562
|
-
# Agent 1 did a better job — promote its changes
|
|
563
282
|
npx wtc promote 1
|
|
564
|
-
|
|
565
|
-
# Review the changes
|
|
566
|
-
git diff
|
|
567
|
-
|
|
568
|
-
# Commit if happy
|
|
569
|
-
git add -A && git commit -m "feat: add auth (from agent 1)"
|
|
570
|
-
|
|
571
|
-
# Clean up
|
|
283
|
+
git add -A && git commit -m "feat: auth from agent 1"
|
|
572
284
|
npx wtc clean
|
|
573
285
|
```
|
|
574
286
|
|
|
575
287
|
## Requirements
|
|
576
288
|
|
|
577
289
|
- **Node.js** >= 18
|
|
578
|
-
- **Git** with worktree support
|
|
579
|
-
- **Docker** with Compose v2 (`docker compose
|
|
580
|
-
-
|
|
290
|
+
- **Git** with worktree support
|
|
291
|
+
- **Docker** with Compose v2 (`docker compose`)
|
|
292
|
+
- `docker-compose.yml` with `${VAR:-default}` port patterns
|
|
581
293
|
|
|
582
294
|
## Troubleshooting
|
|
583
295
|
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
`wtc` looks for compose files in the git repo root (found via `git rev-parse --show-toplevel`), not the current directory. Make sure your compose file is in the repo root and named one of: `compose.yaml`, `compose.yml`, `docker-compose.yaml`, `docker-compose.yml`.
|
|
296
|
+
**"No compose file found"** — `wtc` looks in the git repo root, not the current directory.
|
|
587
297
|
|
|
588
|
-
|
|
298
|
+
**"No extra worktrees found"** — Create worktrees first: `git worktree add ../my-feature my-branch`
|
|
589
299
|
|
|
590
|
-
|
|
300
|
+
**Ports not changing** — Use `${VAR:-default}` for host ports, not hardcoded numbers.
|
|
591
301
|
|
|
592
|
-
|
|
593
|
-
git worktree add ../my-feature my-feature-branch
|
|
594
|
-
```
|
|
595
|
-
|
|
596
|
-
### Ports not changing
|
|
597
|
-
|
|
598
|
-
Make sure your compose file uses `${VAR:-default}` for host ports, not hardcoded numbers. `wtc` can only override ports that use env var patterns.
|
|
599
|
-
|
|
600
|
-
### "Docker daemon not running"
|
|
601
|
-
|
|
602
|
-
Start Docker Desktop (or the Docker daemon) before running `wtc start`.
|
|
603
|
-
|
|
604
|
-
### Promote fails with "Not a valid object name detached"
|
|
605
|
-
|
|
606
|
-
This happened in older versions when a worktree was in detached HEAD state. Update to the latest version — this is now handled correctly.
|
|
607
|
-
|
|
608
|
-
### Stale containers after manual cleanup
|
|
609
|
-
|
|
610
|
-
If you manually removed worktrees without running `wtc clean`, orphaned containers may remain. Run `wtc clean` to remove them, or manually:
|
|
611
|
-
|
|
612
|
-
```bash
|
|
613
|
-
docker ps -a --filter "name=-wt-" -q | xargs docker rm -f
|
|
614
|
-
```
|
|
302
|
+
**Stale containers** — Run `wtc clean`, or manually: `docker ps -a --filter "name=-wt-" -q | xargs docker rm -f`
|
|
615
303
|
|
|
616
304
|
## License
|
|
617
305
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "worktree-compose",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Zero-config Docker Compose isolation for git worktrees",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -29,6 +29,14 @@
|
|
|
29
29
|
"development",
|
|
30
30
|
"mcp"
|
|
31
31
|
],
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "git+https://github.com/LevwTech/worktree-compose.git"
|
|
35
|
+
},
|
|
36
|
+
"bugs": {
|
|
37
|
+
"url": "https://github.com/LevwTech/worktree-compose/issues"
|
|
38
|
+
},
|
|
39
|
+
"homepage": "https://github.com/LevwTech/worktree-compose#readme",
|
|
32
40
|
"license": "MIT",
|
|
33
41
|
"dependencies": {
|
|
34
42
|
"@modelcontextprotocol/sdk": "^1.12.1",
|