diff --git a/rust/crates/commands/src/lib.rs b/rust/crates/commands/src/lib.rs index a4cc0b9..dc329d1 100644 --- a/rust/crates/commands/src/lib.rs +++ b/rust/crates/commands/src/lib.rs @@ -221,8 +221,10 @@ const SLASH_COMMAND_SPECS: &[SlashCommandSpec] = &[ SlashCommandSpec { name: "session", aliases: &[], - summary: "List, switch, or fork managed local sessions", - argument_hint: Some("[list|switch |fork [branch-name]]"), + summary: "List, switch, fork, or delete managed local sessions", + argument_hint: Some( + "[list|switch |fork [branch-name]|delete [--force]]", + ), resume_supported: false, }, SlashCommandSpec { @@ -1526,7 +1528,7 @@ fn parse_session_command(args: &[&str]) -> Result Err(usage_error("session", "[list|switch |fork [branch-name]]")), + ["list", ..] => Err(usage_error("session", "[list|switch |fork [branch-name]|delete [--force]]")), ["switch"] => Err(usage_error("session switch", "")), ["switch", target] => Ok(SlashCommand::Session { action: Some("switch".to_string()), @@ -1550,12 +1552,33 @@ fn parse_session_command(args: &[&str]) -> Result Err(command_error( + ["delete"] => Err(usage_error("session delete", " [--force]")), + ["delete", target] => Ok(SlashCommand::Session { + action: Some("delete".to_string()), + target: Some((*target).to_string()), + }), + ["delete", target, "--force"] => Ok(SlashCommand::Session { + action: Some("delete-force".to_string()), + target: Some((*target).to_string()), + }), + ["delete", _target, unexpected] => Err(command_error( &format!( - "Unknown /session action '{action}'. Use list, switch , or fork [branch-name]." + "Unsupported /session delete flag '{unexpected}'. Use --force to skip confirmation." ), "session", - "/session [list|switch |fork [branch-name]]", + "/session delete [--force]", + )), + ["delete", ..] => Err(command_error( + "Unexpected arguments for /session delete.", + "session", + "/session delete [--force]", + )), + [action, ..] => Err(command_error( + &format!( + "Unknown /session action '{action}'. Use list, switch , fork [branch-name], or delete [--force]." + ), + "session", + "/session [list|switch |fork [branch-name]|delete [--force]]", )), } } diff --git a/rust/crates/rusty-claude-cli/src/main.rs b/rust/crates/rusty-claude-cli/src/main.rs index 73fa23b..189a65a 100644 --- a/rust/crates/rusty-claude-cli/src/main.rs +++ b/rust/crates/rusty-claude-cli/src/main.rs @@ -4059,9 +4059,55 @@ impl LiveCli { ); Ok(true) } + Some("delete") => { + let Some(target) = target else { + println!("Usage: /session delete [--force]"); + return Ok(false); + }; + let handle = resolve_session_reference(target)?; + if handle.id == self.session.id { + println!( + "delete: refusing to delete the active session '{}'.\nSwitch to another session first with /session switch .", + handle.id + ); + return Ok(false); + } + if !confirm_session_deletion(&handle.id) { + println!("delete: cancelled."); + return Ok(false); + } + delete_managed_session(&handle.path)?; + println!( + "Session deleted\n Deleted session {}\n File {}", + handle.id, + handle.path.display(), + ); + Ok(false) + } + Some("delete-force") => { + let Some(target) = target else { + println!("Usage: /session delete [--force]"); + return Ok(false); + }; + let handle = resolve_session_reference(target)?; + if handle.id == self.session.id { + println!( + "delete: refusing to delete the active session '{}'.\nSwitch to another session first with /session switch .", + handle.id + ); + return Ok(false); + } + delete_managed_session(&handle.path)?; + println!( + "Session deleted\n Deleted session {}\n File {}", + handle.id, + handle.path.display(), + ); + Ok(false) + } Some(other) => { println!( - "Unknown /session action '{other}'. Use /session list, /session switch , or /session fork [branch-name]." + "Unknown /session action '{other}'. Use /session list, /session switch , /session fork [branch-name], or /session delete [--force]." ); Ok(false) } @@ -4347,6 +4393,24 @@ fn latest_managed_session() -> Result Result<(), Box> { + if !path.exists() { + return Err(format!("session file does not exist: {}", path.display()).into()); + } + fs::remove_file(path)?; + Ok(()) +} + +fn confirm_session_deletion(session_id: &str) -> bool { + print!("Delete session '{session_id}'? This cannot be undone. [y/N]: "); + io::stdout().flush().unwrap_or(()); + let mut answer = String::new(); + if io::stdin().read_line(&mut answer).is_err() { + return false; + } + matches!(answer.trim(), "y" | "Y" | "yes" | "Yes" | "YES") +} + fn format_missing_session_reference(reference: &str) -> String { format!( "session not found: {reference}\nHint: managed sessions live in .claw/sessions/. Try `{LATEST_SESSION_REFERENCE}` for the most recent session or `/session list` in the REPL."