// Configuration for poc-memory // // Loaded from ~/.config/poc-memory/config.toml (or POC_MEMORY_CONFIG env). // Falls back to sensible defaults if no config file exists. use std::path::PathBuf; use std::sync::OnceLock; static CONFIG: OnceLock = OnceLock::new(); #[derive(Debug, Clone)] pub struct Config { /// Display name for the human user in transcripts/prompts. pub user_name: String, /// Display name for the AI assistant. pub assistant_name: String, /// Base directory for memory data (store, logs, status). pub data_dir: PathBuf, /// Directory containing Claude session transcripts. pub projects_dir: PathBuf, /// Core node keys that should never be decayed/deleted. pub core_nodes: Vec, } impl Default for Config { fn default() -> Self { let home = PathBuf::from(std::env::var("HOME").expect("HOME not set")); Self { user_name: "User".to_string(), assistant_name: "Assistant".to_string(), data_dir: home.join(".claude/memory"), projects_dir: home.join(".claude/projects"), core_nodes: vec!["identity.md".to_string()], } } } impl Config { fn load_from_file() -> Self { let path = std::env::var("POC_MEMORY_CONFIG") .map(PathBuf::from) .unwrap_or_else(|_| { PathBuf::from(std::env::var("HOME").expect("HOME not set")) .join(".config/poc-memory/config.toml") }); let mut config = Config::default(); let Ok(content) = std::fs::read_to_string(&path) else { return config; }; // Simple TOML parser — we only need flat key = "value" pairs. for line in content.lines() { let line = line.trim(); if line.is_empty() || line.starts_with('#') { continue; } let Some((key, value)) = line.split_once('=') else { continue }; let key = key.trim(); let value = value.trim().trim_matches('"'); match key { "user_name" => config.user_name = value.to_string(), "assistant_name" => config.assistant_name = value.to_string(), "data_dir" => config.data_dir = expand_home(value), "projects_dir" => config.projects_dir = expand_home(value), "core_nodes" => { config.core_nodes = value.split(',') .map(|s| s.trim().to_string()) .filter(|s| !s.is_empty()) .collect(); } _ => {} } } config } } fn expand_home(path: &str) -> PathBuf { if let Some(rest) = path.strip_prefix("~/") { PathBuf::from(std::env::var("HOME").expect("HOME not set")).join(rest) } else { PathBuf::from(path) } } /// Get the global config (loaded once on first access). pub fn get() -> &'static Config { CONFIG.get_or_init(Config::load_from_file) }