dev-prism 0.4.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 (98) hide show
  1. package/README.md +180 -131
  2. package/bin/dev-prism.js +77 -97
  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-Y3GR6XK7.js → chunk-NJAITOCG.js} +3 -13
  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 -4
  17. package/dist/commands/create.js +5 -7
  18. package/dist/commands/destroy.d.ts +1 -1
  19. package/dist/commands/destroy.js +4 -5
  20. package/dist/commands/env.d.ts +7 -0
  21. package/dist/commands/env.js +9 -0
  22. package/dist/commands/info.js +4 -2
  23. package/dist/commands/list.d.ts +1 -3
  24. package/dist/commands/list.js +2 -5
  25. package/dist/commands/prune.d.ts +1 -1
  26. package/dist/commands/prune.js +2 -5
  27. package/dist/commands/with-env.d.ts +3 -0
  28. package/dist/commands/with-env.js +9 -0
  29. package/dist/index.d.ts +9 -12
  30. package/dist/index.js +58 -56
  31. package/dist/lib/config.d.ts +4 -7
  32. package/dist/lib/config.js +1 -3
  33. package/dist/lib/db.d.ts +26 -0
  34. package/dist/lib/db.js +28 -0
  35. package/dist/lib/env.d.ts +7 -5
  36. package/dist/lib/env.js +9 -9
  37. package/dist/lib/worktree.d.ts +2 -3
  38. package/dist/lib/worktree.js +1 -3
  39. package/package.json +8 -8
  40. package/dist/chunk-35SHBLIZ.js +0 -69
  41. package/dist/chunk-3ATDGV4Y.js +0 -22
  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-AOM6BONB.js +0 -98
  50. package/dist/chunk-AW2FJGXA.js +0 -38
  51. package/dist/chunk-C4WLIOBR.js +0 -67
  52. package/dist/chunk-D6QWWXZD.js +0 -49
  53. package/dist/chunk-FKTFCSU7.js +0 -78
  54. package/dist/chunk-GBN67HYD.js +0 -57
  55. package/dist/chunk-GKXXK2ZH.js +0 -203
  56. package/dist/chunk-GWDGC2OE.js +0 -116
  57. package/dist/chunk-H4HPDIY3.js +0 -95
  58. package/dist/chunk-HCCZKLC4.js +0 -64
  59. package/dist/chunk-HZUN6NRB.js +0 -70
  60. package/dist/chunk-J36LRUXM.js +0 -60
  61. package/dist/chunk-JHR4WADC.js +0 -200
  62. package/dist/chunk-JIU574KX.js +0 -41
  63. package/dist/chunk-KZJE62TK.js +0 -203
  64. package/dist/chunk-LDK6QMR6.js +0 -67
  65. package/dist/chunk-LEHA65A7.js +0 -59
  66. package/dist/chunk-LNIOSGC4.js +0 -78
  67. package/dist/chunk-LOVO4P3Y.js +0 -41
  68. package/dist/chunk-NNVP5F6I.js +0 -77
  69. package/dist/chunk-OL25TBYX.js +0 -67
  70. package/dist/chunk-P3ETW2KK.js +0 -166
  71. package/dist/chunk-PJKUD2N2.js +0 -22
  72. package/dist/chunk-PKC2ZED2.js +0 -168
  73. package/dist/chunk-PS76Q3HD.js +0 -168
  74. package/dist/chunk-QSG5CXPX.js +0 -171
  75. package/dist/chunk-QUMZI5KK.js +0 -98
  76. package/dist/chunk-RC2AUYZ7.js +0 -49
  77. package/dist/chunk-SMFAL2VP.js +0 -69
  78. package/dist/chunk-SSQ7XBY2.js +0 -30
  79. package/dist/chunk-SUMJLXT7.js +0 -30
  80. package/dist/chunk-UHI2QJFI.js +0 -200
  81. package/dist/chunk-VR3QWHHB.js +0 -57
  82. package/dist/chunk-X5A6H4Q7.js +0 -70
  83. package/dist/chunk-X6FHBEAS.js +0 -200
  84. package/dist/chunk-XUWQUDLT.js +0 -67
  85. package/dist/commands/logs.d.ts +0 -8
  86. package/dist/commands/logs.js +0 -8
  87. package/dist/commands/start.d.ts +0 -7
  88. package/dist/commands/start.js +0 -9
  89. package/dist/commands/stop-all.d.ts +0 -3
  90. package/dist/commands/stop-all.js +0 -9
  91. package/dist/commands/stop.d.ts +0 -3
  92. package/dist/commands/stop.js +0 -7
  93. package/dist/lib/docker.d.ts +0 -12
  94. package/dist/lib/docker.js +0 -14
  95. package/dist/lib/ports.d.ts +0 -6
  96. package/dist/lib/ports.js +0 -8
  97. package/dist/lib/store.d.ts +0 -46
  98. package/dist/lib/store.js +0 -6
package/README.md CHANGED
@@ -4,27 +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
- **Minimal orchestration, maximal Docker Compose.** This tool does the bare minimum:
12
- 1. Creates git worktrees for isolated working directories
13
- 2. Generates `.env.session` with calculated ports
14
- 3. Runs `docker compose` commands
11
+ **Allocate ports. Inject env. Get out of the way.**
15
12
 
16
- All Docker configuration lives in `docker-compose.session.yml` in your project - a standard file you control.
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.
17
19
 
18
20
  ## Features
19
21
 
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
20
24
  - **Git worktrees** for isolated working directories (or in-place mode)
21
- - **Docker Compose** handles all container orchestration
22
- - **Unique ports** per session (calculated from session ID, displayed as clickable URLs)
23
- - **Auto-assign** session IDs or choose your own
24
- - **SQLite tracking** — all sessions stored in a local database
25
- - **Two modes**: Docker (apps in containers) or Native (apps run locally)
25
+ - **App-specific env** different env vars for different apps in a monorepo
26
26
  - **Claude Code integration** built-in (`dev-prism claude`)
27
- - **Portable**: Works with any project
27
+ - **Portable**: Works with any project, any runtime, any Docker setup
28
28
 
29
29
  ## Installation
30
30
 
@@ -34,66 +34,101 @@ npm install -g dev-prism
34
34
  pnpm add -D dev-prism
35
35
  ```
36
36
 
37
+ ## Quick Start
38
+
39
+ ```bash
40
+ # Create a session (allocates ports + creates worktree)
41
+ dev-prism create --branch feature/auth
42
+
43
+ # Or create in current directory
44
+ dev-prism create --in-place
45
+
46
+ # Start Docker services with allocated ports
47
+ dev-prism with-env -- docker compose up -d
48
+
49
+ # Run app with session env injected
50
+ dev-prism with-env my-app -- pnpm dev
51
+
52
+ # Show allocated ports and env vars
53
+ dev-prism info
54
+
55
+ # Print env vars (for eval or piping)
56
+ dev-prism env
57
+
58
+ # Write .env file for IDE
59
+ dev-prism env --write .env
60
+
61
+ # Destroy session
62
+ dev-prism destroy
63
+ ```
64
+
37
65
  ## Usage
38
66
 
39
67
  ### Create a session
40
68
 
41
69
  ```bash
42
- # Auto-assign session ID
70
+ # Create with worktree (generates timestamp-based branch)
43
71
  dev-prism create
44
72
 
45
- # Explicit session ID
46
- dev-prism create 001
47
-
48
73
  # Custom branch name
49
74
  dev-prism create --branch feature/my-feature
50
75
 
51
- # Native mode - only infrastructure in Docker, apps run via pnpm dev
52
- dev-prism create --mode native
53
-
54
- # Exclude specific apps from Docker
55
- dev-prism create --without web,widget
56
-
57
- # In-place mode - use current directory instead of creating worktree
76
+ # In-place mode use current directory instead of creating worktree
58
77
  dev-prism create --in-place
59
-
60
- # Stream logs after creation instead of detaching
61
- dev-prism create --no-detach
62
78
  ```
63
79
 
64
- ### List sessions
80
+ ### Inject env and run commands
65
81
 
66
82
  ```bash
67
- 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
68
91
  ```
69
92
 
70
- ### Session info (for current directory)
93
+ `with-env` is a no-op outside sessions — safe to use unconditionally in scripts and Makefiles.
94
+
95
+ ### View env vars
71
96
 
72
97
  ```bash
73
- 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
103
+
104
+ # Include app-specific vars
105
+ dev-prism env --app convas-app
74
106
  ```
75
107
 
76
- ### Start/Stop services
108
+ ### List sessions
77
109
 
78
110
  ```bash
79
- dev-prism stop 001 # Stop without destroying
80
- dev-prism start 001 # Start again
81
- dev-prism stop-all # Stop all sessions
111
+ dev-prism list
82
112
  ```
83
113
 
84
- ### View logs
114
+ ### Session info
85
115
 
86
116
  ```bash
87
- dev-prism logs 001
117
+ dev-prism info
88
118
  ```
89
119
 
90
120
  ### Cleanup
91
121
 
92
122
  ```bash
93
- dev-prism destroy 001 # Destroy specific session
94
- dev-prism destroy --all # Destroy all sessions
95
- dev-prism prune # Remove all stopped sessions
96
- dev-prism prune -y # Skip confirmation
123
+ # Destroy session in current directory
124
+ dev-prism destroy
125
+
126
+ # Destroy all sessions
127
+ dev-prism destroy --all
128
+
129
+ # Remove orphaned sessions from database
130
+ dev-prism prune
131
+ dev-prism prune -y # Skip confirmation
97
132
  ```
98
133
 
99
134
  ### Claude Code integration
@@ -103,134 +138,148 @@ dev-prism claude # Install Claude Code skill + CLAUDE.md
103
138
  dev-prism claude --force # Overwrite existing files
104
139
  ```
105
140
 
106
- ## Port Allocation
107
-
108
- Formula: `port = portBase + (sessionId * 100) + offset`
109
-
110
- With base port 47000:
111
-
112
- | Service | Session 001 | Session 002 | Session 003 |
113
- |----------------|-------------|-------------|-------------|
114
- | APP_PORT | 47100 | 47200 | 47300 |
115
- | WEB_PORT | 47101 | 47201 | 47301 |
116
- | POSTGRES_PORT | 47110 | 47210 | 47310 |
117
- | MAILPIT_SMTP | 47111 | 47211 | 47311 |
118
- | MAILPIT_WEB | 47112 | 47212 | 47312 |
119
-
120
141
  ## Configuration
121
142
 
122
- ### session.config.mjs
143
+ ### prism.config.mjs
123
144
 
124
145
  ```javascript
125
146
  export default {
126
- // Required
127
- portBase: 47000,
128
- sessionsDir: '../my-project-sessions',
129
-
130
- // Port offsets - become env vars for docker-compose
131
- // Formula: portBase + (sessionId * 100) + offset
132
- ports: {
133
- APP_PORT: 0, // 47100, 47200, 47300...
134
- WEB_PORT: 1, // 47101, 47201, 47301...
135
- POSTGRES_PORT: 10, // 47110, 47210, 47310...
136
- REDIS_PORT: 11, // 47111, 47211, 47311...
137
- },
138
-
139
- // Docker Compose profiles for app containers (used in docker mode)
140
- // These match service names with `profiles: ["app-name"]` in docker-compose
141
- apps: ['app', 'web'],
142
-
143
- // .env files to copy to session worktree (DATABASE_URL auto-updated)
144
- envFiles: [
145
- 'apps/my-app/.env',
146
- 'packages/db/.env',
147
- ],
147
+ ports: ['postgres', 'mailpit_http', 'mailpit_smtp', 'app', 'web'],
148
148
 
149
- // Commands to run after session creation
150
- 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
+ },
151
155
 
152
- // Optional: app-specific env for CLI commands from host (native mode)
153
- appEnv: {
154
- 'apps/my-app': {
155
- 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',
156
160
  },
161
+ 'convas-web': { PORT: '${web}' },
157
162
  },
163
+
164
+ setup: ['pnpm install'],
158
165
  };
159
166
  ```
160
167
 
161
- ### docker-compose.session.yml (standard Docker Compose)
168
+ ### docker-compose.yml (user-managed)
162
169
 
163
170
  ```yaml
164
171
  services:
165
172
  postgres:
166
173
  image: postgres:16
167
- container_name: postgres-${SESSION_ID}
168
174
  ports:
169
- - "${POSTGRES_PORT}:5432"
175
+ - "${POSTGRES_PORT:-5432}:5432"
170
176
  environment:
171
177
  POSTGRES_USER: postgres
172
178
  POSTGRES_PASSWORD: postgres
173
- healthcheck:
174
- test: ["CMD-SHELL", "pg_isready -U postgres"]
175
- interval: 5s
176
- timeout: 5s
177
- retries: 5
178
-
179
- my-app:
180
- profiles: ["apps"] # Only runs in docker mode
181
- build:
182
- context: .
183
- dockerfile: apps/my-app/Dockerfile.dev
184
- container_name: my-app-${SESSION_ID}
179
+ volumes:
180
+ - postgres-data:/var/lib/postgresql/data
181
+
182
+ mailpit:
183
+ image: axllent/mailpit
185
184
  ports:
186
- - "${APP_PORT}:3000"
187
- environment:
188
- DATABASE_URL: postgresql://postgres:postgres@postgres:5432/postgres
189
- depends_on:
190
- postgres:
191
- condition: service_healthy
185
+ - "${MAILPIT_HTTP_PORT:-8025}:8025"
186
+ - "${MAILPIT_SMTP_PORT:-1025}:1025"
187
+
188
+ volumes:
189
+ postgres-data:
192
190
  ```
193
191
 
194
- ## How It Works
192
+ The `:-` defaults mean it works without dev-prism too (solo dev, standard ports).
195
193
 
196
- 1. **Create session**: `dev-prism create`
197
- - Auto-assigns next available session ID (or use explicit ID)
198
- - Creates git worktree at `../project-sessions/session-001` (or uses current dir with `--in-place`)
199
- - Generates `.env.session` with calculated ports
200
- - Records session in local SQLite database
201
- - Runs `docker compose --env-file .env.session up -d`
202
- - Runs setup commands
194
+ ## How It Works
203
195
 
204
- 2. **Docker Compose** reads `.env.session` and substitutes `${VAR}` placeholders
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
205
199
 
206
- 3. **Docker mode** (`--profile apps`): All services including apps run in containers
207
- 4. **Native mode**: Only infrastructure runs; apps use `pnpm dev` with `.env.session`
200
+ ### Typical workflow
208
201
 
209
- All session state is tracked in a SQLite database (`~/.dev-prism/sessions.db`), making both worktree and in-place sessions first-class citizens across all commands.
202
+ ```bash
203
+ dev-prism create --branch feature/auth
204
+ cd ../sessions/feature/auth
210
205
 
211
- ## Generated Files
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
212
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"
213
212
  ```
214
- session-001/
215
- ├── .env.session # Port variables for docker-compose
216
- ├── docker-compose.session.yml # (from git, not generated)
217
- └── apps/my-app/.env.session # App-specific env for host CLI
213
+
214
+ ## Architecture
215
+
216
+ ### SQLite as Source of Truth
217
+
218
+ Location: `<project-root>/.dev-prism/sessions.db`
219
+
220
+ ```sql
221
+ CREATE TABLE sessions (
222
+ id TEXT PRIMARY KEY, -- working directory path
223
+ branch TEXT,
224
+ created_at TEXT NOT NULL
225
+ );
226
+
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
+ );
218
233
  ```
219
234
 
220
- Example `.env.session`:
221
- ```bash
222
- SESSION_ID=001
223
- POSTGRES_PORT=47110
224
- MAILPIT_SMTP_PORT=47111
225
- MAILPIT_WEB_PORT=47112
226
- APP_PORT=47100
235
+ ### Port Allocation Strategy
236
+
237
+ Two-phase: `get-port` finds free TCP ports, SQLite UNIQUE prevents cross-session conflicts.
238
+
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
+
227
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
+ ```
251
+
252
+ This makes `with-env` safe to use unconditionally in scripts, Makefiles, and CI.
228
253
 
229
254
  ## Portability
230
255
 
231
256
  To use in another project:
232
257
 
233
258
  1. Install: `pnpm add -D dev-prism`
234
- 2. Create `session.config.mjs` with port offsets
235
- 3. Create `docker-compose.session.yml` with `${VAR}` placeholders
236
- 4. Run `dev-prism create`
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`
262
+
263
+ dev-prism doesn't generate any Docker files — you own your Docker setup entirely.
264
+
265
+ ## Migration from v0.6.x
266
+
267
+ v0.7.0 is a breaking change that removes Docker orchestration:
268
+
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`
275
+
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
282
+
283
+ ## License
284
+
285
+ MIT
package/bin/dev-prism.js CHANGED
@@ -6,114 +6,96 @@ import { destroySession } from '../dist/commands/destroy.js';
6
6
  import { listSessions } from '../dist/commands/list.js';
7
7
  import { installClaude } from '../dist/commands/claude.js';
8
8
  import { showInfo } from '../dist/commands/info.js';
9
- import { startSession } from '../dist/commands/start.js';
10
- import { stopSession } from '../dist/commands/stop.js';
11
- import { stopAllSessions } from '../dist/commands/stop-all.js';
9
+ import { withEnv } from '../dist/commands/with-env.js';
10
+ import { showEnv } from '../dist/commands/env.js';
12
11
  import { pruneSessions } from '../dist/commands/prune.js';
13
- import { streamLogs } from '../dist/commands/logs.js';
14
12
 
15
13
  const program = new Command();
16
14
 
17
15
  program
18
16
  .name('dev-prism')
19
- .description('CLI tool for managing isolated parallel development sessions')
20
- .version('0.4.0');
17
+ .description('Port allocator, env injector, and worktree manager for parallel development')
18
+ .version('0.7.0')
19
+ .enablePositionalOptions();
21
20
 
22
21
  program
23
- .command('create [sessionId]')
24
- .description('Create a new isolated development session')
25
- .option('-m, --mode <mode>', 'App mode: docker (default) or native', 'docker')
26
- .option('-b, --branch <branch>', 'Git branch name (default: session/YYYY-MM-DD/XXX)')
27
- .option('-W, --without <apps>', 'Exclude apps (comma-separated: app,web,widget)', (val) => val.split(','))
28
- .option('--no-detach', 'Stream container logs after starting (default: detach)')
22
+ .command('create')
23
+ .description('Create a new session (allocate ports + optional worktree)')
24
+ .option('-b, --branch <branch>', 'Git branch name (default: session/TIMESTAMP)')
29
25
  .option('--in-place', 'Run in current directory instead of creating a worktree')
30
- .action(async (sessionId, options) => {
26
+ .action(async (options) => {
31
27
  const projectRoot = process.cwd();
32
- await createSession(projectRoot, sessionId, {
33
- mode: options.mode,
28
+ await createSession(projectRoot, {
34
29
  branch: options.branch,
35
- detach: options.detach,
36
- without: options.without,
37
30
  inPlace: options.inPlace,
38
31
  });
39
32
  });
40
33
 
41
34
  program
42
- .command('destroy [sessionId]')
43
- .description('Destroy a development session')
35
+ .command('destroy')
36
+ .description('Destroy session for current directory (deallocate ports + remove worktree)')
44
37
  .option('-a, --all', 'Destroy all sessions')
45
- .action(async (sessionId, options) => {
46
- const projectRoot = process.cwd();
47
- await destroySession(projectRoot, sessionId, { all: options.all });
38
+ .action(async (options) => {
39
+ await destroySession(process.cwd(), { all: options.all });
48
40
  });
49
41
 
50
42
  program
51
43
  .command('list')
52
- .description('List all active development sessions')
53
- .option('-a, --all', 'Show sessions across all projects')
54
- .action(async (options) => {
55
- const projectRoot = process.cwd();
56
- await listSessions(projectRoot, { all: options.all });
44
+ .description('List all sessions')
45
+ .action(async () => {
46
+ await listSessions();
57
47
  });
58
48
 
59
49
  program
60
50
  .command('info')
61
- .description('Show session info for current directory (useful for --in-place sessions)')
51
+ .description('Show session ports and env vars for current directory')
62
52
  .action(async () => {
63
53
  await showInfo(process.cwd());
64
54
  });
65
55
 
66
- program
67
- .command('start <sessionId>')
68
- .description('Start Docker services for a session')
69
- .option('-m, --mode <mode>', 'App mode: docker or native', 'docker')
70
- .option('-W, --without <apps>', 'Exclude apps (comma-separated: app,web,widget)', (val) => val.split(','))
71
- .action(async (sessionId, options) => {
72
- const projectRoot = process.cwd();
73
- await startSession(projectRoot, sessionId, {
74
- mode: options.mode,
75
- without: options.without,
76
- });
77
- });
78
-
79
- program
80
- .command('stop <sessionId>')
81
- .description('Stop Docker services for a session (without destroying)')
82
- .action(async (sessionId) => {
83
- const projectRoot = process.cwd();
84
- await stopSession(projectRoot, sessionId);
85
- });
86
-
87
- program
88
- .command('logs <sessionId>')
89
- .description('Stream logs from a session\'s Docker services')
90
- .option('-m, --mode <mode>', 'App mode: docker or native', 'docker')
91
- .option('-W, --without <apps>', 'Exclude apps (comma-separated: app,web,widget)', (val) => val.split(','))
92
- .option('-n, --tail <lines>', 'Number of lines to show from the end', '50')
93
- .action(async (sessionId, options) => {
94
- const projectRoot = process.cwd();
95
- await streamLogs(projectRoot, sessionId, {
96
- mode: options.mode,
97
- without: options.without,
98
- tail: options.tail,
99
- });
56
+ const withEnvCmd = program
57
+ .command('with-env [app]')
58
+ .description('Inject session env vars and exec a command')
59
+ .passThroughOptions()
60
+ .allowUnknownOption()
61
+ .action(async (app, options, cmd) => {
62
+ // cmd.args includes [app] as its first element when set — strip it.
63
+ const rawArgs = app ? cmd.args.slice(1) : cmd.args;
64
+
65
+ let appName;
66
+ let command;
67
+
68
+ const dashIdx = rawArgs.indexOf('--');
69
+ if (dashIdx !== -1) {
70
+ // dev-prism with-env my-app -- echo hello
71
+ appName = app;
72
+ command = rawArgs.slice(dashIdx + 1);
73
+ } else if (app) {
74
+ // No --, treat app as start of command (not an app name)
75
+ // dev-prism with-env echo hello world
76
+ command = [app, ...rawArgs];
77
+ } else {
78
+ command = rawArgs;
79
+ }
80
+
81
+ await withEnv(command, appName);
100
82
  });
101
83
 
102
84
  program
103
- .command('stop-all')
104
- .description('Stop all running sessions (preserves data)')
105
- .action(async () => {
106
- const projectRoot = process.cwd();
107
- await stopAllSessions(projectRoot);
85
+ .command('env')
86
+ .description('Print or write session env vars')
87
+ .option('-w, --write <path>', 'Write env to file instead of stdout')
88
+ .option('-a, --app <name>', 'Include app-specific env vars')
89
+ .action(async (options) => {
90
+ await showEnv({ write: options.write, app: options.app });
108
91
  });
109
92
 
110
93
  program
111
94
  .command('prune')
112
- .description('Remove all stopped sessions (destroys data)')
95
+ .description('Remove orphaned sessions from the database')
113
96
  .option('-y, --yes', 'Skip confirmation prompt')
114
97
  .action(async (options) => {
115
- const projectRoot = process.cwd();
116
- await pruneSessions(projectRoot, { yes: options.yes });
98
+ await pruneSessions({ yes: options.yes });
117
99
  });
118
100
 
119
101
  program
@@ -131,53 +113,51 @@ program
131
113
  const chalk = (await import('chalk')).default;
132
114
 
133
115
  console.log(`
134
- ${chalk.bold('dev-prism')} - Manage isolated parallel development sessions
116
+ ${chalk.bold('dev-prism')} - Port allocator, env injector, and worktree manager
135
117
 
136
118
  ${chalk.bold('USAGE')}
137
119
  dev-prism <command> [options]
138
120
 
139
121
  ${chalk.bold('COMMANDS')}
140
- ${chalk.cyan('create')} [id] Create a new session (auto-assigns ID if not provided)
141
- ${chalk.cyan('destroy')} <id> Destroy a specific session
142
- ${chalk.cyan('list')} List all sessions and their status
143
- ${chalk.cyan('info')} Show session info for current directory
144
- ${chalk.cyan('start')} <id> Start Docker services for a stopped session
145
- ${chalk.cyan('stop')} <id> Stop Docker services (preserves data)
146
- ${chalk.cyan('stop-all')} Stop all running sessions
147
- ${chalk.cyan('logs')} <id> Stream logs from a session
148
- ${chalk.cyan('prune')} Remove all stopped sessions
122
+ ${chalk.cyan('create')} Create a new session (ports + worktree)
123
+ ${chalk.cyan('destroy')} Destroy session for current directory
124
+ ${chalk.cyan('list')} List all sessions
125
+ ${chalk.cyan('info')} Show session info for current directory
126
+ ${chalk.cyan('with-env')} [app] -- <command> Inject env vars + exec command
127
+ ${chalk.cyan('env')} Print/write session env vars
128
+ ${chalk.cyan('prune')} Remove orphaned sessions
129
+ ${chalk.cyan('claude')} Install Claude Code integration
149
130
 
150
131
  ${chalk.bold('EXAMPLES')}
151
- ${chalk.gray('# Create a new session (auto-assigns next available ID)')}
132
+ ${chalk.gray('# Create a new session with worktree')}
152
133
  $ dev-prism create
153
134
 
154
- ${chalk.gray('# Create session with specific branch')}
135
+ ${chalk.gray('# Create session with specific branch name')}
155
136
  $ dev-prism create --branch feature/my-feature
156
137
 
157
- ${chalk.gray('# Create session in native mode (apps run on host)')}
158
- $ dev-prism create --mode native
159
-
160
- ${chalk.gray('# Create session without web app')}
161
- $ dev-prism create --without web
162
-
163
138
  ${chalk.gray('# Create session in current directory (no worktree)')}
164
139
  $ dev-prism create --in-place
165
140
 
166
- ${chalk.gray('# Check session status in current directory')}
167
- $ dev-prism info
141
+ ${chalk.gray('# Start Docker services with injected ports')}
142
+ $ dev-prism with-env -- docker compose up -d
168
143
 
169
- ${chalk.gray('# Stop all running sessions before switching context')}
170
- $ dev-prism stop-all
144
+ ${chalk.gray('# Run app with app-specific env vars')}
145
+ $ dev-prism with-env my-app -- pnpm dev
171
146
 
172
- ${chalk.gray('# Clean up old stopped sessions')}
173
- $ dev-prism prune
147
+ ${chalk.gray('# Show session env vars')}
148
+ $ dev-prism env
149
+
150
+ ${chalk.gray('# Write env file for IDE')}
151
+ $ dev-prism env --write .env
152
+
153
+ ${chalk.gray('# Destroy session in current directory')}
154
+ $ dev-prism destroy
174
155
 
175
156
  ${chalk.gray('# Destroy all sessions')}
176
157
  $ dev-prism destroy --all
177
158
 
178
- ${chalk.bold('SESSION MODES')}
179
- ${chalk.cyan('docker')} (default) All apps run in containers
180
- ${chalk.cyan('native')} Only infrastructure in Docker, apps on host
159
+ ${chalk.gray('# Clean up orphaned sessions')}
160
+ $ dev-prism prune
181
161
 
182
162
  ${chalk.bold('MORE INFO')}
183
163
  Run ${chalk.cyan('dev-prism <command> --help')} for command-specific options