j-cli 12.9.5

A fast CLI tool for alias management, daily reports, and productivity
use super::super::{PlanDecision, Tool, ToolResult, parse_tool_args, schema_to_tool_params};
use super::entity::TodoItem;
use super::todo_manager::TodoManager;
use schemars::JsonSchema;
use serde::Deserialize;
use serde_json::Value;
use std::sync::{Arc, atomic::AtomicBool};

/// Todo 项参数
#[derive(Deserialize, JsonSchema)]
struct TodoItemParam {
    /// Item ID. Required when merge=true to update existing items. Auto-generated if omitted.
    #[serde(default)]
    id: Option<String>,
    /// The todo item text
    content: String,
    /// Item status: pending, in_progress, completed, or cancelled
    #[serde(default = "default_status")]
    status: String,
}

fn default_status() -> String {
    "pending".to_string()
}

/// TodoWriteTool 参数
#[derive(Deserialize, JsonSchema)]
struct TodoWriteParams {
    /// Array of todo items
    todos: Vec<TodoItemParam>,
    /// If false (default), replace the entire list. If true, only update/add the provided items by id.
    #[serde(default)]
    merge: bool,
}

#[derive(Debug)]
pub struct TodoWriteTool {
    pub manager: Arc<TodoManager>,
}

impl TodoWriteTool {
    pub const NAME: &'static str = "TodoWrite";
}

impl Tool for TodoWriteTool {
    fn name(&self) -> &str {
        Self::NAME
    }

    fn description(&self) -> &str {
        r#"
        Create and manage a structured todo list to maintain state across long turns.

        CRITICAL RULES:
        1. Only ONE item can be 'in_progress' at any time; the system enforces this automatically.
        2. For updates, always use 'merge=true' and only provide the specific items being modified.
        3. Support batch updates: efficiently transition states by marking a task 'completed' and the next 'in_progress' in a single call.
        4. Use this to demonstrate progress and ensure complex requirements are not missed.
        "#
    }

    fn parameters_schema(&self) -> Value {
        schema_to_tool_params::<TodoWriteParams>()
    }

    fn execute(&self, arguments: &str, _cancelled: &Arc<AtomicBool>) -> ToolResult {
        let params: TodoWriteParams = match parse_tool_args(arguments) {
            Ok(p) => p,
            Err(e) => return e,
        };

        let items: Vec<TodoItem> = params
            .todos
            .iter()
            .map(|item| TodoItem {
                id: item.id.clone().unwrap_or_default(),
                content: item.content.clone(),
                status: item.status.clone(),
            })
            .collect();

        match self.manager.write_todos(items, params.merge) {
            Ok(all_todos) => ToolResult {
                output: serde_json::to_string_pretty(&all_todos).unwrap_or_default(),
                is_error: false,
                images: vec![],
                plan_decision: PlanDecision::None,
            },
            Err(e) => ToolResult {
                output: e,
                is_error: true,
                images: vec![],
                plan_decision: PlanDecision::None,
            },
        }
    }
}