Skip to main content

ripl/
session.rs

1use std::collections::hash_map::DefaultHasher;
2use std::fs;
3use std::hash::{Hash, Hasher};
4use std::path::PathBuf;
5
6use crate::providers::Message;
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct SessionCache {
11    pub conversation: Vec<Message>,
12    pub provider: Option<String>,
13    pub model: Option<String>,
14}
15
16pub fn load() -> Option<SessionCache> {
17    let path = session_path();
18    let raw = fs::read_to_string(path).ok()?;
19    serde_json::from_str(&raw).ok()
20}
21
22pub fn save(cache: &SessionCache) {
23    let path = session_path();
24    if let Some(dir) = path.parent() {
25        let _ = fs::create_dir_all(dir);
26    }
27    // Filter system messages — scaffold context is reloaded fresh on each launch.
28    let filtered = SessionCache {
29        conversation: cache
30            .conversation
31            .iter()
32            .filter(|m| m.role != crate::providers::Role::System)
33            .cloned()
34            .collect(),
35        provider: cache.provider.clone(),
36        model: cache.model.clone(),
37    };
38    if let Ok(raw) = serde_json::to_string_pretty(&filtered) {
39        let _ = fs::write(path, raw);
40    }
41}
42
43fn session_path() -> PathBuf {
44    let home = std::env::var("HOME").unwrap_or_else(|_| ".".to_string());
45    let dir = PathBuf::from(home).join(".ripl").join("sessions");
46    let hash = project_hash();
47    dir.join(format!("{}.json", hash))
48}
49
50fn project_hash() -> u64 {
51    let cwd = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
52    let mut hasher = DefaultHasher::new();
53    cwd.to_string_lossy().hash(&mut hasher);
54    hasher.finish()
55}