use super::Session;
use anyhow::Result;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use tokio::fs;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SessionSummary {
pub id: String,
pub title: Option<String>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
pub message_count: usize,
pub agent: String,
#[serde(default)]
pub directory: Option<PathBuf>,
}
pub async fn list_sessions() -> Result<Vec<SessionSummary>> {
let sessions_dir = crate::config::Config::data_dir()
.map(|d| d.join("sessions"))
.ok_or_else(|| anyhow::anyhow!("Could not determine data directory"))?;
if !sessions_dir.exists() {
return Ok(Vec::new());
}
let mut summaries = Vec::new();
let mut entries = fs::read_dir(&sessions_dir).await?;
while let Some(entry) = entries.next_entry().await? {
let path = entry.path();
if path.extension().map(|e| e == "json").unwrap_or(false)
&& let Ok(content) = fs::read_to_string(&path).await
&& let Ok(session) = serde_json::from_str::<Session>(&content)
{
summaries.push(SessionSummary {
id: session.id,
title: session.title,
created_at: session.created_at,
updated_at: session.updated_at,
message_count: session.messages.len(),
agent: session.agent,
directory: session.metadata.directory,
});
}
}
summaries.sort_by(|a, b| b.updated_at.cmp(&a.updated_at));
Ok(summaries)
}
pub async fn list_sessions_for_directory(dir: &std::path::Path) -> Result<Vec<SessionSummary>> {
let all = list_sessions().await?;
let canonical = dir.canonicalize().unwrap_or_else(|_| dir.to_path_buf());
Ok(all
.into_iter()
.filter(|s| {
s.directory
.as_ref()
.map(|d| d.canonicalize().unwrap_or_else(|_| d.clone()) == canonical)
.unwrap_or(false)
})
.collect())
}
pub async fn list_sessions_paged(
dir: &std::path::Path,
limit: usize,
offset: usize,
) -> Result<Vec<SessionSummary>> {
let mut sessions = list_sessions_for_directory(dir).await?;
sessions.sort_by(|a, b| b.updated_at.cmp(&a.updated_at));
Ok(sessions.into_iter().skip(offset).take(limit).collect())
}