diff --git a/crates/goose-cli/src/main.rs b/crates/goose-cli/src/main.rs index 7c95b02f0f..312bf31c0a 100644 --- a/crates/goose-cli/src/main.rs +++ b/crates/goose-cli/src/main.rs @@ -106,6 +106,14 @@ enum Command { )] input_text: Option, + /// Continue in interactive mode after processing input + #[arg( + short = 's', + long = "interactive", + help = "Continue in interactive mode after processing initial input" + )] + interactive: bool, + /// Name for this run session #[arg( short, @@ -182,12 +190,13 @@ async fn main() -> Result<()> { }) => { let mut session = build_session(name, resume, extension, builtin).await; setup_logging(session.session_file().file_stem().and_then(|s| s.to_str()))?; - let _ = session.start().await; + let _ = session.interactive(None).await; return Ok(()); } Some(Command::Run { instructions, input_text, + interactive, name, resume, extension, @@ -213,7 +222,12 @@ async fn main() -> Result<()> { }; let mut session = build_session(name, resume, extension, builtin).await; setup_logging(session.session_file().file_stem().and_then(|s| s.to_str()))?; - let _ = session.headless_start(contents.clone()).await; + + if interactive { + let _ = session.interactive(Some(contents)).await?; + } else { + let _ = session.headless(contents).await?; + } return Ok(()); } Some(Command::Agents(cmd)) => { diff --git a/crates/goose-cli/src/session/mod.rs b/crates/goose-cli/src/session/mod.rs index d359a297ae..bfd370b00d 100644 --- a/crates/goose-cli/src/session/mod.rs +++ b/crates/goose-cli/src/session/mod.rs @@ -103,7 +103,21 @@ impl Session { Ok(()) } - pub async fn start(&mut self) -> Result<()> { + /// Process a single message and get the response + async fn process_message(&mut self, message: String) -> Result<()> { + self.messages.push(Message::user().with_text(&message)); + storage::persist_messages(&self.session_file, &self.messages)?; + self.process_agent_response(false).await?; + Ok(()) + } + + /// Start an interactive session, optionally with an initial message + pub async fn interactive(&mut self, message: Option) -> Result<()> { + // Process initial message if provided + if let Some(msg) = message { + self.process_message(msg).await?; + } + let mut editor = rustyline::Editor::<(), rustyline::history::DefaultHistory>::new()?; // Load history from messages @@ -129,7 +143,7 @@ impl Session { storage::persist_messages(&self.session_file, &self.messages)?; output::show_thinking(); - self.process_agent_response().await?; + self.process_agent_response(true).await?; output::hide_thinking(); } input::InputResult::Exit => break, @@ -184,15 +198,12 @@ impl Session { Ok(()) } - pub async fn headless_start(&mut self, initial_message: String) -> Result<()> { - self.messages - .push(Message::user().with_text(&initial_message)); - storage::persist_messages(&self.session_file, &self.messages)?; - self.process_agent_response().await?; - Ok(()) + /// Process a single message and exit + pub async fn headless(&mut self, message: String) -> Result<()> { + self.process_message(message).await } - async fn process_agent_response(&mut self) -> Result<()> { + async fn process_agent_response(&mut self, interactive: bool) -> Result<()> { let mut stream = self.agent.reply(&self.messages).await?; use futures::StreamExt; @@ -203,9 +214,9 @@ impl Session { Some(Ok(message)) => { self.messages.push(message.clone()); storage::persist_messages(&self.session_file, &self.messages)?; - output::hide_thinking(); + if interactive {output::hide_thinking()}; output::render_message(&message); - output::show_thinking(); + if interactive {output::show_thinking()}; } Some(Err(e)) => { eprintln!("Error: {}", e);