Skip to content

TUI

The TUI is OpenTUI (@opentui/core + @opentui/react). It attaches to the supervisor and never owns child processes.

bash
cd examples/demo-platform
npx @amr-m-abdelgawad/devctl@latest

From a source checkout (bun on PATH):

bash
cd examples/demo-platform
bun run ../../app/src/bin.ts

If a supervisor session already exists, the TUI attaches to it. Preferences: tui.json / DEVCTL_TUI_CONFIG — see Building from source.

First run

With no .devctl configuration the TUI opens setup: “No configuration found. Would you like to run setup? [Enter] Setup [Esc] Exit”. Enter starts the same 9-step wizard as devctl setup (OpenTUI fields, then write and attach the daemon — no process restart). Invalid existing YAML still refuses overwrite.

If a .devctl/config.yaml exists but fails to parse or validate, the TUI shows Configuration error with the actual error instead — pressing Enter here does not run setup, since that would silently overwrite the file the error is about. Fix the file and restart devctl, or run devctl config validate for the same error from the CLI.

When services exist but none are running, the dashboard empty state:

  • enter starts the default profile (first profile name alphabetically) after a plan overlay
  • n / x start or stop the highlighted row (or the space-selected set)
  • a lifecycle panel shows start and stop waves; later start waves wait for health and do not run if a wave fails
  • the panel stays open until esc so you can read the result
  • o picks a profile, then confirms start

Quit

q / /exit / esc twice:

shutdown.stop_services_on_exitBehavior
trueStop managed services and leave
falseDetach immediately
unsetConfirm: enter stops services, d detaches (daemon stays), k stops the daemon and leaves services (devctl down --keep-services), esc stays

Detach (d) leaves the supervisor running. /down --keep-services (or quit k) stops the supervisor and persists PIDs so a later start can adopt them. /down stops services and the supervisor. /stop only stops selected services.

Interaction model

Keyboard-first. Chords use command on macOS and ctrl on Linux and Windows. Help, the status bar, and empty-state hints label the modifier for the OS you are on.

InputWhat it does
/Command overlay — ranked as you type (name, alias, fuzzy, then description). /start api still lists start. / to move, enter to run
command+p / ctrl+pSame command overlay as /
command+x / ctrl+xLeader key (2s), then a shortcut — keymap overlay
?Grouped help — j/k scroll when the list is taller than the terminal
tab / shift+tab / 14Cycle or jump the four nav tabs. Other screens are /auth, /credentials, /doctor, /config, /profiles, /setup, /stats, /settings, /mcp. On a secondary screen, tab returns to the dashboard. When the strip is wider than the terminal it slides ( ).
s l a p d c uDirect letter nav when no overlay owns keys (services, logs, identity, proxy, doctor, config, setup)
rRefresh snapshot (doctor r re-runs checks)
RRestart selected services
j k / arrowsMove selection
enterStart (empty dashboard) or open service detail
spaceMulti-select a service
escBack / close overlay. Twice (when nothing else is open) asks to quit
fFocus log search (g jumps to latest). Remap with keybinds.search
zExpand logs to fill the terminal. z or esc exits
wCycle log wrap: wrap every line (default), clip with ellipsis, or unwrap only the selected row
command+c / ctrl+cCopy the highlighted selection (drag with the mouse). Remap with keybinds.copy
command+= / ctrl+= (and - / 0)Display size (padding/row height, not the terminal font)
esc escTwice to quit when no overlay or back target is open. The OS copy chord does not quit
MouseClick nav, click a service, scroll logs (toggle in Settings)

The status bar only lists keys that work on the current screen. There is no idle command row — / and the OS palette chord open the command overlay.

  1. dashboard · 2. services · 3. logs · 4. proxy

Everything else is a slash command (or a letter jump): /auth, /credentials, /doctor, /config, /profiles, /setup, /stats, /settings. MCP is /mcp, /agent, or Settings → MCP → Settings page.

Screens

  • Dashboard — services, proxy, live log tail. Identity lives on /auth; ADC status is in the header. When nothing is running, a last session panel shows leftover PIDs from the previous supervisor (same data devctl status prints when the socket is down)
  • Services — list plus a live inspector: status chips, two-column facts, then a scrollable resolved env pane (dotenv, profile, secrets, plugins, runtime ports). Narrow terminals stack the panes. enter opens the full detail screen
  • Service detail — same inspector; env pane is focused so j/k scroll. /reveal shows secrets. n/x/R/l
  • Logs — ANSI color codes are stripped so wrap uses visible width; messages wrap to the pane with OpenTUI word wrap. w cycles wrap all / clip / wrap selected. \\ / /split opens a second pane on the same live stream (independent service filter, shared search). /trace <id> or Enter on a log details request id jumps search to that id. See Logs
  • Identity — user, project, source, ADC, gcloud, configured SAs, impersonation AVAILABLE/UNAVAILABLE, IAP (no tokens). /auth login suspends the TUI, runs gcloud auth application-default login on the real terminal, then restores the TUI. /auth logout revokes ADC without leaving the screen
  • Credentials — store backend and entry names only. Tokens stay in the OS keychain or ~/.devctl/credentials
  • Proxy — status + routes (match and upstream wrap instead of clipping); click a route for full details. n start / x stop
  • Doctor — re-runs on every visit; ✓ / ! / ✗ with hints. enter on a busy host port asks to stop that process; it never offers to kill the Docker or Podman daemon. r reruns
  • Config — merged view including tasks. v / /buffer opens a validate/save overlay on cfg.configPath (invalid YAML is not written; esc discards). e / /edit still opens $EDITOR / DEVCTL_EDITOR. /diff shows provenance (devctl config diff). /reload re-reads after an external edit
  • Profiles — members; enter selects and offers start
  • Setup — onboarding checklist. First-run with no config still opens here
  • Settings — grouped prefs: theme, display size, mouse, leader timeout, MCP settings page, about, reset. / writes the highlighted cycle or toggles mouse. Reset asks before restoring defaults. Saves to ~/.devctl/tui.json unless DEVCTL_TUI_CONFIG is set
  • MCP — Listen [ ON ] / [ OFF ], port stepper ‹ N ›, per-agent Copy JSON / Copy TOML, and a Tools list grouped by purpose (inspect, logs, diagnostics, control, setup) with each tool marked read or write; space enables or disables the highlighted one, all on by default. Off by default. See MCP

/reveal toggles secret env values for this session only. The header shows secrets shown.

Slash commands

text
/start [service…]     start selection, args, or the current profile
/stop [service…]
/down [--keep-services]  stop the supervisor (and services unless --keep-services)
/restart [--cascade|-c]  named services only; cascade also restarts dependents
                        R with dependents: Enter = named, c = cascade
/run [task]           one-off task; empty /run opens a picker. Output is in Logs under task:<name>
/exec [service] -- <command…>
                      empty /exec opens a service picker, then type the command
/exec <service> --print-env [--reveal]
                      resolved env (dotenv, profile, secrets, plugins, ports), not config-only vars
/logs /services /auth /credentials /proxy /mcp /doctor /config /profiles /setup
/stats                system and service statistics (sparklines when the supervisor has samples)
/split                second log pane (`\\`); `|` focuses the other pane
/trace <id>           set log search to a request_id / trace_id
/dashboard            return home
/themes [name]        picker with live preview; enter saves to ~/.devctl/tui.json
/settings             theme, mouse, display size, MCP page
/filter               toggle ERROR+
/pause                freeze the live log stream
/reveal               show or hide secret env values
/wrap                 cycle log wrap (all / clip / selected)
/copy                 copy visible logs to the clipboard
/export [path]        write filtered logs to ~/.devctl/exports (or the given path)
/exports              open the export folder
/regex /since /until /history /edit /buffer
/clear
/refresh
/reload               reload .devctl
/diff                 winning config sources and what they shadowed (`devctl config diff`)
/import compose [path] [--write]
/daemon               supervisor bootstrap stderr (`devctl daemon logs`)
/auth login|logout|refresh
/update               install a newer GitHub Release when the method is known (npm/Homebrew)
/version              current version, then the same update check (does not install)
/exit /quit /q

Aliases include /up, /identity, /creds, /agent, /init, /home, /prefs, /task, /provenance, /bootstrap. /down is no longer an alias of /stop.

Leader key

Default leader is command+x on macOS and ctrl+x elsewhere (2 second timeout). Then:

text
n start    x stop    R restart (c cascade if dependents)    s services    l logs
a auth     p proxy   d doctor     c config      o profiles
t themes   e export  r refresh    i setup       h dashboard
q quit     z fullscreen

Override in tui.json (keybinds) or DEVCTL_TUI_CONFIG.

Layout

  • Header — product + version as text, then project and profile; chips only for running count, live proxy, MCP when on, ADC, and secrets-shown
  • Nav — the four primary tabs; the active tab is highlighted, not filled
  • Body — dashboard or a focused screen
  • Command overlay/ and command+p / ctrl+p open the same grouped list with a real OpenTUI input
  • Status bar — live/paused, last human result, contextual keys

Status is never color-only: healthy, running, ! warning, failed, stopped.

Themes

/themes opens a picker with live preview. Built-ins:

  • Product: devctl (default), ember
  • Common dark: tokyonight, catppuccin, nord, gruvbox, kanagawa, dracula, onedark, monokai, rose-pine, everforest, github-dark, iceberg, ayu-dark, oxocarbon, night-owl
  • Light: catppuccin-latte, solarized-light
  • Other: solarized-dark, terminal (black + VGA ANSI chrome; aliases ansi, xterm, console), system (follows macOS AppleInterfaceStyle / COLORFGBG; light uses Solarized Light)

Aliases: mocha → Catppuccin Mocha, latte, one-dark, solarized (dark), rosepine, github, ayu, night owl.

MCP agent chips use brand colors (Claude terracotta, Cursor blue, Kilo gold, Codex green) with light/dark variants.

Released under the MIT License.