Skip to main content

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}