Skip to content

feat(ui): add visual indicators for hook execution#15408

Merged
abhipatel12 merged 3 commits intomainfrom
hooks
Jan 6, 2026
Merged

feat(ui): add visual indicators for hook execution#15408
abhipatel12 merged 3 commits intomainfrom
hooks

Conversation

@abhipatel12
Copy link
Copy Markdown
Collaborator

@abhipatel12 abhipatel12 commented Dec 21, 2025

Summary

Adds a transient visual indicator (🪝 Executing Hook(s)) to the TUI context line to provide real-time feedback during hook execution.

Details

  • Core Instrumentation: Added HookStart and HookEnd events to CoreEventEmitter and instrumented HookRunner/HookEventHandler to emit them during the hook lifecycle.
  • UI State Management: Refactored AppContainer to track activeHooks using useReducer, ensuring robust handling of rapid-fire events.
  • Bug Fix: Fixed a logic bug where multiple identical hooks running in parallel would all be removed when the first one finished. The implementation now uses index-based removal to precisely track each instance.
  • TUI Indicator: Implemented the 🪝 Executing Hook(s) display in ContextSummaryDisplay with support for pluralization and terminal-width-aware truncation (e.g., ... (+N more)).
  • Configuration: Added a user-configurable hooks.notifications setting (default: true) in the 'Advanced' category to toggle visibility.
  • Documentation: Regenerated configuration.md and settings.schema.json to include the new setting.

Demo:

Screen.Recording.2025-12-21.at.6.15.03.PM.mov

Related Issues

Resolves #15262

How to Validate

  1. Configure one or more hooks in your settings.json (e.g., a SessionStart hook with a sleep).
  2. Start the CLI and observe the 🪝 Executing Hook: <name> indicator appearing above the input bar during startup.
  3. Send a prompt that triggers multiple hooks (e.g., BeforeAgent) and verify the pluralization and truncation logic in narrow terminal windows.
  4. Go to /settings and toggle Hook Notifications to verify the UI correctly respects the setting.
  5. Run unit tests: npx vitest packages/cli/src/ui/AppContainer.test.tsx packages/cli/src/ui/components/ContextSummaryDisplay.test.tsx

Pre-Merge Checklist

  • Updated relevant documentation and README (if needed)
  • Added/updated tests (if needed)
  • Noted breaking changes (if any)
  • Validated on required platforms/methods:
    • MacOS
      • npm run
@abhipatel12 abhipatel12 requested a review from a team as a code owner December 21, 2025 23:16
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @abhipatel12, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request enhances the user experience by providing immediate visual feedback in the Terminal User Interface (TUI) when background hooks are actively executing. It integrates new event emissions from the core hook logic into the UI's state management, allowing for a dynamic display that informs users about ongoing processes. This feature is also made configurable, giving users control over its visibility.

Highlights

  • Visual Hook Execution Indicator: Introduced a transient visual indicator (🪝 Executing Hook(s)) in the TUI context line to provide real-time feedback when hooks are running.
  • Core Event Instrumentation: Added HookStart and HookEnd events to CoreEventEmitter and instrumented HookRunner/HookEventHandler to emit these events during the hook lifecycle, providing granular control and visibility into hook execution.
  • UI State Management Refactoring: Refactored AppContainer to track activeHooks using useReducer, enabling robust handling of rapid-fire hook events and ensuring accurate state representation.
  • Bug Fix for Identical Hooks: Fixed a logic bug where multiple identical hooks running in parallel would all be removed when the first one finished. The implementation now uses index-based removal to precisely track and remove each instance.
  • User Configuration: Added a new user-configurable hooks.notifications setting (default: true) in the 'Advanced' category, allowing users to toggle the visibility of hook execution indicators.
  • Documentation Updates: Regenerated configuration.md and settings.schema.json to include the new hooks.notifications setting, ensuring up-to-date documentation for users.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Dec 21, 2025

Size Change: +5.64 kB (+0.03%)

Total Size: 22.2 MB

Filename Size Change
./bundle/gemini.js 22.2 MB +5.64 kB (+0.03%)
ℹ️ View Unchanged
Filename Size
./bundle/sandbox-macos-permissive-closed.sb 1.03 kB
./bundle/sandbox-macos-permissive-open.sb 890 B
./bundle/sandbox-macos-permissive-proxied.sb 1.31 kB
./bundle/sandbox-macos-restrictive-closed.sb 3.29 kB
./bundle/sandbox-macos-restrictive-open.sb 3.36 kB
./bundle/sandbox-macos-restrictive-proxied.sb 3.56 kB

compressed-size-action

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a transient visual indicator for hook execution, which is a great enhancement for user feedback. The implementation is solid, using useReducer for robust state management of active hooks and correctly handling concurrent identical hooks. The new feature is also made configurable with clear documentation and schema updates. The accompanying tests are thorough and cover the new logic well. Overall, this is a high-quality contribution.

@NTaylorMullen
Copy link
Copy Markdown
Collaborator

Consider my approval for the non-UX/UI bits. Should still get sign-off from someone else on the UX fronts

@jacob314
Copy link
Copy Markdown
Contributor

I think we should align this further with the existing infrastructure for showing messages like "Press ctrl-C again to exit"
That means aligning with the same timeouts for how long the messages are displayed for and having the content overwrite the "Using X GEMINI.md files | Y MCP servers"
rather than having it be written to the right of the line.

},
};
const { lastFrame, unmount } = renderWithWidth(120, props);
expect(lastFrame()).toMatchSnapshot();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the previous test was more robust as it would fail if the snapshot was wrong. with this change someone could commit a bad snapshot.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

const startTimes = hookStartTimes.current;

const handleHookStart = (payload: HookStartPayload) => {
const key = `${payload.hookName}:${payload.eventName}`;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this behavior seems a little suspcious if there are multiple nested hooks with the same hookName and eventName. Don't we at least need a count of how many hooks are active with the same key so we don't close the outer hook just because the innder one has ended.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should work as intended. Since we're splicing the index for the first instance, even if we had a hook with the same name/event, we would only be removing one at a time.

I added a test based on another comment that should also verify this.

Lmk if that looks okay!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as a followup we need to cleanup uiState.. All these items are just for displaying the status messages so should be part of separate provider.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created an issue to track here: #15590

Copy link
Copy Markdown
Contributor

@jacob314 jacob314 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Approved once these comments are addressed

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move this all to a separate StatusDisplay component. This list is becoming a tragedy of the commons and isn't related to Composer.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Copy Markdown
Contributor

@jacob314 jacob314 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple other pieces of feedback from Gemini CLI /review-frontend that I generally agree with.

  1. useState vs. useReducer: While the PR description claims to use useReducer for robust handling of rapid-fire
    events, the implementation in useHookDisplayState.ts actually uses useState with functional updates. Given the
    asynchronous nature of hook start/end events and the setTimeout logic, a reducer might be more idiomatic and robust
    here.
  2. Non-Null Assertion: There is a non-null assertion (!) in useHookDisplayState.ts (line 40:
    startTimes.get(key)!.push(now);). Although it follows a has check, we typically prefer to avoid these.
Copy link
Copy Markdown
Collaborator Author

@abhipatel12 abhipatel12 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review! Let me know what you think!

},
};
const { lastFrame, unmount } = renderWithWidth(120, props);
expect(lastFrame()).toMatchSnapshot();
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created an issue to track here: #15590

const startTimes = hookStartTimes.current;

const handleHookStart = (payload: HookStartPayload) => {
const key = `${payload.hookName}:${payload.eventName}`;
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should work as intended. Since we're splicing the index for the first instance, even if we had a hook with the same name/event, we would only be removing one at a time.

I added a test based on another comment that should also verify this.

Lmk if that looks okay!

Copy link
Copy Markdown
Contributor

@jacob314 jacob314 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

Align hook execution display with notification infrastructure:
- Implement `useHookDisplayState` with 1s minimum display duration
- Replace context summary with hook status when hooks are active
- Remove hook emoji per feedback
- Add snapshots for UI components
@abhipatel12
Copy link
Copy Markdown
Collaborator Author

Thanks for the reviews!

@abhipatel12 abhipatel12 added this pull request to the merge queue Jan 6, 2026
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Jan 6, 2026
@abhipatel12 abhipatel12 added this pull request to the merge queue Jan 6, 2026
Merged via the queue into main with commit 61dbab0 Jan 6, 2026
20 checks passed
@abhipatel12 abhipatel12 deleted the hooks branch January 6, 2026 21:31
thacio added a commit to thacio/auditaria that referenced this pull request Jan 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

3 participants