Skip to content

Commit b26c006

Browse files
author
kiran-garre
committed
refactor: Consolidate task logic, parameterize local directory name
1 parent d2a8936 commit b26c006

File tree

3 files changed

+46
-39
lines changed

3 files changed

+46
-39
lines changed

crates/chat-cli/src/cli/chat/cli/todos.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ impl TodoSubcommand {
7777
let mut cleared_one = false;
7878

7979
for todo_status in todos.iter() {
80-
if todo_status.completed.iter().all(|b| *b) {
80+
if todo_status.tasks.iter().all(|b| b.completed) {
8181
match delete_todo(os, &todo_status.id).await {
8282
Ok(_) => cleared_one = true,
8383
Err(e) => {
@@ -185,8 +185,8 @@ impl TodoSubcommand {
185185
let (todos, _) = get_all_todos(os).await?;
186186
for todo in todos.iter() {
187187
out.push(TodoDisplayEntry {
188-
num_completed: todo.completed.iter().filter(|b| **b).count(),
189-
num_tasks: todo.completed.len(),
188+
num_completed: todo.tasks.iter().filter(|t| t.completed).count(),
189+
num_tasks: todo.tasks.len(),
190190
description: todo.description.clone(),
191191
id: todo.id.clone(),
192192
});

crates/chat-cli/src/cli/chat/mod.rs

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,6 @@ use crate::cli::chat::cli::prompts::{
145145
PromptsSubcommand,
146146
};
147147
use crate::cli::chat::message::UserMessage;
148-
use crate::cli::chat::tools::ToolOrigin;
149148
use crate::cli::chat::util::sanitize_unicode_tags;
150149
use crate::database::settings::Setting;
151150
use crate::mcp_client::Prompt;
@@ -2878,7 +2877,7 @@ impl ChatSession {
28782877
Ok(s) => s,
28792878
Err(e) => return Err(ChatError::Custom(format!("Error deserializing todo list: {e}").into())),
28802879
};
2881-
let summary_content = format!(
2880+
let request_content = format!(
28822881
"[SYSTEM NOTE: This is an automated request, not from the user]\n
28832882
Read the TODO list contents below and understand the task description, completed tasks, and provided context.\n
28842883
Call the `load` command of the todo_list tool with the given ID as an argument to display the TODO list to the user and officially resume execution of the TODO list tasks.\n
@@ -2889,23 +2888,13 @@ impl ChatSession {
28892888
id
28902889
);
28912890

2892-
let summary_message = UserMessage::new_prompt(summary_content.clone(), None);
2891+
let summary_message = UserMessage::new_prompt(request_content.clone(), None);
28932892

2894-
// Only send the todo_list tool
2895-
let mut tools = self.conversation.tools.clone();
2896-
tools.retain(|k, v| match k {
2897-
ToolOrigin::Native => {
2898-
v.retain(|tool| match tool {
2899-
api_client::model::Tool::ToolSpecification(tool_spec) => tool_spec.name == "todo_list",
2900-
});
2901-
true
2902-
},
2903-
ToolOrigin::McpServer(_) => false,
2904-
});
2893+
ChatSession::reset_user_turn(self);
29052894

29062895
Ok(ChatState::HandleInput {
29072896
input: summary_message
2908-
.into_user_input_message(self.conversation.model.clone(), &tools)
2897+
.into_user_input_message(self.conversation.model.clone(), &self.conversation.tools)
29092898
.content,
29102899
})
29112900
}

crates/chat-cli/src/cli/chat/tools/todo.rs

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,16 @@ use serde::{
2626
use super::InvokeOutput;
2727
use crate::os::Os;
2828

29-
// Local directory to store todo lists
30-
const TODO_LIST_DIR: &str = ".amazonq/cli-todo-lists/";
29+
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
30+
pub struct Task {
31+
pub task_description: String,
32+
pub completed: bool,
33+
}
3134

3235
/// Contains all state to be serialized and deserialized into a todo list
3336
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
3437
pub struct TodoListState {
35-
pub tasks: Vec<String>,
36-
pub completed: Vec<bool>,
38+
pub tasks: Vec<Task>,
3739
pub description: String,
3840
pub context: Vec<String>,
3941
pub modified_files: Vec<String>,
@@ -43,7 +45,9 @@ pub struct TodoListState {
4345
impl TodoListState {
4446
/// Creates a local directory to store todo lists
4547
pub async fn init_dir(os: &Os) -> Result<()> {
46-
os.fs.create_dir_all(os.env.current_dir()?.join(TODO_LIST_DIR)).await?;
48+
os.fs
49+
.create_dir_all(os.env.current_dir()?.join(get_todo_list_dir(os)?))
50+
.await?;
4751
Ok(())
4852
}
4953

@@ -72,8 +76,8 @@ impl TodoListState {
7276
/// Displays the TodoListState as a to-do list
7377
pub fn display_list(&self, output: &mut impl Write) -> Result<()> {
7478
queue!(output, style::Print("TODO:\n".yellow()))?;
75-
for (index, (task, completed)) in self.tasks.iter().zip(self.completed.iter()).enumerate() {
76-
queue_next_without_newline(output, task.clone(), *completed)?;
79+
for (index, task) in self.tasks.iter().enumerate() {
80+
queue_next_without_newline(output, task.task_description.clone(), task.completed)?;
7781
if index < self.tasks.len() - 1 {
7882
queue!(output, style::Print("\n"))?;
7983
}
@@ -117,12 +121,16 @@ pub fn generate_new_todo_id() -> String {
117121

118122
/// Converts a todo list id to an absolute path in the cwd
119123
pub fn id_to_path(os: &Os, id: &str) -> Result<PathBuf> {
120-
Ok(os.env.current_dir()?.join(TODO_LIST_DIR).join(format!("{id}.json")))
124+
Ok(os
125+
.env
126+
.current_dir()?
127+
.join(get_todo_list_dir(os)?)
128+
.join(format!("{id}.json")))
121129
}
122130

123131
/// Gets all todo lists from the local directory
124132
pub async fn get_all_todos(os: &Os) -> Result<(Vec<TodoListState>, Vec<Report>)> {
125-
let todo_list_dir = os.env.current_dir()?.join(TODO_LIST_DIR);
133+
let todo_list_dir = os.env.current_dir()?.join(get_todo_list_dir(os)?);
126134
let mut read_dir_output = os.fs.read_dir(todo_list_dir).await?;
127135

128136
let mut todos = Vec::new();
@@ -154,6 +162,12 @@ pub async fn delete_todo(os: &Os, id: &str) -> Result<()> {
154162
Ok(())
155163
}
156164

165+
/// Returns the local todo list storage directory
166+
pub fn get_todo_list_dir(os: &Os) -> Result<PathBuf> {
167+
let cwd = os.env.current_dir()?;
168+
Ok(cwd.join(".amazonq").join("cli-todo-lists"))
169+
}
170+
157171
/// Contains the command definitions that allow the model to create,
158172
/// modify, and mark todo list tasks as complete
159173
#[derive(Debug, Clone, Deserialize)]
@@ -203,11 +217,17 @@ impl TodoList {
203217
todo_list_description: task_description,
204218
} => {
205219
let new_id = generate_new_todo_id();
220+
let mut todo_tasks = Vec::new();
221+
for task_description in tasks {
222+
todo_tasks.push(Task {
223+
task_description: task_description.clone(),
224+
completed: false,
225+
});
226+
}
206227

207228
// Create a new todo list with the given tasks and save state
208229
let state = TodoListState {
209-
tasks: tasks.clone(),
210-
completed: vec![false; tasks.len()],
230+
tasks: todo_tasks.clone(),
211231
description: task_description.clone(),
212232
context: Vec::new(),
213233
modified_files: Vec::new(),
@@ -226,7 +246,7 @@ impl TodoList {
226246
let mut state = TodoListState::load(os, id).await?;
227247

228248
for i in completed_indices.iter() {
229-
state.completed[*i] = true;
249+
state.tasks[*i].completed = true;
230250
}
231251

232252
state.context.push(context_update.clone());
@@ -239,21 +259,17 @@ impl TodoList {
239259
// As tasks are being completed, display only the newly completed tasks
240260
// and the next. Only display the whole list when all tasks are completed
241261
let last_completed = completed_indices.iter().max().unwrap();
242-
if *last_completed == state.tasks.len() - 1 || state.completed.iter().all(|c| *c) {
262+
if *last_completed == state.tasks.len() - 1 || state.tasks.iter().all(|t| t.completed) {
243263
state.display_list(output)?;
244264
} else {
245265
let mut display_list = TodoListState {
246266
tasks: completed_indices.iter().map(|i| state.tasks[*i].clone()).collect(),
247267
..Default::default()
248268
};
249-
for _ in 0..completed_indices.len() {
250-
display_list.completed.push(true);
251-
}
252269

253270
// For next state, mark it true/false depending on actual completion state
254271
// This only matters when the model skips around tasks
255272
display_list.tasks.push(state.tasks[*last_completed + 1].clone());
256-
display_list.completed.push(state.completed[*last_completed + 1]);
257273

258274
display_list.display_list(output)?;
259275
}
@@ -271,9 +287,12 @@ impl TodoList {
271287
current_id: id,
272288
} => {
273289
let mut state = TodoListState::load(os, id).await?;
274-
for (i, task) in insert_indices.iter().zip(new_tasks.iter()) {
275-
state.tasks.insert(*i, task.clone());
276-
state.completed.insert(*i, false);
290+
for (i, task_description) in insert_indices.iter().zip(new_tasks.iter()) {
291+
let new_task = Task {
292+
task_description: task_description.clone(),
293+
completed: false,
294+
};
295+
state.tasks.insert(*i, new_task);
277296
}
278297
if let Some(description) = new_description {
279298
state.description = description.clone();
@@ -294,7 +313,6 @@ impl TodoList {
294313
remove_indices.sort();
295314
for i in remove_indices.iter().rev() {
296315
state.tasks.remove(*i);
297-
state.completed.remove(*i);
298316
}
299317
if let Some(description) = new_description {
300318
state.description = description.clone();

0 commit comments

Comments
 (0)