use super::Colors;
use crate::services::mcp_service::{McpServerConfig, McpService};
use anyhow::{anyhow, Result};
use std::collections::HashMap;
fn help_command(_args: &[&str]) -> Result<CommandResult> {
Ok(CommandResult::Success(get_help_text()))
}
fn clear_command(_args: &[&str]) -> Result<CommandResult> {
Ok(CommandResult::Clear)
}
fn exit_command(_args: &[&str]) -> Result<CommandResult> {
Ok(CommandResult::Exit)
}
fn get_help_text() -> String {
format!(
r#"
{}╔════════════════════════════════════════════════╗
║ {}AVAILABLE COMMANDS{} ║
╚════════════════════════════════════════════════╝{}
{}Commands:{}
/help - Show this help menu
/clear - Clear chat history
/tools - List available MCP tools
/context - Show conversation context
/status - Show system status
/exit, /quit - Exit application
{}Tool Invocation:{}
@server/tool - Execute specific MCP tool
{}Keyboard Shortcuts:{}
Ctrl+T - Toggle task navigation mode
Ctrl+C - Exit application
Tab - Auto-complete suggestion
↑/↓ - Navigate history or suggestions
Escape - Clear current input
{}Examples:{}
@solana/get_balance
@solana/get_recent_transactions
/tools
/status
"#,
Colors::CYAN,
Colors::BOLD,
Colors::CYAN,
Colors::RESET,
Colors::GREEN,
Colors::RESET,
Colors::YELLOW,
Colors::RESET,
Colors::MAGENTA,
Colors::RESET,
Colors::BLUE,
Colors::RESET,
)
}
pub struct CommandProcessor {
commands: HashMap<String, CommandHandler>,
}
type CommandHandler = fn(&[&str]) -> Result<CommandResult>;
pub enum CommandResult {
Success(String),
Error(String),
Exit,
Clear,
Continue,
}
impl CommandProcessor {
pub fn new() -> Self {
let mut commands = HashMap::new();
commands.insert("/help".to_string(), help_command as CommandHandler);
commands.insert("/clear".to_string(), clear_command as CommandHandler);
commands.insert("/exit".to_string(), exit_command as CommandHandler);
commands.insert("/quit".to_string(), exit_command as CommandHandler);
Self { commands }
}
pub fn process(&self, input: &str) -> Result<CommandResult> {
let input = input.trim();
if input.is_empty() {
return Ok(CommandResult::Continue);
}
let parts: Vec<&str> = input.split_whitespace().collect();
let command = parts.get(0).unwrap_or(&"");
if let Some(handler) = self.commands.get(*command) {
return handler(&parts[1..]);
}
if command.starts_with('/') {
return Ok(CommandResult::Error(format!(
"Unknown command: {}",
command
)));
}
if command.starts_with('@') {
return self.process_tool_invocation(input);
}
Ok(CommandResult::Continue)
}
fn process_tool_invocation(&self, input: &str) -> Result<CommandResult> {
let parts: Vec<&str> = input[1..].split('/').collect();
if parts.len() != 2 {
return Ok(CommandResult::Error(
"Invalid tool syntax. Use @server/tool".to_string(),
));
}
let server = parts.get(0).ok_or_else(|| anyhow!("Missing server name"))?;
let tool = parts.get(1).ok_or_else(|| anyhow!("Missing tool name"))?;
if server.is_empty() || tool.is_empty() {
return Ok(CommandResult::Error(
"Server and tool names cannot be empty".to_string(),
));
}
Ok(CommandResult::Success(format!(
"{}Executing tool: {}/{}{}",
Colors::YELLOW,
server,
tool,
Colors::RESET
)))
}
pub fn register_command(&mut self, command: &str, handler: CommandHandler) {
self.commands.insert(command.to_string(), handler);
}
pub fn is_command(input: &str) -> bool {
input.trim().starts_with('/') || input.trim().starts_with('@')
}
pub fn parse_tool_invocation(input: &str) -> Option<(String, String, Vec<String>)> {
if !input.starts_with('@') {
return None;
}
let parts: Vec<&str> = input[1..].split_whitespace().collect();
if parts.is_empty() {
return None;
}
let server_tool: Vec<&str> = parts.get(0)?.split('/').collect();
if server_tool.len() != 2 {
return None;
}
let server = server_tool.get(0)?.to_string();
let tool = server_tool.get(1)?.to_string();
if server.is_empty() || tool.is_empty() {
return None;
}
let args: Vec<String> = parts
.get(1..)
.unwrap_or(&[])
.iter()
.map(|s| s.to_string())
.collect();
Some((server, tool, args))
}
}
pub struct CommandContext {
pub chat_history: Vec<String>,
pub mcp_servers: Vec<(String, bool)>, pub current_task: Option<String>,
}
impl CommandContext {
pub fn new() -> Self {
Self {
chat_history: Vec::new(),
mcp_servers: Vec::new(),
current_task: None,
}
}
pub fn add_to_history(&mut self, message: String) {
self.chat_history.push(message);
if self.chat_history.len() > 1000 {
self.chat_history.remove(0);
}
}
pub fn get_recent_history(&self, count: usize) -> Vec<String> {
self.chat_history
.iter()
.rev()
.take(count)
.rev()
.cloned()
.collect()
}
pub fn update_servers(&mut self, servers: Vec<(String, bool)>) {
self.mcp_servers = servers;
}
pub fn get_active_servers(&self) -> Vec<String> {
self.mcp_servers
.iter()
.filter(|(_, enabled)| *enabled)
.map(|(id, _)| id.clone())
.collect()
}
}