demons 0.2.0

Run a project's development commands side-by-side in one terminal
demons-0.2.0 is not a library.

Demons

Demons starts all of a project's long-running development commands in one terminal. Each command gets a real PTY and a pane in a small, purpose-built multiplexer.

$ cd my-project
$ demons

It is designed for the common case where a project needs a server, frontend, worker, log tail, or similar commands running together. Demons is not a general-purpose terminal multiplexer or production process supervisor.

Install

Demons supports Linux and macOS.

Install the published crate:

cargo install demons --locked

Or install from the source tree:

cargo install --path . --locked

To build without installing:

cargo build --release --locked

The binary will be at target/release/demons.

Quick Start

Run the configurator from a project root, or from a subdirectory of a project that already has a demons.toml:

demons init

demons init opens the same menu UI used at runtime, but it does not start any tasks. Use the Tasks tab to add or edit tasks, Settings for app-level options, and Exit to save or discard changes. When an existing config is found in the current directory or a parent directory, demons init edits it in place. Working-directory fields validate before they are applied, and Tab completes directory names relative to the config file.

Or create demons.toml yourself:

[[task]]
name = "server"
command = "cargo run"

[[task]]
name = "web"
command = "npm run dev -- --host 0.0.0.0"
cwd = "./web"
env = { BROWSER = "none" }

Then run:

demons

Demons searches the current directory and its parents for the nearest demons.toml. Use demons --config path/to/file.toml to select one directly.

Controls

Demons has two modes:

  • Input mode: keyboard and child mouse input goes to the selected task.
  • Command mode: keyboard input controls Demons.

Demons starts in command mode. Press Alt+J or click the fixed mode button at the left of the footer to switch between command mode and input mode. Clicking a pane selects it without changing modes.

Key Command mode
Arrow keys or h j k l Move focus
Tab / Shift+Tab Cycle panes
f Toggle fullscreen for the focused pane
PageUp / PageDown Scroll the focused pane by one page
Home / End Jump to the top or bottom of focused pane history
y Copy the focused pane's visible text
Y Copy the focused pane's full scrollback
S Save the focused pane's full scrollback to a temp log file
/ Search the focused pane's scrollback
r Restart the focused task and its dependents
R Restart every task
c Clear the focused pane and its scrollback
? Open the menu
q or Ctrl+C Ask to close Demons
Leader Return to input mode

Click a pane to focus it. Click [↻] in a pane header to restart that task. The mouse wheel scrolls pane history unless the child application has enabled terminal mouse reporting in input mode. Footer command buttons are clickable; paired commands like y / Y and r / R are shown as separate buttons. The ? menu button opens a tabbed menu with Help, Tasks, Settings, and Exit sections.

Drag inside a pane to select text. Double-click selects the word under the mouse; hold and drag after the second click to expand by whole words. Triple-click selects the visible line; hold and drag after the third click to expand by whole lines. If the child application has enabled mouse reporting, use Shift-drag to select instead of sending the drag to the child. Dragging above or below the pane scrolls that pane's history while keeping the selection inside the original pane. Right-click or press Ctrl+Shift+C to copy the selection; terminals that support OSC 52 receive it on the system clipboard. Ctrl+Shift+V, middle-click, or right-click with no active selection pastes the last copied Demons selection back to the focused pane in input mode.

In command mode, y copies the focused pane's visible text and Y copies its full scrollback. S saves the focused pane's full scrollback to a temp log file and copies the file path. / opens a focused-pane search prompt; typing jumps to the newest match and shows the current/total match count. Press Enter to go to the previous match, Shift+Enter to go to the next match, or Esc to leave search mode. Press Tab/Shift+Tab or click another pane while the prompt is open to search that pane instead.

Closing Demons is confirmed: press q or Ctrl+C, then press it again to close, or Esc to cancel. In input mode, those keys still go to a running child process; once the focused pane can no longer accept input, they open the same close confirmation.

Because the leader is intercepted, it cannot be sent to a child while in input mode. Set a different leader if an application or window manager needs Alt+J. The Settings tab can change the leader at runtime, and the Exit tab can save or discard that change. Alt+Backtick is available for one-hand use, but some desktops use it for window switching, so it is not the default.

[settings]
leader = "alt-backtick" # also: "tab", "ctrl-b", "ctrl-q", "ctrl-\\"
multi_click_ms = 500    # double/triple-click timing, 150-1000

Testing The Configurator

To test the complete no-config flow without writing into a real project:

cargo build
repo=$PWD
scratch=$(mktemp -d)
(cd "$scratch" && "$repo/target/debug/demons")

Demons will offer to open the configurator and will write only inside the temporary directory. A simple test command for a task is:

while true; do date; sleep 1; done

Configuration

String commands run through $SHELL -c (falling back to /bin/sh):

[[task]]
name = "api"
command = "RUST_LOG=debug cargo run"

Array commands execute directly, without shell parsing:

[[task]]
name = "api"
command = ["cargo", "run", "--bin", "api"]
cwd = "."
env = { RUST_LOG = "debug" }

Tasks can depend on other tasks. A dependent task starts only after all of its dependencies have started, then waits its own optional start_delay. Restarting a task also restarts its dependents. While a task is waiting for a delayed start, the pane body shows the countdown to launch.

[[task]]
name = "server"
command = "cargo run"

[[task]]
name = "web"
command = "npm run dev"
depends_on = ["server"]
start_delay = "3s"

Task names must be unique. Working directories are resolved relative to the directory containing the config file. Unknown keys and invalid directories are reported before any task starts.

logging, watch, run_on_change, and repeat are reserved schema fields. Demons rejects reserved task fields when set, and rejects logging = true, so a configuration never silently promises behavior that is not implemented.

Process Behavior

Tasks start concurrently in separate process groups. Restart and shutdown signals apply to each full task process tree. On quit, Demons sends SIGTERM, waits up to two seconds, then sends SIGKILL to anything still running. External SIGINT, SIGTERM, and SIGHUP signals trigger the same cleanup.

The VT renderer supports colors, cursor movement, alternate screens, bracketed paste, application cursor keys, and common terminal mouse protocols. Terminal features outside the VT100/xterm model, such as graphics protocols, are not rendered.

Development

make test      # format, clippy, and tests
make build     # release build

Before publishing or cutting a release, run the packaging check. It runs the full test suite and then validates cargo package in an isolated target directory so the workspace target/ is never polluted by the package build:

make release-check

See RELEASING.md for the version bump and publish checklist. See SPEC.md for the v1 behavior and product boundaries.