demons 0.2.0

Run a project's development commands side-by-side in one terminal
# 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.

```text
$ 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:

```sh
cargo install demons --locked
```

Or install from the source tree:

```sh
cargo install --path . --locked
```

To build without installing:

```sh
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`:

```sh
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:

```toml
[[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:

```sh
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.

```toml
[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:

```sh
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:

```sh
while true; do date; sleep 1; done
```

## Configuration

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

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

Array commands execute directly, without shell parsing:

```toml
[[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.

```toml
[[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

```sh
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:

```sh
make release-check
```

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