From 71808783c45c16f97a75c22e850a10f608fe5981 Mon Sep 17 00:00:00 2001 From: Anders Olsson Date: Mon, 25 May 2026 11:49:47 +0200 Subject: [PATCH] feat(xy): clap CLI scaffold --- crates/xy/src/cli/mod.rs | 10 +++++ crates/xy/src/daemon/mod.rs | 4 ++ crates/xy/src/main.rs | 77 +++++++++++++++++++++++++++++++++++-- 3 files changed, 87 insertions(+), 4 deletions(-) create mode 100644 crates/xy/src/cli/mod.rs create mode 100644 crates/xy/src/daemon/mod.rs diff --git a/crates/xy/src/cli/mod.rs b/crates/xy/src/cli/mod.rs new file mode 100644 index 0000000..0b13e58 --- /dev/null +++ b/crates/xy/src/cli/mod.rs @@ -0,0 +1,10 @@ +use crate::paths::Paths; +use anyhow::{bail, Result}; + +pub async fn list(_p: Paths) -> Result { bail!("not implemented") } +pub async fn status(_p: Paths, _name: String) -> Result { bail!("not implemented") } +pub async fn start(_p: Paths, _all: bool, _name: Option) -> Result { bail!("not implemented") } +pub async fn stop(_p: Paths, _all: bool, _name: Option) -> Result { bail!("not implemented") } +pub async fn restart(_p: Paths, _all: bool, _name: Option) -> Result { bail!("not implemented") } +pub async fn reload(_p: Paths) -> Result { bail!("not implemented") } +pub async fn logs(_p: Paths, _name: String, _tail: Option, _follow: bool) -> Result { bail!("not implemented") } diff --git a/crates/xy/src/daemon/mod.rs b/crates/xy/src/daemon/mod.rs new file mode 100644 index 0000000..5e01e5c --- /dev/null +++ b/crates/xy/src/daemon/mod.rs @@ -0,0 +1,4 @@ +use crate::paths::Paths; +use anyhow::{bail, Result}; + +pub async fn run(_paths: Paths) -> Result<()> { bail!("not implemented") } diff --git a/crates/xy/src/main.rs b/crates/xy/src/main.rs index 303b93b..3145272 100644 --- a/crates/xy/src/main.rs +++ b/crates/xy/src/main.rs @@ -1,8 +1,77 @@ +use clap::{Parser, Subcommand}; + +mod cli; +mod daemon; mod paths; -#[allow(dead_code)] mod pidfile; -fn main() { - let p = paths::Paths::resolve().unwrap(); - eprintln!("xy: socket would be at {}", p.socket.display()); +#[derive(Debug, Parser)] +#[command(name = "xy", version, about = "HTTP MCP server supervisor")] +struct Cli { + #[command(subcommand)] + cmd: Cmd, +} + +#[derive(Debug, Subcommand)] +enum Cmd { + /// Run the daemon in the foreground. + Daemon, + /// List all configured servers with state. + List, + /// Show detailed status for a single server. + Status { name: String }, + /// Start a server (or all configured servers with --all). + Start { + #[arg(long, conflicts_with = "name")] all: bool, + #[arg(required_unless_present = "all")] name: Option, + }, + /// Stop a server (or --all). + Stop { + #[arg(long, conflicts_with = "name")] all: bool, + #[arg(required_unless_present = "all")] name: Option, + }, + /// Restart a server (or --all). + Restart { + #[arg(long, conflicts_with = "name")] all: bool, + #[arg(required_unless_present = "all")] name: Option, + }, + /// Re-read config dir and reconcile running servers. + Reload, + /// Stream a server's log. + Logs { + name: String, + #[arg(long)] tail: Option, + #[arg(short = 'f', long)] follow: bool, + }, +} + +#[tokio::main] +async fn main() -> std::process::ExitCode { + tracing_subscriber::fmt() + .with_env_filter(tracing_subscriber::EnvFilter::try_from_default_env() + .unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info"))) + .with_writer(std::io::stderr) + .init(); + + let cli = Cli::parse(); + let paths = match paths::Paths::resolve() { + Ok(p) => p, + Err(e) => { eprintln!("xy: failed to resolve XDG paths: {e}"); return std::process::ExitCode::from(3); } + }; + + let result: anyhow::Result = match cli.cmd { + Cmd::Daemon => daemon::run(paths).await.map(|_| 0), + Cmd::List => cli::list(paths).await, + Cmd::Status { name } => cli::status(paths, name).await, + Cmd::Start { all, name } => cli::start(paths, all, name).await, + Cmd::Stop { all, name } => cli::stop(paths, all, name).await, + Cmd::Restart { all, name } => cli::restart(paths, all, name).await, + Cmd::Reload => cli::reload(paths).await, + Cmd::Logs { name, tail, follow } => cli::logs(paths, name, tail, follow).await, + }; + + match result { + Ok(code) => std::process::ExitCode::from(code as u8), + Err(e) => { eprintln!("xy: {e:#}"); std::process::ExitCode::from(1) } + } }