Skip to content
Mars7attackPublic

About

A CLI cockpit for dev servers across git worktrees

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

11 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🦉 owl

A tiny CLI that shows you which dev servers are running across the git worktrees of your project, on which ports, for how long, and which agent is in each worktree.

When you run several agents (Claude Code, Codex, …) in parallel — each in its own git worktree, each spinning up its own dev server — it gets hard to track who is listening on what. owl answers that, for you and for the agents.

◆ OWL · PygmalionV2    5 UP  1 START  1 ERR  ◌ 27 idle                14:21:09
──────────────────────────────────────────────────────────────────────────────
  STATUS       WORKTREE   BRANCH            UPTIME PORT   URL              AGENT
▌ ● healthy    qa-vc-590  fix/vc-590-ux-r…  1h54m  :3000  localhost:3000   ✦ claude
▏  ▲ 500 ERR   checkout   fix/pay           3m     :5173  localhost:5173   ✦ codex
▏ ◐ starting   api        feat/api          45s    :8000  localhost:8000

A "cockpit" board: a summary chip bar, then dense rows. Status is calm by default (a colored dot + word) and only a 5xx keeps a red pill that screams; pink carries the chrome (brand, worktrees, ports, agent tags); a cyan bar marks the selection.

The dashboard lists only worktrees with a running server; idle ones are summarized as a count (31 idle). Use owl ls -a when you want the full list.

It is observation-first: owl discovers what's already running and never launches or assigns ports. The one state-changing action is killing the selected server from the dashboard (k, with a confirmation).

Install

Requires Go 1.24+ and lsof (preinstalled on macOS; apt install lsof on Linux).

go build -o owl ./cmd/owl      # build a local ./owl binary
go install ./cmd/owl           # install this checkout onto your PATH
# or install onto your PATH:
go install github.com/Mars7attack/owl/cmd/owl@latest

Usage

Run it from anywhere inside your project (it finds every worktree of the repo):

owl                 # live TUI dashboard — keep it in a corner of your screen
owl -i 1s           # refresh faster
owl -C ~/code/app   # point it at another repo

Monitor several projects in one dashboard by repeating -C:

owl -C ~/code/api -C ~/code/web
owl ls -C ~/code/api -C ~/code/web

Save a workspace when you use the same set often:

owl workspace save daily ~/code/api ~/code/web
owl -w daily        # live TUI for the saved workspace
owl ls -w daily     # one-shot JSON snapshot for agents/scripts

owl workspace list
owl workspace show daily
owl workspace rm daily

Saved workspaces live in ~/.config/owl/workspaces.json.

TUI keys: ↑/↓ move · o open in browser · c copy URL · k kill (with confirm) · r refresh · q quit.

For agents and scripts, a one-shot JSON snapshot — fields are named, so an agent reads the keys instead of guessing positional columns. One server object per line keeps it scannable and token-light:

owl ls              # only running servers + a count of idle worktrees
owl ls -a           # list every worktree, including idle ones
{
  "repo": "PygmalionV2",
  "up": 3,
  "idle": 28,
  "servers": [
    {"worktree":"qa-vc-590","branch":"fix/vc-590-ux-review","port":3000,"status":"healthy","uptime":"2h31m","command":"node"},
    {"worktree":"qa-vc-590","branch":"fix/vc-590-ux-review","port":8000,"status":"healthy","uptime":"2h31m","command":"python3.12"},
    {"worktree":"ia-navigation","branch":"refacto/ia-navigation","port":3002,"status":"healthy","uptime":"7m","command":"node","agent":"claude"}
  ]
}

status is healthy, starting, or down, or error with the HTTP code for a 5xx. agent (claude / codex) appears only when a session is running in that worktree. Zero-valued fields are omitted. With -a, idle worktrees are added as {…,"status":"down"} entries and the idle count is dropped.

Workspace snapshots include project context:

{
  "repo": "2 projects",
  "projects": ["api","web"],
  "up": 2,
  "idle": 4,
  "servers": [
    {"project":"api","worktree":"main","branch":"main","port":8000,"status":"healthy","command":"go"},
    {"project":"web","worktree":"checkout","branch":"fix/pay","port":3000,"status":"healthy","command":"node"}
  ]
}

Letting agents use it

The point of owl ls is for agents to call it before they touch a port. Wire it in persistently — a line in your agent instructions (CLAUDE.md / AGENTS.md):

Before starting a dev server, run owl ls to see which ports are already taken, and pick a free one.

— or, in Claude Code, on demand via a /owl slash command (~/.claude/commands/owl.md) that injects that same reminder into a session.

Either way, make clear owl is monitor-only: it reports what's running but never starts a server, so the agent picks a free port and launches it with its own command (owl ls is the only non-interactive subcommand).

How it works

One scan joins two sources:

  1. Listening ports — lsof enumerates listening TCP sockets (port, pid, command).
  2. Worktrees — git worktree list enumerates the repo's worktrees. In workspace mode, this runs once per requested repo, then the process scans are shared across the whole workspace.

Each listening process is mapped to its working directory (also via lsof) and attributed to the worktree whose path is the longest prefix of that directory. Longest-prefix matters because worktrees are usually nested inside the main worktree (e.g. .worktrees/feature/) and dev servers run from a subfolder (e.g. <worktree>/frontend), so a directory legitimately matches several worktrees — the deepest one wins. Worktrees with nothing listening show as down.

Each running server also gets its uptime (from ps), and its worktree is tagged with any Claude Code / Codex session running there — detected by the agent's process name and matched to a worktree the same longest-prefix way.

Status (cheap HTTP probe of each listening port):

status meaning
healthy the server answered with a status below 500 — reachable & serving (redirects and 404 on / are normal)
<code> the server answered with a 5xx — up, but erroring (the code, e.g. 500, is shown)
starting the port is listening but HTTP isn't answering yet (Vite/Next compiling, or a non-HTTP server)
down the worktree exists but nothing is listening for it

Ports in the OS ephemeral range (≥ 49152) are ignored — they're never intentional dev servers (this filters out e.g. Chrome's devtools port).

Architecture

One core, two façades, no daemon:

internal/scan     the scan: lsof ports + git worktrees + ps (uptime, agents), joined; HTTP health (the tested core)
internal/render   the `owl ls` JSON output
internal/tui      the live dashboard (Bubble Tea)
internal/workspace saved workspace config
cmd/owl           flag parsing + dispatch

The scan's inputs (ports, cwd, worktrees, health, uptime, agents) are interfaces, so the join logic is unit-tested with fakes — no real processes or repo needed.

Limitations

  • macOS and Linux only (relies on lsof and ps).
  • Health is HTTP-only: a listening non-HTTP server reads as starting.
  • Attribution needs a process's cwd to live inside a worktree (the normal case for a dev server started in that worktree).
  • Agent detection is heuristic — it matches the claude/codex process name, so an agent launched under a different name may be missed.
  • k kills the listening process (SIGTERM, then SIGKILL); a parent process manager (e.g. pnpm dev) may linger or respawn.

About

A CLI cockpit for dev servers across git worktrees

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages