dev-prism 0.6.0 → 0.7.0

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.
Files changed (149) hide show
  1. package/README.md +143 -286
  2. package/bin/dev-prism.js +65 -87
  3. package/dist/{chunk-7YGOMAJG.js → chunk-3CIXBEXK.js} +22 -28
  4. package/dist/chunk-AHC6CD7F.js +92 -0
  5. package/dist/chunk-FQTS57VO.js +64 -0
  6. package/dist/chunk-GWQPK7MZ.js +50 -0
  7. package/dist/chunk-HDGBJGIH.js +55 -0
  8. package/dist/chunk-ILICQAU7.js +60 -0
  9. package/dist/chunk-IWZN6P6M.js +155 -0
  10. package/dist/chunk-KP56QH72.js +133 -0
  11. package/dist/{chunk-3Q454U3I.js → chunk-NJAITOCG.js} +0 -10
  12. package/dist/{chunk-25WQHUYW.js → chunk-TSNFAXVQ.js} +5 -12
  13. package/dist/chunk-VAPRJUC7.js +67 -0
  14. package/dist/chunk-VL56YPMK.js +45 -0
  15. package/dist/commands/claude.js +1 -1
  16. package/dist/commands/create.d.ts +1 -5
  17. package/dist/commands/create.js +5 -8
  18. package/dist/commands/destroy.d.ts +1 -1
  19. package/dist/commands/destroy.js +4 -6
  20. package/dist/commands/env.d.ts +7 -0
  21. package/dist/commands/env.js +9 -0
  22. package/dist/commands/info.js +4 -3
  23. package/dist/commands/list.js +2 -3
  24. package/dist/commands/prune.js +2 -6
  25. package/dist/commands/with-env.d.ts +3 -0
  26. package/dist/commands/with-env.js +9 -0
  27. package/dist/index.d.ts +9 -14
  28. package/dist/index.js +57 -73
  29. package/dist/lib/config.d.ts +4 -11
  30. package/dist/lib/config.js +1 -3
  31. package/dist/lib/db.d.ts +26 -0
  32. package/dist/lib/db.js +28 -0
  33. package/dist/lib/env.d.ts +7 -5
  34. package/dist/lib/env.js +9 -9
  35. package/dist/lib/worktree.d.ts +1 -2
  36. package/dist/lib/worktree.js +1 -3
  37. package/package.json +9 -6
  38. package/dist/chunk-24OM3LGM.js +0 -35
  39. package/dist/chunk-35SHBLIZ.js +0 -69
  40. package/dist/chunk-3ATDGV4Y.js +0 -22
  41. package/dist/chunk-3FYEGH2G.js +0 -217
  42. package/dist/chunk-3MSC3CGG.js +0 -78
  43. package/dist/chunk-3NW2OWIU.js +0 -78
  44. package/dist/chunk-3TRRZEFR.js +0 -38
  45. package/dist/chunk-4UNCSJRM.js +0 -70
  46. package/dist/chunk-5KDDYO6Y.js +0 -168
  47. package/dist/chunk-63II3EL4.js +0 -98
  48. package/dist/chunk-6YMQTISJ.js +0 -84
  49. package/dist/chunk-76LSQIZI.js +0 -31
  50. package/dist/chunk-7OSTLJLO.js +0 -219
  51. package/dist/chunk-7YEZWM6Y.js +0 -97
  52. package/dist/chunk-AEVARZQ4.js +0 -203
  53. package/dist/chunk-AIVPJ467.js +0 -70
  54. package/dist/chunk-AOM6BONB.js +0 -98
  55. package/dist/chunk-AW2FJGXA.js +0 -38
  56. package/dist/chunk-BXYXWNGH.js +0 -30
  57. package/dist/chunk-C4WLIOBR.js +0 -67
  58. package/dist/chunk-CUQVGZBX.js +0 -44
  59. package/dist/chunk-D6QWWXZD.js +0 -49
  60. package/dist/chunk-DQER5GNG.js +0 -72
  61. package/dist/chunk-DTE5YQMI.js +0 -41
  62. package/dist/chunk-EXRHG5KQ.js +0 -60
  63. package/dist/chunk-FKTFCSU7.js +0 -78
  64. package/dist/chunk-GBN67HYD.js +0 -57
  65. package/dist/chunk-GKXXK2ZH.js +0 -203
  66. package/dist/chunk-GWDGC2OE.js +0 -116
  67. package/dist/chunk-H3L73URT.js +0 -65
  68. package/dist/chunk-H4HPDIY3.js +0 -95
  69. package/dist/chunk-HCCZKLC4.js +0 -64
  70. package/dist/chunk-HZBJF67X.js +0 -60
  71. package/dist/chunk-HZUN6NRB.js +0 -70
  72. package/dist/chunk-I3U6JK77.js +0 -66
  73. package/dist/chunk-J36LRUXM.js +0 -60
  74. package/dist/chunk-JHR4WADC.js +0 -200
  75. package/dist/chunk-JIU574KX.js +0 -41
  76. package/dist/chunk-JMKE3ZKI.js +0 -61
  77. package/dist/chunk-JZ2VPQXP.js +0 -132
  78. package/dist/chunk-KDYKLH6P.js +0 -40
  79. package/dist/chunk-KZJE62TK.js +0 -203
  80. package/dist/chunk-LAOWFCQL.js +0 -21
  81. package/dist/chunk-LDK6QMR6.js +0 -67
  82. package/dist/chunk-LEHA65A7.js +0 -59
  83. package/dist/chunk-LNIOSGC4.js +0 -78
  84. package/dist/chunk-LOVO4P3Y.js +0 -41
  85. package/dist/chunk-MRBTH5PL.js +0 -66
  86. package/dist/chunk-NNVP5F6I.js +0 -77
  87. package/dist/chunk-OL25TBYX.js +0 -67
  88. package/dist/chunk-OOJ6YOGS.js +0 -53
  89. package/dist/chunk-OPEZFBBI.js +0 -219
  90. package/dist/chunk-P3ETW2KK.js +0 -166
  91. package/dist/chunk-PJKUD2N2.js +0 -22
  92. package/dist/chunk-PKC2ZED2.js +0 -168
  93. package/dist/chunk-PS76Q3HD.js +0 -168
  94. package/dist/chunk-Q5DPX4WL.js +0 -219
  95. package/dist/chunk-QSG5CXPX.js +0 -171
  96. package/dist/chunk-QUMZI5KK.js +0 -98
  97. package/dist/chunk-RC2AUYZ7.js +0 -49
  98. package/dist/chunk-RQ245R7T.js +0 -67
  99. package/dist/chunk-SD3TON6N.js +0 -32
  100. package/dist/chunk-SEKH4ZV6.js +0 -60
  101. package/dist/chunk-SMFAL2VP.js +0 -69
  102. package/dist/chunk-SSQ7XBY2.js +0 -30
  103. package/dist/chunk-SUMJLXT7.js +0 -30
  104. package/dist/chunk-UHI2QJFI.js +0 -200
  105. package/dist/chunk-UKYQN4A3.js +0 -38
  106. package/dist/chunk-URGGS3XM.js +0 -95
  107. package/dist/chunk-VR3QWHHB.js +0 -57
  108. package/dist/chunk-VUNPVDSO.js +0 -74
  109. package/dist/chunk-VXP2SPRI.js +0 -51
  110. package/dist/chunk-W54CPPSK.js +0 -217
  111. package/dist/chunk-X2PXZRYU.js +0 -41
  112. package/dist/chunk-X5A6H4Q7.js +0 -70
  113. package/dist/chunk-X6FHBEAS.js +0 -200
  114. package/dist/chunk-XUWQUDLT.js +0 -67
  115. package/dist/chunk-Y3GR6XK7.js +0 -71
  116. package/dist/chunk-YSO3IDZZ.js +0 -40
  117. package/dist/chunk-YY5DA35Z.js +0 -40
  118. package/dist/chunk-Z2ISJMLW.js +0 -92
  119. package/dist/chunk-ZKHNUDSL.js +0 -119
  120. package/dist/commands/logs.d.ts +0 -8
  121. package/dist/commands/logs.js +0 -9
  122. package/dist/commands/start.d.ts +0 -7
  123. package/dist/commands/start.js +0 -10
  124. package/dist/commands/stop-all.d.ts +0 -3
  125. package/dist/commands/stop-all.js +0 -8
  126. package/dist/commands/stop.d.ts +0 -3
  127. package/dist/commands/stop.js +0 -8
  128. package/dist/lib/compose.d.ts +0 -12
  129. package/dist/lib/compose.js +0 -12
  130. package/dist/lib/config.test.d.ts +0 -1
  131. package/dist/lib/config.test.js +0 -32
  132. package/dist/lib/docker-inspect.d.ts +0 -24
  133. package/dist/lib/docker-inspect.js +0 -16
  134. package/dist/lib/docker.d.ts +0 -12
  135. package/dist/lib/docker.js +0 -14
  136. package/dist/lib/env.test.d.ts +0 -1
  137. package/dist/lib/env.test.js +0 -68
  138. package/dist/lib/ports.d.ts +0 -6
  139. package/dist/lib/ports.js +0 -8
  140. package/dist/lib/ports.test.d.ts +0 -1
  141. package/dist/lib/ports.test.js +0 -61
  142. package/dist/lib/session.d.ts +0 -16
  143. package/dist/lib/session.js +0 -13
  144. package/dist/lib/store.d.ts +0 -46
  145. package/dist/lib/store.js +0 -6
  146. package/dist/lib/store.test.d.ts +0 -1
  147. package/dist/lib/store.test.js +0 -205
  148. package/dist/lib/worktree.test.d.ts +0 -1
  149. package/dist/lib/worktree.test.js +0 -41
package/README.md CHANGED
@@ -4,29 +4,27 @@
4
4
  <img src="banner.png" alt="dev-prism - One codebase, many parallel sessions" width="600">
5
5
  </p>
6
6
 
7
- A minimal CLI tool for managing isolated parallel development sessions. Enables multiple Claude Code (or human developer) sessions to work on the same repo simultaneously with complete isolation.
7
+ A port allocator, env injector, and worktree manager for parallel development sessions. Enables multiple Claude Code (or human developer) sessions to work on the same repo simultaneously with complete isolation.
8
8
 
9
9
  ## Philosophy
10
10
 
11
- **Stateless orchestration, Docker as source of truth.** This tool does the bare minimum:
12
- 1. Creates git worktrees for isolated working directories
13
- 2. Generates `docker-compose.session.yml` with random port bindings
14
- 3. Runs `docker compose` commands
15
- 4. Discovers ports from running containers and writes `.env.session`
11
+ **Allocate ports. Inject env. Get out of the way.**
16
12
 
17
- No database, no state tracking—everything is derived from Docker's reality.
13
+ dev-prism does three things:
14
+ 1. Allocates unique ports via SQLite (UNIQUE constraints prevent conflicts)
15
+ 2. Injects those ports into any command via `with-env`
16
+ 3. Optionally manages git worktrees for isolated working directories
17
+
18
+ Docker is the user's responsibility. dev-prism just hands ports to whatever you run.
18
19
 
19
20
  ## Features
20
21
 
21
- - **Stateless** - No database, Docker is the single source of truth
22
+ - **SQLite-backed port allocation** with UNIQUE constraints (zero conflicts)
23
+ - **`with-env` pass-through** — injects env vars into any command, no-op outside sessions
22
24
  - **Git worktrees** for isolated working directories (or in-place mode)
23
- - **Docker Compose** handles all container orchestration
24
- - **Random port allocation** via Docker (zero conflicts)
25
- - **Automatic port discovery** from running containers
26
- - **Auto-healing** - commands always sync to Docker reality
27
- - **Two modes**: Docker (apps in containers) or Native (apps run locally)
25
+ - **App-specific env** different env vars for different apps in a monorepo
28
26
  - **Claude Code integration** built-in (`dev-prism claude`)
29
- - **Portable**: Works with any project
27
+ - **Portable**: Works with any project, any runtime, any Docker setup
30
28
 
31
29
  ## Installation
32
30
 
@@ -39,22 +37,28 @@ pnpm add -D dev-prism
39
37
  ## Quick Start
40
38
 
41
39
  ```bash
42
- # Create a session with worktree
43
- dev-prism create
40
+ # Create a session (allocates ports + creates worktree)
41
+ dev-prism create --branch feature/auth
44
42
 
45
43
  # Or create in current directory
46
44
  dev-prism create --in-place
47
45
 
48
- # List active sessions
49
- dev-prism list
46
+ # Start Docker services with allocated ports
47
+ dev-prism with-env -- docker compose up -d
50
48
 
51
- # Check current directory status
49
+ # Run app with session env injected
50
+ dev-prism with-env my-app -- pnpm dev
51
+
52
+ # Show allocated ports and env vars
52
53
  dev-prism info
53
54
 
54
- # Stop session in current directory
55
- dev-prism stop
55
+ # Print env vars (for eval or piping)
56
+ dev-prism env
56
57
 
57
- # Destroy session in current directory
58
+ # Write .env file for IDE
59
+ dev-prism env --write .env
60
+
61
+ # Destroy session
58
62
  dev-prism destroy
59
63
  ```
60
64
 
@@ -69,61 +73,48 @@ dev-prism create
69
73
  # Custom branch name
70
74
  dev-prism create --branch feature/my-feature
71
75
 
72
- # Native mode - only infrastructure in Docker, apps run via pnpm dev
73
- dev-prism create --mode native
74
-
75
- # Exclude specific apps from Docker
76
- dev-prism create --without web,widget
77
-
78
- # In-place mode - use current directory instead of creating worktree
76
+ # In-place mode use current directory instead of creating worktree
79
77
  dev-prism create --in-place
80
-
81
- # Stream logs after creation instead of detaching
82
- dev-prism create --no-detach
83
78
  ```
84
79
 
85
- ### List sessions
80
+ ### Inject env and run commands
86
81
 
87
82
  ```bash
88
- dev-prism list
83
+ # Inject session env into any command
84
+ dev-prism with-env -- docker compose up -d
85
+ dev-prism with-env -- pnpm dev
86
+ dev-prism with-env -- cargo run
87
+
88
+ # Inject app-specific env (merges global + app config)
89
+ dev-prism with-env convas-app -- pnpm --filter convas-app dev
90
+ dev-prism with-env convas-web -- pnpm --filter convas-web dev
89
91
  ```
90
92
 
91
- Shows only running sessions with their ports and container counts.
93
+ `with-env` is a no-op outside sessions safe to use unconditionally in scripts and Makefiles.
92
94
 
93
- ### Session info
95
+ ### View env vars
94
96
 
95
97
  ```bash
96
- # Show info for current directory
97
- dev-prism info
98
+ # Print all env vars to stdout
99
+ dev-prism env
100
+
101
+ # Write to file (for IDE/GUI tools)
102
+ dev-prism env --write .env
98
103
 
99
- # Or specify directory
100
- dev-prism info /path/to/session
104
+ # Include app-specific vars
105
+ dev-prism env --app convas-app
101
106
  ```
102
107
 
103
- ### Start/Stop services
108
+ ### List sessions
104
109
 
105
110
  ```bash
106
- # Stop session in current directory
107
- dev-prism stop
108
-
109
- # Or specify directory
110
- dev-prism stop /path/to/session
111
-
112
- # Stop all running sessions
113
- dev-prism stop-all
114
-
115
- # Start stopped session
116
- dev-prism start
111
+ dev-prism list
117
112
  ```
118
113
 
119
- ### View logs
114
+ ### Session info
120
115
 
121
116
  ```bash
122
- # Stream logs from current directory
123
- dev-prism logs
124
-
125
- # Or specify directory
126
- dev-prism logs /path/to/session
117
+ dev-prism info
127
118
  ```
128
119
 
129
120
  ### Cleanup
@@ -132,13 +123,10 @@ dev-prism logs /path/to/session
132
123
  # Destroy session in current directory
133
124
  dev-prism destroy
134
125
 
135
- # Or specify directory
136
- dev-prism destroy /path/to/session
137
-
138
126
  # Destroy all sessions
139
127
  dev-prism destroy --all
140
128
 
141
- # Remove all stopped session directories
129
+ # Remove orphaned sessions from database
142
130
  dev-prism prune
143
131
  dev-prism prune -y # Skip confirmation
144
132
  ```
@@ -150,278 +138,147 @@ dev-prism claude # Install Claude Code skill + CLAUDE.md
150
138
  dev-prism claude --force # Overwrite existing files
151
139
  ```
152
140
 
153
- ## Architecture
154
-
155
- ### Stateless Design
156
-
157
- dev-prism v0.6+ has **zero persistent state**. Every command queries Docker to understand current reality:
158
-
159
- ```bash
160
- # Session discovery
161
- docker ps --filter "label=dev-prism.managed=true"
162
-
163
- # Session identity = working directory path
164
- # Stored in container label: dev-prism.working_dir=/path/to/session
165
- ```
166
-
167
- ### Port Management
168
-
169
- Ports are **randomly assigned by Docker** and **discovered after startup**:
170
-
171
- 1. Generate `docker-compose.session.yml` with `"0:5432"` (random host port)
172
- 2. Start containers: `docker compose up -d`
173
- 3. Inspect containers: `docker inspect` to get actual ports
174
- 4. Write `.env.session` with discovered ports
175
-
176
- Example discovered ports:
177
- ```bash
178
- POSTGRES_PORT=54321 # Random
179
- APP_PORT=32768 # Random
180
- WEB_PORT=32769 # Random
181
- ```
182
-
183
- **Why random ports?**
184
- - Zero configuration
185
- - Docker handles conflicts automatically
186
- - Large ephemeral port range (32768-60999)
187
- - Simpler than centralized allocation
188
-
189
- ### Auto-Healing
190
-
191
- Commands always reflect Docker's current state:
192
-
193
- ```bash
194
- # If .env.session is deleted, it regenerates from Docker
195
- dev-prism info # Queries Docker, recreates .env.session
196
-
197
- # If containers are manually removed, session disappears from list
198
- dev-prism list # Only shows what Docker reports
199
-
200
- # If containers exist but .env.session is missing, file is recreated
201
- ```
202
-
203
- No warnings, no errors, no stale state—just current reality.
204
-
205
141
  ## Configuration
206
142
 
207
- ### session.config.mjs
143
+ ### prism.config.mjs
208
144
 
209
145
  ```javascript
210
146
  export default {
211
- // Project name for Docker namespace (defaults to directory name)
212
- projectName: 'myproject',
213
-
214
- // Where to create worktrees (relative to project root)
215
- sessionsDir: '../my-project-sessions',
216
-
217
- // Docker Compose profiles for app containers (used in docker mode)
218
- // These match service names with `profiles: ["app-name"]` in docker-compose
219
- apps: ['app', 'web'],
220
-
221
- // .env files to copy to session worktree (DATABASE_URL auto-updated)
222
- envFiles: [
223
- 'apps/my-app/.env',
224
- 'packages/db/.env',
225
- ],
147
+ ports: ['postgres', 'mailpit_http', 'mailpit_smtp', 'app', 'web'],
226
148
 
227
- // Commands to run after session creation
228
- setup: ['pnpm install', 'pnpm db:push'],
149
+ env: {
150
+ POSTGRES_PORT: '${postgres}',
151
+ MAILPIT_HTTP_PORT: '${mailpit_http}',
152
+ MAILPIT_SMTP_PORT: '${mailpit_smtp}',
153
+ DATABASE_URL: 'postgresql://postgres:postgres@localhost:${postgres}/postgres',
154
+ },
229
155
 
230
- // Optional: app-specific env for CLI commands from host (native mode)
231
- appEnv: {
232
- 'apps/my-app': {
233
- DATABASE_URL: 'postgresql://postgres:postgres@localhost:${POSTGRES_PORT}/postgres',
156
+ apps: {
157
+ 'convas-app': {
158
+ PORT: '${app}',
159
+ DATABASE_URL: 'postgresql://postgres:postgres@localhost:${postgres}/postgres',
234
160
  },
161
+ 'convas-web': { PORT: '${web}' },
235
162
  },
163
+
164
+ setup: ['pnpm install'],
236
165
  };
237
166
  ```
238
167
 
239
- ### docker-compose.yml (your base services)
240
-
241
- Define your services as usual:
168
+ ### docker-compose.yml (user-managed)
242
169
 
243
170
  ```yaml
244
171
  services:
245
172
  postgres:
246
173
  image: postgres:16
174
+ ports:
175
+ - "${POSTGRES_PORT:-5432}:5432"
247
176
  environment:
248
177
  POSTGRES_USER: postgres
249
178
  POSTGRES_PASSWORD: postgres
250
- healthcheck:
251
- test: ["CMD-SHELL", "pg_isready -U postgres"]
252
- interval: 5s
253
- timeout: 5s
254
- retries: 5
255
-
256
- app:
257
- profiles: ["app"] # Only runs in docker mode
258
- build:
259
- context: .
260
- dockerfile: apps/my-app/Dockerfile.dev
261
- environment:
262
- DATABASE_URL: postgresql://postgres:postgres@postgres:5432/postgres
263
- depends_on:
264
- postgres:
265
- condition: service_healthy
266
- ```
267
-
268
- ### Generated docker-compose.session.yml
269
-
270
- dev-prism generates this automatically with random ports and labels:
271
-
272
- ```yaml
273
- # Auto-generated by dev-prism - DO NOT EDIT
274
- version: "3.8"
275
-
276
- x-dev-prism-labels: &dev-prism-labels
277
- dev-prism.managed: "true"
278
- dev-prism.working_dir: "/Users/you/worktrees/session-2026-02-06T12-30-45"
279
- dev-prism.session_id: "/Users/you/worktrees/session-2026-02-06T12-30-45"
280
- dev-prism.created_at: "2026-02-06T12:30:45.123Z"
179
+ volumes:
180
+ - postgres-data:/var/lib/postgresql/data
281
181
 
282
- services:
283
- postgres:
284
- extends:
285
- file: docker-compose.yml
286
- service: postgres
287
- ports:
288
- - "0:5432" # Random host port
289
- labels:
290
- <<: *dev-prism-labels
291
- dev-prism.service: "postgres"
292
- dev-prism.internal_port: "5432"
293
-
294
- app:
295
- extends:
296
- file: docker-compose.yml
297
- service: app
182
+ mailpit:
183
+ image: axllent/mailpit
298
184
  ports:
299
- - "0:3000" # Random host port
300
- labels:
301
- <<: *dev-prism-labels
302
- dev-prism.service: "app"
303
- dev-prism.internal_port: "3000"
185
+ - "${MAILPIT_HTTP_PORT:-8025}:8025"
186
+ - "${MAILPIT_SMTP_PORT:-1025}:1025"
187
+
188
+ volumes:
189
+ postgres-data:
304
190
  ```
305
191
 
192
+ The `:-` defaults mean it works without dev-prism too (solo dev, standard ports).
193
+
306
194
  ## How It Works
307
195
 
308
- 1. **Create session**: `dev-prism create`
309
- - Checks for existing session in directory via Docker labels
310
- - Creates git worktree (or uses current dir with `--in-place`)
311
- - Generates `docker-compose.session.yml` with random port bindings (`"0:5432"`)
312
- - Writes `.env.session` stub with compose project name
313
- - Runs `docker compose up -d`
314
- - Inspects running containers to discover actual ports
315
- - Updates `.env.session` with discovered ports
316
- - Runs setup commands
317
-
318
- 2. **Port discovery**
319
- - Query containers: `docker inspect <container-id>`
320
- - Extract `NetworkSettings.Ports` mappings
321
- - Write discovered ports to `.env.session`
322
-
323
- 3. **Session identity**
324
- - Session ID = full working directory path
325
- - Stored in container labels: `dev-prism.working_dir=/path/to/session`
326
- - One session per directory maximum
327
-
328
- 4. **List sessions**: `dev-prism list`
329
- - Query Docker: `docker ps --filter label=dev-prism.managed=true`
330
- - Group by `working_dir` label
331
- - Show only running sessions
332
-
333
- 5. **Stop session**: `dev-prism stop`
334
- - Find containers via labels
335
- - Run `docker compose stop`
336
- - Delete `.env.session` file
337
-
338
- ## Generated Files
196
+ 1. **`dev-prism create`** allocates ports via `get-port` + SQLite UNIQUE constraints
197
+ 2. **`dev-prism with-env -- <cmd>`** reads ports from SQLite, renders env templates, injects into command
198
+ 3. **Docker Compose** uses `${VAR:-default}` substitution standard, no dev-prism dependency
339
199
 
340
- ```
341
- session-2026-02-06T12-30-45/
342
- ├── .env.session # Discovered ports (gitignored)
343
- ├── docker-compose.session.yml # Generated compose file (gitignored)
344
- └── apps/my-app/.env.session # App-specific env (gitignored)
345
- ```
200
+ ### Typical workflow
346
201
 
347
- Example `.env.session` (after port discovery):
348
202
  ```bash
349
- # Auto-generated by dev-prism
350
- COMPOSE_PROJECT_NAME=myproject-a1b2c3d4
351
- SESSION_DIR=/Users/you/worktrees/session-2026-02-06T12-30-45
352
-
353
- # Discovered ports from running containers
354
- POSTGRES_PORT=54321
355
- APP_PORT=32768
356
- WEB_PORT=32769
357
- ```
203
+ dev-prism create --branch feature/auth
204
+ cd ../sessions/feature/auth
358
205
 
359
- Add to `.gitignore`:
360
- ```
361
- .env.session
362
- docker-compose.session.yml
206
+ dev-prism with-env -- docker compose up -d # infra on allocated ports
207
+ dev-prism with-env convas-app -- pnpm dev # app with PORT + DATABASE_URL
208
+
209
+ # Or in package.json scripts:
210
+ # "dev": "dev-prism with-env -- turbo dev"
211
+ # "docker:up": "dev-prism with-env -- docker compose up -d"
363
212
  ```
364
213
 
365
- ## Portability
214
+ ## Architecture
366
215
 
367
- To use in another project:
216
+ ### SQLite as Source of Truth
368
217
 
369
- 1. Install: `pnpm add -D dev-prism`
370
- 2. Create `session.config.mjs` (optional, has defaults)
371
- 3. Define services in `docker-compose.yml`
372
- 4. Run `dev-prism create`
218
+ Location: `<project-root>/.dev-prism/sessions.db`
373
219
 
374
- dev-prism generates `docker-compose.session.yml` automatically—you never need to write it.
220
+ ```sql
221
+ CREATE TABLE sessions (
222
+ id TEXT PRIMARY KEY, -- working directory path
223
+ branch TEXT,
224
+ created_at TEXT NOT NULL
225
+ );
375
226
 
376
- ## Migration from v0.5.x
227
+ CREATE TABLE port_allocations (
228
+ session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
229
+ service TEXT NOT NULL,
230
+ port INTEGER NOT NULL UNIQUE, -- prevents cross-session conflicts
231
+ PRIMARY KEY (session_id, service)
232
+ );
233
+ ```
377
234
 
378
- v0.6.0 is a breaking change with a new stateless architecture:
235
+ ### Port Allocation Strategy
379
236
 
380
- **What changed:**
381
- - Session IDs: `001, 002, 003` → full directory paths
382
- - Port allocation: calculated → random (Docker assigns)
383
- - State storage: SQLite database → stateless (Docker labels)
384
- - Commands: `dev-prism stop 001` → `dev-prism stop` (uses cwd)
237
+ Two-phase: `get-port` finds free TCP ports, SQLite UNIQUE prevents cross-session conflicts.
385
238
 
386
- **Migration steps:**
387
- 1. Stop all v0.5 sessions: `dev-prism stop-all` (on v0.5.x)
388
- 2. Upgrade to v0.6: `pnpm add -g dev-prism@0.6`
389
- 3. Recreate sessions as needed
239
+ 1. Query all existing allocated + reserved ports
240
+ 2. Use `get-port` to find free ports (excluding existing)
241
+ 3. INSERT all in a single transaction
242
+ 4. Retry once on UNIQUE violation (race condition)
243
+
244
+ ### `with-env` Pass-Through
245
+
246
+ ```
247
+ No project root found → exec command as-is
248
+ No session in DB → exec command as-is
249
+ Session found → render env templates → merge with process.env → exec
250
+ ```
390
251
 
391
- Old session directories can be deleted manually if no longer needed.
252
+ This makes `with-env` safe to use unconditionally in scripts, Makefiles, and CI.
392
253
 
393
- ## Why Stateless?
254
+ ## Portability
394
255
 
395
- **Problems with v0.5.x database approach:**
396
- - State could diverge from Docker reality (manual container removal)
397
- - Required reconciliation logic
398
- - Database was single point of failure
399
- - Stored data that Docker already knows
256
+ To use in another project:
400
257
 
401
- **v0.6+ stateless benefits:**
402
- - Docker is always the source of truth
403
- - No state sync issues
404
- - Survives `docker system prune`
405
- - Simpler codebase (fewer abstractions)
406
- - Auto-heals on every command
407
- - Zero configuration for port conflicts
258
+ 1. Install: `pnpm add -D dev-prism`
259
+ 2. Create `prism.config.mjs` with your ports and env templates
260
+ 3. Write your own `docker-compose.yml` using `${VAR:-default}` for ports
261
+ 4. Run `dev-prism create --in-place`
408
262
 
409
- ## Troubleshooting
263
+ dev-prism doesn't generate any Docker files — you own your Docker setup entirely.
410
264
 
411
- **"No session found in this directory"**
412
- - Session only exists when containers are running
413
- - Run `dev-prism create` to start a new session
265
+ ## Migration from v0.6.x
414
266
 
415
- **"Session already running in this directory"**
416
- - Containers are already running here
417
- - Use `dev-prism stop` first, then `dev-prism create` again
267
+ v0.7.0 is a breaking change that removes Docker orchestration:
418
268
 
419
- **Ports not in .env.session**
420
- - Run `dev-prism info` to regenerate from Docker
269
+ **What changed:**
270
+ - Port allocation: Docker random → `get-port` + SQLite
271
+ - State storage: Docker labels → SQLite database
272
+ - Docker management: Removed (user's responsibility)
273
+ - New commands: `with-env`, `env`
274
+ - Removed commands: `start`, `stop`, `stop-all`, `logs`
421
275
 
422
- **Want predictable ports?**
423
- - Override in your `docker-compose.yml`: `ports: ["5432:5432"]`
424
- - Trade-off: potential conflicts across sessions
276
+ **Migration steps:**
277
+ 1. Stop all v0.6 sessions: `dev-prism stop-all` (on v0.6.x)
278
+ 2. Upgrade: `pnpm add -g dev-prism@0.7`
279
+ 3. Update config to `prism.config.mjs` with new format (ports array, env templates)
280
+ 4. Write your own `docker-compose.yml` with `${VAR:-default}` port substitution
281
+ 5. Recreate sessions
425
282
 
426
283
  ## License
427
284