openlatch-client 0.1.5

The open-source security layer for AI agents — client forwarder
use std::path::PathBuf;

use super::super::binding::AgentBinding;

const EVENT_TYPES: [&str; 9] = [
    "PreToolUse",
    "PostToolUse",
    "UserPromptSubmit",
    "Notification",
    "Stop",
    "SubagentStop",
    "PreCompact",
    "SessionStart",
    "SessionEnd",
];

pub struct ClaudeCodeBinding {
    pub claude_dir: PathBuf,
    pub settings_path: PathBuf,
}

impl ClaudeCodeBinding {
    pub fn detect() -> Option<Self> {
        let home = dirs::home_dir()?;
        let claude_dir = home.join(".claude");
        if claude_dir.is_dir() {
            let settings_path = claude_dir.join("settings.json");
            Some(Self {
                claude_dir,
                settings_path,
            })
        } else {
            None
        }
    }
}

impl AgentBinding for ClaudeCodeBinding {
    fn agent_type(&self) -> &str {
        "claude-code"
    }

    fn settings_path(&self) -> PathBuf {
        self.settings_path.clone()
    }

    fn hook_event_types(&self) -> &[&str] {
        &EVENT_TYPES
    }

    fn token_env_var(&self) -> &str {
        "OPENLATCH_TOKEN"
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::sync::Arc;

    #[test]
    fn claude_code_agent_type() {
        let b = ClaudeCodeBinding {
            claude_dir: PathBuf::from("/home/test/.claude"),
            settings_path: PathBuf::from("/home/test/.claude/settings.json"),
        };
        assert_eq!(b.agent_type(), "claude-code");
    }

    #[test]
    fn claude_code_event_types_has_9_entries() {
        let b = ClaudeCodeBinding {
            claude_dir: PathBuf::from("/home/test/.claude"),
            settings_path: PathBuf::from("/home/test/.claude/settings.json"),
        };
        assert_eq!(b.hook_event_types().len(), 9);
        assert!(b.hook_event_types().contains(&"PreToolUse"));
        assert!(b.hook_event_types().contains(&"SessionEnd"));
    }

    #[test]
    fn claude_code_binding_is_arc_dyn_compatible() {
        let b = ClaudeCodeBinding {
            claude_dir: PathBuf::from("/tmp/.claude"),
            settings_path: PathBuf::from("/tmp/.claude/settings.json"),
        };
        let _arc: Arc<dyn AgentBinding> = Arc::new(b);
    }
}