feat: implement global command history storage (#1440)
This commit is contained in:
@@ -42,7 +42,7 @@ pub fn get_input(
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add valid input to history
|
// Add valid input to history (history saving to file is handled in the Session::interactive method)
|
||||||
if !input.trim().is_empty() {
|
if !input.trim().is_empty() {
|
||||||
editor.add_history_entry(input.as_str())?;
|
editor.add_history_entry(input.as_str())?;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ pub use storage::Identifier;
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use completion::GooseCompleter;
|
use completion::GooseCompleter;
|
||||||
use etcetera::choose_app_strategy;
|
use etcetera::choose_app_strategy;
|
||||||
|
use etcetera::AppStrategy;
|
||||||
use goose::agents::extension::{Envs, ExtensionConfig};
|
use goose::agents::extension::{Envs, ExtensionConfig};
|
||||||
use goose::agents::Agent;
|
use goose::agents::Agent;
|
||||||
use goose::message::{Message, MessageContent};
|
use goose::message::{Message, MessageContent};
|
||||||
@@ -223,24 +224,41 @@ impl Session {
|
|||||||
let completer = GooseCompleter::new(self.completion_cache.clone());
|
let completer = GooseCompleter::new(self.completion_cache.clone());
|
||||||
editor.set_helper(Some(completer));
|
editor.set_helper(Some(completer));
|
||||||
|
|
||||||
// Load history from messages
|
// Create and use a global history file in ~/.config/goose directory
|
||||||
for msg in self
|
// This allows command history to persist across different chat sessions
|
||||||
.messages
|
// instead of being tied to each individual session's messages
|
||||||
.iter()
|
let history_file = choose_app_strategy(crate::APP_STRATEGY.clone())
|
||||||
.filter(|m| m.role == mcp_core::role::Role::User)
|
.expect("goose requires a home dir")
|
||||||
{
|
.in_config_dir("history.txt");
|
||||||
for content in msg.content.iter() {
|
|
||||||
if let Some(text) = content.as_text() {
|
// Ensure config directory exists
|
||||||
if let Err(e) = editor.add_history_entry(text) {
|
if let Some(parent) = history_file.parent() {
|
||||||
eprintln!("Warning: Failed to add history entry: {}", e);
|
if !parent.exists() {
|
||||||
}
|
std::fs::create_dir_all(parent)?;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load history from the global file
|
||||||
|
if history_file.exists() {
|
||||||
|
if let Err(err) = editor.load_history(&history_file) {
|
||||||
|
eprintln!("Warning: Failed to load command history: {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to save history after commands
|
||||||
|
let save_history =
|
||||||
|
|editor: &mut rustyline::Editor<GooseCompleter, rustyline::history::DefaultHistory>| {
|
||||||
|
if let Err(err) = editor.save_history(&history_file) {
|
||||||
|
eprintln!("Warning: Failed to save command history: {}", err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
output::display_greeting();
|
output::display_greeting();
|
||||||
loop {
|
loop {
|
||||||
match input::get_input(&mut editor)? {
|
match input::get_input(&mut editor)? {
|
||||||
input::InputResult::Message(content) => {
|
input::InputResult::Message(content) => {
|
||||||
|
save_history(&mut editor);
|
||||||
|
|
||||||
self.messages.push(Message::user().with_text(&content));
|
self.messages.push(Message::user().with_text(&content));
|
||||||
storage::persist_messages(&self.session_file, &self.messages)?;
|
storage::persist_messages(&self.session_file, &self.messages)?;
|
||||||
|
|
||||||
@@ -250,18 +268,24 @@ impl Session {
|
|||||||
}
|
}
|
||||||
input::InputResult::Exit => break,
|
input::InputResult::Exit => break,
|
||||||
input::InputResult::AddExtension(cmd) => {
|
input::InputResult::AddExtension(cmd) => {
|
||||||
|
save_history(&mut editor);
|
||||||
|
|
||||||
match self.add_extension(cmd.clone()).await {
|
match self.add_extension(cmd.clone()).await {
|
||||||
Ok(_) => output::render_extension_success(&cmd),
|
Ok(_) => output::render_extension_success(&cmd),
|
||||||
Err(e) => output::render_extension_error(&cmd, &e.to_string()),
|
Err(e) => output::render_extension_error(&cmd, &e.to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
input::InputResult::AddBuiltin(names) => {
|
input::InputResult::AddBuiltin(names) => {
|
||||||
|
save_history(&mut editor);
|
||||||
|
|
||||||
match self.add_builtin(names.clone()).await {
|
match self.add_builtin(names.clone()).await {
|
||||||
Ok(_) => output::render_builtin_success(&names),
|
Ok(_) => output::render_builtin_success(&names),
|
||||||
Err(e) => output::render_builtin_error(&names, &e.to_string()),
|
Err(e) => output::render_builtin_error(&names, &e.to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
input::InputResult::ToggleTheme => {
|
input::InputResult::ToggleTheme => {
|
||||||
|
save_history(&mut editor);
|
||||||
|
|
||||||
let current = output::get_theme();
|
let current = output::get_theme();
|
||||||
let new_theme = match current {
|
let new_theme = match current {
|
||||||
output::Theme::Light => {
|
output::Theme::Light => {
|
||||||
@@ -282,12 +306,16 @@ impl Session {
|
|||||||
}
|
}
|
||||||
input::InputResult::Retry => continue,
|
input::InputResult::Retry => continue,
|
||||||
input::InputResult::ListPrompts(extension) => {
|
input::InputResult::ListPrompts(extension) => {
|
||||||
|
save_history(&mut editor);
|
||||||
|
|
||||||
match self.list_prompts(extension).await {
|
match self.list_prompts(extension).await {
|
||||||
Ok(prompts) => output::render_prompts(&prompts),
|
Ok(prompts) => output::render_prompts(&prompts),
|
||||||
Err(e) => output::render_error(&e.to_string()),
|
Err(e) => output::render_error(&e.to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
input::InputResult::PromptCommand(opts) => {
|
input::InputResult::PromptCommand(opts) => {
|
||||||
|
save_history(&mut editor);
|
||||||
|
|
||||||
// name is required
|
// name is required
|
||||||
if opts.name.is_empty() {
|
if opts.name.is_empty() {
|
||||||
output::render_error("Prompt name argument is required");
|
output::render_error("Prompt name argument is required");
|
||||||
|
|||||||
Reference in New Issue
Block a user