ai_memory/cli/commands/
reflection_origin.rs1use crate::models::field_names;
15use anyhow::Result;
16use clap::Args;
17use serde_json::{Value, json};
18
19use crate::cli::CliOutput;
20use crate::storage as db;
21
22#[derive(Args, Debug, Clone)]
24pub struct ReflectionOriginArgs {
25 #[arg(long = "memory-id", value_name = "ID")]
27 pub memory_id: String,
28
29 #[arg(long)]
31 pub json: bool,
32}
33
34pub fn cmd_reflection_origin(
42 db_path: &std::path::Path,
43 args: &ReflectionOriginArgs,
44 out: &mut CliOutput<'_>,
45) -> Result<()> {
46 let conn = db::open(db_path)?;
47 let params = json!({"memory_id": args.memory_id});
48
49 let envelope = crate::mcp::handle_reflection_origin(&conn, ¶ms)
50 .map_err(|e| anyhow::anyhow!("reflection-origin: {e}"))?;
51
52 if args.json {
53 writeln!(out.stdout, "{}", serde_json::to_string(&envelope)?)?;
54 return Ok(());
55 }
56
57 let is_refl = envelope
58 .get(field_names::IS_REFLECTION)
59 .and_then(Value::as_bool)
60 .unwrap_or(false);
61 let peer = envelope
62 .get(field_names::PEER_ORIGIN)
63 .and_then(Value::as_str)
64 .unwrap_or("");
65 let depth = envelope
66 .get(field_names::ORIGINAL_DEPTH)
67 .and_then(Value::as_i64)
68 .unwrap_or(0);
69 writeln!(
70 out.stdout,
71 "reflection-origin: is_reflection={is_refl} peer_origin={peer} original_depth={depth}"
72 )?;
73 Ok(())
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79 use crate::cli::test_utils::{TestEnv, seed_memory};
80
81 #[test]
82 fn reflection_origin_cli_known_id_returns_envelope() {
83 let mut env = TestEnv::fresh();
84 let db = env.db_path.clone();
85 let mid = seed_memory(&db, "ns", "plain", "body");
86 let args = ReflectionOriginArgs {
87 memory_id: mid,
88 json: true,
89 };
90 {
91 let mut out = env.output();
92 cmd_reflection_origin(&db, &args, &mut out).expect("ok");
93 }
94 let stdout = env.stdout_str();
95 let envelope: Value = serde_json::from_str(stdout.trim()).expect("parse envelope");
96 assert_eq!(envelope["is_reflection"].as_bool(), Some(false));
98 }
99
100 #[test]
101 fn reflection_origin_cli_empty_id_returns_err() {
102 let mut env = TestEnv::fresh();
103 let db = env.db_path.clone();
104 let args = ReflectionOriginArgs {
105 memory_id: String::new(),
106 json: true,
107 };
108 let mut out = env.output();
109 let err = cmd_reflection_origin(&db, &args, &mut out).expect_err("must fail");
110 assert!(err.to_string().contains("reflection-origin"), "got: {err}");
111 }
112
113 #[test]
114 fn reflection_origin_cli_text_output() {
115 let mut env = TestEnv::fresh();
116 let db = env.db_path.clone();
117 let mid = seed_memory(&db, "ns", "plain-text", "body");
118 let args = ReflectionOriginArgs {
119 memory_id: mid,
120 json: false,
121 };
122 {
123 let mut out = env.output();
124 cmd_reflection_origin(&db, &args, &mut out).expect("ok");
125 }
126 let stdout = env.stdout_str();
127 assert!(
128 stdout.contains("reflection-origin: is_reflection=false"),
129 "got: {stdout}"
130 );
131 assert!(stdout.contains("peer_origin="), "got: {stdout}");
132 assert!(stdout.contains("original_depth="), "got: {stdout}");
133 }
134}