@devrouter/cli 0.0.1 → 0.0.23

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 CHANGED
@@ -23,21 +23,141 @@ Each repo now uses one file:
23
23
 
24
24
  This is the only supported per-repo config for app routing/runtime definitions.
25
25
 
26
+ ## Two ways to use devrouter
27
+
28
+ Both are configured the same way (`.devrouter.yml`) and can be mixed in one repo.
29
+
30
+ ### 1. Front a devcontainer / existing process: `runtime: proxy` (preferred)
31
+
32
+ The recommended setup is devcontainer first. The devcontainer owns the toolchain,
33
+ databases, auth mocks, app process, and seed data. devrouter owns only the local
34
+ routes. In the best case the container joins `devnet` and exposes stable network
35
+ aliases, so the app and database need no published host ports.
36
+
37
+ ```yaml
38
+ apps:
39
+ - name: app
40
+ host: myapp.localhost
41
+ protocol: http
42
+ runtime: proxy
43
+ upstream: myapp-app:3000 # devnet alias inside the devcontainer compose
44
+ ```
45
+
46
+ ```bash
47
+ dev setup --yes
48
+ devpod up .
49
+ dev repo devcontainer verify --live --yes --json
50
+ ```
51
+
52
+ Why prefer it: the environment is reproducible and runs anywhere the devcontainer
53
+ spec runs, while devrouter gives it stable local HTTPS and database hostnames.
54
+ Agents can add the scaffold with `dev repo inspect`, `dev repo devcontainer write`,
55
+ and `dev repo devcontainer verify`, then include the JSON evidence in a PR. See
56
+ [`docs/DEVCONTAINER.md`](./docs/DEVCONTAINER.md) for the full reference.
57
+
58
+ ### 2. devrouter runs everything — `runtime: host` / `runtime: docker`
59
+
60
+ The original mode: devrouter starts your app (`runtime: host`, via `hostRun`) and
61
+ manages its Docker datastores/dependencies (`runtime: docker`), injecting DB env
62
+ vars. Use it when you are not (yet) on a devcontainer. Fully supported.
63
+
26
64
  ## Core commands
27
65
 
28
- - `dev init [--repo <path>] [--entries-json <json>] [--json]`
66
+ - `dev init [--repo <path>] [--entries-json <json>] [--json] [--write-agents] [--write-skill] [--with-linear]`
67
+ - `dev -V [--repo <path>]` (installed CLI version, local repo version, next upgrade target)
68
+ - `dev upgrade [version] [--repo <path>]`
69
+ - `dev setup --yes [--repo <path>] [--json]`
29
70
  - `dev up`
30
71
  - `dev down`
31
72
  - `dev status [--repo <path>] [--json]`
32
73
  - `dev doctor|verify [--repo <path>] [--json]`
33
74
  - `dev ls [--json]`
34
- - `dev open <name>`
75
+ - `dev open <name>` (matches app name, then service/container/host)
35
76
  - `dev tls install`
36
77
  - `dev repo init [--repo <path>]`
37
- - `dev app add ...`
78
+ - `dev repo inspect [--repo <path>] [--json]`
79
+ - `dev repo devcontainer write [--repo <path>] [--dry-run] [--yes] [--json]`
80
+ - `dev repo devcontainer verify [--repo <path>] [--live] [--yes] [--json]`
81
+ - `dev repo agents [--repo <path>] [--with-linear]`
82
+ - `dev app add ...` (`--kind app|dependency`, default `app`)
38
83
  - `dev app ls [--repo <path>] [--json]`
39
- - `dev app run <name> [--repo <path>] [--yes]`
84
+ - `dev app run <name> [--repo <path>] [--yes] [--workspace <slug>]`
85
+ - `dev app exec <name> [--repo <path>] [--yes] [--shell] [--env <env>] [--workspace <slug>] -- <command>`
40
86
  - `dev app rm <name> [--repo <path>]`
87
+ - `dev logs [-f]`
88
+ - `dev workspace up <branch> [--path <dir>] [--no-devpod] [--open] [--repo <path>]`
89
+ - `dev workspace ls [--repo <path>] [--json]`
90
+ - `dev workspace down <workspace|branch> [--keep-worktree] [--keep-devpod] [--repo <path>]`
91
+
92
+ The current `dev repo devcontainer write` scaffold is intentionally narrow:
93
+ Node + pnpm + Postgres. Other package managers stop with a JSON diagnostic
94
+ instead of writing files that would need manual repair.
95
+ Use `dev repo devcontainer verify --json` for read-only PR evidence; add
96
+ `--live --yes` only after the devcontainer is running and route probes should
97
+ mutate local route state.
98
+
99
+ ## Workspace isolation (parallel worktrees)
100
+
101
+ A **workspace token** lets several git worktrees of the same repo run side-by-side without host or route collisions. The token is a single identity spanning three layers: the devpod workspace id, the routes devrouter registers, and the `${WORKSPACE}` placeholder in `.devrouter.yml` proxy upstreams and devcontainer compose network aliases.
102
+
103
+ **Token resolution precedence** (highest to lowest):
104
+
105
+ 1. `--workspace <slug>` CLI flag
106
+ 2. `DEVROUTER_WORKSPACE` environment variable
107
+ 3. Auto-derived from the linked git worktree's branch name (sanitized: lowercase, non-alphanumeric → `-`, capped at 32 chars)
108
+ 4. None — the primary checkout uses no token and routes exactly as a plain repo (back-compatible)
109
+
110
+ **When a workspace token is active:**
111
+
112
+ - Hosts are auto-namespaced: `web.localhost` → `web.<ws>.localhost`
113
+ - `${WORKSPACE}` in a proxy app's `upstream` (e.g. `upstream: ${WORKSPACE}-app:3000`) is substituted with the token at runtime
114
+ - The committed `.devrouter.yml` is never rewritten — all namespacing is computed in memory
115
+ - TLS: namespaced hosts are not covered by `*.localhost`; devrouter auto-extends mkcert cert SANs for active workspace hosts
116
+
117
+ `${WORKSPACE}` is valid in `upstream` only. Using it in `host` is rejected (hosts are namespaced automatically).
118
+
119
+ **Typical workflow:**
120
+
121
+ ```bash
122
+ # Bring up a feature branch as an isolated workspace
123
+ dev workspace up feat/my-feature
124
+
125
+ # List git worktrees with workspace tokens and route counts
126
+ dev workspace ls
127
+
128
+ # Tear down a workspace (stop devpod, remove worktree, free routes)
129
+ dev workspace down feat/my-feature
130
+ ```
131
+
132
+ **devcontainer integration:** the devcontainer compose service exposes a devnet network alias `${WORKSPACE}-app` (defaulting to the project name in `devcontainer.env`); the proxy app uses `upstream: ${WORKSPACE}-app:<port>`. Workspace `feat-a` → alias `feat-a-app`, host `app.feat-a.localhost`.
133
+
134
+ **Try it:** [`examples/workspace/`](examples/workspace/) is a runnable showcase — `./run.sh` brings up one app in two parallel worktrees (`wsdemo.localhost` and `wsdemo.feat-a.localhost`) served at once, then `./run.sh down` tears it down.
135
+
136
+ **DevPod example:** [`examples/devcontainer/`](examples/devcontainer/) is the
137
+ agent-native devcontainer path end to end — `./run.sh` brings up a DevPod
138
+ workspace, registers app/Postgres proxy routes, runs static/live verification,
139
+ and prints the proof. `./run.sh down` tears it down.
140
+
141
+ **Orphan detection:** `dev doctor` check `routes.orphaned-workspace-routes` reports proxy routes whose worktree directory was removed without `dev workspace down`. It does not mutate route state.
142
+
143
+ ## Upgrade metadata and prompts
144
+
145
+ `dev upgrade` and `dev -V` read local upgrade metadata from `.devrouter.yml` in the target repo (`devrouter.version`):
146
+
147
+ ```yaml
148
+ version: 1
149
+ devrouter:
150
+ version: <semver>
151
+ apps: []
152
+ ```
153
+
154
+ Quick checks:
155
+
156
+ - `dev -V` shows installed CLI version, local repo version, and next available upgrade target.
157
+ - `dev upgrade` lists all upgrade targets newer than the local repo version and marks the next one.
158
+ - `dev upgrade <version>` prints that target release's Agent Adaptation Prompt and then shows if a further version is available.
159
+ - Upgrade prompts are sourced from `upgrade-prompts/<version>.md`.
160
+ - `dev repo init` initializes `devrouter.version` to the installed CLI version.
41
161
 
42
162
  ## AI-native onboarding prompt
43
163
 
@@ -47,6 +167,8 @@ Generate a ready-to-copy onboarding prompt for an AI agent:
47
167
  dev init --repo /absolute/path/to/repo
48
168
  ```
49
169
 
170
+ By default, this command is non-mutating (it prints prompt text only).
171
+
50
172
  Optional: embed target app entries as JSON:
51
173
 
52
174
  ```bash
@@ -59,9 +181,23 @@ JSON mode for machine consumption:
59
181
  dev init --repo /absolute/path/to/repo --json
60
182
  ```
61
183
 
184
+ Optional repo artifact writes are explicit:
185
+
186
+ ```bash
187
+ dev init --repo /absolute/path/to/repo --write-agents --write-skill
188
+ ```
189
+
190
+ Optional: also bootstrap Linear workflow skill/templates and AGENTS section:
191
+
192
+ ```bash
193
+ dev init --repo /absolute/path/to/repo --with-linear --write-agents --write-skill
194
+ ```
195
+
196
+ When `--with-linear` is combined with AGENTS writes, devrouter captures minimal Linear mapping (workspace, team, project). In non-interactive mode it writes placeholders and prints a warning so values can be filled in later.
197
+
62
198
  ## Health diagnostics
63
199
 
64
- Run deep checks for global router state and repo configuration:
200
+ Run check-only diagnostics for global router state, machine prerequisites, route state, and repo configuration:
65
201
 
66
202
  ```bash
67
203
  dev doctor --repo /absolute/path/to/repo
@@ -74,11 +210,17 @@ dev doctor --repo /absolute/path/to/repo --json
74
210
  ```
75
211
 
76
212
  `dev status` now includes readiness hints and next-step commands.
213
+ For host apps that depend on postgres, `dev doctor` also checks host command wrapper precedence and warns with `repo.host-command-env-precedence` when `DATABASE_URI`/`DATABASE_URL` is assigned before a `run --` wrapper boundary.
214
+ When TLS is enabled, `dev doctor` also checks TLS host coverage and warns with `repo.tls-host-coverage` if configured `.localhost` hosts are not covered by the current cert SANs.
215
+ When `.devcontainer/` exists, `dev doctor` checks devnet aliases, published host ports, and proxy upstream alias matches.
216
+ `dev doctor` reports stale host routes and orphaned workspace proxy routes without mutating route state.
77
217
 
78
218
  ## `.devrouter.yml` example
79
219
 
80
220
  ```yaml
81
221
  version: 1
222
+ devrouter:
223
+ version: 0.0.14
82
224
  project:
83
225
  name: my-repo
84
226
  apps:
@@ -95,6 +237,7 @@ apps:
95
237
  allowPortRange: "1024-65535"
96
238
  dependencies:
97
239
  - app: db
240
+ - app: redis
98
241
 
99
242
  - name: db
100
243
  host: db.localhost
@@ -106,13 +249,33 @@ apps:
106
249
  internalPort: 5432
107
250
  composeFiles:
108
251
  - docker-compose.yml
252
+
253
+ - name: redis
254
+ kind: dependency
255
+ runtime: docker
256
+ docker:
257
+ service: redis
258
+ composeFiles:
259
+ - docker-compose.yml
260
+
261
+ # Route to an already-running port (e.g. a devcontainer's published app).
262
+ # No lifecycle, env injection, or dependencies — devrouter only registers the route.
263
+ # Use ${WORKSPACE} in upstream for parallel-worktree isolation (see "Workspace isolation").
264
+ - name: app
265
+ host: app.localhost
266
+ protocol: http
267
+ runtime: proxy
268
+ upstream: 127.0.0.1:3000
269
+ # upstream: ${WORKSPACE}-app:3000 # workspace-aware variant
109
270
  ```
110
271
 
111
272
  Notes:
112
273
 
113
- - TCP mode currently supports PostgreSQL first (`tcpProtocol: postgres`).
114
- - Multi-DB hostname routing on shared `:5432` requires TLS/SNI.
115
- - Plaintext Postgres is not supported for multiplexed hostname routing.
274
+ - `kind` defaults to routed app behavior. Use `kind: dependency` for non-routed Docker dependencies.
275
+ - `runtime: proxy` registers an HTTP route to an externally-managed `upstream` (`host:port`) and does nothing else — use it to put a stable `*.localhost` HTTPS host in front of a devcontainer or any process you start yourself. Loopback upstreams (`localhost`/`127.0.0.1`/`0.0.0.0`) are rewritten to `host.docker.internal` so Traefik (in Docker) can reach the host. The route persists until `dev app rm`.
276
+ - TCP routing supports `tcpProtocol: postgres`, `redis`, `mariadb`, and `mysql` on shared protocol ports with TLS/SNI.
277
+ - Plaintext TCP is not supported for multiplexed hostname routing.
278
+ - Multi-segment `.localhost` hosts are supported (for example `elearning.klicker.localhost`).
116
279
 
117
280
  ## Runtime behavior
118
281
 
@@ -120,11 +283,45 @@ Notes:
120
283
 
121
284
  - reads `.devrouter.yml`
122
285
  - prompts to start declared dependencies (or use `--yes`)
123
- - starts only declared docker dependency services
286
+ - starts docker target services for `runtime: docker` apps, plus declared docker dependencies
287
+ - for `runtime: proxy` apps: registers the route to `upstream` and returns immediately (no process started, no dependencies); re-running is an idempotent upsert and the route persists until `dev app rm`
124
288
  - fails fast if host-runtime dependencies are configured (start those manually)
289
+ - waits for Docker dependencies to become healthy (`--wait`) before proceeding
290
+ - automatically stops Docker dependencies when a host app exits; docker app services remain running until explicit cleanup (`docker compose down`, `dev down`, or equivalent)
291
+ - prints recent dependency logs (last 20 lines) after deps start
292
+ - `kind=dependency` apps are dependency-only: they do not create routes and cannot be direct targets for `dev app run`, `dev app exec`, or `dev open`
293
+ - `kind=dependency` services start as declared in compose (no Traefik labels, no random published ports, no injected env vars)
294
+ - for TCP deps of host apps: publishes a random host port and injects per-dependency `<NAME>_HOST`/`<NAME>_PORT`/`<NAME>_URL` env vars into the host process; for postgres deps also injects `<NAME>_SHADOW_URL` (fixed credentials `prisma:prisma`, databases `prisma`/`shadow`)
295
+ - for one-shot commands, `dev app exec` starts declared docker deps as needed and only stops deps it started in that invocation (already-running deps stay running)
296
+ - if `dev app exec` cannot determine pre-existing running services, it leaves selected deps running to avoid stopping non-owned services
297
+ - when TLS is enabled, `dev app run` / `dev app exec` auto-refresh cert SAN coverage for configured repo hosts before startup (fails fast with `Run: dev tls install` guidance if refresh fails)
298
+ - for one-shot commands, `dev app exec` preserves argv semantics by default (`shell: false`) to avoid nested quoting issues
299
+ - `dev app exec --shell` is explicit and requires one command string after `--`
300
+ - config-level `envMap` on dependency references maps aliases after dependency env resolution (for example `DATABASE_URL: DB_URL`)
125
301
  - starts host app command for host runtime apps
126
302
  - generates docker overlay in `~/.config/devrouter/cache/...` for docker runtime apps
127
303
 
304
+ Secret manager interop (Infisical/Doppler):
305
+
306
+ - dependency env injection from devrouter includes `<NAME>_HOST`, `<NAME>_PORT`, `<NAME>_URL`, and postgres-only `<NAME>_SHADOW_URL`
307
+ - do not assume secret-manager precedence when DB vars overlap; validate effective env before migrate/seed
308
+ - avoid pre-wrapper DB assignments such as `DATABASE_URI=... <wrapper> run -- ...`; wrapper-managed env may override those values
309
+ - map app-specific names in `.devrouter.yml`:
310
+ ```yaml
311
+ dependencies:
312
+ - app: db
313
+ envMap:
314
+ DATABASE_URL: DB_URL
315
+ DIRECT_URL: DB_URL
316
+ SHADOW_DATABASE_URL: DB_SHADOW_URL
317
+ ```
318
+ - safe host-run override pattern when wrapper also defines `DATABASE_URI`: `infisical run --projectId <id> --env=<env> -- env DATABASE_URI=${DB_URL:?missing DB_URL} pnpm dev`
319
+ - non-Prisma mapping example: `dev app exec web --yes -- infisical run --projectId <id> --env=<env> -- pnpm payload migrate`
320
+ - env probe example: `dev app exec web --yes -- printenv DB_URL DATABASE_URL DB_HOST DB_PORT DB_SHADOW_URL SHADOW_DATABASE_URL`
321
+ - run `dev doctor --repo <path>` to surface risky wrapper precedence (`repo.host-command-env-precedence`) before migrations or app startup
322
+
323
+ `dev ls` output includes both configured app identity (`APP`) and runtime service identity (`SERVICE`).
324
+
128
325
  ## First onboarding quick path
129
326
 
130
327
  In a repo that has a host app and a Docker Postgres service:
@@ -133,7 +330,8 @@ In a repo that has a host app and a Docker Postgres service:
133
330
  dev repo init
134
331
  dev app add --name web --host web.localhost --protocol http --runtime host --command "pnpm dev" --cwd .
135
332
  dev app add --name db --host db.localhost --protocol tcp --runtime docker --tcp-protocol postgres --service db --port 5432 --compose-file docker-compose.yml
136
- dev app add --name web --host web.localhost --protocol http --runtime host --command "pnpm dev" --cwd . --depends-on db
333
+ dev app add --name redis --kind dependency --service redis --compose-file docker-compose.yml
334
+ dev app add --name web --host web.localhost --protocol http --runtime host --command "pnpm dev" --cwd . --depends-on db --depends-on redis
137
335
  dev tls install
138
336
  dev app run web --yes
139
337
  dev ls
@@ -144,11 +342,11 @@ Expected endpoints:
144
342
  - `https://web.localhost`
145
343
  - `postgres://db.localhost:5432 (tls required)`
146
344
 
147
- ## Demo workspace (in this repo)
345
+ ## Routing example (without devcontainers)
148
346
 
149
- A complete sample repository is included at:
347
+ A complete no-devcontainer sample repository is included at:
150
348
 
151
- - [`./demo`](./demo)
349
+ - [`./examples/routing`](./examples/routing)
152
350
 
153
351
  It contains:
154
352
 
@@ -157,21 +355,68 @@ It contains:
157
355
  - Postgres in Docker (`db`)
158
356
  - ready-to-use `.devrouter.yml`
159
357
 
160
- Run the end-to-end smoke demo:
358
+ Run the bundled routing smoke:
161
359
 
162
360
  ```bash
163
- pnpm demo:smoke
361
+ pnpm routing:smoke
362
+ ```
363
+
364
+ Run the live DevPod/devcontainer smoke when Docker, DevPod, and mkcert are
365
+ available:
366
+
367
+ ```bash
368
+ pnpm devcontainer:smoke
369
+ pnpm devcontainer:smoke down
164
370
  ```
165
371
 
166
372
  See details:
167
373
 
168
- - [`./demo/README.md`](./demo/README.md)
374
+ - [`./examples/routing/README.md`](./examples/routing/README.md)
375
+ - [`./examples/devcontainer/README.md`](./examples/devcontainer/README.md)
376
+
377
+ ## AI agent discoverability
378
+
379
+ `dev repo agents` writes a devrouter section into the repo's `AGENTS.md` and installs a skill file at `.agents/skills/devrouter/SKILL.md`. The skill content is embedded in the CLI bundle so it stays in sync across repos.
380
+
381
+ If you also want Linear workflow assets and repository mapping metadata, run:
382
+
383
+ ```bash
384
+ dev repo agents --with-linear
385
+ ```
386
+
387
+ This additionally installs:
388
+
389
+ - `.agents/skills/linear-workflow/SKILL.md`
390
+ - `.agents/skills/linear-workflow/references/LINEAR_ISSUE_TEMPLATE.md`
391
+ - `.agents/skills/linear-workflow/references/MILESTONE_PLAN_TEMPLATE.md`
392
+ - `.agents/skills/linear-workflow/references/PROGRESS_UPDATE_TEMPLATE.md`
393
+
394
+ and appends an idempotent `linear-workflow` section to `AGENTS.md`.
395
+
396
+ With `--with-linear`, AGENTS also stores a managed config block:
397
+
398
+ - `<!-- devrouter-linear-workflow-config:start -->`
399
+ - `<!-- devrouter-linear-workflow-config:end -->`
400
+
401
+ The block captures:
402
+
403
+ - `workspace.name`
404
+ - `team.name` (optional `team.key`)
405
+ - `project.name` (optional `project.id`)
406
+
407
+ Required Linear execution hygiene:
408
+
409
+ 1. Set issue status at session start and update it at each phase transition.
410
+ 2. Post progress comments at meaningful checkpoints during implementation.
411
+ 3. Before ending a session, post a final comment with completed work, remaining work, risks, and next step.
412
+ 4. Re-check status and comment freshness toward/at session end before stopping.
169
413
 
170
414
  ## Known limitations (v1)
171
415
 
172
416
  - Host-runtime dependencies are not auto-started; only Docker dependencies are auto-started.
173
- - TCP routing currently supports PostgreSQL only (`tcpProtocol: postgres`).
174
- - Shared `:5432` hostname multiplexing requires TLS/SNI (`sslmode=require` or stronger).
417
+ - `kind=dependency` apps are not direct run/exec/open targets (must be started via a routed app dependency graph).
418
+ - TCP routing supports `tcpProtocol: postgres`, `redis`, `mariadb`, and `mysql`.
419
+ - Shared TCP hostname multiplexing requires TLS/SNI (`sslmode=require` or protocol-equivalent client SNI).
175
420
 
176
421
  ## Router state
177
422
 
@@ -190,5 +435,6 @@ Global managed artifacts remain under:
190
435
  - Setup and bootstrapping: [`docs/GETTING_STARTED.md`](./docs/GETTING_STARTED.md)
191
436
  - Onboarding repositories and AI prompt: [`docs/REPO_ONBOARDING.md`](./docs/REPO_ONBOARDING.md)
192
437
  - Agent contributor guide: [`AGENTS.md`](./AGENTS.md)
193
- - Demo workspace: [`./demo/README.md`](./demo/README.md)
438
+ - Routing example: [`./examples/routing/README.md`](./examples/routing/README.md)
194
439
  - Roadmap: [`docs/PLAN.md`](./docs/PLAN.md)
440
+ - Release and adaptation history: [`CHANGELOG.md`](./CHANGELOG.md) and [`upgrade-prompts/`](./upgrade-prompts/)