use anyhow::Result;
use clap::{Parser, Subcommand};
use super::{commands, cron, ui};
#[derive(Parser, Debug)]
#[command(name = "opencrabs")]
#[command(version, about, long_about = None)]
pub struct Cli {
#[arg(short, long, global = true)]
pub debug: bool,
#[arg(short, long, global = true)]
pub config: Option<String>,
#[arg(short, long, global = true)]
pub profile: Option<String>,
#[command(subcommand)]
pub command: Option<Commands>,
}
#[derive(Subcommand, Debug)]
pub enum Commands {
Chat {
#[arg(short, long)]
session: Option<String>,
#[arg(long)]
onboard: bool,
},
Onboard,
Run {
prompt: String,
#[arg(long, alias = "yolo")]
auto_approve: bool,
#[arg(short, long, default_value = "text")]
format: OutputFormat,
},
Status,
Doctor,
Init {
#[arg(short, long)]
force: bool,
},
Config {
#[arg(short, long)]
show_secrets: bool,
},
Db {
#[command(subcommand)]
operation: DbCommands,
},
Logs {
#[command(subcommand)]
operation: LogCommands,
},
Agent {
#[arg(short, long)]
message: Option<String>,
#[arg(short, long)]
session: Option<String>,
#[arg(long, alias = "yolo")]
auto_approve: bool,
#[arg(short, long, default_value = "text")]
format: OutputFormat,
},
Channel {
#[command(subcommand)]
operation: ChannelCommands,
},
Memory {
#[command(subcommand)]
operation: MemoryCommands,
},
Session {
#[command(subcommand)]
operation: SessionCommands,
},
Service {
#[command(subcommand)]
operation: ServiceCommands,
},
Daemon,
Profile {
#[command(subcommand)]
operation: ProfileCommands,
},
Cron {
#[command(subcommand)]
operation: CronCommands,
},
Completions {
#[arg(value_enum)]
shell: clap_complete::Shell,
},
Version,
}
#[derive(Subcommand, Debug)]
pub enum LogCommands {
Status,
View {
#[arg(short, long, default_value = "50")]
lines: usize,
},
Clean {
#[arg(short = 'a', long, default_value = "7")]
days: u64,
},
Open,
}
#[derive(Subcommand, Debug)]
pub enum DbCommands {
Init,
Stats,
Clear {
#[arg(short, long)]
force: bool,
},
}
#[derive(Subcommand, Debug)]
pub enum CronCommands {
Add {
#[arg(long)]
name: String,
#[arg(long)]
cron: String,
#[arg(long, default_value = "UTC")]
tz: String,
#[arg(long, alias = "message")]
prompt: String,
#[arg(long)]
provider: Option<String>,
#[arg(long)]
model: Option<String>,
#[arg(long, default_value = "off")]
thinking: String,
#[arg(long, default_value = "true")]
auto_approve: bool,
#[arg(long, alias = "deliver")]
deliver_to: Option<String>,
},
List,
Remove {
id: String,
},
Enable {
id: String,
},
Disable {
id: String,
},
Test {
id: String,
},
}
#[derive(Subcommand, Debug)]
pub enum ChannelCommands {
List,
Doctor,
}
#[derive(Subcommand, Debug)]
pub enum MemoryCommands {
List,
Get {
name: String,
},
Stats,
}
#[derive(Subcommand, Debug)]
pub enum SessionCommands {
List {
#[arg(short, long)]
all: bool,
},
Get {
id: String,
},
}
#[derive(Subcommand, Debug)]
pub enum ProfileCommands {
Create {
name: String,
#[arg(short, long)]
description: Option<String>,
},
List,
Delete {
name: String,
#[arg(short, long)]
force: bool,
},
Export {
name: String,
#[arg(short, long)]
output: Option<String>,
},
Import {
path: String,
},
Migrate {
from: String,
to: String,
#[arg(short, long)]
force: bool,
},
}
#[derive(Subcommand, Debug)]
pub enum ServiceCommands {
Install,
Start,
Stop,
Restart,
Status,
Uninstall,
}
#[derive(Debug, Clone, Copy, clap::ValueEnum)]
pub enum OutputFormat {
Text,
Json,
Markdown,
}
pub async fn run() -> Result<()> {
let cli = Cli::parse();
crate::config::profile::set_active_profile(cli.profile.clone())
.unwrap_or_else(|e| tracing::warn!("Profile already set: {}", e));
if let Some(ref name) = cli.profile
&& let Ok(mut registry) = crate::config::profile::ProfileRegistry::load()
{
registry.touch(name);
let _ = registry.save();
}
if cli.debug {
tracing::info!("Debug mode enabled");
}
let config = commands::load_config(cli.config.as_deref()).await?;
let config_path = dirs::config_dir().map(|d| d.join("opencrabs").join("config.toml"));
if let Some(ref path) = config_path
&& !path.exists()
&& config.has_any_api_key()
{
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent).ok();
}
if let Err(e) = config.save(path) {
tracing::warn!("Failed to auto-generate config.toml: {}", e);
} else {
tracing::info!("Auto-generated config.toml from environment");
}
}
match cli.command {
None | Some(Commands::Chat { .. }) => {
let (session, force_onboard) = match &cli.command {
Some(Commands::Chat { session, onboard }) => (session.clone(), *onboard),
_ => (None, false),
};
ui::cmd_chat(&config, session, force_onboard).await
}
Some(Commands::Onboard) => {
ui::cmd_chat(&config, None, true).await
}
Some(Commands::Status) => commands::cmd_status(&config).await,
Some(Commands::Doctor) => commands::cmd_doctor(&config).await,
Some(Commands::Init { force }) => commands::cmd_init(&config, force).await,
Some(Commands::Config { show_secrets }) => {
commands::cmd_config(&config, show_secrets).await
}
Some(Commands::Db { operation }) => commands::cmd_db(&config, operation).await,
Some(Commands::Logs { operation }) => commands::cmd_logs(operation).await,
Some(Commands::Run {
prompt,
auto_approve,
format,
}) => commands::cmd_run(&config, prompt, auto_approve, format).await,
Some(Commands::Agent {
message,
session: _,
auto_approve,
format,
}) => {
if let Some(msg) = message {
commands::cmd_run(&config, msg, auto_approve, format).await
} else {
commands::cmd_agent_interactive(&config, auto_approve).await
}
}
Some(Commands::Channel { operation }) => commands::cmd_channel(&config, operation).await,
Some(Commands::Memory { operation }) => commands::cmd_memory(operation).await,
Some(Commands::Session { operation }) => commands::cmd_session(&config, operation).await,
Some(Commands::Service { operation }) => commands::cmd_service(operation).await,
Some(Commands::Daemon) => ui::cmd_daemon(&config).await,
Some(Commands::Profile { operation }) => commands::cmd_profile(operation).await,
Some(Commands::Cron { operation }) => cron::cmd_cron(&config, operation).await,
Some(Commands::Completions { shell }) => {
use clap::CommandFactory;
clap_complete::generate(
shell,
&mut Cli::command(),
"opencrabs",
&mut std::io::stdout(),
);
Ok(())
}
Some(Commands::Version) => {
println!("opencrabs {}", env!("CARGO_PKG_VERSION"));
Ok(())
}
}
}