-
Notifications
You must be signed in to change notification settings - Fork 407
feat: Include /context hooks and conversation summary in /context show --expand
#1477
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
f413816
5277b3d
78fa9df
da2a89f
cf0d810
c2d8cb1
dcedc5a
a6f1d56
3a96cec
922b4d4
7939d19
2ed035f
b687a1f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1572,6 +1572,9 @@ impl ChatContext { | |
| if let Some(context_manager) = &mut self.conversation_state.context_manager { | ||
| match subcommand { | ||
| command::ContextSubcommand::Show { expand } => { | ||
| fn map_chat_error(e: ErrReport) -> ChatError { | ||
| ChatError::Custom(e.to_string().into()) | ||
| } | ||
| // Display global context | ||
| execute!( | ||
| self.output, | ||
|
|
@@ -1611,6 +1614,28 @@ impl ChatContext { | |
| } | ||
| } | ||
|
|
||
| if expand { | ||
| queue!( | ||
| self.output, | ||
| style::SetAttribute(Attribute::Bold), | ||
| style::SetForegroundColor(Color::DarkYellow), | ||
| style::Print("\n 🔧 Hooks:\n") | ||
| )?; | ||
| Self::print_hook_section( | ||
| &mut self.output, | ||
| &context_manager.global_config.hooks, | ||
| HookTrigger::ConversationStart, | ||
| ) | ||
| .map_err(map_chat_error)?; | ||
|
|
||
| Self::print_hook_section( | ||
| &mut self.output, | ||
| &context_manager.global_config.hooks, | ||
| HookTrigger::PerPrompt, | ||
| ) | ||
| .map_err(map_chat_error)?; | ||
| } | ||
|
|
||
| // Display profile context | ||
| execute!( | ||
| self.output, | ||
|
|
@@ -1650,6 +1675,28 @@ impl ChatContext { | |
| execute!(self.output, style::Print("\n"))?; | ||
| } | ||
|
|
||
| if expand { | ||
| queue!( | ||
| self.output, | ||
| style::SetAttribute(Attribute::Bold), | ||
| style::SetForegroundColor(Color::DarkYellow), | ||
| style::Print(" 🔧 Hooks:\n") | ||
| )?; | ||
| Self::print_hook_section( | ||
| &mut self.output, | ||
| &context_manager.profile_config.hooks, | ||
| HookTrigger::ConversationStart, | ||
| ) | ||
| .map_err(map_chat_error)?; | ||
| Self::print_hook_section( | ||
| &mut self.output, | ||
| &context_manager.profile_config.hooks, | ||
| HookTrigger::PerPrompt, | ||
| ) | ||
| .map_err(map_chat_error)?; | ||
| execute!(self.output, style::Print("\n"))?; | ||
| } | ||
|
|
||
| if global_context_files.is_empty() && profile_context_files.is_empty() { | ||
| execute!( | ||
| self.output, | ||
|
|
@@ -1727,6 +1774,39 @@ impl ChatContext { | |
| style::Print(format!("\nTotal: ~{} tokens\n\n", total_tokens)), | ||
| )?; | ||
|
|
||
| // Show last cached conversation summary if available, otherwise regenerate it | ||
| if expand { | ||
| if let Some(summary) = | ||
| self.conversation_state.latest_summary().map(|s| s.to_owned()) | ||
| { | ||
| let border = "═".repeat(self.terminal_width().min(80)); | ||
| execute!( | ||
| self.output, | ||
| style::Print("\n"), | ||
| style::SetForegroundColor(Color::Cyan), | ||
| style::Print(&border), | ||
| style::Print("\n"), | ||
| style::SetAttribute(Attribute::Bold), | ||
| style::Print(" CONVERSATION SUMMARY"), | ||
| style::Print("\n"), | ||
| style::Print(&border), | ||
| style::SetAttribute(Attribute::Reset), | ||
| style::Print("\n\n"), | ||
| style::Print(&summary), | ||
| style::Print("\n\n") | ||
| )?; | ||
| } else { | ||
| self.compact_history( | ||
|
||
| Some(tool_uses.clone()), | ||
| pending_tool_index, | ||
| None, | ||
| true, | ||
| false, | ||
| ) | ||
| .await?; | ||
| } | ||
| } | ||
|
|
||
| execute!(self.output, style::Print("\n"))?; | ||
| } | ||
| }, | ||
|
|
@@ -1978,48 +2058,6 @@ impl ChatContext { | |
| }, | ||
| } | ||
| } else { | ||
| fn print_hook_section( | ||
| output: &mut impl Write, | ||
| hooks: &HashMap<String, Hook>, | ||
| trigger: HookTrigger, | ||
| ) -> Result<()> { | ||
| let section = match trigger { | ||
| HookTrigger::ConversationStart => "Conversation Start", | ||
| HookTrigger::PerPrompt => "Per Prompt", | ||
| }; | ||
| let hooks: Vec<(&String, &Hook)> = | ||
| hooks.iter().filter(|(_, h)| h.trigger == trigger).collect(); | ||
|
|
||
| queue!( | ||
| output, | ||
| style::SetForegroundColor(Color::Cyan), | ||
| style::Print(format!(" {section}:\n")), | ||
| style::SetForegroundColor(Color::Reset), | ||
| )?; | ||
|
|
||
| if hooks.is_empty() { | ||
| queue!( | ||
| output, | ||
| style::SetForegroundColor(Color::DarkGrey), | ||
| style::Print(" <none>\n"), | ||
| style::SetForegroundColor(Color::Reset) | ||
| )?; | ||
| } else { | ||
| for (name, hook) in hooks { | ||
| if hook.disabled { | ||
| queue!( | ||
| output, | ||
| style::SetForegroundColor(Color::DarkGrey), | ||
| style::Print(format!(" {} (disabled)\n", name)), | ||
| style::SetForegroundColor(Color::Reset) | ||
| )?; | ||
| } else { | ||
| queue!(output, style::Print(format!(" {}\n", name)),)?; | ||
| } | ||
| } | ||
| } | ||
| Ok(()) | ||
| } | ||
| queue!( | ||
| self.output, | ||
| style::SetAttribute(Attribute::Bold), | ||
|
|
@@ -2028,13 +2066,13 @@ impl ChatContext { | |
| style::SetAttribute(Attribute::Reset), | ||
| )?; | ||
|
|
||
| print_hook_section( | ||
| Self::print_hook_section( | ||
| &mut self.output, | ||
| &context_manager.global_config.hooks, | ||
| HookTrigger::ConversationStart, | ||
| ) | ||
| .map_err(map_chat_error)?; | ||
| print_hook_section( | ||
| Self::print_hook_section( | ||
| &mut self.output, | ||
| &context_manager.global_config.hooks, | ||
| HookTrigger::PerPrompt, | ||
|
|
@@ -2049,13 +2087,13 @@ impl ChatContext { | |
| style::SetAttribute(Attribute::Reset), | ||
| )?; | ||
|
|
||
| print_hook_section( | ||
| Self::print_hook_section( | ||
| &mut self.output, | ||
| &context_manager.profile_config.hooks, | ||
| HookTrigger::ConversationStart, | ||
| ) | ||
| .map_err(map_chat_error)?; | ||
| print_hook_section( | ||
| Self::print_hook_section( | ||
| &mut self.output, | ||
| &context_manager.profile_config.hooks, | ||
| HookTrigger::PerPrompt, | ||
|
|
@@ -2632,6 +2670,45 @@ impl ChatContext { | |
| }) | ||
| } | ||
|
|
||
| // Prints hook configuration grouped by trigger: conversation sesiion start or per user message | ||
| fn print_hook_section(output: &mut impl Write, hooks: &HashMap<String, Hook>, trigger: HookTrigger) -> Result<()> { | ||
|
||
| let section = match trigger { | ||
| HookTrigger::ConversationStart => "On Session Start", | ||
| HookTrigger::PerPrompt => "Per User Message", | ||
| }; | ||
| let hooks: Vec<(&String, &Hook)> = hooks.iter().filter(|(_, h)| h.trigger == trigger).collect(); | ||
|
|
||
| queue!( | ||
| output, | ||
| style::SetForegroundColor(Color::Cyan), | ||
| style::Print(format!(" {section}:\n")), | ||
| style::SetForegroundColor(Color::Reset), | ||
| )?; | ||
|
|
||
| if hooks.is_empty() { | ||
| queue!( | ||
| output, | ||
| style::SetForegroundColor(Color::DarkGrey), | ||
| style::Print(" <none>\n"), | ||
| style::SetForegroundColor(Color::Reset) | ||
| )?; | ||
| } else { | ||
| for (name, hook) in hooks { | ||
| if hook.disabled { | ||
| queue!( | ||
| output, | ||
| style::SetForegroundColor(Color::DarkGrey), | ||
| style::Print(format!(" {} (disabled)\n", name)), | ||
| style::SetForegroundColor(Color::Reset) | ||
| )?; | ||
| } else { | ||
| queue!(output, style::Print(format!(" {}\n", name)),)?; | ||
| } | ||
| } | ||
| } | ||
| Ok(()) | ||
| } | ||
|
|
||
| async fn tool_use_execute(&mut self, mut tool_uses: Vec<QueuedTool>) -> Result<ChatState, ChatError> { | ||
| // Verify tools have permissions. | ||
| for (index, tool) in tool_uses.iter_mut().enumerate() { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is inside the
elseblock ofif global_context_files.is_empty() && profile_context_files.is_empty() {, so it won't ever get executed if both global and profile context matches nothing.Also shouldn't be necessary to
to_ownedhere, we should be able to just print a referenceThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes total sense. Moved the logic out of the else block and ditched the unnecessary
.to_owned(). Learned something new again — thanks for the sharp eyes!