remem/install/host.rs
1use anyhow::{bail, Result};
2use clap::ValueEnum;
3use std::path::PathBuf;
4
5/// Which host(s) to (un)install into.
6#[derive(Debug, Clone, Copy, ValueEnum, PartialEq, Eq)]
7pub enum InstallTarget {
8 /// Install to every host whose config directory already exists.
9 Auto,
10 /// Install only to Claude Code (~/.claude.json + ~/.claude/settings.json).
11 Claude,
12 /// Install only to Codex (~/.codex/config.toml).
13 Codex,
14 /// Install to every known host, creating config files if missing.
15 All,
16}
17
18/// Outcome of a hook installation attempt.
19pub enum HookSupport {
20 /// Hooks were installed.
21 Installed,
22 /// Host does not support hooks; reason for user-visible log.
23 /// Retained for future hosts (e.g. Cursor) that may not expose a hook
24 /// system with the same shape as Claude/Codex.
25 #[allow(dead_code)]
26 Skipped(&'static str),
27}
28
29pub struct HookRepairReport {
30 pub path: PathBuf,
31 pub registered: usize,
32 pub expected: usize,
33 pub mcp_warning: Option<String>,
34 pub scope_warning: Option<String>,
35}
36
37/// A host capable of running the remem MCP server.
38///
39/// Each host owns its own config file format and mutation logic. The runtime
40/// layer only orchestrates which hosts to touch.
41pub trait InstallHost {
42 /// Short identifier printed in logs (e.g. "claude", "codex").
43 fn name(&self) -> &'static str;
44
45 /// Path to the primary config file this host manages.
46 fn config_path(&self) -> PathBuf;
47
48 /// True when the host appears to be installed on this machine.
49 /// Used by `InstallTarget::Auto` to decide whether to touch it.
50 fn is_available(&self) -> bool;
51
52 /// Add / update the remem MCP server entry. Idempotent.
53 fn install_mcp(&self, bin: &str) -> Result<()>;
54
55 /// Remove any remem MCP server entry. Idempotent.
56 fn uninstall_mcp(&self, bin: &str) -> Result<()>;
57
58 /// Add / update remem hooks. Hosts without hook support return `Skipped`.
59 fn install_hooks(&self, bin: &str) -> Result<HookSupport>;
60
61 /// Repair host hooks without touching MCP, runtime store, or tokens.
62 fn repair_hooks(&self, _bin: &str) -> Result<HookRepairReport> {
63 bail!("{} hook repair is not supported", self.name())
64 }
65
66 /// Remove remem hooks. No-op if the host doesn't support hooks.
67 fn uninstall_hooks(&self, bin: &str) -> Result<()>;
68
69 /// Describe the writes a real install would do, without touching disk.
70 /// Returned lines are printed verbatim in dry-run mode.
71 fn dry_run_plan(&self, bin: &str) -> Vec<String>;
72}