ai_memory/cli/commands/
entity_register.rs1use crate::models::field_names;
14use anyhow::Result;
15use clap::Args;
16use serde_json::{Value, json};
17
18use crate::cli::CliOutput;
19use crate::storage as db;
20
21#[derive(Args, Debug, Clone)]
23pub struct EntityRegisterArgs {
24 #[arg(long = "canonical-name", value_name = "NAME")]
26 pub canonical_name: String,
27
28 #[arg(long, value_name = "NS")]
30 pub namespace: String,
31
32 #[arg(long, value_name = "CSV", value_delimiter = ',')]
34 pub aliases: Vec<String>,
35
36 #[arg(long = "agent-id", value_name = "AGENT_ID")]
38 pub agent_id: Option<String>,
39
40 #[arg(long)]
42 pub json: bool,
43}
44
45pub fn cmd_entity_register(
54 db_path: &std::path::Path,
55 args: &EntityRegisterArgs,
56 out: &mut CliOutput<'_>,
57) -> Result<()> {
58 let conn = db::open(db_path)?;
59
60 let mut params = json!({
61 (field_names::CANONICAL_NAME): args.canonical_name,
62 "namespace": args.namespace,
63 });
64 if !args.aliases.is_empty() {
65 params["aliases"] = json!(args.aliases);
66 }
67 if let Some(a) = &args.agent_id {
68 params["agent_id"] = json!(a);
69 }
70
71 let envelope = crate::mcp::handle_entity_register(&conn, ¶ms, None)
72 .map_err(|e| anyhow::anyhow!("entity-register: {e}"))?;
73
74 if args.json {
75 writeln!(out.stdout, "{}", serde_json::to_string(&envelope)?)?;
76 return Ok(());
77 }
78
79 let id = envelope
80 .get("entity_id")
81 .and_then(Value::as_str)
82 .unwrap_or("?");
83 let created = envelope
84 .get("created")
85 .and_then(Value::as_bool)
86 .unwrap_or(false);
87 writeln!(
88 out.stdout,
89 "entity-register: entity_id={id} created={created}"
90 )?;
91 Ok(())
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97 use crate::cli::test_utils::TestEnv;
98
99 #[test]
100 fn entity_register_cli_happy_path_writes_envelope() {
101 let mut env = TestEnv::fresh();
102 let db = env.db_path.clone();
103 let args = EntityRegisterArgs {
104 canonical_name: "Alice".into(),
105 namespace: "characters".into(),
106 aliases: vec!["al".into(), "ali".into()],
107 agent_id: Some("ai:tester".into()),
108 json: true,
109 };
110 {
111 let mut out = env.output();
112 cmd_entity_register(&db, &args, &mut out).expect("ok");
113 }
114 let stdout = env.stdout_str();
115 let envelope: Value = serde_json::from_str(stdout.trim()).expect("parse envelope");
116 assert_eq!(envelope["canonical_name"].as_str(), Some("Alice"));
117 assert_eq!(envelope["created"].as_bool(), Some(true));
118 }
119
120 #[test]
121 fn entity_register_cli_empty_name_returns_err() {
122 let mut env = TestEnv::fresh();
123 let db = env.db_path.clone();
124 let args = EntityRegisterArgs {
125 canonical_name: String::new(),
126 namespace: "characters".into(),
127 aliases: vec![],
128 agent_id: None,
129 json: true,
130 };
131 let mut out = env.output();
132 let err = cmd_entity_register(&db, &args, &mut out).expect_err("must fail");
133 assert!(err.to_string().contains("entity-register"), "got: {err}");
134 }
135
136 #[test]
137 fn entity_register_cli_text_output_no_aliases_no_agent() {
138 let mut env = TestEnv::fresh();
139 let db = env.db_path.clone();
140 let args = EntityRegisterArgs {
141 canonical_name: "Dave".into(),
142 namespace: "characters".into(),
143 aliases: vec![],
144 agent_id: None,
145 json: false,
146 };
147 {
148 let mut out = env.output();
149 cmd_entity_register(&db, &args, &mut out).expect("ok");
150 }
151 let stdout = env.stdout_str();
152 assert!(
153 stdout.contains("entity-register: entity_id="),
154 "got: {stdout}"
155 );
156 assert!(stdout.contains("created=true"), "got: {stdout}");
157 }
158}