diff --git a/crates/goose-cli/src/cli.rs b/crates/goose-cli/src/cli.rs index 9e4483436e..32337bff94 100644 --- a/crates/goose-cli/src/cli.rs +++ b/crates/goose-cli/src/cli.rs @@ -315,6 +315,16 @@ enum Command { )] interactive: bool, + /// Run without storing a session file + #[arg( + long = "no-session", + help = "Run without storing a session file", + long_help = "Execute commands without creating or using a session file. Useful for automated runs.", + conflicts_with = "resume", + conflicts_with = "identifier" + )] + no_session: bool, + /// Identifier for this run session #[command(flatten)] identifier: Option, @@ -459,6 +469,7 @@ pub async fn cli() -> Result<()> { let mut session: crate::Session = build_session(SessionBuilderConfig { identifier: identifier.map(extract_identifier), resume, + no_session: false, extensions, remote_extensions, builtins, @@ -499,6 +510,7 @@ pub async fn cli() -> Result<()> { interactive, identifier, resume, + no_session, debug, extensions, remote_extensions, @@ -558,6 +570,7 @@ pub async fn cli() -> Result<()> { let mut session = build_session(SessionBuilderConfig { identifier: identifier.map(extract_identifier), resume, + no_session, extensions, remote_extensions, builtins, @@ -625,7 +638,18 @@ pub async fn cli() -> Result<()> { Ok(()) } else { // Run session command by default - let mut session = build_session(SessionBuilderConfig::default()).await; + let mut session = build_session(SessionBuilderConfig { + identifier: None, + resume: false, + no_session: false, + extensions: Vec::new(), + remote_extensions: Vec::new(), + builtins: Vec::new(), + extensions_override: None, + additional_system_prompt: None, + debug: false, + }) + .await; setup_logging( session.session_file().file_stem().and_then(|s| s.to_str()), None, diff --git a/crates/goose-cli/src/commands/bench.rs b/crates/goose-cli/src/commands/bench.rs index 8055ac9b4c..a6ef200c0f 100644 --- a/crates/goose-cli/src/commands/bench.rs +++ b/crates/goose-cli/src/commands/bench.rs @@ -34,6 +34,7 @@ pub async fn agent_generator( let base_session = build_session(SessionBuilderConfig { identifier, resume: false, + no_session: false, extensions: requirements.external, remote_extensions: requirements.remote, builtins: requirements.builtin, diff --git a/crates/goose-cli/src/session/builder.rs b/crates/goose-cli/src/session/builder.rs index 38ce3c7f26..a5c77368bc 100644 --- a/crates/goose-cli/src/session/builder.rs +++ b/crates/goose-cli/src/session/builder.rs @@ -21,6 +21,8 @@ pub struct SessionBuilderConfig { pub identifier: Option, /// Whether to resume an existing session pub resume: bool, + /// Whether to run without a session file + pub no_session: bool, /// List of stdio extension commands to add pub extensions: Vec, /// List of remote extension commands to add @@ -54,7 +56,17 @@ pub async fn build_session(session_config: SessionBuilderConfig) -> Session { let _ = agent.update_provider(new_provider).await; // Handle session file resolution and resuming - let session_file = if session_config.resume { + let session_file = if session_config.no_session { + // Use a temporary path that won't be written to + #[cfg(unix)] + { + std::path::PathBuf::from("/dev/null") + } + #[cfg(windows)] + { + std::path::PathBuf::from("NUL") + } + } else if session_config.resume { if let Some(identifier) = session_config.identifier { let session_file = session::get_path(identifier); if !session_file.exists() { @@ -87,7 +99,7 @@ pub async fn build_session(session_config: SessionBuilderConfig) -> Session { session::get_path(id) }; - if session_config.resume { + if session_config.resume && !session_config.no_session { // Read the session metadata let metadata = session::read_metadata(&session_file).unwrap_or_else(|e| { output::render_error(&format!("Failed to read session metadata: {}", e)); diff --git a/crates/goose-cli/src/session/output.rs b/crates/goose-cli/src/session/output.rs index b62a442cdc..a3f7f9c939 100644 --- a/crates/goose-cli/src/session/output.rs +++ b/crates/goose-cli/src/session/output.rs @@ -527,6 +527,8 @@ fn shorten_path(path: &str, debug: bool) -> String { pub fn display_session_info(resume: bool, provider: &str, model: &str, session_file: &Path) { let start_session_msg = if resume { "resuming session |" + } else if session_file.to_str() == Some("/dev/null") || session_file.to_str() == Some("NUL") { + "running without session |" } else { "starting session |" }; @@ -538,11 +540,15 @@ pub fn display_session_info(resume: bool, provider: &str, model: &str, session_f style("model:").dim(), style(model).cyan().dim(), ); - println!( - " {} {}", - style("logging to").dim(), - style(session_file.display()).dim().cyan(), - ); + + if session_file.to_str() != Some("/dev/null") && session_file.to_str() != Some("NUL") { + println!( + " {} {}", + style("logging to").dim(), + style(session_file.display()).dim().cyan(), + ); + } + println!( " {} {}", style("working directory:").dim(),