mirror of
https://github.com/aljazceru/goose.git
synced 2025-12-20 07:34:27 +01:00
feat: add session list command in cli (#1586)
This commit is contained in:
@@ -3,4 +3,5 @@ pub mod bench;
|
|||||||
pub mod configure;
|
pub mod configure;
|
||||||
pub mod info;
|
pub mod info;
|
||||||
pub mod mcp;
|
pub mod mcp;
|
||||||
|
pub mod session;
|
||||||
pub mod update;
|
pub mod update;
|
||||||
|
|||||||
47
crates/goose-cli/src/commands/session.rs
Normal file
47
crates/goose-cli/src/commands/session.rs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
use goose::session::info::{get_session_info, SessionInfo};
|
||||||
|
|
||||||
|
pub fn handle_session_list(verbose: bool, format: String) -> Result<()> {
|
||||||
|
let sessions = match get_session_info() {
|
||||||
|
Ok(sessions) => sessions,
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!("Failed to list sessions: {:?}", e);
|
||||||
|
return Err(anyhow::anyhow!("Failed to list sessions"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match format.as_str() {
|
||||||
|
"json" => {
|
||||||
|
println!("{}", serde_json::to_string(&sessions)?);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
if sessions.is_empty() {
|
||||||
|
println!("No sessions found");
|
||||||
|
return Ok(());
|
||||||
|
} else {
|
||||||
|
println!("Available sessions:");
|
||||||
|
for SessionInfo {
|
||||||
|
id,
|
||||||
|
path,
|
||||||
|
metadata,
|
||||||
|
modified,
|
||||||
|
} in sessions
|
||||||
|
{
|
||||||
|
let description = if metadata.description.is_empty() {
|
||||||
|
"(none)"
|
||||||
|
} else {
|
||||||
|
&metadata.description
|
||||||
|
};
|
||||||
|
let output = format!("{} - {} - {}", id, description, modified);
|
||||||
|
if verbose {
|
||||||
|
println!(" {}", output);
|
||||||
|
println!(" Path: {}", path);
|
||||||
|
} else {
|
||||||
|
println!("{}", output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ use goose_cli::commands::bench::{list_suites, run_benchmark};
|
|||||||
use goose_cli::commands::configure::handle_configure;
|
use goose_cli::commands::configure::handle_configure;
|
||||||
use goose_cli::commands::info::handle_info;
|
use goose_cli::commands::info::handle_info;
|
||||||
use goose_cli::commands::mcp::run_server;
|
use goose_cli::commands::mcp::run_server;
|
||||||
|
use goose_cli::commands::session::handle_session_list;
|
||||||
use goose_cli::logging::setup_logging;
|
use goose_cli::logging::setup_logging;
|
||||||
use goose_cli::session;
|
use goose_cli::session;
|
||||||
use goose_cli::session::build_session;
|
use goose_cli::session::build_session;
|
||||||
@@ -53,6 +54,23 @@ fn extract_identifier(identifier: Identifier) -> session::Identifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Subcommand)]
|
||||||
|
enum SessionCommand {
|
||||||
|
#[command(about = "List all available sessions")]
|
||||||
|
List {
|
||||||
|
#[arg(short, long, help = "List all available sessions")]
|
||||||
|
verbose: bool,
|
||||||
|
|
||||||
|
#[arg(
|
||||||
|
short,
|
||||||
|
long,
|
||||||
|
help = "Output format (text, json)",
|
||||||
|
default_value = "text"
|
||||||
|
)]
|
||||||
|
format: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Subcommand)]
|
#[derive(Subcommand)]
|
||||||
enum Command {
|
enum Command {
|
||||||
/// Configure Goose settings
|
/// Configure Goose settings
|
||||||
@@ -77,6 +95,8 @@ enum Command {
|
|||||||
visible_alias = "s"
|
visible_alias = "s"
|
||||||
)]
|
)]
|
||||||
Session {
|
Session {
|
||||||
|
#[command(subcommand)]
|
||||||
|
command: Option<SessionCommand>,
|
||||||
/// Identifier for the chat session
|
/// Identifier for the chat session
|
||||||
#[command(flatten)]
|
#[command(flatten)]
|
||||||
identifier: Option<Identifier>,
|
identifier: Option<Identifier>,
|
||||||
@@ -299,12 +319,20 @@ async fn main() -> Result<()> {
|
|||||||
let _ = run_server(&name).await;
|
let _ = run_server(&name).await;
|
||||||
}
|
}
|
||||||
Some(Command::Session {
|
Some(Command::Session {
|
||||||
|
command,
|
||||||
identifier,
|
identifier,
|
||||||
resume,
|
resume,
|
||||||
debug,
|
debug,
|
||||||
extension,
|
extension,
|
||||||
builtin,
|
builtin,
|
||||||
}) => {
|
}) => {
|
||||||
|
match command {
|
||||||
|
Some(SessionCommand::List { verbose, format }) => {
|
||||||
|
handle_session_list(verbose, format)?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
// Run session command by default
|
||||||
let mut session = build_session(
|
let mut session = build_session(
|
||||||
identifier.map(extract_identifier),
|
identifier.map(extract_identifier),
|
||||||
resume,
|
resume,
|
||||||
@@ -313,7 +341,6 @@ async fn main() -> Result<()> {
|
|||||||
debug,
|
debug,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
setup_logging(
|
setup_logging(
|
||||||
session.session_file().file_stem().and_then(|s| s.to_str()),
|
session.session_file().file_stem().and_then(|s| s.to_str()),
|
||||||
None,
|
None,
|
||||||
@@ -321,6 +348,8 @@ async fn main() -> Result<()> {
|
|||||||
let _ = session.interactive(None).await;
|
let _ = session.interactive(None).await;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some(Command::Run {
|
Some(Command::Run {
|
||||||
instructions,
|
instructions,
|
||||||
input_text,
|
input_text,
|
||||||
|
|||||||
@@ -7,16 +7,9 @@ use axum::{
|
|||||||
};
|
};
|
||||||
use goose::message::Message;
|
use goose::message::Message;
|
||||||
use goose::session;
|
use goose::session;
|
||||||
|
use goose::session::info::{get_session_info, SessionInfo};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
#[derive(Serialize)]
|
|
||||||
struct SessionInfo {
|
|
||||||
id: String,
|
|
||||||
path: String,
|
|
||||||
modified: String,
|
|
||||||
metadata: session::SessionMetadata,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
struct SessionListResponse {
|
struct SessionListResponse {
|
||||||
sessions: Vec<SessionInfo>,
|
sessions: Vec<SessionInfo>,
|
||||||
@@ -44,43 +37,9 @@ async fn list_sessions(
|
|||||||
return Err(StatusCode::UNAUTHORIZED);
|
return Err(StatusCode::UNAUTHORIZED);
|
||||||
}
|
}
|
||||||
|
|
||||||
let sessions = match session::list_sessions() {
|
let sessions = get_session_info().map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||||
Ok(sessions) => sessions,
|
|
||||||
Err(e) => {
|
|
||||||
tracing::error!("Failed to list sessions: {:?}", e);
|
|
||||||
return Err(StatusCode::INTERNAL_SERVER_ERROR);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let session_infos = sessions
|
Ok(Json(SessionListResponse { sessions }))
|
||||||
.into_iter()
|
|
||||||
.map(|(id, path)| {
|
|
||||||
// Get last modified time as string
|
|
||||||
let modified = path
|
|
||||||
.metadata()
|
|
||||||
.and_then(|m| m.modified())
|
|
||||||
.map(|time| {
|
|
||||||
chrono::DateTime::<chrono::Utc>::from(time)
|
|
||||||
.format("%Y-%m-%d %H:%M:%S UTC")
|
|
||||||
.to_string()
|
|
||||||
})
|
|
||||||
.unwrap_or_else(|_| "Unknown".to_string());
|
|
||||||
|
|
||||||
// Get session description
|
|
||||||
let metadata = session::read_metadata(&path).expect("Failed to read session metadata");
|
|
||||||
|
|
||||||
SessionInfo {
|
|
||||||
id,
|
|
||||||
path: path.to_string_lossy().to_string(),
|
|
||||||
modified,
|
|
||||||
metadata,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Ok(Json(SessionListResponse {
|
|
||||||
sessions: session_infos,
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a specific session's history
|
// Get a specific session's history
|
||||||
|
|||||||
49
crates/goose/src/session/info.rs
Normal file
49
crates/goose/src/session/info.rs
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
use anyhow::Result;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use crate::session::{self, SessionMetadata};
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct SessionInfo {
|
||||||
|
pub id: String,
|
||||||
|
pub path: String,
|
||||||
|
pub modified: String,
|
||||||
|
pub metadata: SessionMetadata,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_session_info() -> Result<Vec<SessionInfo>> {
|
||||||
|
let sessions = match session::list_sessions() {
|
||||||
|
Ok(sessions) => sessions,
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!("Failed to list sessions: {:?}", e);
|
||||||
|
return Err(anyhow::anyhow!("Failed to list sessions"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let session_infos = sessions
|
||||||
|
.into_iter()
|
||||||
|
.map(|(id, path)| {
|
||||||
|
// Get last modified time as string
|
||||||
|
let modified = path
|
||||||
|
.metadata()
|
||||||
|
.and_then(|m| m.modified())
|
||||||
|
.map(|time| {
|
||||||
|
chrono::DateTime::<chrono::Utc>::from(time)
|
||||||
|
.format("%Y-%m-%d %H:%M:%S UTC")
|
||||||
|
.to_string()
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|_| "Unknown".to_string());
|
||||||
|
|
||||||
|
// Get session description
|
||||||
|
let metadata = session::read_metadata(&path).expect("Failed to read session metadata");
|
||||||
|
|
||||||
|
SessionInfo {
|
||||||
|
id,
|
||||||
|
path: path.to_string_lossy().to_string(),
|
||||||
|
modified,
|
||||||
|
metadata,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok(session_infos)
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
pub mod info;
|
||||||
pub mod storage;
|
pub mod storage;
|
||||||
|
|
||||||
// Re-export common session types and functions
|
// Re-export common session types and functions
|
||||||
@@ -6,3 +7,5 @@ pub use storage::{
|
|||||||
get_path, list_sessions, persist_messages, read_messages, read_metadata, update_metadata,
|
get_path, list_sessions, persist_messages, read_messages, read_metadata, update_metadata,
|
||||||
Identifier, SessionMetadata,
|
Identifier, SessionMetadata,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub use info::{get_session_info, SessionInfo};
|
||||||
|
|||||||
Reference in New Issue
Block a user