use crate::error::{CommandExitCode, Result};
use clap::{Parser, Subcommand};
pub mod agent;
pub mod mcp;
pub mod owners;
pub mod release;
pub use agent::{AgentSubCommands, ContextOpts, ExportDocsOpts};
pub use mcp::{McpServeOpts, McpSubCommands};
pub use owners::OwnersSubCommands;
pub use release::{
AnalyzeOpts, BumpOpts, CheckOpts, PlanOpts, PublishOpts, ReleaseSubCommands, ResumeOpts,
SimulateOpts, StatusOpts,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OutputFormat {
Json,
Yaml,
Human,
}
impl OutputFormat {
pub fn from_str(s: &str) -> Option<Self> {
match s.to_lowercase().as_str() {
"json" => Some(Self::Json),
"yaml" => Some(Self::Yaml),
"human" => Some(Self::Human),
_ => None,
}
}
}
#[derive(Parser, Debug)]
#[command(name = "cargo-governor")]
#[command(bin_name = "cargo-governor")]
#[command(about, long_about = None)]
#[command(version)]
#[command(propagate_version = true)]
pub struct Cli {
#[arg(hide = true)]
pub governor: Option<String>,
#[arg(short = 'w', long, global = true)]
pub workspace: Option<String>,
#[arg(short = 'c', long, global = true)]
pub config: Option<String>,
#[arg(short = 'f', long, global = true)]
pub format: Option<String>,
#[arg(short = 'o', long, global = true)]
pub output: Option<String>,
#[arg(short = 'v', long, global = true)]
pub verbose: bool,
#[arg(short = 'q', long, global = true)]
pub quiet: bool,
#[arg(long, global = true)]
pub dry_run: bool,
#[command(subcommand)]
pub command: Commands,
}
#[derive(Subcommand, Debug)]
pub enum Commands {
#[command(name = "owners")]
Owners {
#[command(subcommand)]
subcmd: OwnersSubCommands,
},
#[command(name = "release")]
Release {
#[command(subcommand)]
subcmd: ReleaseSubCommands,
},
#[command(name = "agent")]
Agent {
#[command(subcommand)]
subcmd: AgentSubCommands,
},
#[command(name = "mcp")]
Mcp {
#[command(subcommand)]
subcmd: McpSubCommands,
},
}
pub async fn run() -> Result<CommandExitCode> {
use crate::cli::{Cli, Commands, OutputFormat};
let args: Vec<String> = std::env::args().collect();
let cli = if args.len() > 1 && args[1] == "governor" {
let args_without_governor: Vec<String> = std::iter::once(args[0].clone())
.chain(args.into_iter().skip(2))
.collect();
Cli::parse_from(args_without_governor)
} else {
Cli::parse()
};
let format = cli
.format
.and_then(|f| OutputFormat::from_str(&f))
.unwrap_or(OutputFormat::Json);
let workspace_path = cli.workspace.unwrap_or_else(|| ".".to_string());
match cli.command {
Commands::Owners { subcmd } => handle_owners(subcmd, &workspace_path, format).await,
Commands::Release { subcmd } => {
handle_release(subcmd, &workspace_path, format, cli.dry_run).await
}
Commands::Agent { subcmd } => handle_agent(subcmd),
Commands::Mcp { subcmd } => handle_mcp(subcmd, &workspace_path).await,
}
}
async fn handle_owners(
subcmd: OwnersSubCommands,
workspace_path: &str,
format: OutputFormat,
) -> Result<CommandExitCode> {
match subcmd {
OwnersSubCommands::Show { .. } => {
crate::commands::owners::show_cmd(workspace_path, &subcmd, format).await
}
OwnersSubCommands::Check { .. } => {
crate::commands::owners::check_cmd(workspace_path, &subcmd, format).await
}
OwnersSubCommands::Sync { .. } => {
crate::commands::owners::sync_cmd(workspace_path, &subcmd, format).await
}
}
}
async fn handle_release(
subcmd: ReleaseSubCommands,
workspace_path: &str,
format: OutputFormat,
dry_run: bool,
) -> Result<CommandExitCode> {
match subcmd {
ReleaseSubCommands::Analyze(opts) => {
crate::commands::release::analyze::execute(workspace_path, opts, format).await
}
ReleaseSubCommands::Plan(opts) => {
crate::commands::release::plan::execute(workspace_path, opts, format).await
}
ReleaseSubCommands::Bump(opts) => {
crate::commands::release::bump::execute(workspace_path, opts, format, dry_run).await
}
ReleaseSubCommands::Publish(opts) => {
crate::commands::release::publish::execute(workspace_path, opts, format, dry_run).await
}
ReleaseSubCommands::Simulate(opts) => {
crate::commands::release::simulate::execute(workspace_path, opts, format).await
}
ReleaseSubCommands::Check(opts) => {
crate::commands::release::check::execute(workspace_path, opts, format).await
}
ReleaseSubCommands::Status(opts) => {
crate::commands::release::status::execute(workspace_path, opts, format).await
}
ReleaseSubCommands::Resume(opts) => {
crate::commands::release::resume::execute(workspace_path, opts, format).await
}
ReleaseSubCommands::Full(opts) => {
crate::commands::release::full::execute(workspace_path, opts, format, dry_run).await
}
}
}
fn handle_agent(subcmd: AgentSubCommands) -> Result<CommandExitCode> {
match subcmd {
AgentSubCommands::Context(opts) => crate::commands::agent::context(&opts),
AgentSubCommands::ExportDocs(opts) => crate::commands::agent::export_docs(&opts),
}
}
async fn handle_mcp(subcmd: McpSubCommands, workspace_path: &str) -> Result<CommandExitCode> {
match subcmd {
McpSubCommands::Serve(opts) => crate::commands::mcp::serve(workspace_path, opts).await,
}
}