Skip to content
OhadCPublic

About

Per-folder dev port ranges, so parallel worktrees and AI agents never fight over ports.

Resources

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Repository files navigation

leeway

npm ci license

Per-folder dev port ranges, so parallel worktrees and AI agents never fight over ports.

Two checkouts of the same repo both want port 5173. One fails with EADDRINUSE, or vite quietly moves to 5174 and the other worktree's tests talk to the wrong server. Run three agents in three worktrees and this happens all day.

leeway gives every folder its own stable, contiguous range of ports and hands them to your servers as environment variables:

$ leeway vite
leeway: ports 21000-21001 for /home/me/app
  PORT_WEB  21000
  PORT_API  21001

The worktree next door gets 21002-21003, and keeps that range on every run.

Quick start

npm install -D leeway

List your dev ports once, in a leeway.json at the top of the repo, and commit it:

{
    "ports": {
        "PORT_WEB": { "port": 5173 },
        "PORT_API": { "port": 3001 }
    }
}

Start servers through leeway:

{
    "scripts": {
        "dev": "leeway vite",
        "api": "leeway node server.ts"
    }
}

Read the ports in code with devPorts, which falls back to the default ports when not run under leeway:

import { devPorts } from "leeway";
import config from "./leeway.json" with { type: "json" };

server.listen(devPorts(config).PORT_API);

Or let the vite plugin set server.port with strictPort, and pass every port to browser code:

import { leeway } from "leeway/vite";
import { defineConfig } from "vite";
import config from "./leeway.json" with { type: "json" };

export default defineConfig({ plugins: [leeway({ config, port: "PORT_WEB" })] });

What you get

  • Stable ranges: a folder keeps its range across runs, so bookmarks and env files keep working.
  • No collisions: ranges come from the machine's pools (21000-29999 unless you set others with leeway pools set), never overlap, and skip ports already taken, even when several leeway runs start at once. When something else takes one of a folder's ports later, leeway names it and leeway move gives the folder a new range.
  • Default ports still work: leeway --default vite runs on the ports in leeway.json, and code reading devPorts outside leeway gets them too.
  • Placeholders: leeway curl http://localhost:{{PORT_API}} puts the port in the command.
  • Cleanup: leeway list shows every range on the machine with what is listening, plus orphans left by deleted worktrees. leeway kill stops a folder's servers, including the watcher that would restart them, and leeway kill --orphans kills every orphan. leeway release gives a folder's range back to the pools.
  • Agents: leeway env --export in a Claude Code SessionStart hook gives every agent session its range up front. The opt-in agent guard makes devPorts refuse default ports in an agent shell that has none.
  • Linux, macOS and Windows. Node >= 22.18.

Docs

The full reference is in packages/leeway/README.md: config, the vite plugin, every CLI command and mode, agent setup, recipes and known limits. To work on leeway, see CONTRIBUTING.md.

License

MIT

About

Per-folder dev port ranges, so parallel worktrees and AI agents never fight over ports.

Resources

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages