@laurence79/wireit-tui
v0.2.0
Published
A terminal UI for bringing up and monitoring a wireit-managed dev stack
Downloads
282
Readme
wireit-tui
A terminal UI for bringing up and monitoring a wireit-managed dev stack. Visualise the package + script tree, start scripts (and watch their dependencies start), tail per-service logs, and bounce a service when it wedges — driving wireit's analyzer + executor in-process. Shaped like foreman/overmind/Tilt, but over wireit's existing dependency graph.
Status: v1. See
docs/tui-spec.mdfor the full design.
Install
@laurence79/wireit-tui is a separate package from @laurence79/wireit, so
its render code never bloats a base install. It declares @laurence79/wireit as
a peer dependency:
npm install --save-dev @laurence79/wireit-tuiRun
From an interactive terminal anywhere inside your repo:
npx @laurence79/wireit-tuiwireit-tui discovers your packages, analyzes every wireit-configured script,
and presents the tree. Pick what to start, watch it come up, and tail its logs.
It renders with Ink (React for the
terminal) and runs on plain Node ≥22. It needs a real TTY.
Options
| Flag | Description |
| ---------------------- | ------------------------------------------------------------------------------ |
| -p, --package <glob> | Restrict discovery to these dirs/globs (repeatable). Overrides auto-discovery. |
| --cache <mode> | Output caching: none (default) or local. |
| --no-auto-restart | Disable file-change auto-restart for all services. |
| -h, --help | Show help. |
Keys
| Key | Action |
| -------------------- | -------------------------------------------------------- |
| ↑/↓ (or k/j) | Move selection |
| enter / space | Start the selected script; stop a running service |
| s | Stop the selected script |
| r | Restart (bounce) the selected service in place |
| a | Toggle file-change auto-restart for the selected service |
| pgup / pgdn | Scroll the focused log; g / G jump to top / bottom |
| q / Ctrl-C | Quit (stops everything it started) |
Status glyphs
○ stopped ◐ starting ● ready/running ✓ ran ✗ crashed/failed
◆ service · standard script ⟳ armed for file-change auto-restartHow it works
wireit-tui is an interactive driver, not a logger: it owns the run loop,
asks wireit's analyzer for the graph, starts scripts on a keypress, and renders
the resulting event stream.
┌─ wireit-tui (this package) ──────────────────┐
│ Ink (React) view │
│ per-script log ring buffers │
│ tree state + keybindings (the engine) │
└──────────────┬────────────────────────────────┘
in-process │ Wireit.session(...) ← the only contract
┌──────────────▼─ wireit (lean core) ───────────┐
│ Session facade ── encapsulates ──┐ │
│ Analyzer · Executor · forwarding Logger │ │
└────────────────────────────────────────────────┘Everything runs in one Node process and one wireit Executor, rooted at
a synthetic no-command "aggregator" whose dependencies are the set of scripts
you've started. Because there is a single executor:
- Shared dependencies run once. Start two services that share a database service and the database starts exactly once.
- Persistent services are carried forward, not bounced, across actions: starting a new service adopts the already-running ones rather than restarting them.
bouncerestarts just one service. Its process is aborted and dropped from the carry-forward map, so on the next run only it re-establishes while its siblings keep running.
Startable units
Any wireit-configured script can be started:
- A service (
"service": true) stays up and is restart-/bounce-able. - A standard script runs once; its own service dependencies are ephemeral (started → run → stopped) — the "invoke a dev-time function" case.
Auto-restart on file change
Fully-tracked services (those whose inputs wireit knows via files) are armed
for file-change auto-restart by default (the ⟳ badge). When a watched input
changes, the service's fingerprint changes and wireit restarts it to the latest.
Toggle per-service with a, or globally with --no-auto-restart. Services whose
inputs wireit can't see are never auto-restarted; bounce them manually with r.
Failure handling
The session runs with watch semantics and failureMode: 'continue', so a single
failing script never tears down the session or its sibling services. A persistent
service that exits unexpectedly is surfaced as crashed (red) — it is not
supervised/auto-restarted (that invites crash loops that mask real errors); fix
the code (auto-restart) or bounce it (r).
Rendering
The view is built with Ink — React
for the terminal, with Yoga flexbox layout — rendered into the alternate screen
buffer. The state machine (TuiEngine: tree state, status, per-script log
buffers, keybindings) is dependency-free and produces a plain ViewModel; only
the Ink App component imports ink, so the renderer stays isolated.
Log handling: per-script ring buffers normalize output (SGR colour preserved,
\r as in-place overwrite, cursor/alt-screen escapes dropped) and the focused
pane is windowed to the visible height before render — so Ink only ever lays
out a screenful regardless of log volume, and a child's own ANSI colour survives
into the pane. A bounce inserts a ─── restarted ─── marker instead of clearing
history. Because wireit spawns all children piped (never inheriting the TTY), the
TUI owns the screen cleanly; a faithful per-pane VT mode would need a PTY on
wireit's spawn path, which is deferred.
Programmatic API
The same building blocks are exported for embedding or testing:
import {Wireit} from '@laurence79/wireit'; // the Session facade
import {discoverPackageDirs, TuiEngine, runTui} from '@laurence79/wireit-tui';
const session = await Wireit.session({
packageDirs: await discoverPackageDirs(process.cwd()),
});
session.events.subscribe((event) => {
/* every wireit Event, carrying its `script` */
});
await session.start({packageDir, name: 'serve'});
await session.bounce({packageDir, name: 'serve'});
await runTui(session); // or drive TuiEngine yourself behind any ViewLimitations (v1)
- No persisted selection / named profiles. You pick what to start each launch.
- No live tree updates. The graph is analyzed once at launch.
- Line-buffer fidelity, not a full VT emulator (no alt-screen apps in panes).
- No cross-terminal service sharing. Services are private to the session.
kill -9of the TUI can orphan detached services (pre-existing wireit spawn behaviour).
