use anyhow::Result;
use clap::{CommandFactory, Parser, Subcommand};
use clap_complete::{generate, Shell};
use octomind::config::Config;
use octomind::session;
mod commands;
#[derive(Parser)]
#[command(name = "octomind")]
#[command(version = env!("CARGO_PKG_VERSION"))]
#[command(about = "Octomind is a smart AI developer assistant with configurable MCP support")]
struct CliArgs {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
Config(commands::ConfigArgs),
Session(commands::SessionArgs),
Run(commands::RunArgs),
Ask(commands::AskArgs),
Shell(commands::ShellArgs),
Server(commands::ServerArgs),
Vars(commands::VarsArgs),
Completion {
#[arg(value_enum)]
shell: Shell,
},
}
#[tokio::main]
async fn main() -> Result<(), anyhow::Error> {
let _tracker = octomind::config::get_env_tracker();
if let Err(e) = octomind::config::get_env_tracker()
.lock()
.unwrap()
.load_dotenv_override()
{
octomind::log_debug!("Failed to load .env file: {}", e);
}
let args = CliArgs::parse();
let config = Config::load()?;
let result = run_with_cleanup(args, config).await;
if let Err(e) = octomind::mcp::server::cleanup_servers() {
octomind::log_error!("Warning: Error cleaning up MCP servers: {}", e);
}
result
}
async fn initialize_mcp_for_role_with_progress(
role: &str,
config: &Config,
is_interactive: bool,
) -> Result<(), anyhow::Error> {
use indicatif::{ProgressBar, ProgressStyle};
use std::time::Duration;
if is_interactive {
let spinner = ProgressBar::new_spinner();
spinner.set_style(
ProgressStyle::default_spinner()
.template(" {spinner:.cyan} {msg:.cyan}")
.unwrap()
.tick_chars("⠋⠙⠹⠸⠼⠴⠦⠧"),
);
spinner.set_message("Initializing MCP servers...");
spinner.enable_steady_tick(Duration::from_millis(80));
let progress_callback = |server_name: &str| {
spinner.set_message(format!("Starting MCP server: {}", server_name));
};
let result = octomind::mcp::initialize_mcp_for_role_with_callback(
role,
config,
Some(&progress_callback),
)
.await;
spinner.finish_and_clear();
print!("\x1B[2K\r");
std::io::Write::flush(&mut std::io::stdout()).ok();
result
} else {
octomind::mcp::initialize_mcp_for_role(role, config).await
}
}
async fn run_with_cleanup(args: CliArgs, config: Config) -> Result<(), anyhow::Error> {
match &args.command {
Commands::Session(session_args) => {
initialize_mcp_for_role_with_progress(&session_args.role, &config, true).await?;
}
Commands::Run(run_args) => {
initialize_mcp_for_role_with_progress(&run_args.role, &config, false).await?;
}
Commands::Server(server_args) => {
initialize_mcp_for_role_with_progress(&server_args.role, &config, true).await?;
}
_ => {
}
}
match &args.command {
Commands::Config(config_args) => commands::config::execute(config_args, config)?,
Commands::Session(session_args) => {
session::chat::run_interactive_session(session_args, &config).await?
}
Commands::Run(run_args) => {
let input = run_args.get_input()?;
let session_args = run_args.to_session_args();
session::chat::run_interactive_session_with_input(&session_args, &config, &input)
.await?
}
Commands::Ask(ask_args) => commands::ask::execute(ask_args, &config).await?,
Commands::Shell(shell_args) => commands::shell::execute(shell_args, &config).await?,
Commands::Server(server_args) => commands::server::execute(server_args, &config).await?,
Commands::Vars(vars_args) => commands::vars::execute(vars_args, &config).await?,
Commands::Completion { shell } => {
let mut app = CliArgs::command();
let name = app.get_name().to_string();
generate(*shell, &mut app, name, &mut std::io::stdout());
}
}
Ok(())
}