use anyhow::Result;
use chrono::{DateTime, Utc};
use tokio::time::{Duration, Instant};
use crate::agent::AgentStatus;
use crate::coordination::{CoordinationBus, StatusTracker, TaskQueue};
use crate::execution::{ExecutionEngine, TaskStatus};
#[derive(Debug, Clone, PartialEq)]
pub enum Tab {
Overview,
Agents,
Tasks,
Logs,
Delegation,
}
impl Tab {
pub fn title(&self) -> &str {
match self {
Tab::Overview => "Overview",
Tab::Agents => "Agents",
Tab::Tasks => "Tasks",
Tab::Logs => "Logs",
Tab::Delegation => "Delegation",
}
}
}
#[derive(Debug, Clone)]
pub struct AgentInfo {
pub id: String,
pub name: String,
pub specialization: String,
pub provider_type: String,
pub provider_icon: String,
pub provider_color: String,
pub status: AgentStatus,
pub current_task: Option<String>,
pub tasks_completed: u32,
pub last_activity: DateTime<Utc>,
pub workspace: String,
}
#[derive(Debug, Clone)]
pub struct TaskInfo {
pub id: String,
pub description: String,
pub priority: String,
pub task_type: String,
pub status: String,
pub assigned_agent: Option<String>,
pub created_at: DateTime<Utc>,
}
#[derive(Debug, Clone)]
pub struct LogEntry {
pub timestamp: DateTime<Utc>,
pub level: String,
pub agent: Option<String>,
pub message: String,
}
#[derive(Debug, Clone)]
pub struct DelegationInfo {
pub task_description: String,
pub recommended_agent: String,
pub confidence: f64,
pub reasoning: String,
pub created_at: DateTime<Utc>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum DelegationMode {
Analyze,
Delegate,
ViewStats,
}
pub struct App {
pub current_tab: Tab,
pub selected_agent: usize,
pub selected_task: usize,
pub selected_log: usize,
pub selected_delegation: usize,
pub agents: Vec<AgentInfo>,
pub tasks: Vec<TaskInfo>,
pub logs: Vec<LogEntry>,
pub delegation_decisions: Vec<DelegationInfo>,
pub system_status: String,
pub total_agents: usize,
pub active_agents: usize,
pub pending_tasks: usize,
pub completed_tasks: usize,
pub tasks_executed: usize,
pub tasks_failed: usize,
pub success_rate: f64,
pub orchestration_usage: f64,
pub total_sessions: usize,
pub active_sessions: usize,
pub multi_agent_enabled: bool,
pub input_mode: InputMode,
pub input_buffer: String,
pub delegation_mode: DelegationMode,
pub delegation_input: String,
pub coordination_bus: CoordinationBus,
pub status_tracker: StatusTracker,
pub task_queue: TaskQueue,
pub execution_engine: Option<ExecutionEngine>,
pub terminal_width: u16,
pub terminal_height: u16,
pub last_update: Instant,
pub update_interval: Duration,
pub should_quit: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub enum InputMode {
Normal,
AddingTask,
CreatingAgent,
Command,
DelegationInput,
}
impl App {
pub async fn new() -> Result<Self> {
let coordination_bus = CoordinationBus::new().await?;
let status_tracker = StatusTracker::new().await?;
let task_queue = TaskQueue::new().await?;
Ok(Self {
current_tab: Tab::Overview,
selected_agent: 0,
selected_task: 0,
selected_log: 0,
selected_delegation: 0,
agents: Vec::new(),
tasks: Vec::new(),
logs: Vec::new(),
delegation_decisions: Vec::new(),
system_status: "Starting...".to_string(),
total_agents: 0,
active_agents: 0,
pending_tasks: 0,
completed_tasks: 0,
tasks_executed: 0,
tasks_failed: 0,
success_rate: 100.0,
orchestration_usage: 0.0,
total_sessions: 0,
active_sessions: 0,
multi_agent_enabled: true,
input_mode: InputMode::Normal,
input_buffer: String::new(),
delegation_mode: DelegationMode::Analyze,
delegation_input: String::new(),
coordination_bus,
status_tracker,
task_queue,
execution_engine: None, terminal_width: 80,
terminal_height: 24,
last_update: Instant::now(),
update_interval: Duration::from_millis(500), should_quit: false,
})
}
pub fn next_tab(&mut self) {
self.current_tab = match self.current_tab {
Tab::Overview => Tab::Agents,
Tab::Agents => Tab::Tasks,
Tab::Tasks => Tab::Logs,
Tab::Logs => Tab::Delegation,
Tab::Delegation => Tab::Overview,
};
self.reset_selection();
}
pub fn previous_tab(&mut self) {
self.current_tab = match self.current_tab {
Tab::Overview => Tab::Delegation,
Tab::Agents => Tab::Overview,
Tab::Tasks => Tab::Agents,
Tab::Logs => Tab::Tasks,
Tab::Delegation => Tab::Logs,
};
self.reset_selection();
}
pub fn previous_item(&mut self) {
match self.current_tab {
Tab::Agents => {
if self.selected_agent > 0 {
self.selected_agent -= 1;
}
}
Tab::Tasks => {
if self.selected_task > 0 {
self.selected_task -= 1;
}
}
Tab::Logs => {
if self.selected_log > 0 {
self.selected_log -= 1;
}
}
Tab::Delegation => {
if self.selected_delegation > 0 {
self.selected_delegation -= 1;
}
}
_ => {}
}
}
pub fn next_item(&mut self) {
match self.current_tab {
Tab::Agents => {
if self.selected_agent < self.agents.len().saturating_sub(1) {
self.selected_agent += 1;
}
}
Tab::Tasks => {
if self.selected_task < self.tasks.len().saturating_sub(1) {
self.selected_task += 1;
}
}
Tab::Logs => {
if self.selected_log < self.logs.len().saturating_sub(1) {
self.selected_log += 1;
}
}
Tab::Delegation => {
if self.selected_delegation < self.delegation_decisions.len().saturating_sub(1) {
self.selected_delegation += 1;
}
}
_ => {}
}
}
fn reset_selection(&mut self) {
match self.current_tab {
Tab::Agents => self.selected_agent = 0,
Tab::Tasks => self.selected_task = 0,
Tab::Logs => self.selected_log = 0,
Tab::Delegation => self.selected_delegation = 0,
_ => {}
}
}
pub async fn activate_selected(&mut self) -> Result<()> {
match self.current_tab {
Tab::Agents => {
if let Some(agent) = self.agents.get(self.selected_agent).cloned() {
match agent.status {
AgentStatus::Available => {
self.start_agent(&agent.id).await?;
}
AgentStatus::Working => {
self.show_agent_details(&agent.id).await?;
}
_ => {
self.show_agent_details(&agent.id).await?;
}
}
}
}
Tab::Tasks => {
if let Some(task) = self.tasks.get(self.selected_task) {
let task_id = task.id.clone();
self.show_task_details(&task_id).await?;
}
}
_ => {}
}
Ok(())
}
pub async fn start_agent(&mut self, agent_id: &str) -> Result<()> {
let mut agent_info = None;
if let Some(agent) = self.agents.iter_mut().find(|a| a.id == agent_id) {
agent.status = AgentStatus::Working;
agent.last_activity = Utc::now();
agent_info = Some((agent.name.clone(), agent.specialization.clone()));
self.active_agents += 1;
}
if let Some((name, specialization)) = agent_info {
self.add_log(
"System",
&format!("🚀 Starting agent: {} ({})", name, specialization),
)
.await;
if specialization.contains("Master") {
self.add_log("Master", "🎯 Master Claude Code orchestrator activated")
.await;
self.add_log("Master", "📋 Ready to coordinate multi-agent tasks")
.await;
}
}
Ok(())
}
pub async fn start_agent_by_id(&mut self, identifier: &str) -> Result<()> {
let agent_to_start = self
.agents
.iter()
.find(|a| a.id == identifier || a.name == identifier)
.map(|a| a.id.clone());
if let Some(agent_id) = agent_to_start {
self.start_agent(&agent_id).await?;
} else {
self.add_log("System", &format!("Agent not found: {}", identifier))
.await;
}
Ok(())
}
pub async fn create_new_session(&mut self) -> Result<()> {
self.input_mode = InputMode::CreatingAgent;
self.input_buffer.clear();
self.add_log("System", "Enter agent type (frontend/backend/devops/qa):")
.await;
Ok(())
}
pub async fn delete_current_session(&mut self) -> Result<()> {
if let Some(agent) = self.agents.get(self.selected_agent) {
self.add_log("System", &format!("Deleting agent: {}", agent.name))
.await;
}
Ok(())
}
pub async fn show_status(&mut self) -> Result<()> {
self.current_tab = Tab::Overview;
self.refresh_data().await?;
Ok(())
}
pub async fn add_task_prompt(&mut self) -> Result<()> {
self.input_mode = InputMode::AddingTask;
self.input_buffer.clear();
self.add_log("System", "Enter task description:").await;
Ok(())
}
pub async fn open_command_prompt(&mut self) -> Result<()> {
self.input_mode = InputMode::Command;
self.input_buffer.clear();
self.add_log("System", "Enter command (help for available commands):")
.await;
Ok(())
}
pub async fn refresh_data(&mut self) -> Result<()> {
self.load_agents().await?;
self.load_tasks().await?;
self.update_system_stats().await?;
self.last_update = Instant::now();
Ok(())
}
pub fn set_execution_engine(&mut self, engine: ExecutionEngine) {
self.execution_engine = Some(engine);
}
pub fn cancel_current_action(&mut self) {
self.input_mode = InputMode::Normal;
self.input_buffer.clear();
}
pub fn update_size(&mut self, width: u16, height: u16) {
self.terminal_width = width;
self.terminal_height = height;
}
pub async fn update(&mut self) -> Result<()> {
if self.last_update.elapsed() >= self.update_interval {
self.refresh_data().await?;
}
Ok(())
}
async fn load_agents(&mut self) -> Result<()> {
let statuses = self.status_tracker.get_all_statuses().await?;
self.agents.clear();
let master_agent = AgentInfo {
id: "master-claude-code".to_string(),
name: "master".to_string(),
specialization: "Master Claude Code".to_string(),
provider_type: "claude_code".to_string(),
provider_icon: "👑".to_string(),
provider_color: "gold".to_string(),
status: AgentStatus::Available,
current_task: None,
tasks_completed: 0,
last_activity: Utc::now(),
workspace: "/workspace".to_string(),
};
self.agents.push(master_agent);
let default_agents = vec![
("qa-agent", "qa", "QA Specialist"),
("devops-agent", "devops", "DevOps Specialist"),
("test-agent", "test", "Test Specialist"),
("error-agent", "error", "Error Handler"),
("backend-agent", "backend", "Backend Specialist"),
("frontend-agent", "frontend", "Frontend Specialist"),
];
for (id, name, spec) in default_agents {
let agent = AgentInfo {
id: id.to_string(),
name: name.to_string(),
specialization: spec.to_string(),
provider_type: "claude_code".to_string(),
provider_icon: "🤖".to_string(),
provider_color: "blue".to_string(),
status: AgentStatus::Available,
current_task: None,
tasks_completed: 0,
last_activity: Utc::now(),
workspace: "Unknown".to_string(),
};
self.agents.push(agent);
}
for status in statuses.iter() {
if let (Some(agent_id), Some(status_val)) = (
status.get("agent_id").and_then(|v| v.as_str()),
status.get("status").and_then(|v| v.as_str()),
) {
if self.agents.iter().any(|a| a.id == agent_id) {
continue;
}
let agent_info = AgentInfo {
id: agent_id.to_string(),
name: agent_id.split('-').next().unwrap_or("Unknown").to_string(),
specialization: status
.get("specialization")
.and_then(|v| v.as_str())
.unwrap_or("Unknown")
.to_string(),
provider_type: "claude_code".to_string(),
provider_icon: "🤖".to_string(),
provider_color: "blue".to_string(),
status: parse_agent_status(status_val),
current_task: status
.get("current_task")
.and_then(|v| v.as_str())
.map(|s| s.to_string()),
tasks_completed: status
.get("tasks_completed")
.and_then(|v| v.as_u64())
.unwrap_or(0) as u32,
last_activity: status
.get("timestamp")
.and_then(|v| v.as_str())
.and_then(|s| DateTime::parse_from_rfc3339(s).ok())
.map(|dt| dt.with_timezone(&Utc))
.unwrap_or_else(Utc::now),
workspace: status
.get("workspace")
.and_then(|v| v.as_str())
.unwrap_or("Unknown")
.to_string(),
};
self.agents.push(agent_info);
}
}
if self.selected_agent >= self.agents.len() {
self.selected_agent = 0;
}
Ok(())
}
async fn load_tasks(&mut self) -> Result<()> {
self.tasks.clear();
if let Some(ref execution_engine) = self.execution_engine {
let executor = execution_engine.get_executor();
let task_queue = executor.get_task_queue();
let all_tasks = task_queue.list_tasks(None, None).await;
for queued_task in all_tasks {
let status_str = match &queued_task.status {
TaskStatus::Pending => "⏳ Pending",
TaskStatus::Assigned { .. } => "📋 Assigned",
TaskStatus::InProgress { .. } => "🏃 In Progress",
TaskStatus::Completed { .. } => "✅ Completed",
TaskStatus::Failed { .. } => "❌ Failed",
TaskStatus::Cancelled { .. } => "🚫 Cancelled",
};
let task_info = TaskInfo {
id: queued_task.task.id.clone(),
description: queued_task.task.description.clone(),
priority: format!("{:?}", queued_task.task.priority),
task_type: format!("{}", queued_task.task.task_type), status: status_str.to_string(),
assigned_agent: queued_task.assigned_agent,
created_at: queued_task.created_at,
};
self.tasks.push(task_info);
}
} else {
let pending_tasks = self.task_queue.get_pending_tasks().await?;
for task in pending_tasks {
let task_info = TaskInfo {
id: task.id.clone(),
description: task.description.clone(),
priority: format!("{:?}", task.priority),
task_type: format!("{}", task.task_type),
status: "Pending".to_string(),
assigned_agent: None,
created_at: Utc::now(), };
self.tasks.push(task_info);
}
}
if self.selected_task >= self.tasks.len() {
self.selected_task = 0;
}
Ok(())
}
async fn update_system_stats(&mut self) -> Result<()> {
self.total_agents = self.agents.len();
self.active_agents = self
.agents
.iter()
.filter(|a| matches!(a.status, AgentStatus::Available | AgentStatus::Working))
.count();
if let Some(ref execution_engine) = self.execution_engine {
let executor = execution_engine.get_executor();
let queue_stats = executor.get_task_queue().get_stats().await;
let execution_stats = executor.get_stats().await;
self.pending_tasks = queue_stats.pending_count + queue_stats.active_count;
self.completed_tasks = execution_stats.tasks_succeeded;
self.tasks_executed = execution_stats.tasks_executed;
self.tasks_failed = execution_stats.tasks_failed;
self.orchestration_usage = execution_stats.orchestration_usage;
self.success_rate = if execution_stats.tasks_executed > 0 {
(execution_stats.tasks_succeeded as f64 / execution_stats.tasks_executed as f64)
* 100.0
} else {
100.0
};
self.system_status = if self.active_agents > 0 {
format!("Running ({:.1}% success)", self.success_rate)
} else {
"Stopped".to_string()
};
} else {
self.pending_tasks = self.tasks.len();
self.completed_tasks =
self.agents.iter().map(|a| a.tasks_completed).sum::<u32>() as usize;
self.system_status = if self.active_agents > 0 {
"Running".to_string()
} else {
"Stopped".to_string()
};
}
Ok(())
}
async fn show_agent_details(&mut self, agent_id: &str) -> Result<()> {
self.add_log(
"System",
&format!("Showing details for agent: {}", agent_id),
)
.await;
Ok(())
}
async fn show_task_details(&mut self, task_id: &str) -> Result<()> {
self.add_log("System", &format!("Showing details for task: {}", task_id))
.await;
Ok(())
}
async fn add_log(&mut self, source: &str, message: &str) {
let log_entry = LogEntry {
timestamp: Utc::now(),
level: "INFO".to_string(),
agent: if source == "System" {
None
} else {
Some(source.to_string())
},
message: message.to_string(),
};
self.logs.push(log_entry);
if self.logs.len() > 1000 {
self.logs.remove(0);
}
self.selected_log = self.logs.len().saturating_sub(1);
}
pub fn handle_char_input(&mut self, c: char) {
if self.input_mode != InputMode::Normal {
self.input_buffer.push(c);
}
}
pub fn handle_backspace(&mut self) {
if self.input_mode != InputMode::Normal && !self.input_buffer.is_empty() {
self.input_buffer.pop();
}
}
pub async fn process_input(&mut self) -> Result<()> {
let input = self.input_buffer.trim().to_string();
match self.input_mode {
InputMode::AddingTask => {
if !input.is_empty() {
self.execute_add_task(&input).await?;
}
}
InputMode::CreatingAgent => {
if !input.is_empty() {
self.execute_create_agent(&input).await?;
}
}
InputMode::Command => {
if !input.is_empty() {
self.execute_command(&input).await?;
}
}
InputMode::DelegationInput => {
if !input.is_empty() {
self.execute_delegation_action(&input).await?;
}
}
_ => {}
}
self.input_mode = InputMode::Normal;
self.input_buffer.clear();
Ok(())
}
async fn execute_add_task(&mut self, description: &str) -> Result<()> {
let (desc, priority, task_type) = self.parse_task_description(description);
let task =
crate::agent::Task::new(uuid::Uuid::new_v4().to_string(), desc, priority, task_type);
self.task_queue.add_task(&task).await?;
self.add_log("System", &format!("Task added: {}", task.description))
.await;
self.refresh_data().await?;
Ok(())
}
async fn execute_create_agent(&mut self, agent_type: &str) -> Result<()> {
let agent_type = agent_type.to_lowercase();
match agent_type.as_str() {
"frontend" | "backend" | "devops" | "qa" => {
self.add_log("System", &format!("Creating {} agent...", agent_type))
.await;
self.add_log(
"System",
&format!("{} agent created successfully", agent_type),
)
.await;
}
_ => {
self.add_log(
"System",
"Invalid agent type. Use: frontend, backend, devops, qa",
)
.await;
}
}
Ok(())
}
async fn execute_command(&mut self, command: &str) -> Result<()> {
let parts: Vec<&str> = command.split_whitespace().collect();
if parts.is_empty() {
return Ok(());
}
let cmd = parts[0].to_lowercase();
let args = &parts[1..];
match cmd.as_str() {
"help" => self.show_help().await,
"status" => self.show_detailed_status().await?,
"agents" => self.list_agents_command().await?,
"tasks" => self.list_tasks_command().await?,
"task" => {
if !args.is_empty() {
let full_desc = args.join(" ");
self.execute_add_task(&full_desc).await?;
} else {
self.add_log("System", "Usage: task <description>").await;
}
}
"agent" => {
if let Some(agent_type) = args.first() {
self.execute_create_agent(agent_type).await?;
} else {
self.add_log("System", "Usage: agent <type>").await;
}
}
"start_agent" | "activate" => {
if let Some(agent_id) = args.first() {
self.start_agent_by_id(agent_id).await?;
} else {
self.add_log("System", "Usage: start_agent <agent_id|agent_name>")
.await;
}
}
"start" => self.start_orchestrator().await?,
"stop" => self.stop_orchestrator().await?,
"refresh" => {
self.refresh_data().await?;
self.add_log("System", "Data refreshed").await;
}
"clear" => {
self.logs.clear();
self.add_log("System", "Logs cleared").await;
}
"worktree" => {
if args.is_empty() {
self.list_worktrees().await?;
} else {
match args[0] {
"list" => self.list_worktrees().await?,
"prune" => self.prune_worktrees().await?,
_ => self.add_log("System", "Usage: worktree [list|prune]").await,
}
}
}
_ => {
self.add_log(
"System",
&format!(
"Unknown command: {}. Type 'help' for available commands.",
cmd
),
)
.await;
}
}
Ok(())
}
async fn show_help(&mut self) {
let help_text = vec![
"Available commands:",
" help - Show this help",
" status - Show system status",
" agents - List all agents",
" tasks - List all tasks",
" task <description> - Add new task",
" agent <type> - Create new agent (frontend/backend/devops/qa)",
" start_agent <id|name> - Start/activate an agent (e.g., 'start_agent master')",
" activate <id|name> - Alias for start_agent",
" start - Start orchestrator",
" stop - Stop orchestrator",
" refresh - Refresh all data",
" clear - Clear logs",
" worktree [list|prune] - Manage worktrees",
];
for line in help_text {
self.add_log("System", line).await;
}
}
fn parse_task_description(
&self,
description: &str,
) -> (String, crate::agent::Priority, crate::agent::TaskType) {
use crate::agent::{Priority, TaskType};
let mut desc = description.to_string();
let mut priority = Priority::Medium;
let mut task_type = TaskType::Development;
if desc.contains("[high]") || desc.contains("[urgent]") {
priority = Priority::High;
desc = desc.replace("[high]", "").replace("[urgent]", "");
} else if desc.contains("[low]") {
priority = Priority::Low;
desc = desc.replace("[low]", "");
} else if desc.contains("[critical]") {
priority = Priority::Critical;
desc = desc.replace("[critical]", "");
}
if desc.contains("[test]") || desc.contains("[testing]") {
task_type = TaskType::Testing;
desc = desc.replace("[test]", "").replace("[testing]", "");
} else if desc.contains("[docs]") || desc.contains("[documentation]") {
task_type = TaskType::Documentation;
desc = desc.replace("[docs]", "").replace("[documentation]", "");
} else if desc.contains("[infra]") || desc.contains("[infrastructure]") {
task_type = TaskType::Infrastructure;
desc = desc.replace("[infra]", "").replace("[infrastructure]", "");
} else if desc.contains("[bug]") || desc.contains("[bugfix]") {
task_type = TaskType::Bugfix;
desc = desc.replace("[bug]", "").replace("[bugfix]", "");
} else if desc.contains("[feature]") {
task_type = TaskType::Feature;
desc = desc.replace("[feature]", "");
}
(desc.trim().to_string(), priority, task_type)
}
async fn show_detailed_status(&mut self) -> Result<()> {
self.add_log("System", "=== Detailed System Status ===")
.await;
self.add_log("System", &format!("System Status: {}", self.system_status))
.await;
self.add_log("System", &format!("Total Agents: {}", self.total_agents))
.await;
self.add_log("System", &format!("Active Agents: {}", self.active_agents))
.await;
self.add_log("System", &format!("Pending Tasks: {}", self.pending_tasks))
.await;
self.add_log(
"System",
&format!("Completed Tasks: {}", self.completed_tasks),
)
.await;
if let Some(master) = self
.agents
.iter()
.find(|a| a.specialization.contains("Master"))
{
self.add_log(
"System",
&format!("👑 Master Claude Code: {:?}", master.status),
)
.await;
}
Ok(())
}
async fn list_agents_command(&mut self) -> Result<()> {
if self.agents.is_empty() {
self.add_log("System", "No agents found").await;
} else {
self.add_log("System", "Active agents:").await;
let agent_info: Vec<String> = self
.agents
.iter()
.map(|agent| {
format!(
" {} ({}) - {:?}",
agent.name, agent.specialization, agent.status
)
})
.collect();
for info in agent_info {
self.add_log("System", &info).await;
}
}
Ok(())
}
async fn list_tasks_command(&mut self) -> Result<()> {
if self.tasks.is_empty() {
self.add_log("System", "No pending tasks").await;
} else {
self.add_log("System", "Pending tasks:").await;
let task_info: Vec<String> = self
.tasks
.iter()
.map(|task| {
format!(
" {} - {} ({})",
task.description, task.priority, task.task_type
)
})
.collect();
for info in task_info {
self.add_log("System", &info).await;
}
}
Ok(())
}
async fn start_orchestrator(&mut self) -> Result<()> {
self.add_log("System", "Starting orchestrator...").await;
self.system_status = "Running".to_string();
self.add_log("System", "Orchestrator started successfully")
.await;
Ok(())
}
async fn stop_orchestrator(&mut self) -> Result<()> {
self.add_log("System", "Stopping orchestrator...").await;
self.system_status = "Stopped".to_string();
self.add_log("System", "Orchestrator stopped").await;
Ok(())
}
async fn list_worktrees(&mut self) -> Result<()> {
self.add_log("System", "Git worktrees:").await;
self.add_log("System", " No worktrees found").await;
Ok(())
}
async fn prune_worktrees(&mut self) -> Result<()> {
self.add_log("System", "Pruning stale worktrees...").await;
self.add_log("System", "Worktree pruning completed").await;
Ok(())
}
pub fn switch_delegation_mode(&mut self) {
self.delegation_mode = match self.delegation_mode {
DelegationMode::Analyze => DelegationMode::Delegate,
DelegationMode::Delegate => DelegationMode::ViewStats,
DelegationMode::ViewStats => DelegationMode::Analyze,
};
}
pub async fn start_delegation_input(&mut self) -> Result<()> {
self.input_mode = InputMode::DelegationInput;
self.delegation_input.clear();
match self.delegation_mode {
DelegationMode::Analyze => {
self.add_log("Master", "Enter task description to analyze:")
.await;
}
DelegationMode::Delegate => {
self.add_log("Master", "Enter task description to delegate:")
.await;
}
_ => {}
}
Ok(())
}
async fn execute_delegation_action(&mut self, input: &str) -> Result<()> {
match self.delegation_mode {
DelegationMode::Analyze => {
self.analyze_task_for_delegation(input).await?;
}
DelegationMode::Delegate => {
self.delegate_task_to_agent(input).await?;
}
_ => {}
}
Ok(())
}
async fn analyze_task_for_delegation(&mut self, task_description: &str) -> Result<()> {
self.add_log(
"Master",
&format!("🔍 Analyzing task: '{}'", task_description),
)
.await;
let (recommended_agent, confidence, reasoning) =
self.analyze_task_content(task_description);
let delegation_info = DelegationInfo {
task_description: task_description.to_string(),
recommended_agent: recommended_agent.clone(),
confidence,
reasoning: reasoning.clone(),
created_at: chrono::Utc::now(),
};
self.delegation_decisions.push(delegation_info);
self.add_log(
"Master",
&format!(
"✅ Analysis complete: {} agent recommended ({:.1}% confidence)",
recommended_agent,
confidence * 100.0
),
)
.await;
self.add_log("Master", &format!("📝 Reasoning: {}", reasoning))
.await;
Ok(())
}
async fn delegate_task_to_agent(&mut self, input: &str) -> Result<()> {
let parts: Vec<&str> = input.splitn(2, ' ').collect();
if parts.len() < 2 {
self.add_log("Master", "Usage: <agent_type> <task_description>")
.await;
return Ok(());
}
let agent_type = parts[0];
let task_description = parts[1];
let valid_agents = ["frontend", "backend", "devops", "qa"];
if !valid_agents.contains(&agent_type) {
self.add_log(
"Master",
&format!(
"Invalid agent type: {}. Valid agents: {}",
agent_type,
valid_agents.join(", ")
),
)
.await;
return Ok(());
}
self.add_log(
"Master",
&format!(
"🎯 Delegating task to {} agent: '{}'",
agent_type, task_description
),
)
.await;
let task = crate::agent::Task::new(
uuid::Uuid::new_v4().to_string(),
task_description.to_string(),
crate::agent::Priority::Medium,
crate::agent::TaskType::Development,
);
self.task_queue.add_task(&task).await?;
let delegation_info = DelegationInfo {
task_description: task_description.to_string(),
recommended_agent: agent_type.to_string(),
confidence: 1.0, reasoning: "Manual delegation by Master".to_string(),
created_at: chrono::Utc::now(),
};
self.delegation_decisions.push(delegation_info);
self.add_log(
"Master",
&format!("✅ Task delegated to {} agent successfully", agent_type),
)
.await;
self.refresh_data().await?;
Ok(())
}
fn analyze_task_content(&self, task_description: &str) -> (String, f64, String) {
let desc_lower = task_description.to_lowercase();
if desc_lower.contains("ui")
|| desc_lower.contains("html")
|| desc_lower.contains("css")
|| desc_lower.contains("javascript")
|| desc_lower.contains("component")
|| desc_lower.contains("react")
|| desc_lower.contains("vue")
|| desc_lower.contains("frontend")
{
return (
"Frontend".to_string(),
0.9,
"Contains UI/frontend keywords".to_string(),
);
}
if desc_lower.contains("api")
|| desc_lower.contains("server")
|| desc_lower.contains("database")
|| desc_lower.contains("backend")
|| desc_lower.contains("endpoint")
|| desc_lower.contains("node")
|| desc_lower.contains("express")
|| desc_lower.contains("rest")
{
return (
"Backend".to_string(),
0.9,
"Contains API/backend keywords".to_string(),
);
}
if desc_lower.contains("test")
|| desc_lower.contains("testing")
|| desc_lower.contains("qa")
|| desc_lower.contains("quality")
|| desc_lower.contains("validation")
|| desc_lower.contains("unit")
{
return (
"QA".to_string(),
0.9,
"Contains testing/QA keywords".to_string(),
);
}
if desc_lower.contains("deploy")
|| desc_lower.contains("ci/cd")
|| desc_lower.contains("docker")
|| desc_lower.contains("infrastructure")
|| desc_lower.contains("pipeline")
|| desc_lower.contains("build")
|| desc_lower.contains("devops")
{
return (
"DevOps".to_string(),
0.9,
"Contains infrastructure/DevOps keywords".to_string(),
);
}
(
"Backend".to_string(),
0.6,
"General development task, defaulting to backend".to_string(),
)
}
pub fn get_delegation_stats(&self) -> String {
if self.delegation_decisions.is_empty() {
return "No delegation decisions yet".to_string();
}
let total = self.delegation_decisions.len();
let mut agent_counts = std::collections::HashMap::new();
let mut total_confidence = 0.0;
for decision in &self.delegation_decisions {
*agent_counts
.entry(decision.recommended_agent.clone())
.or_insert(0) += 1;
total_confidence += decision.confidence;
}
let avg_confidence = total_confidence / total as f64;
let mut stats = "📊 Delegation Statistics:\n".to_string();
stats.push_str(&format!("Total delegations: {}\n", total));
stats.push_str(&format!(
"Average confidence: {:.1}%\n",
avg_confidence * 100.0
));
stats.push_str("Agent distribution:\n");
for (agent, count) in agent_counts {
let percentage = (count as f64 / total as f64) * 100.0;
stats.push_str(&format!(" {}: {} ({:.1}%)\n", agent, count, percentage));
}
stats
}
pub async fn handle_delegation_enter(&mut self) -> Result<()> {
match self.delegation_mode {
DelegationMode::Analyze | DelegationMode::Delegate => {
self.start_delegation_input().await?;
}
DelegationMode::ViewStats => {
let stats = self.get_delegation_stats();
for line in stats.lines() {
self.add_log("Master", line).await;
}
}
}
Ok(())
}
pub fn handle_key(&mut self, key: crossterm::event::KeyEvent) -> Result<()> {
use crossterm::event::{KeyCode, KeyModifiers};
match self.input_mode {
InputMode::Normal => {
match key.code {
KeyCode::Char('q') if key.modifiers.contains(KeyModifiers::CONTROL) => {
self.should_quit = true;
}
KeyCode::Tab => self.next_tab(),
KeyCode::BackTab => self.previous_tab(),
KeyCode::Up | KeyCode::Char('k') => self.previous_item(),
KeyCode::Down | KeyCode::Char('j') => self.next_item(),
KeyCode::Enter => {
tokio::spawn({
let mut app_clone = self.clone_for_async();
async move {
let _ = app_clone.activate_selected().await;
}
});
}
KeyCode::Char('n') => {
tokio::spawn({
let mut app_clone = self.clone_for_async();
async move {
let _ = app_clone.create_new_session().await;
}
});
}
KeyCode::Char('t') => {
tokio::spawn({
let mut app_clone = self.clone_for_async();
async move {
let _ = app_clone.add_task_prompt().await;
}
});
}
KeyCode::Char(':') => {
self.input_mode = InputMode::Command;
self.input_buffer.clear();
}
_ => {}
}
}
InputMode::AddingTask
| InputMode::CreatingAgent
| InputMode::Command
| InputMode::DelegationInput => {
match key.code {
KeyCode::Esc => {
self.input_mode = InputMode::Normal;
self.input_buffer.clear();
}
KeyCode::Enter => {
let input = self.input_buffer.clone();
let mode = self.input_mode.clone();
self.input_mode = InputMode::Normal;
self.input_buffer.clear();
tokio::spawn({
let mut app_clone = self.clone_for_async();
async move {
match mode {
InputMode::AddingTask => {
let _ = app_clone.add_task(&input).await;
}
InputMode::CreatingAgent => {
let _ = app_clone.create_agent(&input).await;
}
InputMode::Command => {
let _ = app_clone.execute_command(&input).await;
}
InputMode::DelegationInput => {
let _ = app_clone.handle_delegation_input(&input).await;
}
_ => {}
}
}
});
}
KeyCode::Char(c) => {
self.input_buffer.push(c);
}
KeyCode::Backspace => {
self.input_buffer.pop();
}
_ => {}
}
}
}
Ok(())
}
pub fn handle_mouse(&mut self, mouse: crossterm::event::MouseEvent) -> Result<()> {
use crossterm::event::{MouseButton, MouseEventKind};
match mouse.kind {
MouseEventKind::Down(MouseButton::Left) => {
if mouse.row == 0 {
let tab_width = self.terminal_width / 5; let tab_index = mouse.column / tab_width;
self.current_tab = match tab_index {
0 => Tab::Overview,
1 => Tab::Agents,
2 => Tab::Tasks,
3 => Tab::Logs,
4 => Tab::Delegation,
_ => self.current_tab.clone(),
};
self.reset_selection();
}
}
MouseEventKind::ScrollDown => {
self.next_item();
}
MouseEventKind::ScrollUp => {
self.previous_item();
}
_ => {}
}
Ok(())
}
pub fn handle_resize(&mut self, width: u16, height: u16) -> Result<()> {
self.terminal_width = width;
self.terminal_height = height;
Ok(())
}
pub async fn add_task(&mut self, description: &str) -> Result<()> {
let task = TaskInfo {
id: format!("task-{}", uuid::Uuid::new_v4()),
description: description.to_string(),
priority: "Medium".to_string(),
task_type: "Development".to_string(),
status: "Pending".to_string(),
assigned_agent: None,
created_at: Utc::now(),
};
self.tasks.push(task);
self.pending_tasks += 1;
self.add_log("System", &format!("Task added: {}", description))
.await;
Ok(())
}
pub async fn create_agent(&mut self, agent_type: &str) -> Result<()> {
let agent = AgentInfo {
id: format!("agent-{}", uuid::Uuid::new_v4()),
name: format!("{}-specialist", agent_type),
specialization: agent_type.to_string(),
provider_type: "Claude Code".to_string(),
provider_icon: "🤖".to_string(),
provider_color: "#4CAF50".to_string(),
status: AgentStatus::Available,
current_task: None,
tasks_completed: 0,
last_activity: Utc::now(),
workspace: format!("./workspace/{}", agent_type),
};
self.agents.push(agent);
self.total_agents += 1;
self.add_log(
"System",
&format!("Agent created: {}-specialist", agent_type),
)
.await;
Ok(())
}
pub async fn handle_delegation_input(&mut self, input: &str) -> Result<()> {
match self.delegation_mode {
DelegationMode::Analyze => {
self.add_log("Delegation", &format!("Analyzing: {}", input))
.await;
let decision = DelegationInfo {
task_description: input.to_string(),
recommended_agent: "frontend-specialist".to_string(),
confidence: 0.85,
reasoning: "Task requires frontend expertise".to_string(),
created_at: Utc::now(),
};
self.delegation_decisions.push(decision);
}
DelegationMode::Delegate => {
self.add_log("Delegation", &format!("Delegating: {}", input))
.await;
if let Some(task) = self.tasks.iter_mut().find(|t| t.status == "Pending") {
task.assigned_agent = Some("frontend-specialist".to_string());
task.status = "Assigned".to_string();
}
}
DelegationMode::ViewStats => {
self.add_log("Delegation", "Viewing delegation statistics")
.await;
}
}
Ok(())
}
fn clone_for_async(&self) -> Self {
Self {
current_tab: self.current_tab.clone(),
selected_agent: self.selected_agent,
selected_task: self.selected_task,
selected_log: self.selected_log,
selected_delegation: self.selected_delegation,
agents: self.agents.clone(),
tasks: self.tasks.clone(),
logs: self.logs.clone(),
delegation_decisions: self.delegation_decisions.clone(),
system_status: self.system_status.clone(),
total_agents: self.total_agents,
active_agents: self.active_agents,
pending_tasks: self.pending_tasks,
completed_tasks: self.completed_tasks,
tasks_executed: self.tasks_executed,
tasks_failed: self.tasks_failed,
success_rate: self.success_rate,
orchestration_usage: self.orchestration_usage,
total_sessions: self.total_sessions,
active_sessions: self.active_sessions,
multi_agent_enabled: self.multi_agent_enabled,
input_mode: self.input_mode.clone(),
input_buffer: self.input_buffer.clone(),
delegation_mode: self.delegation_mode.clone(),
delegation_input: self.delegation_input.clone(),
coordination_bus: self.coordination_bus.clone(),
status_tracker: self.status_tracker.clone(),
task_queue: self.task_queue.clone(),
execution_engine: self.execution_engine.clone(),
terminal_width: self.terminal_width,
terminal_height: self.terminal_height,
last_update: self.last_update,
update_interval: self.update_interval,
should_quit: self.should_quit,
}
}
}
fn parse_agent_status(status: &str) -> AgentStatus {
match status {
"Initializing" => AgentStatus::Initializing,
"Available" => AgentStatus::Available,
"Working" => AgentStatus::Working,
"WaitingForReview" => AgentStatus::WaitingForReview,
"ShuttingDown" => AgentStatus::ShuttingDown,
_ => AgentStatus::Error(format!("Unknown status: {}", status)),
}
}