A local dev dashboard: one page at http://localhost:4000 that launches, supervises, and opens
all the projects on your machine — instead of juggling a terminal window per app and remembering
each start command.
Each app still runs as its own process on its own port. The dashboard starts/stops them, shows live status, and gives every project its own workspace: a left sidebar with one item per project and per-project tabs — Info, Console (a real pty for a shell or Claude Code), Logs, Docs, Files, Settings, and Preview — plus an Overview board of all projects at a glance. Vanilla HTML/CSS/JS frontend with no build step; the only runtime dependency is Express.
- Real process supervision — each app is spawned in its own process group with a TCP readiness probe, so Stop reliably kills the whole tree and frees the ports. Multi-process apps (backend + frontend) are first-class.
- Overview board — a card per app with a status header (gray stopped · amber starting · green running · red crashed), Start / Stop / Restart, and an Open link once the app's port answers.
- Console tabs — real pty terminal sessions per project (plain shell or a Claude Code session), which survive a page reload and reattach. The sidebar badges show crashed › Claude waiting › session exited › busy.
- Live logs — streamed over SSE per process with a replayed ring buffer, in a tab or a pop-out window.
- Files tab — a lazy project file tree with a CodeMirror 6 editor (atomic writes, stale-file conflict detection, binaries and dependency dirs excluded).
- Docs on every card — each project's Markdown files (
README,LOG,IDEAS, plus anything else in the root ordocs/) open in an in-page viewer/editor. - Git at a glance — branch, dirty count, ahead/behind, and last commit on the Info tab.
- App previews — automatic headless-Chrome screenshots of each running app, shown in the Preview tab and archived as a cheap time-lapse of the project.
- Add projects from the UI — pick a folder, the dashboard auto-detects the framework, start
command, and a collision-free port; tweak and save. Every project is editable and deletable.
Everything writes to one hand-editable file,
apps.json, which also hot-reloads on hand edits. - Shortcuts — open the project folder in Finder, a terminal, or your editor of choice; keyboard-first navigation (⌘K switcher, ⌘1–9 projects, ⌘[ / ⌘] tabs, ⌘E new console).
- Always-on mode — an optional macOS launchd login item keeps the dashboard running in the background, so you can bookmark it or make it your browser home page.
- Node.js 18+ (the server is plain Node + Express, no build step).
- Python 3 for the pty bridge behind the Console tabs (macOS ships it).
- macOS for the extras: Finder/Terminal/editor shortcuts, the native folder picker, the launchd always-on mode, and app previews (headless Chrome/Chromium/Edge/Brave). The core supervision + UI is plain Node and not macOS-specific.
git clone https://github.com/smombartz/mission-control.git
cd mission-control
npm install
npm start # opens http://localhost:4000 in your browserYou'll get an empty dashboard — add a project from the sidebar and the form pre-fills the name, framework tag, start command, and port from the folder. Or seed the registry by hand:
cp apps.example.json apps.json # then edit paths/commands to match your projectsUseful env vars: NO_OPEN=1 npm start skips auto-opening the browser; DASHBOARD_PORT=5000
overrides the port; PROJECTS_DIR=~/Projects sets the folder picker's starting location.
Press Ctrl-C to quit — the dashboard stops every app it started before exiting (no orphan processes, all ports freed).
⌘K switcher · ⌘B sidebar · ⌘0 overview · ⌘1–9 nth project · ⌘[ / ⌘] prev/next tab ·
⌘E new console · ⌘⇧E new Claude session · ⌘⇧D close tab (⌘W is reserved by the browser) ·
⌘S save (docs/files) · / focus the filter · Esc close/clear.
One file is the single source of truth for every app, editable two ways with the same result:
- From the UI — add a project from the sidebar, or edit any project in its Settings tab. Changes hot-register into the running dashboard (no restart needed).
- By hand — edit
apps.json; the dashboard watches the file and reloads it live.
An app is a name, a folder, and one or more processes (command + port), plus optional project
links. See apps.example.json for a single-process and a multi-process
example. The file is git-ignored — it's personal to your machine.
bash scripts/install-launchd.shThis installs a macOS login item (a launchd agent, label com.<your-username>.devdashboard) that
keeps the dashboard running in the background — no Dock app, no popups. Bookmark
http://localhost:4000 (or make it your home page) and it's always loadable. It auto-restarts
on crashes and comes back at the next login. Logs go to ~/Library/Logs/dev-dashboard.log.
LABEL="com.$(id -un).devdashboard"
# restart (e.g. after pulling a new version)
launchctl kickstart -k gui/$(id -u)/$LABEL
# stop now (graceful — stops all managed apps; stays down until next login)
launchctl kill SIGTERM gui/$(id -u)/$LABEL
# uninstall the login item entirely
launchctl bootout gui/$(id -u)/$LABEL && rm ~/Library/LaunchAgents/$LABEL.plistOn Apple Silicon, the supervisor wraps every spawn in arch -arm64 so apps run native arm64 even
though launchd launches in an x86_64-preferring context (otherwise Python apps fail to load their
arm64 wheels).
apps.json— the single registry: top-leveldashboardPort/defaultDocs/docsDirsplus anapps[]array. The dashboard's own entry is a code constant inserver.js, not in the file.apps.order.json— the saved display order, overlaying the registry order at boot.server.js— Express + Server-Sent Events. Three SSE connections per page regardless of tab count: one status stream (which also carries terminal-session events), one multiplexed log stream, and one multiplexed terminal stream with resume offsets (reloads and tab changes never replay what the page already has).lib/supervisor.js— spawns each process in its own process group (detached: true) so Stop can signal the whole tree; tracks state with a TCP readiness probe; buffers logs; registers, updates, and removes apps live.lib/registry.js— load/save/validate ofapps.json(atomic writes), the command tokenizer, the order sidecar, and the on-disk watcher behind live reloads.lib/terminals.js+lib/pty-bridge.py— real pty sessions for the Console tabs, with scrollback resume and Claude Code busy/waiting detection (OSC 9;4 progress sequences).lib/files.js— the Files tab's tree + read/write, with path containment, atomic writes, an mtime precondition, and binary/dependency-dir exclusion.lib/git.js— read-only git status.lib/detect.js— read-only folder inspection (package.json framework,manage.py,run.sh) that powers the add form's auto-fill.lib/preview.js— headless-Chrome screenshot capture of each running app, no dependencies.public/— the whole frontend: vanilla ES modules, no build step. Shared design tokens and card styles live inpublic/styles/; the shell/panel styles inpublic/css/. CodeMirror 6 is one prebuilt, committed bundle (public/vendor/cm6.js, rebuilt viascripts/build-cm6.sh).docs/design-system.html(card) anddocs/design-system-shell.html(shell) are live style guides.
Only runtime dependency: express (plus vendored marked, dompurify, xterm, and CodeMirror
in public/vendor/).
