wip: telemetry progress

This commit is contained in:
Yeachan-Heo
2026-04-01 04:30:29 +00:00
parent ac6c5d00a8
commit 5170718306
12 changed files with 1060 additions and 70 deletions

View File

@@ -64,7 +64,7 @@ impl HookRunner {
#[must_use]
pub fn run_pre_tool_use(&self, tool_name: &str, tool_input: &str) -> HookRunResult {
self.run_commands(
Self::run_commands(
HookEvent::PreToolUse,
self.config.pre_tool_use(),
tool_name,
@@ -82,7 +82,7 @@ impl HookRunner {
tool_output: &str,
is_error: bool,
) -> HookRunResult {
self.run_commands(
Self::run_commands(
HookEvent::PostToolUse,
self.config.post_tool_use(),
tool_name,
@@ -93,7 +93,6 @@ impl HookRunner {
}
fn run_commands(
&self,
event: HookEvent,
commands: &[String],
tool_name: &str,
@@ -114,19 +113,19 @@ impl HookRunner {
"tool_result_is_error": is_error,
})
.to_string();
let invocation = HookInvocation {
event,
tool_name,
tool_input,
tool_output,
is_error,
payload: &payload,
};
let mut messages = Vec::new();
for command in commands {
match self.run_command(
command,
event,
tool_name,
tool_input,
tool_output,
is_error,
&payload,
) {
match Self::run_command(command, &invocation) {
HookCommandOutcome::Allow { message } => {
if let Some(message) = message {
messages.push(message);
@@ -149,29 +148,23 @@ impl HookRunner {
HookRunResult::allow(messages)
}
fn run_command(
&self,
command: &str,
event: HookEvent,
tool_name: &str,
tool_input: &str,
tool_output: Option<&str>,
is_error: bool,
payload: &str,
) -> HookCommandOutcome {
fn run_command(command: &str, invocation: &HookInvocation<'_>) -> HookCommandOutcome {
let mut child = shell_command(command);
child.stdin(std::process::Stdio::piped());
child.stdout(std::process::Stdio::piped());
child.stderr(std::process::Stdio::piped());
child.env("HOOK_EVENT", event.as_str());
child.env("HOOK_TOOL_NAME", tool_name);
child.env("HOOK_TOOL_INPUT", tool_input);
child.env("HOOK_TOOL_IS_ERROR", if is_error { "1" } else { "0" });
if let Some(tool_output) = tool_output {
child.env("HOOK_EVENT", invocation.event.as_str());
child.env("HOOK_TOOL_NAME", invocation.tool_name);
child.env("HOOK_TOOL_INPUT", invocation.tool_input);
child.env(
"HOOK_TOOL_IS_ERROR",
if invocation.is_error { "1" } else { "0" },
);
if let Some(tool_output) = invocation.tool_output {
child.env("HOOK_TOOL_OUTPUT", tool_output);
}
match child.output_with_stdin(payload.as_bytes()) {
match child.output_with_stdin(invocation.payload.as_bytes()) {
Ok(output) => {
let stdout = String::from_utf8_lossy(&output.stdout).trim().to_string();
let stderr = String::from_utf8_lossy(&output.stderr).trim().to_string();
@@ -189,8 +182,9 @@ impl HookRunner {
},
None => HookCommandOutcome::Warn {
message: format!(
"{} hook `{command}` terminated by signal while handling `{tool_name}`",
event.as_str()
"{} hook `{command}` terminated by signal while handling `{}`",
invocation.event.as_str(),
invocation.tool_name
),
},
}
@@ -198,13 +192,23 @@ impl HookRunner {
Err(error) => HookCommandOutcome::Warn {
message: format!(
"{} hook `{command}` failed to start for `{tool_name}`: {error}",
event.as_str()
invocation.event.as_str(),
tool_name = invocation.tool_name
),
},
}
}
}
struct HookInvocation<'a> {
event: HookEvent,
tool_name: &'a str,
tool_input: &'a str,
tool_output: Option<&'a str>,
is_error: bool,
payload: &'a str,
}
enum HookCommandOutcome {
Allow { message: Option<String> },
Deny { message: Option<String> },