Local control plane for developers. Ports, processes, and dev servers in one browser tab — see what's running on your machine, kill what's stuck, never fight over :3000 again.
Optional adapters surface AI-coding sessions (Claude Code, Aider) the same way, so you can resume any chat from any project without hunting through ~/.config.
Cross-platform (Windows-first, Mac-portable). Zero npm dependencies. Single localhost service. Pure-vanilla HTML — no build step, no React, nothing to lock down.
Most dev pain on a single machine isn't the code — it's the surrounding state:
- "Why is
:3000in use?" → was it that stale Next.js? a Vite from yesterday? Discord? Activity Monitor doesn't tell you which project owns it. - "I'll just kill node…" → kills the wrong one, breaks two other apps.
- "Should this Flask app be on
:5000or:8000?" → flips a coin, conflict, surprise restart cascade.
command-deck answers each of those in one glance.
| Panel | What | Killer feature |
|---|---|---|
| Processes | Every TCP listener on the box, with project name, stack (next/vite/flask/uvicorn/django/electron/...), full command line, parent process, executable path, RSS, user. Sorted: your projects → dev tools → unmapped → system. | Project inferred from ports.json registration or command-line path (Dev/GitHub/<category>/<project>/...) — you see "whatdayisit / next / 4575M" not just "node.exe / 3000". |
| Ports | A registry mapping project name → assigned TCP port. Edit inline, save to ~/.claude/ports.json. |
Two Next.js apps will never both want :3000 again. The dev wrapper reads this and refuses to start when an assigned port is held. |
| Sessions (optional) | AI-coding sessions across providers via pluggable adapters. v1 ships Claude Code + Aider; Cursor and Codex are scaffolded. | One UI for "every AI-assisted session you've run on this machine," not one app per tool. |
| Settings | Refresh mode (highlight / reload-tabs / restart-server), poll intervals, Caddy proxy mode toggle. | All persisted to settings.json; instantly applied across open tabs over SSE. |
git clone https://github.com/itsSabbir/command-deck.git ~/Dev/GitHub/tools/command-deck
cd ~/Dev/GitHub/tools/command-deck
npm start # serves on http://127.0.0.1:7890Pin the tab. Optionally install as a desktop app (Chrome/Edge → ⋮ → Install) for a window without browser chrome. Autostart on login: see docs/autostart.md.
bin/dev.sh (bash) and bin/dev.ps1 (PowerShell) read ports.json, set the right env var per stack (PORT, NEXT_PUBLIC_PORT, VITE_PORT, FLASK_RUN_PORT, UVICORN_PORT), refuse to start when the assigned port is already held, and register/deregister the PID with the deck.
cd ~/Dev/GitHub/web/whatdayisit
~/Dev/GitHub/tools/command-deck/bin/dev.sh # detects Next.js, uses port 3002Drop this in your shell profile to alias dev globally:
alias dev='bash ~/Dev/GitHub/tools/command-deck/bin/dev.sh'Toggle in Settings → Caddy → "Generate Caddyfile" and the deck writes a config that maps every entry in ports.json to a *.localhost hostname. Run caddy run --config <generated-Caddyfile> and your projects become:
http://whatdayisit.localhost # → 127.0.0.1:3002
http://ossc.localhost # → 127.0.0.1:5001
http://command-deck.localhost # → 127.0.0.1:7890
No more port juggling in the URL bar. Caddy isn't bundled — install separately (caddyserver.com/docs/install).
The Sessions panel is fed by adapters in server/adapters/:
- claude — tails
~/.claude/projects/*/*.jsonl. Click "resume" → copiesclaude --resume <id>. - aider — walks configured project roots for
.aider.chat.history.md. Click "open" → opens the project dir. - cursor (scaffolded) — TODO: parse
~/AppData/Roaming/Cursor/User/.... - codex (scaffolded) — TODO: parse Codex CLI logs.
Each adapter is a 50-line file. PRs welcome for new tools.
If you don't use AI coding tools, the panel just shows an empty state. The deck's value (ports + processes) is unaffected.
If you use Claude Code, add hooks/notify-deck.sh as the third entry in your global Claude Stop chain so the deck knows when an agent finished and which project's files just changed:
When a project's files change (Stop hook fires, or the deck detects mtime bumps):
- highlight (default, safest) — project gets a "changed" pill in the deck. You click "Refresh" yourself. Never disrupts an in-flight form.
- reload-tabs — broadcasts
location.reload()to any browser tab that loaded/reload-injector.js. - restart-server — kills and restarts the dev process via the remembered command (only works for processes started via the
devwrapper).
Set in the deck UI under Settings, persisted to settings.json.
~/.claude/
├── ports.json # project → port registry (synced cross-machine if you use sync-claude.sh)
├── command-deck/
│ ├── processes.json # PIDs registered by dev-wrapper
│ └── Caddyfile # generated by Caddy proxy mode (when enabled)
└── projects/ # Claude Code session source (read-only to deck)
The deck never writes outside ~/.claude/ and <repo>/settings.json.
- Binds to
127.0.0.1only. Do not expose to LAN — the deck can kill arbitrary processes on its host. - No auth (none needed for localhost-only).
- Pure-Node, zero npm deps → trivially auditable in one afternoon.
An Electron tray app lives in tray/. It launches the deck server in the background, shows a ▶ icon in the taskbar/menu bar, and lets you show/hide the window, reload, or restart the server without touching a terminal.
cd tray
npm install # ~150 MB, Electron only
npm startThe tray variant starts the deck server automatically on launch and keeps it running as a child process. Quit from the tray menu to shut both down cleanly.
v0.3 — ports + processes are battle-tested; AI session adapters are usable but young; Caddy proxy mode is generate-only (managed mode planned); tray wrapper ships. Roadmap in docs/roadmap.md.
MIT.