Skip to content

Conversation

@JayadityaGit
Copy link
Contributor

TL;DR

This PR improves the Gemini CLI experience by allowing a single Ctrl+C press to cancel an in-progress stream, matching common CLI expectations. The existing behavior of double Ctrl+C to exit the app is preserved.


Summary of Changes

The main logic updates are in:

  • packages/cli/src/ui/App.tsx – to detect a single Ctrl+C press during streaming.
  • packages/cli/src/ui/hooks/useGeminiStream.ts – where the cancellation logic was extracted into a reusable cancelRequest function.

Why This Matters

Previously, users needed to press the Esc key to cancel a long response — not a familiar pattern in most CLI tools. With this update, the CLI aligns with standard UX expectations:

  • Intuitive Interruption: Pressing Ctrl+C once during streaming now cancels the stream immediately.

  • Unified Cancellation Logic: Both Esc and Ctrl+C call a centralized cancelRequest function, reducing code duplication.

  • User Feedback:

    • If you cancel during streaming: Request cancelled. is shown.
    • If no stream is active: Press Ctrl+C again to exit. is shown, just like before.

How to Test It

  1. Start Gemini CLI.

  2. Trigger a long streaming response (e.g., ask it to write a novel about space-faring cats).

  3. Single Ctrl+C Cancellation:

    • During the stream, press Ctrl+C once.
    • ✅ Expected: Stream cancels, you see Request cancelled., and return to the input prompt.
  4. Esc Key Cancellation:

    • Trigger another stream, press Esc.
    • ✅ Expected: Same behavior as Ctrl+C.
  5. Double Ctrl+C Exit:

    • During a stream, press Ctrl+C twice quickly.
    • ✅ Expected: CLI exits.
  6. Idle Ctrl+C Handling:

    • While idle (no stream), press Ctrl+C once.
    • ✅ Expected: Shows Press Ctrl+C again to exit. and waits. Press again within the window to exit.

Testing Matrix

Platform npm run npx Docker Podman Seatbelt
Linux
macOS
Windows

(✅ = Tested; ❓ = Untested; – = Unsupported)


Linked Issue

#5837

@JayadityaGit JayadityaGit requested a review from a team as a code owner August 8, 2025 12:14
Copy link
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.

Summary of Changes

Hello @JayadityaGit, 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!

I've improved the Gemini CLI's user experience by implementing a more intuitive way to cancel in-progress streaming responses. Previously, users had to press the Esc key, which is not a standard CLI pattern. Now, a single Ctrl+C press will cancel the stream, bringing the CLI in line with common command-line tool expectations. This change also centralizes the cancellation logic into a reusable function and provides clearer user feedback, while maintaining the existing double Ctrl+C behavior for exiting the application.

Highlights

  • Single Ctrl+C for Stream Cancellation: Users can now press Ctrl+C once to stop an active streaming response, aligning the CLI's behavior with common command-line expectations.
  • Centralized Cancellation Logic: The logic for canceling a stream has been refactored into a reusable cancelRequest function, reducing code duplication and improving maintainability.
  • Improved User Feedback: Clear messages are now displayed to the user for stream cancellation (Request cancelled.) and when prompting for a second Ctrl+C to exit (Press Ctrl+C again to exit.).
  • Preservation of Double Ctrl+C Exit: The existing behavior of pressing Ctrl+C twice to exit the application remains unchanged, ensuring familiar functionality is preserved.
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 in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

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 issue 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 or fill out our survey to provide feedback.

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
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 welcome improvement by allowing stream cancellation with a single Ctrl+C, aligning the CLI with standard user expectations. The refactoring of the cancellation logic into a reusable cancelRequest function in useGeminiStream.ts is a good architectural choice that improves maintainability.

My review identifies one high-severity issue where the UI state for tool calls is not correctly updated upon cancellation, which could lead to user confusion. I've provided a detailed comment and a code suggestion to address this.

@JayadityaGit JayadityaGit marked this pull request as draft August 8, 2025 12:18
@JayadityaGit JayadityaGit force-pushed the feat/ctrl-c-stream-stop branch from b2cbe02 to 099bb91 Compare August 8, 2025 12:25
@JayadityaGit JayadityaGit marked this pull request as ready for review August 8, 2025 12:26
@JayadityaGit JayadityaGit changed the title Feat/ctrl c stream stop feat(cli): support single Ctrl+C to cancel streaming, preserving double Ctrl+C to exit Aug 8, 2025
@JayadityaGit
Copy link
Contributor Author

/gemini review

Copy link
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 great usability improvement by allowing stream cancellation with a single Ctrl+C, which is a standard convention in command-line tools. The refactoring of the cancellation logic into a reusable cancelRequest function is a good architectural choice.

My review identifies one high-severity issue in the new cancelRequest function where pending tool calls are not correctly handled upon cancellation, which could lead to an inconsistent UI state. I've provided a detailed comment with a code suggestion to address this.

Other than that, the changes look good and the feature is a welcome addition to the CLI.

Comment on lines 186 to 214
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The current cancellation logic for tool calls appears to be flawed. It checks pendingHistoryItemRef.current.type === 'tool_group', but pendingHistoryItemRef is used for text-based pending items (like Gemini's streaming response), not for tool calls. The actual state for tool calls is managed in the toolCalls variable.

This means that when a user cancels (Ctrl+C or Esc) while tools are executing, the pending tool call display will disappear from the UI without being correctly saved to the history with a 'Canceled' status. This leads to an inconsistent UI and loss of session history.

To fix this, the logic should check toolCalls.length and use mapTrackedToolCallsToDisplay to build the history item for the tool group. Additionally, toolCalls should be added to the useCallback dependency array to ensure the function has access to the latest state.

  const cancelRequest = useCallback(() => {
    if (turnCancelledRef.current) {
      return;
    }
    turnCancelledRef.current = true;
    abortControllerRef.current?.abort();

    // Persist any pending tool calls to history with a Canceled status.
    if (toolCalls.length > 0) {
      const toolGroupDisplay = mapTrackedToolCallsToDisplay(toolCalls);
      const updatedTools = toolGroupDisplay.tools.map((tool) =>
        tool.status === ToolCallStatus.Pending ||
        tool.status === ToolCallStatus.Confirming ||
        tool.status === ToolCallStatus.Executing
          ? { ...tool, status: ToolCallStatus.Canceled }
          : tool,
      );
      const updatedToolGroup: HistoryItemToolGroup = {
        ...toolGroupDisplay,
        tools: updatedTools,
      };
      addItem(updatedToolGroup, Date.now());
    }

    // Persist any other pending history item (e.g., streaming text).
    if (pendingHistoryItemRef.current) {
      addItem(pendingHistoryItemRef.current, Date.now());
    }

    addItem(
      {
        type: MessageType.INFO,
        text: 'Request cancelled.',
      },
      Date.now(),
    );
    setPendingHistoryItem(null);
    onCancelSubmit();
    setIsResponding(false);
    setThought(null); // Also reset any active "thought" display.
  }, [
    addItem,
    onCancelSubmit,
    pendingHistoryItemRef,
    setPendingHistoryItem,
    setIsResponding,
    setThought,
    toolCalls,
  ]);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks !

@JayadityaGit
Copy link
Contributor Author

I’d appreciate it if a human reviewer could take a look at this when possible. Thank you!

Copy link
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 great improvement to the CLI's user experience by allowing stream cancellation with a single Ctrl+C. The implementation is well-structured, centralizing the cancellation logic in a reusable cancelRequest function. I've found one potential issue with how pending items are persisted to history on cancellation, which could lead to duplicate entries. My feedback includes a suggestion to fix this.

Comment on lines 210 to 197
Copy link
Contributor

Choose a reason for hiding this comment

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

high

There's a potential for duplicating history items when a request with active tool calls is cancelled. The code first persists a new tool group with a 'Canceled' status based on the toolCalls state (lines 194-208). Immediately after, it unconditionally persists pendingHistoryItemRef.current.

If pendingHistoryItemRef.current also contains a tool_group item (which is possible if useReactToolScheduler sets it), this will result in a duplicate tool group being added to the history. This could corrupt the conversation history and affect subsequent model responses.

To fix this, we should avoid persisting pendingHistoryItemRef.current if it's a tool group and we've already handled persisting the tool calls.

    // Persist any other pending history item (e.g., streaming text).
    if (
      pendingHistoryItemRef.current &&
      !(toolCalls.length > 0 && pendingHistoryItemRef.current.type === 'tool_group')
    ) {
      addItem(pendingHistoryItemRef.current, Date.now());
    }

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sure !

@JayadityaGit JayadityaGit force-pushed the feat/ctrl-c-stream-stop branch from bfc2684 to 4f81f38 Compare August 8, 2025 22:20
@JayadityaGit
Copy link
Contributor Author

/gemini review

Copy link
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 standard CLI user experience by allowing a single Ctrl+C to cancel an in-progress stream, while preserving the double Ctrl+C to exit. The core logic is refactored into a reusable cancelRequest function in useGeminiStream.ts. My review focuses on the correctness of this new cancellation logic. I've found one potential issue where partial tool output might be displayed for a canceled tool, which could be confusing. I've provided a suggestion to clear this partial output upon cancellation.

Comment on lines 196 to 202
Copy link
Contributor

Choose a reason for hiding this comment

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

high

When a tool call is cancelled, especially while it's in the Executing state, it might have produced partial output that is stored in resultDisplay. Simply changing the status to Canceled while preserving this partial output could be misleading in the UI. It's better to also clear the resultDisplay to avoid showing incomplete data for a canceled operation.

Suggested change
const updatedTools = toolGroupDisplay.tools.map((tool) =>
tool.status === ToolCallStatus.Pending ||
tool.status === ToolCallStatus.Confirming ||
tool.status === ToolCallStatus.Executing
? { ...tool, status: ToolCallStatus.Canceled }
: tool,
);
const updatedTools = toolGroupDisplay.tools.map((tool) =>
tool.status === ToolCallStatus.Pending ||
tool.status === ToolCallStatus.Confirming ||
tool.status === ToolCallStatus.Executing
? { ...tool, status: ToolCallStatus.Canceled, resultDisplay: undefined }
: tool,
);

Copy link
Collaborator

@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.

Thanks for the pull request. Please add tests and then I will approve. Would be particularly useful to test the new logic added cancelling tool calls.
One other note that if you have a pending tool call awaiting confirmation then Ctrl-C does not cancel it while Esc does. That would be good to fix as with this change users should expect to be able to use ctrl-C to cancel everywhere. Example of what I mean:

Image Pressing Ctrl-C does nothing here other than to threaten to exit the app.

@JayadityaGit JayadityaGit marked this pull request as draft August 8, 2025 23:39
@JayadityaGit JayadityaGit marked this pull request as ready for review August 9, 2025 00:15
@JayadityaGit JayadityaGit requested a review from jacob314 August 9, 2025 00:16
@JayadityaGit
Copy link
Contributor Author

@jacob314 I’ve added the tests and updated the implementation so that a single Ctrl+C works the same as Esc. Thank you for your review.

@JayadityaGit
Copy link
Contributor Author

@jacob314 , The preflight check failed during the test:ci phase for the @google/gemini-cli package due to a JavaScript heap out of memory error. This occurred while running tests with coverage using Vitest. All 1388 tests in
the dependent packages passed successfully.

The failure seems related to resource constraints during the coverage reporting step rather than an actual test failure. Increasing the Node.js memory limit for the test process might resolve the issue. Consider
running tests with NODE_OPTIONS="--max-old-space-size=4096" or investigating the coverage configuration.

Copy link
Collaborator

@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.

approved after these comments are addressed. please be sure to revert the files that shouldn't have been changed. there was one file I commented on last round and another file added this round.

@JayadityaGit JayadityaGit force-pushed the feat/ctrl-c-stream-stop branch from d82a025 to 1fcf053 Compare August 9, 2025 04:18
@JayadityaGit
Copy link
Contributor Author

Hi @jacob314, I’ve implemented the requested changes. Let me know if you have any further suggestions or improvements.

@JayadityaGit JayadityaGit requested a review from jacob314 August 9, 2025 04:35
Copy link
Collaborator

@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.

Thanks for the quick responses iterating on the pr. That makes it really easy for me to review!
lgtm

@jacob314 jacob314 enabled auto-merge August 9, 2025 06:17
@JayadityaGit
Copy link
Contributor Author

@jacob314, I tried running vitest run --coverage but the job failed with a "JavaScript heap out of memory" error. Interestingly, the tests pass when run in isolation, so this might be related to memory usage during the full coverage run. Is there anything we can do to address this, such as optimizing the tests or increasing Node.js’s memory limit?

@jacob314
Copy link
Collaborator

jacob314 commented Aug 9, 2025

Unfortunately there is a merge conflict. Please resolve and then I will merge.

auto-merge was automatically disabled August 9, 2025 08:20

Head branch was pushed to by a user without write access

@JayadityaGit
Copy link
Contributor Author

@jacob314 I’ve tried resolving the conflicts. Could you please review and confirm if everything looks correct? I’m still getting the hang of Git, so I’m a bit unsure.

@JayadityaGit JayadityaGit force-pushed the feat/ctrl-c-stream-stop branch from 7fd491f to 4988fda Compare August 10, 2025 16:49
@JayadityaGit JayadityaGit force-pushed the feat/ctrl-c-stream-stop branch from 4988fda to 8cb9465 Compare August 10, 2025 17:02
@JayadityaGit
Copy link
Contributor Author

Hi @jacob314,

Just wanted to provide an update on this PR.

To resolve the merge conflicts with the main branch, I've rebased this feature branch (feat/ctrl-c-stream-stop) on top of the latest upstream/main.

A conflict arose in packages/cli/src/ui/App.tsx during the rebase. We resolved it by integrating the cancelOngoingRequest() functionality from main into the newer, refactored key-handling logic in this branch.
This preserves both the structural improvements and the request cancellation feature. The branch has been force-pushed, so this PR should now be up-to-date and conflict-free.

As of now, this PR adds the intended functionality for Ctrl+C to cancel both ongoing streams and pending tool calls. I am still in the process of figuring out how to write the tests for this behavior.

Thanks

@JayadityaGit JayadityaGit requested a review from jacob314 August 10, 2025 17:14
@SandyTao520 SandyTao520 added this pull request to the merge queue Aug 12, 2025
Merged via the queue into google-gemini:main with commit 2d1a6af Aug 12, 2025
14 checks passed
thacio added a commit to thacio/auditaria that referenced this pull request Aug 12, 2025
SandyTao520 pushed a commit that referenced this pull request Aug 12, 2025
acoliver referenced this pull request in vybestack/llxprt-code Aug 12, 2025
JeongJaeSoon pushed a commit to JeongJaeSoon/gemini-cli that referenced this pull request Aug 21, 2025
involvex pushed a commit to involvex/gemini-cli that referenced this pull request Sep 11, 2025
reconsumeralization pushed a commit to reconsumeralization/gemini-cli that referenced this pull request Sep 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants