diff --git a/rust/crates/runtime/src/conversation.rs b/rust/crates/runtime/src/conversation.rs index 358e1cc..07ac8bc 100644 --- a/rust/crates/runtime/src/conversation.rs +++ b/rust/crates/runtime/src/conversation.rs @@ -5,7 +5,7 @@ use crate::compact::{ compact_session, estimate_session_tokens, CompactionConfig, CompactionResult, }; use crate::config::RuntimeFeatureConfig; -use crate::hooks::{HookAbortSignal, HookRunResult, HookRunner}; +use crate::hooks::{HookAbortSignal, HookProgressReporter, HookRunResult, HookRunner}; use crate::permissions::{ PermissionContext, PermissionOutcome, PermissionPolicy, PermissionPrompter, }; @@ -100,6 +100,7 @@ pub struct ConversationRuntime { usage_tracker: UsageTracker, hook_runner: HookRunner, hook_abort_signal: HookAbortSignal, + hook_progress_reporter: Option>, } impl ConversationRuntime @@ -171,6 +172,77 @@ where self } + fn run_pre_tool_use_hook(&mut self, tool_name: &str, input: &str) -> HookRunResult { + if let Some(reporter) = self.hook_progress_reporter.as_mut() { + self.hook_runner.run_pre_tool_use_with_context( + tool_name, + input, + Some(&self.hook_abort_signal), + Some(reporter.as_mut()), + ) + } else { + self.hook_runner.run_pre_tool_use_with_context( + tool_name, + input, + Some(&self.hook_abort_signal), + None, + ) + } + } + + fn run_post_tool_use_hook( + &mut self, + tool_name: &str, + input: &str, + output: &str, + is_error: bool, + ) -> HookRunResult { + if let Some(reporter) = self.hook_progress_reporter.as_mut() { + self.hook_runner.run_post_tool_use_with_context( + tool_name, + input, + output, + is_error, + Some(&self.hook_abort_signal), + Some(reporter.as_mut()), + ) + } else { + self.hook_runner.run_post_tool_use_with_context( + tool_name, + input, + output, + is_error, + Some(&self.hook_abort_signal), + None, + ) + } + } + + fn run_post_tool_use_failure_hook( + &mut self, + tool_name: &str, + input: &str, + output: &str, + ) -> HookRunResult { + if let Some(reporter) = self.hook_progress_reporter.as_mut() { + self.hook_runner.run_post_tool_use_failure_with_context( + tool_name, + input, + output, + Some(&self.hook_abort_signal), + Some(reporter.as_mut()), + ) + } else { + self.hook_runner.run_post_tool_use_failure_with_context( + tool_name, + input, + output, + Some(&self.hook_abort_signal), + None, + ) + } + } + #[allow(clippy::too_many_lines)] pub fn run_turn( &mut self, @@ -314,77 +386,6 @@ where }) } - fn run_pre_tool_use_hook(&mut self, tool_name: &str, input: &str) -> HookRunResult { - if let Some(reporter) = self.hook_progress_reporter.as_mut() { - self.hook_runner.run_pre_tool_use_with_context( - tool_name, - input, - Some(&self.hook_abort_signal), - Some(reporter.as_mut()), - ) - } else { - self.hook_runner.run_pre_tool_use_with_context( - tool_name, - input, - Some(&self.hook_abort_signal), - None, - ) - } - } - - fn run_post_tool_use_hook( - &mut self, - tool_name: &str, - input: &str, - output: &str, - is_error: bool, - ) -> HookRunResult { - if let Some(reporter) = self.hook_progress_reporter.as_mut() { - self.hook_runner.run_post_tool_use_with_context( - tool_name, - input, - output, - is_error, - Some(&self.hook_abort_signal), - Some(reporter.as_mut()), - ) - } else { - self.hook_runner.run_post_tool_use_with_context( - tool_name, - input, - output, - is_error, - Some(&self.hook_abort_signal), - None, - ) - } - } - - fn run_post_tool_use_failure_hook( - &mut self, - tool_name: &str, - input: &str, - output: &str, - ) -> HookRunResult { - if let Some(reporter) = self.hook_progress_reporter.as_mut() { - self.hook_runner.run_post_tool_use_failure_with_context( - tool_name, - input, - output, - Some(&self.hook_abort_signal), - Some(reporter.as_mut()), - ) - } else { - self.hook_runner.run_post_tool_use_failure_with_context( - tool_name, - input, - output, - Some(&self.hook_abort_signal), - None, - ) - } - } - #[must_use] pub fn compact(&self, config: CompactionConfig) -> CompactionResult { compact_session(&self.session, config) diff --git a/rust/crates/rusty-claude-cli/src/main.rs b/rust/crates/rusty-claude-cli/src/main.rs index 935bade..7baed9b 100644 --- a/rust/crates/rusty-claude-cli/src/main.rs +++ b/rust/crates/rusty-claude-cli/src/main.rs @@ -1923,7 +1923,7 @@ fn build_runtime( ) -> Result, Box> { let feature_config = build_runtime_feature_config()?; - let runtime = ConversationRuntime::new_with_features( + let mut runtime = ConversationRuntime::new_with_features( session, AnthropicRuntimeClient::new(model, enable_tools, emit_output, allowed_tools.clone())?, CliToolExecutor::new(allowed_tools, emit_output), @@ -1931,9 +1931,45 @@ fn build_runtime( system_prompt, feature_config, ); + if emit_output { + runtime = runtime.with_hook_progress_reporter(Box::new(CliHookProgressReporter)); + } Ok(runtime) } +struct CliHookProgressReporter; + +impl runtime::HookProgressReporter for CliHookProgressReporter { + fn on_event(&mut self, event: &runtime::HookProgressEvent) { + match event { + runtime::HookProgressEvent::Started { + event, + tool_name, + command, + } => eprintln!( + "[hook {event_name}] {tool_name}: {command}", + event_name = event.as_str() + ), + runtime::HookProgressEvent::Completed { + event, + tool_name, + command, + } => eprintln!( + "[hook done {event_name}] {tool_name}: {command}", + event_name = event.as_str() + ), + runtime::HookProgressEvent::Cancelled { + event, + tool_name, + command, + } => eprintln!( + "[hook cancelled {event_name}] {tool_name}: {command}", + event_name = event.as_str() + ), + } + } +} + struct CliPermissionPrompter { current_mode: PermissionMode, }