worktree-compose 0.1.0 → 0.1.3
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/LICENSE +21 -0
- package/README.md +120 -430
- package/package.json +9 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 LevwTech
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,172 +1,139 @@
|
|
|
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.
|
|
5
|
+
Zero-config Docker Compose isolation for git worktrees.
|
|
22
6
|
|
|
23
|
-
|
|
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
|
|
7
|
+
Every worktree gets its own ports, database, cache, and containers — automatically.
|
|
29
8
|
|
|
30
|
-
|
|
9
|
+
https://github.com/LevwTech/worktree-compose/raw/main/wtc-explainer.mp4
|
|
31
10
|
|
|
32
11
|
```bash
|
|
33
12
|
npm install -D worktree-compose
|
|
34
|
-
# or
|
|
35
|
-
pnpm add -D worktree-compose
|
|
36
|
-
# or
|
|
37
|
-
yarn add -D worktree-compose
|
|
38
13
|
```
|
|
39
14
|
|
|
40
|
-
|
|
15
|
+
```
|
|
16
|
+
npx wtc list
|
|
17
|
+
|
|
18
|
+
┌───────┬───────────────┬────────┬────────────────────────┬─────────────────────────────────────────────────────────┐
|
|
19
|
+
│ Index │ Branch │ Status │ URL │ Ports │
|
|
20
|
+
├───────┼───────────────┼────────┼────────────────────────┼─────────────────────────────────────────────────────────┤
|
|
21
|
+
│ - │ main │ - │ - │ postgres:5434 redis:6380 backend:8000 frontend:5173 │
|
|
22
|
+
├───────┼───────────────┼────────┼────────────────────────┼─────────────────────────────────────────────────────────┤
|
|
23
|
+
│ 1 │ feature-auth │ up │ http://localhost:25174 │ postgres:25435 redis:26381 backend:28001 frontend:25174 │
|
|
24
|
+
├───────┼───────────────┼────────┼────────────────────────┼─────────────────────────────────────────────────────────┤
|
|
25
|
+
│ 2 │ fix-billing │ down │ http://localhost:25175 │ postgres:25436 redis:26382 backend:28002 frontend:25175 │
|
|
26
|
+
└───────┴───────────────┴────────┴────────────────────────┴─────────────────────────────────────────────────────────┘
|
|
27
|
+
```
|
|
41
28
|
|
|
42
|
-
##
|
|
29
|
+
## Usage
|
|
43
30
|
|
|
44
31
|
```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
|
|
32
|
+
# Start isolated stacks for all worktrees
|
|
50
33
|
npx wtc start
|
|
51
34
|
|
|
52
|
-
#
|
|
35
|
+
# Start specific worktrees
|
|
36
|
+
npx wtc start 1
|
|
37
|
+
npx wtc start 1 2 3
|
|
38
|
+
|
|
39
|
+
# See what's running
|
|
53
40
|
npx wtc list
|
|
54
41
|
|
|
55
|
-
#
|
|
42
|
+
# Stop worktrees
|
|
43
|
+
npx wtc stop
|
|
44
|
+
npx wtc stop 1
|
|
56
45
|
|
|
57
|
-
#
|
|
46
|
+
# Restart (re-sync files, rebuild containers)
|
|
47
|
+
npx wtc restart 1
|
|
48
|
+
|
|
49
|
+
# Pull a worktree's changes into your current branch
|
|
58
50
|
npx wtc promote 1
|
|
59
51
|
|
|
60
|
-
#
|
|
52
|
+
# Tear down everything (containers, worktrees, volumes)
|
|
61
53
|
npx wtc clean
|
|
62
54
|
```
|
|
63
55
|
|
|
64
|
-
##
|
|
65
|
-
|
|
66
|
-
### 1. Auto-Detection
|
|
56
|
+
## The Problem
|
|
67
57
|
|
|
68
|
-
|
|
58
|
+
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
59
|
|
|
70
|
-
|
|
71
|
-
2. `compose.yml`
|
|
72
|
-
3. `docker-compose.yaml`
|
|
73
|
-
4. `docker-compose.yml`
|
|
60
|
+
## The Solution
|
|
74
61
|
|
|
75
|
-
|
|
62
|
+
`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
63
|
|
|
77
|
-
|
|
64
|
+
## Preparing Your docker-compose.yml
|
|
78
65
|
|
|
79
|
-
|
|
66
|
+
For `wtc` to isolate a service's port, the host port must use the `${VAR:-default}` pattern:
|
|
80
67
|
|
|
81
68
|
```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).
|
|
69
|
+
# wtc CAN isolate this
|
|
70
|
+
ports:
|
|
71
|
+
- "${BACKEND_PORT:-8000}:8000"
|
|
115
72
|
|
|
116
|
-
|
|
73
|
+
# wtc CANNOT isolate this (hardcoded)
|
|
74
|
+
ports:
|
|
75
|
+
- "8080:8080"
|
|
76
|
+
```
|
|
117
77
|
|
|
118
|
-
|
|
78
|
+
If `wtc` finds hardcoded ports, it warns you and suggests the fix:
|
|
119
79
|
|
|
120
80
|
```
|
|
121
|
-
|
|
81
|
+
⚠ Service "nginx" uses a raw port mapping (8080:80).
|
|
82
|
+
To enable port isolation, change it to: "${NGINX_PORT:-8080}:80"
|
|
122
83
|
```
|
|
123
84
|
|
|
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 |
|
|
85
|
+
### Supported port formats
|
|
130
86
|
|
|
131
|
-
|
|
87
|
+
```yaml
|
|
88
|
+
# Standard
|
|
89
|
+
- "${BACKEND_PORT:-8000}:8000"
|
|
132
90
|
|
|
133
|
-
|
|
91
|
+
# Same var for host and container
|
|
92
|
+
- "${FRONTEND_PORT:-5173}:${FRONTEND_PORT:-5173}"
|
|
134
93
|
|
|
135
|
-
|
|
94
|
+
# IP-bound
|
|
95
|
+
- "127.0.0.1:${API_PORT:-3000}:3000"
|
|
136
96
|
|
|
137
|
-
|
|
97
|
+
# With protocol
|
|
98
|
+
- "${BACKEND_PORT:-8000}:8000/tcp"
|
|
138
99
|
|
|
100
|
+
# Multiple ports per service
|
|
101
|
+
- "${BACKEND_PORT:-8000}:8000"
|
|
102
|
+
- "${DEBUG_PORT:-9229}:9229"
|
|
103
|
+
|
|
104
|
+
# Long-form syntax
|
|
105
|
+
- target: 8000
|
|
106
|
+
published: "${BACKEND_PORT:-8000}"
|
|
107
|
+
protocol: tcp
|
|
139
108
|
```
|
|
140
|
-
{repo-name}-wt-{index}-{branch}
|
|
141
|
-
```
|
|
142
109
|
|
|
143
|
-
|
|
110
|
+
## How It Works
|
|
111
|
+
|
|
112
|
+
### Port Allocation
|
|
144
113
|
|
|
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`
|
|
114
|
+
Each worktree N gets unique ports: `20000 + default_port + worktree_index`
|
|
149
115
|
|
|
150
|
-
|
|
116
|
+
| Service | Main (default) | Worktree 1 | Worktree 2 | Worktree 3 |
|
|
117
|
+
|----------|---------------|------------|------------|------------|
|
|
118
|
+
| postgres | 5434 | 25435 | 25436 | 25437 |
|
|
119
|
+
| redis | 6380 | 26381 | 26382 | 26383 |
|
|
120
|
+
| backend | 8000 | 28001 | 28002 | 28003 |
|
|
121
|
+
| frontend | 5173 | 25174 | 25175 | 25176 |
|
|
151
122
|
|
|
152
|
-
###
|
|
123
|
+
### Container Isolation
|
|
153
124
|
|
|
154
|
-
|
|
125
|
+
Each worktree gets its own `COMPOSE_PROJECT_NAME`: `{repo}-wt-{index}-{branch}`. This means separate containers, networks, and volumes. Nothing is shared.
|
|
155
126
|
|
|
156
|
-
|
|
157
|
-
- Every Dockerfile referenced in `build.dockerfile` fields
|
|
158
|
-
- `.env` (or `.env.example` if `.env` doesn't exist)
|
|
127
|
+
### File Sync
|
|
159
128
|
|
|
160
|
-
|
|
129
|
+
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
130
|
|
|
162
|
-
###
|
|
131
|
+
### Env Injection
|
|
163
132
|
|
|
164
|
-
After copying `.env`, wtc appends
|
|
133
|
+
After copying `.env`, `wtc` appends an idempotent block with allocated port overrides:
|
|
165
134
|
|
|
166
135
|
```bash
|
|
167
136
|
# existing .env content stays untouched...
|
|
168
|
-
OPENAI_API_KEY=sk-...
|
|
169
|
-
DATABASE_URL=...
|
|
170
137
|
|
|
171
138
|
# --- wtc port overrides ---
|
|
172
139
|
POSTGRES_PORT=25435
|
|
@@ -176,306 +143,89 @@ FRONTEND_PORT=25174
|
|
|
176
143
|
# --- end wtc ---
|
|
177
144
|
```
|
|
178
145
|
|
|
179
|
-
This is **idempotent** — running `wtc start` again strips the old block and writes a fresh one.
|
|
180
|
-
|
|
181
146
|
## Commands
|
|
182
147
|
|
|
183
148
|
### `wtc start [indices...]`
|
|
184
149
|
|
|
185
|
-
Start Docker Compose stacks
|
|
150
|
+
Start Docker Compose stacks. Syncs files, injects ports, runs `docker compose up -d --build`.
|
|
186
151
|
|
|
187
152
|
```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
|
-
|
|
201
|
-
```
|
|
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
|
|
153
|
+
npx wtc start # all worktrees
|
|
154
|
+
npx wtc start 1 # worktree 1 only
|
|
155
|
+
npx wtc start 1 2 3 # worktrees 1, 2, and 3
|
|
214
156
|
```
|
|
215
157
|
|
|
216
|
-
After starting, it prints a table of all worktrees with their URLs.
|
|
217
|
-
|
|
218
158
|
### `wtc stop [indices...]`
|
|
219
159
|
|
|
220
|
-
Stop
|
|
160
|
+
Stop stacks. Runs `docker compose down`. Volumes are preserved.
|
|
221
161
|
|
|
222
162
|
```bash
|
|
223
|
-
npx wtc stop #
|
|
224
|
-
npx wtc stop 1 #
|
|
225
|
-
npx wtc stop 1 2 # stop worktrees 1 and 2
|
|
163
|
+
npx wtc stop # all
|
|
164
|
+
npx wtc stop 1 # worktree 1 only
|
|
226
165
|
```
|
|
227
166
|
|
|
228
|
-
Runs `docker compose down` for each target worktree. Data in named volumes is preserved — only containers and networks are removed.
|
|
229
|
-
|
|
230
167
|
### `wtc restart [indices...]`
|
|
231
168
|
|
|
232
|
-
|
|
169
|
+
Full restart: stop, re-sync files, re-inject env, rebuild, start. Use after migrations, Dockerfile changes, or config updates.
|
|
233
170
|
|
|
234
171
|
```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
|
|
172
|
+
npx wtc restart 1
|
|
238
173
|
```
|
|
239
174
|
|
|
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
175
|
### `wtc list` / `wtc ls`
|
|
247
176
|
|
|
248
|
-
Show all worktrees with
|
|
177
|
+
Show all worktrees with branch, status, URL, and ports.
|
|
249
178
|
|
|
250
179
|
```bash
|
|
251
180
|
npx wtc list
|
|
252
181
|
```
|
|
253
182
|
|
|
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
183
|
### `wtc promote <index>`
|
|
275
184
|
|
|
276
|
-
Copy
|
|
185
|
+
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
186
|
|
|
278
187
|
```bash
|
|
279
188
|
npx wtc promote 1
|
|
280
189
|
```
|
|
281
190
|
|
|
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
191
|
### `wtc clean`
|
|
315
192
|
|
|
316
|
-
Stop all
|
|
193
|
+
Stop all containers, remove all worktrees, prune stale Docker resources.
|
|
317
194
|
|
|
318
195
|
```bash
|
|
319
196
|
npx wtc clean
|
|
320
197
|
```
|
|
321
198
|
|
|
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
199
|
## Configuration (Optional)
|
|
393
200
|
|
|
394
|
-
`wtc` works zero-config
|
|
201
|
+
`wtc` works zero-config. For project-specific needs, create `.wtcrc.json` in your repo root:
|
|
395
202
|
|
|
396
203
|
```json
|
|
397
204
|
{
|
|
398
|
-
"sync": ["
|
|
205
|
+
"sync": [".generated/prisma-client", "local-certs/"],
|
|
399
206
|
"envOverrides": {
|
|
400
207
|
"VITE_API_URL": "http://localhost:${BACKEND_PORT}"
|
|
401
208
|
}
|
|
402
209
|
}
|
|
403
210
|
```
|
|
404
211
|
|
|
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)
|
|
212
|
+
Or use a `"wtc"` key in `package.json`.
|
|
422
213
|
|
|
423
214
|
### `sync`
|
|
424
215
|
|
|
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.
|
|
216
|
+
Extra files/directories to copy from main into each worktree on start. Use for gitignored or generated files that Docker needs but aren't committed — like generated clients, local certificates, or build artifacts.
|
|
441
217
|
|
|
442
218
|
### `envOverrides`
|
|
443
219
|
|
|
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).
|
|
220
|
+
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
221
|
|
|
466
222
|
## MCP Server
|
|
467
223
|
|
|
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.
|
|
224
|
+
Built-in [MCP](https://modelcontextprotocol.io/) server so AI agents can manage their stack programmatically.
|
|
473
225
|
|
|
474
226
|
### Setup
|
|
475
227
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
**Claude Code** (`.claude/settings.json` or `.claude/settings.local.json`):
|
|
228
|
+
**Claude Code** (`.claude/settings.json`):
|
|
479
229
|
|
|
480
230
|
```json
|
|
481
231
|
{
|
|
@@ -501,117 +251,57 @@ The MCP server uses stdio transport. Configure it in your agent's MCP settings:
|
|
|
501
251
|
}
|
|
502
252
|
```
|
|
503
253
|
|
|
504
|
-
###
|
|
254
|
+
### Tools
|
|
505
255
|
|
|
506
256
|
| Tool | Parameters | Description |
|
|
507
257
|
|------|-----------|-------------|
|
|
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 |
|
|
514
|
-
|
|
515
|
-
### Agent Workflow Example
|
|
258
|
+
| `wtc_start` | `indices?: number[]` | Start worktree stacks |
|
|
259
|
+
| `wtc_stop` | `indices?: number[]` | Stop worktree stacks |
|
|
260
|
+
| `wtc_restart` | `indices?: number[]` | Restart after migrations/config changes |
|
|
261
|
+
| `wtc_list` | none | List worktrees (returns JSON) |
|
|
262
|
+
| `wtc_promote` | `index: number` | Pull worktree changes into current branch |
|
|
263
|
+
| `wtc_clean` | none | Tear down everything |
|
|
516
264
|
|
|
517
|
-
|
|
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:
|
|
265
|
+
## Full Example
|
|
528
266
|
|
|
529
267
|
```bash
|
|
530
|
-
# You have a repo with docker-compose.yml containing postgres, redis, backend, frontend
|
|
531
268
|
cd myapp
|
|
532
|
-
|
|
533
|
-
# Install wtc
|
|
534
269
|
pnpm add -D worktree-compose
|
|
535
270
|
|
|
536
|
-
# Create branches for two AI agents to work on
|
|
537
271
|
git branch agent-1-auth
|
|
538
272
|
git branch agent-2-auth
|
|
539
|
-
|
|
540
|
-
# Create worktrees
|
|
541
273
|
git worktree add ../myapp-agent-1 agent-1-auth
|
|
542
274
|
git worktree add ../myapp-agent-2 agent-2-auth
|
|
543
275
|
|
|
544
|
-
# Start both stacks
|
|
545
276
|
npx wtc start
|
|
277
|
+
# Worktree 1: backend:28001 frontend:25174
|
|
278
|
+
# Worktree 2: backend:28002 frontend:25175
|
|
546
279
|
|
|
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
|
|
280
|
+
# Compare side by side
|
|
559
281
|
# http://localhost:25174 (agent 1)
|
|
560
282
|
# http://localhost:25175 (agent 2)
|
|
561
283
|
|
|
562
|
-
# Agent 1 did a better job — promote its changes
|
|
563
284
|
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
|
|
285
|
+
git add -A && git commit -m "feat: auth from agent 1"
|
|
572
286
|
npx wtc clean
|
|
573
287
|
```
|
|
574
288
|
|
|
575
289
|
## Requirements
|
|
576
290
|
|
|
577
291
|
- **Node.js** >= 18
|
|
578
|
-
- **Git** with worktree support
|
|
579
|
-
- **Docker** with Compose v2 (`docker compose
|
|
580
|
-
-
|
|
292
|
+
- **Git** with worktree support
|
|
293
|
+
- **Docker** with Compose v2 (`docker compose`)
|
|
294
|
+
- `docker-compose.yml` with `${VAR:-default}` port patterns
|
|
581
295
|
|
|
582
296
|
## Troubleshooting
|
|
583
297
|
|
|
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`.
|
|
298
|
+
**"No compose file found"** — `wtc` looks in the git repo root, not the current directory.
|
|
587
299
|
|
|
588
|
-
|
|
300
|
+
**"No extra worktrees found"** — Create worktrees first: `git worktree add ../my-feature my-branch`
|
|
589
301
|
|
|
590
|
-
|
|
302
|
+
**Ports not changing** — Use `${VAR:-default}` for host ports, not hardcoded numbers.
|
|
591
303
|
|
|
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
|
-
```
|
|
304
|
+
**Stale containers** — Run `wtc clean`, or manually: `docker ps -a --filter "name=-wt-" -q | xargs docker rm -f`
|
|
615
305
|
|
|
616
306
|
## License
|
|
617
307
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "worktree-compose",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
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",
|