pub mod commit;
pub mod memory;
pub mod review;
use anyhow::Result;
use colored::*;
use crate::agent::Agent;
pub async fn dispatch(agent: &mut Agent, input: &str) -> Result<bool> {
let input = input.trim();
if !input.starts_with('/') {
return Ok(false);
}
let parts: Vec<&str> = input.splitn(2, ' ').collect();
let command = parts[0];
let args = parts.get(1).unwrap_or(&"").trim();
match command {
"/help" => {
print_help();
Ok(true)
}
"/clear" => {
agent.session.messages.clear();
println!("{}", "✓ Conversation history cleared.".green());
Ok(true)
}
"/model" => {
if args.is_empty() {
println!("Current model: {}", agent.model_info().cyan());
println!(
"\nUsage: {} <provider> [model_name]",
"/model".bold()
);
println!("Available providers: anthropic, groq, google, ollama, lm_studio, llama_cpp, custom");
} else {
let parts: Vec<&str> = args.splitn(2, ' ').collect();
let provider_name = parts[0].trim();
let model_name = parts.get(1).map(|s| s.trim().to_string());
let mut config = agent.config.clone();
match config.prompt_for_key_if_missing(provider_name) {
Ok(_) => {
agent.config = config;
match crate::query::create_provider(&agent.config, provider_name, model_name) {
Ok(provider) => {
agent.engine.switch_provider(provider);
agent.session.provider_name = agent.engine.provider_name().to_string();
agent.session.model_name = agent.engine.model_id().to_string();
println!(
"{}",
format!("✓ Switched model provider to: {}", agent.model_info()).green()
);
}
Err(e) => {
println!("{}", format!("✗ Failed to switch model provider: {}", e).red());
}
}
}
Err(e) => {
println!("{}", format!("✗ Failed to configure key: {}", e).red());
}
}
}
Ok(true)
}
"/commit" => {
commit::handle(agent, args).await?;
Ok(true)
}
"/review" => {
review::handle(agent, args).await?;
Ok(true)
}
"/memory" => {
memory::handle(agent, args).await?;
Ok(true)
}
"/sessions" => {
list_sessions(agent).await?;
Ok(true)
}
"/config" => {
println!("{}", agent.config.display_summary());
Ok(true)
}
"/jobs" => {
if args.is_empty() || args == "list" {
let tasks = crate::tools::task_manager::TaskManager::global().list_tasks();
if tasks.is_empty() {
println!("{}", "No background tasks registered.".dimmed());
} else {
println!("{}", "\n━━━ Background Tasks ━━━".bold());
for task in &tasks {
let status_str = match &task.status {
crate::tools::task_manager::TaskStatus::Running => "Running".cyan(),
crate::tools::task_manager::TaskStatus::Finished { exit_code } => format!("Finished ({})", exit_code).green(),
crate::tools::task_manager::TaskStatus::Failed { error } => format!("Failed: {}", error).red(),
crate::tools::task_manager::TaskStatus::Killed => "Killed".yellow(),
};
println!(
" {} [{}] — {}\n cmd: {}\n logs: {}",
task.id.bold().cyan(),
status_str,
task.started_at.format("%Y-%m-%d %H:%M:%S"),
task.command.dimmed(),
task.log_path
);
}
}
} else if let Some(task_id) = args.strip_prefix("kill ") {
let task_id = task_id.trim();
match crate::tools::task_manager::TaskManager::global().kill_task(task_id) {
Ok(true) => println!("{}", format!("✓ Successfully killed task '{}'", task_id).green()),
Ok(false) => println!("{}", format!("✗ Task '{}' is not running or not found.", task_id).red()),
Err(e) => println!("{}", format!("✗ Error killing task '{}': {}", task_id, e).red()),
}
} else if let Some(task_id) = args.strip_prefix("logs ") {
let task_id = task_id.trim();
let tasks = crate::tools::task_manager::TaskManager::global().list_tasks();
if let Some(task) = tasks.iter().find(|t| t.id == task_id) {
let log_path = std::path::Path::new(&task.log_path);
if log_path.exists() {
match std::fs::read_to_string(log_path) {
Ok(content) => {
println!("{}", format!("\n━━━ Logs for {} ━━━", task_id).bold());
let lines: Vec<&str> = content.lines().collect();
let start = lines.len().saturating_sub(20);
if start > 0 {
println!("{}", "... (truncated)".dimmed());
}
for line in &lines[start..] {
println!("{}", line);
}
}
Err(e) => println!("{}", format!("✗ Failed to read log file: {}", e).red()),
}
} else {
println!("{}", "✗ Log file does not exist.".red());
}
} else {
println!("{}", format!("✗ Task '{}' not found.", task_id).red());
}
} else {
println!("{}", "Usage: /jobs [list|kill <task_id>|logs <task_id>]".yellow());
}
Ok(true)
}
_ => {
println!(
"{}",
format!("Unknown command: {}. Type /help for available commands.", command).red()
);
Ok(true)
}
}
}
fn print_help() {
println!("{}", "\n━━━ Available Commands ━━━".bold());
println!(" {} Ask the AI to generate a commit message and commit", "/commit".cyan());
println!(" {} Ask the AI to review recent git changes", "/review".cyan());
println!(" {} Show or set persistent memory notes", "/memory".cyan());
println!(" {} Switch the active model provider", "/model".cyan());
println!(" {} Clear conversation history", "/clear".cyan());
println!(" {} List saved sessions", "/sessions".cyan());
println!(" {} Show current configuration", "/config".cyan());
println!(" {} Manage background jobs", "/jobs".cyan());
println!(" {} Show this help message", "/help".cyan());
println!();
}
async fn list_sessions(agent: &Agent) -> Result<()> {
let summaries = crate::session::Session::list_all(&agent.config.session_dir).await?;
if summaries.is_empty() {
println!("{}", "No saved sessions found.".dimmed());
} else {
println!("{}", "\n━━━ Saved Sessions ━━━".bold());
for summary in &summaries {
println!(" {}", summary);
}
println!(
"\n{}",
"Use: gigi resume <session-id> to resume a session.".dimmed()
);
}
Ok(())
}