Skip to main content

steer_core/tools/static_tools/
todo.rs

1use async_trait::async_trait;
2
3use crate::tools::capability::Capabilities;
4use crate::tools::static_tool::{StaticTool, StaticToolContext, StaticToolError};
5use steer_tools::result::{TodoListResult, TodoWriteResult};
6use steer_tools::tools::todo::TodoWriteFileOperation;
7use steer_tools::tools::todo::read::{TodoReadError, TodoReadParams, TodoReadToolSpec};
8use steer_tools::tools::todo::write::{TodoWriteError, TodoWriteParams, TodoWriteToolSpec};
9
10const TODO_READ_DESCRIPTION: &str = r#"Use this tool to read the current to-do list for the session. This tool should be used proactively and frequently to ensure that you are aware of
11the status of the current task list. You should make use of this tool as often as possible, especially in the following situations:
12- At the beginning of conversations to see what's pending
13- Before starting new tasks to prioritize work
14- When the user asks about previous tasks or plans
15- Whenever you're uncertain about what to do next
16- After completing tasks to update your understanding of remaining work
17- After every few messages to ensure you're on track
18
19Usage:
20- This tool takes in no parameters. So leave the input blank or empty. DO NOT include a dummy object, placeholder string or a key like "input" or "empty". LEAVE IT BLANK.
21- Returns a list of todo items with their status, priority, and content
22- Use this information to track progress and plan next steps
23- If no todos exist yet, an empty list will be returned"#;
24
25const TODO_WRITE_DESCRIPTION: &str = r"Use this tool to create and manage a structured task list for your current coding session. This helps you track progress, organize complex tasks, and demonstrate thoroughness to the user.
26
27When to Use This Tool:
281. Complex multi-step tasks - When a task requires 3 or more distinct steps
292. Non-trivial tasks - Tasks requiring careful planning
303. User explicitly requests todo list
314. User provides multiple tasks
325. After receiving new instructions
336. After completing a task - Mark it complete
34
35When NOT to Use This Tool:
361. Single, straightforward tasks
372. Trivial tasks
383. Tasks completed in less than 3 steps
394. Purely conversational requests
40
41Task States:
42- pending: Task not yet started
43- in_progress: Currently working on (limit to ONE at a time)
44- completed: Task finished successfully
45
46Task Management:
47- Update status in real-time
48- Mark tasks complete IMMEDIATELY after finishing
49- Only have ONE task in_progress at any time";
50
51pub struct TodoReadTool;
52
53#[async_trait]
54impl StaticTool for TodoReadTool {
55    type Params = TodoReadParams;
56    type Output = TodoListResult;
57    type Spec = TodoReadToolSpec;
58
59    const DESCRIPTION: &'static str = TODO_READ_DESCRIPTION;
60    const REQUIRES_APPROVAL: bool = false;
61    const REQUIRED_CAPABILITIES: Capabilities = Capabilities::WORKSPACE;
62
63    async fn execute(
64        &self,
65        _params: Self::Params,
66        ctx: &StaticToolContext,
67    ) -> Result<Self::Output, StaticToolError<TodoReadError>> {
68        if ctx.is_cancelled() {
69            return Err(StaticToolError::Cancelled);
70        }
71
72        let todos = ctx
73            .services
74            .event_store
75            .load_todos(ctx.session_id)
76            .await
77            .map_err(|e| {
78                StaticToolError::execution(TodoReadError::Io {
79                    message: e.to_string(),
80                })
81            })?
82            .unwrap_or_default();
83
84        Ok(TodoListResult { todos })
85    }
86}
87
88pub struct TodoWriteTool;
89
90#[async_trait]
91impl StaticTool for TodoWriteTool {
92    type Params = TodoWriteParams;
93    type Output = TodoWriteResult;
94    type Spec = TodoWriteToolSpec;
95
96    const DESCRIPTION: &'static str = TODO_WRITE_DESCRIPTION;
97    const REQUIRES_APPROVAL: bool = false;
98    const REQUIRED_CAPABILITIES: Capabilities = Capabilities::WORKSPACE;
99
100    async fn execute(
101        &self,
102        params: Self::Params,
103        ctx: &StaticToolContext,
104    ) -> Result<Self::Output, StaticToolError<TodoWriteError>> {
105        if ctx.is_cancelled() {
106            return Err(StaticToolError::Cancelled);
107        }
108
109        let existing = ctx
110            .services
111            .event_store
112            .load_todos(ctx.session_id)
113            .await
114            .map_err(|e| {
115                StaticToolError::execution(TodoWriteError::Io {
116                    message: e.to_string(),
117                })
118            })?;
119
120        ctx.services
121            .event_store
122            .save_todos(ctx.session_id, &params.todos)
123            .await
124            .map_err(|e| {
125                StaticToolError::execution(TodoWriteError::Io {
126                    message: e.to_string(),
127                })
128            })?;
129
130        let operation = if existing.is_some() {
131            TodoWriteFileOperation::Modified
132        } else {
133            TodoWriteFileOperation::Created
134        };
135
136        Ok(TodoWriteResult {
137            todos: params.todos,
138            operation,
139        })
140    }
141}