Skip to content

Commit 63f853d

Browse files
committed
feat(/clear): add confirmation prompt before clearing conversation history
1 parent 43f666f commit 63f853d

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
@@ -706,7 +706,8 @@ where
706706
}
707707
}
708708

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

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

766740
self.conversation_state.append_user_transcript(&user_input);
@@ -850,16 +824,39 @@ where
850824
}
851825
},
852826
Command::Clear => {
853-
// Clear the conversation including summary
854-
self.conversation_state.clear(false);
855-
827+
execute!(self.output, cursor::Show)?;
856828
execute!(
857829
self.output,
830+
style::SetForegroundColor(Color::DarkGrey),
831+
style::Print("\nAre you sure? This will erase the conversation history for the current session. "),
832+
style::Print("["),
858833
style::SetForegroundColor(Color::Green),
859-
style::Print("\nConversation history cleared.\n\n"),
860-
style::SetForegroundColor(Color::Reset)
834+
style::Print("y"),
835+
style::SetForegroundColor(Color::DarkGrey),
836+
style::Print("/"),
837+
style::SetForegroundColor(Color::Green),
838+
style::Print("n"),
839+
style::SetForegroundColor(Color::DarkGrey),
840+
style::Print("]:\n\n"),
841+
style::SetForegroundColor(Color::Reset),
861842
)?;
862843

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

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