1use std::path::PathBuf;
2use std::sync::Arc;
3
4use serde_json::Value;
5
6use crate::config;
7use crate::dispatch;
8use crate::error::CoreError;
9use crate::output::{OutputFormat, format_output};
10use crate::persistence;
11use crate::server::{
12 ServerState, check_legacy_database, initialize_state, resolve_index_directory,
13};
14
15pub async fn execute(
16 state: Arc<ServerState>,
17 method: &str,
18 params: Value,
19 format: &OutputFormat,
20) -> Result<(), CoreError> {
21 let result = dispatch::route(method, &state, params).await?;
22 let formatted = format_output(&result, format);
23 println!("{formatted}");
24 save_indexes_if_mutating(method, &state)?;
25 Ok(())
26}
27
28pub fn build_state(config: &crate::config::Config) -> Result<Arc<ServerState>, CoreError> {
29 let cwd = std::env::current_dir()
30 .map_err(|error| CoreError::InitFailed(format!("cwd unavailable: {error}")))?;
31 let home = home_dir_or_error()?;
32 build_state_with_dirs(config, &cwd, &home)
33}
34
35pub fn build_state_with_dirs(
36 config: &crate::config::Config,
37 cwd: &std::path::Path,
38 home_dir: &std::path::Path,
39) -> Result<Arc<ServerState>, CoreError> {
40 let project_dir = match config::resolve_project_dir(cwd, None) {
41 Ok(path) => path,
42 Err(CoreError::ProjectDirNotFound) => {
43 return Err(CoreError::InitFailed(
44 "no .engram/ directory found in cwd or ancestors — run 'engram init' in your project root".into(),
45 ));
46 }
47 Err(other) => return Err(other),
48 };
49 check_legacy_database(&project_dir, home_dir)?;
50 let state = initialize_state(config, &project_dir, home_dir)?;
51 Ok(Arc::new(state))
52}
53
54fn home_dir_or_error() -> Result<PathBuf, CoreError> {
55 match std::env::var("HOME") {
56 Ok(value) if !value.is_empty() => Ok(PathBuf::from(value)),
57 _ => Err(CoreError::InitFailed(
58 "HOME environment variable not set".into(),
59 )),
60 }
61}
62
63fn save_indexes_if_mutating(method: &str, state: &Arc<ServerState>) -> Result<(), CoreError> {
64 let mutating = matches!(
65 method,
66 "memory_store"
67 | "memory_consolidate_apply"
68 | "memory_import"
69 | "memory_insights"
70 | "memory_migrate_apply"
71 | "memory_train_generate"
72 | "memory_train_delete"
73 | "memory_reembed"
74 );
75 if !mutating {
76 return Ok(());
77 }
78 let index_directory = resolve_index_directory(&state.database_path);
79 let indexes = state.indexes.read().unwrap();
80 persistence::save_to_disk(&index_directory, &indexes)
81}