Merge branch 'linear/nap-276-tui-scaffold'
ci / rust (push) Successful in 16m15s

This commit is contained in:
2026-05-28 19:24:44 +00:00
7 changed files with 115 additions and 3 deletions
Generated
+9
View File
@@ -947,6 +947,7 @@ dependencies = [
"maestro-cli",
"maestro-daemon",
"maestro-registry",
"maestro-tui",
"tokio",
"tracing-subscriber",
]
@@ -1080,6 +1081,14 @@ dependencies = [
"uuid",
]
[[package]]
name = "maestro-tui"
version = "0.1.0"
dependencies = [
"clap",
"thiserror 1.0.69",
]
[[package]]
name = "maestro-web"
version = "0.1.0"
+1
View File
@@ -19,6 +19,7 @@ jsonwebtoken = { version = "9", default-features = false }
libc = "0.2"
maestro-config = { path = "crates/maestro-config" }
maestro-protocol = { path = "crates/maestro-protocol" }
maestro-tui = { path = "crates/maestro-tui" }
nix = { version = "0.29", default-features = false, features = ["term", "signal"] }
rand = "0.8"
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls", "cookies", "stream"] }
+12
View File
@@ -0,0 +1,12 @@
[package]
name = "maestro-tui"
version.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
publish.workspace = true
description = "Terminal UI client over the local maestro daemon's `/api/*` surface."
[dependencies]
clap.workspace = true
thiserror.workspace = true
+40
View File
@@ -0,0 +1,40 @@
//! Clap-derive subcommand definition for `maestro tui`.
#[derive(Debug, clap::Args)]
pub struct TuiArgs {
/// Override the local daemon's HTTP base URL (default `http://127.0.0.1:7070`).
#[arg(long, env = "MAESTRO_DAEMON_URL")]
pub daemon_url: Option<String>,
}
#[cfg(test)]
mod tests {
use super::*;
use clap::Parser;
#[derive(Debug, Parser)]
struct Harness {
#[command(subcommand)]
cmd: Cmd,
}
#[derive(Debug, clap::Subcommand)]
enum Cmd {
Tui(TuiArgs),
}
#[test]
fn parses_with_daemon_url() {
let h = Harness::try_parse_from(["maestro", "tui", "--daemon-url", "http://10.0.0.1:7070"])
.unwrap();
let Cmd::Tui(args) = h.cmd;
assert_eq!(args.daemon_url.as_deref(), Some("http://10.0.0.1:7070"));
}
#[test]
fn parses_without_daemon_url() {
let h = Harness::try_parse_from(["maestro", "tui"]).unwrap();
let Cmd::Tui(args) = h.cmd;
assert!(args.daemon_url.is_none());
}
}
+32
View File
@@ -0,0 +1,32 @@
//! `maestro tui` — terminal UI client over the local daemon's `/api/*` surface.
//!
//! Design: `docs/superpowers/specs/2026-05-28-maestro-tui-design.md`
//! Implementation: `docs/superpowers/plans/2026-05-28-maestro-tui.md`
//!
//! Phase 1 (NAP-276) scaffolds the crate and a `Tui` subcommand stub. The real
//! event loop, rendering, and `/api/*` plumbing land in NAP-277 onward.
#![deny(missing_debug_implementations)]
pub mod cli;
/// Inputs for [`run`]. Built by the umbrella binary from the parsed
/// `TuiArgs` plus any global flags.
#[derive(Debug, Clone)]
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,
}
#[derive(Debug, thiserror::Error)]
pub enum TuiError {
#[error("not implemented")]
NotImplemented,
}
/// Entry point. Phase-1 stub — returns `NotImplemented` so the subcommand
/// dispatch path is exercisable end-to-end before the renderer lands.
pub async fn run(_cfg: RunConfig) -> Result<(), TuiError> {
Err(TuiError::NotImplemented)
}
+3 -1
View File
@@ -12,10 +12,11 @@ name = "maestro"
path = "src/main.rs"
[features]
default = ["cli", "daemon", "registry"]
default = ["cli", "daemon", "registry", "tui"]
cli = ["dep:maestro-cli"]
daemon = ["dep:maestro-daemon"]
registry = ["dep:maestro-registry"]
tui = ["dep:maestro-tui"]
[dependencies]
clap.workspace = true
@@ -24,3 +25,4 @@ tracing-subscriber.workspace = true
maestro-cli = { path = "../maestro-cli", optional = true }
maestro-daemon = { path = "../maestro-daemon", optional = true }
maestro-registry = { path = "../maestro-registry", optional = true }
maestro-tui = { path = "../maestro-tui", optional = true }
+18 -2
View File
@@ -7,8 +7,13 @@
//! cargo install maestro # default: cli + daemon + registry
//! ```
#[cfg(not(any(feature = "cli", feature = "daemon", feature = "registry")))]
compile_error!("at least one of the `cli`, `daemon`, `registry` features must be enabled");
#[cfg(not(any(
feature = "cli",
feature = "daemon",
feature = "registry",
feature = "tui"
)))]
compile_error!("at least one of the `cli`, `daemon`, `registry`, `tui` features must be enabled");
use std::path::PathBuf;
use std::process::ExitCode;
@@ -73,6 +78,10 @@ enum Command {
/// Always-on registry: presence index + session catalog.
#[cfg(feature = "registry")]
Registry,
/// Open the terminal UI.
#[cfg(feature = "tui")]
Tui(maestro_tui::cli::TuiArgs),
}
fn main() -> ExitCode {
@@ -153,6 +162,13 @@ async fn dispatch(cli: Cli) -> Result<(), Box<dyn std::error::Error>> {
Command::Daemon { cmd } => maestro_daemon::cli::dispatch(cli.config, cmd).await?,
#[cfg(feature = "registry")]
Command::Registry => maestro_registry::run(cli.config).await?,
#[cfg(feature = "tui")]
Command::Tui(args) => {
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?
}
}
Ok(())
}