From 08682507d93bebcf9267c3c565f9000c6946ecdd Mon Sep 17 00:00:00 2001 From: Raduan Al-Shedivat <88370223+dbraduan@users.noreply.github.com> Date: Wed, 23 Apr 2025 17:21:27 +0200 Subject: [PATCH] Add support for ascending/descending ordering of goose session list (#2087) --- crates/goose-cli/src/cli.rs | 15 +++++++++-- crates/goose-cli/src/commands/session.rs | 14 +++++++--- crates/goose-server/src/routes/session.rs | 5 ++-- crates/goose/src/session/info.rs | 31 ++++++++++++++++++++--- 4 files changed, 54 insertions(+), 11 deletions(-) diff --git a/crates/goose-cli/src/cli.rs b/crates/goose-cli/src/cli.rs index bb168d11..e400dfbe 100644 --- a/crates/goose-cli/src/cli.rs +++ b/crates/goose-cli/src/cli.rs @@ -73,6 +73,13 @@ enum SessionCommand { default_value = "text" )] format: String, + + #[arg( + long = "ascending", + help = "Sort by date in ascending order (oldest first)", + long_help = "Sort sessions by date in ascending order (oldest first). Default is descending order (newest first)." + )] + ascending: bool, }, #[command(about = "Remove sessions")] Remove { @@ -393,8 +400,12 @@ pub async fn cli() -> Result<()> { builtins, }) => { return match command { - Some(SessionCommand::List { verbose, format }) => { - handle_session_list(verbose, format)?; + Some(SessionCommand::List { + verbose, + format, + ascending, + }) => { + handle_session_list(verbose, format, ascending)?; Ok(()) } Some(SessionCommand::Remove { id, regex }) => { diff --git a/crates/goose-cli/src/commands/session.rs b/crates/goose-cli/src/commands/session.rs index f09b8184..6a36d61d 100644 --- a/crates/goose-cli/src/commands/session.rs +++ b/crates/goose-cli/src/commands/session.rs @@ -1,5 +1,5 @@ use anyhow::{Context, Result}; -use goose::session::info::{get_session_info, SessionInfo}; +use goose::session::info::{get_session_info, SessionInfo, SortOrder}; use regex::Regex; use std::fs; @@ -21,7 +21,7 @@ pub fn remove_session(session: &SessionInfo) -> Result<()> { } pub fn handle_session_remove(id: String, regex_string: String) -> Result<()> { - let sessions = match get_session_info() { + let sessions = match get_session_info(SortOrder::Descending) { Ok(sessions) => sessions, Err(e) => { tracing::error!("Failed to remove sessions: {:?}", e); @@ -57,8 +57,14 @@ pub fn handle_session_remove(id: String, regex_string: String) -> Result<()> { Ok(()) } -pub fn handle_session_list(verbose: bool, format: String) -> Result<()> { - let sessions = match get_session_info() { +pub fn handle_session_list(verbose: bool, format: String, ascending: bool) -> Result<()> { + let sort_order = if ascending { + SortOrder::Ascending + } else { + SortOrder::Descending + }; + + let sessions = match get_session_info(sort_order) { Ok(sessions) => sessions, Err(e) => { tracing::error!("Failed to list sessions: {:?}", e); diff --git a/crates/goose-server/src/routes/session.rs b/crates/goose-server/src/routes/session.rs index 02b232da..e887e95e 100644 --- a/crates/goose-server/src/routes/session.rs +++ b/crates/goose-server/src/routes/session.rs @@ -8,7 +8,7 @@ use axum::{ }; use goose::message::Message; use goose::session; -use goose::session::info::{get_session_info, SessionInfo}; +use goose::session::info::{get_session_info, SessionInfo, SortOrder}; use serde::Serialize; #[derive(Serialize)] @@ -30,7 +30,8 @@ async fn list_sessions( ) -> Result, StatusCode> { verify_secret_key(&headers, &state)?; - let sessions = get_session_info().map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; + let sessions = + get_session_info(SortOrder::Descending).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; Ok(Json(SessionListResponse { sessions })) } diff --git a/crates/goose/src/session/info.rs b/crates/goose/src/session/info.rs index 1259aa3d..44c2ca4f 100644 --- a/crates/goose/src/session/info.rs +++ b/crates/goose/src/session/info.rs @@ -1,5 +1,6 @@ use anyhow::Result; use serde::Serialize; +use std::cmp::Ordering; use crate::session::{self, SessionMetadata}; @@ -11,7 +12,13 @@ pub struct SessionInfo { pub metadata: SessionMetadata, } -pub fn get_session_info() -> Result> { +/// Sort order for listing sessions +pub enum SortOrder { + Ascending, + Descending, +} + +pub fn get_session_info(sort_order: SortOrder) -> Result> { let sessions = match session::list_sessions() { Ok(sessions) => sessions, Err(e) => { @@ -19,7 +26,7 @@ pub fn get_session_info() -> Result> { return Err(anyhow::anyhow!("Failed to list sessions")); } }; - let session_infos = sessions + let mut session_infos = sessions .into_iter() .map(|(id, path)| { // Get last modified time as string @@ -43,7 +50,25 @@ pub fn get_session_info() -> Result> { metadata, } }) - .collect(); + .collect::>(); + + // Sort sessions by modified date + // Since all dates are in ISO format (YYYY-MM-DD HH:MM:SS UTC), we can just use string comparison + // This works because the ISO format ensures lexicographical ordering matches chronological ordering + session_infos.sort_by(|a, b| { + if a.modified == "Unknown" && b.modified == "Unknown" { + return Ordering::Equal; + } else if a.modified == "Unknown" { + return Ordering::Greater; // Unknown dates go last + } else if b.modified == "Unknown" { + return Ordering::Less; + } + + match sort_order { + SortOrder::Ascending => a.modified.cmp(&b.modified), + SortOrder::Descending => b.modified.cmp(&a.modified), + } + }); Ok(session_infos) }