1use crate::types::*;
5
6pub struct HookSystem {
7 hooks: Vec<(HookEvent, String)>,
8}
9
10impl Default for HookSystem {
11 fn default() -> Self {
12 Self::new()
13 }
14}
15
16impl HookSystem {
17 pub fn new() -> Self {
18 Self { hooks: Vec::new() }
19 }
20
21 pub fn register_defaults(&mut self) {
22 self.hooks.push((HookEvent::SessionStart, "session-start".into()));
23 self.hooks.push((HookEvent::PreToolUse, "pre-bash-guard".into()));
24 self.hooks.push((HookEvent::PostToolUse, "post-edit-lint".into()));
25 self.hooks.push((HookEvent::PostToolFailure, "post-fail-diagnose".into()));
26 self.hooks.push((HookEvent::SessionStop, "auto-handoff".into()));
27 }
28
29 pub fn fire(&self, event: HookEvent, tool_name: Option<&str>, input: Option<&serde_json::Value>) -> HookResult {
30 if event == HookEvent::PreToolUse && tool_name == Some("bash") {
32 if let Some(input) = input {
33 let command = input.get("command").and_then(|v| v.as_str()).unwrap_or("");
34 let dangerous = [
35 "rm -rf /", "rm -rf ~", "> /dev/sd", "mkfs.", "chmod 000",
36 ];
37 for pattern in dangerous {
38 if command.contains(pattern) {
39 return HookResult {
40 allow: false,
41 message: Some(format!("Dangerous command blocked: {command}")),
42 };
43 }
44 }
45 }
46 }
47
48 HookResult { allow: true, message: None }
49 }
50
51}