mod handlers;
use crate::config;
use crate::errors::Error;
use crate::memory::MemoryStore;
use std::process::ExitCode;
#[derive(clap::Subcommand)]
pub enum Commands {
Validate {
text: String,
},
Add {
text: String,
#[arg(short = 'm', long)]
metadata: Option<String>,
#[arg(long)]
force: bool,
#[arg(long, default_value = "fact")]
memory_type: String,
#[arg(long, default_value = "active")]
status: String,
#[arg(long)]
supersedes: Option<String>,
},
Search {
query: String,
#[arg(short = 'l', long, default_value = "5")]
limit: usize,
#[arg(long)]
recency: Option<f64>,
#[arg(long)]
hybrid: bool,
#[arg(long)]
no_hybrid: bool,
#[arg(long)]
memory_type: Option<String>,
#[arg(long)]
status: Option<String>,
#[arg(long)]
include_candidates: bool,
#[arg(long)]
no_touch: bool,
},
Get {
id: String,
#[arg(long)]
no_touch: bool,
},
List {
#[arg(short = 'l', long, default_value = "10")]
limit: usize,
#[arg(long)]
memory_type: Option<String>,
#[arg(long)]
status: Option<String>,
#[arg(long)]
include_candidates: bool,
},
Delete {
id: String,
},
Update {
id: String,
#[arg(short = 't', long)]
text: Option<String>,
#[arg(short = 'm', long)]
metadata: Option<String>,
#[arg(long)]
memory_type: Option<String>,
#[arg(long)]
status: Option<String>,
},
Version,
#[cfg(feature = "mcp")]
Mcp,
}
pub fn execute(
command: &Commands,
store: &mut MemoryStore,
project_id: String,
config: &config::Config,
json: bool,
) -> Result<ExitCode, Error> {
match command {
Commands::Validate { text } => {
handlers::handle_validate(text, &config.embedding_model, json)
}
Commands::Add {
text,
metadata,
force,
memory_type,
status,
supersedes,
} => handlers::handle_add(
store,
&project_id,
text,
metadata.as_deref(),
*force,
memory_type,
status,
supersedes.as_deref(),
json,
),
Commands::Search {
query,
limit,
recency,
hybrid,
no_hybrid,
memory_type,
status,
include_candidates,
no_touch,
} => handlers::handle_search(
store,
&project_id,
&handlers::SearchContext {
query: query.clone(),
limit: *limit,
recency: *recency,
hybrid: *hybrid,
no_hybrid: *no_hybrid,
memory_type: memory_type.clone(),
status: status.clone(),
include_candidates: *include_candidates,
no_touch: *no_touch,
},
config,
json,
),
Commands::Get { id, no_touch } => handlers::handle_get(store, id, *no_touch, json),
Commands::List {
limit,
memory_type,
status,
include_candidates,
} => handlers::handle_list(
store,
&project_id,
*limit,
memory_type.as_deref(),
status.as_deref(),
*include_candidates,
json,
),
Commands::Delete { id } => handlers::handle_delete(store, id, json),
Commands::Update {
id,
text,
metadata,
memory_type,
status,
} => handlers::handle_update(
store,
id,
text.as_deref(),
metadata.as_deref(),
memory_type.as_deref(),
status.as_deref(),
json,
),
Commands::Version => handlers::handle_version(json),
#[cfg(feature = "mcp")]
Commands::Mcp => unreachable!("Mcp is handled before execute"),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_validate_short_text() {
let result =
handlers::handle_validate("hello world", "not-a-real-model-should-fail", false);
assert!(result.is_err());
}
#[test]
fn test_validate_long_text() {
let long_text = "a".repeat(1000);
let result = handlers::handle_validate(&long_text, "not-a-real-model-should-fail", false);
assert!(result.is_err());
}
}