Skip to main content

tracevault_core/
hooks.rs

1use serde::{Deserialize, Serialize};
2use thiserror::Error;
3
4#[derive(Debug, Clone, Serialize, Deserialize)]
5pub struct HookEvent {
6    pub session_id: String,
7    pub transcript_path: String,
8    pub cwd: String,
9    #[serde(default)]
10    pub permission_mode: Option<String>,
11    pub hook_event_name: String,
12    #[serde(default)]
13    pub tool_name: Option<String>,
14    #[serde(default)]
15    pub tool_input: Option<serde_json::Value>,
16    #[serde(default)]
17    pub tool_response: Option<serde_json::Value>,
18    #[serde(default)]
19    pub tool_use_id: Option<String>,
20}
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct HookResponse {
24    #[serde(skip_serializing_if = "Option::is_none")]
25    pub r#continue: Option<bool>,
26    #[serde(skip_serializing_if = "Option::is_none")]
27    pub suppress_output: Option<bool>,
28}
29
30impl HookResponse {
31    pub fn allow() -> Self {
32        Self {
33            r#continue: None,
34            suppress_output: Some(true),
35        }
36    }
37}
38
39#[derive(Debug, Error)]
40pub enum HookError {
41    #[error("Failed to parse hook event: {0}")]
42    ParseError(#[from] serde_json::Error),
43    #[error("IO error: {0}")]
44    IoError(#[from] std::io::Error),
45}
46
47pub fn parse_hook_event(json: &str) -> Result<HookEvent, HookError> {
48    Ok(serde_json::from_str(json)?)
49}
50
51impl HookEvent {
52    /// Extract file path from tool_input if this is a Write or Edit event
53    pub fn file_path(&self) -> Option<String> {
54        self.tool_input
55            .as_ref()?
56            .get("file_path")?
57            .as_str()
58            .map(String::from)
59    }
60
61    pub fn is_file_modification(&self) -> bool {
62        matches!(self.tool_name.as_deref(), Some("Write") | Some("Edit"))
63    }
64}