diff --git a/crates/goose-cli/src/commands/session.rs b/crates/goose-cli/src/commands/session.rs index 19a382c8b8d8..0e23dac496a5 100644 --- a/crates/goose-cli/src/commands/session.rs +++ b/crates/goose-cli/src/commands/session.rs @@ -54,18 +54,11 @@ pub fn build_session<'a>( Err(_) => Box::new(RustylinePrompt::new()), }; - println!( - "{} {} {} {} {}", - style("starting session |").dim(), - style("provider:").dim(), - style(loaded_profile.provider).cyan().dim(), - style("model:").dim(), - style(loaded_profile.model).cyan().dim(), - ); - println!( - " {} {}", - style("logging to").dim(), - style(session_file.display()).dim().cyan(), + display_session_info( + resume, + loaded_profile.provider, + loaded_profile.model, + session_file.as_path(), ); Box::new(Session::new(agent, prompt, session_file)) } @@ -147,6 +140,27 @@ fn load_profile(profile_name: Option) -> Box { loaded_profile } +fn display_session_info(resume: bool, provider: String, model: String, session_file: &Path) { + let start_session_msg = if resume { + "resuming session |" + } else { + "starting session |" + }; + println!( + "{} {} {} {} {}", + style(start_session_msg).dim(), + style("provider:").dim(), + style(provider).cyan().dim(), + style("model:").dim(), + style(model).cyan().dim(), + ); + println!( + " {} {}", + style("logging to").dim(), + style(session_file.display()).dim().cyan(), + ); +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/goose-cli/src/prompt.rs b/crates/goose-cli/src/prompt.rs index 980ac9b5d861..9c3180b12a22 100644 --- a/crates/goose-cli/src/prompt.rs +++ b/crates/goose-cli/src/prompt.rs @@ -11,6 +11,9 @@ pub trait Prompt { fn show_busy(&mut self); fn hide_busy(&self); fn close(&self); + /// Load the user's message history into the prompt for command history navigation. First message is the oldest message. + /// When history is supported by the prompt. + fn load_user_message_history(&mut self, _messages: Vec) {} fn goose_ready(&self) { println!("\n"); println!("Goose is running! Enter your instructions, or try asking what goose can do."); diff --git a/crates/goose-cli/src/prompt/rustyline.rs b/crates/goose-cli/src/prompt/rustyline.rs index 48cf371c5a82..4bfd64cfe88b 100644 --- a/crates/goose-cli/src/prompt/rustyline.rs +++ b/crates/goose-cli/src/prompt/rustyline.rs @@ -9,6 +9,7 @@ use super::{ use anyhow::Result; use cliclack::spinner; use goose::message::Message; +use mcp_core::Role; use rustyline::{DefaultEditor, EventHandler, KeyCode, KeyEvent, Modifiers}; const PROMPT: &str = "\x1b[1m\x1b[38;5;30m( O)> \x1b[0m"; @@ -136,6 +137,18 @@ impl Prompt for RustylinePrompt { } } + fn load_user_message_history(&mut self, messages: Vec) { + for message in messages.into_iter().filter(|m| m.role == Role::User) { + for content in message.content { + if let Some(text) = content.as_text() { + if let Err(e) = self.editor.add_history_entry(text) { + eprintln!("Failed to add to history: {}", e); + } + } + } + } + } + fn close(&self) { // No cleanup required } diff --git a/crates/goose-cli/src/session.rs b/crates/goose-cli/src/session.rs index cc1ce4b96899..0d0270d3eafc 100644 --- a/crates/goose-cli/src/session.rs +++ b/crates/goose-cli/src/session.rs @@ -102,7 +102,11 @@ pub struct Session<'a> { } impl<'a> Session<'a> { - pub fn new(agent: Box, prompt: Box, session_file: PathBuf) -> Self { + pub fn new( + agent: Box, + mut prompt: Box, + session_file: PathBuf, + ) -> Self { let messages = match readable_session_file(&session_file) { Ok(file) => deserialize_messages(file).unwrap_or_else(|e| { eprintln!( @@ -117,6 +121,8 @@ impl<'a> Session<'a> { } }; + prompt.load_user_message_history(messages.clone()); + Session { agent, prompt,