use clap::{Parser, Subcommand};
use colored::*;
use std::process;
mod api;
mod auth;
mod commands;
mod config;
mod utils;
use auth::AuthManager;
use commands::{healthcheck, list, pull, search, upload};
use utils::error::CarpResult;
#[derive(Parser)]
#[command(
name = "carp",
version = env!("CARGO_PKG_VERSION"),
about = "Command-line tool for the Claude Agent Registry Portal",
long_about = "Carp is a CLI tool for discovering, pulling, and publishing Claude AI agents from the registry."
)]
struct Cli {
#[command(subcommand)]
command: Commands,
#[arg(long, global = true, help = "Enable verbose output")]
verbose: bool,
#[arg(long, global = true, help = "Suppress all output except errors")]
quiet: bool,
#[arg(
long,
global = true,
env = "CARP_API_KEY",
hide_env_values = true,
help = "API key for authentication (can also be set via CARP_API_KEY environment variable)"
)]
api_key: Option<String>,
}
#[derive(Subcommand)]
enum Commands {
Healthcheck,
List,
Search {
query: String,
#[arg(short, long, help = "Number of results to show")]
limit: Option<usize>,
#[arg(long, help = "Show only exact matches")]
exact: bool,
},
Pull {
agent: Option<String>,
#[arg(short, long, help = "Target directory")]
output: Option<String>,
#[arg(long, help = "Force overwrite existing files")]
force: bool,
},
Upload {
#[arg(
short,
long,
help = "Directory to scan for agents (prompts if not provided)"
)]
directory: Option<String>,
},
Auth {
#[command(subcommand)]
auth_command: AuthCommands,
},
}
#[derive(Subcommand)]
enum AuthCommands {
Login,
Status,
Logout,
}
#[tokio::main]
async fn main() {
let cli = Cli::parse();
if let Err(e) = run(cli).await {
eprintln!("{} {}", "Error:".red().bold(), e);
process::exit(1);
}
}
async fn run(cli: Cli) -> CarpResult<()> {
match cli.command {
Commands::Healthcheck => healthcheck::execute(cli.verbose).await,
Commands::List => list::execute(cli.verbose).await,
Commands::Search {
query,
limit,
exact,
} => search::execute(query, limit, exact, cli.verbose).await,
Commands::Pull {
agent,
output,
force,
} => pull::execute(agent, output, force, cli.verbose).await,
Commands::Upload { directory } => {
upload::execute(directory, cli.api_key, cli.verbose).await
}
Commands::Auth { auth_command } => match auth_command {
AuthCommands::Login => AuthManager::login().await,
AuthCommands::Status => AuthManager::status_with_key(cli.api_key.as_deref()).await,
AuthCommands::Logout => AuthManager::logout().await,
},
}
}