diff --git a/rust/crates/commands/src/lib.rs b/rust/crates/commands/src/lib.rs index e090491..b3609bf 100644 --- a/rust/crates/commands/src/lib.rs +++ b/rust/crates/commands/src/lib.rs @@ -72,7 +72,7 @@ const SLASH_COMMAND_SPECS: &[SlashCommandSpec] = &[ SlashCommandSpec { name: "clear", summary: "Start a fresh local session", - argument_hint: None, + argument_hint: Some("[--confirm]"), resume_supported: true, }, SlashCommandSpec { @@ -114,7 +114,7 @@ pub enum SlashCommand { Compact, Model { model: Option }, Permissions { mode: Option }, - Clear, + Clear { confirm: bool }, Cost, Resume { session_path: Option }, Config, @@ -143,7 +143,9 @@ impl SlashCommand { "permissions" => Self::Permissions { mode: parts.next().map(ToOwned::to_owned), }, - "clear" => Self::Clear, + "clear" => Self::Clear { + confirm: parts.next() == Some("--confirm"), + }, "cost" => Self::Cost, "resume" => Self::Resume { session_path: parts.next().map(ToOwned::to_owned), @@ -225,7 +227,7 @@ pub fn handle_slash_command( SlashCommand::Status | SlashCommand::Model { .. } | SlashCommand::Permissions { .. } - | SlashCommand::Clear + | SlashCommand::Clear { .. } | SlashCommand::Cost | SlashCommand::Resume { .. } | SlashCommand::Config @@ -263,7 +265,14 @@ mod tests { mode: Some("read-only".to_string()), }) ); - assert_eq!(SlashCommand::parse("/clear"), Some(SlashCommand::Clear)); + assert_eq!( + SlashCommand::parse("/clear"), + Some(SlashCommand::Clear { confirm: false }) + ); + assert_eq!( + SlashCommand::parse("/clear --confirm"), + Some(SlashCommand::Clear { confirm: true }) + ); assert_eq!(SlashCommand::parse("/cost"), Some(SlashCommand::Cost)); assert_eq!( SlashCommand::parse("/resume session.json"), @@ -285,7 +294,7 @@ mod tests { assert!(help.contains("/compact")); assert!(help.contains("/model [model]")); assert!(help.contains("/permissions [read-only|workspace-write|danger-full-access]")); - assert!(help.contains("/clear")); + assert!(help.contains("/clear [--confirm]")); assert!(help.contains("/cost")); assert!(help.contains("/resume ")); assert!(help.contains("/config")); @@ -349,6 +358,10 @@ mod tests { ) .is_none()); assert!(handle_slash_command("/clear", &session, CompactionConfig::default()).is_none()); + assert!( + handle_slash_command("/clear --confirm", &session, CompactionConfig::default()) + .is_none() + ); assert!(handle_slash_command("/cost", &session, CompactionConfig::default()).is_none()); assert!(handle_slash_command( "/resume session.json", diff --git a/rust/crates/rusty-claude-cli/src/main.rs b/rust/crates/rusty-claude-cli/src/main.rs index e053fe0..e06095c 100644 --- a/rust/crates/rusty-claude-cli/src/main.rs +++ b/rust/crates/rusty-claude-cli/src/main.rs @@ -299,7 +299,15 @@ fn run_resume_command( message: Some(result.message), }) } - SlashCommand::Clear => { + SlashCommand::Clear { confirm } => { + if !confirm { + return Ok(ResumeCommandOutcome { + session: session.clone(), + message: Some( + "clear: confirmation required; rerun with /clear --confirm".to_string(), + ), + }); + } let cleared = Session::new(); cleared.save_to_path(session_path)?; Ok(ResumeCommandOutcome { @@ -448,7 +456,7 @@ impl LiveCli { SlashCommand::Compact => self.compact()?, SlashCommand::Model { model } => self.set_model(model)?, SlashCommand::Permissions { mode } => self.set_permissions(mode)?, - SlashCommand::Clear => self.clear_session()?, + SlashCommand::Clear { confirm } => self.clear_session(confirm)?, SlashCommand::Cost => self.print_cost(), SlashCommand::Resume { session_path } => self.resume_session(session_path)?, SlashCommand::Config => Self::print_config()?, @@ -526,7 +534,14 @@ impl LiveCli { Ok(()) } - fn clear_session(&mut self) -> Result<(), Box> { + fn clear_session(&mut self, confirm: bool) -> Result<(), Box> { + if !confirm { + println!( + "clear: confirmation required; run /clear --confirm to start a fresh session." + ); + return Ok(()); + } + self.runtime = build_runtime_with_permission_mode( Session::new(), self.model.clone(), @@ -1274,7 +1289,7 @@ mod tests { assert!(help.contains("/status")); assert!(help.contains("/model [model]")); assert!(help.contains("/permissions [read-only|workspace-write|danger-full-access]")); - assert!(help.contains("/clear")); + assert!(help.contains("/clear [--confirm]")); assert!(help.contains("/cost")); assert!(help.contains("/resume ")); assert!(help.contains("/config")); @@ -1367,6 +1382,18 @@ mod tests { assert_eq!(normalize_permission_mode("unknown"), None); } + #[test] + fn clear_command_requires_explicit_confirmation_flag() { + assert_eq!( + SlashCommand::parse("/clear"), + Some(SlashCommand::Clear { confirm: false }) + ); + assert_eq!( + SlashCommand::parse("/clear --confirm"), + Some(SlashCommand::Clear { confirm: true }) + ); + } + #[test] fn parses_resume_and_config_slash_commands() { assert_eq!( @@ -1375,6 +1402,10 @@ mod tests { session_path: Some("saved-session.json".to_string()) }) ); + assert_eq!( + SlashCommand::parse("/clear --confirm"), + Some(SlashCommand::Clear { confirm: true }) + ); assert_eq!(SlashCommand::parse("/config"), Some(SlashCommand::Config)); assert_eq!(SlashCommand::parse("/memory"), Some(SlashCommand::Memory)); assert_eq!(SlashCommand::parse("/init"), Some(SlashCommand::Init));