Skip to main content
Stavros Apostolou
[ Lab Project 01 ]

Pier

A terminal dashboard for every local dev server, even the ones it didn’t start.

[ TUI ][ TypeScript · Ink · React · Node · execa ]

Problem

A normal day of local development spreads several dev servers across terminal tabs: a Next.js app here, a Vite build there, a Python server for a quick script. You lose track of what’s actually running, forgotten servers quietly leak memory, and the one error that matters scrolls out of view minutes before you look for it. There’s no single place that shows everything listening on localhost.

Solution

Pier is a terminal dashboard that scans for anything listening on the common dev ports, not just the servers it launched itself, and lists each one with its port, working directory, and live RAM and CPU. It tails their logs in one place and classifies each line as an error, warning, or request, so problems surface instead of scrolling away. Servers that have gone quiet and stopped serving are stopped automatically, so nothing lingers after you’ve moved on.

Journey

I built Pier as an Ink app (React, rendered to the terminal instead of the browser) and coded it with Claude Code over a short, focused burst. The whole design turned on one decision: discover servers rather than only manage the ones Pier started. That single choice is what makes it useful the moment you open it, and it’s also what made it hard: instead of owning the processes, Pier has to read the operating system through tools like lsof, ps, and pgrep, and keep the picture live with background polling loops running at different cadences.

Challenges

  1. /01

    A modern dev server isn’t one process. A Next.js server shows up in lsof as a whole tree: a shell that spawns node, which spawns a compiler, which spawns workers. A naive scan lists one server as four and reports a fraction of its real memory.

    Fix → Pier walks the descendant process tree with pgrep -P, collapses the whole tree back to a single dashboard entry, and sums RAM and CPU across all of it, so a forking server reports the footprint you actually care about.

  2. /02

    Auto-stopping idle servers sounds simple until you define “idle”. Judging by log output alone would kill a server that’s silently serving requests, and spare a chatty one that nobody’s using while you refactor in peace.

    Fix → A server counts as idle only when two things are true at once: no log output and no active TCP connection to its port for the timeout window. Silence plus no traffic is idle; either one alone is not.

  3. /03

    The best feature, showing servers Pier didn’t launch, comes with a catch: macOS won’t let you attach to another terminal’s stdout after the fact, so a discovered server has no logs to tail.

    Fix → “Claim & restart” closes the gap: Pier reads the original command straight off the running process with ps, kills the external process, and respawns it under its own management, so from that moment on the logs stream.