@devrouter/cli 0.0.1 → 0.0.22

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,124 @@ 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 going forward. A **devcontainer** (DevPod, VS Code Dev
33
+ Containers, `@devcontainers/cli`, Codespaces) owns the *environment* — toolchain,
34
+ databases, auth mocks, the app process, seeding — and publishes the app on a local
35
+ port. devrouter is a thin **routing layer**: it puts a stable `*.localhost` HTTPS
36
+ host (shared `:443`, mkcert TLS) in front of that port and does nothing else.
37
+
38
+ ```yaml
39
+ apps:
40
+ - name: app
41
+ host: myapp.localhost
42
+ protocol: http
43
+ runtime: proxy
44
+ upstream: 127.0.0.1:3000 # the port your devcontainer publishes
45
+ ```
46
+
47
+ ```bash
48
+ dev up && dev tls install # one-time
49
+ dev app run app # registers the route; the container owns start/stop
50
+ ```
51
+
52
+ Why prefer it: the environment is reproducible and runs anywhere the devcontainer
53
+ spec runs (including remote/cloud via DevPod), devrouter never duplicates the
54
+ DB/lifecycle/env work, and the two layers can't fight. See
55
+ [`docs/DEVCONTAINER.md`](./docs/DEVCONTAINER.md) for the end-to-end walkthrough.
56
+
57
+ ### 2. devrouter runs everything — `runtime: host` / `runtime: docker`
58
+
59
+ The original mode: devrouter starts your app (`runtime: host`, via `hostRun`) and
60
+ manages its Docker datastores/dependencies (`runtime: docker`), injecting DB env
61
+ vars. Use it when you are not (yet) on a devcontainer. Fully supported.
62
+
26
63
  ## Core commands
27
64
 
28
- - `dev init [--repo <path>] [--entries-json <json>] [--json]`
65
+ - `dev init [--repo <path>] [--entries-json <json>] [--json] [--write-agents] [--write-skill] [--with-linear]`
66
+ - `dev -V [--repo <path>]` (installed CLI version, local repo version, next upgrade target)
67
+ - `dev upgrade [version] [--repo <path>]`
29
68
  - `dev up`
30
69
  - `dev down`
31
70
  - `dev status [--repo <path>] [--json]`
32
71
  - `dev doctor|verify [--repo <path>] [--json]`
33
72
  - `dev ls [--json]`
34
- - `dev open <name>`
73
+ - `dev open <name>` (matches app name, then service/container/host)
35
74
  - `dev tls install`
36
75
  - `dev repo init [--repo <path>]`
37
- - `dev app add ...`
76
+ - `dev repo agents [--repo <path>] [--with-linear]`
77
+ - `dev app add ...` (`--kind app|dependency`, default `app`)
38
78
  - `dev app ls [--repo <path>] [--json]`
39
- - `dev app run <name> [--repo <path>] [--yes]`
79
+ - `dev app run <name> [--repo <path>] [--yes] [--workspace <slug>]`
80
+ - `dev app exec <name> [--repo <path>] [--yes] [--shell] [--env-map TARGET=SOURCE] [--workspace <slug>] -- <command>`
40
81
  - `dev app rm <name> [--repo <path>]`
82
+ - `dev logs [-f]`
83
+ - `dev workspace up <branch> [--path <dir>] [--no-devpod] [--open] [--repo <path>]`
84
+ - `dev workspace ls [--repo <path>] [--json]`
85
+ - `dev workspace down <workspace|branch> [--keep-worktree] [--keep-devpod] [--repo <path>]`
86
+
87
+ ## Workspace isolation (parallel worktrees)
88
+
89
+ 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.
90
+
91
+ **Token resolution precedence** (highest to lowest):
92
+
93
+ 1. `--workspace <slug>` CLI flag
94
+ 2. `DEVROUTER_WORKSPACE` environment variable
95
+ 3. Auto-derived from the linked git worktree's branch name (sanitized: lowercase, non-alphanumeric → `-`, capped at 32 chars)
96
+ 4. None — the primary checkout uses no token and routes exactly as a plain repo (back-compatible)
97
+
98
+ **When a workspace token is active:**
99
+
100
+ - Hosts are auto-namespaced: `web.localhost` → `web.<ws>.localhost`
101
+ - `${WORKSPACE}` in a proxy app's `upstream` (e.g. `upstream: ${WORKSPACE}-app:3000`) is substituted with the token at runtime
102
+ - The committed `.devrouter.yml` is never rewritten — all namespacing is computed in memory
103
+ - TLS: namespaced hosts are not covered by `*.localhost`; devrouter auto-extends mkcert cert SANs for active workspace hosts
104
+
105
+ `${WORKSPACE}` is valid in `upstream` only. Using it in `host` is rejected (hosts are namespaced automatically).
106
+
107
+ **Typical workflow:**
108
+
109
+ ```bash
110
+ # Bring up a feature branch as an isolated workspace
111
+ dev workspace up feat/my-feature
112
+
113
+ # List git worktrees with workspace tokens and route counts
114
+ dev workspace ls
115
+
116
+ # Tear down a workspace (stop devpod, remove worktree, free routes)
117
+ dev workspace down feat/my-feature
118
+ ```
119
+
120
+ **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`.
121
+
122
+ **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.
123
+
124
+ **GC:** `dev doctor` check `routes.orphaned-workspace-routes` reclaims proxy routes whose worktree directory was removed without `dev workspace down`. Only orphaned workspace routes are reclaimed; primary-checkout routes are never touched.
125
+
126
+ ## Upgrade metadata and prompts
127
+
128
+ `dev upgrade` and `dev -V` read local upgrade metadata from `.devrouter.yml` in the target repo (`devrouter.version`):
129
+
130
+ ```yaml
131
+ version: 1
132
+ devrouter:
133
+ version: <semver>
134
+ apps: []
135
+ ```
136
+
137
+ Quick checks:
138
+
139
+ - `dev -V` shows installed CLI version, local repo version, and next available upgrade target.
140
+ - `dev upgrade` lists all upgrade targets newer than the local repo version and marks the next one.
141
+ - `dev upgrade <version>` prints that target release's Agent Adaptation Prompt and then shows if a further version is available.
142
+ - Upgrade prompts are sourced from `upgrade-prompts/<version>.md`.
143
+ - `dev repo init` initializes `devrouter.version` to the installed CLI version.
41
144
 
42
145
  ## AI-native onboarding prompt
43
146
 
@@ -47,6 +150,8 @@ Generate a ready-to-copy onboarding prompt for an AI agent:
47
150
  dev init --repo /absolute/path/to/repo
48
151
  ```
49
152
 
153
+ By default, this command is non-mutating (it prints prompt text only).
154
+
50
155
  Optional: embed target app entries as JSON:
51
156
 
52
157
  ```bash
@@ -59,6 +164,20 @@ JSON mode for machine consumption:
59
164
  dev init --repo /absolute/path/to/repo --json
60
165
  ```
61
166
 
167
+ Optional repo artifact writes are explicit:
168
+
169
+ ```bash
170
+ dev init --repo /absolute/path/to/repo --write-agents --write-skill
171
+ ```
172
+
173
+ Optional: also bootstrap Linear workflow skill/templates and AGENTS section:
174
+
175
+ ```bash
176
+ dev init --repo /absolute/path/to/repo --with-linear --write-agents --write-skill
177
+ ```
178
+
179
+ 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.
180
+
62
181
  ## Health diagnostics
63
182
 
64
183
  Run deep checks for global router state and repo configuration:
@@ -74,11 +193,16 @@ dev doctor --repo /absolute/path/to/repo --json
74
193
  ```
75
194
 
76
195
  `dev status` now includes readiness hints and next-step commands.
196
+ 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.
197
+ 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.
198
+ `dev doctor` also reclaims orphaned workspace proxy routes (`routes.orphaned-workspace-routes`) whose worktree directory was removed without `dev workspace down`.
77
199
 
78
200
  ## `.devrouter.yml` example
79
201
 
80
202
  ```yaml
81
203
  version: 1
204
+ devrouter:
205
+ version: 0.0.14
82
206
  project:
83
207
  name: my-repo
84
208
  apps:
@@ -95,6 +219,7 @@ apps:
95
219
  allowPortRange: "1024-65535"
96
220
  dependencies:
97
221
  - app: db
222
+ - app: redis
98
223
 
99
224
  - name: db
100
225
  host: db.localhost
@@ -106,13 +231,34 @@ apps:
106
231
  internalPort: 5432
107
232
  composeFiles:
108
233
  - docker-compose.yml
234
+
235
+ - name: redis
236
+ kind: dependency
237
+ runtime: docker
238
+ docker:
239
+ service: redis
240
+ composeFiles:
241
+ - docker-compose.yml
242
+
243
+ # Route to an already-running port (e.g. a devcontainer's published app).
244
+ # No lifecycle, env injection, or dependencies — devrouter only registers the route.
245
+ # Use ${WORKSPACE} in upstream for parallel-worktree isolation (see "Workspace isolation").
246
+ - name: app
247
+ host: app.localhost
248
+ protocol: http
249
+ runtime: proxy
250
+ upstream: 127.0.0.1:3000
251
+ # upstream: ${WORKSPACE}-app:3000 # workspace-aware variant
109
252
  ```
110
253
 
111
254
  Notes:
112
255
 
256
+ - `kind` defaults to routed app behavior. Use `kind: dependency` for non-routed Docker dependencies.
257
+ - `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`.
113
258
  - TCP mode currently supports PostgreSQL first (`tcpProtocol: postgres`).
114
259
  - Multi-DB hostname routing on shared `:5432` requires TLS/SNI.
115
260
  - Plaintext Postgres is not supported for multiplexed hostname routing.
261
+ - Multi-segment `.localhost` hosts are supported (for example `elearning.klicker.localhost`).
116
262
 
117
263
  ## Runtime behavior
118
264
 
@@ -120,11 +266,36 @@ Notes:
120
266
 
121
267
  - reads `.devrouter.yml`
122
268
  - prompts to start declared dependencies (or use `--yes`)
123
- - starts only declared docker dependency services
269
+ - starts docker target services for `runtime: docker` apps, plus declared docker dependencies
270
+ - 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
271
  - fails fast if host-runtime dependencies are configured (start those manually)
272
+ - waits for Docker dependencies to become healthy (`--wait`) before proceeding
273
+ - automatically stops Docker dependencies when a host app exits; docker app services remain running until explicit cleanup (`docker compose down`, `dev down`, or equivalent)
274
+ - prints recent dependency logs (last 20 lines) after deps start
275
+ - `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`
276
+ - `kind=dependency` services start as declared in compose (no Traefik labels, no random published ports, no injected env vars)
277
+ - for TCP deps of host apps: publishes a random host port and injects `<NAME>_HOST`/`<NAME>_PORT` env vars into the host process; for postgres deps also injects `DATABASE_URL` and `SHADOW_DATABASE_URL` (fixed credentials `prisma:prisma`, databases `prisma`/`shadow`)
278
+ - 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)
279
+ - if `dev app exec` cannot determine pre-existing running services, it leaves selected deps running to avoid stopping non-owned services
280
+ - 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)
281
+ - for one-shot commands, `dev app exec` preserves argv semantics by default (`shell: false`) to avoid nested quoting issues
282
+ - `dev app exec --shell` is explicit and requires one command string after `--`
283
+ - `dev app exec --env-map TARGET=SOURCE` (repeatable) maps aliases after dependency env resolution (for example `DATABASE_URI=DATABASE_URL`)
125
284
  - starts host app command for host runtime apps
126
285
  - generates docker overlay in `~/.config/devrouter/cache/...` for docker runtime apps
127
286
 
287
+ Secret manager interop (Infisical/Doppler):
288
+
289
+ - dependency env injection from devrouter includes `<NAME>_HOST`, `<NAME>_PORT`, `DATABASE_URL`, and `SHADOW_DATABASE_URL`
290
+ - do not assume secret-manager precedence when DB vars overlap; validate effective env before migrate/seed
291
+ - avoid pre-wrapper DB assignments such as `DATABASE_URI=... <wrapper> run -- ...`; wrapper-managed env may override those values
292
+ - safe host-run override pattern when wrapper also defines `DATABASE_URI`: `infisical run --projectId <id> --env=<env> -- env DATABASE_URI=${DATABASE_URL:?missing DATABASE_URL} pnpm dev`
293
+ - non-Prisma mapping example: `dev app exec web --yes --env-map DATABASE_URI=DATABASE_URL -- infisical run --projectId <id> --env=<env> -- pnpm payload migrate`
294
+ - env probe example: `dev app exec web --yes --env-map DATABASE_URI=DATABASE_URL -- printenv DATABASE_URL DATABASE_URI DB_HOST DB_PORT SHADOW_DATABASE_URL`
295
+ - run `dev doctor --repo <path>` to surface risky wrapper precedence (`repo.host-command-env-precedence`) before migrations or app startup
296
+
297
+ `dev ls` output includes both configured app identity (`APP`) and runtime service identity (`SERVICE`).
298
+
128
299
  ## First onboarding quick path
129
300
 
130
301
  In a repo that has a host app and a Docker Postgres service:
@@ -133,7 +304,8 @@ In a repo that has a host app and a Docker Postgres service:
133
304
  dev repo init
134
305
  dev app add --name web --host web.localhost --protocol http --runtime host --command "pnpm dev" --cwd .
135
306
  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
307
+ dev app add --name redis --kind dependency --service redis --compose-file docker-compose.yml
308
+ dev app add --name web --host web.localhost --protocol http --runtime host --command "pnpm dev" --cwd . --depends-on db --depends-on redis
137
309
  dev tls install
138
310
  dev app run web --yes
139
311
  dev ls
@@ -167,9 +339,47 @@ See details:
167
339
 
168
340
  - [`./demo/README.md`](./demo/README.md)
169
341
 
342
+ ## AI agent discoverability
343
+
344
+ `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.
345
+
346
+ If you also want Linear workflow assets and repository mapping metadata, run:
347
+
348
+ ```bash
349
+ dev repo agents --with-linear
350
+ ```
351
+
352
+ This additionally installs:
353
+
354
+ - `.agents/skills/linear-workflow/SKILL.md`
355
+ - `.agents/skills/linear-workflow/references/LINEAR_ISSUE_TEMPLATE.md`
356
+ - `.agents/skills/linear-workflow/references/MILESTONE_PLAN_TEMPLATE.md`
357
+ - `.agents/skills/linear-workflow/references/PROGRESS_UPDATE_TEMPLATE.md`
358
+
359
+ and appends an idempotent `linear-workflow` section to `AGENTS.md`.
360
+
361
+ With `--with-linear`, AGENTS also stores a managed config block:
362
+
363
+ - `<!-- devrouter-linear-workflow-config:start -->`
364
+ - `<!-- devrouter-linear-workflow-config:end -->`
365
+
366
+ The block captures:
367
+
368
+ - `workspace.name`
369
+ - `team.name` (optional `team.key`)
370
+ - `project.name` (optional `project.id`)
371
+
372
+ Required Linear execution hygiene:
373
+
374
+ 1. Set issue status at session start and update it at each phase transition.
375
+ 2. Post progress comments at meaningful checkpoints during implementation.
376
+ 3. Before ending a session, post a final comment with completed work, remaining work, risks, and next step.
377
+ 4. Re-check status and comment freshness toward/at session end before stopping.
378
+
170
379
  ## Known limitations (v1)
171
380
 
172
381
  - Host-runtime dependencies are not auto-started; only Docker dependencies are auto-started.
382
+ - `kind=dependency` apps are not direct run/exec/open targets (must be started via a routed app dependency graph).
173
383
  - TCP routing currently supports PostgreSQL only (`tcpProtocol: postgres`).
174
384
  - Shared `:5432` hostname multiplexing requires TLS/SNI (`sslmode=require` or stronger).
175
385
 
@@ -192,3 +402,4 @@ Global managed artifacts remain under:
192
402
  - Agent contributor guide: [`AGENTS.md`](./AGENTS.md)
193
403
  - Demo workspace: [`./demo/README.md`](./demo/README.md)
194
404
  - Roadmap: [`docs/PLAN.md`](./docs/PLAN.md)
405
+ - Release and adaptation history: [`CHANGELOG.md`](./CHANGELOG.md) and [`upgrade-prompts/`](./upgrade-prompts/)