pub mod cache;
mod loader;
mod parser;
pub mod path;
use crate::error::{AppError, Result};
use chrono::{DateTime, Local};
use std::path::PathBuf;
use std::time::SystemTime;
pub use loader::{
delete_session_by_uuid, find_jsonl_by_uuid, load_all_conversations,
load_all_conversations_streaming,
};
pub use parser::process_conversation_file;
pub use path::{convert_path_to_project_dir_name, format_short_name_from_path, is_same_project};
#[derive(Clone, Debug)]
pub struct ParseError {
pub line_number: usize,
pub line_content: String,
pub error_message: String,
pub context_before: Vec<String>,
pub context_after: Vec<String>,
}
#[derive(Clone)]
pub struct Conversation {
pub path: PathBuf,
pub index: usize,
pub timestamp: DateTime<Local>,
pub preview: String,
pub preview_first: String,
pub preview_last: String,
pub full_text: String,
pub search_text_lower: String,
pub project_name: Option<String>,
pub project_path: Option<PathBuf>,
pub cwd: Option<PathBuf>,
pub message_count: usize,
pub parse_errors: Vec<ParseError>,
pub summary: Option<String>,
pub custom_title: Option<String>,
pub model: Option<String>,
pub total_tokens: u64,
pub duration_minutes: Option<u64>,
}
pub struct Project {
pub name: String, pub display_name: String, pub modified: SystemTime,
}
pub enum LoaderMessage {
Fatal(AppError),
ProjectError,
Batch(Vec<Conversation>),
Done,
}
pub fn get_claude_projects_root() -> Result<PathBuf> {
let claude_dir = if let Ok(config_dir) = std::env::var("CLAUDE_CONFIG_DIR") {
PathBuf::from(config_dir)
} else {
let home_dir = home::home_dir().ok_or_else(|| {
AppError::Io(std::io::Error::new(
std::io::ErrorKind::NotFound,
"Could not determine home directory",
))
})?;
home_dir.join(".claude")
};
Ok(claude_dir.join("projects"))
}
pub fn get_claude_projects_dir(current_dir: &std::path::Path) -> Result<PathBuf> {
let converted = convert_path_to_project_dir_name(current_dir);
Ok(get_claude_projects_root()?.join(converted))
}