From 06fc1656baf5513911a9849d287e336db970927f Mon Sep 17 00:00:00 2001 From: Eric Han Date: Sat, 23 May 2026 17:56:36 +0900 Subject: [PATCH 1/2] chore(build): allow esbuild postinstall script under pnpm 11 pnpm 11 blocks build scripts by default. esbuild's postinstall is trusted; approve it in pnpm-workspace.yaml so local developers don't hit ERR_PNPM_IGNORED_BUILDS on every install. Co-Authored-By: Claude Opus 4.7 (1M context) --- pnpm-workspace.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 3334c0e..2dfb249 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1 +1,3 @@ packages: [] +allowBuilds: + esbuild: true From 81314689b7030fb1c387c97e9b3fd5662d03ba6a Mon Sep 17 00:00:00 2001 From: Eric Han Date: Sat, 23 May 2026 17:57:26 +0900 Subject: [PATCH 2/2] docs: sync skills, README, install guide with mjs-only config Reflect the worktreeConfig migration (PR #65) across user-facing docs and fill onboarding gaps. - dev-proxy-init/SKILL.md: collapse Phase 3.2/3.3 into one mjs file, drop "routes only" warning, frontmatter verified-against meta, mjs worktreeConfig template - dev-proxy-review/SKILL.md: add semantic-release config check (releaserc + workflow), npm pack whitelist verification, BREAKING CHANGE detection, GitHub Packages mirror verification, drop hard-coded version - installation.md: macOS DNS resolver timing tip (5-10s + dscacheutil), per-line sudo annotations, worktree-not-in-wizard warning, all worktreeConfig examples now mjs, migration flow updated - README.md / README_KO.md: config file count 4 -> 3, worktreeConfig moved into mjs example, port-change comment de-stuttered, legacy fallback note - CLAUDE.md: Config paragraph clarifies legacy fallback covers both routes and worktreeConfig Closes #68 Co-Authored-By: Claude Opus 4.7 (1M context) --- .claude/skills/dev-proxy-init/SKILL.md | 139 ++++++++++++----------- .claude/skills/dev-proxy-review/SKILL.md | 77 ++++++++++--- CLAUDE.md | 2 +- README.md | 93 +++++++-------- README_KO.md | 92 +++++++-------- docs/guide/installation.md | 81 ++++++++----- 6 files changed, 272 insertions(+), 212 deletions(-) diff --git a/.claude/skills/dev-proxy-init/SKILL.md b/.claude/skills/dev-proxy-init/SKILL.md index bf8494e..1be82f9 100644 --- a/.claude/skills/dev-proxy-init/SKILL.md +++ b/.claude/skills/dev-proxy-init/SKILL.md @@ -6,7 +6,7 @@ description: | setup. Triggers on: "setup dev-proxy", "dev-proxy init", "dev-proxy setup", "configure dev-proxy", "add dev-proxy", "initialize dev-proxy". metadata: - version: 1.0.0 + verified-against: "@reopt-ai/dev-proxy >= 1.2.0" --- # dev-proxy-init Skill @@ -61,20 +61,21 @@ array — these will be preserved. If this project's path is already in ### 1.4 Check for legacy project config -If `.dev-proxy.json` exists in the project root and contains a `routes` key -(or a `worktrees` key without a sibling `.dev-proxy.worktrees.json`), this is a -pre-split layout. Inform the user and suggest: +If `.dev-proxy.json` exists in the project root, this is a pre-migration +layout (current versions write `dev-proxy.config.mjs` and +`.dev-proxy.worktrees.json`; `.dev-proxy.json` is only read as a fallback). +Inform the user and suggest: ```bash dev-proxy migrate ``` -The command splits the legacy file three ways: +The command relocates every field out of the legacy file: - `routes` → `dev-proxy.config.mjs` -- `worktrees` instance map → `.dev-proxy.worktrees.json` (new file, CLI-managed) -- `worktreeConfig` → stays in `.dev-proxy.json` (the legacy file is removed if - it would otherwise be empty) +- `worktreeConfig` → `dev-proxy.config.mjs` (same default export) +- `worktrees` instance map → `.dev-proxy.worktrees.json` (CLI-managed) +- `.dev-proxy.json` is deleted once empty It is idempotent. If the user chooses to migrate, run the command, then continue at Phase 3.3. @@ -166,25 +167,27 @@ Is this correct? Would you like to add, remove, or change any routes? ## Phase 3: Generate Configuration -There are up to three project-level files: +There are two project-level files for new setups: -- **`dev-proxy.config.mjs`** — routes (the main config users edit by hand). - Always created by `dev-proxy init`. -- **`.dev-proxy.json`** — `worktreeConfig` schema (only required when worktrees - are used). Hand-edited. -- **`.dev-proxy.worktrees.json`** — CLI-managed worktree instance map. `init` - writes an empty placeholder; `dev-proxy worktree create/destroy/add/remove` - keep it in sync. **Do not hand-edit.** +- **`dev-proxy.config.mjs`** — hand-edited. Holds `routes` and (optionally) + `worktreeConfig`. `dev-proxy init` creates this if it doesn't exist (and + asks before overwriting). +- **`.dev-proxy.worktrees.json`** — CLI-managed worktree instance map. + `init` writes an empty placeholder; `dev-proxy worktree +create/destroy/add/remove` keep it in sync. **Do not hand-edit.** -All three are project configuration and should be committed to git. +Both should be committed to git. + +> **Legacy `.dev-proxy.json`:** Older projects may still have this file. It +> is read as a fallback for `routes`, `worktreeConfig`, and `worktrees` when +> the newer files are absent. New setups should not create it — `dev-proxy +migrate` removes it once everything has been moved into the two files above. > **Note on `.mjs` vs `.js`:** `dev-proxy init` writes `dev-proxy.config.mjs` > by default. `dev-proxy.config.js` is also accepted (used in projects that > already have `"type": "module"` in `package.json`). Both forms work the > same way; pick one. Resolution order at runtime is `.mjs` → `.js` → legacy -> `.dev-proxy.json` for routes. The `worktrees` map is read from -> `.dev-proxy.worktrees.json` first, falling back to a `worktrees` key in -> the legacy `.dev-proxy.json` for projects mid-migration. +> `.dev-proxy.json`. ### 3.1 Global config (`~/.dev-proxy/config.json`) @@ -208,11 +211,11 @@ Write `~/.dev-proxy/config.json`: } ``` -### 3.2 Project routes (`dev-proxy.config.mjs`) +### 3.2 Project config (`dev-proxy.config.mjs`) -This is the main configuration file — only routes go here. If -`dev-proxy.config.mjs` or `dev-proxy.config.js` already exists, ask the user -before overwriting. +This is the main configuration file. It holds **routes** and (optionally) +**worktreeConfig** in one default export. If `dev-proxy.config.mjs` or +`dev-proxy.config.js` already exists, ask the user before overwriting. Create `dev-proxy.config.mjs` in the project root with the confirmed routes. The format must match exactly: @@ -238,45 +241,49 @@ Format rules: - Include trailing commas - JSDoc `@type` annotation enables IDE autocomplete via the exported `Config` type -> **Routes only.** Do not add `worktreeConfig` here. The runtime currently -> reads `worktreeConfig` from `.dev-proxy.json` only (see 3.3) — putting it -> in the `.mjs` file will silently do nothing and `dev-proxy worktree create` -> will fail with `worktreeConfig not configured in .dev-proxy.json`. +If the user also wants worktree support, see 3.3 — `worktreeConfig` is +added to the same default export. -### 3.3 Worktree config (`.dev-proxy.json`) and instance map (`.dev-proxy.worktrees.json`) +### 3.3 Worktree support (optional) -The two roles are now in two separate files: +For users who want `dev-proxy worktree create/destroy` to manage git +worktrees with auto-allocated ports, two pieces are required: -1. **`.dev-proxy.json`** holds `worktreeConfig` — user-authored worktree - settings (port range, directory pattern, services, hooks). Hand-edited. - Only required if the user wants worktree support. -2. **`.dev-proxy.worktrees.json`** holds the live `worktrees` instance map - that `dev-proxy worktree create/destroy/add/remove` rewrite. `dev-proxy -init` always creates this as `{ "worktrees": {} }` so the CLI commands - have a place to write to. +1. **`worktreeConfig`** — user-authored schema (port range, directory + pattern, services, hooks). Added to the `dev-proxy.config.mjs` default + export. Hand-edited. +2. **`.dev-proxy.worktrees.json`** — live `worktrees` instance map that + `dev-proxy worktree create/destroy/add/remove` rewrite. `dev-proxy +init` always creates this as `{ "worktrees": {} }` so the CLI has a + place to write to. If the user does not need worktree support, the empty -`.dev-proxy.worktrees.json` placeholder is enough — skip the rest of this -section. Do **not** create `.dev-proxy.json` in that case. +`.dev-proxy.worktrees.json` placeholder created by `init` is enough — skip +the rest of this section. -If the user **wants worktree support**, write `.dev-proxy.json`: +If the user **wants worktree support**, extend `dev-proxy.config.mjs`: -```json -{ - "worktreeConfig": { - "portRange": [4101, 5000], - "directory": "../-{branch}", - "services": { - "api": { "env": "API_PORT" }, - "web": { "env": "PORT" } +```js +/** @type {import('@reopt-ai/dev-proxy').Config} */ +export default { + routes: { + api: "http://localhost:4000", + web: "http://localhost:3001", + }, + worktreeConfig: { + portRange: [4101, 5000], + directory: "../-{branch}", + services: { + api: { env: "API_PORT" }, + web: { env: "PORT" }, }, - "envFile": ".env.local", - "hooks": { + envFile: ".env.local", + hooks: { "post-create": "pnpm install", - "post-remove": "echo cleanup" - } - } -} + "post-remove": "echo cleanup", + }, + }, +}; ``` Ask the user for: @@ -287,9 +294,10 @@ Ask the user for: - Post-create hook (e.g., `pnpm install`, `npm install`) — optional - Post-remove hook — optional -Both files should be **committed to git**: `.dev-proxy.json` is hand-edited -schema and `.dev-proxy.worktrees.json` is shared worktree state teammates -need to share. +Both files should be **committed to git**: `dev-proxy.config.mjs` is +hand-edited config (routes + worktreeConfig) and +`.dev-proxy.worktrees.json` is shared worktree state teammates need to +share. ### 3.4 Framework-specific setup @@ -437,11 +445,10 @@ Output the final setup summary: ``` dev-proxy setup complete! - Routes: /dev-proxy.config.mjs - Worktree map: /.dev-proxy.worktrees.json (CLI-managed) - Worktree config: /.dev-proxy.json (only if worktrees enabled) - Global: ~/.dev-proxy/config.json - Domain: + Config: /dev-proxy.config.mjs (routes + worktreeConfig) + Worktree map: /.dev-proxy.worktrees.json (CLI-managed) + Global: ~/.dev-proxy/config.json + Domain: Routes: http://web.:3000 → localhost:3001 @@ -463,10 +470,10 @@ dev-proxy setup complete! - Never run `sudo` commands without explicit user confirmation - Always use `http://localhost:` format for route targets (not bare ports) - The `"*"` wildcard route is optional — only add if the user wants a fallback -- `dev-proxy.config.mjs` holds **routes only** — do not add `worktreeConfig` here (the runtime ignores it; worktree commands look in `.dev-proxy.json`) -- `.dev-proxy.json` holds **`worktreeConfig` only** (when worktrees are used) — not the live `worktrees` instance map +- `dev-proxy.config.mjs` holds **routes + (optional) `worktreeConfig`** in one default export - `.dev-proxy.worktrees.json` holds the **CLI-managed `worktrees` instance map** — never hand-edit it; `dev-proxy worktree create/destroy/add/remove` keep it in sync -- All three files should be **committed to git** — they are shared project configuration teammates need -- If the project already has a `.dev-proxy.json` with routes or with a `worktrees` key sitting next to no `.dev-proxy.worktrees.json`, suggest running `dev-proxy migrate` — it splits routes into `.mjs`, `worktrees` into `.dev-proxy.worktrees.json`, leaves `worktreeConfig` in place, and removes `.dev-proxy.json` if nothing else remains +- Both files should be **committed to git** — they are shared project configuration teammates need +- Never create `.dev-proxy.json` for new setups — it is a read-only legacy fallback that `dev-proxy migrate` removes +- If the project already has a `.dev-proxy.json`, suggest running `dev-proxy migrate` — it moves `routes` and `worktreeConfig` into `dev-proxy.config.mjs`, `worktrees` into `.dev-proxy.worktrees.json`, and deletes the legacy file - Never modify the user's application code, `.env` files, or `package.json` - Always confirm the proposed route map before writing any files diff --git a/.claude/skills/dev-proxy-review/SKILL.md b/.claude/skills/dev-proxy-review/SKILL.md index 0dab8b8..d1a05df 100644 --- a/.claude/skills/dev-proxy-review/SKILL.md +++ b/.claude/skills/dev-proxy-review/SKILL.md @@ -7,7 +7,7 @@ description: | Triggers on: "dev-proxy review", "release review", "pre-release check", "review", "publish review". metadata: - version: 2.0.0 + verified-against: "@reopt-ai/dev-proxy >= 1.2.0" internal: true --- @@ -59,19 +59,39 @@ If it crashes or prints nothing, the build is broken. npm pack --dry-run ``` -Confirm the output includes exactly these entries: +`package.json` uses a `files` whitelist — only entries listed there are +shipped. Cross-reference: -- `bin/dev-proxy.js` -- `dist/**/*.js` (compiled source) -- `LICENSE` -- `README.md` -- `README_KO.md` -- `package.json` +```bash +jq -r '.files[]' package.json +``` + +Every glob in `files` must appear in `npm pack` output, and nothing else +(no `src/`, `node_modules/`, `.github/`, test files, `tsconfig*.json`, +`.releaserc.json`). Check the total package size is reasonable (under 150 kB). + +### 1.5 Verify semantic-release configuration + +```bash +cat .releaserc.json +ls .github/workflows/release.yml +``` + +Confirm: -It must NOT include: `src/`, `node_modules/`, `.github/`, test files, `tsconfig*.json`. -Check the total package size is reasonable (under 150 kB). +- `.releaserc.json` lists `@semantic-release/commit-analyzer`, + `@semantic-release/release-notes-generator`, `@semantic-release/npm`, + `@semantic-release/exec` (GitHub Packages mirror publish), and + `@semantic-release/github` plugins in that order +- `branches` is `["main"]` +- `.github/workflows/release.yml` contains a `pnpm exec semantic-release` + step with `packages: write` permission (mirror publish needs it) -### 1.5 Clean working tree +If any plugin is missing or misordered, stop and fix before continuing — +release will partially succeed and leave npmjs.org / GitHub Packages out +of sync. + +### 1.6 Clean working tree ```bash git status @@ -120,7 +140,7 @@ Read `docs/guide/installation.md`. Verify: - Config JSON examples match the `RawGlobalConfig` and `RawProjectConfig` interfaces in `src/cli/config-io.ts` - `worktreeConfig` example matches the `WorktreeConfig` interface - All CLI commands referenced actually exist -- The three project config files (`dev-proxy.config.mjs` for routes, `.dev-proxy.json` for `worktreeConfig`, `.dev-proxy.worktrees.json` for the CLI-managed worktree instance map) are described accurately and consistently with `PROJECT_CONFIG_NAME` / `PROJECT_WORKTREES_NAME` in `src/proxy/config.ts` +- The two project files for new setups (`dev-proxy.config.mjs` for routes + `worktreeConfig`, `.dev-proxy.worktrees.json` for the CLI-managed worktree instance map) are described accurately and consistently with `JS_CONFIG_NAMES` / `PROJECT_WORKTREES_NAME` in `src/proxy/config.ts`. `.dev-proxy.json` should be described as a legacy read-only fallback only (deleted by `dev-proxy migrate`) ### 2.5 Help text @@ -148,7 +168,17 @@ Every commit must follow `(): `. Verify: - Breaking changes use `!` suffix (e.g., `feat!: ...`) - No commits violate the convention (semantic-release ignores non-conforming commits) -### 3.3 Preview release impact +### 3.3 Detect explicit breaking changes + +```bash +git log $(git describe --tags --abbrev=0 2>/dev/null || echo HEAD~20)..HEAD --grep="BREAKING CHANGE" --format="%h %s" +``` + +If anything prints, the next release will be a **major** bump even if all +commit subjects look like patches. Confirm this with the user before +pushing. + +### 3.4 Preview release impact Based on commit types, inform the user what semantic-release will do: @@ -183,16 +213,21 @@ git push origin main ### 4.3 Wait for CI ```bash -gh run list --branch main --limit 1 --json status,conclusion,name +gh run list --branch main --workflow release.yml --limit 1 --json status,conclusion,name ``` -The latest run must show `"conclusion": "success"`. If it shows `"status": "in_progress"`, wait and re-check: +The latest `release.yml` run must show `"conclusion": "success"`. If it +shows `"status": "in_progress"`, wait and re-check: ```bash gh run watch ``` -**If CI fails, investigate the failure before retrying.** +**If CI fails, investigate the failure before retrying.** Common +failures: `@semantic-release/exec` mirror publish (GitHub Packages +authentication or missing `packages: write` permission). The mirror is +fail-soft — npmjs.org release still succeeds, but verify both targets in +step 4.4. ### 4.4 Verify release was created @@ -200,10 +235,18 @@ If releasable commits were present: ```bash npm view @reopt-ai/dev-proxy version +npm view @reopt-ai/dev-proxy version --registry=https://npm.pkg.github.com gh release list --limit 1 ``` -Confirm the new version matches the expected bump from step 3.3. +Confirm: + +- Both registries report the new version +- The GitHub Release exists with the same tag +- The new version matches the expected bump from step 3.4 + +If the GitHub Packages mirror lags behind npmjs.org, check the +`@semantic-release/exec` step in the release workflow logs. ### 4.5 Print summary diff --git a/CLAUDE.md b/CLAUDE.md index 0f648ae..cb2e357 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -39,7 +39,7 @@ Split happens at store ingress (`pushHttp`/`pushWs`). Events are immutable after **Proxy flow**: `createProxyServer()` → emitter fires `request` / `request:complete` / `request:error` / `ws` → store receives via `pushHttp`/`pushWs` → React renders via snapshot. -**Config**: `~/.dev-proxy/config.json` (global: domain, ports, TLS, `projects` array) → each project's `dev-proxy.config.mjs` (routes + `worktreeConfig`) plus `.dev-proxy.worktrees.json` (CLI-managed worktree instance map). Legacy `.dev-proxy.json` (pre-`migrate`) is still read with the new file taking precedence. No cwd-based search — projects are explicitly registered. +**Config**: `~/.dev-proxy/config.json` (global: domain, ports, TLS, `projects` array) → each project's `dev-proxy.config.mjs` (routes + `worktreeConfig`) plus `.dev-proxy.worktrees.json` (CLI-managed worktree instance map). Legacy `.dev-proxy.json` (pre-`migrate`) is still read as a fallback for both `routes` and `worktreeConfig` — the mjs file wins when both define the same key, and `dev-proxy migrate` deletes the legacy file once everything has moved. No cwd-based search — projects are explicitly registered. **Routing**: `host.split(".")[0]` extracts subdomain → exact match in merged routes → `"*"` wildcard fallback → `null` (502). Worktree syntax: `branch--app.domain` → lookup in project worktrees → unregistered = offline error page (no silent fallback). diff --git a/README.md b/README.md index fc9a624..144085e 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ dev-proxy is: - Upstream `http`/`https` and `ws`/`wss` target support - Git worktree-based dynamic routing via project config - Auto-generated TLS certificates via [mkcert](https://github.com/FiloSottile/mkcert) -- Project-based config: global (`~/.dev-proxy/config.json`) + per-project (`dev-proxy.config.mjs` for routes, `.dev-proxy.json` for `worktreeConfig`, `.dev-proxy.worktrees.json` for the CLI-managed worktree map) +- Project-based config: global (`~/.dev-proxy/config.json`) + per-project (`dev-proxy.config.mjs` for routes + `worktreeConfig`, `.dev-proxy.worktrees.json` for the CLI-managed worktree map). Legacy `.dev-proxy.json` is read as a fallback. ## Prerequisites @@ -95,15 +95,16 @@ cd dev-proxy && pnpm install && pnpm proxy ## Configuration -Config is split into four files: +Config lives in three files for new setups: 1. **`~/.dev-proxy/config.json`** — Global settings (domain, ports, TLS, project list) -2. **`/dev-proxy.config.mjs`** — Per-project routes (canonical format) -3. **`/.dev-proxy.json`** — Per-project `worktreeConfig` (only required if you use git worktree routing). Hand-edited. -4. **`/.dev-proxy.worktrees.json`** — CLI-managed worktree instance map (`dev-proxy init` writes an empty placeholder; the worktree CLI commands keep it in sync — do not edit by hand) +2. **`/dev-proxy.config.mjs`** — Per-project routes + (optional) `worktreeConfig`. Hand-edited. +3. **`/.dev-proxy.worktrees.json`** — CLI-managed worktree instance map (`dev-proxy init` writes an empty `{ "worktrees": {} }` placeholder; the worktree CLI commands keep it in sync — do not hand-edit) > Easiest path: run `dev-proxy init` from your project directory and the wizard creates these files for you. The sections below cover the file format if you'd rather author them by hand. +> **Legacy `/.dev-proxy.json`** is a read-only fallback for older projects (pre-mjs layouts). Run `dev-proxy migrate` once and it goes away. + ### Global Config (`~/.dev-proxy/config.json`) ```json @@ -137,18 +138,18 @@ export default { - When multiple projects register the same subdomain, the first one wins - `certPath`/`keyPath` are set in the global config, resolved relative to `~/.dev-proxy/` - `dev-proxy.config.js` is also accepted (used when `package.json` has `"type": "module"`); `.mjs` takes precedence if both exist -- Resolution order at runtime: `dev-proxy.config.mjs` → `dev-proxy.config.js` → `.dev-proxy.json` (legacy fallback for `routes`) -- The CLI-managed `worktrees` map always lives in `.dev-proxy.worktrees.json`. `worktreeConfig` (if present) stays in `.dev-proxy.json` and is hand-edited. +- Resolution order at runtime: `dev-proxy.config.mjs` → `dev-proxy.config.js` → `.dev-proxy.json` (legacy fallback for both `routes` and `worktreeConfig`) +- The CLI-managed `worktrees` instance map always lives in `.dev-proxy.worktrees.json`. `worktreeConfig` (if present) belongs in the same `dev-proxy.config.mjs` default export as `routes` — see [Worktree Routing](#worktree-routing). ### Migrating from `.dev-proxy.json` -Earlier versions stored everything (routes, `worktreeConfig`, and the `worktrees` instance map) in `.dev-proxy.json`. The format still works at read time, but `dev-proxy.config.mjs` + `.dev-proxy.worktrees.json` is the documented split. To migrate every registered project at once: +Earlier versions stored everything (routes, `worktreeConfig`, and the `worktrees` instance map) in `.dev-proxy.json`. The format still works at read time as a fallback, but new setups use `dev-proxy.config.mjs` (routes + `worktreeConfig`) + `.dev-proxy.worktrees.json` (CLI-managed instance map). To consolidate every registered project at once: ```bash dev-proxy migrate ``` -The command moves `routes` from `.dev-proxy.json` into `dev-proxy.config.mjs`, moves the `worktrees` instance map into `.dev-proxy.worktrees.json`, and leaves `worktreeConfig` (if any) in `.dev-proxy.json`. When `.dev-proxy.json` would otherwise be empty after the split, it is removed. The command is idempotent and projects already migrated are skipped. +The command moves `routes` and `worktreeConfig` into `dev-proxy.config.mjs`, moves the `worktrees` instance map into `.dev-proxy.worktrees.json`, and deletes `.dev-proxy.json` once it is empty. It is idempotent — projects already migrated are skipped. ### HTTPS @@ -167,7 +168,7 @@ dev-proxy supports git worktree-based dynamic routing. When you use `branch--app **Automatic lifecycle management:** -Routes go in `dev-proxy.config.mjs`; the `worktreeConfig` schema goes in `.dev-proxy.json` (you author this); the live `worktrees` instance map goes in `.dev-proxy.worktrees.json` (the CLI manages this). Use `services` to define per-subdomain port mappings — dev-proxy allocates ports automatically and generates a `.env.local` file so your dev servers know which port to listen on: +Routes **and** `worktreeConfig` go in the same `dev-proxy.config.mjs` default export (you author this); the live `worktrees` instance map goes in `.dev-proxy.worktrees.json` (the CLI manages this). Use `services` to define per-subdomain port mappings — dev-proxy allocates ports automatically and generates a `.env.local` file so your dev servers know which port to listen on: ```js // dev-proxy.config.mjs @@ -178,26 +179,20 @@ export default { data: "http://localhost:4001", "*": "http://localhost:3001", }, -}; -``` - -```json -// .dev-proxy.json -{ - "worktreeConfig": { - "portRange": [4101, 5000], - "directory": "../myproject-{branch}", - "services": { - "www": { "env": "PORT" }, - "data": { "env": "DATA_PORT" } + worktreeConfig: { + portRange: [4101, 5000], + directory: "../myproject-{branch}", + services: { + www: { env: "PORT" }, + data: { env: "DATA_PORT" }, }, - "envFile": ".env.local", - "hooks": { + envFile: ".env.local", + hooks: { "post-create": "pnpm install", - "post-remove": "echo cleanup done" - } - } -} + "post-remove": "echo cleanup done", + }, + }, +}; ``` ```json @@ -320,8 +315,8 @@ Kill the existing process or use a different port in your config: # Find and kill lsof -ti :3000 | xargs kill -# Or change port -# Or change port in ~/.dev-proxy/config.json: "port": 3080 +# Or change the proxy port in ~/.dev-proxy/config.json +# ("port": 3080) ``` ### mkcert not found @@ -364,25 +359,25 @@ If you see `Raw mode is not supported`, you're running in a non-TTY context (e.g ## CLI Reference -| Command | Description | -| -------------------------------------- | ------------------------------------------------------------------------ | -| `dev-proxy` | Start proxy and open traffic inspector | -| `dev-proxy init` | Interactive setup wizard | -| `dev-proxy migrate` | Split legacy `.dev-proxy.json` into `.mjs` + `.dev-proxy.worktrees.json` | -| `dev-proxy status` | Show configuration and routing table | -| `dev-proxy doctor` | Run environment diagnostics | -| `dev-proxy config` | View global settings | -| `dev-proxy config set ` | Modify global settings (domain, port, httpsPort) | -| `dev-proxy project add [path]` | Register a project (default: cwd) | -| `dev-proxy project remove ` | Unregister a project | -| `dev-proxy project list` | List registered projects | -| `dev-proxy worktree create ` | Create worktree with auto port + hooks | -| `dev-proxy worktree destroy ` | Destroy worktree with hooks + cleanup | -| `dev-proxy worktree add ` | Register worktree manually (no git operations) | -| `dev-proxy worktree remove ` | Unregister worktree manually | -| `dev-proxy worktree list` | List all worktrees | -| `dev-proxy --help` | Show help | -| `dev-proxy --version` | Show version | +| Command | Description | +| -------------------------------------- | ----------------------------------------------------------------------------------------------- | +| `dev-proxy` | Start proxy and open traffic inspector | +| `dev-proxy init` | Interactive setup wizard | +| `dev-proxy migrate` | Move legacy `.dev-proxy.json` content into `.mjs` + `.dev-proxy.worktrees.json`, then delete it | +| `dev-proxy status` | Show configuration and routing table | +| `dev-proxy doctor` | Run environment diagnostics | +| `dev-proxy config` | View global settings | +| `dev-proxy config set ` | Modify global settings (domain, port, httpsPort) | +| `dev-proxy project add [path]` | Register a project (default: cwd) | +| `dev-proxy project remove ` | Unregister a project | +| `dev-proxy project list` | List registered projects | +| `dev-proxy worktree create ` | Create worktree with auto port + hooks | +| `dev-proxy worktree destroy ` | Destroy worktree with hooks + cleanup | +| `dev-proxy worktree add ` | Register worktree manually (no git operations) | +| `dev-proxy worktree remove ` | Unregister worktree manually | +| `dev-proxy worktree list` | List all worktrees | +| `dev-proxy --help` | Show help | +| `dev-proxy --version` | Show version | ## Architecture diff --git a/README_KO.md b/README_KO.md index 8bc43b8..70ccc76 100644 --- a/README_KO.md +++ b/README_KO.md @@ -43,7 +43,7 @@ dev-proxy는: - 업스트림 `http`/`https`, `ws`/`wss` 타깃 지원 - 프로젝트 설정 기반 Git worktree 동적 라우팅 - [mkcert](https://github.com/FiloSottile/mkcert)를 이용한 TLS 인증서 자동 생성 -- 프로젝트 기반 설정: 전역 (`~/.dev-proxy/config.json`) + 프로젝트별 (라우트는 `dev-proxy.config.mjs`, `worktreeConfig`는 `.dev-proxy.json`, CLI가 관리하는 워크트리 맵은 `.dev-proxy.worktrees.json`) +- 프로젝트 기반 설정: 전역 (`~/.dev-proxy/config.json`) + 프로젝트별 (`dev-proxy.config.mjs`에 라우트 + `worktreeConfig`, `.dev-proxy.worktrees.json`에 CLI가 관리하는 워크트리 맵). 레거시 `.dev-proxy.json`은 fallback으로만 읽힙니다. ## 요구 사항 @@ -95,15 +95,16 @@ cd dev-proxy && pnpm install && pnpm proxy ## 설정 -설정은 네 개의 파일로 나뉩니다: +신규 설정에서는 세 개의 파일을 씁니다: 1. **`~/.dev-proxy/config.json`** — 전역 설정 (도메인, 포트, TLS, 프로젝트 목록) -2. **`<프로젝트>/dev-proxy.config.mjs`** — 프로젝트별 라우트 (표준 형식) -3. **`<프로젝트>/.dev-proxy.json`** — `worktreeConfig` (Git worktree 라우팅을 사용할 때만 필요). 사람이 직접 편집합니다. -4. **`<프로젝트>/.dev-proxy.worktrees.json`** — CLI가 관리하는 워크트리 인스턴스 맵 (`dev-proxy init`이 빈 placeholder를 만들고 워크트리 CLI 명령이 자동 갱신 — 직접 편집하지 마세요) +2. **`<프로젝트>/dev-proxy.config.mjs`** — 프로젝트별 라우트 + (선택) `worktreeConfig`. 사람이 직접 편집합니다. +3. **`<프로젝트>/.dev-proxy.worktrees.json`** — CLI가 관리하는 워크트리 인스턴스 맵 (`dev-proxy init`이 빈 `{ "worktrees": {} }` placeholder를 만들고 워크트리 CLI 명령이 자동 갱신 — 직접 편집하지 마세요) > 가장 간단한 방법은 프로젝트 디렉터리에서 `dev-proxy init`을 실행하는 것입니다. 마법사가 위 파일들을 자동으로 생성합니다. 아래 섹션은 직접 작성하고 싶을 때 참고하세요. +> **레거시 `<프로젝트>/.dev-proxy.json`**은 옛 레이아웃(pre-mjs) 프로젝트를 위해 읽기 전용 fallback으로만 동작합니다. `dev-proxy migrate`를 한 번 실행하면 사라집니다. + ### 전역 설정 (`~/.dev-proxy/config.json`) ```json @@ -137,18 +138,18 @@ export default { - 여러 프로젝트가 같은 서브도메인을 등록하면 먼저 등록된 것이 우선 - `certPath`/`keyPath`는 전역 설정에서 지정하며, `~/.dev-proxy/` 기준 상대 경로로 해석됩니다 - `dev-proxy.config.js`도 지원됩니다 (`package.json`에 `"type": "module"`이 있을 때 사용). 둘 다 있으면 `.mjs`가 우선합니다 -- 런타임 해석 순서: `dev-proxy.config.mjs` → `dev-proxy.config.js` → `.dev-proxy.json` (`routes` 레거시 fallback) -- CLI가 관리하는 `worktrees` 맵은 항상 `.dev-proxy.worktrees.json`에 저장됩니다. `worktreeConfig`(있는 경우)는 `.dev-proxy.json`에 남아 사람이 편집합니다. +- 런타임 해석 순서: `dev-proxy.config.mjs` → `dev-proxy.config.js` → `.dev-proxy.json` (`routes`와 `worktreeConfig` 양쪽 모두에 대한 레거시 fallback) +- CLI가 관리하는 `worktrees` 인스턴스 맵은 항상 `.dev-proxy.worktrees.json`에 저장됩니다. `worktreeConfig`(있는 경우)는 `dev-proxy.config.mjs`의 같은 default export 안에 `routes`와 나란히 두세요 — [Worktree 라우팅](#worktree-라우팅) 참고. ### `.dev-proxy.json`에서 마이그레이션 -이전 버전은 모든 것(routes, `worktreeConfig`, `worktrees` 인스턴스 맵)을 `.dev-proxy.json` 한 파일에 담았습니다. 읽기는 여전히 호환되지만, 표준은 `dev-proxy.config.mjs` + `.dev-proxy.worktrees.json` 분리입니다. 등록된 모든 프로젝트를 한 번에 마이그레이션하려면: +이전 버전은 모든 것(routes, `worktreeConfig`, `worktrees` 인스턴스 맵)을 `.dev-proxy.json` 한 파일에 담았습니다. 읽기는 여전히 fallback으로 호환되지만, 신규 설정은 `dev-proxy.config.mjs` (라우트 + `worktreeConfig`) + `.dev-proxy.worktrees.json` (CLI 관리) 조합을 씁니다. 등록된 모든 프로젝트를 한 번에 정리하려면: ```bash dev-proxy migrate ``` -이 명령은 `.dev-proxy.json`의 `routes`를 `dev-proxy.config.mjs`로, `worktrees` 인스턴스 맵을 `.dev-proxy.worktrees.json`으로 옮기고, `worktreeConfig`(있는 경우)는 그대로 `.dev-proxy.json`에 남깁니다. 분리 후 `.dev-proxy.json`이 비게 되면 파일 자체를 삭제합니다. 멱등하며 이미 마이그레이션된 프로젝트는 건너뜁니다. +이 명령은 `.dev-proxy.json`의 `routes`와 `worktreeConfig`를 `dev-proxy.config.mjs`로, `worktrees` 인스턴스 맵을 `.dev-proxy.worktrees.json`으로 옮긴 뒤 `.dev-proxy.json`이 비면 파일 자체를 삭제합니다. 멱등하며 이미 마이그레이션된 프로젝트는 건너뜁니다. ### HTTPS @@ -167,7 +168,7 @@ dev-proxy는 Git worktree 기반 동적 라우팅을 지원합니다. 호스트 **자동 라이프사이클 관리:** -라우트는 `dev-proxy.config.mjs`에, `worktreeConfig` 스키마는 `.dev-proxy.json`에(직접 작성), 실시간 `worktrees` 인스턴스 맵은 `.dev-proxy.worktrees.json`에(CLI가 관리) 둡니다. `services`로 서브도메인별 포트 매핑을 정의하면 dev-proxy가 포트를 자동 할당하고 `.env.local`을 생성하여 dev 서버가 어떤 포트에서 listen할지 알 수 있습니다: +라우트와 `worktreeConfig` 모두 `dev-proxy.config.mjs`의 같은 default export에 둡니다(사람이 작성). 실시간 `worktrees` 인스턴스 맵은 `.dev-proxy.worktrees.json`에(CLI가 관리) 둡니다. `services`로 서브도메인별 포트 매핑을 정의하면 dev-proxy가 포트를 자동 할당하고 `.env.local`을 생성하여 dev 서버가 어떤 포트에서 listen할지 알 수 있습니다: ```js // dev-proxy.config.mjs @@ -178,26 +179,20 @@ export default { data: "http://localhost:4001", "*": "http://localhost:3001", }, -}; -``` - -```json -// .dev-proxy.json -{ - "worktreeConfig": { - "portRange": [4101, 5000], - "directory": "../myproject-{branch}", - "services": { - "www": { "env": "PORT" }, - "data": { "env": "DATA_PORT" } + worktreeConfig: { + portRange: [4101, 5000], + directory: "../myproject-{branch}", + services: { + www: { env: "PORT" }, + data: { env: "DATA_PORT" }, }, - "envFile": ".env.local", - "hooks": { + envFile: ".env.local", + hooks: { "post-create": "pnpm install", - "post-remove": "echo cleanup done" - } - } -} + "post-remove": "echo cleanup done", + }, + }, +}; ``` ```json @@ -320,7 +315,8 @@ Error: port 3000 is already in use (another dev-proxy instance may already be ru # 찾아서 종료 lsof -ti :3000 | xargs kill -# 또는 ~/.dev-proxy/config.json에서 포트 변경: "port": 3080 +# 또는 ~/.dev-proxy/config.json에서 프록시 포트 변경 +# ("port": 3080) ``` ### mkcert를 찾을 수 없음 @@ -363,25 +359,25 @@ Next.js 서비스로 라우팅되는 서브도메인마다 하나씩 추가합 ## CLI 레퍼런스 -| 명령어 | 설명 | -| -------------------------------------- | ------------------------------------------------------------------------ | -| `dev-proxy` | 프록시 시작 + 트래픽 인스펙터 | -| `dev-proxy init` | 인터랙티브 설정 위자드 | -| `dev-proxy migrate` | 레거시 `.dev-proxy.json`을 `.mjs` + `.dev-proxy.worktrees.json`으로 분리 | -| `dev-proxy status` | 현재 설정 및 라우팅 테이블 | -| `dev-proxy doctor` | 환경 진단 | -| `dev-proxy config` | 글로벌 설정 조회 | -| `dev-proxy config set ` | 글로벌 설정 수정 (domain, port, httpsPort) | -| `dev-proxy project add [path]` | 프로젝트 등록 (기본: cwd) | -| `dev-proxy project remove ` | 프로젝트 해제 | -| `dev-proxy project list` | 등록된 프로젝트 목록 | -| `dev-proxy worktree create ` | 워크트리 생성 (자동 포트 + 훅 실행) | -| `dev-proxy worktree destroy ` | 워크트리 제거 (훅 실행 + 정리) | -| `dev-proxy worktree add ` | 워크트리 수동 등록 (git 조작 없음) | -| `dev-proxy worktree remove ` | 워크트리 수동 해제 | -| `dev-proxy worktree list` | 워크트리 목록 | -| `dev-proxy --help` | 도움말 | -| `dev-proxy --version` | 버전 | +| 명령어 | 설명 | +| -------------------------------------- | ------------------------------------------------------------------------------------------- | +| `dev-proxy` | 프록시 시작 + 트래픽 인스펙터 | +| `dev-proxy init` | 인터랙티브 설정 위자드 | +| `dev-proxy migrate` | 레거시 `.dev-proxy.json` 내용을 `.mjs` + `.dev-proxy.worktrees.json`으로 옮기고 파일을 삭제 | +| `dev-proxy status` | 현재 설정 및 라우팅 테이블 | +| `dev-proxy doctor` | 환경 진단 | +| `dev-proxy config` | 글로벌 설정 조회 | +| `dev-proxy config set ` | 글로벌 설정 수정 (domain, port, httpsPort) | +| `dev-proxy project add [path]` | 프로젝트 등록 (기본: cwd) | +| `dev-proxy project remove ` | 프로젝트 해제 | +| `dev-proxy project list` | 등록된 프로젝트 목록 | +| `dev-proxy worktree create ` | 워크트리 생성 (자동 포트 + 훅 실행) | +| `dev-proxy worktree destroy ` | 워크트리 제거 (훅 실행 + 정리) | +| `dev-proxy worktree add ` | 워크트리 수동 등록 (git 조작 없음) | +| `dev-proxy worktree remove ` | 워크트리 수동 해제 | +| `dev-proxy worktree list` | 워크트리 목록 | +| `dev-proxy --help` | 도움말 | +| `dev-proxy --version` | 버전 | ## 구조 diff --git a/docs/guide/installation.md b/docs/guide/installation.md index 51d00bc..8f72c30 100644 --- a/docs/guide/installation.md +++ b/docs/guide/installation.md @@ -50,7 +50,12 @@ dev-proxy init If a `dev-proxy.config.mjs` already exists, the wizard asks before overwriting. Skip Step 3B and continue at Step 4. -> **Worktree support is not part of the wizard.** If the user wants worktrees, finish `init`, then add a `worktreeConfig` block to `.dev-proxy.json` (create the file if it doesn't exist) as shown in Step 3B (substep 3). +> ⚠ **Worktree support is not part of the wizard.** `init` only sets up +> `routes`. If the user wants `dev-proxy worktree create/destroy` to +> manage git worktrees with auto-allocated ports, finish `init`, then +> hand-edit `dev-proxy.config.mjs` to add a `worktreeConfig` block +> alongside `routes` (see Step 3B, substep 3 for the shape). **Do not +> create or edit `.dev-proxy.worktrees.json` by hand** — the CLI owns it. ## Step 3B: Configure (Manual) @@ -79,7 +84,7 @@ Then write `~/.dev-proxy/config.json` — replace placeholders with the user's a ### 2. Project config (routes) -Routes live in **`/dev-proxy.config.mjs`** — this is the canonical format. Do not place routes in `.dev-proxy.json`; that file is reserved for the optional `worktreeConfig` schema (see substep 3). The CLI-managed worktree instance map lives in a separate `.dev-proxy.worktrees.json`. +Routes live in **`/dev-proxy.config.mjs`** — this is the canonical format. `worktreeConfig` (substep 3) is added to the same default export when worktree support is needed. The CLI-managed worktree instance map lives in a separate `.dev-proxy.worktrees.json`. If a `dev-proxy.config.mjs` already exists, read it first and merge — do not overwrite existing routes. @@ -106,27 +111,33 @@ Two special keys: ### 3. Worktree support (optional) -If the user wants worktrees, add `worktreeConfig` to **`/.dev-proxy.json`** (not the `.mjs` file). The `services` field maps each subdomain to the env variable name the app reads for its port: +If the user wants worktrees, extend `dev-proxy.config.mjs` with a `worktreeConfig` block alongside `routes`. The `services` field maps each subdomain to the env variable name the app reads for its port: -```json -{ - "worktreeConfig": { - "portRange": [4101, 5000], - "directory": "../-{branch}", - "services": { - "www": { "env": "PORT" }, - "data": { "env": "DATA_PORT" } +```js +// /dev-proxy.config.mjs +/** @type {import('@reopt-ai/dev-proxy').Config} */ +export default { + routes: { + www: "http://localhost:3001", + data: "http://localhost:4000", + }, + worktreeConfig: { + portRange: [4101, 5000], + directory: "../-{branch}", + services: { + www: { env: "PORT" }, + data: { env: "DATA_PORT" }, }, - "envFile": ".env.local", - "hooks": { + envFile: ".env.local", + hooks: { "post-create": "", - "post-remove": "echo cleanup" - } - } -} + "post-remove": "echo cleanup", + }, + }, +}; ``` -Routes still live in `dev-proxy.config.mjs` from substep 2 — only `worktreeConfig` goes into `.dev-proxy.json`. The live `worktrees` instance map lives in `.dev-proxy.worktrees.json` and is managed entirely by the CLI. +The live `worktrees` instance map lives in `.dev-proxy.worktrees.json` and is managed entirely by the CLI. **Do not hand-edit it.** When `dev-proxy worktree create ` runs, it: @@ -137,7 +148,7 @@ When `dev-proxy worktree create ` runs, it: The `{branch}` placeholder in `directory` is replaced with the branch name at runtime. -> **File-split summary.** Routes → `dev-proxy.config.mjs` (you author this). `worktreeConfig` schema → `.dev-proxy.json` (you author this). Live `worktrees` map → `.dev-proxy.worktrees.json` (the CLI manages this — do not hand-edit). All three files are safe to commit so teammates share the same setup. +> **File-split summary.** `dev-proxy.config.mjs` holds `routes` + (optional) `worktreeConfig` — you author this. `.dev-proxy.worktrees.json` holds the live `worktrees` map — the CLI manages it (never hand-edit). Both should be committed so teammates share the same setup. (Older projects may still have a `.dev-proxy.json` — see "Migrating from `.dev-proxy.json`" below.) ## Step 4: DNS Setup @@ -172,15 +183,22 @@ grep -q "" "$(brew --prefix)/etc/dnsmasq.conf" 2>/dev/null || \ Start/restart dnsmasq and configure the resolver: ```bash -sudo brew services restart dnsmasq -sudo mkdir -p /etc/resolver -echo "nameserver 127.0.0.1" | sudo tee /etc/resolver/ +sudo brew services restart dnsmasq # restart so the new domain entry is loaded +sudo mkdir -p /etc/resolver # macOS-only directory for per-TLD resolvers +echo "nameserver 127.0.0.1" | sudo tee /etc/resolver/ # route the TLD to local dnsmasq ``` Replace `` with the actual domain (e.g., `myapp.dev`). > **Important:** The `sudo` commands require user confirmation. Explain what each command does before running. Never run sudo commands without user awareness. +> ⏱ **Wait 5–10 seconds** after writing `/etc/resolver/` before testing. macOS picks up new resolvers asynchronously — `ping` may fail immediately after the command runs even when everything is correct. If it still fails after 10 s, force a flush: +> +> ```bash +> sudo dscacheutil -flushcache +> sudo killall -HUP mDNSResponder +> ``` + ### Linux Linux `/etc/hosts` does not support wildcards. Each subdomain must be added individually. @@ -251,9 +269,9 @@ To run in the background, use a terminal multiplexer or a separate terminal tab. ## Migrating from `.dev-proxy.json` -Earlier versions of dev-proxy stored everything (routes, `worktreeConfig`, and the `worktrees` instance map) in a single `.dev-proxy.json`. That layout still works at read time, but the documented split is `dev-proxy.config.mjs` (routes) + `.dev-proxy.json` (`worktreeConfig` only) + `.dev-proxy.worktrees.json` (CLI-managed instance map). +Earlier versions of dev-proxy stored everything (routes, `worktreeConfig`, and the `worktrees` instance map) in a single `.dev-proxy.json`. New setups should not create that file — current versions use `dev-proxy.config.mjs` (routes + `worktreeConfig`) + `.dev-proxy.worktrees.json` (CLI-managed instance map). -To migrate every project registered in `~/.dev-proxy/config.json` in one shot: +The legacy file is still **read** as a fallback so existing projects keep working. To clean it up across every registered project in one shot: ```bash dev-proxy migrate @@ -261,13 +279,14 @@ dev-proxy migrate For each project, this command: -1. Reads `routes` from `.dev-proxy.json` (if any) and writes them to `dev-proxy.config.mjs`. -2. Moves the `worktrees` instance map into `.dev-proxy.worktrees.json`. -3. Leaves `worktreeConfig` (if any) in `.dev-proxy.json`. If `.dev-proxy.json` would otherwise be empty after the split, the file is removed. +1. Moves `routes` from `.dev-proxy.json` into `dev-proxy.config.mjs`. +2. Moves `worktreeConfig` from `.dev-proxy.json` into the same `dev-proxy.config.mjs` default export. +3. Moves the `worktrees` instance map into `.dev-proxy.worktrees.json`. +4. Deletes `.dev-proxy.json` once everything has been relocated. -Projects already migrated are skipped. The command is idempotent — running it twice is safe. +Projects already migrated are skipped (the command reports `cleaned-legacy` / `skipped-no-legacy`). It is idempotent — running it twice is safe. -> **Resolution order at runtime:** `dev-proxy.config.mjs` → `dev-proxy.config.js` → `.dev-proxy.json` (legacy fallback for `routes`). The CLI-managed `worktrees` map is read from `.dev-proxy.worktrees.json` first; the legacy `worktrees` key inside `.dev-proxy.json` only acts as a fallback for projects that haven't run `migrate` yet. +> **Resolution order at runtime:** `dev-proxy.config.mjs` → `dev-proxy.config.js` → `.dev-proxy.json` (legacy fallback for `routes` and `worktreeConfig`). The CLI-managed `worktrees` map is read from `.dev-proxy.worktrees.json` first; the legacy `worktrees` key inside `.dev-proxy.json` only acts as a fallback for projects that haven't run `migrate` yet. ## Managing Projects @@ -287,7 +306,7 @@ dev-proxy project list ## Managing Worktrees -Requires `worktreeConfig` in the project's `.dev-proxy.json` (see Step 3B, substep 3). +Requires `worktreeConfig` in the project's `dev-proxy.config.mjs` (see Step 3B, substep 3). ```bash # Create: git worktree + auto port + runs post-create hook @@ -324,6 +343,6 @@ Common issues: - **Port in use** — Check with `lsof -ti :3000`. Kill the process or change the port via `dev-proxy config set port 3080`. - **HTTPS not working** — Ensure mkcert is installed (`which mkcert`) and the local CA is set up (`mkcert -install`). - **Config not loading** — Run `dev-proxy status` to see what config is actually loaded. Check file paths and that `dev-proxy.config.mjs` has a default export. -- **Routes seem stale after editing `.dev-proxy.json`** — If `dev-proxy.config.mjs` exists, routes are read from there and `.dev-proxy.json` `routes` are ignored. Either edit the `.mjs` file or delete it to fall back to JSON. +- **Routes / `worktreeConfig` seem stale after editing `.dev-proxy.json`** — If `dev-proxy.config.mjs` exists, both `routes` and `worktreeConfig` are read from there; the same keys in `.dev-proxy.json` are ignored. Either edit the `.mjs` file or run `dev-proxy migrate` to consolidate. The legacy file is only used as a fallback when the corresponding key is absent from `.mjs`. - **Worktrees not appearing after editing `.dev-proxy.worktrees.json` by hand** — That file is CLI-managed; the recommended path is `dev-proxy worktree create/add` so the in-memory registry stays in sync. Manual edits will be picked up on the next file-watch tick, but the CLI may overwrite them. - **Next.js HMR not working / origin errors** — Next.js >= 15.0 validates request origins. Add `allowedDevOrigins` to `next.config.mjs` with each proxy subdomain URL (e.g., `["http://web.localhost:3000"]`). See the [Next.js docs on allowedDevOrigins](https://nextjs.org/docs/app/api-reference/config/next-config-js/allowedDevOrigins).