Skip to content

fix(cli): use tmux-safe thinking indicator#22067

Open
apfine wants to merge 6 commits intogoogle-gemini:mainfrom
apfine:linux-cli
Open

fix(cli): use tmux-safe thinking indicator#22067
apfine wants to merge 6 commits intogoogle-gemini:mainfrom
apfine:linux-cli

Conversation

@apfine
Copy link
Copy Markdown
Contributor

@apfine apfine commented Mar 11, 2026

Description

This change fixes terminal redraw interference when Gemini CLI is running inside a tmux pane.

While the CLI is thinking, the loading spinner redraws frequently. In tmux split-pane setups, those terminal control sequence updates can interfere with mouse text selection in neighboring panes, causing highlighted text to disappear while Gemini is still responding.

The goal of this change is to preserve visible progress feedback while reducing the redraw behavior that disrupts adjacent tmux panes.

Changes Made

  • Added tmux environment detection in the Gemini thinking spinner path
  • Kept the existing animated gradient spinner behavior unchanged outside tmux
  • Replaced the high-frequency animated spinner with a low-frequency dots indicator in tmux:
    • .
    • ..
    • ...
  • Reduced update frequency in tmux to 750ms
  • Used a fixed-width container for the tmux indicator to avoid layout shifts
  • Added focused regression tests for:
    • normal non-tmux spinner behavior
    • tmux-safe indicator behavior
    • screen-reader fallback behavior

Testing

npm test --workspace @google/gemini-cli -- src/ui/components/GeminiSpinner.test.tsx
npm test --workspace @google/gemini-cli -- src/ui/components/GeminiSpinner.test.tsx src/ui/components/GeminiRespondingSpinner.test.tsx src/ui/components/LoadingIndicator.test.tsx src/ui/components/CliSpinner.test.tsx
npx eslint packages/cli/src/ui/components/GeminiSpinner.tsx packages/cli/src/ui/components/GeminiSpinner.test.tsx

Results:

GeminiSpinner.test.tsx: passed (3/3)

spinner/loading component suite: passed (28/28)

targeted ESLint on touched files: passed

Related Issues
Fixes #19675

@apfine apfine requested a review from a team as a code owner March 11, 2026 20:02
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, 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 addresses terminal redraw interference experienced when the Gemini CLI's loading spinner runs inside a tmux pane by introducing a tmux-safe, low-frequency indicator. Additionally, it enhances the AntigravityInstaller's reliability by implementing a more robust command discovery process that sanitizes environment variables and includes fallback options, ensuring the CLI can locate the necessary tools more effectively.

Highlights

  • Tmux-Safe Thinking Indicator: Implemented tmux environment detection for the Gemini CLI thinking spinner.
  • Reduced Redraw Frequency: Replaced the high-frequency animated spinner with a low-frequency dots indicator in tmux environments, updating every 750ms.
  • Fixed-Width Container: Used a fixed-width container for the tmux indicator to prevent layout shifts and interference with adjacent panes.
  • Existing Spinner Behavior Preserved: Maintained the original animated gradient spinner behavior for non-tmux environments.
  • Enhanced Test Coverage: Added comprehensive regression tests for normal, tmux-safe, and screen-reader fallback spinner behaviors.
  • Robust Antigravity CLI Discovery: Improved the AntigravityInstaller to safely handle ANTIGRAVITY_CLI_ALIAS environment variables and implement robust fallback mechanisms for finding the correct CLI command.
Changelog
  • packages/cli/src/ui/components/GeminiSpinner.tsx
    • The spinner component now detects if it's running within a tmux environment.
    • If in tmux, it switches from the high-frequency animated gradient spinner to a simpler, low-frequency (750ms update) dot-based indicator (., .., ...) displayed within a fixed-width box to prevent redraw issues.
  • packages/core/src/ide/ide-installer.test.ts
    • New tests were added to verify the AntigravityInstaller's behavior when the ANTIGRAVITY_CLI_ALIAS environment variable contains unsafe characters, ensuring it falls back to safe commands.
    • Additional tests cover the fallback logic for finding agy or antigravity (or their .cmd equivalents on Windows) when the primary alias is not found or unavailable.
    • The failure message for a command not found was updated to be more informative and include supported commands.
  • packages/core/src/ide/ide-installer.ts
    • The install method was updated to first sanitize the ANTIGRAVITY_CLI_ALIAS environment variable to prevent command injection.
    • It then attempts to find the command using a prioritized list: the sanitized alias (if valid), followed by platform-specific fallback commands (agy, antigravity or agy.cmd, antigravity.cmd).
    • The error message for a missing command now explicitly lists the supported fallback commands.
Activity
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.

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 two valuable but unrelated changes. The first is a user experience improvement for tmux users, which correctly addresses the issue of terminal redraw interference by using a low-frequency indicator. The implementation is clean and effective. The second change is a critical security fix and UX enhancement for the Antigravity IDE installer. It correctly sanitizes the ANTIGRAVITY_CLI_ALIAS environment variable to prevent command injection vulnerabilities and adds helpful fallbacks for command detection. While both changes are positive, they should ideally be in separate, focused pull requests as per the project's contribution guidelines (Keep PRs small, focused...). This improves reviewability and makes the commit history easier to follow. For instance, the important security fix is not mentioned in the PR title or description, which could cause it to be overlooked.

@gemini-cli gemini-cli bot added priority/p2 Important but can be addressed in a future release. area/core Issues related to User Interface, OS Support, Core Functionality help wanted We will accept PRs from all issues marked as "help wanted". Thanks for your support! labels Mar 11, 2026
@apfine
Copy link
Copy Markdown
Contributor Author

apfine commented Mar 14, 2026

@mbleigh @dewitt

Please review this Pull request.

@apfine
Copy link
Copy Markdown
Contributor Author

apfine commented Mar 14, 2026

@Adib234 sir please could you review this ?

according to gemini code assist bot there were some issues so I resolved them .

It would be very helpful sir !!

@spencer426 spencer426 self-requested a review March 17, 2026 18:24
const TMUX_UPDATE_INTERVAL_MS = 750;
const TMUX_FRAMES = ['.', '..', '...'] as const;

function isTmuxEnvironment(): boolean {
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 isTmuxEnvironment() function duplicates existing logic. We already have an inTmux() function in packages/cli/src/ui/utils/commandUtils.ts. Could you please export inTmux from that file and use it in GeminiSpinner.tsx instead of duplicating the logic?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Sure !!

Copy link
Copy Markdown
Contributor

@spencer426 spencer426 left a comment

Choose a reason for hiding this comment

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

The PR description mentions adding focused regression tests for the new behavior GeminiSpinner.test.tsx, but the test file is not included in the actual PR diff. It looks like it might have been missed during the commit/push process. Could you please add it?

@apfine
Copy link
Copy Markdown
Contributor Author

apfine commented Mar 19, 2026

The PR description mentions adding focused regression tests for the new behavior GeminiSpinner.test.tsx, but the test file is not included in the actual PR diff. It looks like it might have been missed during the commit/push process. Could you please add it?

Sure !!

@apfine apfine requested a review from spencer426 March 19, 2026 13:07
@apfine
Copy link
Copy Markdown
Contributor Author

apfine commented Mar 19, 2026

@spencer426 I have done the suggested changes.

Please review it .

Thankyou so much for your valuable time !!

Copy link
Copy Markdown
Contributor

@spencer426 spencer426 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 update. I've left a few inline comments regarding our testing guidelines.

beforeEach(() => {
vi.unstubAllEnvs();
mockUseIsScreenReaderEnabled.mockReturnValue(false);
});
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.

To prevent test flakiness, explicitly unset the TMUX and TERM environment variables here. If someone runs npm test inside tmux, inTmux() will evaluate to true and cause the non-tmux test to fail.

vi.stubEnv('TMUX', '');
vi.stubEnv('TERM', '');

afterEach(() => {
vi.useRealTimers();
vi.unstubAllEnvs();
});
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.

Per the Core Testing Guidelines, please add vi.restoreAllMocks() here to prevent test pollution.

);

await waitUntilReady();
expect(lastFrame()).toContain('.');
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 React Testing Guidelines specify: "Use toMatchSnapshot to verify that rendering works as expected rather than matching against the raw content of the output." Please update these assertions to use toMatchSnapshot() for each frame state.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Thanks for the update

@apfine apfine requested a review from spencer426 March 21, 2026 05:03
@apfine
Copy link
Copy Markdown
Contributor Author

apfine commented Mar 21, 2026

@spencer426 I have did the changes .

Please review them !!

Copy link
Copy Markdown
Contributor

@spencer426 spencer426 left a comment

Choose a reason for hiding this comment

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

LGTM

@apfine
Copy link
Copy Markdown
Contributor Author

apfine commented Mar 27, 2026

@spencer426
Please could you help me get this PR merged with main ??

@SHARABUMANOJKUMAR
Copy link
Copy Markdown

Hi, I would like to work on this issue.

I’m a GSoC 2026 applicant and have experience with Node.js and CLI tools. I’m particularly interested in improving CLI behavior and terminal compatibility.

Could you please guide me on how to proceed?

Thanks!

@SHARABUMANOJKUMAR
Copy link
Copy Markdown

Apologies, I mistakenly commented on a PR instead of an issue.

Thank you for the clarification. I’ll look for an appropriate open issue to contribute to.

Comment on lines +52 to +73
it('renders a tmux-safe fixed-width dots indicator when in tmux', async () => {
vi.useFakeTimers();
vi.stubEnv('TMUX', '/tmp/tmux-1000/default,123,0');
const { lastFrame, waitUntilReady, unmount } = renderWithProviders(
<GeminiSpinner />,
);

await waitUntilReady();
expect(lastFrame()).toMatchSnapshot();

await act(async () => {
await vi.advanceTimersByTimeAsync(750);
});
await waitUntilReady();
expect(lastFrame()).toMatchSnapshot();

await act(async () => {
await vi.advanceTimersByTimeAsync(750);
});
await waitUntilReady();
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.

Here, the tmux dots animation test advances through all three frames (......) but stops there. It never tests what happens on the next tick, which is the most important one: that's where the modulo logic in GeminiSpinner.tsx (lines 55-57) wraps the index from 2 back to 0 so the animation loops.

If someone accidentally removed the % TMUX_FRAMES.length from GeminiSpinner.tsx (line 56), the index would keep climbing: 0 → 1 → 2 → 3. Index 3 doesn't exist in a 3-element array, so the animation would break. But the current test stops after reaching index 2, so it would still pass and never catch that problem.

Adding one more timer advance before the unmount() at line 74 would cover this:

await act(async () => {
  await vi.advanceTimersByTimeAsync(750);
});
await waitUntilReady();
// Verify wrap-around: should cycle back to '.'
expect(lastFrame()).toMatchSnapshot();

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

sure , I am looking in to it .
Thankyou I will revert back soon with fixes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/core Issues related to User Interface, OS Support, Core Functionality help wanted We will accept PRs from all issues marked as "help wanted". Thanks for your support! priority/p2 Important but can be addressed in a future release.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make the CLI more friendly with tmux

5 participants