This repository has been archived on 2026-10-08. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
maestro/docs/INSTALL.md
T
Miguel PalhasandClaude Opus 5 b1527f5fc1 fix(maestro): align TUI and web shortcuts
Spawn was `s`/`S` in the TUI and `n`/`N` in the web client; the TUI now
uses `n`/`N` too. The web sidebar gains `c`, `r` and `x` — collapse,
rename, kill — which the TUI already bound.

docs/SIDEBAR.md now carries the shared table, so a new binding has one
place to land on both clients.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 08:25:35 +01:00

304 lines
11 KiB
Markdown

# Installing Maestro
The repo is private, so grab `install.sh` while signed in to gitea, then run it:
1. Open <https://git.naps.pt/naps62/maestro/src/branch/main/install.sh>, click
**Raw**, save as `install.sh`. Or from any existing clone:
```sh
git show origin/main:install.sh > install.sh
```
2. Run it:
```sh
chmod +x install.sh && ./install.sh
```
The bootstrap script builds `maestro-installer` into a temp directory, runs the
wizard, and cleans up — only the unified `maestro` binary ends up persisted in
`~/.cargo/bin`.
**Updating is the same command.** Re-running `./install.sh` detects an existing
systemd unit, refreshes it, and runs `systemctl --user restart maestro` instead
of `start`. No `--update` flag needed. **The restart keeps your sessions:** the unit sets
`KillMode=process`, so systemd signals only the daemon — the tmux server (and
every session) survives, and the new daemon re-adopts them on start. A unit
written before this default lacks the line; re-run `./install.sh` to refresh it.
Inside the script, `cargo install --git https://git.naps.pt/naps62/maestro …`
uses your already-configured git credentials for this host (HTTPS helper or
SSH agent). If only SSH is set up:
```sh
MAESTRO_REPO_URL=ssh://git@git.naps.pt/naps62/maestro ./install.sh
```
Pass flags through to the wizard after `./install.sh`:
```sh
./install.sh --yes --no-systemd
```
The wizard:
1. Runs `cargo install` for the unified `maestro` crate (pinned to the
committed `Cargo.lock` via `--locked`). The same binary hosts the CLI,
the per-host daemon (`maestro daemon`), and the TUI, gated by Cargo
features.
2. On systemd hosts, optionally writes a user unit at
`~/.config/systemd/user/maestro.service`, reloads systemd, and offers to
enable + start it.
The binary lands in `~/.cargo/bin/maestro` — make sure that's on your `$PATH`.
## Nix / NixOS
The repo is a flake. It exposes the `maestro` package, the SPA bundle it
embeds, and a Home Manager module:
```sh
nix run git+https://git.naps.pt/naps62/maestro -- list # the maestro binary
```
`packages.maestro` is the unified CLI + daemon + TUI binary, with the browser
SPA embedded, wrapped so `tmux` and `git` are on its `PATH`. The Nix build
points `MAESTRO_WEB_DIST` at `packages.webFrontend`, so `build.rs` skips pnpm
entirely and no network fetch happens mid-compile. Set that variable yourself
to reuse a bundle built elsewhere; leave it unset and the build falls back to
running `pnpm install && pnpm build` in `web/`.
Home Manager, with the daemon running as a *user* unit (same trust boundary as
the tmux server it wraps):
```nix
{
inputs.maestro.url = "git+https://git.naps.pt/naps62/maestro";
# in your Home Manager configuration:
imports = [ inputs.maestro.homeManagerModules.default ];
services.maestro = {
enable = true; # installs the binary, runs `maestro daemon`
settings = {
daemon.bind_addr = "127.0.0.1:7070";
};
};
}
```
`settings` is written verbatim to `~/.config/maestro/config.toml` in the schema
below; every field has a default, so `{}` writes no file at all. The daemon
serves the browser UI off that same address, and there is no auth layer — bind
it to a LAN or VPN address only on a trusted segment, never a public IP.
`services.maestro.daemon.enable = false` installs the CLI and TUI without
running a local daemon, for a host that only talks to another machine's.
The daemon unit carries the same `KillMode=process` as the wizard-written one,
so `systemctl --user restart maestro-daemon` leaves the tmux server and every
session alive.
## Thin builds for single-role hosts
The default install pulls in everything (`cli` + `daemon` + `tui` + `web`).
Single-role hosts can opt out of unused dependencies:
```sh
# Daemon-only host — no CLI or TUI:
cargo install --git https://git.naps.pt/naps62/maestro \
--no-default-features --features daemon --locked maestro
# CLI-only client (no local daemon, no TUI):
cargo install --git https://git.naps.pt/naps62/maestro \
--no-default-features --features cli --locked maestro
```
Neither of those builds the browser UI: `web` is left out, so pnpm and node
aren't needed. Add `--features daemon,web` to put it back.
The wizard always installs the default-feature build.
## The terminal UI: `maestro tui`
`maestro tui` is the keyboard-and-mouse driven terminal UI client. It ships
in the default feature set, so a vanilla `cargo install maestro` builds it.
A bare `maestro` opens the TUI too — the missing subcommand defaults to `tui`
(#119), so `maestro --daemon-url http://host:7070` works as well. Builds
without the `tui` feature have no UI to open and print help instead (exit 0).
Drop the `tui` feature to skip it on hosts that don't need the UI surface
(`--no-default-features --features cli,daemon`).
It talks to the local `maestro daemon` over `http://127.0.0.1:7070` by default.
Override the daemon URL with `--daemon-url` or `$MAESTRO_DAEMON_URL`.
The `[tui]` section in `~/.config/maestro/config.toml` (or whatever path you
pass via the global `--config` flag) controls the keymap. Shipped defaults
cover every v1 action — only override what you want to change:
```toml
[tui]
theme = "dark" # see the theme list below
leader = "ctrl-a" # one byte that escapes Insert mode
chord_timeout_ms = 1000 # how long to hold a partial chord like `ctrl-w`
[tui.keymap.normal]
"i" = "EnterInsert"
"n" = "OpenSpawnModal"
"N" = "FastForwardSpawn"
"x" = "KillHighlighted"
"r" = "RenameHighlighted"
"?" = "ShowHelp"
"ctrl-w h" = "FocusSidebar"
"enter" = "AttachHighlighted"
"pageup" = "ScrollPanePageUp"
"pagedown" = "ScrollPanePageDown"
[tui.keymap.insert]
# (leader is added automatically as LeaveInsert; only override here when you
# want extra Insert-mode bindings)
```
`theme` selects the complete TUI palette. `dark` is the default: Maestro's
graphite palette, near-neutral chrome where the activity colours are the only
hue on screen; `light` is its light peer; `ansi` uses the terminal's named
ANSI colours for terminals without truecolour. Five house presets recolour
the graphite base with an accent, and the remaining presets adapt well-known
editor themes — in both groups individual colours are lifted or darkened
where they would fall short of the WCAG AA contrast floors the palette tests
assert:
| name | based on |
|-------------------|------------------|
| `ice-cyan` | graphite base, signal-cyan accent |
| `periwinkle` | indigo base, blue-violet accent |
| `amber-phosphor` | warm graphite, CRT-amber accent |
| `ember` | warm graphite, orange-red accent |
| `orchid` | violet base, muted violet accent |
| `solarized-dark` | Solarized Dark |
| `solarized-light` | Solarized Light |
| `catppuccin` | Catppuccin Mocha |
| `github-dark` | GitHub Dark |
| `github-light` | GitHub Light |
| `tokyo-night` | Tokyo Night |
| `gruvbox` | Gruvbox Dark |
| `nord` | Nord |
| `dracula` | Dracula |
An unknown name is rejected as an `[tui].theme` configuration error.
The full action vocabulary is the `Action` enum in
`crates/maestro-tui/src/action.rs`.
## The browser UI
The daemon serves the SPA off its own listener. Open
`http://127.0.0.1:7070/` — or whatever `[daemon].bind_addr` is — and the
browser talks to the same process the CLI and TUI do. One binary, one port,
nothing extra to install or start.
**Build prerequisite.** The `web` cargo feature is on by default and embeds the
bundle at compile time: `build.rs` runs `pnpm install --frozen-lockfile &&
pnpm build` in `web/`, so pnpm and node must be on `PATH`. Two ways around
that — point `MAESTRO_WEB_DIST` at a bundle built elsewhere, or build with
`--no-default-features` and pick the features you want (see *Thin builds*
above).
**Trust model.** There is no authentication layer: anyone who can reach
`[daemon].bind_addr` can load the UI and drive every session on the host.
Pick `127.0.0.1` for local-only, a WireGuard/LAN address for one-segment
access, and never a public IP — the network perimeter is the only boundary
Maestro has.
**Upgrading from the two-binary install.** Re-running `./install.sh` removes
the old `maestro-web.service` unit for you; nothing to undo by hand. A leftover
`[web]` block in `~/.config/maestro/config.toml` is ignored rather than
rejected, but it no longer does anything — move the address you care about to
`[daemon].bind_addr`.
## Flags
| Flag | Purpose |
|---|---|
| `--git <url>` | Install from a different git repo (default: this one). |
| `--branch <name>` | Pin a branch with `--git`. |
| `--path <dir>` | Install from a local workspace checkout (`--path` + `-p maestro`). |
| `--no-locked` | Drop `--locked` from `cargo install` (resolves a fresh dep tree). |
| `--exec-start <path>` | Override the systemd unit's `ExecStart` (default `~/.cargo/bin/maestro daemon`). |
| `--no-systemd` | Skip the systemd step even if detected. |
| `-y`, `--yes` | Non-interactive: install the binary + drop systemd unit + reload + enable, but **don't** auto-start the unit. |
## Where tmux sockets land
`maestro daemon` defaults `TMUX_TMPDIR` to `$XDG_RUNTIME_DIR` (= `/run/user/$UID`)
so a plain `tmux ls` in your shell — which typically uses the same env var
— sees maestro-managed sessions. Override in
`~/.config/maestro/config.toml`:
```toml
[daemon]
tmux_tmpdir = "/tmp"
```
Pick `/tmp` if the daemon must outlive your login session and you can't run
`loginctl enable-linger` (the runtime dir is wiped on logout, taking the
tmux sockets with it). The env var `MAESTRO_DAEMON_TMUX_TMPDIR` takes
precedence over the file.
## What the systemd unit looks like
The wizard writes a *user* unit (`systemctl --user`), since `maestro daemon`
runs per-user and uses tmux sockets under `$XDG_RUNTIME_DIR`. Body:
```ini
[Unit]
Description=Maestro per-host daemon
Documentation=https://git.naps.pt/naps62/maestro
After=network-online.target
[Service]
Type=simple
ExecStart=%h/.cargo/bin/maestro daemon
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5
[Install]
WantedBy=default.target
```
Check status / logs:
```sh
systemctl --user status maestro
journalctl --user -u maestro -f
```
## Running the wizard without the bootstrap script
If you'd rather skip the script:
```sh
cargo install --git https://git.naps.pt/naps62/maestro maestro-installer
maestro-install
```
This leaves `maestro-installer` permanently in `~/.cargo/bin` — the bootstrap
script avoids that by installing to a scratch dir.
## Fully manual install (no wizard)
```sh
cargo install --git https://git.naps.pt/naps62/maestro --locked maestro
maestro daemon # foreground; or wire your own service manager
```
Every setting has a default, so `~/.config/maestro/config.toml` is optional —
create it only to change the bind address, spawn presets, or TUI
keymap. (Worktrees live inside their base repos at `<repo>/worktrees/<branch>`;
set `[daemon].worktree_root` only to override that.)
## Non-Linux hosts
The wizard works on macOS for the `cargo install` part. The systemd step is
automatically skipped (probe of
`/run/systemd/system`). launchd integration is not yet supported — start
`maestro daemon` from your shell or your own service manager.