use std::path::{Path, PathBuf};
use miette::{Result, miette};
use crate::{
daat_locus_paths::{DaatLocusPaths, daat_locus_paths},
daemon::connect_daemon_status,
reasoning::compiled::COMPILED_DIR_NAME,
};
async fn get_daat_locus_home() -> PathBuf {
daat_locus_paths().await.root().to_path_buf()
}
async fn reject_if_daemon_running(reset_name: &str) -> Result<()> {
if connect_daemon_status().await.is_ok() {
return Err(miette!(
"{reset_name} refused while DaatLocus daemon is running; run `daat-locus stop` first"
));
}
Ok(())
}
pub async fn run_memory_reset() -> Result<()> {
let home = get_daat_locus_home().await;
reject_if_daemon_running("memory reset").await?;
clear_memory_state(&home).await?;
println!(
"[memory-reset] reset memory persistence under {}",
home.display()
);
println!("[memory-reset] cleared: runtime_conversation");
println!("[memory-reset] cleared: runtime_error_cases.jsonl, reasoning_traces.jsonl");
println!("[memory-reset] cleared: current plan");
println!("[memory-reset] preserved: config/, state/, artifacts/, logs/");
Ok(())
}
async fn clear_memory_state(home: &Path) -> Result<()> {
let paths = DaatLocusPaths::from_root(home.to_path_buf());
clear_files(&[
paths.memory_file("runtime_conversation.json"),
paths.memory_file("runtime_conversation"),
paths.memory_file("plan"),
paths.journal_file("runtime_error_cases.jsonl"),
paths.journal_file("reasoning_traces.jsonl"),
])
.await?;
Ok(())
}
pub async fn run_state_reset() -> Result<()> {
let home = get_daat_locus_home().await;
reject_if_daemon_running("state reset").await?;
let cleared = clear_state_files(&home).await?;
println!("[state-reset] reset runtime state under {}", home.display());
if cleared.is_empty() {
println!("[state-reset] nothing to remove");
} else {
println!("[state-reset] cleared: {}", cleared.join(", "));
}
println!("[state-reset] preserved: config/, memory/, artifacts/, logs/");
Ok(())
}
async fn clear_state_files(home: &Path) -> Result<Vec<String>> {
let paths = DaatLocusPaths::from_root(home.to_path_buf());
let files = [
"events",
"pending_work_queue",
"telegram_transport_state",
"telegram_update_offset",
];
clear_named_files(paths.state_dir(), &files).await
}
pub async fn run_compile_reset() -> Result<()> {
let home = get_daat_locus_home().await;
reject_if_daemon_running("compile reset").await?;
let cleared = clear_compiled_artifacts(&home).await?;
println!(
"[compile-reset] cleared compile/evaluation artifacts under {}",
home.display()
);
if cleared.is_empty() {
println!("[compile-reset] nothing to remove");
} else {
println!("[compile-reset] cleared: {}", cleared.join(", "));
}
println!("[compile-reset] preserved: config/, state/, memory, logs/");
Ok(())
}
async fn clear_compiled_artifacts(home: &Path) -> Result<Vec<String>> {
let mut cleared = Vec::new();
let paths = DaatLocusPaths::from_root(home.to_path_buf());
for dir_name in [COMPILED_DIR_NAME, "evaluations"] {
let path = paths.artifact_dir(dir_name);
if path.exists() {
tokio::fs::remove_dir_all(&path)
.await
.map_err(|err| miette!("failed to remove {}: {err}", path.display()))?;
cleared.push(dir_name.to_string());
}
}
Ok(cleared)
}
pub async fn run_reset_all() -> Result<()> {
let home = get_daat_locus_home().await;
reject_if_daemon_running("reset all").await?;
clear_memory_state(&home).await?;
let state_cleared = clear_state_files(&home).await?;
let artifact_cleared = clear_compiled_artifacts(&home).await?;
let log_cleared = clear_log_dirs(&home).await?;
println!("[reset] reset all state under {}", home.display());
if state_cleared.is_empty() {
println!("[reset] cleared state: none");
} else {
println!("[reset] cleared state: {}", state_cleared.join(", "));
}
println!(
"[reset] cleared memory: runtime_conversation, runtime_error_cases.jsonl, reasoning_traces.jsonl"
);
if artifact_cleared.is_empty() {
println!("[reset] cleared compile artifacts: none");
} else {
println!(
"[reset] cleared compile artifacts: {}",
artifact_cleared.join(", ")
);
}
if log_cleared.is_empty() {
println!("[reset] cleared logs: none");
} else {
println!("[reset] cleared logs: {}", log_cleared.join(", "));
}
println!("[reset] preserved: config.toml, telegram_acl.json");
Ok(())
}
async fn clear_log_dirs(home: &Path) -> Result<Vec<String>> {
let mut cleared = Vec::new();
let paths = DaatLocusPaths::from_root(home.to_path_buf());
let path = paths.logs_dir();
if path.exists() {
tokio::fs::remove_dir_all(&path)
.await
.map_err(|err| miette!("failed to remove {}: {err}", path.display()))?;
cleared.push("logs".to_string());
}
Ok(cleared)
}
async fn clear_named_files(dir: PathBuf, file_names: &[&str]) -> Result<Vec<String>> {
let mut cleared = Vec::new();
for file_name in file_names {
let path = dir.join(file_name);
if path.exists() {
tokio::fs::remove_file(&path)
.await
.map_err(|err| miette!("failed to remove {}: {err}", path.display()))?;
cleared.push((*file_name).to_string());
}
}
Ok(cleared)
}
async fn clear_files(paths: &[PathBuf]) -> Result<()> {
for path in paths {
if path.exists() {
tokio::fs::remove_file(path)
.await
.map_err(|err| miette!("failed to remove {}: {err}", path.display()))?;
}
}
Ok(())
}