-
Notifications
You must be signed in to change notification settings - Fork 241
Add to-do list functionality to QCLI #2533
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
kiran-garre
wants to merge
37
commits into
main
Choose a base branch
from
kiran-garre/todo-list
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 26 commits
Commits
Show all changes
37 commits
Select commit
Hold shift + click to select a range
264c308
feat: update build scripts to build qchat (#2198)
brandonskiser 415624a
feat: Add basic todo list tool
30fc467
Slash command + persistence in progress commit
da54512
feat: Add basic persistence functionality to todo_list
2a09ea9
Add append-only context and "modified files" fields to "complete" com…
398bdb4
refactor: Change to-do lists to use database over filesystem
c000ffb
feat: Add `view` subcommand for /todos
302cecd
feat: Add clear-finished subcommand for /todos
a9501e7
feat: Add `add` and `remove` functionality for to-do lists
814a96a
fix: Add feedback after calling todo_list commands
216ce00
feat: Cleaned up todo list UX and removed /todos show
714474a
feat: Add /todos show
06837f2
trying to merge changes
1a4e7e1
fix: Fix all merge conflicts, back to working state
3299478
chore: Resolve merge conflicts, run formatter and clippy
e31c7ed
chore: Fix real clippy errors (hopefully)
058cc62
Merge branch 'main' of github.com:aws/amazon-q-developer-cli into kir…
12a70b5
chore: Run formatter
ef44517
chore: Remove old/unrelated files
cbbfa20
chore: Remove old/unrelated files again
5ebf87a
refactor: Several changes made to `todo_list` tool
5e76bc1
chore: Fix typo in tool_index.json
7f91788
chore: Remove buildspec files
70423f9
chore: Edit buildspec files to be identical to main
81b1dac
chore: Remove debugging in todos.rs
e7ea8af
chore: Merging changes from main
d7aa647
refactor: Convert from global database store to per-directory filesys…
ee6b932
merge commit
6d00e0f
Merge branch 'main' into kiran-garre/todo-list
4b684f9
fix: Update dependencies to fix slab issue
f2fc3d1
chore: Change variable names, add comments, add file extension to tod…
2fcbe56
Merge commit
9e0b732
Removed merge markers from Cargo.lock
030c79a
Fixed Cargo.lock
cbc8625
fix: Modify system prompt to contain current todo list id
2bf1c40
Merge branch 'main' of github.com:aws/amazon-q-developer-cli
2d9c3b4
Merge branch 'main' into kiran-garre/todo-list
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -44,4 +44,3 @@ artifacts: | |
# Signatures | ||
- ./*.asc | ||
- ./*.sig | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,4 +38,3 @@ artifacts: | |
- ./*.zip | ||
# Hashes | ||
- ./*.sha256 | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
use clap::Subcommand; | ||
use crossterm::execute; | ||
use crossterm::style::{ | ||
self, | ||
Stylize, | ||
}; | ||
use dialoguer::FuzzySelect; | ||
use eyre::Result; | ||
|
||
use crate::cli::chat::tools::todo::TodoState; | ||
use crate::cli::chat::{ | ||
ChatError, | ||
ChatSession, | ||
ChatState, | ||
}; | ||
use crate::os::Os; | ||
|
||
#[derive(Debug, PartialEq, Subcommand)] | ||
pub enum TodoSubcommand { | ||
/// Delete all completed to-do lists | ||
ClearFinished, | ||
|
||
/// Resume a selected to-do list | ||
Resume, | ||
|
||
/// View a to-do list | ||
View, | ||
|
||
/// Delete a to-do list | ||
Delete, | ||
} | ||
|
||
/// Used for displaying completed and in-progress todo lists | ||
pub struct TodoDisplayEntry { | ||
pub num_completed: usize, | ||
pub num_tasks: usize, | ||
pub description: String, | ||
pub id: String, | ||
} | ||
|
||
impl std::fmt::Display for TodoDisplayEntry { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
if self.num_completed == self.num_tasks { | ||
write!(f, "{} {}", "✓".green().bold(), self.description.clone(),) | ||
} else { | ||
write!( | ||
f, | ||
"{} {} ({}/{})", | ||
"✗".red().bold(), | ||
self.description.clone(), | ||
self.num_completed, | ||
self.num_tasks | ||
) | ||
} | ||
} | ||
} | ||
|
||
impl TodoSubcommand { | ||
pub async fn execute(self, os: &mut Os, session: &mut ChatSession) -> Result<ChatState, ChatError> { | ||
match self { | ||
Self::ClearFinished => { | ||
let entries = match os.database.get_all_todos() { | ||
Ok(e) => e, | ||
Err(e) => return Err(ChatError::Custom(format!("Could not get all to-do lists: {e}").into())), | ||
}; | ||
let mut cleared_one = false; | ||
|
||
for (id, value) in entries.iter() { | ||
let todo_status = match value.as_str() { | ||
Some(s) => match serde_json::from_str::<TodoState>(s) { | ||
Ok(state) => state, | ||
|
||
// FIX: Silent fail | ||
Err(_) => continue, | ||
}, | ||
None => continue, | ||
}; | ||
if todo_status.completed.iter().all(|b| *b) { | ||
match os.database.delete_todo(id) { | ||
Ok(_) => cleared_one = true, | ||
Err(e) => { | ||
return Err(ChatError::Custom(format!("Could not delete to-do list: {e}").into())); | ||
}, | ||
}; | ||
} | ||
} | ||
if cleared_one { | ||
execute!( | ||
session.stderr, | ||
style::Print("✔ Cleared finished to-do lists!\n".green()) | ||
)?; | ||
} else { | ||
execute!(session.stderr, style::Print("No finished to-do lists to clear!\n"))?; | ||
} | ||
}, | ||
Self::Resume => match Self::get_descriptions_and_statuses(os) { | ||
Ok(entries) => { | ||
if entries.is_empty() { | ||
execute!(session.stderr, style::Print("No to-do lists to resume!\n"),)?; | ||
} else if let Some(index) = fuzzy_select_todos(&entries, "Select a to-do list to resume:") { | ||
if index < entries.len() { | ||
execute!( | ||
session.stderr, | ||
style::Print(format!( | ||
"{} {}", | ||
"⟳ Resuming:".magenta(), | ||
entries[index].description.clone() | ||
)) | ||
)?; | ||
return session.resume_todo_request(os, &entries[index].id).await; | ||
} | ||
} | ||
}, | ||
Err(e) => return Err(ChatError::Custom(format!("Could not show to-do lists: {e}").into())), | ||
}, | ||
Self::View => match Self::get_descriptions_and_statuses(os) { | ||
Ok(entries) => { | ||
if entries.is_empty() { | ||
execute!(session.stderr, style::Print("No to-do lists to view!\n"))?; | ||
} else if let Some(index) = fuzzy_select_todos(&entries, "Select a to-do list to view:") { | ||
if index < entries.len() { | ||
let list = TodoState::load(os, &entries[index].id).map_err(|e| { | ||
ChatError::Custom(format!("Could not load current to-do list: {e}").into()) | ||
})?; | ||
execute!( | ||
session.stderr, | ||
style::Print(format!( | ||
"{} {}\n\n", | ||
"Viewing:".magenta(), | ||
entries[index].description.clone() | ||
)) | ||
)?; | ||
if list.display_list(&mut session.stderr).is_err() { | ||
return Err(ChatError::Custom("Could not display the selected to-do list".into())); | ||
} | ||
execute!(session.stderr, style::Print("\n"),)?; | ||
} | ||
} | ||
}, | ||
Err(_) => return Err(ChatError::Custom("Could not show to-do lists".into())), | ||
}, | ||
Self::Delete => match Self::get_descriptions_and_statuses(os) { | ||
Ok(entries) => { | ||
if entries.is_empty() { | ||
execute!(session.stderr, style::Print("No to-do lists to delete!\n"))?; | ||
} else if let Some(index) = fuzzy_select_todos(&entries, "Select a to-do list to delete:") { | ||
if index < entries.len() { | ||
os.database.delete_todo(&entries[index].id).map_err(|e| { | ||
ChatError::Custom(format!("Could not delete the selected to-do list: {e}").into()) | ||
})?; | ||
execute!( | ||
session.stderr, | ||
style::Print("✔ Deleted to-do list: ".green()), | ||
style::Print(format!("{}\n", entries[index].description.clone().dark_grey())) | ||
)?; | ||
} | ||
} | ||
}, | ||
Err(_) => return Err(ChatError::Custom("Could not show to-do lists".into())), | ||
}, | ||
} | ||
Ok(ChatState::PromptUser { | ||
skip_printing_tools: true, | ||
}) | ||
} | ||
|
||
/// Convert all to-do list state entries to displayable entries | ||
fn get_descriptions_and_statuses(os: &Os) -> Result<Vec<TodoDisplayEntry>> { | ||
let mut out = Vec::new(); | ||
let entries = os.database.get_all_todos()?; | ||
for (id, value) in entries.iter() { | ||
let temp_struct = match value.as_str() { | ||
Some(s) => match serde_json::from_str::<TodoState>(s) { | ||
Ok(state) => state, | ||
Err(_) => continue, | ||
}, | ||
None => continue, | ||
}; | ||
|
||
out.push(TodoDisplayEntry { | ||
num_completed: temp_struct.completed.iter().filter(|b| **b).count(), | ||
num_tasks: temp_struct.completed.len(), | ||
description: temp_struct.task_description, | ||
id: id.clone(), | ||
}); | ||
} | ||
Ok(out) | ||
} | ||
} | ||
|
||
fn fuzzy_select_todos(entries: &[TodoDisplayEntry], prompt_str: &str) -> Option<usize> { | ||
FuzzySelect::new() | ||
.with_prompt(prompt_str) | ||
.items(entries) | ||
.report(false) | ||
.interact_opt() | ||
.unwrap_or(None) | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,7 @@ use crossterm::{ | |
execute, | ||
style, | ||
}; | ||
use eyre::Result; | ||
use serde::{ | ||
Deserialize, | ||
Serialize, | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This
database.get_all_todos
api could instead be(Vec<TodoState>, Vec<DatabaseError>)
so we just get a list of valid todos, and todos that failed to load or deserialize for whatever reason, so we don't have to duplicate this deser logic multiple times maybe