feat(maestro-tui): wire --config flag + document tui in INSTALL.md (NAP-283)
ci / rust (push) Failing after 5m13s

- `RunConfig` grows a `config_path: Option<PathBuf>` field, plumbed from the
  umbrella binary's global `--config` flag through to `MaestroConfigFile`
  loading. Explicit `--config <path>` wins over `$MAESTRO_CONFIG` over the
  platform default; a missing file still falls back to shipped defaults.
- `docs/INSTALL.md` gains a "terminal UI" section: that `maestro tui` ships
  in the default feature set, how to opt out (`--no-default-features
  --features cli,daemon`), the `MAESTRO_DAEMON_URL` override, and a worked
  example of `[tui]` / `[tui.keymap.normal]` config pointing at the
  full action vocabulary in the design spec.
- Adds a thin-build line for `--features cli` (CLI-only client) and lists
  `tui` in the default-feature set.
- CLAUDE.md repo-layout block lists the new `maestro-tui` crate.

Closes the NAP-275 v1 epic.
This commit is contained in:
2026-05-28 22:42:30 +00:00
parent c599fcfdec
commit 278c81d2bc
4 changed files with 62 additions and 5 deletions
+1
View File
@@ -28,6 +28,7 @@ crates/
├── maestro-daemon/ # `maestro daemon` — tmux wrapper + WS endpoints (NAP-212, NAP-214)
├── maestro-registry/ # `maestro registry` — phone book + HTTP API (NAP-213, NAP-215)
├── maestro-web/ # standalone `maestro-web` browser UI (NAP-241, NAP-249)
├── maestro-tui/ # `maestro tui` — ratatui session manager (NAP-275 epic)
├── maestro-installer/ # `maestro-install` wizard: cargo install + systemd (NAP-225)
└── maestro-e2e/ # cross-process integration tests (NAP-217)
web/ # browser SPA (NAP-218)
+12 -2
View File
@@ -22,6 +22,7 @@ pub mod state;
pub mod tabs;
pub mod terminal;
use std::path::PathBuf;
use std::time::Duration;
use ratatui::backend::CrosstermBackend;
@@ -38,6 +39,10 @@ pub struct RunConfig {
/// HTTP base URL of the local daemon, e.g. `http://127.0.0.1:7070`.
/// No trailing slash.
pub daemon_url: String,
/// Explicit `--config <path>` override from the umbrella binary's global
/// flag. When `None`, [`maestro_config::resolve_config_path`] picks the
/// default (`$MAESTRO_CONFIG` or `~/.config/maestro/config.toml`).
pub config_path: Option<PathBuf>,
}
#[derive(Debug, thiserror::Error)]
@@ -62,8 +67,13 @@ pub async fn run(cfg: RunConfig) -> Result<(), TuiError> {
// Health check — fail fast if no daemon is reachable.
health_check(&base_url).await?;
// Load runtime config from disk if present; fall back to defaults.
let cfg_file = match maestro_config::resolve_config_path() {
// Load runtime config: explicit `--config <path>` wins, then
// `$MAESTRO_CONFIG`, then the platform default. Missing file is fine —
// we fall back to shipped defaults.
let cfg_path = cfg.config_path.clone().map(Ok).unwrap_or_else(|| {
maestro_config::resolve_config_path().map_err(|e| TuiError::Config(e.to_string()))
});
let cfg_file = match cfg_path {
Ok(path) => maestro_config::MaestroConfigFile::load_or_default(&path)
.map_err(|e| TuiError::Config(e.to_string()))?,
Err(_) => maestro_config::MaestroConfigFile::default(),
+5 -1
View File
@@ -167,7 +167,11 @@ async fn dispatch(cli: Cli) -> Result<(), Box<dyn std::error::Error>> {
let daemon_url = args
.daemon_url
.unwrap_or_else(|| "http://127.0.0.1:7070".to_string());
maestro_tui::run(maestro_tui::RunConfig { daemon_url }).await?
maestro_tui::run(maestro_tui::RunConfig {
daemon_url,
config_path: cli.config,
})
.await?
}
}
Ok(())
+44 -2
View File
@@ -52,8 +52,8 @@ The binary lands in `~/.cargo/bin/maestro` — make sure that's on your `$PATH`.
## Thin builds for single-role hosts
The default install pulls in everything. Registry-only and daemon-only hosts
can opt out of unused dependencies:
The default install pulls in everything (`cli` + `daemon` + `registry` + `tui`).
Single-role hosts can opt out of unused dependencies:
```sh
# Registry server — no tmux/PTY/jwt-verifier code:
@@ -63,10 +63,52 @@ cargo install --git https://git.naps.pt/naps62/maestro \
# Daemon-only host — no embedded web assets, no registry HTTP surface:
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
```
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.
Drop the `tui` feature to skip it on hosts that don't need the UI surface
(`--no-default-features --features cli,daemon`).
It talks only to the **local** `maestro daemon` over `http://127.0.0.1:7070`
by default; peer daemons reach you transparently via the catalog mirror.
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]
leader = "ctrl-a" # one byte that escapes Insert mode
chord_timeout_ms = 1000 # how long to hold a partial chord like `g`
[tui.keymap.normal]
"i" = "EnterInsert"
"s" = "OpenSpawnModal"
"x" = "KillHighlighted"
"?" = "ShowHelp"
"ctrl-t" = "NewTab"
"g t" = "NextTab"
"ctrl-w h" = "FocusSidebar"
"enter" = "AttachHighlighted"
[tui.keymap.insert]
# (leader is added automatically as LeaveInsert; only override here when you
# want extra Insert-mode bindings)
```
The full action vocabulary lives in
`docs/superpowers/specs/2026-05-28-maestro-tui-design.md` §3.8.
## Adding the browser UI (optional)
`maestro-web` is a separate binary that serves the SPA bundle and reverse-