Your agents keep starting the same server.
Portly runs it once. Every agent, terminal, and task then reads the same live state: what is running, on which port, under which PID, and whether it is actually healthy.
- Native Swift 6One 4 MB app bundle. No Electron, no Node daemon.
- 127.0.0.1 onlyThe control API can spawn processes, so it never binds to the network.
- Real PTYsColors, prompts, and scrollback survive the way they do in your terminal.
- MIT licensedEvery line that can start a process is readable in the repo.
The cost
Five parallel tasks, five copies of your app.
An agent starts a background job because nothing told it the server already exists. Do that across four terminals and two editors and you get duplicate process trees, fallback ports nobody wrote down, logs split across tabs, and a laptop doing the same work five times over.
5 dev servers · 5 bundlers · 5 file watchers
- codex
pnpm devpid 82104:3000 - claude
pnpm devpid 82391:3001port 3000 in use - cursor
pnpm devpid 82618:3002port 3001 in use - codex
pnpm devpid 82943:3003port 3002 in use - claude
pnpm devpid 83117:3004port 3003 in use
Four of these should not exist.
Agent interface
Inspect first. Launch only if needed.
Portly ships a skill that Claude Code, Codex, and Cursor pick up from ~/.agents/skills/portly. It teaches one rule: read the live state before you spawn anything. The CLI answers in JSON so the agent never has to parse a spinner.
Before anything is launched, the agent reads live state.
$ portly status --json
{ "apiPort" : 7737, "projects" : [ { "color" : "#0A84FF", "icon" : "shippingbox.fill", "id" : "prj_62bafa3d", "name" : "NowStack", "root" : "/Users/you/Developer/nowstack", "servers" : [ { "command" : "pnpm dev", "healthy" : true, "id" : "srv_db263513", "name" : "web", "pid" : 82104, "port" : 3000, "restartCount" : 0, "startedAt" : "2026-08-01T06:44:52Z", "state" : "running", "url" : "http://localhost:3000" } ] } ], "version" : "0.1.0" }
The app
A native window, a menu bar, nothing in the background.
Portly looks and behaves like part of macOS. It is the same supervisor whether you drive it by hand, from the menu bar, or through the CLI.
Capabilities
What the supervisor actually does.
No cloud account, no dashboard subscription, no process manager buried inside an agent framework.
Process supervision
Each server runs through zsh -lc in a real pseudo terminal, so nvm, mise, and your shell PATH behave exactly as they do when you type the command yourself.
PORT · PORTLY=1 · PORTLY_SERVERHealth, not liveness
A TCP probe on the configured port, or an HTTP path with an expected status. A process that is alive but not serving your route is reported unhealthy.
healthIntervalSeconds: 10Crash recovery
Unhealthy servers restart automatically until the retry budget runs out, then park as failed with the last exit code and error still visible.
maxRestartAttempts: 5Port ownership
Ask who holds a port and get the PID, the command, and the user. Portly sends SIGTERM only when you ask it to, and never adopts an unknown process on its own.
portly port 3000 --jsonLogs that outlive the tab
In-memory scrollback for the app plus a rotating file per server, readable from the CLI long after the terminal that started it is gone.
~/.config/portly/logs/One hand-editable config
Projects, commands, ports, and health checks live in a single JSON file that Portly watches. Edit it, commit it, or let the app write it.
~/.config/portly/config.jsonLaunch at login
A per-user LaunchAgent that carries the servers running at handoff into the supervised session, and gives them back when you disable it.
portly forever enableLoopback HTTP API
Every CLI action is a route. Responses are JSON envelopes with ok, data, and error, which is what makes the whole thing scriptable.
GET /status · POST /start
Install
Two lines, then it is yours.
The script builds the app, ad-hoc signs it, installs the portly binary on your PATH, and drops the agent skill in ~/.agents/skills/portly. On Linux, skip the app — use the headless CLI.
$ git clone https://github.com/Melvynx/portly.git $ cd portly && ./build.sh --run ✓ Portly.app installed in /Applications ✓ portly on PATH ✓ skill linked at ~/.agents/skills/portly