collet 0.1.1

Relentless agentic coding orchestrator with zero-drop agent loops
Documentation
use super::super::App;

impl App {
    pub(crate) fn handle_swarm_command(&mut self, input: &str) -> Option<String> {
        // /attach and /detach work even without swarm_knowledge
        if input == "/detach" {
            if matches!(
                self.state.view_mode,
                crate::tui::state::ViewMode::WorkerAttached { .. }
            ) {
                self.state.detach_worker();
                return Some("Detached from worker.".to_string());
            }
            return Some("Not attached to any worker.".to_string());
        }
        if let Some(agent_id) = input.strip_prefix("/attach ").map(str::trim) {
            if agent_id.is_empty() {
                return Some("Usage: /attach <agent_id>".to_string());
            }
            // Verify this agent exists in swarm status
            let exists = self
                .state
                .swarm_status
                .as_ref()
                .map(|h| h.agents.iter().any(|a| a.agent_id == agent_id))
                .unwrap_or(false);
            if !exists {
                return Some(format!("Worker `{agent_id}` not found."));
            }
            self.state.attach_worker(agent_id);
            return Some(format!(
                "Attached to worker `{agent_id}`. Press Esc to detach."
            ));
        }

        let kb = self.swarm_knowledge.as_ref()?;

        if let Some(agent_id) = input.strip_prefix("/cancel-worker ").map(str::trim) {
            if agent_id.is_empty() {
                return Some("Usage: /cancel-worker <agent_id>".to_string());
            }
            let kb = kb.clone();
            let id = agent_id.to_string();
            tokio::spawn(async move {
                kb.cancel_worker(&id).await;
            });
            return Some(format!("Cancelling worker `{agent_id}`..."));
        }

        if let Some(rest) = input.strip_prefix("/redirect-worker ").map(str::trim) {
            let (agent_id, instruction) = rest.split_once(' ').unwrap_or((rest, ""));
            if agent_id.is_empty() || instruction.is_empty() {
                return Some("Usage: /redirect-worker <agent_id> <new focus>".to_string());
            }
            let kb = kb.clone();
            let id = agent_id.to_string();
            let instr = crate::agent::swarm::knowledge::WorkerInstruction::Redirect {
                new_focus: instruction.to_string(),
            };
            tokio::spawn(async move {
                kb.send_instruction(&id, instr).await;
            });
            return Some(format!("Redirecting worker `{agent_id}`..."));
        }

        if let Some(rest) = input.strip_prefix("/hint-worker ").map(str::trim) {
            let (agent_id, text) = rest.split_once(' ').unwrap_or((rest, ""));
            if agent_id.is_empty() || text.is_empty() {
                return Some("Usage: /hint-worker <agent_id> <hint text>".to_string());
            }
            let kb = kb.clone();
            let id = agent_id.to_string();
            let instr = crate::agent::swarm::knowledge::WorkerInstruction::Hint {
                text: text.to_string(),
            };
            tokio::spawn(async move {
                kb.send_instruction(&id, instr).await;
            });
            return Some(format!("Sent hint to worker `{agent_id}`."));
        }

        if let Some(rest) = input.strip_prefix("/pause-worker ").map(str::trim) {
            if rest.is_empty() {
                return Some("Usage: /pause-worker <agent_id>".to_string());
            }
            let kb = kb.clone();
            let id = rest.to_string();
            let instr = crate::agent::swarm::knowledge::WorkerInstruction::Pause;
            tokio::spawn(async move {
                kb.send_instruction(&id, instr).await;
            });
            return Some(format!("Pausing worker `{rest}`..."));
        }

        if let Some(rest) = input.strip_prefix("/resume-worker ").map(str::trim) {
            if rest.is_empty() {
                return Some("Usage: /resume-worker <agent_id>".to_string());
            }
            let kb = kb.clone();
            let id = rest.to_string();
            let instr = crate::agent::swarm::knowledge::WorkerInstruction::Resume;
            tokio::spawn(async move {
                kb.send_instruction(&id, instr).await;
            });
            return Some(format!("Resuming worker `{rest}`..."));
        }

        if let Some(rest) = input.strip_prefix("/extend-worker ").map(str::trim) {
            let parts: Vec<&str> = rest.splitn(2, ' ').collect();
            let agent_id = parts.first().copied().unwrap_or("");
            let extra: u32 = parts.get(1).and_then(|s| s.parse().ok()).unwrap_or(10);
            if agent_id.is_empty() {
                return Some("Usage: /extend-worker <agent_id> [iterations]".to_string());
            }
            let kb = kb.clone();
            let id = agent_id.to_string();
            tokio::spawn(async move {
                kb.extend_worker(&id, extra).await;
            });
            return Some(format!(
                "Extending worker `{agent_id}` by {extra} iterations."
            ));
        }

        if input == "/workers" || input == "/list-workers" {
            let kb = kb.clone();
            // Active worker IDs — can't await here so we show from TUI state.
            if let Some(ref hive) = self.state.swarm_status {
                let lines: Vec<String> = hive
                    .agents
                    .iter()
                    .map(|a| {
                        let status = match &a.status {
                            crate::tui::state::SwarmAgentStatus::Pending => "⏳ pending",
                            crate::tui::state::SwarmAgentStatus::Running => "▶ running",
                            crate::tui::state::SwarmAgentStatus::Paused => "⏸ paused",
                            crate::tui::state::SwarmAgentStatus::Completed { success } => {
                                if *success {
                                    "✓ done"
                                } else {
                                    "✗ failed"
                                }
                            }
                        };
                        let tool_info = a
                            .current_tool
                            .as_deref()
                            .map(|t| format!(" ({})", t))
                            .unwrap_or_default();
                        format!(
                            "  {} [{}] — {}{} | {} tools",
                            a.agent_id, status, a.task_preview, tool_info, a.tool_calls
                        )
                    })
                    .collect();
                return Some(format!("Active workers:\n{}", lines.join("\n")));
            }
            let _ = kb;
            return Some("No active swarm.".to_string());
        }

        None
    }
}