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