feat: Implement background shell commands
Summary
Implement support for running shell commands in the background. This feature allows long-running commands to execute without blocking the CLI, improving responsiveness.
Details
This PR introduces the capability to execute shell commands asynchronously. Previously, all shell commands were blocking, which could lead to a poor user experience for operations that take a significant amount of time. This change leverages process management to run commands in a non-blocking manner, allowing the CLI to remain interactive. The output of background commands can be retrieved as they become available.
Related Issues
Related to #<issue_number>
How to Validate
Validate ShellTool
- Run a long-running shell command using the new background command functionality, (e.g "please run npm run preflight in the background", "please run a shell command that sleeps for 10min in the background")
- Verify that the CLI remains responsive and does not block while the command is running in the background.
- press ctrl+b to view the background process
Validate ShellCommandProcessor
- Press Shift+! to enter shell mode
- execute any command that will wait for user input (vim, nvim, gemini)
- press ctrl+b to put it in the background
- press ctrl+b to view the background process
- Verify that the CLI remains responsive and does not block while the command is running in the background.
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
- [ ] npx
- [ ] Docker
- [ ] Podman
- [ ] Seatbelt
- [ ] Windows
- [ ] npm run
- [ ] npx
- [ ] Docker
- [ ] Linux
- [ ] npm run
- [ ] npx
- [ ] Docker
- [ ] MacOS
Summary of Changes
Hello @galz10, I'm Gemini Code Assist[^1]! 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 introduces a new feature that allows users to execute shell commands in the background, significantly enhancing the CLI's multitasking capabilities. It provides a dedicated user interface for monitoring and interacting with these background processes, complete with new keybindings for seamless management. The underlying shell execution service has been extensively refactored to support persistent background processes, real-time output streaming, and lifecycle management, while the run_shell_command tool has been updated to enable AI models to leverage this new functionality.
Highlights
- Background Shell Execution: Introduced the capability to run shell commands in the background, allowing long-running processes to operate without blocking the main user interface.
- Dedicated UI for Background Shells: A new
BackgroundShellDisplaycomponent has been added to visualize and interact with active background processes, including their output and the ability to switch between them. - New Keybindings for Management: Added
Ctrl+Bto either move the current foreground shell command to the background or toggle the visibility of the background shell view.Ctrl+Owas also introduced to toggle a list of all active background shell processes. - Enhanced Shell Execution Service: The core
ShellExecutionServicehas been significantly upgraded to manage persistent background PTYs, provide real-time output updates via subscriptions, handle process exit events, and allow explicit termination of background processes. - Tool Integration: The
run_shell_commandtool now supports anis_backgroundparameter, enabling AI models to initiate and manage tasks that run in the background.
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 :thumbsup: and :thumbsdown: 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.
[^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.
Thanks for your hard work on this pull request!
This pull request is quite large, which can make it challenging and time-consuming for reviewers to go through thoroughly.
To help us review it more efficiently and get your changes merged faster, we kindly request you consider breaking this into smaller, more focused pull requests. Each smaller PR should ideally address a single logical change or a small set of related changes.
For example, you could separate out refactoring, new feature additions, and bug fixes into individual PRs. This makes it easier to understand, review, and test each component independently.
We appreciate your understanding and cooperation. Feel free to reach out if you need any assistance with this!
Size Change: +26.7 kB (+0.12%)
Total Size: 21.6 MB
| Filename | Size | Change |
|---|---|---|
./bundle/gemini.js |
21.6 MB | +26.7 kB (+0.12%) |
ℹ️ 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 |
Hello! This is a fantastic feature addition. The implementation of background shell commands is a significant UX improvement for long-running tasks. The architecture using ShellExecutionService to detach the PTY is clever.
I have a few critical comments regarding React best practices (specifically hooks), process lifecycle management, and potential keybinding conflicts.
Critical Issues
-
Forbidden
eslint-disableinBackgroundShellDisplay.tsx: Inpackages/cli/src/ui/components/BackgroundShellDisplay.tsx, you have disabled the exhaustive deps rule:// We only want this to run on mount (when the component becomes visible) // eslint-disable-next-line react-hooks/exhaustive-deps }, []);We explicitly forbid disabling
react-hooks/exhaustive-deps. This often leads to stale closures and bugs that are hard to debug. Fix: Move this logic up toAppContainer.tsx. You already havetoggleBackgroundShellandsetIsBackgroundShellListOpen. When the background shell view is toggled on (e.g., in the key handler or a specific effect inAppContainerthat watchesisBackgroundShellVisibletransitions), you can check the number of shells and decide whether to open the list or not. This keeps the child component pure and the logic where state is managed. -
setTimeoutpreventing process exit inShellExecutionService.ts: Inpackages/core/src/services/shellExecutionService.ts:setTimeout( () => { this.exitedPtyInfo.delete(ptyProcess.pid); }, 5 * 60 * 1000, );This 5-minute timer is likely to keep the Node.js event loop alive, preventing the CLI from exiting gracefully if the user tries to quit while this timer is active. Fix: Chain
.unref()to the timeout:setTimeout(...).unref().
Code Quality & Best Practices
-
Keyboard Shortcut (
F11):F11is the standard shortcut for "Toggle Full Screen" in many terminal emulators and operating systems (including macOS "Show Desktop" and VSCode). This is likely to conflict. Suggestion: The PR description mentionsCtrl+B. Consider using that if available, orCtrl+Alt+B? IfF11is strongly desired, please document the potential conflicts or verify it works in major environments (VSCode, iTerm2, Windows Terminal). -
Type Safety in
useGeminiStream.ts:const data = response?.data as unknown as ShellToolData;Please avoid double casting with
unknown. Define theShellToolDatainterface clearly or import it if possible, and assert the type safely or use a type guard. -
BackgroundShellDisplayEffect Dependencies: The effect that callsShellExecutionService.resizePtydepends onwidthandheight. Ensure thatresizePtyis debounced or efficient enough that resizing the terminal window doesn't cause excessive IPC/PTY calls, althoughink's render cycle usually dampens this enough.
Tests
-
Integration Tests: The unit tests look good, but this is a complex feature involving IPC and PTY management. Request: Please add an integration test in
integration-tests/(e.g.,background-shell.test.ts) that:- Starts the CLI.
- Runs a command like
sleep 1. - Backgrounds it.
- Verifies the UI shows the background indicator.
- Verifies the process eventually exits. This ensures the end-to-end flow works as expected.
-
Test Utilities: In
packages/cli/src/ui/components/BackgroundShellDisplay.test.tsx, you are usingactfromreactalongsiderenderfrom the test utils. This is generally correct given our custom render, but please double-check that you aren't getting any "not wrapped in act" warnings in the console when running these tests.
Great work on a complex feature! Looking forward to the updates.
@galz10 Ctrl+b is not opening/showing background processes even though UI shows there is one.
I am still uncertain whether the Using: 2 GEMINI.md files | 1 Background process is visually enough for users to notice there is a background process.
Is there a world where we want the 1 Background process to be the same color as shell mode border? Would be more noticeable and make it more clear that background processes equate to background shells.
maybe @jacob314 has thoughts on this?
When a process is moved to the background we need to surface the shortcut to show background commands to the user...
Command moved to background. View background processes using "shortcut"
Otherwise the user has no idea how to view their shells 😄