#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HookResponse {
AllowJson,
EmptyJson,
}
impl HookResponse {
#[inline]
pub fn as_bytes(&self) -> &'static [u8] {
match self {
HookResponse::AllowJson => b"{\"decision\":\"allow\"}",
HookResponse::EmptyJson => b"{}",
}
}
#[inline]
pub fn as_str(&self) -> &'static str {
match self {
HookResponse::AllowJson => "{\"decision\":\"allow\"}",
HookResponse::EmptyJson => "{}",
}
}
}
pub trait PlatformDescriptor: Send + Sync + 'static {
fn id(&self) -> &'static str;
fn display_name(&self) -> &'static str;
fn aliases(&self) -> &'static [&'static str];
fn wire_client_id(&self) -> u16;
fn pre_tool_response(&self) -> HookResponse {
HookResponse::AllowJson
}
fn matches_name(&self, name: &str) -> bool {
let lower = name.trim().to_ascii_lowercase();
self.id() == lower || self.aliases().iter().any(|&a| a == lower)
}
}
pub struct AntigravityDescriptor;
impl PlatformDescriptor for AntigravityDescriptor {
fn id(&self) -> &'static str {
"antigravity"
}
fn display_name(&self) -> &'static str {
"Google Antigravity"
}
fn aliases(&self) -> &'static [&'static str] {
&["agy", "gemini"]
}
fn wire_client_id(&self) -> u16 {
1
}
fn pre_tool_response(&self) -> HookResponse {
HookResponse::AllowJson
}
}
pub struct ClaudeCodeDescriptor;
impl PlatformDescriptor for ClaudeCodeDescriptor {
fn id(&self) -> &'static str {
"claude"
}
fn display_name(&self) -> &'static str {
"Claude Code"
}
fn aliases(&self) -> &'static [&'static str] {
&["claude-code", "claudecode"]
}
fn wire_client_id(&self) -> u16 {
2
}
fn pre_tool_response(&self) -> HookResponse {
HookResponse::AllowJson
}
}
pub struct CodexDescriptor;
impl PlatformDescriptor for CodexDescriptor {
fn id(&self) -> &'static str {
"codex"
}
fn display_name(&self) -> &'static str {
"OpenAI Codex"
}
fn aliases(&self) -> &'static [&'static str] {
&["openai", "codex-cli"]
}
fn wire_client_id(&self) -> u16 {
3
}
fn pre_tool_response(&self) -> HookResponse {
HookResponse::EmptyJson
}
}
pub struct GrokDescriptor;
impl PlatformDescriptor for GrokDescriptor {
fn id(&self) -> &'static str {
"grok"
}
fn display_name(&self) -> &'static str {
"xAI Grok"
}
fn aliases(&self) -> &'static [&'static str] {
&["xai", "grok-cli"]
}
fn wire_client_id(&self) -> u16 {
4
}
fn pre_tool_response(&self) -> HookResponse {
HookResponse::AllowJson
}
}
pub struct PiDescriptor;
impl PlatformDescriptor for PiDescriptor {
fn id(&self) -> &'static str {
"pi"
}
fn display_name(&self) -> &'static str {
"Pi Agent"
}
fn aliases(&self) -> &'static [&'static str] {
&["pi-cli"]
}
fn wire_client_id(&self) -> u16 {
5
}
fn pre_tool_response(&self) -> HookResponse {
HookResponse::AllowJson
}
}
pub static BUILTIN_PLATFORMS: &[&dyn PlatformDescriptor] = &[
&AntigravityDescriptor,
&ClaudeCodeDescriptor,
&CodexDescriptor,
&GrokDescriptor,
&PiDescriptor,
];
pub fn find_platform_by_name(name: &str) -> Option<&'static (dyn PlatformDescriptor + 'static)> {
BUILTIN_PLATFORMS
.iter()
.copied()
.find(|p| p.matches_name(name))
}
pub fn find_platform_by_wire_id(id: u16) -> Option<&'static (dyn PlatformDescriptor + 'static)> {
BUILTIN_PLATFORMS
.iter()
.copied()
.find(|p| p.wire_client_id() == id)
}