@devrouter/cli 0.0.23 → 0.0.24

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.
@@ -158,32 +158,32 @@ This folder is managed by the devrouter CLI.
158
158
 
159
159
  ## Commands
160
160
 
161
- - dev init [--write-agents] [--write-skill] [--with-linear]
162
- - dev -V [--repo <path>] (installed/local version + next upgrade)
163
- - dev upgrade [version] [--repo <path>]
164
- - dev setup --yes [--repo <path>] [--json]
165
- - dev up
166
- - dev down
167
- - dev status
168
- - dev doctor
169
- - dev ls
170
- - dev open <name>
171
- - dev logs [-f] [--tail N]
172
- - dev repo init
173
- - dev repo inspect [--repo <path>] [--json]
174
- - dev repo devcontainer write [--repo <path>] [--dry-run] [--yes] [--json]
175
- - dev repo devcontainer verify [--repo <path>] [--live] [--yes] [--json]
176
- - dev repo agents [--with-linear]
177
- - dev app add --name <name> --host <host.localhost> --protocol <http|tcp> --runtime <host|docker>
178
- - dev app run <name>
179
- - dev app exec <name> [--shell] [--env <env>] -- <command>
180
- - dev app ls
181
- - dev app rm <name>
182
- - dev tls install
161
+ - devrouter init [--write-agents] [--write-skill] [--with-linear]
162
+ - devrouter -V [--repo <path>] (installed/local version + next upgrade)
163
+ - devrouter upgrade [version] [--repo <path>]
164
+ - devrouter setup --yes [--repo <path>] [--json]
165
+ - devrouter up
166
+ - devrouter down
167
+ - devrouter status
168
+ - devrouter doctor
169
+ - devrouter ls
170
+ - devrouter open <name>
171
+ - devrouter logs [-f] [--tail N]
172
+ - devrouter repo init
173
+ - devrouter repo inspect [--repo <path>] [--json]
174
+ - devrouter repo devcontainer write [--repo <path>] [--dry-run] [--yes] [--json]
175
+ - devrouter repo devcontainer verify [--repo <path>] [--live] [--yes] [--json]
176
+ - devrouter repo agents [--with-linear]
177
+ - devrouter app add --name <name> --host <host.localhost> --protocol <http|tcp> --runtime <host|docker>
178
+ - devrouter app run <name>
179
+ - devrouter app exec <name> [--shell] [--env <env>] -- <command>
180
+ - devrouter app ls
181
+ - devrouter app rm <name>
182
+ - devrouter tls install
183
183
 
184
184
  ## Troubleshooting
185
185
 
186
- If dev up fails with port conflicts on 80/443 (or TCP protocol ports), run:
186
+ If devrouter up fails with port conflicts on 80/443 (or TCP protocol ports), run:
187
187
 
188
188
  - lsof -nP -iTCP:80 -sTCP:LISTEN
189
189
  - lsof -nP -iTCP:443 -sTCP:LISTEN
@@ -552,7 +552,14 @@ function listHostRouteState() {
552
552
  return [];
553
553
  }
554
554
  return parsed.filter((item) => item && typeof item === "object").map((item) => item);
555
- } catch {
555
+ } catch (err) {
556
+ const error = err;
557
+ if (error.code !== "ENOENT") {
558
+ process.stderr.write(
559
+ `Warning: devrouter host routes state file is corrupted or unreadable (${error.message}). Recreating route state.
560
+ `
561
+ );
562
+ }
556
563
  return [];
557
564
  }
558
565
  }
@@ -1536,7 +1543,7 @@ function buildOnboardingPrompt(options = {}) {
1536
1543
  "",
1537
1544
  "Top-level .devrouter.yml schema:",
1538
1545
  "- version: 1 (required)",
1539
- "- devrouter.version: semantic version string (recommended; required for `dev -V`/`dev upgrade`)",
1546
+ "- devrouter.version: semantic version string (recommended; required for `devrouter -V`/`devrouter upgrade`)",
1540
1547
  "- project.name: string (optional)",
1541
1548
  `- secretManager.command: string (optional; SM command including trailing \`--\` boundary; supports \`${SECRET_MANAGER_ENV_PLACEHOLDER}\` template placeholder)`,
1542
1549
  `- secretManager.defaultEnv: string (optional; fallback env for \`${SECRET_MANAGER_ENV_PLACEHOLDER}\` template; required when command contains \`${SECRET_MANAGER_ENV_PLACEHOLDER}\`)`,
@@ -1603,20 +1610,20 @@ function buildOnboardingPrompt(options = {}) {
1603
1610
  '- Example healthcheck for postgres: `test: ["CMD-SHELL", "pg_isready -U <user> -d <db>"]` with `interval: 5s`, `timeout: 3s`, `retries: 20`.',
1604
1611
  "",
1605
1612
  "Runtime behavior to account for:",
1606
- "- Docker dependencies can be auto-started by dev app run.",
1607
- "- dev app run waits for Docker dependencies to become healthy before starting the host or docker app.",
1608
- "- Docker dependencies are automatically stopped when a host app exits (Ctrl+C or error); docker app services remain running until explicit cleanup (`docker compose down`, `dev down`, or equivalent).",
1613
+ "- Docker dependencies can be auto-started by devrouter app run.",
1614
+ "- devrouter app run waits for Docker dependencies to become healthy before starting the host or docker app.",
1615
+ "- Docker dependencies are automatically stopped when a host app exits (Ctrl+C or error); docker app services remain running until explicit cleanup (`docker compose down`, `devrouter down`, or equivalent).",
1609
1616
  "- Recent dependency logs (last 20 lines) are printed after dependencies start.",
1610
1617
  "- Host-runtime dependencies are NOT auto-started in v1 (must be started manually).",
1611
- "- kind=dependency entries are dependency-only: they do not create routes and cannot be direct targets for `dev app run`, `dev app exec`, or `dev open`.",
1618
+ "- kind=dependency entries are dependency-only: they do not create routes and cannot be direct targets for `devrouter app run`, `devrouter app exec`, or `devrouter open`.",
1612
1619
  "- kind=dependency services are started/stopped as declared in compose (no Traefik labels added, no env/port injection).",
1613
1620
  `- For TCP dependencies of host apps, devrouter publishes a random host port and injects per-dep deterministic vars: \`{PREFIX}_HOST=localhost\`, \`{PREFIX}_PORT=<port>\`, \`{PREFIX}_URL\` (protocol-specific), \`{PREFIX}_SHADOW_URL\` (postgres only). \`{PREFIX} = dep.name.toUpperCase().replace(/-/g, "_")\`.`,
1614
1621
  "- Config-level `envMap` on dependency references aliases per-dep vars to app-expected names. Example: `envMap: { DATABASE_URL: DB_URL }` maps per-dep `DB_URL` to `DATABASE_URL` in the app process.",
1615
1622
  "- If the repo's Postgres docker-compose service uses different credentials than the injected defaults (`prisma:prisma`), flag this to the user and recommend aligning the compose env vars or using `envMap` aliasing.",
1616
1623
  "- Postgres multiplexing on shared :5432 requires TLS/SNI (useful for psql 17+, pgAdmin, not standard app clients).",
1617
- "- When TLS is enabled, `dev app run` and `dev app exec` auto-refresh cert SAN coverage for configured repo hosts before startup.",
1624
+ "- When TLS is enabled, `devrouter app run` and `devrouter app exec` auto-refresh cert SAN coverage for configured repo hosts before startup.",
1618
1625
  "- For TCP/Postgres, standard app frameworks should use the injected port env vars; only direct-TLS-capable tools should use sslmode=require on :5432.",
1619
- "- `dev app exec <name> -- <command>` starts dependencies as needed, resolves env vars, and runs a one-shot command with the resolved env. Exec stops only dependencies started by that invocation (already-running dependencies stay running). If ownership detection fails, exec leaves selected dependencies running to avoid non-owned teardown. Exec preserves argv semantics by default (`shell: false`) and supports explicit shell mode via `--shell` when needed.",
1626
+ "- `devrouter app exec <name> -- <command>` starts dependencies as needed, resolves env vars, and runs a one-shot command with the resolved env. Exec stops only dependencies started by that invocation (already-running dependencies stay running). If ownership detection fails, exec leaves selected dependencies running to avoid non-owned teardown. Exec preserves argv semantics by default (`shell: false`) and supports explicit shell mode via `--shell` when needed.",
1620
1627
  "- `envMap` on dependency references (config-level) aliases per-dep vars after dependency env resolution. `envMap` fails fast when source var is missing.",
1621
1628
  "",
1622
1629
  "Workspace isolation (parallel git worktrees / agents):",
@@ -1624,11 +1631,11 @@ function buildOnboardingPrompt(options = {}) {
1624
1631
  "- Token resolution precedence: `--workspace <slug>` flag > `DEVROUTER_WORKSPACE` env var > auto-derived from a linked git worktree branch (sanitized: lowercase, non-alphanumeric \u2192 `-`, capped at 32 chars) > none. The primary checkout resolves to no token and routes exactly as before (fully back-compatible).",
1625
1632
  `- When a workspace is active: hosts auto-namespace (\`web.localhost\` \u2192 \`web.<ws>.localhost\`), \`${WORKSPACE_PLACEHOLDER}\` in \`upstream\` is substituted with the token, and the docker \`router\` key is suffixed per workspace. The runtime config is computed in memory only \u2014 the committed \`.devrouter.yml\` is never rewritten.`,
1626
1633
  "- TLS: namespaced hosts (`web.<ws>.localhost`) are not covered by the `*.localhost` wildcard; devrouter auto-extends the mkcert cert SANs for active hosts when TLS is enabled.",
1627
- "- Lifecycle: `dev workspace up <branch>` (create worktree + devpod + routes), `dev workspace ls` (list worktrees/tokens/route counts), `dev workspace down <workspace|branch>` (free routes + stop devpod + remove worktree). `dev doctor` reports orphaned workspace proxy routes whose worktree dir was removed without `dev workspace down`.",
1634
+ "- Lifecycle: `devrouter workspace up <branch>` (create worktree + devpod + routes), `devrouter workspace ls` (list worktrees/tokens/route counts), `devrouter workspace down <workspace|branch>` (free routes + stop devpod + remove worktree). `devrouter doctor` reports orphaned workspace proxy routes whose worktree dir was removed without `devrouter workspace down`.",
1628
1635
  `- devcontainer integration: the devcontainer compose service exposes a devnet alias \`${WORKSPACE_PLACEHOLDER}-app\` (default \`WORKSPACE=<project>\` in \`devcontainer.env\`), and the proxy app uses \`upstream: ${WORKSPACE_PLACEHOLDER}-app:<port>\`. Spinning up workspace \`feat-a\` \u2192 alias \`feat-a-app\`, host \`app.feat-a.localhost\`.`,
1629
1636
  "",
1630
1637
  "Secret Manager Integration (config-based):",
1631
- "- Optional top-level `secretManager.command` in `.devrouter.yml` wraps `dev app run` and `dev app exec` commands with the SM command and re-applies devrouter-injected dep env vars after the SM boundary via `env KEY=VAL` prefix.",
1638
+ "- Optional top-level `secretManager.command` in `.devrouter.yml` wraps `devrouter app run` and `devrouter app exec` commands with the SM command and re-applies devrouter-injected dep env vars after the SM boundary via `env KEY=VAL` prefix.",
1632
1639
  `- Example config: \`secretManager: { command: "infisical run --env ${SECRET_MANAGER_ENV_PLACEHOLDER} --", defaultEnv: "dev" }\`.`,
1633
1640
  `- \`${SECRET_MANAGER_ENV_PLACEHOLDER}\` template placeholder in \`secretManager.command\` is resolved at runtime. \`defaultEnv\` provides the fallback; \`--env\` CLI flag overrides it.`,
1634
1641
  "- When configured, the effective command becomes: `<secretManager.command> env {PREFIX}_URL=<val> ... <user-command>`.",
@@ -1641,40 +1648,40 @@ function buildOnboardingPrompt(options = {}) {
1641
1648
  "- Secret managers may also define DB variables. Do not assume secret-manager precedence. Confirm the effective values in the command process before migrate/seed.",
1642
1649
  "- For apps that require legacy names like `DATABASE_URL`, use config-level `envMap` on the dependency reference: `envMap: { DATABASE_URL: DB_URL }`.",
1643
1650
  "- Avoid pre-wrapper DB assignments such as `DATABASE_URI=... <wrapper> run -- ...`; wrapper-managed env may override those values.",
1644
- "- `dev doctor --repo <REPO_PATH>` warns on risky pre-wrapper DB assignments before `run --` for host apps that depend on postgres.",
1645
- "- With TLS enabled, `dev doctor --repo <REPO_PATH>` also warns on cert SAN mismatches for configured hosts (`repo.tls-host-coverage`).",
1646
- "- Robust one-shot migrate example (argv-safe, no nested shell quoting): `dev app exec <name> --repo <REPO_PATH> --yes -- infisical run --projectId <id> --env=<env> -- pnpm payload migrate`.",
1647
- "- Robust one-shot seed example: `dev app exec <name> --repo <REPO_PATH> --yes -- infisical run --projectId <id> --env=<env> -- pnpm payload seed`.",
1648
- "- Environment probe/debug example (run before migrations): `dev app exec <name> --repo <REPO_PATH> --yes -- printenv DB_URL DB_HOST DB_PORT DB_SHADOW_URL`.",
1651
+ "- `devrouter doctor --repo <REPO_PATH>` warns on risky pre-wrapper DB assignments before `run --` for host apps that depend on postgres.",
1652
+ "- With TLS enabled, `devrouter doctor --repo <REPO_PATH>` also warns on cert SAN mismatches for configured hosts (`repo.tls-host-coverage`).",
1653
+ "- Robust one-shot migrate example (argv-safe, no nested shell quoting): `devrouter app exec <name> --repo <REPO_PATH> --yes -- infisical run --projectId <id> --env=<env> -- pnpm payload migrate`.",
1654
+ "- Robust one-shot seed example: `devrouter app exec <name> --repo <REPO_PATH> --yes -- infisical run --projectId <id> --env=<env> -- pnpm payload seed`.",
1655
+ "- Environment probe/debug example (run before migrations): `devrouter app exec <name> --repo <REPO_PATH> --yes -- printenv DB_URL DB_HOST DB_PORT DB_SHADOW_URL`.",
1649
1656
  "- Use `--shell` only when shell expansion is required; it must receive exactly one command string after `--`.",
1650
1657
  "- Warning: Do not run migration/seed until env probe confirms expected DB variables and values.",
1651
1658
  "",
1652
1659
  "Required workflow:",
1653
- "1) Run `dev setup --yes --json` for devrouter-owned machine state; use `dev doctor --repo <REPO_PATH> --json` to diagnose missing prerequisites without mutation.",
1654
- "2) Run `dev repo inspect --repo <REPO_PATH> --json` before editing files.",
1655
- "3) For the supported Node/pnpm/Postgres devcontainer shape, run `dev repo devcontainer write --repo <REPO_PATH> --dry-run --json`, review the plan, then run `dev repo devcontainer write --repo <REPO_PATH> --yes`.",
1660
+ "1) Run `devrouter setup --yes --json` for devrouter-owned machine state; use `devrouter doctor --repo <REPO_PATH> --json` to diagnose missing prerequisites without mutation.",
1661
+ "2) Run `devrouter repo inspect --repo <REPO_PATH> --json` before editing files.",
1662
+ "3) For the supported Node/pnpm/Postgres devcontainer shape, run `devrouter repo devcontainer write --repo <REPO_PATH> --dry-run --json`, review the plan, then run `devrouter repo devcontainer write --repo <REPO_PATH> --yes`.",
1656
1663
  "4) For unsupported shapes or custom existing files, make minimal manual edits and explain the assumptions.",
1657
- "5) Verify static evidence with `dev repo devcontainer verify --repo <REPO_PATH> --json`; after the devcontainer is running, use `dev repo devcontainer verify --repo <REPO_PATH> --live --yes --json` for route probes.",
1664
+ "5) Verify static evidence with `devrouter repo devcontainer verify --repo <REPO_PATH> --json`; after the devcontainer is running, use `devrouter repo devcontainer verify --repo <REPO_PATH> --live --yes --json` for route probes.",
1658
1665
  "6) Keep edits minimal, explicit, and idempotent. Do not modify unrelated services.",
1659
1666
  "7) If required info is missing or ambiguous, stop and ask targeted questions.",
1660
1667
  "",
1661
1668
  "Validation commands to run/report for the devcontainer path:",
1662
- "- dev setup --yes --json",
1663
- "- dev doctor --repo <REPO_PATH> --json",
1664
- "- dev repo inspect --repo <REPO_PATH> --json",
1665
- "- dev repo devcontainer write --repo <REPO_PATH> --dry-run --json",
1666
- "- dev repo devcontainer write --repo <REPO_PATH> --yes",
1667
- "- dev repo devcontainer verify --repo <REPO_PATH> --json",
1668
- "- After `devpod up <REPO_PATH>`: dev repo devcontainer verify --repo <REPO_PATH> --live --yes --json",
1669
+ "- devrouter setup --yes --json",
1670
+ "- devrouter doctor --repo <REPO_PATH> --json",
1671
+ "- devrouter repo inspect --repo <REPO_PATH> --json",
1672
+ "- devrouter repo devcontainer write --repo <REPO_PATH> --dry-run --json",
1673
+ "- devrouter repo devcontainer write --repo <REPO_PATH> --yes",
1674
+ "- devrouter repo devcontainer verify --repo <REPO_PATH> --json",
1675
+ "- After `devpod up <REPO_PATH>`: devrouter repo devcontainer verify --repo <REPO_PATH> --live --yes --json",
1669
1676
  "",
1670
1677
  "Validation commands to run/report for host/docker runtime apps:",
1671
- "- dev setup --yes --json",
1672
- "- dev doctor --repo <REPO_PATH> --json",
1673
- "- dev app ls --repo <REPO_PATH>",
1674
- "- For each entry (when safe): dev app run <name> --repo <REPO_PATH> --yes",
1675
- "- Run one-shot commands with dep env: dev app exec <name> --repo <REPO_PATH> --yes -- <command>",
1676
- "- Probe effective env before migration/seed: dev app exec <name> --repo <REPO_PATH> --yes -- printenv DB_URL DB_HOST DB_PORT DB_SHADOW_URL",
1677
- "- dev ls",
1678
+ "- devrouter setup --yes --json",
1679
+ "- devrouter doctor --repo <REPO_PATH> --json",
1680
+ "- devrouter app ls --repo <REPO_PATH>",
1681
+ "- For each entry (when safe): devrouter app run <name> --repo <REPO_PATH> --yes",
1682
+ "- Run one-shot commands with dep env: devrouter app exec <name> --repo <REPO_PATH> --yes -- <command>",
1683
+ "- Probe effective env before migration/seed: devrouter app exec <name> --repo <REPO_PATH> --yes -- printenv DB_URL DB_HOST DB_PORT DB_SHADOW_URL",
1684
+ "- devrouter ls",
1678
1685
  "- For HTTP entries: curl -I http://<host>",
1679
1686
  '- For TCP postgres entries: provide connection hint (example: psql "... sslmode=require")',
1680
1687
  "",
@@ -1687,8 +1694,8 @@ function buildOnboardingPrompt(options = {}) {
1687
1694
  "6) Unresolved questions/risks (if any).",
1688
1695
  "7) Definition-of-done checklist status:",
1689
1696
  " - .devrouter.yml exists and validates",
1690
- " - dev app ls matches expected entries",
1691
- " - dev ls exposes expected endpoints",
1697
+ " - devrouter app ls matches expected entries",
1698
+ " - devrouter ls exposes expected endpoints",
1692
1699
  " - HTTP routes reachable",
1693
1700
  " - TCP Postgres route configured with TLS requirement noted",
1694
1701
  ...withLinear ? [
@@ -1705,9 +1712,9 @@ function buildOnboardingPrompt(options = {}) {
1705
1712
  "- While implementing Linear-tracked work, set issue status at session start and at each phase transition.",
1706
1713
  "- Post progress comments at meaningful checkpoints during implementation (not only at the end).",
1707
1714
  "- Before ending a session, post a final recap comment with completed work, remaining work, risks, and next step, then re-check status/comment freshness.",
1708
- "- Optional bootstrap commands for repo artifacts: `dev init --repo <REPO_PATH> --with-linear --write-agents --write-skill` or `dev repo agents --repo <REPO_PATH> --with-linear`.",
1709
- "- If the repository uses devrouter, keep `.devrouter.yml` metadata `devrouter.version` updated and run `dev -V` to verify installed/local versions plus the next target.",
1710
- "- Resolve adaptation prompts with `dev upgrade` (list targets) and `dev upgrade <version>` (target prompt), sourced from `upgrade-prompts/<version>.md` in the devrouter release."
1715
+ "- Optional bootstrap commands for repo artifacts: `devrouter init --repo <REPO_PATH> --with-linear --write-agents --write-skill` or `devrouter repo agents --repo <REPO_PATH> --with-linear`.",
1716
+ "- If the repository uses devrouter, keep `.devrouter.yml` metadata `devrouter.version` updated and run `devrouter -V` to verify installed/local versions plus the next target.",
1717
+ "- Resolve adaptation prompts with `devrouter upgrade` (list targets) and `devrouter upgrade <version>` (target prompt), sourced from `upgrade-prompts/<version>.md` in the devrouter release."
1711
1718
  ] : [],
1712
1719
  "",
1713
1720
  renderCommandIntentSection()
@@ -1721,93 +1728,93 @@ var init_ai_prompt = __esm({
1721
1728
  init_repo_config();
1722
1729
  COMMAND_INTENTS = [
1723
1730
  {
1724
- command: "dev init [--with-linear]",
1731
+ command: "devrouter init [--with-linear]",
1725
1732
  purpose: "Print the AI onboarding prompt template for a repository (non-mutating by default)."
1726
1733
  },
1727
1734
  {
1728
- command: "dev -V",
1735
+ command: "devrouter -V",
1729
1736
  purpose: "Show installed CLI version, local repo version, and next upgrade target."
1730
1737
  },
1731
1738
  {
1732
- command: "dev upgrade [version]",
1739
+ command: "devrouter upgrade [version]",
1733
1740
  purpose: "Show upgrade targets from .devrouter.yml devrouter.version and print target adaptation prompt."
1734
1741
  },
1735
1742
  {
1736
- command: "dev setup",
1743
+ command: "devrouter setup",
1737
1744
  purpose: "Run first-time devrouter machine setup and report structured diagnostics."
1738
1745
  },
1739
1746
  {
1740
- command: "dev up",
1747
+ command: "devrouter up",
1741
1748
  purpose: "Start shared Traefik and ensure the shared devnet network."
1742
1749
  },
1743
- { command: "dev down", purpose: "Stop the shared Traefik router stack." },
1750
+ { command: "devrouter down", purpose: "Stop the shared Traefik router stack." },
1744
1751
  {
1745
- command: "dev status",
1752
+ command: "devrouter status",
1746
1753
  purpose: "Show router/container/network/TLS health and bound ports."
1747
1754
  },
1748
1755
  {
1749
- command: "dev doctor",
1756
+ command: "devrouter doctor",
1750
1757
  purpose: "Run deep diagnostics across global router state and repo config."
1751
1758
  },
1752
1759
  {
1753
- command: "dev ls",
1760
+ command: "devrouter ls",
1754
1761
  purpose: "List active HTTP and TCP routes resolved by devrouter."
1755
1762
  },
1756
1763
  {
1757
- command: "dev open <name>",
1764
+ command: "devrouter open <name>",
1758
1765
  purpose: "Open HTTP routes or print connection hints for TCP routes (match app/service/host)."
1759
1766
  },
1760
1767
  {
1761
- command: "dev tls install",
1768
+ command: "devrouter tls install",
1762
1769
  purpose: "Install mkcert certs and enable TLS/HTTPS for local routing."
1763
1770
  },
1764
1771
  {
1765
- command: "dev repo init",
1772
+ command: "devrouter repo init",
1766
1773
  purpose: "Create `.devrouter.yml` in a target repository."
1767
1774
  },
1768
1775
  {
1769
- command: "dev repo inspect",
1776
+ command: "devrouter repo inspect",
1770
1777
  purpose: "Inspect package, scripts, compose services, env names, devcontainer, devrouter config, and agent guidance for onboarding."
1771
1778
  },
1772
1779
  {
1773
- command: "dev repo devcontainer write",
1780
+ command: "devrouter repo devcontainer write",
1774
1781
  purpose: "Dry-run or write conservative managed Node/pnpm/Postgres devcontainer/devrouter scaffold files."
1775
1782
  },
1776
1783
  {
1777
- command: "dev repo devcontainer verify",
1784
+ command: "devrouter repo devcontainer verify",
1778
1785
  purpose: "Emit static onboarding evidence, or live route probes with --live --yes."
1779
1786
  },
1780
1787
  {
1781
- command: "dev app add",
1788
+ command: "devrouter app add",
1782
1789
  purpose: "Add or update one app entry in `.devrouter.yml`."
1783
1790
  },
1784
- { command: "dev app ls", purpose: "List app entries from `.devrouter.yml`." },
1791
+ { command: "devrouter app ls", purpose: "List app entries from `.devrouter.yml`." },
1785
1792
  {
1786
- command: "dev app run [--env <env>]",
1793
+ command: "devrouter app run [--env <env>]",
1787
1794
  purpose: "Run one configured app and reconcile its route at runtime (--env overrides SM defaultEnv)."
1788
1795
  },
1789
1796
  {
1790
- command: "dev app exec [--shell] [--env <env>]",
1797
+ command: "devrouter app exec [--shell] [--env <env>]",
1791
1798
  purpose: "Run a one-shot command with resolved dependency env vars (env aliasing via config-level envMap)."
1792
1799
  },
1793
1800
  {
1794
- command: "dev app rm [--keep-config]",
1801
+ command: "devrouter app rm [--keep-config]",
1795
1802
  purpose: "Remove one app entry from `.devrouter.yml` and free its route. `--keep-config` frees only the live route/hostname (e.g. to release one claimed by another repo) and leaves the config file untouched."
1796
1803
  },
1797
1804
  {
1798
- command: "dev repo agents [--with-linear]",
1805
+ command: "devrouter repo agents [--with-linear]",
1799
1806
  purpose: "Write/update devrouter section in the repo's AGENTS.md and optionally add Linear workflow assets."
1800
1807
  },
1801
1808
  {
1802
- command: "dev workspace up <branch> [--path <dir>] [--no-devpod] [--open]",
1809
+ command: "devrouter workspace up <branch> [--path <dir>] [--no-devpod] [--open]",
1803
1810
  purpose: "Create a git worktree for <branch>, bring up its devpod (`devpod up --id <ws>`), and register workspace-namespaced routes."
1804
1811
  },
1805
1812
  {
1806
- command: "dev workspace ls [--json]",
1813
+ command: "devrouter workspace ls [--json]",
1807
1814
  purpose: "List git worktrees with their resolved workspace token and active route count."
1808
1815
  },
1809
1816
  {
1810
- command: "dev workspace down <workspace|branch> [--keep-worktree] [--keep-devpod]",
1817
+ command: "devrouter workspace down <workspace|branch> [--keep-worktree] [--keep-devpod]",
1811
1818
  purpose: "Free a workspace's routes, stop its devpod, and remove its worktree (routes are freed by state-file workspace tag, no config load)."
1812
1819
  }
1813
1820
  ];
@@ -1827,11 +1834,11 @@ function buildDevrouterSection() {
1827
1834
  `\`${DEVROUTER_SKILL_REL_PATH}\``,
1828
1835
  "",
1829
1836
  "Quick validation sequence:",
1830
- "- `dev up`",
1831
- "- `dev tls install` (required when repo defines tcp/postgres apps)",
1832
- "- `dev app ls --repo .`",
1833
- "- `dev app run <host-app> --repo . --yes`",
1834
- "- `dev ls`"
1837
+ "- `devrouter up`",
1838
+ "- `devrouter tls install` (required when repo defines tcp/postgres apps)",
1839
+ "- `devrouter app ls --repo .`",
1840
+ "- `devrouter app run <host-app> --repo . --yes`",
1841
+ "- `devrouter ls`"
1835
1842
  ].join("\n");
1836
1843
  }
1837
1844
  function buildLinearWorkflowSection() {
@@ -1859,8 +1866,8 @@ function buildLinearWorkflowSection() {
1859
1866
  "- Re-check status and comment freshness toward/at session end before stopping.",
1860
1867
  "",
1861
1868
  "Bootstrap commands:",
1862
- "- `dev init --with-linear --write-agents --write-skill`",
1863
- "- `dev repo agents --with-linear`"
1869
+ "- `devrouter init --with-linear --write-agents --write-skill`",
1870
+ "- `devrouter repo agents --with-linear`"
1864
1871
  ].join("\n");
1865
1872
  }
1866
1873
  function yamlQuote(value) {
@@ -2034,7 +2041,7 @@ Local dev routing via a shared Traefik reverse proxy. Provides stable \`*.localh
2034
2041
  \`\`\`yaml
2035
2042
  version: 1
2036
2043
  devrouter:
2037
- version: <semver> # required for dev -V / dev upgrade
2044
+ version: <semver> # required for devrouter -V / devrouter upgrade
2038
2045
  project:
2039
2046
  name: <string> # optional
2040
2047
  apps:
@@ -2067,7 +2074,7 @@ apps:
2067
2074
  # protocol: tcp
2068
2075
  # tcpProtocol: postgres # selects shared entrypoint :5432
2069
2076
  # upstream: <db-alias>:5432 # devnet alias of the DB container
2070
- # Requires \`dev tls install\` (SNI is read from the TLS ClientHello). Connect
2077
+ # Requires \`devrouter tls install\` (SNI is read from the TLS ClientHello). Connect
2071
2078
  # with direct-SSL so the ClientHello carries SNI, e.g.:
2072
2079
  # psql "host=db.<app>.localhost port=5432 sslmode=require sslnegotiation=direct ..."
2073
2080
 
@@ -2102,7 +2109,7 @@ Validation rules:
2102
2109
 
2103
2110
  - \`kind=app\`: \`host\` must end with \`.localhost\`
2104
2111
  - \`kind=app\`: \`runtime=host\` supports \`protocol=http\` only
2105
- - \`kind=app\`: \`runtime=proxy\` supports \`protocol=http\` or \`protocol=tcp\`, requires \`upstream\` (\`host:port\`), and forbids \`hostRun\`/\`docker\`/\`dependencies\` (it only registers a route to an externally-managed upstream). \`protocol=tcp\` additionally requires \`tcpProtocol\` and TLS (\`dev tls install\`)
2112
+ - \`kind=app\`: \`runtime=proxy\` supports \`protocol=http\` or \`protocol=tcp\`, requires \`upstream\` (\`host:port\`), and forbids \`hostRun\`/\`docker\`/\`dependencies\` (it only registers a route to an externally-managed upstream). \`protocol=tcp\` additionally requires \`tcpProtocol\` and TLS (\`devrouter tls install\`)
2106
2113
  - \`kind=app\`: \`protocol=tcp\` requires \`runtime=docker\` (devrouter-managed container) or \`runtime=proxy\` (externally-managed upstream), plus a supported \`tcpProtocol\` (postgres/redis/mariadb/mysql)
2107
2114
  - \`kind=dependency\`: must use \`runtime=docker\` and does not allow routed fields (\`host\`/\`protocol\`/\`tcpProtocol\`/\`hostRun\`/\`docker.internalPort\`/\`docker.router\`)
2108
2115
  - Unknown keys rejected (strict schema)
@@ -2126,7 +2133,7 @@ healthcheck:
2126
2133
 
2127
2134
  ## Env var injection
2128
2135
 
2129
- When a host app depends on a TCP Docker service, \`dev app run\` and \`dev app exec\` inject per-dep deterministic vars (where \`{PREFIX} = dep.name.toUpperCase().replace(/-/g, "_")\`):
2136
+ When a host app depends on a TCP Docker service, \`devrouter app run\` and \`devrouter app exec\` inject per-dep deterministic vars (where \`{PREFIX} = dep.name.toUpperCase().replace(/-/g, "_")\`):
2130
2137
 
2131
2138
  | Variable | Value |
2132
2139
  | ----------------------- | ----------------------------------------------------------- |
@@ -2147,7 +2154,7 @@ Run several worktrees of one repo in parallel without host/route collisions. A *
2147
2154
  - **When active**: hosts auto-namespace (\`web.localhost\` \u2192 \`web.<ws>.localhost\`), \`\${WORKSPACE}\` in \`upstream\` is substituted with the token, and the docker \`router\` key is suffixed per workspace. The runtime config is computed in memory only \u2014 the committed \`.devrouter.yml\` is never rewritten.
2148
2155
  - **TLS**: namespaced hosts (\`web.<ws>.localhost\`) are not covered by the \`*.localhost\` wildcard; devrouter auto-extends the mkcert cert SANs for active hosts when TLS is enabled.
2149
2156
  - **devcontainer integration**: the devcontainer compose service exposes a devnet alias \`\${WORKSPACE}-app\` (default \`WORKSPACE=<project>\` in \`devcontainer.env\`); the proxy app uses \`upstream: \${WORKSPACE}-app:<port>\`. Workspace \`feat-a\` \u2192 alias \`feat-a-app\`, host \`app.feat-a.localhost\`.
2150
- - **Lifecycle**: \`dev workspace up <branch>\` (create worktree + devpod + routes), \`dev workspace ls\` (list worktrees/tokens/route counts), \`dev workspace down <workspace|branch>\` (free routes by state-file workspace tag + stop devpod + remove worktree). \`dev doctor\` reports orphaned workspace proxy routes whose worktree dir was removed without \`dev workspace down\`.
2157
+ - **Lifecycle**: \`devrouter workspace up <branch>\` (create worktree + devpod + routes), \`devrouter workspace ls\` (list worktrees/tokens/route counts), \`devrouter workspace down <workspace|branch>\` (free routes by state-file workspace tag + stop devpod + remove worktree). \`devrouter doctor\` reports orphaned workspace proxy routes whose worktree dir was removed without \`devrouter workspace down\`.
2151
2158
 
2152
2159
  ## Secret manager interop (Infisical/Doppler)
2153
2160
 
@@ -2171,31 +2178,31 @@ Run several worktrees of one repo in parallel without host/route collisions. A *
2171
2178
  \`\`\`
2172
2179
  - Prefer argv-safe command forms. Do not wrap \`infisical run\` or \`doppler run\` in \`sh -lc\` unless shell expansion is strictly required.
2173
2180
  - Canonical Infisical migrate command:
2174
- \`dev app exec <app> --yes -- infisical run --projectId <id> --env=<env> -- pnpm payload migrate\`
2181
+ \`devrouter app exec <app> --yes -- infisical run --projectId <id> --env=<env> -- pnpm payload migrate\`
2175
2182
  - Canonical env probe command (run before migrate/seed):
2176
- \`dev app exec <app> --yes -- printenv DB_URL DB_HOST DB_PORT DB_SHADOW_URL\`
2183
+ \`devrouter app exec <app> --yes -- printenv DB_URL DB_HOST DB_PORT DB_SHADOW_URL\`
2177
2184
  - Canonical Doppler migrate command:
2178
- \`dev app exec <app> --yes -- doppler run -- pnpm payload migrate\`
2185
+ \`devrouter app exec <app> --yes -- doppler run -- pnpm payload migrate\`
2179
2186
  - Precedence best practice: avoid defining per-dep var names in Infisical/Doppler when you expect devrouter local DB injection.
2180
2187
  - Precedence best practice: store remote/prod URLs under non-conflicting names (for example \`PROD_DATABASE_URL\`) and map intentionally via \`envMap\`.
2181
2188
  - Precedence best practice: if secret manager must define DB vars, run the env probe and verify values before any migration/seed.
2182
- - Use \`dev app exec --shell -- "<single command string>"\` only when shell expansion is required.
2189
+ - Use \`devrouter app exec --shell -- "<single command string>"\` only when shell expansion is required.
2183
2190
  - \`envMap\` fails fast when source var is missing so migrations do not run with partial mapping.
2184
2191
 
2185
2192
  ## Upgrade handling (required)
2186
2193
 
2187
2194
  - Keep \`.devrouter.yml\` metadata \`devrouter.version\` aligned with the currently applied devrouter release.
2188
- - Verify versions with \`dev -V\` (shows installed CLI version, local repo version, and next upgrade target).
2189
- - Use \`dev upgrade\` to list available upgrade targets and \`dev upgrade <version>\` to print that target's Agent Adaptation Prompt from \`upgrade-prompts/<version>.md\`.
2195
+ - Verify versions with \`devrouter -V\` (shows installed CLI version, local repo version, and next upgrade target).
2196
+ - Use \`devrouter upgrade\` to list available upgrade targets and \`devrouter upgrade <version>\` to print that target's Agent Adaptation Prompt from \`upgrade-prompts/<version>.md\`.
2190
2197
  - Do not assume user-provided instructions include all required adaptation steps.
2191
- - After upgrading the CLI in a dependent repo, refresh discoverability artifacts with \`dev repo agents\` (or \`dev init --write-agents --write-skill\`).
2192
- - Re-run validation after upgrade: \`dev doctor --repo .\`, \`dev app ls --repo .\`, one representative \`dev app exec\` flow, and \`dev ls\`.
2198
+ - After upgrading the CLI in a dependent repo, refresh discoverability artifacts with \`devrouter repo agents\` (or \`devrouter init --write-agents --write-skill\`).
2199
+ - Re-run validation after upgrade: \`devrouter doctor --repo .\`, \`devrouter app ls --repo .\`, one representative \`devrouter app exec\` flow, and \`devrouter ls\`.
2193
2200
 
2194
2201
  ## Optional Linear workflow bootstrap
2195
2202
 
2196
2203
  - To add Linear task-management workflow assets to a repo, run:
2197
- - \`dev init --with-linear --write-agents --write-skill\`, or
2198
- - \`dev repo agents --with-linear\`
2204
+ - \`devrouter init --with-linear --write-agents --write-skill\`, or
2205
+ - \`devrouter repo agents --with-linear\`
2199
2206
  - This writes \`.agents/skills/linear-workflow/SKILL.md\` and reference templates, plus an idempotent AGENTS section.
2200
2207
  - On AGENTS write flows, devrouter asks for minimal Linear mapping (workspace/team/project) and stores it in a managed AGENTS block:
2201
2208
  - \`<!-- devrouter-linear-workflow-config:start -->\`
@@ -2204,65 +2211,65 @@ Run several worktrees of one repo in parallel without host/route collisions. A *
2204
2211
 
2205
2212
  ## Commands
2206
2213
 
2207
- - \`dev init [--write-agents] [--write-skill] [--with-linear]\`: print AI onboarding prompt (non-mutating by default)
2208
- - \`dev -V [--repo .]\`: show installed CLI version, local repo version, and next upgrade target
2209
- - \`dev upgrade [version] [--repo .]\`: list upgrade targets or print target Agent Adaptation Prompt
2210
- - \`dev setup --yes [--repo .] [--json]\`: first-run machine setup plus structured diagnostics
2211
- - \`dev up\` / \`dev down\`: start/stop shared Traefik router
2212
- - \`dev status\`: router/container/network/TLS health
2213
- - \`dev doctor [--repo .]\`: deep diagnostics (global + repo)
2214
- - \`dev ls\`: list active HTTP + TCP routes
2215
- - \`dev open <name>\`: open HTTP route or print TCP connection hint (matches app name, then service/container/host identities)
2216
- - \`dev logs [-f]\`: Traefik access logs
2217
- - \`dev tls install\`: install mkcert certs, enable HTTPS + TCP/SNI
2218
- - \`dev repo init\`: create \`.devrouter.yml\`
2219
- - \`dev repo inspect [--json]\`: inspect package, scripts, compose services, env names, devcontainer, devrouter config, and agent guidance for onboarding
2220
- - \`dev repo devcontainer write --dry-run --json\`: plan conservative Node/pnpm/Postgres devcontainer/devrouter scaffold files without writing
2221
- - \`dev repo devcontainer write --yes\`: write managed Node/pnpm/Postgres devcontainer/devrouter scaffold files when no custom-file conflicts exist
2222
- - \`dev repo devcontainer verify --json\`: emit read-only onboarding evidence for PRs
2223
- - \`dev repo devcontainer verify --live --yes --json\`: register proxy routes and probe HTTP routes after the devcontainer is running
2224
- - \`dev repo agents [--with-linear]\`: write devrouter section in AGENTS.md + install this skill (and optional Linear workflow assets)
2225
- - \`dev app add\`: add/update app entry in \`.devrouter.yml\`
2226
- - \`dev app ls\`: list app entries
2227
- - \`dev app run <name> [--env <env>] [--workspace <slug>]\`: run app with dependency lifecycle (--env overrides SM defaultEnv; --workspace overrides the per-workspace token)
2228
- - \`dev app exec <name> [--shell] [--env <env>] [--workspace <slug>] -- <cmd>\`: one-shot command with resolved dep env
2229
- - \`dev app rm <name> [--keep-config]\`: remove app entry (\`--keep-config\` frees only the live route/hostname, leaves \`.devrouter.yml\` untouched)
2230
- - \`dev workspace up <branch> [--path <dir>] [--no-devpod] [--open]\`: create a worktree + devpod + namespaced routes
2231
- - \`dev workspace ls [--json]\`: list git worktrees with workspace token + route count
2232
- - \`dev workspace down <workspace|branch> [--keep-worktree] [--keep-devpod]\`: free routes + stop devpod + remove worktree
2214
+ - \`devrouter init [--write-agents] [--write-skill] [--with-linear]\`: print AI onboarding prompt (non-mutating by default)
2215
+ - \`devrouter -V [--repo .]\`: show installed CLI version, local repo version, and next upgrade target
2216
+ - \`devrouter upgrade [version] [--repo .]\`: list upgrade targets or print target Agent Adaptation Prompt
2217
+ - \`devrouter setup --yes [--repo .] [--json]\`: first-run machine setup plus structured diagnostics
2218
+ - \`devrouter up\` / \`devrouter down\`: start/stop shared Traefik router
2219
+ - \`devrouter status\`: router/container/network/TLS health
2220
+ - \`devrouter doctor [--repo .]\`: deep diagnostics (global + repo)
2221
+ - \`devrouter ls\`: list active HTTP + TCP routes
2222
+ - \`devrouter open <name>\`: open HTTP route or print TCP connection hint (matches app name, then service/container/host identities)
2223
+ - \`devrouter logs [-f]\`: Traefik access logs
2224
+ - \`devrouter tls install\`: install mkcert certs, enable HTTPS + TCP/SNI
2225
+ - \`devrouter repo init\`: create \`.devrouter.yml\`
2226
+ - \`devrouter repo inspect [--json]\`: inspect package, scripts, compose services, env names, devcontainer, devrouter config, and agent guidance for onboarding
2227
+ - \`devrouter repo devcontainer write --dry-run --json\`: plan conservative Node/pnpm/Postgres devcontainer/devrouter scaffold files without writing
2228
+ - \`devrouter repo devcontainer write --yes\`: write managed Node/pnpm/Postgres devcontainer/devrouter scaffold files when no custom-file conflicts exist
2229
+ - \`devrouter repo devcontainer verify --json\`: emit read-only onboarding evidence for PRs
2230
+ - \`devrouter repo devcontainer verify --live --yes --json\`: register proxy routes and probe HTTP routes after the devcontainer is running
2231
+ - \`devrouter repo agents [--with-linear]\`: write devrouter section in AGENTS.md + install this skill (and optional Linear workflow assets)
2232
+ - \`devrouter app add\`: add/update app entry in \`.devrouter.yml\`
2233
+ - \`devrouter app ls\`: list app entries
2234
+ - \`devrouter app run <name> [--env <env>] [--workspace <slug>]\`: run app with dependency lifecycle (--env overrides SM defaultEnv; --workspace overrides the per-workspace token)
2235
+ - \`devrouter app exec <name> [--shell] [--env <env>] [--workspace <slug>] -- <cmd>\`: one-shot command with resolved dep env
2236
+ - \`devrouter app rm <name> [--keep-config]\`: remove app entry (\`--keep-config\` frees only the live route/hostname, leaves \`.devrouter.yml\` untouched)
2237
+ - \`devrouter workspace up <branch> [--path <dir>] [--no-devpod] [--open]\`: create a worktree + devpod + namespaced routes
2238
+ - \`devrouter workspace ls [--json]\`: list git worktrees with workspace token + route count
2239
+ - \`devrouter workspace down <workspace|branch> [--keep-worktree] [--keep-devpod]\`: free routes + stop devpod + remove worktree
2233
2240
 
2234
2241
  ## Validation workflow
2235
2242
 
2236
2243
  For devcontainer onboarding:
2237
2244
 
2238
- 1. \`dev setup --repo . --yes --json\`
2239
- 2. \`dev doctor --repo . --json\`
2240
- 3. \`dev repo inspect --repo . --json\`
2241
- 4. \`dev repo devcontainer write --repo . --dry-run --json\`
2242
- 5. \`dev repo devcontainer write --repo . --yes\`
2243
- 6. \`dev repo devcontainer verify --repo . --json\`
2245
+ 1. \`devrouter setup --repo . --yes --json\`
2246
+ 2. \`devrouter doctor --repo . --json\`
2247
+ 3. \`devrouter repo inspect --repo . --json\`
2248
+ 4. \`devrouter repo devcontainer write --repo . --dry-run --json\`
2249
+ 5. \`devrouter repo devcontainer write --repo . --yes\`
2250
+ 6. \`devrouter repo devcontainer verify --repo . --json\`
2244
2251
  7. Start the devcontainer, for example \`devpod up .\`
2245
- 8. \`dev repo devcontainer verify --repo . --live --yes --json\`
2252
+ 8. \`devrouter repo devcontainer verify --repo . --live --yes --json\`
2246
2253
 
2247
2254
  For existing host/docker runtime apps:
2248
2255
 
2249
- 1. \`dev setup --repo . --yes\`
2250
- 2. \`dev doctor --repo .\`
2251
- 3. \`dev app ls --repo .\`
2252
- 4. \`dev app run <host-app> --repo . --yes\`
2253
- 5. \`dev ls\`
2256
+ 1. \`devrouter setup --repo . --yes\`
2257
+ 2. \`devrouter doctor --repo .\`
2258
+ 3. \`devrouter app ls --repo .\`
2259
+ 4. \`devrouter app run <host-app> --repo . --yes\`
2260
+ 5. \`devrouter ls\`
2254
2261
  6. \`curl -I https://<host>.localhost\`
2255
- 7. For TCP/Postgres, use \`dev open <name>\` for the connection hint.
2262
+ 7. For TCP/Postgres, use \`devrouter open <name>\` for the connection hint.
2256
2263
 
2257
2264
  ## Runtime behavior notes
2258
2265
 
2259
- - \`dev app run\` auto-starts Docker dependencies and waits for health. Host app runs stop auto-started docker deps on exit; docker app runs leave target services running until explicit cleanup.
2266
+ - \`devrouter app run\` auto-starts Docker dependencies and waits for health. Host app runs stop auto-started docker deps on exit; docker app runs leave target services running until explicit cleanup.
2260
2267
  - Host-runtime dependencies are NOT auto-started (v1).
2261
- - \`kind=dependency\` entries do not create routes and cannot be direct targets for \`dev app run\`, \`dev app exec\`, or \`dev open\`.
2268
+ - \`kind=dependency\` entries do not create routes and cannot be direct targets for \`devrouter app run\`, \`devrouter app exec\`, or \`devrouter open\`.
2262
2269
  - \`kind=dependency\` services start as declared in compose (no Traefik label wiring, no random port publishing, no injected env vars).
2263
- - Postgres on shared \`:5432\` requires TLS/SNI (\`dev tls install\`). Standard app clients should use the injected random port instead.
2264
- - \`dev app exec\` follows the same dep lifecycle for one-shot commands and preserves argv semantics by default (\`shell: false\`).
2265
- - \`dev app exec --shell\` is explicit and requires exactly one command string after \`--\`.
2270
+ - Postgres on shared \`:5432\` requires TLS/SNI (\`devrouter tls install\`). Standard app clients should use the injected random port instead.
2271
+ - \`devrouter app exec\` follows the same dep lifecycle for one-shot commands and preserves argv semantics by default (\`shell: false\`).
2272
+ - \`devrouter app exec --shell\` is explicit and requires exactly one command string after \`--\`.
2266
2273
  - Secret-manager overlap caveat: if Infisical/Doppler defines DB vars too, probe effective env (\`printenv DB_URL DB_HOST DB_PORT\`) before migrate/seed.
2267
2274
  `;
2268
2275
  LINEAR_WORKFLOW_SKILL_CONTENT = `---
@@ -2314,7 +2321,7 @@ When working on Linear-tracked issues, this is required:
2314
2321
 
2315
2322
  ## Devrouter-specific note
2316
2323
 
2317
- If the repository uses devrouter, use \`dev upgrade\` to resolve the required Agent Adaptation Prompt for the target version before major changes (prompt files are versioned under \`upgrade-prompts/<version>.md\`).
2324
+ If the repository uses devrouter, use \`devrouter upgrade\` to resolve the required Agent Adaptation Prompt for the target version before major changes (prompt files are versioned under \`upgrade-prompts/<version>.md\`).
2318
2325
  `;
2319
2326
  LINEAR_ISSUE_TEMPLATE_CONTENT = `# Linear Issue Template
2320
2327
 
@@ -2976,7 +2983,7 @@ function printUpgradeTargets(currentVersion, availableTargets) {
2976
2983
  process.stdout.write(`- ${release.version}${suffix}
2977
2984
  `);
2978
2985
  }
2979
- process.stdout.write("\nRun `dev upgrade <version>` to print the Agent Adaptation Prompt for a target version.\n");
2986
+ process.stdout.write("\nRun `devrouter upgrade <version>` to print the Agent Adaptation Prompt for a target version.\n");
2980
2987
  }
2981
2988
  async function runUpgradeCommand(options, deps = {}) {
2982
2989
  const catalog = loadUpgradeCatalog({
@@ -3153,13 +3160,11 @@ function commandExists(command) {
3153
3160
  return result.status === 0;
3154
3161
  }
3155
3162
  function ensureMkcert() {
3156
- if (commandExists("mkcert")) {
3157
- return;
3158
- }
3159
- if (!commandExists("brew")) {
3160
- throw new Error("mkcert is missing and Homebrew is not available.");
3163
+ if (!commandExists("mkcert")) {
3164
+ throw new Error(
3165
+ "mkcert is not installed. Please install it to use TLS features (e.g., 'brew install mkcert' or via your package manager)."
3166
+ );
3161
3167
  }
3162
- runOrThrow("brew", ["install", "mkcert"]);
3163
3168
  }
3164
3169
  function normalizeHost(host) {
3165
3170
  return host.trim().toLowerCase();
@@ -3885,7 +3890,7 @@ function buildGlobalToolChecks(repoPath) {
3885
3890
  level: compose.ok ? "ok" : "error",
3886
3891
  summary: compose.ok ? "Docker Compose v2 is reachable." : "Docker Compose v2 is not reachable.",
3887
3892
  details: firstLine(compose.output) ?? compose.error,
3888
- suggestion: compose.ok ? void 0 : "Install/start Docker with Compose v2, then run: dev setup --yes"
3893
+ suggestion: compose.ok ? void 0 : "Install/start Docker with Compose v2, then run: devrouter setup --yes"
3889
3894
  });
3890
3895
  const mkcert = runTool("mkcert", ["-version"]);
3891
3896
  const brew = runTool("brew", ["--version"]);
@@ -3894,7 +3899,7 @@ function buildGlobalToolChecks(repoPath) {
3894
3899
  level: mkcert.ok ? "ok" : "warn",
3895
3900
  summary: mkcert.ok ? "mkcert is installed." : "mkcert is not installed.",
3896
3901
  details: mkcert.ok ? firstLine(mkcert.output) : mkcert.error,
3897
- suggestion: mkcert.ok ? void 0 : brew.ok ? "Install mkcert: brew install mkcert" : "Install mkcert for local HTTPS, then run: dev setup --yes"
3902
+ suggestion: mkcert.ok ? void 0 : brew.ok ? "Install mkcert: brew install mkcert" : "Install mkcert for local HTTPS, then run: devrouter setup --yes"
3898
3903
  });
3899
3904
  const devpod = runTool("devpod", ["version"]);
3900
3905
  checks.push({
@@ -4670,7 +4675,7 @@ async function runSetup(options = {}) {
4670
4675
  actions.push(action("failed", {
4671
4676
  id: "setup.confirmation",
4672
4677
  summary: "Setup requires --yes before mutating devrouter-owned machine state.",
4673
- suggestion: "Run: dev setup --yes"
4678
+ suggestion: "Run: devrouter setup --yes"
4674
4679
  }));
4675
4680
  const doctor2 = await buildDoctorReport({ repo: options.repo });
4676
4681
  const partialReport2 = { actions, checks: doctor2.checks };
@@ -4699,7 +4704,7 @@ async function runSetup(options = {}) {
4699
4704
  id: "global.router-files",
4700
4705
  summary: "Failed to ensure global router files.",
4701
4706
  details: message,
4702
- suggestion: "Check write access to ~/.config/devrouter, then run: dev setup --yes"
4707
+ suggestion: "Check write access to ~/.config/devrouter, then run: devrouter setup --yes"
4703
4708
  }));
4704
4709
  }
4705
4710
  try {
@@ -4715,7 +4720,7 @@ async function runSetup(options = {}) {
4715
4720
  id: "global.devnet",
4716
4721
  summary: "Failed to ensure shared Docker network devnet.",
4717
4722
  details: message,
4718
- suggestion: "Start Docker and verify Docker context, then run: dev setup --yes"
4723
+ suggestion: "Start Docker and verify Docker context, then run: devrouter setup --yes"
4719
4724
  }));
4720
4725
  }
4721
4726
  try {
@@ -4731,7 +4736,7 @@ async function runSetup(options = {}) {
4731
4736
  id: "global.router-stack",
4732
4737
  summary: "Failed to start shared Traefik router.",
4733
4738
  details: message,
4734
- suggestion: "Resolve Docker/port conflicts on 80, 443, or 5432, then run: dev setup --yes"
4739
+ suggestion: "Resolve Docker/port conflicts on 80, 443, or 5432, then run: devrouter setup --yes"
4735
4740
  }));
4736
4741
  }
4737
4742
  const mkcert = runTool("mkcert", ["-version"]);
@@ -4740,7 +4745,7 @@ async function runSetup(options = {}) {
4740
4745
  id: "global.tls",
4741
4746
  summary: "Skipped TLS setup because mkcert is not installed.",
4742
4747
  details: mkcert.error,
4743
- suggestion: "Install mkcert, then run: dev setup --yes"
4748
+ suggestion: "Install mkcert, then run: devrouter setup --yes"
4744
4749
  }));
4745
4750
  } else {
4746
4751
  try {
@@ -4756,7 +4761,7 @@ async function runSetup(options = {}) {
4756
4761
  id: "global.tls",
4757
4762
  summary: "Failed to install local TLS certificates.",
4758
4763
  details: message,
4759
- suggestion: "Run: dev tls install"
4764
+ suggestion: "Run: devrouter tls install"
4760
4765
  }));
4761
4766
  }
4762
4767
  }
@@ -4811,10 +4816,46 @@ var init_setup2 = __esm({
4811
4816
  });
4812
4817
 
4813
4818
  // src/util/ports.ts
4819
+ function parseSsPortListeners(stdout, port) {
4820
+ const listeners = [];
4821
+ const lines = stdout.split(/\r?\n/);
4822
+ for (const line of lines) {
4823
+ if (!line.trim()) continue;
4824
+ const parts = line.trim().split(/\s+/);
4825
+ if (parts.length < 4) continue;
4826
+ const localAddr = parts[3];
4827
+ const colonIdx = localAddr.lastIndexOf(":");
4828
+ if (colonIdx === -1) continue;
4829
+ const linePort = Number(localAddr.slice(colonIdx + 1));
4830
+ if (linePort !== port) continue;
4831
+ const usersCol = parts.slice(5).join(" ");
4832
+ const pidMatch = /pid=(\d+)/.exec(usersCol);
4833
+ const cmdMatch = /"([^"]+)"/.exec(usersCol);
4834
+ const command = cmdMatch ? cmdMatch[1] : "?";
4835
+ const pid = pidMatch ? pidMatch[1] : "?";
4836
+ listeners.push({
4837
+ port,
4838
+ command,
4839
+ pid,
4840
+ user: "?",
4841
+ address: localAddr
4842
+ });
4843
+ }
4844
+ return listeners;
4845
+ }
4814
4846
  function findPortListeners(port) {
4815
4847
  const result = (0, import_node_child_process6.spawnSync)("lsof", ["-nP", `-iTCP:${port}`, "-sTCP:LISTEN"], {
4816
4848
  encoding: "utf-8"
4817
4849
  });
4850
+ if (result.error && result.error.code === "ENOENT") {
4851
+ if (process.platform === "linux") {
4852
+ const ssResult = (0, import_node_child_process6.spawnSync)("ss", ["-H", "-lntp", "-p"], { encoding: "utf-8" });
4853
+ if (ssResult.status === 0 && ssResult.stdout) {
4854
+ return parseSsPortListeners(ssResult.stdout, port);
4855
+ }
4856
+ }
4857
+ return [];
4858
+ }
4818
4859
  if (result.status !== 0 || !result.stdout.trim()) {
4819
4860
  return [];
4820
4861
  }
@@ -5784,10 +5825,10 @@ function renderReadme(projectName) {
5784
5825
  Use this repo through the devcontainer, with devrouter providing stable local routes.
5785
5826
 
5786
5827
  \`\`\`bash
5787
- dev setup --yes
5828
+ devrouter setup --yes
5788
5829
  devpod up .
5789
- dev app run app --repo . --yes
5790
- dev app run db --repo . --yes
5830
+ devrouter app run app --repo . --yes
5831
+ devrouter app run db --repo . --yes
5791
5832
  \`\`\`
5792
5833
 
5793
5834
  - App: https://${projectName}.localhost
@@ -5797,19 +5838,19 @@ dev app run db --repo . --yes
5797
5838
  function postWriteNextSteps(repoPath) {
5798
5839
  const quotedRepoPath = shellSingleQuote(repoPath);
5799
5840
  return [
5800
- `Run: dev setup --repo ${quotedRepoPath} --yes`,
5841
+ `Run: devrouter setup --repo ${quotedRepoPath} --yes`,
5801
5842
  `Run: cd ${quotedRepoPath} && devpod up .`,
5802
- `Run: dev app run app --repo ${quotedRepoPath} --yes`,
5803
- `Run: dev app run db --repo ${quotedRepoPath} --yes`,
5804
- `Optional: dev repo agents --repo ${quotedRepoPath}`
5843
+ `Run: devrouter app run app --repo ${quotedRepoPath} --yes`,
5844
+ `Run: devrouter app run db --repo ${quotedRepoPath} --yes`,
5845
+ `Optional: devrouter repo agents --repo ${quotedRepoPath}`
5805
5846
  ];
5806
5847
  }
5807
5848
  function issueNextSteps(issues) {
5808
5849
  const steps = issues.filter((issue) => issue.level === "error").map((issue) => issue.suggestion).filter((suggestion) => Boolean(suggestion));
5809
5850
  if (steps.length > 0) {
5810
- return [...steps, "Re-run: dev repo devcontainer write --dry-run --json"];
5851
+ return [...steps, "Re-run: devrouter repo devcontainer write --dry-run --json"];
5811
5852
  }
5812
- return ["Resolve reported errors, then re-run: dev repo devcontainer write --dry-run --json"];
5853
+ return ["Resolve reported errors, then re-run: devrouter repo devcontainer write --dry-run --json"];
5813
5854
  }
5814
5855
  function packageManagerIssues(repo) {
5815
5856
  if (!repo.packageManager) {
@@ -5919,7 +5960,7 @@ function buildPlan(repoPath, dryRun, version) {
5919
5960
  filePlans.push({
5920
5961
  path: "AGENTS.md",
5921
5962
  action: "suggest",
5922
- reason: "run dev repo agents after reviewing the scaffold"
5963
+ reason: "run devrouter repo agents after reviewing the scaffold"
5923
5964
  });
5924
5965
  return {
5925
5966
  files,
@@ -5930,7 +5971,7 @@ function buildPlan(repoPath, dryRun, version) {
5930
5971
  dryRun,
5931
5972
  files: filePlans,
5932
5973
  issues,
5933
- nextSteps: issues.some((issue) => issue.level === "error") ? issueNextSteps(issues) : dryRun ? [`Review this plan, then run: dev repo devcontainer write --repo ${shellSingleQuote(repoPath)} --yes`] : postWriteNextSteps(repoPath)
5974
+ nextSteps: issues.some((issue) => issue.level === "error") ? issueNextSteps(issues) : dryRun ? [`Review this plan, then run: devrouter repo devcontainer write --repo ${shellSingleQuote(repoPath)} --yes`] : postWriteNextSteps(repoPath)
5934
5975
  }
5935
5976
  };
5936
5977
  }
@@ -5958,10 +5999,10 @@ function writeDevcontainer(options = {}) {
5958
5999
  id: "repo.devcontainer.confirmation",
5959
6000
  level: "error",
5960
6001
  summary: "Writing devcontainer files requires --yes.",
5961
- suggestion: `Run: dev repo devcontainer write --repo ${shellSingleQuote(repoPath)} --yes`
6002
+ suggestion: `Run: devrouter repo devcontainer write --repo ${shellSingleQuote(repoPath)} --yes`
5962
6003
  }
5963
6004
  ],
5964
- nextSteps: [`Run: dev repo devcontainer write --repo ${shellSingleQuote(repoPath)} --yes`]
6005
+ nextSteps: [`Run: devrouter repo devcontainer write --repo ${shellSingleQuote(repoPath)} --yes`]
5965
6006
  };
5966
6007
  }
5967
6008
  for (const file of files) {
@@ -6093,7 +6134,7 @@ function requiredFileChecks(repoPath) {
6093
6134
  id: "repo.devcontainer.verify-files",
6094
6135
  level: missing.length === 0 ? "ok" : "error",
6095
6136
  summary: missing.length === 0 ? "Required devcontainer/devrouter files are present." : `Missing required devcontainer/devrouter file(s): ${missing.join(", ")}.`,
6096
- suggestion: missing.length === 0 ? void 0 : "Run: dev repo devcontainer write --dry-run --json"
6137
+ suggestion: missing.length === 0 ? void 0 : "Run: devrouter repo devcontainer write --dry-run --json"
6097
6138
  };
6098
6139
  }
6099
6140
  function proxyConfigCheck(apps) {
@@ -6101,7 +6142,7 @@ function proxyConfigCheck(apps) {
6101
6142
  id: "repo.devcontainer.verify-proxy-apps",
6102
6143
  level: apps.length > 0 ? "ok" : "error",
6103
6144
  summary: apps.length > 0 ? `Found ${apps.length} proxy app(s) for devcontainer routing.` : "No proxy app entries found for devcontainer routing.",
6104
- suggestion: apps.length > 0 ? void 0 : "Add runtime: proxy app entries to .devrouter.yml or run: dev repo devcontainer write --dry-run --json"
6145
+ suggestion: apps.length > 0 ? void 0 : "Add runtime: proxy app entries to .devrouter.yml or run: devrouter repo devcontainer write --dry-run --json"
6105
6146
  };
6106
6147
  }
6107
6148
  function workspaceTemplateCheck(apps) {
@@ -6144,7 +6185,7 @@ function doctorGateCheck(doctor) {
6144
6185
  level: blocking.length === 0 ? "ok" : "error",
6145
6186
  summary: blocking.length === 0 ? "Doctor has no blocking devcontainer diagnostics." : `Doctor reported ${blocking.length} blocking devcontainer diagnostic(s).`,
6146
6187
  details: blocking.length > 0 ? blocking.map((check) => check.id).join(", ") : void 0,
6147
- suggestion: blocking.length > 0 ? "Run: dev doctor --repo <path> --json" : void 0
6188
+ suggestion: blocking.length > 0 ? "Run: devrouter doctor --repo <path> --json" : void 0
6148
6189
  };
6149
6190
  }
6150
6191
  function routeUrl2(host) {
@@ -6167,7 +6208,7 @@ function registerProxyRoute(repoPath, app, workspace) {
6167
6208
  const { port, upstreamHost: upstreamHost2 } = parseUpstream(app.upstream);
6168
6209
  if (app.protocol === "tcp" && !isTLSEnabled()) {
6169
6210
  throw new Error(
6170
- `App "${app.name}" is a TCP proxy route, which requires TLS (SNI). Run \`dev tls install\` first.`
6211
+ `App "${app.name}" is a TCP proxy route, which requires TLS (SNI). Run \`devrouter tls install\` first.`
6171
6212
  );
6172
6213
  }
6173
6214
  if (app.protocol === "tcp") {
@@ -6199,7 +6240,7 @@ async function liveChecks(repoPath, yes) {
6199
6240
  id: "repo.devcontainer.verify-live-confirmation",
6200
6241
  level: "error",
6201
6242
  summary: "Live devcontainer verification requires --yes.",
6202
- suggestion: "Run: dev repo devcontainer verify --live --yes --json"
6243
+ suggestion: "Run: devrouter repo devcontainer verify --live --yes --json"
6203
6244
  }
6204
6245
  ]
6205
6246
  };
@@ -6216,7 +6257,7 @@ async function liveChecks(repoPath, yes) {
6216
6257
  level: "error",
6217
6258
  summary: "Could not load runtime config for live verification.",
6218
6259
  details: error instanceof Error ? error.message : String(error),
6219
- suggestion: "Fix .devrouter.yml and re-run: dev repo devcontainer verify --live --yes --json"
6260
+ suggestion: "Fix .devrouter.yml and re-run: devrouter repo devcontainer verify --live --yes --json"
6220
6261
  }
6221
6262
  ]
6222
6263
  };
@@ -6268,7 +6309,7 @@ async function liveChecks(repoPath, yes) {
6268
6309
  level: "error",
6269
6310
  summary: `Could not register proxy route '${app.name}'.`,
6270
6311
  details: message,
6271
- suggestion: "Run: dev setup --yes, start the devcontainer, then retry live verification."
6312
+ suggestion: "Run: devrouter setup --yes, start the devcontainer, then retry live verification."
6272
6313
  });
6273
6314
  }
6274
6315
  }
@@ -6294,7 +6335,7 @@ async function verifyDevcontainer(options = {}) {
6294
6335
  level: "error",
6295
6336
  summary: "Could not load .devrouter.yml for devcontainer verification.",
6296
6337
  details: error instanceof Error ? error.message : String(error),
6297
- suggestion: "Fix .devrouter.yml and re-run: dev repo devcontainer verify --json"
6338
+ suggestion: "Fix .devrouter.yml and re-run: devrouter repo devcontainer verify --json"
6298
6339
  });
6299
6340
  }
6300
6341
  let liveRoutes;
@@ -7025,6 +7066,35 @@ function parseListeningPorts(outputText) {
7025
7066
  }
7026
7067
  return Array.from(ports.values()).sort((a, b) => a - b);
7027
7068
  }
7069
+ function parseSsListeningPorts(stdout, targetPids) {
7070
+ const ports = /* @__PURE__ */ new Set();
7071
+ const lines = stdout.split(/\r?\n/);
7072
+ for (const line of lines) {
7073
+ if (!line.trim()) continue;
7074
+ let matchedPid = false;
7075
+ for (const pid of targetPids) {
7076
+ if (line.includes(`pid=${pid}`)) {
7077
+ matchedPid = true;
7078
+ break;
7079
+ }
7080
+ }
7081
+ if (!matchedPid) {
7082
+ continue;
7083
+ }
7084
+ const parts = line.trim().split(/\s+/);
7085
+ if (parts.length < 4) continue;
7086
+ const localAddr = parts[3];
7087
+ const colonIdx = localAddr.lastIndexOf(":");
7088
+ if (colonIdx !== -1) {
7089
+ const portStr = localAddr.slice(colonIdx + 1);
7090
+ const port = Number(portStr);
7091
+ if (Number.isInteger(port) && port > 0) {
7092
+ ports.add(port);
7093
+ }
7094
+ }
7095
+ }
7096
+ return Array.from(ports.values()).sort((a, b) => a - b);
7097
+ }
7028
7098
  function detectListeningPorts(pids) {
7029
7099
  if (pids.length === 0) {
7030
7100
  return [];
@@ -7034,6 +7104,15 @@ function detectListeningPorts(pids) {
7034
7104
  ["-nP", "-iTCP", "-sTCP:LISTEN", "-a", "-p", pids.join(",")],
7035
7105
  { encoding: "utf-8" }
7036
7106
  );
7107
+ if (result.error && result.error.code === "ENOENT") {
7108
+ if (process.platform === "linux") {
7109
+ const ssResult = (0, import_node_child_process11.spawnSync)("ss", ["-H", "-lntp", "-p"], { encoding: "utf-8" });
7110
+ if (ssResult.status === 0 && ssResult.stdout) {
7111
+ return parseSsListeningPorts(ssResult.stdout, new Set(pids));
7112
+ }
7113
+ }
7114
+ return [];
7115
+ }
7037
7116
  if (result.status !== 0) {
7038
7117
  return [];
7039
7118
  }
@@ -7670,7 +7749,11 @@ async function workspaceUp(branch, opts = {}) {
7670
7749
  if (!opts.noDevpod && hasDevpod()) {
7671
7750
  const dp = (0, import_node_child_process12.spawnSync)("devpod", ["up", worktreePath, "--id", ws, "--open-ide=false"], {
7672
7751
  stdio: "inherit",
7673
- env: { ...process.env, WORKSPACE: ws }
7752
+ env: {
7753
+ ...process.env,
7754
+ WORKSPACE: ws,
7755
+ DEVCONTAINER_COMPOSE_OVERLAY: "docker-compose.devrouter.yml"
7756
+ }
7674
7757
  });
7675
7758
  if (dp.status !== 0) {
7676
7759
  process.stderr.write(`Warning: 'devpod up' failed; continuing with route registration.
@@ -7854,7 +7937,7 @@ async function runVersionCommand(options, deps = {}) {
7854
7937
  `);
7855
7938
  process.stdout.write(`All upgrade targets: ${availableTargets.map((entry) => entry.version).join(", ")}
7856
7939
  `);
7857
- process.stdout.write(`Run: dev upgrade ${next.version}
7940
+ process.stdout.write(`Run: devrouter upgrade ${next.version}
7858
7941
  `);
7859
7942
  }
7860
7943
  var init_version = __esm({
@@ -7866,7 +7949,7 @@ var init_version = __esm({
7866
7949
 
7867
7950
  // src/cli.ts
7868
7951
  var import_commander = require("commander");
7869
- var CLI_VERSION = true ? "0.0.23" : "0.0.0-dev";
7952
+ var CLI_VERSION = true ? "0.0.24" : "0.0.0-dev";
7870
7953
  var VERSION_FLAGS = /* @__PURE__ */ new Set(["-V", "--version"]);
7871
7954
  function withErrorHandling(action2) {
7872
7955
  return async (...args) => {
@@ -7881,7 +7964,7 @@ function withErrorHandling(action2) {
7881
7964
  };
7882
7965
  }
7883
7966
  var program = new import_commander.Command();
7884
- program.name("dev").description("Local dev router CLI for stable .localhost routing across repositories").showSuggestionAfterError(true).showHelpAfterError();
7967
+ program.name("devrouter").description("Local dev router CLI for stable .localhost routing across repositories").showSuggestionAfterError(true).showHelpAfterError();
7885
7968
  program.command("init").description("Print an AI onboarding prompt template for adapting a repository to devrouter").option("--repo <path>", "Repository path to embed in the prompt (defaults to current directory)").option("--entries-json <json>", "Optional JSON array of app entries to embed in the prompt").option("--json", "Output prompt and command intents as JSON").option("--write-agents", "Write/update devrouter section in AGENTS.md").option("--write-skill", "Write .agents/skills/devrouter/SKILL.md").option("--with-linear", "Include optional Linear workflow guidance/artifacts when writing").action(withErrorHandling(async (options) => {
7886
7969
  const { runInitCommand: runInitCommand2 } = await Promise.resolve().then(() => (init_init(), init_exports));
7887
7970
  await runInitCommand2(options);