use crate::models::field_names;
use anyhow::Result;
use clap::Args;
use serde_json::{Value, json};
use crate::cli::CliOutput;
use crate::storage as db;
#[derive(Args, Debug, Clone)]
pub struct EntityRegisterArgs {
#[arg(long = "canonical-name", value_name = "NAME")]
pub canonical_name: String,
#[arg(long, value_name = "NS")]
pub namespace: String,
#[arg(long, value_name = "CSV", value_delimiter = ',')]
pub aliases: Vec<String>,
#[arg(long = "agent-id", value_name = "AGENT_ID")]
pub agent_id: Option<String>,
#[arg(long)]
pub json: bool,
}
pub fn cmd_entity_register(
db_path: &std::path::Path,
args: &EntityRegisterArgs,
out: &mut CliOutput<'_>,
) -> Result<()> {
let conn = db::open(db_path)?;
let mut params = json!({
(field_names::CANONICAL_NAME): args.canonical_name,
"namespace": args.namespace,
});
if !args.aliases.is_empty() {
params["aliases"] = json!(args.aliases);
}
if let Some(a) = &args.agent_id {
params["agent_id"] = json!(a);
}
let envelope = crate::mcp::handle_entity_register(&conn, ¶ms, None)
.map_err(|e| anyhow::anyhow!("entity-register: {e}"))?;
if args.json {
writeln!(out.stdout, "{}", serde_json::to_string(&envelope)?)?;
return Ok(());
}
let id = envelope
.get("entity_id")
.and_then(Value::as_str)
.unwrap_or("?");
let created = envelope
.get("created")
.and_then(Value::as_bool)
.unwrap_or(false);
writeln!(
out.stdout,
"entity-register: entity_id={id} created={created}"
)?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::cli::test_utils::TestEnv;
#[test]
fn entity_register_cli_happy_path_writes_envelope() {
let mut env = TestEnv::fresh();
let db = env.db_path.clone();
let args = EntityRegisterArgs {
canonical_name: "Alice".into(),
namespace: "characters".into(),
aliases: vec!["al".into(), "ali".into()],
agent_id: Some("ai:tester".into()),
json: true,
};
{
let mut out = env.output();
cmd_entity_register(&db, &args, &mut out).expect("ok");
}
let stdout = env.stdout_str();
let envelope: Value = serde_json::from_str(stdout.trim()).expect("parse envelope");
assert_eq!(envelope["canonical_name"].as_str(), Some("Alice"));
assert_eq!(envelope["created"].as_bool(), Some(true));
}
#[test]
fn entity_register_cli_empty_name_returns_err() {
let mut env = TestEnv::fresh();
let db = env.db_path.clone();
let args = EntityRegisterArgs {
canonical_name: String::new(),
namespace: "characters".into(),
aliases: vec![],
agent_id: None,
json: true,
};
let mut out = env.output();
let err = cmd_entity_register(&db, &args, &mut out).expect_err("must fail");
assert!(err.to_string().contains("entity-register"), "got: {err}");
}
#[test]
fn entity_register_cli_text_output_no_aliases_no_agent() {
let mut env = TestEnv::fresh();
let db = env.db_path.clone();
let args = EntityRegisterArgs {
canonical_name: "Dave".into(),
namespace: "characters".into(),
aliases: vec![],
agent_id: None,
json: false,
};
{
let mut out = env.output();
cmd_entity_register(&db, &args, &mut out).expect("ok");
}
let stdout = env.stdout_str();
assert!(
stdout.contains("entity-register: entity_id="),
"got: {stdout}"
);
assert!(stdout.contains("created=true"), "got: {stdout}");
}
}