kota 0.1.3

A lightweight, highly extensible ai code agent, built in Rust.
Documentation
use serde::{Deserialize, Serialize};
use std::sync::{Arc, Mutex};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum TaskStatus {
    Pending,
    InProgress,
    Completed,
    Blocked,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Task {
    pub id: usize,
    pub description: String,
    pub status: TaskStatus,
    pub dependencies: Vec<usize>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Plan {
    pub title: String,
    pub tasks: Vec<Task>,
    pub next_id: usize,
}

impl Plan {
    pub fn new(title: String) -> Self {
        Self {
            title,
            tasks: Vec::new(),
            next_id: 1,
        }
    }

    pub fn add_task(&mut self, description: String, dependencies: Vec<usize>) -> usize {
        let id = self.next_id;
        self.next_id += 1;
        self.tasks.push(Task {
            id,
            description,
            status: TaskStatus::Pending,
            dependencies,
        });
        id
    }

    pub fn update_status(&mut self, task_id: usize, status: TaskStatus) -> bool {
        if let Some(task) = self.tasks.iter_mut().find(|t| t.id == task_id) {
            task.status = status;
            true
        } else {
            false
        }
    }

    pub fn get_next_tasks(&self) -> Vec<&Task> {
        self.tasks
            .iter()
            .filter(|t| matches!(t.status, TaskStatus::Pending))
            .filter(|t| {
                t.dependencies.iter().all(|dep_id| {
                    self.tasks
                        .iter()
                        .find(|task| task.id == *dep_id)
                        .map(|dep| matches!(dep.status, TaskStatus::Completed))
                        .unwrap_or(false)
                })
            })
            .collect()
    }

    pub fn format(&self) -> String {
        let mut output = format!("📋 Plan: {}\n\n", self.title);

        for task in &self.tasks {
            let status_icon = match task.status {
                TaskStatus::Pending => "",
                TaskStatus::InProgress => "🔄",
                TaskStatus::Completed => "",
                TaskStatus::Blocked => "🚫",
            };

            output.push_str(&format!(
                "{} [{}] {}\n",
                status_icon, task.id, task.description
            ));

            if !task.dependencies.is_empty() {
                output.push_str(&format!("   Dependencies: {:?}\n", task.dependencies));
            }
        }

        let next_tasks = self.get_next_tasks();
        if !next_tasks.is_empty() {
            output.push_str("\n🎯 Next available tasks:\n");
            for task in next_tasks {
                output.push_str(&format!("   • [{}] {}\n", task.id, task.description));
            }
        }

        output
    }
}

#[derive(Clone)]
pub struct PlanManager {
    current_plan: Arc<Mutex<Option<Plan>>>,
}

impl PlanManager {
    pub fn new() -> Self {
        Self {
            current_plan: Arc::new(Mutex::new(None)),
        }
    }

    pub fn get_plan(&self) -> Option<Plan> {
        self.current_plan.lock().ok()?.clone()
    }

    pub fn set_plan(&self, plan: Plan) {
        *self.current_plan.lock().unwrap() = Some(plan);
    }

    pub fn clear_plan(&self) {
        *self.current_plan.lock().unwrap() = None;
    }

    pub fn update_plan<F>(&self, f: F) -> bool
    where
        F: FnOnce(&mut Plan),
    {
        let mut guard = self.current_plan.lock().unwrap();
        if let Some(plan) = guard.as_mut() {
            f(plan);
            true
        } else {
            false
        }
    }
}

impl Default for PlanManager {
    fn default() -> Self {
        Self::new()
    }
}