Skip to main content

memstead_cli/commands/
reload.rs

1//! `memstead reload` — refresh the engine's in-memory store from on-disk
2//! branch state. CLI surface parity with the MCP `memstead_reload` tool:
3//! without the `Reload` subcommand variant, `memstead reload` would refuse
4//! with `unrecognized subcommand` while the same op stays reachable through
5//! MCP. AGENTS.md's parity rule
6//! ("every operation reachable through the engine SHOULD be
7//! reachable via both UniFFI and CLI") makes this the correct
8//! direction to close.
9
10use clap::Parser;
11
12use crate::CliError;
13use crate::output::{print_json, print_markdown};
14use crate::setup::{CliContext, CliEngine};
15
16#[derive(Parser, Debug)]
17pub struct Args {
18    /// Writable mem name to reload. Omit to reload every writable
19    /// mem. Mirrors the MCP `memstead_reload` parameter shape and the
20    /// op's semantics: per-mem form is cheap and skips the
21    /// workspace-level settings refresh; workspace-wide form
22    /// (omit `--mem`) reloads every mem and also re-reads the
23    /// workspace policy to pick up edits.
24    #[arg(long)]
25    pub mem: Option<String>,
26}
27
28pub fn run(ctx: &CliContext, args: Args) -> anyhow::Result<()> {
29    let mut engine = match ctx.cli_engine()? {
30        #[cfg(feature = "mem-repo")]
31        CliEngine::MemRepo(engine) => engine,
32        CliEngine::Filesystem(engine) => engine,
33    };
34    let reports = match args.mem.as_deref() {
35        Some(name) => engine
36            .reload_one_mem_report(name)
37            .map(|r| vec![r])
38            .map_err(CliError::from_engine_op)?,
39        None => engine
40            .reload_each_writable_mem_reports()
41            .map_err(CliError::from_engine_op)?,
42    };
43
44    if ctx.json {
45        print_json(&serde_json::json!({ "reports": reports }))?;
46    } else {
47        let mut lines = vec![
48            format!("# Reloaded {} mem(s)", reports.len()),
49            String::new(),
50        ];
51        for r in &reports {
52            lines.push(format!(
53                "- `{}` — {} entities, head {} → {}{}",
54                r.mem,
55                r.entities_loaded,
56                short_sha(&r.head_before),
57                short_sha(&r.head_after),
58                if r.changed_entity_ids.is_empty() {
59                    String::new()
60                } else {
61                    format!(" ({} changed)", r.changed_entity_ids.len())
62                },
63            ));
64        }
65        print_markdown(&lines.join("\n"));
66    }
67    Ok(())
68}
69
70fn short_sha(sha: &str) -> &str {
71    let n = sha.len().min(8);
72    &sha[..n]
73}