bzzz-cli 0.1.0

Bzzz CLI - Command line interface for Agent orchestration
//! Bzzz CLI - Command line interface for Agent orchestration

use clap::{ArgGroup, Parser, Subcommand};
use std::path::PathBuf;

mod commands;
mod run_registry;

use commands::output::OutputFormat;

#[derive(Parser)]
#[command(name = "bzzz")]
#[command(about = "Bzzz - Agent orchestration CLI", long_about = None)]
struct Cli {
    #[command(subcommand)]
    command: Commands,
}

#[derive(Subcommand)]
enum Commands {
    /// Initialize a new SwarmFile from template
    Init {
        /// Path to create the SwarmFile
        #[arg(short, long)]
        file: PathBuf,

        /// Orchestration pattern type (sequence, parallel, conditional, loop, delegate)
        #[arg(long)]
        pattern: Option<String>,

        /// Template type (minimal, full)
        #[arg(long)]
        template: Option<String>,
    },

    /// Inspect and analyze a SwarmFile
    Inspect {
        /// Path to SwarmFile to inspect
        #[arg(short, long)]
        file: PathBuf,
    },

    /// Run an Agent or Swarm
    Run {
        /// Path to Agent Spec or SwarmFile
        #[arg(short, long)]
        file: PathBuf,

        /// Run in background
        #[arg(short, long)]
        background: bool,

        /// Timeout in seconds (overrides SwarmFile timeout)
        #[arg(short, long)]
        timeout: Option<u64>,

        /// Runtime to use (local, docker, http)
        #[arg(short, long, default_value = "local")]
        runtime: String,

        /// Input parameters as JSON string (e.g. '{"key": "value"}')
        #[arg(short, long)]
        input: Option<String>,

        /// Output format (text or json)
        #[arg(short = 'o', long = "output", default_value = "text")]
        output: OutputFormat,

        /// Enable adaptive runtime monitoring (dynamic worker adjustment)
        #[arg(short, long)]
        adaptive: bool,
    },

    /// Spawn an inline worker for dynamic orchestration
    Spawn {
        /// Role name for the spawned worker
        #[arg(short, long)]
        role: String,

        /// Command to execute
        #[arg(short, long)]
        command: String,

        /// Environment variables (KEY=VALUE format, multiple allowed)
        #[arg(short, long)]
        env: Vec<String>,

        /// Timeout in seconds
        #[arg(short, long)]
        timeout: Option<u64>,

        /// Input parameters as JSON string
        #[arg(short, long)]
        input: Option<String>,

        /// Output format (text or json)
        #[arg(short = 'o', long = "output", default_value = "text")]
        output: OutputFormat,
    },

    /// Check execution status
    Status {
        /// Run ID to check
        #[arg(short, long)]
        id: String,
    },

    /// Stop a running execution
    Stop {
        /// Run ID to stop
        #[arg(short, long)]
        id: String,

        /// Force stop
        #[arg(short, long)]
        force: bool,
    },

    /// List all runs or context information
    #[command(group(ArgGroup::new("list_mode").args(&["running", "agents", "patterns", "templates"])))]
    List {
        /// Show only running executions
        #[arg(short, long)]
        running: bool,

        /// List workers from a SwarmFile or discover A2A Agent Card
        #[arg(long)]
        agents: bool,

        /// Path to SwarmFile (required for --agents without --url)
        #[arg(short, long)]
        file: Option<PathBuf>,

        /// URL to discover A2A Agent Card (requires --agents)
        #[arg(long)]
        url: Option<String>,

        /// List supported orchestration patterns
        #[arg(long)]
        patterns: bool,

        /// List available template files
        #[arg(long)]
        templates: bool,

        /// Output format (text or json)
        #[arg(short = 'o', long = "output", default_value = "text")]
        output: OutputFormat,
    },

    /// Validate an Agent Spec or SwarmFile
    Validate {
        /// Path to validate
        #[arg(short, long)]
        file: PathBuf,
    },

    /// Start HTTP server with health endpoint
    Serve {
        /// Port to listen on (default: 8080, can be set via BZZZ_HEALTH_PORT env var)
        #[arg(short, long)]
        port: Option<u16>,
    },
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    tracing_subscriber::fmt::init();

    let cli = Cli::parse();

    match cli.command {
        Commands::Init { file, pattern, template } => {
            commands::init::execute(file, pattern, template)?;
        }
        Commands::Inspect { file } => {
            commands::inspect::execute(file).await?;
        }
        Commands::Run { file, background, timeout, runtime, input, output, adaptive } => {
            commands::run::execute(file, background, timeout, &runtime, input, output, adaptive).await?;
        }
        Commands::Spawn { role, command, env, timeout, input, output } => {
            commands::spawn::execute(role, command, env, timeout, input, output).await?;
        }
        Commands::Status { id } => {
            commands::status::execute(&id).await?;
        }
        Commands::Stop { id, force } => {
            commands::stop::execute(&id, force).await?;
        }
        Commands::List { running, agents, file, url, patterns, templates, output } => {
            if agents {
                if let Some(agent_url) = url {
                    // Discover A2A Agent Card from URL
                    commands::list::execute_agents_discovery(agent_url, output).await?;
                } else {
                    // List workers from SwarmFile
                    let swarm_file = file.expect("--agents requires -f <file> or --url <url>");
                    commands::list::execute_agents(swarm_file, output).await?;
                }
            } else if patterns {
                commands::list::execute_patterns(output)?;
            } else if templates {
                commands::list::execute_templates(output)?;
            } else {
                commands::list::execute(running, output).await?;
            }
        }
        Commands::Validate { file } => {
            commands::validate::execute(file).await?;
        }
        Commands::Serve { port } => {
            commands::serve::execute(port).await?;
        }
    }

    Ok(())
}