Skip to content

Commit 06c2e21

Browse files
authored
feat(/clear): add confirmation prompt before clearing conversation history (aws#1180)
1 parent e42f218 commit 06c2e21

File tree

2 files changed

+75
-36
lines changed

2 files changed

+75
-36
lines changed

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

Lines changed: 73 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -707,7 +707,8 @@ where
707707
}
708708
}
709709

710-
if !skip_printing_tools && pending_tool_index.is_some() {
710+
let show_tool_use_confirmation_dialog = !skip_printing_tools && pending_tool_index.is_some();
711+
if show_tool_use_confirmation_dialog {
711712
execute!(
712713
self.output,
713714
style::SetForegroundColor(Color::DarkGrey),
@@ -732,36 +733,9 @@ where
732733
)?;
733734
}
734735

735-
// Require two consecutive sigint's to exit.
736-
let mut ctrl_c = false;
737-
let user_input = loop {
738-
let all_tools_trusted = self.conversation_state.tools.iter().all(|t| match t {
739-
FigTool::ToolSpecification(t) => self.tool_permissions.is_trusted(&t.name),
740-
});
741-
742-
// Generate prompt based on active context profile and trusted tools
743-
let prompt = prompt::generate_prompt(self.conversation_state.current_profile(), all_tools_trusted);
744-
745-
match (self.input_source.read_line(Some(&prompt))?, ctrl_c) {
746-
(Some(line), _) => {
747-
// Handle empty line case - reprompt the user
748-
if line.trim().is_empty() {
749-
continue;
750-
}
751-
break line;
752-
},
753-
(None, false) => {
754-
execute!(
755-
self.output,
756-
style::Print(format!(
757-
"\n(To exit, press Ctrl+C or Ctrl+D again or type {})\n\n",
758-
"/quit".green()
759-
))
760-
)?;
761-
ctrl_c = true;
762-
},
763-
(None, true) => return Ok(ChatState::Exit),
764-
}
736+
let user_input = match self.read_user_input(&self.generate_tool_trust_prompt(), false) {
737+
Some(input) => input,
738+
None => return Ok(ChatState::Exit),
765739
};
766740

767741
self.conversation_state.append_user_transcript(&user_input);
@@ -851,16 +825,39 @@ where
851825
}
852826
},
853827
Command::Clear => {
854-
// Clear the conversation including summary
855-
self.conversation_state.clear(false);
856-
828+
execute!(self.output, cursor::Show)?;
857829
execute!(
858830
self.output,
831+
style::SetForegroundColor(Color::DarkGrey),
832+
style::Print("\nAre you sure? This will erase the conversation history for the current session. "),
833+
style::Print("["),
859834
style::SetForegroundColor(Color::Green),
860-
style::Print("\nConversation history cleared.\n\n"),
861-
style::SetForegroundColor(Color::Reset)
835+
style::Print("y"),
836+
style::SetForegroundColor(Color::DarkGrey),
837+
style::Print("/"),
838+
style::SetForegroundColor(Color::Green),
839+
style::Print("n"),
840+
style::SetForegroundColor(Color::DarkGrey),
841+
style::Print("]:\n\n"),
842+
style::SetForegroundColor(Color::Reset),
862843
)?;
863844

845+
// Setting `exit_on_single_ctrl_c` for better ux: exit the confirmation dialog rather than the CLI
846+
let user_input = match self.read_user_input("> ".yellow().to_string().as_str(), true) {
847+
Some(input) => input,
848+
None => "".to_string(),
849+
};
850+
851+
if ["y", "Y"].contains(&user_input.as_str()) {
852+
self.conversation_state.clear(true);
853+
execute!(
854+
self.output,
855+
style::SetForegroundColor(Color::Green),
856+
style::Print("\nConversation history cleared.\n\n"),
857+
style::SetForegroundColor(Color::Reset)
858+
)?;
859+
}
860+
864861
ChatState::PromptUser {
865862
tool_uses: None,
866863
pending_tool_index: None,
@@ -2143,6 +2140,46 @@ where
21432140
Ok(())
21442141
}
21452142

2143+
/// Helper function to read user input with a prompt and Ctrl+C handling
2144+
fn read_user_input(&mut self, prompt: &str, exit_on_single_ctrl_c: bool) -> Option<String> {
2145+
let mut ctrl_c = false;
2146+
loop {
2147+
match (self.input_source.read_line(Some(prompt)), ctrl_c) {
2148+
(Ok(Some(line)), _) => {
2149+
if line.trim().is_empty() {
2150+
continue; // Reprompt if the input is empty
2151+
}
2152+
return Some(line);
2153+
},
2154+
(Ok(None), false) => {
2155+
if exit_on_single_ctrl_c {
2156+
return None;
2157+
}
2158+
execute!(
2159+
self.output,
2160+
style::Print(format!(
2161+
"\n(To exit the CLI, press Ctrl+C or Ctrl+D again or type {})\n\n",
2162+
"/quit".green()
2163+
))
2164+
)
2165+
.unwrap_or_default();
2166+
ctrl_c = true;
2167+
},
2168+
(Ok(None), true) => return None, // Exit if Ctrl+C was pressed twice
2169+
(Err(_), _) => return None,
2170+
}
2171+
}
2172+
}
2173+
2174+
/// Helper function to generate a prompt based on the current context
2175+
fn generate_tool_trust_prompt(&self) -> String {
2176+
let all_tools_trusted = self.conversation_state.tools.iter().all(|t| match t {
2177+
FigTool::ToolSpecification(t) => self.tool_permissions.is_trusted(&t.name),
2178+
});
2179+
2180+
prompt::generate_prompt(self.conversation_state.current_profile(), all_tools_trusted)
2181+
}
2182+
21462183
async fn send_tool_use_telemetry(&mut self) {
21472184
for (_, mut event) in self.tool_use_telemetry_events.drain() {
21482185
event.user_input_id = match self.tool_use_status {

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
"scripts": {
1313
"build": "turbo build",
1414
"b": "turbo build",
15+
"format": "cargo +nightly fmt",
16+
"clippy": "cargo clippy --locked --workspace --color always -- -D warnings",
1517
"test": "vitest",
1618
"test:ci": "vitest run --run --coverage",
1719
"test:ui": "vitest --ui",

0 commit comments

Comments
 (0)