use serde_json::{Value, json};
use crate::common::Sandbox;
mod claude_code;
mod cline;
mod codex;
mod continue_cli;
mod copilot_cli;
mod cursor;
mod gemini_cli;
mod goose;
mod hermes;
mod openclaw;
mod opencode;
mod pi;
pub type Payload = fn(&Sandbox) -> String;
pub struct Edit {
pub change: fn(&str) -> String,
pub call: Payload,
}
pub struct Mcp {
pub file: &'static str,
pub servers: fn(&str) -> String,
pub command: &'static str,
pub server: &'static str,
pub tool: &'static str,
pub other_tool: Option<&'static str>,
pub pinned: &'static str,
pub call: fn(&Sandbox, &str) -> String,
}
pub struct HooksOff {
pub file: &'static str,
pub change: fn(&str) -> String,
pub before_init: bool,
pub names: &'static str,
}
pub struct Row {
pub id: &'static str,
pub hook: &'static str,
pub env: Option<&'static str>,
pub verdict_at: &'static str,
pub read_verdict: Option<fn(&Value) -> &'static str>,
pub reason_at: &'static [&'static str],
pub persons_file: Option<&'static str>,
pub owns_file: bool,
pub extra_dir: Option<&'static str>,
pub edited: Option<Edit>,
pub governed: &'static str,
pub unknown_tool: fn() -> Value,
pub unknown_event: fn() -> Value,
pub checks_unknown_tools: bool,
pub secret_read: Option<Payload>,
pub benign: Option<Payload>,
pub ungoverned: &'static [Payload],
pub malformed: Option<(Payload, Option<&'static str>)>,
pub review: Option<Payload>,
pub mcp: Option<Mcp>,
pub hooks_off: Option<HooksOff>,
}
const BASE: Row = Row {
id: "",
hook: "",
env: None,
verdict_at: "",
read_verdict: None,
reason_at: &[],
persons_file: None,
owns_file: false,
extra_dir: None,
edited: None,
governed: "",
unknown_tool: || Value::Null,
unknown_event: || Value::Null,
checks_unknown_tools: false,
secret_read: None,
benign: None,
ungoverned: &[],
malformed: None,
review: None,
mcp: None,
hooks_off: None,
};
const PRE_TOOL_USE: Row = Row {
verdict_at: "/hookSpecificOutput/permissionDecision",
reason_at: &["/hookSpecificOutput/permissionDecisionReason"],
..BASE
};
const DECISION: Row = Row {
verdict_at: "/decision",
reason_at: &["/reason"],
..BASE
};
pub fn rows() -> Vec<Row> {
vec![
claude_code::row(),
codex::row(),
cursor::row(),
continue_cli::row(),
openclaw::row(),
copilot_cli::row(),
pi::row(),
gemini_cli::row(),
opencode::row(),
hermes::row(),
cline::row(),
goose::row(),
]
}
fn pre_tool_use(tool: &str) -> Value {
json!({"session_id": "s", "cwd": "/p", "hook_event_name": "PreToolUse",
"tool_name": tool, "tool_input": {"file_path": "~/.ssh/id_rsa"}})
}
fn post_tool_use() -> Value {
json!({"session_id": "s", "hook_event_name": "PostToolUse",
"tool_name": "Bash", "tool_input": {"command": "ls"}})
}