use chrono::Local;
use crate::config::LoreConfig;
use crate::error::Result;
use crate::file_manager::FileManager;
use crate::git_manager::GitManager;
use crate::reflection::ReflectionEngine;
pub struct LoreFS {
config: LoreConfig,
file_manager: FileManager,
git_manager: Option<GitManager>,
reflection_engine: ReflectionEngine,
turn_count: usize,
}
impl LoreFS {
pub fn new(config: LoreConfig) -> Result<Self> {
let file_manager = FileManager::new(config.base_dir.clone())?;
let git_manager = if config.git_enabled {
Some(GitManager::new(file_manager.base_path())?)
} else {
None
};
let lore = Self {
config,
file_manager,
git_manager,
reflection_engine: ReflectionEngine::new(),
turn_count: 0,
};
lore.init_templates()?;
Ok(lore)
}
fn init_templates(&self) -> Result<()> {
let templates = [
("system/persona.md", "# Persona\nDefine the agent's personality and identity here."),
("system/preferences.md", "# Preferences\nStore user-specific preferences and global settings."),
("system/workflow.md", "# Workflow\nDocument standard operating procedures and task flows."),
("MEMORY.md", "# Long-term Memory\nKey facts and decisions go here."),
("USER.md", "# User Profile\nInformation about the user."),
];
for (path, content) in templates {
if self.file_manager.read_file(path).is_err() {
self.file_manager.write_file(path, content)?;
}
}
if let Some(git) = &self.git_manager {
git.commit("Initial memory structure")?;
}
Ok(())
}
pub fn add(&mut self, text: &str, file: &str, tags: &[&str]) -> Result<()> {
let timestamp = Local::now().format("%Y-%m-%d %H:%M:%S");
let tag_str = if tags.is_empty() {
"".to_string()
} else {
format!(" #{}", tags.join(" #"))
};
let entry = format!("- [{}] {}{}", timestamp, text, tag_str);
self.file_manager.append_to_file(file, &entry)?;
self.increment_turn()?;
if self.config.auto_commit {
if let Some(git) = &self.git_manager {
git.commit(&format!("Add memory to {}: {}", file, text))?;
}
}
Ok(())
}
pub fn update_file(&mut self, path: &str, content: &str) -> Result<()> {
self.file_manager.write_file(path, content)?;
self.increment_turn()?;
if self.config.auto_commit {
if let Some(git) = &self.git_manager {
git.commit(&format!("Update memory file: {}", path))?;
}
}
Ok(())
}
pub fn search(&self, query: &str, _limit: usize, _scope: Option<&str>) -> Result<Vec<(String, String)>> {
self.file_manager.search(query)
}
pub fn get_memory_tree(&self) -> String {
self.file_manager.get_tree()
}
pub fn reflect(&self) -> Result<()> {
self.reflection_engine.reflect(&self.file_manager)
}
fn increment_turn(&mut self) -> Result<()> {
self.turn_count += 1;
if self.turn_count % self.config.reflection_every_n_turns == 0 {
self.reflect()?;
}
Ok(())
}
pub fn read_file(&self, path: &str) -> Result<String> {
self.file_manager.read_file(path)
}
pub fn get_pinned_context(&self) -> Result<String> {
let mut context = String::new();
context.push_str("=== PINNED MEMORY CONTEXT ===\n\n");
for file in &self.config.pinned_files {
if let Ok(content) = self.file_manager.read_file(file) {
context.push_str(&format!("--- {} ---\n", file));
context.push_str(&content);
context.push_str("\n\n");
}
}
context.push_str("=============================\n");
Ok(context)
}
}