use serde_json::{Value, json};
use crate::{
Result,
common::protocol,
hook::{Answer, Event, HookInput, Output, ToolCall, ToolKind},
};
pub(crate) fn parse(
harness: &str,
event: Event,
text: &str,
tool_kind: fn(&str) -> ToolKind,
) -> serde_json::Result<HookInput> {
let raw = protocol::raw_object(text)?;
let mut input = HookInput::new(harness, event, raw.clone());
let text = |k: &str| protocol::text(&raw, k);
input.session_id = text("session_id");
input.transcript_path = text("transcript_path");
input.cwd = text("cwd");
input.prompt = text("prompt");
input.source = text("source");
input.tool = text("tool_name").map(|name| {
let kind = tool_kind(&name);
ToolCall::new(
name,
kind,
raw.get("tool_input").cloned().unwrap_or(Value::Null),
)
});
input.tool_output = raw.get("tool_output").cloned();
input.continuing = raw["continuing"].as_bool().unwrap_or(false);
Ok(input)
}
pub(crate) fn answer(harness: &str, event: Event, answer: &Answer) -> Result<Output> {
Ok(match (answer, event) {
(Answer::Allow { stderr }, _) => {
Output::json(json!({ "answer": "allow" })).stderr(stderr.clone())
}
(Answer::Deny { reason }, Event::PreTool) => {
Output::json(json!({ "answer": "deny", "reason": reason }))
}
(Answer::Continue { reason }, Event::Stop) => {
Output::json(json!({ "answer": "continue", "reason": reason }))
}
(Answer::Context { text }, Event::SessionStart | Event::PromptSubmit | Event::PostTool) => {
Output::json(json!({ "answer": "context", "text": text }))
}
_ => return Err(protocol::unsupported(harness, event, answer)),
})
}
pub(crate) fn comment_safe(name: &str) -> String {
name.chars()
.map(|c| if c.is_control() { ' ' } else { c })
.collect()
}
pub(crate) fn fill(
template: &str,
harness: &str,
tool: &str,
integration: &crate::integration::Integration,
) -> String {
let hooks: Vec<Value> = integration
.hooks()
.iter()
.map(|h| {
let mut spec = json!({ "event": h.event().as_str(), "command": h.command(harness) });
if let Some(t) = h.timeout_value() {
spec["timeout"] = json!(u64::try_from(t.as_millis()).unwrap_or(u64::MAX));
}
spec
})
.collect();
template
.replace("__TOOL_COMMENT__", &comment_safe(tool))
.replace("__TOOL__", &Value::String(tool.to_string()).to_string())
.replace("__HOOKS__", &Value::Array(hooks).to_string())
}