Skip to content

Commit e76e4c7

Browse files
GoodluckHjsamuel1
authored andcommitted
feat(/clear): add confirmation prompt before clearing conversation history (#1180)
1 parent 44368f1 commit e76e4c7

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
@@ -693,7 +693,8 @@ where
693693
}
694694
}
695695

696-
if !skip_printing_tools && pending_tool_index.is_some() {
696+
let show_tool_use_confirmation_dialog = !skip_printing_tools && pending_tool_index.is_some();
697+
if show_tool_use_confirmation_dialog {
697698
execute!(
698699
self.output,
699700
style::SetForegroundColor(Color::DarkGrey),
@@ -718,36 +719,9 @@ where
718719
)?;
719720
}
720721

721-
// Require two consecutive sigint's to exit.
722-
let mut ctrl_c = false;
723-
let user_input = loop {
724-
let all_tools_trusted = self.conversation_state.tools.iter().all(|t| match t {
725-
FigTool::ToolSpecification(t) => self.tool_permissions.is_trusted(&t.name),
726-
});
727-
728-
// Generate prompt based on active context profile and trusted tools
729-
let prompt = prompt::generate_prompt(self.conversation_state.current_profile(), all_tools_trusted);
730-
731-
match (self.input_source.read_line(Some(&prompt))?, ctrl_c) {
732-
(Some(line), _) => {
733-
// Handle empty line case - reprompt the user
734-
if line.trim().is_empty() {
735-
continue;
736-
}
737-
break line;
738-
},
739-
(None, false) => {
740-
execute!(
741-
self.output,
742-
style::Print(format!(
743-
"\n(To exit, press Ctrl+C or Ctrl+D again or type {})\n\n",
744-
"/quit".green()
745-
))
746-
)?;
747-
ctrl_c = true;
748-
},
749-
(None, true) => return Ok(ChatState::Exit),
750-
}
722+
let user_input = match self.read_user_input(&self.generate_tool_trust_prompt(), false) {
723+
Some(input) => input,
724+
None => return Ok(ChatState::Exit),
751725
};
752726

753727
self.conversation_state.append_user_transcript(&user_input);
@@ -837,16 +811,39 @@ where
837811
}
838812
},
839813
Command::Clear => {
840-
// Clear the conversation including summary
841-
self.conversation_state.clear(false);
842-
814+
execute!(self.output, cursor::Show)?;
843815
execute!(
844816
self.output,
817+
style::SetForegroundColor(Color::DarkGrey),
818+
style::Print("\nAre you sure? This will erase the conversation history for the current session. "),
819+
style::Print("["),
845820
style::SetForegroundColor(Color::Green),
846-
style::Print("\nConversation history cleared.\n\n"),
847-
style::SetForegroundColor(Color::Reset)
821+
style::Print("y"),
822+
style::SetForegroundColor(Color::DarkGrey),
823+
style::Print("/"),
824+
style::SetForegroundColor(Color::Green),
825+
style::Print("n"),
826+
style::SetForegroundColor(Color::DarkGrey),
827+
style::Print("]:\n\n"),
828+
style::SetForegroundColor(Color::Reset),
848829
)?;
849830

831+
// Setting `exit_on_single_ctrl_c` for better ux: exit the confirmation dialog rather than the CLI
832+
let user_input = match self.read_user_input("> ".yellow().to_string().as_str(), true) {
833+
Some(input) => input,
834+
None => "".to_string(),
835+
};
836+
837+
if ["y", "Y"].contains(&user_input.as_str()) {
838+
self.conversation_state.clear(true);
839+
execute!(
840+
self.output,
841+
style::SetForegroundColor(Color::Green),
842+
style::Print("\nConversation history cleared.\n\n"),
843+
style::SetForegroundColor(Color::Reset)
844+
)?;
845+
}
846+
850847
ChatState::PromptUser {
851848
tool_uses: None,
852849
pending_tool_index: None,
@@ -2150,6 +2147,46 @@ where
21502147
Ok(())
21512148
}
21522149

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