Compare commits

..

691 Commits

Author SHA1 Message Date
github-actions[bot] ea96293e16 chore(release): v0.1.18 2025-08-06 00:58:42 +00:00
Jerop Kipruto cd7e60e008 switch from heads to tags in url path (#5638) 2025-08-05 17:47:28 -07:00
Sandy Tao 59bde4a612 fix(core) Fix not resetting when after first get out of completion suggestions (#5635)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-08-06 00:37:44 +00:00
Bryan Morgan 02f7e48c51 Removed GitHub Actions experiment files (#5627) 2025-08-06 00:01:18 +00:00
christine betts aeb6602266 Remove a few witty loading phrases (#5631)
Co-authored-by: matt korwel <matt.korwel@gmail.com>
2025-08-05 23:59:14 +00:00
David Rees 805114aef8 fix(docs): Fix code block delimiters in commands.md (#5521)
Co-authored-by: Sandy Tao <sandytao520@icloud.com>
2025-08-05 23:30:57 +00:00
Justin Mahood 91035ad7b0 Fix(vim): Fix shell mode in Vim mode (#5567)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-08-05 23:29:37 +00:00
Bryant Chandler 12a9bc3ed9 feat(core, cli): Introduce high-performance FileSearch engine (#5136)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-08-05 23:18:03 +00:00
Allen Hutchison 2141b39c3d feat(cli): route non-interactive output to stderr (#5624) 2025-08-05 23:11:21 +00:00
Shreya Keshive 268627469b Refactor IDE client state management, improve user-facing error messages, and add logging of connection events (#5591)
Co-authored-by: matt korwel <matt.korwel@gmail.com>
2025-08-05 22:52:58 +00:00
Jacob MacDonald 6a72cd064b check for the prompt capability before listing prompts from MCP servers (#5616)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
Co-authored-by: Sandy Tao <sandytao520@icloud.com>
2025-08-05 22:50:30 +00:00
sangwook aebe3ace3c perf(core): implement parallel file processing for 74% performance improvement (#4763)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
Co-authored-by: Sandy Tao <sandytao520@icloud.com>
2025-08-05 22:47:18 +00:00
8bitmp3 c402784d97 Fix and improve Gemini CLI troubleshooting.md doc (#2734)
Co-authored-by: Sandy Tao <sandytao520@icloud.com>
2025-08-05 22:43:41 +00:00
William Thurston bed6ab1cce fix(start): use absolute path to resolve CLI package (#3196)
Co-authored-by: Abhi <43648792+abhipatel12@users.noreply.github.com>
Co-authored-by: Sandy Tao <sandytao520@icloud.com>
2025-08-05 22:43:15 +00:00
xyizko 1b08a6c063 fix(minor): Grammar and Typos (#5053)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-08-05 22:41:27 +00:00
Sandy Tao 82fa7a0660 fix(format) Fix format for .github/workflows/weekly-velocity-report.yml (#5622) 2025-08-05 22:32:06 +00:00
Bryan Morgan 2e9236fab4 Update weekly-velocity-report.yml 2025-08-05 18:11:06 -04:00
Mikhail Aksenov dadf05809c feat: mcp - support audiences for OAuth2 (#5265) 2025-08-05 22:02:16 +00:00
Ramón Medrano Llamas 29c3825604 fix(mcp): clear prompt registry on refresh to prevent duplicates (#5385)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
Co-authored-by: Sandy Tao <sandytao520@icloud.com>
2025-08-05 21:59:31 +00:00
Hiroaki Mitsuyoshi faf6a5497a feat(docs): Add /chat delete command in commands.md (#5408)
Co-authored-by: Sandy Tao <sandytao520@icloud.com>
2025-08-05 21:58:09 +00:00
Jacob Richman dd85aaa951 bug(core): Fix flaky test by using waitFor. (#5540)
Co-authored-by: Sandy Tao <sandytao520@icloud.com>
2025-08-05 21:56:38 +00:00
Gal Zahavi aacae1de43 fix(core): prevent UI shift after vim edit (#5315) 2025-08-05 21:55:54 +00:00
Sandy Tao 8d993156e7 Fix format (#5617) 2025-08-05 21:38:43 +00:00
Bryan Morgan 57003ca68c Update weekly-velocity-report.yml 2025-08-05 17:18:19 -04:00
Bryan Morgan 47de37eb0a Update weekly-velocity-report.yml 2025-08-05 17:10:37 -04:00
Bryan Morgan dc7b4fda64 Update weekly-velocity-report.yml 2025-08-05 17:08:22 -04:00
Bryan Morgan 3dcca31796 Update weekly-velocity-report.yml 2025-08-05 17:00:44 -04:00
Bryan Morgan c194a6ac3b GitHub Action for velocity reporting purposes (#5607) 2025-08-05 20:33:59 +00:00
Bryan Morgan d421fa9e64 Testing basic velocity report action 2025-08-05 15:55:50 -04:00
Luccas Paroni 2778c7d851 feat(core): Parse Multimodal MCP Tool responses (#5529)
Co-authored-by: Luccas Paroni <luccasparoni@google.com>
2025-08-05 19:19:47 +00:00
Oleksandr Gotgelf b465145229 chore(settings): clean up comments in settings.ts (#5576) 2025-08-05 19:10:16 +00:00
Alexander J f2d6748432 fix: small typo in ROADMAP.md (#5593)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-08-05 19:04:10 +00:00
joshualitt 08f1431946 bug(core): fix contentRangeTruncated calculation. (#5329)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-08-05 18:52:39 +00:00
David East 43d5aaa798 fix(mcp): ensure authorization url is valid when containing query params (#5545)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-08-05 18:44:30 +00:00
Yuki Okita 5c8268b6f4 feat: Multi-Directory Workspace Support (part 3: configuration in settings.json) (#5354)
Co-authored-by: Allen Hutchison <adh@google.com>
2025-08-05 17:01:01 +00:00
Jack Wotherspoon d0cda58f1f docs: update typo in commands.md (#5584) 2025-08-05 14:03:58 +00:00
N. Taylor Mullen c7a1de4983 chore(release): v0.1.17 (#5561) 2025-08-04 21:37:32 -07:00
DeWitt Clinton 49001a0f83 Remove the "local modifications" string from bug and about reports. (#5552) 2025-08-05 04:01:19 +00:00
Olcan 11ecf6fc86 fix self-reference in build script (#5548) 2025-08-05 01:12:21 +00:00
Harold Mciver 99ba2f6424 Update MCP client to connect to servers with only prompts (#5290) 2025-08-04 21:38:23 +00:00
christine betts 93f8fe3671 [ide-mode] Add openDiff tool to IDE MCP server (#4519)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-08-04 21:36:23 +00:00
Mo Moadeli e7b468e122 feat(cli): Prevent redundant opening of browser tabs when zero MCP servers are configured (#5367)
Co-authored-by: Allen Hutchison <adh@google.com>
2025-08-04 21:20:49 +00:00
Shreya Keshive dca040908a ide-mode flag cleanup (#5531) 2025-08-04 21:06:50 +00:00
Shreya Keshive 2180dd13dc Improve user-facing error messages for IDE mode (#5522) 2025-08-04 21:06:17 +00:00
Richie Foreman 11808ef7ed fix(core): Allow model to be set from settings.json (#5527) 2025-08-04 20:41:58 +00:00
Sandy Tao 8da6d23688 refactor(core): Rename useSlashCompletion to useCommandCompletion (#5532) 2025-08-04 20:35:26 +00:00
Seth Vargo 37b83e05a7 Use new URLs for downloading workflows (#5524) 2025-08-04 20:10:36 +00:00
Jacob MacDonald 5caf23d627 remove unnecessary checks in WriteFileChecks.getDescription (#5526) 2025-08-04 19:12:33 +00:00
Sandy Tao d1bfba1abb feat(core): Add trailing space when completing an at completion suggestion (#5475)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-08-04 18:30:59 +00:00
Sandy Tao b9fe4fc263 feat(cli): Handle Punctuation in @ Command Parsing (#5482) 2025-08-04 17:49:15 +00:00
Pyush Sinha e506b40c27 fix: /help remove flickering and respect clear shortcut (ctr+l) (#3611)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
Co-authored-by: Allen Hutchison <adh@google.com>
2025-08-04 16:53:50 +00:00
owenofbrien 83a04c4755 Cloud Shell surface logging fix (#5364) 2025-08-04 16:48:46 +00:00
matt korwel 94b7b402c5 feat(docs): create new documentation for automation and triage (#5363) 2025-08-04 08:49:14 -07:00
Kumbham Ajay Goud a8984a9b30 Fix: Preserve conversation history when changing auth methods via /auth (#5216)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-08-03 22:03:01 +00:00
Ali Al Jufairi acd48a1259 docs(fix): Update themes documentation to include new color keys for… (#5467) 2025-08-03 21:56:27 +00:00
N. Taylor Mullen 70478b92a9 chore(release): v0.1.16 (#5478) 2025-08-03 13:38:03 -07:00
Shreya Keshive 2cdaf912ba Generate NOTICES.TXT and surface via command (#5310) 2025-08-03 20:19:34 +00:00
Ayesha Shafique 072d8ba289 feat: Add reverse search capability for shell commands (#4793) 2025-08-03 19:53:24 +00:00
Oleksandr Gotgelf 03ed37d0dc fix: exclude DEBUG and DEBUG_MODE from project .env files by default (#5289)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-08-03 18:44:15 +00:00
Billy Biggs bedcbb9feb Add a setting to disable the version update nag message (#5449) 2025-08-03 18:20:55 +00:00
Gal Zahavi 820169ba2e feat(autoupdate): Improve update check and refactor for testability (#5389) 2025-08-02 03:17:32 +00:00
TIRUMALASETTI PRANITH 15a1f1af9d fix(config): Resolve duplicate config loading from home directory (#5090)
Co-authored-by: Allen Hutchison <adh@google.com>
Co-authored-by: Allen Hutchison <allen@hutchison.org>
2025-08-01 22:22:17 +00:00
Allen Hutchison 387706607d fix(tests): refactor integration tests to be less flaky (#4890)
Co-authored-by: matt korwel <matt.korwel@gmail.com>
2025-08-01 21:33:33 +00:00
mrcabbage972 dccca91fc9 Switch utility calls to use the gemini-2.5-flash-lite model (#5193)
Co-authored-by: Anjali Sridhar <anjsridhar@gmail.com>
2025-08-01 21:11:51 +00:00
owenofbrien a6a386f72a Propagate prompt (#5033) 2025-08-01 19:37:56 +00:00
joshualitt 67d16992cf bug(cli): Prefer IPv4 dns resolution by default. (#5338) 2025-08-01 19:30:39 +00:00
Santhosh Kumar 9382334a5e feat(github): add workflow to manage stale issues and PRs (#4871)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-08-01 19:26:03 +00:00
Sandy Tao c795168e9c feat(core): Use completionStart/End for slash command auto-completion (#5374) 2025-08-01 18:51:38 +00:00
Billy Biggs 24c5a15d7a Add a setting to disable auth mode validation on startup (#5358) 2025-08-01 18:49:03 +00:00
andrea-berling c725e258c6 feat(sandbox): Add SANDBOX_FLAGS for custom container options (#2036)
Co-authored-by: matt korwel <matt.korwel@gmail.com>
2025-08-01 16:32:44 +00:00
Brian de Alwis d42e3f1e7f doc: use standard Google security policy for GitHub projects (#5062) 2025-08-01 16:12:32 +00:00
Silvio Junior 7748e56153 [Fix Telemetry for tool calls, PR 1/n] Propagate tool reported errors via ToolCallResponseInfo and ToolResult (#5222) 2025-08-01 15:20:08 +00:00
cornmander e126d2fcd9 Add missing emacs entry in UI. (#5351) 2025-08-01 14:40:05 +00:00
Brian Ray dc9f17bb4a New browser launcher for MCP OAuth. (#5261) 2025-08-01 05:47:22 +00:00
Sandy Tao f21ff09389 fix(core): Remove json output schema form the next speaker check prompt (#5325)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-08-01 01:17:52 +00:00
Raushan Raj 6c3fb18ef6 Update gemini-automated-issue-triage.yml (#5312)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-08-01 01:14:26 +00:00
Tommaso Sciortino a3a432e3cf Fix bug executing commands in windows whose flags contain spaces (#5317) 2025-08-01 00:27:07 +00:00
Miguel Solorio 6f7beb414c Highlight slash commands in history (#5323) 2025-07-31 23:24:23 +00:00
Jacob Richman 61e382444a fix(ux) bug in replaceRange dealing with newLines that was breaking vim support (#5320) 2025-07-31 23:16:29 +00:00
Sandy Tao 32809a7be7 feat(cli): Improve @ autocompletion for mid-sentence edits (#5321) 2025-07-31 23:07:12 +00:00
Paige Bailey 37a3f1e6b6 Add emacs support, as per user requests. :) (#1633)
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Co-authored-by: N. Taylor Mullen <ntaylormullen@google.com>
Co-authored-by: Jacob Richman <jacob314@gmail.com>
Co-authored-by: matt korwel <matt.korwel@gmail.com>
Co-authored-by: matt korwel <mattkorwel@google.com>
2025-07-31 22:46:04 +00:00
JeromeJu 574015edd9 feat: Implement /setup-github command (#5069) 2025-07-31 22:14:22 +00:00
Yuki Okita f9a05401c1 feat: Multi-Directory Workspace Support (part2: add "directory" command) (#5241) 2025-07-31 19:02:08 +00:00
Niladri Das 9a6422f331 fix: CLAUDE.md compatibility for GEMINI.md '@' file import behavior (#2978)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Allen Hutchison <adh@google.com>
2025-07-31 16:36:50 +00:00
joshualitt ae86c7ba05 bug(core): UI reporting for truncated read_file. (#5155)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-07-31 16:31:14 +00:00
anj-s 65be9cab47 Fix: Ensure that non interactive mode and interactive mode are calling the same entry points (#5137) 2025-07-31 12:36:12 +00:00
Sandy Tao 23c014e29c Replace FlashDecidedToContinueEvent with NextSpeakerCheckEvent (#5257) 2025-07-31 04:47:04 +00:00
Kazunari001 3ef2c6d198 feat(docs): Add /init command in commands.md (#5187)
Co-authored-by: saucykazugmail <saucydog0922@gmail.com>
Co-authored-by: Gal Zahavi <38544478+galz10@users.noreply.github.com>
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-07-31 01:52:40 +00:00
Seth Troisi c77a22d4c6 Add render counter in debug mode (#5242)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-07-31 00:43:11 +00:00
Gal Zahavi d06e17fbd9 Improve error message for discoverTools function (#4157) 2025-07-31 00:16:21 +00:00
Shreya Keshive 0c6f788406 Exclude companion extension from release versioning (#5226)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-07-30 22:49:26 +00:00
christine betts 325bb89137 Add toggleable IDE mode setting (#5146) 2025-07-30 22:36:24 +00:00
Olcan ac1bb5ee42 confirm save_memory tool, with ability to see diff and edit manually for advanced changes that may override past memories (#5237) 2025-07-30 22:21:31 +00:00
Allen Hutchison 498edb57ab fix(testing): make ModelStatsDisplay snapshot test deterministic (#5236)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-07-30 22:09:32 +00:00
christine betts 7bc8766542 Introduce IDE mode installer (#4877) 2025-07-30 21:26:31 +00:00
Yuki Okita c1fe688956 feat: Multi-Directory Workspace Support (part1: add --include-directories option) (#4605)
Co-authored-by: Allen Hutchison <adh@google.com>
2025-07-30 20:38:20 +00:00
Srinath Padmanabhan 21965f986c Srithreepo Fixes for Scheduled triage (#5158) 2025-07-30 20:38:02 +00:00
shamso-goog 32b1ef3779 feat(ui): Update tool confirmation cancel button text (#4820)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-07-30 20:37:51 +00:00
Hyunsu Shin bcce1e7b84 perf(core): parallelize bfsFileSearch for 40% faster CLI startup (#5185) 2025-07-30 17:32:03 +00:00
Olcan bc23009f61 do not mention GEMINI.md in system prompt as it is not fixed and can confuse model as it is not mentioned by memory tool and memory file paths are generally not exposed to model (yet) (#5202) 2025-07-30 17:21:15 +00:00
yaksh gandhi b447c329db docs: Update chat command documentation with checkpoint locations (#5027)
Co-authored-by: Bryan Morgan <bryanmorgan@google.com>
Co-authored-by: F. Hinkelmann <franziska.hinkelmann@gmail.com>
2025-07-30 10:01:08 +00:00
N. Taylor Mullen fd434626c5 chore(release): v0.1.15 (#5163) 2025-07-29 22:03:54 -07:00
Sandy Tao 8985e489a5 Skip and reset loop checking around code blocks (#5144) 2025-07-30 04:05:03 +00:00
Jenna Inouye 0ce89392b8 Docs: add documentation for .geminiignore (#5123)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-07-30 03:36:26 +00:00
Sambhav Khanna d5a1b717c2 fix(update): correctly report new updates (#4821)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-07-30 00:11:15 +00:00
Allen Hutchison 091804c750 feat(docs): document GEMINI.md import syntax (#5145) 2025-07-29 23:41:31 +00:00
Ava d64c3d6af8 Add Starcraft ref to witty loading phrases (#5120)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-07-29 23:22:13 +00:00
Tommaso Sciortino 327f915610 Fix typo in RFC 9728 impl (#5126) 2025-07-29 23:03:39 +00:00
Srinath Padmanabhan 008051e42d Update Triage Logic to improve issue categorization. (#5110) 2025-07-29 21:44:48 +00:00
Shreya Keshive 293bb82019 Adds centralized support to log slash commands + sub commands (#5128) 2025-07-29 20:20:37 +00:00
shamso-goog 80079cd2a5 feat(cli): introduce /init command for GEMINI.md creation (#4852)
Co-authored-by: matt korwel <matt.korwel@gmail.com>
2025-07-29 16:49:01 +00:00
Daniel Lee 7356764a48 feat(commands): add custom commands support for extensions (#4703) 2025-07-29 01:40:47 +00:00
Gal Zahavi 871e0dfab8 feat: Add auto update functionality (#4686) 2025-07-29 00:56:52 +00:00
Shreya Keshive 83c4dddb7e Only enable IDE integration if gemini-cli is running in the same path as open workspace (#5068) 2025-07-28 20:55:00 +00:00
Seth Troisi 1c1aa047ff feat: Add tests for checkpoint tag sanitization (#4882) 2025-07-28 20:43:39 +00:00
Abhi b08679c906 Add new fallback state as prefactor for routing (#5065) 2025-07-28 19:55:50 +00:00
Danny b6c2c64f9b Adds docs outlining keyboard shortcuts for gemini-cli (#4727)
Co-authored-by: dannyzen <dannyrosen@google.com>
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-07-28 19:35:06 +00:00
Shreya Keshive cfe3753d4c Refactors companion VS Code extension to import & use notification schema defined in gemini-cli (#5059) 2025-07-28 18:20:56 +00:00
N. Taylor Mullen 9aef0a8e6c Revert "feat: Add /config refresh command" (#5060) 2025-07-28 18:13:46 +00:00
Neha Prasad a5ea113a8e fix: Clear previous thoughts when starting new prompts (#4966) 2025-07-28 17:57:33 +00:00
christine betts 379765da23 Add documentation for MCP prompts (#4897)
Co-authored-by: matt korwel <matt.korwel@gmail.com>
Co-authored-by: Bryan Morgan <bryanmorgan@google.com>
2025-07-28 16:01:15 +00:00
Alexander Parshakov f7e559223d docs: Add more examples to Popular tasks (#4979)
Co-authored-by: Bryan Morgan <bryanmorgan@google.com>
2025-07-28 15:54:09 +00:00
Ramón Medrano Llamas 0170791800 feat: Add /config refresh command (#4993)
Co-authored-by: Bryan Morgan <bryanmorgan@google.com>
2025-07-28 15:46:43 +00:00
Shreya Keshive e275441651 Updates schema, UX and prompt for IDE context (#5046) 2025-07-28 15:03:22 +00:00
James Woo f2e006179d Fix author attribution (#5042) 2025-07-28 14:45:23 +00:00
N. Taylor Mullen bd85070411 Revert "Propagate user_prompt_id to GenerateConentRequest for logging" (#5007) 2025-07-27 19:28:20 -07:00
Jenna Inouye 9ed351260c Update documentation for read_many_files. (#4874)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-07-27 22:25:04 +00:00
Jenna Inouye ab0d9df658 Clarify ToS and privacy documentation FAQs. (#4899) 2025-07-27 22:24:53 +00:00
Hiroaki Mitsuyoshi bce6eb5014 feat(chat): Implement /chat delete command (#2401) 2025-07-27 22:18:12 +00:00
Leeroy Ding 9ca48c00a6 fix: yolo mode not respected (#4972) 2025-07-27 21:42:26 +00:00
Abhi 0b5cc96362 (model) - Use Flash Lite For Next Speaker Checks (#4991) 2025-07-27 21:40:55 +00:00
owenofbrien b497791c59 Propagate user_prompt_id to GenerateConentRequest for logging (#4741) 2025-07-27 21:34:39 +00:00
Abhi 36e1e57252 (docs) - Fix small markdown mistake for custom commands docs (#4983) 2025-07-27 21:33:58 +00:00
Hyeladi Bassi a9f04eba2c refactor(telemetry): enhance flushToClearcut method with retry logic and early return for empty events (#1601)
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
2025-07-27 18:18:27 +00:00
Brian Ray c45c14ee0e Bug: add resource parameter to MCP OAuth Flow (#4981)
Co-authored-by: Your Name <you@example.com>
2025-07-27 18:09:45 +00:00
Abhi 576cebc928 feat: Add Shell Command Execution to Custom Commands (#4917) 2025-07-27 06:00:26 +00:00
Dmitry Lyalin 9e61b3510c docs: add missing --prompt-interactive/-i flag documentation (#4950) 2025-07-27 02:41:19 +00:00
Abhi 3e81359c6b (fix): Custom Commands follow symlinks (#4907) 2025-07-26 03:27:23 +00:00
Shreya Keshive 771cb229ab fix: Clean up transport on IDE connection failure (#4902) 2025-07-26 01:57:34 +00:00
Abhi ca5dd28ab6 refactor(core): Centralize shell logic into ShellExecutionService (#4823) 2025-07-26 01:56:49 +00:00
Jacob Richman ad2ef080aa Fix so legacy custom themes still load. (#4757)
Co-authored-by: matt korwel <matt.korwel@gmail.com>
2025-07-26 00:36:42 +00:00
Jacob Richman b089845f1c fix(ui): remove extraneous whitespace from startup screen (#3990) 2025-07-26 00:36:19 +00:00
Jacob Richman 21fef1620d Handle unhandled rejections more gracefully. (#4417)
Co-authored-by: Tommaso Sciortino <sciortino@gmail.com>
2025-07-26 00:35:26 +00:00
Jacob Richman fb751c542b Regression test for paste when the terminal lacks focus. (#4898) 2025-07-25 23:29:15 +00:00
Tommaso Sciortino f9cfb20897 Run pre-merge checks in mac os as well as ubutnu+win (#4900) 2025-07-25 23:09:37 +00:00
Tommaso Sciortino 17331001a0 Run presubmit tests in windows as well as linux. (#4672)
Co-authored-by: matt korwel <matt.korwel@gmail.com>
2025-07-25 15:57:30 -07:00
Sijie Wang fbdc8d5ab3 Vim mode (#3936) 2025-07-25 22:36:42 +00:00
Tommaso Sciortino aa71438684 Fix grep.test to work on windows. (#4889) 2025-07-25 21:32:28 +00:00
Tommaso Sciortino 23e0dc6960 Fix test to be windows compatible. (#4875) 2025-07-25 21:31:10 +00:00
Tommaso Sciortino be898710fe Make glob.test.ts win compatible. (#4891) 2025-07-25 21:30:39 +00:00
Tommaso Sciortino 4c144e616d Make fileDiscoveryService.test.ts win compatible (#4892) 2025-07-25 21:30:15 +00:00
Tommaso Sciortino 65aabfede8 Make oauth2 test windows compatible. (#4895) 2025-07-25 21:29:54 +00:00
christine betts eb65034117 Load and use MCP server prompts as slash commands in the CLI (#4828)
Co-authored-by: harold <haroldmciver@google.com>
Co-authored-by: N. Taylor Mullen <ntaylormullen@google.com>
2025-07-25 20:56:33 +00:00
Jacob Richman de96887789 Fix bugs breaking drag and drop of files. (#4887)
Co-authored-by: matt korwel <matt.korwel@gmail.com>
2025-07-25 20:26:13 +00:00
Abhi 13b0971291 fix(ci): Fix failing release workflow by creating a self-contained bundle (#4888)
Co-authored-by: Jerop Kipruto <jerop@google.com>
2025-07-25 20:18:59 +00:00
Sandy Tao d76cedb68f Implement hashing based loop detection (#4831) 2025-07-25 19:53:19 +00:00
Tommaso Sciortino 91f016d44a Make restoreCommand test windows compatible. (#4873) 2025-07-25 19:26:09 +00:00
matt korwel 820105e982 Safer Shell command Execution (#4795)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: N. Taylor Mullen <ntaylormullen@google.com>
2025-07-25 19:25:32 +00:00
Tommaso Sciortino 7ddbf97634 Fix userStartupWarnings to be windows compatible. (#4868) 2025-07-25 19:05:29 +00:00
Tommaso Sciortino f0e3e6ee8a Make shell.test.ts windows compatible (#4858) 2025-07-25 19:05:21 +00:00
christine betts 1b8ba5ca6b [ide-mode] Create an IDE manager class to handle connecting to and exposing methods from the IDE server (#4797) 2025-07-25 17:46:55 +00:00
Tommaso Sciortino 3c16429fc4 Make useCompletion.test.ts windows compatible (#4766) 2025-07-25 17:32:59 +00:00
Tommaso Sciortino f379aa833f Make errorReporting test windows compatible. (#4856) 2025-07-25 17:32:23 +00:00
Tommaso Sciortino e500eb5562 Fix read-file.test.ts to be windows compatible. (#4864) 2025-07-25 17:31:22 +00:00
Gal Zahavi 6321442865 feat(auth): Enhance non-interactive gcp auth (#4811) 2025-07-25 17:19:38 +00:00
Allen Hutchison fb0db2dfd6 adh/bugfix/1563 (#4822) 2025-07-25 16:39:37 +00:00
christine betts 1d3ad9d075 Add drawer for active files in IDE mode (#4682)
Co-authored-by: Shreya <shreyakeshive@google.com>
2025-07-25 14:50:34 +00:00
Tommaso Sciortino 5d4b02ca85 Upgrade test to work on windows. (#4815) 2025-07-25 07:15:41 +00:00
Sandy Tao 1d7eb0d250 [Refactor] Centralizes autocompletion logic within useCompletion (#4740) 2025-07-25 04:41:35 +00:00
Ramón Medrano Llamas 273e74c09d feat: add /mcp refresh command (#4566) 2025-07-25 01:14:45 +00:00
Seth Troisi e9ee686ab6 Sanitize checkpoint tags (#4813) 2025-07-24 23:05:13 +00:00
Aditya Timalsina f0400912fd docs: clarify shell command does not use default shell (#2203)
Co-authored-by: Jenna Inouye <jinouye@google.com>
Co-authored-by: N. Taylor Mullen <ntaylormullen@google.com>
2025-07-24 20:18:35 +00:00
Shreya Keshive 30b6cf8a00 Add "Gemini CLI: Run" command shortcut (#4792) 2025-07-24 19:55:39 +00:00
christine betts 4e376c0447 [ide-mode] Send the cursor and selected text from the IDE server (#4621) 2025-07-24 19:54:41 +00:00
anj-s f9930c2d36 Add support for logging the entire request and response object (#4725) 2025-07-24 18:45:53 +00:00
christine betts e05173d9cc Move vscode extension build into its own command (#4799) 2025-07-24 18:40:50 +00:00
ashwinpvg d254d4ce00 Add Google credentials provider for authenticating with MCP servers (#4748) 2025-07-24 17:37:39 +00:00
Daniel Lee 3dd6e431df feat: add GEMINI_CLI environment variable to spawned shell commands (#4791) 2025-07-24 17:13:00 +00:00
Ali Al Jufairi 52980510c9 Updated Docs to inform users about the ability to have custom theme (#4632)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-07-24 17:04:54 +00:00
Sandy Tao 0ef9c0b792 Log prompt id when a loop is detected (#4765)
Co-authored-by: N. Taylor Mullen <ntaylormullen@google.com>
2025-07-24 05:37:28 +00:00
Shreya Keshive 6380bfe35c Minor refactoring of VS Code companion extension code (#4761) 2025-07-24 05:32:55 +00:00
Thomas Burnham b1e0fb157b feat(cli): display timestamp in /chat list (#4733)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-07-24 04:48:52 +00:00
Shreya Keshive 107ce8afa3 Polish companion extension README and package.json file ahead of publishing (#4729) 2025-07-24 00:08:25 +00:00
Tommaso Sciortino e9e2f55144 Fix InputPrompt.test.tsx to be windows compatible (#4736) 2025-07-23 22:49:09 +00:00
Miguel Solorio 2e28bb90a0 Update diff colors (#4747)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-07-23 22:39:22 +00:00
Tommaso Sciortino e21b5c95aa Fix tests to work in windows (#4754) 2025-07-23 22:07:19 +00:00
Brandon Keiji d7a304bcff feat(memory): make directory search limit on memory discovery configurable with settings.json (#4460) 2025-07-23 21:48:35 +00:00
smhendrickson 9d3164621a add --telemetry-outfile flag (#4689) 2025-07-23 21:48:24 +00:00
Tommaso Sciortino 209c8783b4 Make ideCommand test windows compatible. (#4746) 2025-07-23 21:01:51 +00:00
Tommaso Sciortino 4fd7cf9177 Fix memoryDiscovery test to work in windows. (#4742) 2025-07-23 20:14:06 +00:00
Abhi bbe95f1eaa feat(commands): Implement argument handling for custom commands via a prompt pipeline (#4702) 2025-07-23 20:11:23 +00:00
Anjali Sridhar 2d1eafae95 Revert "wip"
This reverts commit 1de246236b.
2025-07-23 06:51:40 -07:00
Anjali Sridhar 1de246236b wip 2025-07-23 06:49:27 -07:00
Sandy Tao 7c3a84075d Log flash continue (#4700) 2025-07-23 01:01:24 +00:00
Sandy Tao 67008d4643 Log when flash model decided to continue (#4698)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-07-23 00:31:57 +00:00
Tommaso Sciortino 30c68922a3 Fix windows bugs in atCommandProcessor.ts (#4684) 2025-07-23 00:18:57 +00:00
HyeongHo Jun a00f1bb916 feat(core): add partUtils module with unit tests (#4575)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-07-22 23:57:06 +00:00
Werner Robitza 487debe525 add notes on API keys and .gemini/.env to authentication.md (#2004)
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
Co-authored-by: Sandy Tao <sandytao520@icloud.com>
2025-07-22 21:10:13 +00:00
Oliver Kowalke 5dce1df5db feat(core): Resolve GEMINI_SYSTEM_MD case-insensitively (#4538)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-07-22 17:23:04 +00:00
Jerop Kipruto e306b34a6e feat(auth): support gemini api key and vertex auth in non-interactive mode (#4631) 2025-07-22 14:52:40 +00:00
Brian Ray 4d653c833a MCP OAuth Part 3 - CLI/UI/Documentation (#4319)
Co-authored-by: Greg Shikhman <shikhman@google.com>
2025-07-22 14:05:36 +00:00
Brian Ray 258c848909 MCP OAuth Part 2 - MCP Client Integration (#4318)
Co-authored-by: Greg Shikhman <shikhman@google.com>
2025-07-22 13:34:56 +00:00
Tommaso Sciortino 138ff73821 Fix windows bugs + refactor tests. (#4634) 2025-07-22 05:21:37 +00:00
Abhi 9daead63dd (feat): Initial Version of Custom Commands (#4572) 2025-07-22 04:34:55 +00:00
Nick Salerni 5f813ef510 feat(ci): run e2e tests on macos during ci (#4422) 2025-07-22 04:01:20 +00:00
Pascal Birchler ffa42a79dd chore: Expand node version test matrix (#2700)
Co-authored-by: matt korwel <matt.korwel@gmail.com>
Co-authored-by: N. Taylor Mullen <ntaylormullen@google.com>
2025-07-21 16:33:54 -07:00
Marat Boshernitsan 5066bc5384 Refactor the logic for deciding whether to launch a browser into config (#4622) 2025-07-21 23:23:28 +00:00
Pascal Birchler 97cf26ec53 fix(eslint): remove custom rule in favor of eslint-plugin-import (#3012)
Co-authored-by: Sandy Tao <sandytao520@icloud.com>
2025-07-21 23:22:13 +00:00
Shreya Keshive 01ea0b8657 Address flaky integration tests with retries (#4604) 2025-07-21 22:46:00 +00:00
BOYI 12765eb775 fix: character encoding issues in shell command processor (#1949)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
Co-authored-by: Sandy Tao <sandytao520@icloud.com>
2025-07-21 22:26:40 +00:00
darkcocoa 4c3532d2b3 fix: Add warning message for token limit truncation (#2260)
Co-authored-by: Sandy Tao <sandytao520@icloud.com>
2025-07-21 21:57:11 +00:00
Josh Soref dc2ac144b7 Various spelling improvements (#3497)
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
Co-authored-by: Sandy Tao <sandytao520@icloud.com>
2025-07-21 21:54:44 +00:00
Jean-Phi Baconnais 8be5f8038a docs: add Homebrew installation instructions (#2973)
Co-authored-by: Pascal Birchler <pascalb@google.com>
2025-07-21 21:45:24 +00:00
Seydulla Narkulyyev f7b4e74932 feat(cli):suggestion-navigation-shortcut (#3641)
Co-authored-by: N. Taylor Mullen <ntaylormullen@google.com>
2025-07-21 21:43:23 +00:00
Wen Sun b4d00ab4fb feat(edit): Prevent no-op edits (#3520) 2025-07-21 21:24:02 +00:00
christine betts 1969d805f2 [ide-mode] Use active files and selected text in user prompt (#4614) 2025-07-21 20:52:02 +00:00
Tommaso Sciortino d7a57d85a3 Simplify streaming code for code assist server (#4619) 2025-07-21 20:44:43 +00:00
christine betts f95674e646 Add env var to configure system settings path (#4339) 2025-07-21 20:14:07 +00:00
anthony bushong 74d0f4c79f fix: handle cross-device issues with running otel collector (#4458) 2025-07-21 19:44:18 +00:00
christine betts 9bdcdf97d8 [ide-mode] Keep track of recently-opened files and send them to the CLI (#4463) 2025-07-21 17:54:37 +00:00
sangwook 45b764943a feat: Make file type detection and binary checks asynchronous (#3286) (#3288) 2025-07-20 23:16:42 +00:00
Seydulla Narkulyyev f4d077cc1f docs: add documentation for mcp excludeTools and includeTools (#3409)
Co-authored-by: Jack Wotherspoon <jackwoth@google.com>
2025-07-20 23:11:49 +00:00
ljxfstorm 5f8fff4db3 build(deps): Add missing resolved and integrity for all dependencies (#3501) 2025-07-20 22:52:53 +00:00
Didier Durand 0f6405e28d fix typos in diverse files (#3550)
Co-authored-by: Pascal Birchler <pascal.birchler@gmail.com>
Co-authored-by: Pascal Birchler <pascalb@google.com>
Co-authored-by: N. Taylor Mullen <ntaylormullen@google.com>
2025-07-20 22:36:34 +00:00
Yuki Okita 0996d91f0b feat(cli): add warnings when gemini-cli is called in the root directory (#4542)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: N. Taylor Mullen <ntaylormullen@google.com>
2025-07-20 21:57:09 +00:00
Abhi 2a95c8287e prefactor(commands): Command Service Prefactor for Extensible Commands (#4511) 2025-07-20 20:57:34 +00:00
Cole Miller 7a9821607b Check for zeditor if zed binary is not found (#3680)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
2025-07-20 19:35:21 +00:00
Devansh Sharma 8f85ac7de0 feat: Added /copy command for copying output to clipboard with new Command Service approach (#3706)
Co-authored-by: Abhi <43648792+abhipatel12@users.noreply.github.com>
Co-authored-by: N. Taylor Mullen <ntaylormullen@google.com>
2025-07-20 18:57:41 +00:00
Pyush Sinha a01b1219a3 feat: full implementation for .geminiignore in settings and respective tool calls (#3727) 2025-07-20 07:55:33 +00:00
Ali Al Jufairi 76b935d598 Feature custom themes logic (#2639)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-07-20 07:51:18 +00:00
osbornesec c0bfa388c5 fix: prevent RangeError in GitIgnoreParser for root paths (#3417)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-07-20 07:12:53 +00:00
N. Taylor Mullen fe00686c58 chore(release): v0.1.13 (#4515) 2025-07-19 13:40:55 -07:00
N. Taylor Mullen a464675c4d Extend mcp-server timeout. (#4514) 2025-07-19 20:28:50 +00:00
N. Taylor Mullen 412b78c5ab Simplify MCP connection errors. (#4508) 2025-07-19 12:44:51 -07:00
Yuki Okita 73d5d988f5 feat(core): display declined confirmation code diff (#4440) 2025-07-19 17:47:09 +00:00
Jacob Richman f650be2c3a Make shell output consistent. (#4469) 2025-07-19 00:30:28 +00:00
Tommaso Sciortino 4dbd9f30b6 Revert background agent commits (#4479) 2025-07-19 00:28:40 +00:00
Marat Boshernitsan 5b7b6fe608 Automatically detect non-interactive environments and fall back to a manual, code-based authentication flow (#4475) 2025-07-19 00:22:50 +00:00
Tommaso Sciortino 003609239f Add /background commands (when background agent is configured) (#4407)
Co-authored-by: Bryan Morgan <bryanmorgan@google.com>
2025-07-18 22:38:04 +00:00
Tommaso Sciortino 04bbc60b97 Demo background agent (#4409)
Co-authored-by: Bryan Morgan <bryanmorgan@google.com>
2025-07-18 22:21:46 +00:00
Shreya Keshive 73745ecd03 Display open IDE file in context section above input box rather than in the footer (#4470) 2025-07-18 22:14:46 +00:00
Sandy Tao 4915050ad4 improve command completion trigger logic based on cursor position (#4462)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-07-18 21:54:10 +00:00
Tommaso Sciortino b5f5ea2c31 Use simple name for MCP tools where possible. (#4459) 2025-07-18 21:29:09 +00:00
christine betts d7041a6595 [ide-mode] Send ping every 30 seconds to prevent client from closing connection (#4329) 2025-07-18 19:33:04 +00:00
Billy Biggs 18c3bf3a42 Summarize extensions and MCP servers on startup (#3977) 2025-07-18 18:45:00 +00:00
Sandy Tao 9dadf22958 Implement loop check with LLM (#4337)
Co-authored-by: N. Taylor Mullen <ntaylormullen@google.com>
2025-07-18 16:33:46 +00:00
Brian Ray c5761317f4 MCP OAuth Part 1 - OAuth Infrastructure (#4316) 2025-07-18 14:14:23 +00:00
Harold Mciver de27ea6095 feat(cli): allow executing commands on perfect match (#4397)
Co-authored-by: Jenna Inouye <jinouye@google.com>
2025-07-18 04:55:29 +00:00
Abhi 8497176168 (fix): broken releases and e2e workflows (#4428) 2025-07-18 04:10:58 +00:00
Yongsheng Xu 91c69731c7 feat(auth): Enhance OAuth callback for robust Docker support (#3532)
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
2025-07-18 01:55:26 +00:00
Miguel Solorio 2f5eecfc49 Light theme color improvements (#4396) 2025-07-18 00:46:33 +00:00
Keith Lyons 8ade3e7ee2 feat(ui): hide cursor when terminal is unfocused (#4012) 2025-07-18 00:45:42 +00:00
Yuki Okita 584a50a342 fix(cli): not show update avaialble messages when running gemini-cli locally (#4052) 2025-07-18 00:44:45 +00:00
Abhi ca07b5b0c4 Migrate /corgi (#4419) 2025-07-17 23:40:36 +00:00
Abhi 5df6c9fb66 migrate restore command (#4388) 2025-07-17 23:23:17 +00:00
HyeongHo Jun f0dc9690b7 Test/add loadcodeassist mock (#4287)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-07-17 22:57:54 +00:00
Pascal Birchler 4b8838bea4 fix: remove direct gaxios dependency (#4289) 2025-07-17 22:54:19 +00:00
Abdelkader Boudih 695afac33e Update ESLint and related packages to latest versions (#4296) 2025-07-17 22:52:01 +00:00
Miguel Solorio 5b7bf74d66 Add numbers to selection list (#4320) 2025-07-17 22:51:42 +00:00
goldyonatan 6aac93ee07 Fix #4220: allow up/down arrow to toggle history when only one sugges… (#4377) 2025-07-17 22:30:39 +00:00
Conrad Irwin 761ffc6338 Zed integration (#4266)
Co-authored-by: Agus Zubiaga <agus@zed.dev>
Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
Co-authored-by: mkorwel <matt.korwel@gmail.com>
2025-07-17 22:25:23 +00:00
Yun Peng 12401898f1 Fix link for checkpointing doc for --checkpointing (#4304)
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
2025-07-17 21:26:56 +00:00
warjiang 606a7702de feat(cli): add explicit proxy option in cli (#2526)
Co-authored-by: Dcatfly <dcatfly@gmail.com>
2025-07-17 18:57:37 +00:00
Ali 4ca471bac6 Fix ANSI escape crash in text buffer (#3987) (#3999)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-07-17 17:53:30 +00:00
Shreya Keshive 88b5f20943 Update companionPort not existing to be a warning so the user can still proceed with running /ide install (#4382) 2025-07-17 16:59:57 +00:00
Abhi 79d36ac0a5 (fix) - regression to quit shortcut from command migration (#4374) 2025-07-17 16:07:10 +00:00
uttamkanodia14 fc8c38b2bc Logging surface field in the start_session_event. Also logging sessio… (#4362) 2025-07-17 15:53:59 +00:00
christine betts cbda781f73 [ide-mode] Add active file to user model request (#4312) 2025-07-17 14:35:23 +00:00
Nick Salerni 0d64355be6 bug(ux): update context percentage when /clear command is run (#4162)
Co-authored-by: matt korwel <matt.korwel@gmail.com>
2025-07-17 14:14:35 +00:00
Sandy Tao ac8e98511e Fix resetting loop counts on every other event (#4348) 2025-07-17 05:32:48 +00:00
Harold Mciver 9ab44ea9d6 updated /quit to use new slash command arch (#4259)
Co-authored-by: Abhi <abhipatel@google.com>
2025-07-17 02:40:56 +00:00
Harold Mciver 01e66bb123 update /bug to new slash command arch (#4246)
Co-authored-by: Abhi <abhipatel@google.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Abhi <43648792+abhipatel12@users.noreply.github.com>
2025-07-17 01:46:35 +00:00
Shreya Keshive 0c76affe6d Minor refactoring of IDE companion server (#4331) 2025-07-17 01:03:56 +00:00
Harold Mciver fbe09cd35e update /editor to new slash command arch (#4153)
Co-authored-by: Abhi <abhipatel@google.com>
2025-07-17 00:27:36 +00:00
Shreya Keshive ab9eb9377f Add /ide status & /ide install commands to manage IDE integration (#4265) 2025-07-16 22:36:14 +00:00
Shreya Keshive 69a8ae6a89 Minor UX updates for IDE mode (#4311) 2025-07-16 21:33:56 +00:00
Wanlin Du f6ee0d182b fix: update google/genai to v1.9.0 and switch to parametersJsonSchema for MCP related tools (#4176)
Co-authored-by: Jack Wotherspoon <jackwoth@google.com>
2025-07-16 21:32:34 +00:00
Harold Mciver 21eb44b242 update /tools to new slash command arch (#4236)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: matt korwel <matt.korwel@gmail.com>
2025-07-16 20:12:22 +00:00
Shreya Keshive e4ed1aabac Include companion VS Code extension VSIX as part of build/release (#4254) 2025-07-16 19:06:39 +00:00
christine betts 34c1b5811a Revert "[ide-mode] Thread active file through to system prompt" (#4308) 2025-07-16 16:22:29 +00:00
Harold Mciver ddcac4201d update /docs to new slash command arch (#4133)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-07-16 15:56:05 +00:00
Ramon Figueiredo f4cd0055fd chore: update numbering in release-docker.yaml (#4249)
Co-authored-by: Jack Wotherspoon <jackwoth@google.com>
2025-07-16 14:47:12 +00:00
Sandy Tao cba272082d Run model availability check in the background to speed up startup (#4256) 2025-07-16 04:13:30 +00:00
Matias d622e596a1 feat(cli): clear input buffer on CTRL+C when not executing commands (#1729)
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
2025-07-16 03:35:58 +00:00
Brian Ray 0903421b1a Move MCP slash command to new system (#3678)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Abhi <abhipatel@google.com>
2025-07-16 02:35:05 +00:00
Abhi b72e3dfb43 migrate compress command (#4271) 2025-07-16 01:59:16 +00:00
Ben Guo e88b9362dc refactor: Optimize the display information of "/chat list" and "/chat resume" (#2857)
Co-authored-by: Ben Guo <hundunben@gmail.com>
2025-07-16 00:47:56 +00:00
Christian Demeke 1d67b41ccd Clarifies that Gemini Code Assist, when offered through the Google De… (#3086) 2025-07-15 23:51:48 +00:00
Lee Won Jun ec5e9d1025 Improve altName completion behavior in slash commands (#4227)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-07-15 23:45:10 +00:00
Elvin 615748657a Update MaxSizedBox.tsx (#2233)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
Co-authored-by: Pascal Birchler <pascalb@google.com>
2025-07-15 23:35:03 +00:00
christine betts 222e362fc2 [ide-mode] Thread active file through to system prompt (#4264) 2025-07-15 22:20:00 +00:00
christine betts b61016f2a5 Set port dynamically in VSCode extension and read from it in gemini-cli and send initial notification (#4255) 2025-07-15 22:13:03 +00:00
Harold Mciver bf51de1a4d update /extensions to new slash command arch (#4229)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-07-15 21:40:09 +00:00
christine betts 58f1aa6ceb Add support for allowed/excluded MCP server names in settings (#4135)
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
2025-07-15 20:45:24 +00:00
Harold Mciver 03b3917f62 updated /stats to use new slash command arch (#4146) 2025-07-15 20:10:04 +00:00
N. Taylor Mullen 8d9dc44b71 Revert "Update to yargs v18 (#3759)" (#4252) 2025-07-15 12:36:54 -07:00
Shreya Keshive 776d6b6ea0 Upload VSIX of companion VS Code extension (#4241)
Co-authored-by: matt korwel <matt.korwel@gmail.com>
2025-07-15 18:44:03 +00:00
Pascal Birchler af551f4a41 Revert Node <20 warning (#4244) 2025-07-15 18:42:27 +00:00
Sandy Tao 7b49560265 Include api key in header instead of in the URL (#4240) 2025-07-15 18:37:14 +00:00
Sambhav Khanna 40c4070846 feat(tool): sort tool list alphabetically for deterministic output (#3095)
Co-authored-by: Pascal Birchler <pascalb@google.com>
2025-07-15 18:35:35 +00:00
anj-s d3ee9de3c3 Enable tool summarization only when explicitly set in settings.json (#4140)
Co-authored-by: matt korwel <matt.korwel@gmail.com>
2025-07-15 17:22:31 +00:00
christine betts 7effdad3e2 [ide-mode] Stream notifications when the active file changes (#4147)
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
2025-07-15 16:19:22 +00:00
Shreya Keshive b09bc66560 Adds the user's active file in the IDE to the footer (#4154) 2025-07-15 14:19:59 +00:00
Jvr 97cc1e6418 Fix: Standardize capitalization for "Troubleshooting Guide" in README (#2763)
Co-authored-by: Pascal Birchler <pascalb@google.com>
2025-07-15 09:50:54 +00:00
Devansh Sharma 123c3e7c7f feat: add a warning that shows if user uses node -v <20 #2930 (#3371)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-07-15 09:49:46 +00:00
Srinath Padmanabhan f5d5213504 Roadmap release (#4178)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: matt korwel <matt.korwel@gmail.com>
2025-07-15 07:25:35 +00:00
Abhi 6e258e2850 migrate /about (#4207) 2025-07-15 06:22:46 +00:00
Pascal Birchler 2862ae7344 Update to yargs v18 (#3759) 2025-07-15 06:09:12 +00:00
Tommaso Sciortino fefa7ecbea Pure refactor: Consolidate isWithinRoot() function calling. (#4163) 2025-07-15 05:55:49 +00:00
Abhi e584241141 Migrate /privacy to new architecture (#4202) 2025-07-15 05:45:06 +00:00
Sandy Tao 886faa2990 Log the 2 types of loop detection (#4193) 2025-07-15 04:44:07 +00:00
Sandy Tao 734da8b9d2 Introduce loop detection service that breaks simple loop (#3919)
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
Co-authored-by: N. Taylor Mullen <ntaylormullen@google.com>
2025-07-15 03:25:16 +00:00
Sandy Tao 7ffe8038ef Make @ command sort file without extension name (#4158) 2025-07-14 21:10:26 +00:00
Bryan Morgan ff3722a3a7 Fix circular reference JSON serialization in telemetry logging (#4150) 2025-07-14 20:20:06 +00:00
Tommaso Sciortino 5008aea90d Refactor MCP code for reuse and testing (#3880) 2025-07-14 18:19:33 +00:00
Abhi 9dc812dd4b fix(checkpoint): Prevent silent failure and enable for non-Git projects (#4144) 2025-07-14 17:23:51 +00:00
Tommaso Sciortino 2f1d6234de Don't start uncompressed history with a function response (#4141) 2025-07-14 17:09:11 +00:00
haroldmciver-go c313c3dee1 updated '/auth' to use new slash command arch (#3797)
Co-authored-by: Abhi <43648792+abhipatel12@users.noreply.github.com>
2025-07-14 16:22:37 +00:00
Pascal Birchler 80c81f2a4c fix(docs): clarify global installation in readme (#3781) 2025-07-14 16:21:40 +00:00
Shreya Keshive fadc477001 Add feature flag for IDE integration (#3927)
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
2025-07-14 16:04:08 +00:00
christine betts e9d680e8a4 Introduce VSCode companion extension (#3917) 2025-07-14 15:34:44 +00:00
Billy Biggs 64f1d80b26 Add documentation for the /extensions command (#4051)
Co-authored-by: Bryan Morgan <bryanmorgan@google.com>
2025-07-14 14:33:05 +00:00
Will 保哥 34bbfa0e15 Fix markdown heading level issue (#3381) 2025-07-14 12:13:43 +00:00
Billy Biggs bc4182b9d2 Show stderr output from mcp servers in debug mode (#4049) 2025-07-14 04:42:22 +00:00
Nick Popovic c7840966e2 Fix duplicate help text for build targets in Makefile (#4092) 2025-07-14 04:42:07 +00:00
K 3110e8f810 feat(cli): add hideBanner setting to disable startup banner (#2803)
Co-authored-by: Pascal Birchler <pascalb@google.com>
2025-07-14 04:37:31 +00:00
Abhi 8d0a4082a4 Fix(ci): Correct container publishing pipeline and improve robustness (#4093) 2025-07-14 04:19:58 +00:00
Billy Biggs ef8ec98489 Add back support for escaping newline with a \ character (#4064) 2025-07-14 03:34:20 +00:00
N. Taylor Mullen b018e2d3ad Fix docker release yaml to use correct commands. (#4025) 2025-07-12 23:46:49 -07:00
N. Taylor Mullen 8cf7f530e1 chore(release): v0.1.12 (#4023) 2025-07-12 23:09:54 -07:00
Sandy Tao b29c02dd34 Fix not using flash for next speaker check (#4016) 2025-07-13 05:12:47 +00:00
N. Taylor Mullen 44ef0408f3 feat(tools): Centralize shell tool summarization (#4009) 2025-07-13 04:09:12 +00:00
N. Taylor Mullen 09a3b7d5e1 feat: Invert scroll arrow default in RadioButtonSelect (#4006) 2025-07-13 03:58:00 +00:00
N. Taylor Mullen 26a79fec25 feat: Add GEMINI_DEFAULT_AUTH_TYPE support (#4002) 2025-07-13 03:43:35 +00:00
N. Taylor Mullen 4442e893c3 fix(auth): Remove sharp edges from headless auth (#3985) 2025-07-12 15:42:47 -07:00
Yuki Okita 890982a811 fix(core): make the commented-out test workable (#3885) 2025-07-12 19:13:22 +00:00
Isuru Gunarathne b3cbde5cf3 Fix typo in README.md regarding Vertex AI API key (#3946)
Co-authored-by: Abhi <43648792+abhipatel12@users.noreply.github.com>
2025-07-12 07:23:09 +00:00
Abhi 642d2e8d51 ensure system instruction also uses <state_snapshot> (#3951) 2025-07-12 06:15:56 +00:00
Jayson Dasher c9e194ec6a feat: Add clipboard image paste support for macOS (#1580)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
2025-07-12 04:06:49 +00:00
Jacob Richman c4ea17692f Fix extra whitespace in markdown rendering. (#3943)
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
2025-07-12 03:38:07 +00:00
Miguel Solorio d89ccf2250 Add scrolling to theme dialog (#3895)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-07-12 01:05:21 +00:00
Jacob Richman 82bde57868 Fix issues that resulted in Gemini trying to use relative paths where absolute paths were required (#3938) 2025-07-12 00:49:26 +00:00
N. Taylor Mullen 6ebe97c704 chore(release): v0.1.11 (#3939) 2025-07-12 00:33:17 +00:00
Daniel Lee 5b6608ad84 feat(cli): add support for --prompt-interactive/-i flag (#1743) 2025-07-11 23:52:56 +00:00
uttamkanodia14 5b5f496436 Adds Flash Fallback logging and clearcut logging (#3843) 2025-07-11 21:10:25 +00:00
Gaurav 764809753a fix: TypeError: Cannot read properties of undefined (reading 'authType') (#3914) 2025-07-11 21:08:49 +00:00
christine betts a071f604e3 Remove unused demo file (#3886) 2025-07-11 21:05:39 +00:00
Seth Troisi eab47b9131 Add oauth test (#3916) 2025-07-11 21:05:27 +00:00
Miguel Solorio 448838dea8 Add visual cues for nightly version (#3701)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-07-11 20:43:57 +00:00
haroldmciver-go 4197f30278 update /theme to new slash command arch (#3791)
Co-authored-by: matt korwel <matt.korwel@gmail.com>
Co-authored-by: Abhi <43648792+abhipatel12@users.noreply.github.com>
2025-07-11 20:01:28 +00:00
Jack Wotherspoon 2826c7a1c6 feat: add headers support to SSE transport MCP servers (#3902) 2025-07-11 19:59:42 +00:00
Gaurav 8f12e8a114 fix: clearcut logging (retry #3744) (#3751) 2025-07-11 17:57:35 +00:00
uttamkanodia14 93284281de Logs the auth type in the user prompts, api responses and errors (#3795) 2025-07-11 16:47:46 +00:00
Pascal Birchler ed00612cf7 chore: pin GitHub Actions to SHAs (#2987)
Co-authored-by: Jack Wotherspoon <jackwoth@google.com>
2025-07-11 16:46:06 +00:00
anj-s 23197151c2 Summarize tool call outputs using tool specific summarizers (#3745) 2025-07-11 16:29:08 +00:00
Bryan Morgan cdbe2fffd9 Added in proper checks for customer tiers in 429/Quota error messaging (#3863)
Co-authored-by: Ioannis Papapanagiotou <iduckhd@hotmail.com>
2025-07-11 15:25:30 +00:00
anj-s c9e1e6d3bd Add support for specifying maxSessionTurns via the settings configuration (#3507) 2025-07-11 14:55:03 +00:00
anj-s 0151a9e1a3 Remove extra logging in debug mode (#3864) 2025-07-11 14:43:51 +00:00
Tommaso Sciortino 166f5eaa66 Don't print MCP server logs. It's too noisy. (#3867) 2025-07-11 14:42:43 +00:00
Ioannis Papapanagiotou 62ebd51e11 docs: correct typo in CONTRIBUTING.md (#3852) 2025-07-11 13:29:05 +00:00
chrisheecho e8fd2d6147 Update authentication.md to show how to use API keys in GCP (#3042)
Co-authored-by: Pascal Birchler <pascalb@google.com>
2025-07-11 13:25:11 +00:00
Max Bain a634e03177 docs: Include video and audio in multi-file tool documentation (#3380)
Co-authored-by: Pascal Birchler <pascalb@google.com>
2025-07-11 13:22:43 +00:00
Jinhwan Kim 9195a1c026 Fix typo in authentication.md (#3348) 2025-07-11 13:04:42 +00:00
trapezoid daed8b0f90 chore(deps): Pin @google/genai to 1.8.0 (#3834)
Co-authored-by: matt korwel <matt.korwel@gmail.com>
2025-07-11 06:58:32 +00:00
Masato Sugiyama 8d054898af chore: remove unused ink-text-input dependency (#2388) 2025-07-11 05:32:02 +00:00
Seth Troisi 8a128d8dc6 Add NO_BROWSER environment variable to trigger offline oauth flow (#3713) 2025-07-11 01:59:02 +00:00
Billy Biggs ab66e3a24e Work around bracketed paste support for node < 20 (#2476)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-07-10 19:56:34 +00:00
matt korwel 75a128e7ee chore(release): v0.1.10 (#3749)
Co-authored-by: Gaurav <39389231+gsquared94@users.noreply.github.com>
Co-authored-by: Aryan Sawant <156219699+aryanjsawant@users.noreply.github.com>
Co-authored-by: neo.alienson <neo@01man.com>
2025-07-10 16:55:22 +00:00
Tommaso Sciortino 870797c16c Indent subcommands in help output (#3703) 2025-07-10 16:21:20 +00:00
Mithlesh kumar bf508bfd77 Cleanup: Removed duplicate guidelines prompt (#3741) 2025-07-10 16:04:59 +00:00
neo.alienson 5ecc13729a Fix invalid docker command and invalid JSON in the mcpServers example. (#3672) 2025-07-10 08:33:04 +00:00
Aryan Sawant 316c0fa37b Fix Patch for grep.test.ts (#3747) 2025-07-10 06:08:58 +00:00
matt korwel 58607b92df Revert "fix: Use Email for Clearcut Logging and Refactor User Info Fetching" (#3744) 2025-07-09 21:51:37 -07:00
Gaurav b7f8e1360f fix: Use Email for Clearcut Logging and Refactor User Info Fetching (#3620) 2025-07-10 04:17:40 +00:00
christine betts da50a1eefb Add system-wide settings config for administrators (#3498)
Co-authored-by: Jack Wotherspoon <jackwoth@google.com>
2025-07-09 21:16:42 +00:00
uttamkanodia14 063481faa4 Adding TurnId to Tool call and API responses and error logs. (#3039)
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
2025-07-09 18:49:30 +00:00
Tyler 6c12f9e0d9 Use yargs array type for the allowedMcpServerNames flag instead of processing the list directly ourselves. (#3600) 2025-07-09 18:38:38 +00:00
Pascal Birchler 017a0a6c86 fix(gha): only post coverage comment for 22.x (#3613) 2025-07-09 18:25:48 +00:00
N. Taylor Mullen 725b23e37a Revert "chore(deps): bump google-auth-library from 9.15.1 to 10.1.0 (… (#3676) 2025-07-09 18:19:28 +00:00
N. Taylor Mullen b3d3f40115 Revert "chore(deps): Add Dependabot config (#2972)" (#3675) 2025-07-09 18:17:04 +00:00
Bryan Morgan 8a6509ffeb Remove auto-execution on Flash in the event of a 429/Quota failover (#3662)
Co-authored-by: Jenna Inouye <jinouye@google.com>
2025-07-09 17:55:56 +00:00
Ed Harrod 01e756481f mcp-server: Fix debug flag (#3667) 2025-07-09 17:52:49 +00:00
Jenna Inouye d5db4f0b93 Update Terms of Service and Privacy Notice for clarity. (#3036) 2025-07-09 16:23:58 +00:00
Bryan Morgan b0cce95286 Improve quota- and resource-related 429 error handling, also taking Code Assist customer tiers into consideration (#3609) 2025-07-09 14:18:15 +00:00
Pascal Birchler 8f2da86aa5 Use full terminal width for --help (#3515)
Co-authored-by: N. Taylor Mullen <ntaylormullen@google.com>
2025-07-09 07:02:59 +00:00
N. Taylor Mullen d094026b3b fix(deps): revert yargs bump and fix npx regression (#3610) 2025-07-08 23:44:56 -07:00
dependabot[bot] 95782b7b47 chore(deps): bump google-auth-library from 9.15.1 to 10.1.0 (#3583)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-09 06:01:18 +00:00
DaeYeong Kim 0e13b7f0c6 docs(contributing): mention macOS Seatbelt in GEMINI_SANDBOX examples (#3537)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-07-09 06:01:16 +00:00
dependabot[bot] c5abd7a302 chore(deps): bump dotenv from 16.6.1 to 17.1.0 (#3589)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-09 06:01:06 +00:00
dependabot[bot] 52db1b83a0 chore(deps): bump ws from 8.18.2 to 8.18.3 (#3581)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-09 05:47:51 +00:00
Principal-Ideal 4d882d9b58 chore: fix typo (#3570) 2025-07-09 05:46:55 +00:00
dependabot[bot] 5a50958f28 chore(deps): bump dorny/test-reporter from 1 to 2 (#3575)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Abhi <43648792+abhipatel12@users.noreply.github.com>
2025-07-09 05:46:24 +00:00
dependabot[bot] d787f1b3de chore(deps): bump actions/create-github-app-token from 1 to 2 (#3576)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-09 05:46:14 +00:00
dependabot[bot] b12149be7c chore(deps): bump mime-types and @types/mime-types (#3582)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-09 05:44:40 +00:00
dependabot[bot] 9089107a8d chore(deps-dev): bump esbuild from 0.25.5 to 0.25.6 (#3586)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-09 05:42:49 +00:00
dependabot[bot] bedecc03f4 chore(deps-dev): bump globals from 16.2.0 to 16.3.0 (#3587)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-09 05:42:23 +00:00
dependabot[bot] f6cd65190f chore(deps): bump yargs from 17.7.2 to 18.0.0 (#3590)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-09 05:40:56 +00:00
dependabot[bot] 698ec8172c chore(deps): bump gaxios from 6.7.1 to 7.1.1 (#3592)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-09 05:39:42 +00:00
Tommaso Sciortino 9273e8ddae Fix version of shell-quote (#3557) 2025-07-09 00:40:49 +00:00
Pascal Birchler 3e157a2331 chore(deps): Add Dependabot config (#2972) 2025-07-09 00:23:59 +00:00
Pascal Birchler 2916753409 chore: add CodeQL analysis (#2992) 2025-07-09 00:23:51 +00:00
Pascal Birchler c8cf954e6e fix(auth): do not blindly default to API key auth (#3235)
Co-authored-by: matt korwel <matt.korwel@gmail.com>
Co-authored-by: N. Taylor Mullen <ntaylormullen@google.com>
2025-07-08 22:10:36 +00:00
Sandy Tao 0506b40a39 Fix bad request in model check (#3568) 2025-07-08 22:06:47 +00:00
Jack Wotherspoon 27a2d8af14 refactor: consolidate all flags to use hyphens (deprecate underscore flags) (#3541) 2025-07-08 20:56:12 +00:00
Marat Boshernitsan 2ed1b378cb fix: Honor DEBUG and CLI_TITLE environment variables (#3560) 2025-07-08 19:34:17 +00:00
Billy Biggs c0940a194e Add a command line option to enable and list extensions (#3191) 2025-07-08 16:57:34 +00:00
Marat Boshernitsan f1647d9e19 Improve auth env var validation logic and messaging to detect settings that confuse GenAI SDK (#1381)
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-07-08 16:37:10 +00:00
Tommaso Sciortino 5c759d48c7 Don't enforce leading slash since that's not true on Windows (#3545) 2025-07-08 16:30:49 +00:00
Tommaso Sciortino 4dab31f1c8 Improve Function Call argument validation and typing (#2881)
Co-authored-by: N. Taylor Mullen <ntaylormullen@google.com>
2025-07-08 06:48:44 +00:00
matt korwel 137ffec3f6 Fix nightly Release (#3511) 2025-07-08 06:41:39 +00:00
Tommaso Sciortino 0c70a99b56 Preserve recent history when compressing. (#3049)
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
2025-07-08 06:32:09 +00:00
Ayesha Shafique 23e3c7d6ec style: Format execution time as minutes, seconds (#2707) 2025-07-08 06:14:42 +00:00
Sandy Tao f7ad9a7e47 Fix infinite loop in start.js on Windows (#3506) 2025-07-08 03:49:22 +00:00
warjiang a34cc6124c ci: disable scheduled jobs in forked repo (#3093)
Co-authored-by: Jack Wotherspoon <jackwoth@google.com>
2025-07-08 01:14:39 +00:00
Jack Wotherspoon ba58e077eb chore: add general usage message to --help message (#3500) 2025-07-08 01:13:31 +00:00
matt korwel a4097ae6f9 Release and Packaging: Clean up (#3489) 2025-07-07 23:36:51 +00:00
Tommaso Sciortino 4e84989d8f Fix double "esc" bug in Auth dialog (#3493) 2025-07-07 23:27:24 +00:00
Tommaso Sciortino 426b6905da Fix typo and add tests for auth validation. (#3491) 2025-07-07 22:52:04 +00:00
Marat Boshernitsan 48c2aa296a Enable Gemini CLI to reuse user's auth in Cloud Shell (#3070) 2025-07-07 22:02:13 +00:00
Tommaso Sciortino 357546a2aa Initialize MCP tools once at start up instead of every time we auth. (#3483) 2025-07-07 22:01:59 +00:00
Abhi aa10ccba71 feature(commands) - Refactor Slash Command + Vision For the Future (#3175) 2025-07-07 20:45:44 +00:00
Tyler 6eccb474c7 refactor: rename allowed_mcp_server_names to allowed-mcp-server-names (#3469) 2025-07-07 19:47:27 +00:00
Sambhav Khanna 97a472f2fb fix(cli): Prevent Tab from auto-executing incomplete slash commands (#2919)
Co-authored-by: matt korwel <matt.korwel@gmail.com>
2025-07-07 19:43:27 +00:00
Tommaso Sciortino 2b135d0e9e Remove unneeded code. (#3467)
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
2025-07-07 19:35:29 +00:00
chrisheecho 7cc84cd6af Update README.md to show API key usage for Vertex (#3060)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-07-07 18:43:27 +00:00
Colt McAnlis 8f4046c71a fix: EditTool can clobber human edits to the same file. (#3043)
Co-authored-by: Colt McAnlis <colton@google.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2025-07-07 17:28:56 +00:00
Tyler 229ae03631 Add --allowed_mcp_server_names flag (#3464) 2025-07-07 16:45:58 +00:00
Jordan Demeulenaere 355fb4ac67 Add excludeTools and includeTools to mcpServers config (#2976) 2025-07-07 16:34:26 +00:00
Gennadiy Civil aa8e5776eb Add new test to verify that when an Authorization header is provided (#3023) 2025-07-07 16:34:16 +00:00
N. Taylor Mullen 17dfa267d5 Re-enable backticks in shell tool usage. (#3360) 2025-07-07 16:15:10 +00:00
Jerop Kipruto 48ebd728b3 Migrate Gemini CLI Action workflows to Direct WIF authentication (#3456) 2025-07-07 16:06:31 +00:00
Pugazhendhi 524ede52d2 feat: add .svg support (#3229) 2025-07-07 05:51:32 +00:00
zfflxx 97d9386e3f @file don't respect config respectGitIgnore=false (#3382) (#3387)
Co-authored-by: Ryan Fang <ryan.fang@gllue.com>
2025-07-07 05:48:39 +00:00
zfflxx bb8f6b376d Fix nested markdown Rendering for table headers and rows #3331 (#3362)
Co-authored-by: Ryan Fang <ryan.fang@gllue.com>
2025-07-07 05:33:46 +00:00
Jack Wotherspoon b70fba5b09 fix: respect env variables in .env for settings.json variable substitution (#3416) 2025-07-07 05:13:13 +00:00
Yuki Okita 87a44ec468 feat(core): improve error messages in isCommandAllowed (#3349) 2025-07-07 05:03:36 +00:00
matt korwel 20825e4114 Release misc (#3418) 2025-07-07 03:16:42 +00:00
Devansh Sharma 39d4095a4c feat: YOLO mode shorctut displayed inside /help (#3367) 2025-07-06 15:48:29 +00:00
Pyush Sinha 39e8509452 feat: add user startup warnings, add home directory check (#3056) 2025-07-06 06:27:00 +00:00
N. Taylor Mullen da9b1baa6e Update @google/genai -> 1.8.0 (#3339) 2025-07-06 01:10:57 +00:00
Adam Weidman 9211905ff1 feat: Handle inline content modification in tool scheduler (#2883) 2025-07-05 23:19:41 +00:00
Daniel Sibaja 2b8a565f89 Fix #2922: Prevent @ concatenation to valid paths in shellmode. (#2932) 2025-07-05 22:20:12 +00:00
BigUncle b564d4a088 fix(core): Sanitize tool parameters to fix 400 API errors (#3300) 2025-07-05 21:58:51 +00:00
matt korwel 5c9372372c cleaning up prompts for release (#3335) 2025-07-05 21:20:47 +00:00
matt korwel a7256f630c Relase: Clean up and condensing (#3321) 2025-07-05 20:58:59 +00:00
N. Taylor Mullen 4be32d1f73 fix(cli): Group cancelled tool call responses to prevent API errors (#3333) 2025-07-05 20:56:39 +00:00
이동현 8adc586973 fix: small typo (#3183)
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
2025-07-05 15:33:42 +00:00
matt korwel 4963a1eea8 Mk nohup (#3285) 2025-07-05 15:27:22 +00:00
Didier Durand ab96676e36 fix typos in diverse files (#3284)
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
2025-07-05 15:23:39 +00:00
matt korwel 47dc16d243 feat: Update minimum Node.js version to 20 (#3277) 2025-07-05 14:55:15 +00:00
Ned Nguyen 80aad5a42c Doc: update gemini-cli README.md to require Node.js version 20+ (#3247)
Co-authored-by: matt korwel <matt.korwel@gmail.com>
2025-07-05 02:44:23 +00:00
Will 保哥 c94fcd1094 fix a command usage issue in deployment.md (#2862)
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
2025-07-05 02:05:56 +00:00
matt korwel 7aa67f324c Mk sign nightly release commits (#3264) 2025-07-05 01:32:58 +00:00
matt korwel e90e0015ea Signing tags (#3254) 2025-07-04 22:04:05 +00:00
matt korwel 9ff3592e01 Mk nightly relase tag formatting (#3206) 2025-07-04 16:30:29 +00:00
Jack Wotherspoon 806d858c45 ci: update issue templates to use GitHub alert (#3167) 2025-07-04 15:59:09 +00:00
Mithlesh kumar f4923468dc chore: typo fixes (#3203)
Co-authored-by: Jack Wotherspoon <jackwoth@google.com>
2025-07-04 15:38:43 +00:00
matt korwel ef736f0d1c Mk nightly relase tag formatting (#3204) 2025-07-03 22:44:04 -07:00
matt korwel adc63e6882 fix tagging for nightly (#3202) 2025-07-04 05:16:35 +00:00
matt korwel d43ea268b0 Releasing: Utilizing Github Actions and Tagging for release. (#2852)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: N. Taylor Mullen <ntaylormullen@google.com>
2025-07-04 03:57:01 +00:00
Sandy Tao 32db5ba0e1 Refactor text-buffer to use reducer (#2652) 2025-07-04 00:53:17 +00:00
moon jooho 8d3fec08e5 Add and improve JSDoc comments for core tool methods (#3128) 2025-07-04 00:13:02 +00:00
Bryan Morgan 654f8aeb61 Fixed Google User Id pass to Clearcut (#3147) 2025-07-03 20:54:35 +00:00
SunskyXH ab63a5f183 fix(client): get model from config in flashFallbackHandler (#2118)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-07-03 19:43:48 +00:00
Devansh Sharma 64767c52fe fix: show ctrl+s shortcut to expand debug console #2002 (#2491)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-07-03 19:42:02 +00:00
Fausto Núñez Alberro dcb9381138 Use AccentBlue: 'blue' in ANSI theme instead of hard-coded #0000FF (#3100)
Co-authored-by: Riccardo Carlesso <palladiusbonton@gmail.com>
2025-07-03 14:32:47 +00:00
moon jooho a68f28e5cc fix: remove unnecessary whitespace (#2781)
Co-authored-by: F. Hinkelmann <franziska.hinkelmann@gmail.com>
2025-07-03 10:22:32 +00:00
Taeik Lim 5057502e8d docs: fix typos in CONTRIBUTING.md (#2722)
Signed-off-by: Taeik Lim <sibera21@gmail.com>
Co-authored-by: F. Hinkelmann <franziska.hinkelmann@gmail.com>
2025-07-03 09:46:23 +00:00
Jerop Kipruto 85a1d814a7 refactor(ci): improve pr triage (#3082) 2025-07-03 02:45:34 +00:00
Jerop Kipruto b463249729 feat(workflows): add automated and scheduled PR triage (#3062) 2025-07-03 00:41:23 +00:00
Michael Carolin e7b0b49c82 Fix typo in README (#3061) 2025-07-02 23:58:38 +00:00
Eddie Santos 82a0ac3d1e Update notification template (#3035) 2025-07-02 20:26:14 +00:00
Abhi edd69cb7d4 help: add shift+tab tip (#2892) 2025-07-02 03:18:01 +00:00
Vachan cd069fd436 Reduce the threshold for when we compress history. (#2898) 2025-07-02 00:18:13 +00:00
Seth Troisi 38445f63f0 make tag required for /chat (#2904) 2025-07-02 00:17:08 +00:00
Brandon Keiji 34935d6558 chore: bump version to 0.1.9 (#2906) 2025-07-01 23:30:04 +00:00
Bryan Morgan dbe88f6e0e Added support for session_id in API calls (#2886) 2025-07-01 23:16:09 +00:00
Tommaso Sciortino 3492c429b9 Add excludedTools to extensions. (#2853) 2025-07-01 23:13:46 +00:00
Allen Hutchison e94decea39 feat(core): Add infinite loop protection to client (#2793) 2025-07-01 23:09:21 +00:00
Billy Biggs 3a995305c0 Fix characters being dropped in text-buffer (#2504)
Co-authored-by: Sandy Tao <sandytao520@icloud.com>
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-07-01 23:07:41 +00:00
Tommaso Sciortino 82afc75350 Special case mime type for ts file. (#2902) 2025-07-01 23:05:33 +00:00
Seth Troisi 383306e17e Improve slashCommand autoCompletion logic (#2776) 2025-07-01 22:51:43 +00:00
Preston Holmes 8957ad7fc3 Docs: Add a page detailing quota and cost information (#2894)
Co-authored-by: Jenna Inouye <jinouye@google.com>
2025-07-01 22:28:15 +00:00
Santhosh Kumar 0275ab0108 feat: add audio and video support to read_file (#2556) 2025-07-01 19:22:32 +00:00
Logan Kilpatrick 63ed8d6499 Update README.md (#2729)
Co-authored-by: Allen Hutchison <adh@google.com>
2025-07-01 19:16:40 +00:00
Tommaso Sciortino fe125d59b9 Use the constant placeholders for ".gemini/settings.json" in gemini.tsx (#2860) 2025-07-01 18:00:21 +00:00
Jerop Kipruto 58b14b7ccf feat: add weekly community report workflow (#2855) 2025-07-01 16:19:51 +00:00
Jacob Richman 2bf8e8b2c7 Fix spurious logs about invalid MaxSizedBox children due to Ink6 + React19 migration (#2794) 2025-07-01 15:54:27 +00:00
MirzaSamadAhmedBaig 01186e3aff Make clean script cross-platform (#1990)
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
2025-07-01 15:30:18 +00:00
Tian Jian Wang a4062cb44a feat: Add markdown table rendering support (#1955)
Co-authored-by: heartyguy <heartyguy@users.noreply.github.com>
Co-authored-by: Allen Hutchison <adh@google.com>
2025-07-01 03:25:19 +00:00
Hemant 1a30b9656f docs(auth): clarify env-file discovery & recommend (#2402) 2025-07-01 03:23:48 +00:00
Eddie Santos e5b1208bd8 update check + tests (#2772) 2025-07-01 03:03:16 +00:00
Jerop Kipruto e10c208fbe feat(triage): improve automated issue triage workflows (#2778) 2025-07-01 02:59:46 +00:00
Jvr 84355bb447 Fix: Use HTTPS in docs and correct formatting typo in troubleshooting guide (#2762)
Co-authored-by: Seth Troisi <sethtroisi@google.com>
2025-07-01 00:39:45 +00:00
Abhi f91927569c refactor(ui): revamp exit stats display (#2771) 2025-07-01 00:28:49 +00:00
Tommaso Sciortino 3587054d32 Rename AuthType LOGIN_WITH_GOOGLE_PERSONAL -> LOGIN_WITH_GOOGLE (#2769) 2025-07-01 00:11:54 +00:00
Tommaso Sciortino 0ca5c07135 Use structured prompt for compression. (#2747) 2025-07-01 00:04:33 +00:00
Tommaso Sciortino dbd626054f Remove unused method (#2721) 2025-06-30 22:53:05 +00:00
owenofbrien f19b9ed4f8 Removed fallback logic for gaia id logging (#2761) 2025-06-30 22:51:17 +00:00
Tommaso Sciortino 505a5d617b Fix CODE_ASSIST_ENDPOINT env var. (#2712) 2025-06-30 22:41:14 +00:00
Jerop Kipruto 36e099ac22 fix(workflows): use preview release gemini-cli in triage workflows (#2759) 2025-06-30 22:34:08 +00:00
Jerop Kipruto 09d494d174 feat(workflows): add issues list command to automated triage workflow (#2749) 2025-06-30 21:04:48 +00:00
Jerop Kipruto 9794d329d3 refactor(workflows): separate issue triage into two workflows (#2746) 2025-06-30 20:30:22 +00:00
Tommaso Sciortino 5c4c833ddd Fix oauth credential caching. (#2709) 2025-06-30 15:47:01 +00:00
Jerop Kipruto f3849627fc feat(shell): Enable prefix matching for flexible command validation (#2653) 2025-06-30 15:42:35 +00:00
Abhi 770f862832 feat: Change /stats to include more detailed breakdowns (#2615) 2025-06-30 00:44:33 +00:00
Adam Spiers 0fd602eb43 feat: add support to remote MCP servers for custom HTTP headers (#2477) 2025-06-30 00:09:08 +00:00
Marcin Jahn d1eb86581c feat(cli): Add hideTips setting (#1524)
Co-authored-by: Allen Hutchison <adh@google.com>
2025-06-29 23:56:37 +00:00
Faizan Alam 1732e90d52 Highlight previous user input (#2507)
Co-authored-by: Allen Hutchison <adh@google.com>
2025-06-29 23:28:56 +00:00
Devansh 2096f971cd fix:Update /help to show correct newline key combo for different OS #… (#2043)
Co-authored-by: Allen Hutchison <adh@google.com>
2025-06-29 23:16:14 +00:00
Niladri Das f848d35758 feat: modular GEMINI.md imports with @file.md syntax (#1585) (#2230) 2025-06-29 22:51:47 +00:00
uttamkanodia14 ada4061a45 Fix clearcut-logger.ts to event name GEMINI_CLI_API_RESPONSE_TOOL_TOK… (#1875) 2025-06-29 22:14:13 +00:00
Bryan Morgan cdb803b9a4 Added obfuscated google account ID to clearcut log messages (#2593) 2025-06-29 20:35:20 +00:00
Tim Rogers dbe63e7234 fix: Correct pluralization of the number of occurrences in EditTool tool errors (#2463) 2025-06-29 19:53:59 +00:00
Adam Spiers 0cfaeedf03 chore: add .editorconfig (#2572) 2025-06-29 19:40:32 +00:00
Jerop Kipruto d8d78d73f9 feat: allow command-specific restrictions for ShellTool (#2605) 2025-06-29 19:32:26 +00:00
Noritaka Kobayashi 19a0276142 refactor: remove unnecessary "await" (#2574) 2025-06-29 19:15:27 +00:00
Noritaka Kobayashi 9ae2595bfd refactor: remove unnecessary assertion (#2579) 2025-06-29 19:06:03 +00:00
Umair Idris 65a58c3b03 Clarify .gemini/config.yaml is for the PR review bot (not CLI). (#2495) 2025-06-29 18:54:23 +00:00
Will 保哥 51eba66528 Fix a heading issue for Authentication Setup doc (#2592) 2025-06-29 18:45:19 +00:00
Will 保哥 91502193ec Fix a broken link (#2598) 2025-06-29 18:43:27 +00:00
yuki yano c860dac233 feat: add Neovim editor support (#1448) 2025-06-29 17:25:22 +00:00
Zircoz 87d4fc0560 docs: Add uninstallation instructions to README (#1985)
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Co-authored-by: Allen Hutchison <adh@google.com>
Co-authored-by: Allen Hutchison <allen@hutchison.org>
2025-06-29 13:41:58 +00:00
Noritaka Kobayashi b980a47879 refactor: remove imported multiple times (#1846) 2025-06-29 09:09:15 +00:00
Noritaka Kobayashi e188daab91 refactor: use for...of loop instead of traditional for loop (#1840) 2025-06-29 08:53:09 +00:00
Tommaso Sciortino fc21d1cae3 Esc to exit privacy screen in error state (#2527) 2025-06-29 07:50:53 +00:00
Ahmad Awais ⌘ 19a9b50aab 📦 NEW: Theme Shades of Purple (#2114)
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
2025-06-29 03:43:17 +00:00
anj-s 7b86a2015f Remove debug logs that are not actionable but numerous (#2030) 2025-06-29 02:33:53 +00:00
Krushna Sharma 33bfda9879 docs: fix typos and grammatical errors (#2459)
Co-authored-by: Allen Hutchison <adh@google.com>
2025-06-29 01:23:49 +00:00
JimmyLiao 399841b6e0 fix: Correct start script reference in create_alias.sh (#1487) 2025-06-28 23:28:44 +00:00
Vladislav Semyanov f31b1274bf docs: remove duplicate tool descriptions in file-system.md (#1790)
Co-authored-by: N. Taylor Mullen <ntaylormullen@google.com>
2025-06-28 23:26:38 +00:00
Jerop Kipruto fbb3a2e6f0 chore(gha): pin issue triage workflow to a specific commit (#2496) 2025-06-28 23:14:44 +00:00
Umair Idris 2769188d30 Re-enable Gemini Code Assist PR review bot (#2254) 2025-06-28 22:05:40 +00:00
Leo 5d3a64d747 fix file extension in "modify flow" temp files (#2478) 2025-06-28 21:51:03 +00:00
Leo 601d9ba36d fix edit retrigger (#2306) 2025-06-28 18:02:44 +00:00
Pyush Sinha 3518ff7663 feat: add VSCodium editor support (#2299)
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
Co-authored-by: Allen Hutchison <adh@google.com>
2025-06-28 17:44:31 +00:00
christine betts 0d51e4b4b7 Add troubleshooting note about CI env variables (#2229)
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
2025-06-28 16:25:56 +00:00
Reid 8c13c1e82a fix: typo (#2415)
Signed-off-by: reidliu41 <reid201711@gmail.com>
2025-06-28 16:18:20 +00:00
Kalle Ahlström 9665928860 chore: add proper pluralization handling for match in grep tool (#2344)
Co-authored-by: Allen Hutchison <adh@google.com>
2025-06-28 14:41:25 +00:00
Billy Biggs 25cdf9b762 Inline the description and schema of the shell tool in the source (#1709) 2025-06-28 09:53:03 +00:00
Mot ad7839ea4c quiet dotenv log message (#2239)
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
2025-06-28 02:03:20 +00:00
SADIK KUZU 2e20effb43 Fix typos (#1629)
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-06-28 01:29:59 +00:00
Noritaka Kobayashi 221b066900 chore: fix typo in mcp-client (#1555)
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
2025-06-28 01:14:59 +00:00
Jerop Kipruto 6c4391dda5 improve triage prompt (#2314) 2025-06-28 00:53:38 +00:00
Jerop Kipruto ac2bc3af7c add issue triage using gemini cli (#2310) 2025-06-28 00:30:56 +00:00
Brandon Keiji e8d5dec380 chore: bump to 0.1.8 (#2308) 2025-06-28 00:08:16 +00:00
Vachan db115c468a Updates error handling in case of incorrect tool calling. (#2304) 2025-06-27 23:57:40 +00:00
Sandy Tao 150df382f8 Upgrade to Ink 6 and React 19 (#2096)
Co-authored-by: jacob314 <jacob314@gmail.com>
2025-06-27 23:39:54 +00:00
Brandon Keiji 19d2a0fb35 fix: add missing gaxios dependency (#2302) 2025-06-27 23:24:03 +00:00
Philipp Schmid ac24fd27e4 Update Auth Label to include AI Studio (#2130)
Co-authored-by: Allen Hutchison <adh@google.com>
2025-06-27 21:33:26 +00:00
Xi Chen d35e7d3eb1 Fix a circular dependency (#2246)
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
2025-06-27 20:59:47 +00:00
Brandon Keiji 8d1d40cc7a chore: bump to 0.1.6 (#2285) 2025-06-27 20:57:07 +00:00
Tommaso Sciortino a2a46c7c67 Add privacy notice slash command (#2059) 2025-06-27 19:07:38 +00:00
Billy Biggs 4fbffdf617 Handle stdin for prompts using readline for escape character parsing (#1972) 2025-06-27 17:57:32 +00:00
Preston Holmes 5fd6664c4b Further clarify the situations where a Project ID is required (#2029)
Co-authored-by: Tommaso Sciortino <sciortino@gmail.com>
2025-06-27 16:51:28 +00:00
Preston Holmes 3aabb940f5 Add the current auth method and GCP Project config to the about message (#2112) 2025-06-27 15:46:27 +00:00
Srinath Padmanabhan 3ebf54f367 Refine Fallback message providing more options. (#1961) 2025-06-27 15:21:46 +00:00
Billy Biggs 582b4861a9 Use 2-space indent for saved checkpoint files (#1152) (#1977)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
2025-06-27 13:39:27 +00:00
DongJin Jeong a943cee45d Fix typos and formatting issues (#1480) 2025-06-27 10:31:42 +00:00
Seth Troisi 6742a1b7f9 Explicit message for missing subcommand (#2019) 2025-06-27 05:01:00 +00:00
Brandon Keiji bf873a1d85 feat: add prepublishOnly checks (#2052) 2025-06-26 23:49:43 +00:00
Brandon Keiji d9892ada7f fix: add repository field to package.jsons (#2032) 2025-06-26 22:36:34 +00:00
Bryan Morgan 560905154c Updating the first user message to mention the product name (#2037) 2025-06-26 22:32:19 +00:00
anj-s 267173c7e8 Revert "feat: Add model selection logic (#1678)" (#1983) 2025-06-26 20:59:16 +00:00
Tommaso Sciortino c55b15f705 Improve LoadCodeAssist error handling (#1645) 2025-06-26 15:27:20 +00:00
N. Taylor Mullen 24ccc9c457 feat: Add model selection logic (#1678) 2025-06-26 14:51:32 +00:00
Vladislav Semyanov 121bba3464 docs: fix broken configuration link in themes.md (#1780) 2025-06-26 07:37:42 +00:00
김진엽 (Nathan) 02bd8dfeff docs: fix multiple typos in documentation files (#1781) 2025-06-26 07:36:31 +00:00
Masato Sugiyama ee5bf842eb fix: remove unnecessary @gemini-code/core mock from slashCommandProcessor test (#1739)
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
2025-06-26 04:56:51 +00:00
Keith Ballinger 891116a6c2 Flaky test (#1405)
Co-authored-by: matt korwel <matt.korwel@gmail.com>
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
2025-06-26 04:45:39 +00:00
Noritaka Kobayashi dbe217828d chore: fix typos in comment-out (#1540)
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
2025-06-26 04:34:53 +00:00
Andrew Drozdov b8ae12a109 Update geminiChat.ts (#1681)
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
2025-06-26 04:27:35 +00:00
Billy Biggs 759ad4cc96 When resuming a checkpoint always add items to history even if not shown (#1653)
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
2025-06-26 04:23:47 +00:00
Jennifer Davis 4d4b95a41d fix: add engines check to ensure older Node.js versions don't fail at… (#1752) 2025-06-26 04:09:40 +00:00
mpAtGoogle 046c7f84bc Update LICENSE (#1664) 2025-06-26 03:34:01 +00:00
Brandon Keiji 01ff27709d chore: bump to 0.1.5 (#1731) 2025-06-26 02:48:16 +00:00
anj-s 1078a546fe Fix doc link in the auth dialog (#1688) 2025-06-26 01:59:13 +00:00
Brandon Keiji a8763abfb7 feat: add release trigger configuration (#1697) 2025-06-26 01:54:58 +00:00
Bryan Morgan bb797ded7d 429 fix (#1668) 2025-06-26 01:45:38 +00:00
Jerop Kipruto b6b9923dc3 Streamline issue submission with YAML forms (#1608) 2025-06-25 22:50:24 +00:00
Tommaso Sciortino 79c647d486 Merge "Login with Google Workspace" auth option into "Login with Google" (#1574)
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
2025-06-25 22:38:18 +00:00
anj-s 00b24c917e Update usage stats doc (#1636) 2025-06-25 22:26:07 +00:00
Logan Kilpatrick 32b2e79b62 Update README.md (#1632) 2025-06-25 21:41:52 +00:00
anj-s 5aa6b9a84b Update docs and add faq section (#1625) 2025-06-25 21:32:16 +00:00
Jerop Kipruto 8c5a0b6f88 prompt users to search for existing issues before creating a new one (#1595) 2025-06-25 20:29:51 +00:00
zhiyue b0cf9bcece fix(telemetry): handle all promise rejections in ClearcutLogger (#1557)
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
2025-06-25 20:14:44 +00:00
Seth Troisi 31f32421a3 Minor style changes to prompt (#1578) 2025-06-25 20:00:44 +00:00
Bryan Morgan eacbb3551c changed 429 failover from 3 consecutive to 2 for OAuth users (#1579) 2025-06-25 19:37:23 +00:00
anj-s 4b5ca6bc77 Add tos and privacy links docs for clarity (#1571) 2025-06-25 19:32:25 +00:00
Mark McDonald 452b82162b Adding some troubleshooting text for login issues (#1451) 2025-06-25 19:00:36 +00:00
DongJin Jeong aa0e375508 Fix: update npx command to correct GitHub repository URL (#1488) 2025-06-25 18:58:11 +00:00
Jenna Inouye 6a0b8a733b Add information request to bug reports: login method. (#1501) 2025-06-25 18:55:35 +00:00
Arjun Lall 63a7fbc5fd Fixed Checkpointing docs for enabling checkpointing using settings.json (#1534) 2025-06-25 18:51:38 +00:00
N. Taylor Mullen 3a369ddec3 feat: Refine model fallback messaging to reflect reality. (#1527) 2025-06-25 17:33:32 +00:00
starsandskies 0915bf7d67 Remove GEMINI_CODE_ASSIST env variable from configuration.md doc (#1514) 2025-06-25 16:45:40 +00:00
Marat Boshernitsan 9897a2b80a Clarify why authentication failures might be happening and direct users to documentation (#1478)
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
Co-authored-by: matt korwel <matt.korwel@gmail.com>
2025-06-25 16:11:54 +00:00
Jason Davenport 819507c5e8 Update Readme to call out gemini command after npm install (#1423)
Co-authored-by: Mark McDonald <macd@google.com>
2025-06-25 16:03:07 +00:00
matt korwel 45b2b382cc Prod: Integration Tests Main Only (#1461) 2025-06-25 15:56:06 +00:00
Thomas Kosiewski af4dfd9327 Update authentication.md (#1429)
Co-authored-by: matt korwel <matt.korwel@gmail.com>
2025-06-25 15:48:15 +00:00
matt korwel 21cfe9f680 issue template update (#1441) 2025-06-25 14:44:34 +00:00
matt korwel 6991ba1387 Version 0 1 1 (#1426) 2025-06-25 13:47:27 +00:00
Asad Memon 452dca4301 Fix typo in intro on MCP line (#1421) 2025-06-25 13:04:42 +00:00
N. Taylor Mullen 852210e108 Shipping it! (#1418) 2025-06-25 12:42:04 +00:00
Brandon Keiji f6c36f75e3 fix: prepublish changes to package names (#1420) 2025-06-25 12:41:11 +00:00
Jenna Inouye a3c46c0d31 Docs: Add link links to tools/index.md. (#1419) 2025-06-25 11:16:17 +00:00
Mark McDonald f78479d7ce Add intro text back to README (#1417)
Co-authored-by: N. Taylor Mullen <ntaylormullen@google.com>
2025-06-25 10:12:31 +00:00
Mark McDonald cfc13fbd58 Add 'npm i' instruction to README (#1416) 2025-06-25 09:46:05 +00:00
matt korwel 9a093e4b51 CI: Linting Fix (#1413)
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
2025-06-25 04:18:51 +00:00
Preston Holmes 4cc2b27f1d Docs: update docs to clarify the differences between Google account login o… (#1409)
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
Co-authored-by: matt korwel <mattkorwel@google.com>
Co-authored-by: matt korwel <matt.korwel@gmail.com>
2025-06-25 04:15:45 +00:00
Scott Densmore 39bfa108b5 refactor: remove deplicate dependency in slashCommandProcessor (#1410)
Co-authored-by: matt korwel <matt.korwel@gmail.com>
2025-06-25 04:07:20 +00:00
Daniel Tedesco 268d29f05c docs: fix typos in documentation (#1411)
Co-authored-by: Dan Tedesco <dted@google.com>
2025-06-25 03:53:03 +00:00
Keith Ballinger b6ccf12551 [June 25] handle early output pipe closer (#1402) 2025-06-25 00:39:01 +00:00
matt korwel fbd8725c07 fix(update-notifier): resolve __dirname error on npx execution (#1406) 2025-06-25 00:26:50 +00:00
matt korwel 2505af8522 Prerelease: Cleanup (#1404) 2025-06-24 23:41:36 +00:00
Brian Ray d45d414c93 sandbox doc (#1390)
Co-authored-by: matt korwel <matt.korwel@gmail.com>
Co-authored-by: Jenna Inouye <jinouye@google.com>
2025-06-24 23:11:39 +00:00
Seth Troisi d8000c9248 Add slashCommand dependency (#1401) 2025-06-24 22:55:26 +00:00
Marat Boshernitsan e3def2dd49 fix: use correct directory for update checks (#1394) 2025-06-24 22:51:16 +00:00
Bryan Morgan e356949d3f [JUNE 25] Permanent failover to Flash model for OAuth users after persistent 429 errors (#1376)
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
2025-06-24 22:48:55 +00:00
Jenna Inouye 4bf18da2b0 Documents: Add docs tools pages (#1397) 2025-06-24 22:31:58 +00:00
Jacob Richman 8bd5645dd4 Truncate all strings before displaying in a tool messages to avoid stack overflows (#1395) 2025-06-24 22:31:55 +00:00
Mark McDonald 1f6fe59def Added ToS links for each surface (#1365)
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
Co-authored-by: matt korwel <matt.korwel@gmail.com>
2025-06-24 22:31:50 +00:00
anj-s 418f67086b Add the link to the privacy policy to the Usage Statistics section and remove debug logs (#1393) 2025-06-24 22:31:45 +00:00
cperry-goog 13cff94b1a docs: add screenshot to README (#1396) 2025-06-24 22:31:40 +00:00
Louis Jimenez 7421bf681b Checkpointing documentation (#1321)
Co-authored-by: matt korwel <matt.korwel@gmail.com>
2025-06-24 22:31:32 +00:00
matt korwel db57d38d92 feat: add merge_group trigger to CI and E2E workflows (#1400) 2025-06-24 15:30:31 -07:00
Jacob Richman 5ff00b0c5d Remove uses of the spread operator that appear to have caused a maximum call stack size exceeded error (#1389) 2025-06-24 21:38:25 +00:00
Jacob Richman 75ed7aaa06 Jacob314/max old space (#1314) 2025-06-24 14:18:55 -07:00
Sandy Tao a411c415a8 Do not render mcp responses as markdown (#1388) 2025-06-24 14:10:41 -07:00
Brian Ray e613cbc448 MCP documentation improvements (#1386)
Co-authored-by: Jenna Inouye <jinouye@google.com>
2025-06-24 13:19:36 -07:00
Bryan Morgan 5099f104bc [June 25] Fixed user agent string to be standards-compliant (#1384) 2025-06-24 09:11:09 -07:00
Billy Biggs b47a4240ff Bug/1369 at command recursive search (#1370) 2025-06-24 06:48:26 +00:00
cornmander a2ed4266aa Update editor.ts (#1371) 2025-06-24 06:41:40 +00:00
Scott Densmore 324715ee8b Add Zed Editor to Eidtor List (#1372) 2025-06-23 23:32:09 -07:00
starsandskies 9f5a625730 Fix broken doc links (#1368) 2025-06-23 20:56:57 -07:00
Abhi d3f13c71ae feat: add custom message for 429 errors (#1366) 2025-06-24 03:43:00 +00:00
Bryan Morgan f7caca5f94 Updated README.md (#1367) 2025-06-23 23:37:07 -04:00
Tommaso Sciortino 0abd2a644e Improve Auth error messaging (#1358) 2025-06-24 01:37:41 +00:00
Seth Troisi 104f23da90 Add /chat list (#1361) 2025-06-24 01:11:45 +00:00
owenofbrien 37034045ae Fix Clearcut logging wire format (#1359)
Fix for Clearcut logging wire format based on validation thread with Clearcut / Concord eng
2025-06-23 17:47:37 -07:00
Jerop Kipruto aca034fdfe Refactor usage statistics to be a top-level setting (#1363)
This commit refactors the `usageStatisticsEnabled` setting from a sub-property of the `telemetry` configuration to a top-level setting. This change simplifies the configuration by decoupling usage statistics from the telemetry settings.

The documentation has also been updated to reflect this change.
2025-06-24 00:29:31 +00:00
cperry-goog b3741f7016 docs: update configuration.md with new CLI options (#1360) 2025-06-23 17:24:55 -07:00
anj-s 3012684469 Fix duplicate startSession logs and duplicate logging events over the wire (#1357) 2025-06-24 00:05:42 +00:00
Seth Troisi 335802f4dd moving /save, /resume to /chat <save|resume> (#1355) 2025-06-23 16:56:08 -07:00
Jacob Richman f741630572 Polish Theme Dialog (#1356) 2025-06-23 16:43:17 -07:00
Seth Troisi 8c6545bf9d Include all chat messages (#1354) 2025-06-23 22:41:33 +00:00
Bryan Morgan e21dbed8c8 [June 25] Updated docs for telemetry and user statistics (#1346) 2025-06-23 22:25:49 +00:00
starsandskies 160d6a6552 Docs: Add telemetry to the list of configuration options (#1348) 2025-06-23 22:18:07 +00:00
Jerop Kipruto b443b5e800 Ensure telemetry events are flushed immediately (#1344)
The previous implementation used `flushIfNeeded` to batch most telemetry events, but it was not reliably sending them, leading to data loss. Notably, the `startSession` event, which already used `flushToClearcut`, was working correctly, indicating an issue with the batching logic itself.

This change replaces all calls to `flushIfNeeded` with `flushToClearcut` to align all event logging with the working `startSession` implementation and ensure that events are sent immediately. This prioritizes the reliability of data collection over network efficiency.

This is a temporary solution to prevent further data loss. The underlying issue with the batching mechanism in `flushIfNeeded` should be investigated and fixed in the future, at which point this change can be reverted.
2025-06-23 22:05:02 +00:00
Jerop Kipruto 58572a6eaa Use concurrently to run start script with GCP telemetry (#1329)
## TLDR

Introduces the `concurrently` package to simplify the dev startup process with GCP telemetry enabled.

## Dive Deeper

Previously, developers had to run the telemetry script and the main application start script in separate terminals. This change updates the `start:gcp` script to use `concurrently`, allowing both processes to be launched and managed with a single command. This improves the developer experience and reduces the chance of forgetting to start one of the required processes. 

## Reviewer Test Plan
Set the required environment variable:

```shell
export OTLP_GOOGLE_CLOUD_PROJECT=<your-project-id>
```

Run the following command:

```shell
npm run start:gcp
```

#750 

cc @teeler
2025-06-23 22:01:22 +00:00
Bryan Morgan e423d20a8d Updated docs and /stats command to support lack of token caching support for OAuth users (b/426943001) (#1307) 2025-06-23 21:55:24 +00:00
Sandy Tao fcb8be2fb9 Refine refresh static logic (#1349) 2025-06-23 21:45:15 +00:00
Jacob Richman 1faf53a3af Remove fallback to render normall rather than using custom MaxSizedBox layout logic (#1340) 2025-06-23 21:41:45 +00:00
N. Taylor Mullen fd58d3267e feat: Open MCP docs if no MCPs are configured (#1325) 2025-06-23 21:35:23 +00:00
Abhi dc76bcc433 Add error messaging for 429 errors (#1316) 2025-06-23 21:30:13 +00:00
Jerop Kipruto 21e6a36cf1 docs(telemetry): relocate telemetry documentation (#1327)
Moves the telemetry.md file from docs/core to the top-level docs/ directory to make it more discoverable.

Updates the link in the main index.md and removes the old reference from the CLI configuration page.
2025-06-23 21:24:48 +00:00
Jerop Kipruto 98f3a7066e refactor: rename disableDataCollection to dataCollectionEnabled (#1319)
Renames the `disableDataCollection` flag to the more intuitive and positive `dataCollectionEnabled`.

This change improves code clarity by avoiding double negatives and making the purpose of the flag more direct. The logic has been inverted wherever the flag is used to accommodate the new naming convention.

Using a suffix like `"Enabled"` follows a common convention that improves readability. 
- A condition like `if (dataCollectionEnabled)` reads like a natural language sentence ("if data collection is enabled"), which reduces cognitive load.
- Distinguishes the boolean flag (representing a state) from potential functions that would perform an action (e.g., `enableDataCollection()` or `disableDataCollection()`), avoiding ambiguity between checking a value and calling a function.

#750
2025-06-23 21:19:40 +00:00
Jerop Kipruto 4d88054d35 Fix batch flush to Clearcut (#1337)
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
2025-06-23 21:12:58 +00:00
starsandskies d2ae7af487 Edit pass of docs/core/index.md (#1338)
Make a pass through the docs/core/index.md file to hopefully improve readability and consistency.

Of particular note, I've cut out a big chunk that felt mostly like implementation details that aren't particularly relevant for public consumption, and I cut out a discussion of cli/core interconnection that is covered better higher up in the docs hierarchy
2025-06-23 21:07:03 +00:00
cperry-goog 139668c1d1 Docs/authentication doc (#1341) 2025-06-23 13:02:52 -07:00
cperry-goog 1825105d8f feat(docs): add /chat and /restore commands (#1336) 2025-06-23 12:10:23 -07:00
Jacob Richman 3e25c350f2 A couple of these log messages were now showing up every time on app startup (#1335) 2025-06-23 11:31:13 -07:00
N. Taylor Mullen 4b7307accb Remove .gitignore logging from startup. (#1323) 2025-06-23 11:12:42 -07:00
owenofbrien 631591ce79 Enable clearcut logging by default (#1309)
Clearcut logging can now be disabled via disableDataCollection in user settings
2025-06-23 10:18:58 -07:00
Tommaso Sciortino 07880d43d2 Sanitize MCP FunctionDeclarations to workaround Vertex bug (#1330) 2025-06-23 09:13:53 -07:00
Mark McDonald 7b39dd8b28 Added some use cases to the README (#1257) 2025-06-23 07:07:18 +00:00
matt korwel d0800ab917 Adding .gitattributes (#1303) 2025-06-23 06:58:41 +00:00
Scott Densmore 99d521569d Scotdensmore/first run auth fix (#1322) 2025-06-22 22:52:25 -07:00
Jacob Richman 156feff5b1 Fix so that pressing ctrl-s a second time toggles off constrain height mode (#1306) 2025-06-23 05:42:20 +00:00
Mark McDonald 523aeec544 Use shorter URL for docs link (#1324) 2025-06-23 05:37:41 +00:00
Jerop Kipruto 64e1c7df75 docs: update telemetry documentation and scripts (#1318) 2025-06-23 06:10:26 +01:00
matt korwel da128e725d {bug} Vertex Auth Support (#1302)
Co-authored-by: Tommaso Sciortino <sciortino@gmail.com>
2025-06-23 00:30:58 +00:00
Abhi d8ecbde9bd add bash block to doc for easy copypasta (#1317) 2025-06-22 20:19:13 -04:00
Billy Biggs c9bea8e646 Plumb extension context filenames through for /memory refresh (#1312) 2025-06-22 16:17:05 -07:00
cperry-goog b05b8673cd update tips (#1315) 2025-06-22 16:02:48 -07:00
Jacob Richman b831ffc1b3 Jacob314/auto exit unconstrained height mode (#1293)
Co-authored-by: Scott Densmore <scottdensmore@mac.com>
2025-06-22 13:43:36 -07:00
N. Taylor Mullen 6a1c62731b Fix seatbelt sandboxing when GEMINI_SANDBOX="" and starting with -s (#1298) 2025-06-22 18:33:29 +00:00
N. Taylor Mullen 3c656cfbc6 feat: Only show ctrl-s when idle (#1299) 2025-06-22 15:57:53 +00:00
N. Taylor Mullen cb76b08e31 feat: Add /docs command and update UI (#1297) 2025-06-22 15:54:10 +00:00
owenofbrien 4cfab0a893 Clearcut logging - initial implementation (#1274)
Flag-guarded initial implementation of a clearcut logger to collect telemetry data and send it to Concord for dashboards, etc.
2025-06-22 14:26:48 +00:00
Abhi c9950b3cb2 feat: Add client-initiated tool call handling (#1292) 2025-06-22 01:35:36 -04:00
cperry-goog 5cf8dc4f07 Docs update (#1295) 2025-06-22 05:06:58 +00:00
Louis Jimenez 383212034c Provide a .gitconfig for shadow repos (#1294) 2025-06-22 00:13:03 -04:00
Billy Biggs 0779697da6 Add setting enableRecursiveFileSearch to control @-file completion (#1290) 2025-06-22 01:23:35 +00:00
Jacob Richman 63f6a497cb Jacob314/overflow notification and one MaxSizedBox bug fix (#1288) 2025-06-22 00:54:10 +00:00
cperry-goog e20171e7dd Updating missing commands (#1289) 2025-06-21 17:41:03 -07:00
cperry-goog 47780e984c docs: update README (#1287) 2025-06-21 16:18:23 -07:00
Marlon Gamez 076f81f130 point Code Assist client to prod server (#1201) 2025-06-21 22:25:18 +00:00
cperry-goog e255eb6124 feat(cli): update Tips.tsx component (#1284) 2025-06-21 12:58:00 -07:00
Billy Biggs 99a6dc0267 Update memory and context summary UI for multiple context filenames (#1282) 2025-06-21 19:15:43 +00:00
Sandy Tao 03af6235a9 Fix Static duplication and input prompt tearing (#1279) 2025-06-21 11:11:42 -07:00
Tommaso Sciortino f9b2a33732 Remove packages/cli/README.md (#1278) 2025-06-21 16:43:01 +01:00
582 changed files with 84383 additions and 16483 deletions
+13
View File
@@ -0,0 +1,13 @@
root = true
[*]
charset = utf-8
insert_final_newline = true
end_of_line = lf
indent_style = space
indent_size = 2
max_line_length = 80
[Makefile]
indent_style = tab
indent_size = 8
-47
View File
@@ -1,47 +0,0 @@
steps:
# Step 1: Install root dependencies (includes workspaces)
- name: 'us-west1-docker.pkg.dev/gemini-code-dev/gemini-code-containers/gemini-code-builder'
entrypoint: 'npm'
args: ['install']
# Step 2: Update version in root package.json
- name: 'us-west1-docker.pkg.dev/gemini-code-dev/gemini-code-containers/gemini-code-builder'
entrypoint: 'bash'
args:
- -c # Use bash -c to allow for command substitution and string manipulation
- |
current_version=$(npm pkg get version | sed 's/"//g')
new_version="$${current_version}-$SHORT_SHA.$_REVISION"
npm pkg set "version=$${new_version}"
echo "Set root package.json version to: $${new_version}"
# Step 3: Run prerelease:dev to update workspace versions and dependencies
- name: 'us-west1-docker.pkg.dev/gemini-code-dev/gemini-code-containers/gemini-code-builder'
entrypoint: 'npm'
args: ['run', 'prerelease:dev'] # This will run prerelease:version and prerelease:deps
# Step 4: Authenticate for Docker and NPM
- name: 'us-west1-docker.pkg.dev/gemini-code-dev/gemini-code-containers/gemini-code-builder'
entrypoint: 'npm'
args: ['run', 'auth']
# Step 5: Run the master release script
- name: 'us-west1-docker.pkg.dev/gemini-code-dev/gemini-code-containers/gemini-code-builder'
entrypoint: 'npm'
args: ['run', 'publish:release']
env:
- 'GEMINI_SANDBOX=$_CONTAINER_TOOL'
- 'SANDBOX_IMAGE_REGISTRY=$_SANDBOX_IMAGE_REGISTRY'
- 'SANDBOX_IMAGE_NAME=$_SANDBOX_IMAGE_NAME'
- 'NPM_PUBLISH_TAG=$_NPM_PUBLISH_TAG'
options:
defaultLogsBucketBehavior: REGIONAL_USER_OWNED_BUCKET
dynamicSubstitutions: true
substitutions:
_REVISION: '0'
_SANDBOX_IMAGE_REGISTRY: 'us-west1-docker.pkg.dev/gemini-code-dev/gemini-code-containers'
_SANDBOX_IMAGE_NAME: 'gemini-cli-sandbox'
_NPM_PUBLISH_TAG: 'head'
_CONTAINER_TOOL: 'docker'
-30
View File
@@ -1,30 +0,0 @@
steps:
- name: 'us-west1-docker.pkg.dev/gemini-code-dev/gemini-code-containers/gemini-code-builder'
entrypoint: 'npm'
args: ['install']
- name: 'us-west1-docker.pkg.dev/gemini-code-dev/gemini-code-containers/gemini-code-builder'
entrypoint: 'npm'
args: ['run', 'auth']
- name: 'us-west1-docker.pkg.dev/gemini-code-dev/gemini-code-containers/gemini-code-builder'
entrypoint: 'npm'
args:
[
'run',
'prerelease:version',
'--workspaces',
'--',
'--suffix="$SHORT_SHA.$_REVISION"',
]
- name: 'us-west1-docker.pkg.dev/gemini-code-dev/gemini-code-containers/gemini-code-builder'
entrypoint: 'npm'
args: ['run', 'prerelease:deps', '--workspaces']
- name: 'us-west1-docker.pkg.dev/gemini-code-dev/gemini-code-containers/gemini-code-builder'
entrypoint: 'npm'
args: ['publish', '--tag=head', '--dry-run', '--workspace=@gemini-cli/cli']
options:
defaultLogsBucketBehavior: REGIONAL_USER_OWNED_BUCKET
+75
View File
@@ -0,0 +1,75 @@
steps:
# Step 1: Install root dependencies (includes workspaces)
- name: 'us-west1-docker.pkg.dev/gemini-code-dev/gemini-code-containers/gemini-code-builder'
id: 'Install Dependencies'
entrypoint: 'npm'
args: ['install']
# Step 2: Authenticate for Docker (so we can push images to the artifact registry)
- name: 'us-west1-docker.pkg.dev/gemini-code-dev/gemini-code-containers/gemini-code-builder'
id: 'Authenticate docker'
entrypoint: 'npm'
args: ['run', 'auth']
# Step 3: Build workspace packages
- name: 'us-west1-docker.pkg.dev/gemini-code-dev/gemini-code-containers/gemini-code-builder'
id: 'Build packages'
entrypoint: 'npm'
args: ['run', 'build:packages']
# Step 4: Determine Docker Image Tag
- name: 'us-west1-docker.pkg.dev/gemini-code-dev/gemini-code-containers/gemini-code-builder'
id: 'Determine Docker Image Tag'
entrypoint: 'bash'
args:
- -c
- |
SHELL_TAG_NAME="$TAG_NAME"
FINAL_TAG="$SHORT_SHA" # Default to SHA
if [[ "$$SHELL_TAG_NAME" == *"-nightly"* ]]; then
echo "Nightly release detected."
FINAL_TAG="$${SHELL_TAG_NAME#v}"
# Also escape the variable in the regex match
elif [[ "$$SHELL_TAG_NAME" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Official release detected."
FINAL_TAG="$${SHELL_TAG_NAME#v}"
else
echo "Development/RC release detected. Using commit SHA as tag."
fi
echo "Determined image tag: $$FINAL_TAG"
echo "$$FINAL_TAG" > /workspace/image_tag.txt
# Step 5: Build sandbox container image
- name: 'us-west1-docker.pkg.dev/gemini-code-dev/gemini-code-containers/gemini-code-builder'
id: 'Build sandbox Docker image'
entrypoint: 'bash'
args:
- -c
- |
export GEMINI_SANDBOX_IMAGE_TAG=$$(cat /workspace/image_tag.txt)
echo "Using Docker image tag for build: $$GEMINI_SANDBOX_IMAGE_TAG"
npm run build:sandbox -- --output-file /workspace/final_image_uri.txt
env:
- 'GEMINI_SANDBOX=$_CONTAINER_TOOL'
# Step 8: Publish sandbox container image
- name: 'us-west1-docker.pkg.dev/gemini-code-dev/gemini-code-containers/gemini-code-builder'
id: 'Publish sandbox Docker image'
entrypoint: 'bash'
args:
- -c
- |
set -e
FINAL_IMAGE_URI=$$(cat /workspace/final_image_uri.txt)
echo "Pushing sandbox image: $${FINAL_IMAGE_URI}"
$_CONTAINER_TOOL push "$${FINAL_IMAGE_URI}"
env:
- 'GEMINI_SANDBOX=$_CONTAINER_TOOL'
options:
defaultLogsBucketBehavior: REGIONAL_USER_OWNED_BUCKET
dynamicSubstitutions: true
substitutions:
_CONTAINER_TOOL: 'docker'
+5 -3
View File
@@ -1,10 +1,12 @@
# Config for the Gemini Pull Request Review Bot.
# https://github.com/marketplace/gemini-code-assist
have_fun: false
code_review:
disable: true
disable: false
comment_severity_threshold: HIGH
max_review_comments: -1
pull_request_opened:
help: false
summary: false
code_review: false
summary: true
code_review: true
ignore_patterns: []
+24
View File
@@ -0,0 +1,24 @@
# Set the default behavior for all files to automatically handle line endings.
# This will ensure that all text files are normalized to use LF (line feed)
# line endings in the repository, which helps prevent cross-platform issues.
* text=auto eol=lf
# Explicitly declare files that must have LF line endings for proper execution
# on Unix-like systems.
*.sh eol=lf
*.bash eol=lf
Makefile eol=lf
# Explicitly declare binary file types to prevent Git from attempting to
# normalize their line endings.
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
*.pdf binary
*.woff binary
*.woff2 binary
*.eot binary
*.ttf binary
*.otf binary
-13
View File
@@ -1,13 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**Additional context**
Add any other context about the problem here.
+55
View File
@@ -0,0 +1,55 @@
name: Bug Report
description: Report a bug to help us improve Gemini CLI
labels: ['kind/bug', 'status/need-triage']
body:
- type: markdown
attributes:
value: |
> [!IMPORTANT]
> Thanks for taking the time to fill out this bug report!
>
> Please search **[existing issues](https://github.com/google-gemini/gemini-cli/issues)** to see if an issue already exists for the bug you encountered.
- type: textarea
id: problem
attributes:
label: What happened?
description: A clear and concise description of what the bug is.
validations:
required: true
- type: textarea
id: expected
attributes:
label: What did you expect to happen?
validations:
required: true
- type: textarea
id: info
attributes:
label: Client information
description: Please paste the full text from the `/about` command run from Gemini CLI. Also include which platform (macOS, Windows, Linux).
value: |
<details>
```console
$ gemini /about
# paste output here
```
</details>
validations:
required: true
- type: textarea
id: login-info
attributes:
label: Login information
description: Describe how you are logging in (e.g., Google Account, API key).
- type: textarea
id: additional-context
attributes:
label: Anything else we need to know?
description: Add any other context about the problem here.
@@ -0,0 +1,33 @@
name: Feature Request
description: Suggest an idea for this project
labels: ['kind/enhancement', 'status/need-triage']
body:
- type: markdown
attributes:
value: |
> [!IMPORTANT]
> Thanks for taking the time to suggest an enhancement!
>
> Please search **[existing issues](https://github.com/google-gemini/gemini-cli/issues)** to see if a similar feature has already been requested.
- type: textarea
id: feature
attributes:
label: What would you like to be added?
description: A clear and concise description of the enhancement.
validations:
required: true
- type: textarea
id: rationale
attributes:
label: Why is this needed?
description: A clear and concise description of why this enhancement is needed.
validations:
required: true
- type: textarea
id: additional-context
attributes:
label: Additional context
description: Add any other context or screenshots about the feature request here.
@@ -17,6 +17,9 @@ inputs:
node_version:
description: 'Node.js version for context in messages'
required: true
os:
description: 'The os for context in messages'
required: true
github_token:
description: 'GitHub token for posting comments'
required: true
@@ -91,7 +94,7 @@ runs:
echo "</details>" >> "$comment_file"
echo "" >> "$comment_file"
echo "_For detailed HTML reports, please see the 'coverage-reports-${{ inputs.node_version }}' artifact from the main CI run._" >> "$comment_file"
echo "_For detailed HTML reports, please see the 'coverage-reports-${{ inputs.node_version }}-${{ inputs.os }}' artifact from the main CI run._" >> "$comment_file"
- name: Post Coverage Comment
uses: thollander/actions-comment-pull-request@v3
+17 -3
View File
@@ -4,11 +4,11 @@
## Dive Deeper
<!-- more thoughts and in depth discussion here -->
<!-- more thoughts and in-depth discussion here -->
## Reviewer Test Plan
<!-- when a person reviewes your code they should ideally be pulling and running that code. How would they validate your change works and if relevant what are some good classes of example prompts and ways they can exercise your changes -->
<!-- when a person reviews your code they should ideally be pulling and running that code. How would they validate your change works and if relevant what are some good classes of example prompts and ways they can exercise your changes -->
## Testing Matrix
@@ -24,4 +24,18 @@
## Linked issues / bugs
<!-- Add links to any gh issues or other external bugs --->
<!--
Link to any related issues or bugs.
**If this PR fully resolves the issue, use one of the following keywords to automatically close the issue when this PR is merged:**
- Closes #<issue_number>
- Fixes #<issue_number>
- Resolves #<issue_number>
*Example: `Resolves #123`*
**If this PR is only related to an issue or is a partial fix, simply reference the issue number without a keyword:**
*Example: `This PR makes progress on #456` or `Related to #789`*
-->
+163
View File
@@ -0,0 +1,163 @@
#!/bin/bash
set -euo pipefail
# Initialize a comma-separated string to hold PR numbers that need a comment
PRS_NEEDING_COMMENT=""
# Function to process a single PR
process_pr() {
local PR_NUMBER=$1
echo "🔄 Processing PR #$PR_NUMBER"
# Get PR body with error handling
local PR_BODY
if ! PR_BODY=$(gh pr view "$PR_NUMBER" --repo "$GITHUB_REPOSITORY" --json body -q .body 2>/dev/null); then
echo " ⚠️ Could not fetch PR #$PR_NUMBER details"
return 1
fi
# Look for issue references using multiple patterns
local ISSUE_NUMBER=""
# Pattern 1: Direct reference like #123
if [ -z "$ISSUE_NUMBER" ]; then
ISSUE_NUMBER=$(echo "$PR_BODY" | grep -oE '#[0-9]+' | head -1 | sed 's/#//' 2>/dev/null || echo "")
fi
# Pattern 2: Closes/Fixes/Resolves patterns (case-insensitive)
if [ -z "$ISSUE_NUMBER" ]; then
ISSUE_NUMBER=$(echo "$PR_BODY" | grep -iE '(closes?|fixes?|resolves?) #[0-9]+' | grep -oE '#[0-9]+' | head -1 | sed 's/#//' 2>/dev/null || echo "")
fi
if [ -z "$ISSUE_NUMBER" ]; then
echo "⚠️ No linked issue found for PR #$PR_NUMBER, adding status/need-issue label"
if ! gh pr edit "$PR_NUMBER" --repo "$GITHUB_REPOSITORY" --add-label "status/need-issue" 2>/dev/null; then
echo " ⚠️ Failed to add label (may already exist or have permission issues)"
fi
# Add PR number to the list
if [ -z "$PRS_NEEDING_COMMENT" ]; then
PRS_NEEDING_COMMENT="$PR_NUMBER"
else
PRS_NEEDING_COMMENT="$PRS_NEEDING_COMMENT,$PR_NUMBER"
fi
echo "needs_comment=true" >> $GITHUB_OUTPUT
else
echo "🔗 Found linked issue #$ISSUE_NUMBER"
# Remove status/need-issue label if present
if ! gh pr edit "$PR_NUMBER" --repo "$GITHUB_REPOSITORY" --remove-label "status/need-issue" 2>/dev/null; then
echo " status/need-issue label not present or could not be removed"
fi
# Get issue labels
echo "📥 Fetching labels from issue #$ISSUE_NUMBER"
local ISSUE_LABELS=""
if ! ISSUE_LABELS=$(gh issue view "$ISSUE_NUMBER" --repo "$GITHUB_REPOSITORY" --json labels -q '.labels[].name' 2>/dev/null | tr '\n' ',' | sed 's/,$//' || echo ""); then
echo " ⚠️ Could not fetch issue #$ISSUE_NUMBER (may not exist or be in different repo)"
ISSUE_LABELS=""
fi
# Get PR labels
echo "📥 Fetching labels from PR #$PR_NUMBER"
local PR_LABELS=""
if ! PR_LABELS=$(gh pr view "$PR_NUMBER" --repo "$GITHUB_REPOSITORY" --json labels -q '.labels[].name' 2>/dev/null | tr '\n' ',' | sed 's/,$//' || echo ""); then
echo " ⚠️ Could not fetch PR labels"
PR_LABELS=""
fi
echo " Issue labels: $ISSUE_LABELS"
echo " PR labels: $PR_LABELS"
# Convert comma-separated strings to arrays
local ISSUE_LABEL_ARRAY PR_LABEL_ARRAY
IFS=',' read -ra ISSUE_LABEL_ARRAY <<< "$ISSUE_LABELS"
IFS=',' read -ra PR_LABEL_ARRAY <<< "$PR_LABELS"
# Find labels to add (on issue but not on PR)
local LABELS_TO_ADD=""
for label in "${ISSUE_LABEL_ARRAY[@]}"; do
if [ -n "$label" ] && [[ ! " ${PR_LABEL_ARRAY[*]} " =~ " ${label} " ]]; then
if [ -z "$LABELS_TO_ADD" ]; then
LABELS_TO_ADD="$label"
else
LABELS_TO_ADD="$LABELS_TO_ADD,$label"
fi
fi
done
# Find labels to remove (on PR but not on issue)
local LABELS_TO_REMOVE=""
for label in "${PR_LABEL_ARRAY[@]}"; do
if [ -n "$label" ] && [[ ! " ${ISSUE_LABEL_ARRAY[*]} " =~ " ${label} " ]]; then
# Don't remove status/need-issue since we already handled it
if [ "$label" != "status/need-issue" ]; then
if [ -z "$LABELS_TO_REMOVE" ]; then
LABELS_TO_REMOVE="$label"
else
LABELS_TO_REMOVE="$LABELS_TO_REMOVE,$label"
fi
fi
fi
done
# Apply label changes
if [ -n "$LABELS_TO_ADD" ]; then
echo " Adding labels: $LABELS_TO_ADD"
if ! gh pr edit "$PR_NUMBER" --repo "$GITHUB_REPOSITORY" --add-label "$LABELS_TO_ADD" 2>/dev/null; then
echo " ⚠️ Failed to add some labels"
fi
fi
if [ -n "$LABELS_TO_REMOVE" ]; then
echo " Removing labels: $LABELS_TO_REMOVE"
if ! gh pr edit "$PR_NUMBER" --repo "$GITHUB_REPOSITORY" --remove-label "$LABELS_TO_REMOVE" 2>/dev/null; then
echo " ⚠️ Failed to remove some labels"
fi
fi
if [ -z "$LABELS_TO_ADD" ] && [ -z "$LABELS_TO_REMOVE" ]; then
echo "✅ Labels already synchronized"
fi
echo "needs_comment=false" >> $GITHUB_OUTPUT
fi
}
# If PR_NUMBER is set, process only that PR
if [ -n "${PR_NUMBER:-}" ]; then
if ! process_pr "$PR_NUMBER"; then
echo "❌ Failed to process PR #$PR_NUMBER"
exit 1
fi
else
# Otherwise, get all open PRs and process them
# The script logic will determine which ones need issue linking or label sync
echo "📥 Getting all open pull requests..."
if ! PR_NUMBERS=$(gh pr list --repo "$GITHUB_REPOSITORY" --state open --limit 1000 --json number -q '.[].number' 2>/dev/null); then
echo "❌ Failed to fetch PR list"
exit 1
fi
if [ -z "$PR_NUMBERS" ]; then
echo "✅ No open PRs found"
else
# Count the number of PRs
PR_COUNT=$(echo "$PR_NUMBERS" | wc -w | tr -d ' ')
echo "📊 Found $PR_COUNT open PRs to process"
for pr_number in $PR_NUMBERS; do
if ! process_pr "$pr_number"; then
echo "⚠️ Failed to process PR #$pr_number, continuing with next PR..."
continue
fi
done
fi
fi
# Ensure output is always set, even if empty
if [ -z "$PRS_NEEDING_COMMENT" ]; then
echo "prs_needing_comment=[]" >> $GITHUB_OUTPUT
else
echo "prs_needing_comment=[$PRS_NEEDING_COMMENT]" >> $GITHUB_OUTPUT
fi
echo "✅ PR triage completed"
+50 -42
View File
@@ -7,24 +7,22 @@ on:
branches: [main, release]
pull_request:
branches: [main, release]
merge_group:
jobs:
build:
name: Build and Lint
lint:
name: Lint
runs-on: ubuntu-latest
permissions:
contents: read # For checkout
strategy:
matrix:
node-version: [20.x]
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Set up Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
- name: Set up Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: ${{ matrix.node-version }}
node-version-file: '.nvmrc'
cache: 'npm'
- name: Install dependencies
@@ -36,7 +34,7 @@ jobs:
git diff --exit-code
- name: Run linter
run: npm run lint
run: npm run lint:ci
- name: Build project
run: npm run build
@@ -44,54 +42,42 @@ jobs:
- name: Run type check
run: npm run typecheck
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: build-artifacts-${{ matrix.node-version }}
path: |
packages/*/dist
package-lock.json # Only upload dist and lockfile
test:
name: Test
runs-on: ubuntu-latest
needs: build # This job depends on the 'build' job
runs-on: ${{ matrix.os }}
needs: lint
permissions:
contents: read
checks: write
pull-requests: write
strategy:
matrix:
node-version: [20.x] # Should match the build job's matrix
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: [20.x, 22.x, 24.x]
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Set up Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: build-artifacts-${{ matrix.node-version }}
path: . # Download to the root, this will include package-lock.json and packages/*/dist
# Restore/create package structure for dist folders if necessary.
# The download-artifact action with path: . should place them correctly if the
# upload paths were relative to the workspace root.
# Example: if uploaded `packages/cli/dist`, it will be at `./packages/cli/dist`.
- name: Build project
run: npm run build
- name: Install dependencies for testing
run: npm ci # Install fresh dependencies using the downloaded package-lock.json
- name: Run tests and generate reports
run: NO_COLOR=true npm run test:ci
run: npm run test:ci
env:
NO_COLOR: true
- name: Publish Test Report (for non-forks)
if: always() && (github.event.pull_request.head.repo.full_name == github.repository)
uses: dorny/test-reporter@v1
uses: dorny/test-reporter@dc3a92680fcc15842eef52e8c4606ea7ce6bd3f3 # v2
with:
name: Test Results (Node ${{ matrix.node-version }})
path: packages/*/junit.xml
@@ -100,16 +86,16 @@ jobs:
- name: Upload Test Results Artifact (for forks)
if: always() && (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository)
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: test-results-fork-${{ matrix.node-version }}
name: test-results-fork-${{ matrix.node-version }}-${{ matrix.os }}
path: packages/*/junit.xml
- name: Upload coverage reports
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
if: always()
with:
name: coverage-reports-${{ matrix.node-version }}
name: coverage-reports-${{ matrix.node-version }}-${{ matrix.os }}
path: packages/*/coverage
post_coverage_comment:
@@ -123,15 +109,17 @@ jobs:
pull-requests: write # For commenting
strategy:
matrix:
node-version: [20.x] # Should match the test job's matrix
# Reduce noise by only posting the comment once
os: [ubuntu-latest]
node-version: [22.x]
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Download coverage reports artifact
uses: actions/download-artifact@v4
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
name: coverage-reports-${{ matrix.node-version }}
name: coverage-reports-${{ matrix.node-version }}-${{ matrix.os }}
path: coverage_artifact # Download to a specific directory
- name: Post Coverage Comment using Composite Action
@@ -142,4 +130,24 @@ jobs:
cli_full_text_summary_file: coverage_artifact/cli/coverage/full-text-summary.txt
core_full_text_summary_file: coverage_artifact/core/coverage/full-text-summary.txt
node_version: ${{ matrix.node-version }}
os: ${{ matrix.os }}
github_token: ${{ secrets.GITHUB_TOKEN }}
codeql:
name: CodeQL
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Initialize CodeQL
uses: github/codeql-action/init@181d5eefc20863364f96762470ba6f862bdef56b # v3
with:
languages: javascript
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@181d5eefc20863364f96762470ba6f862bdef56b # v3
+188
View File
@@ -0,0 +1,188 @@
name: Generate Weekly Community Report 📊
on:
schedule:
- cron: '0 12 * * 1' # Run at 12:00 UTC on Monday
workflow_dispatch:
inputs:
days:
description: 'Number of days to look back for the report'
required: true
default: '7'
jobs:
generate-report:
name: Generate Report 📝
if: ${{ github.repository == 'google-gemini/gemini-cli' }}
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: read
discussions: read
contents: read
id-token: write
steps:
- name: Generate GitHub App Token 🔑
id: generate_token
uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.PRIVATE_KEY }}
- name: Generate Report 📜
id: report
env:
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
REPO: ${{ github.repository }}
DAYS: ${{ github.event.inputs.days || '7' }}
run: |
set -e
START_DATE=$(date -u -d "$DAYS days ago" +'%Y-%m-%d')
END_DATE=$(date -u +'%Y-%m-%d')
echo "⏳ Generating report for contributions from $START_DATE to $END_DATE..."
declare -A author_is_googler
check_googler_status() {
local author=$1
if [[ "$author" == *"[bot]" ]]; then
author_is_googler[$author]=1
return 1
fi
if [[ -v "author_is_googler[$author]" ]]; then
return ${author_is_googler[$author]}
fi
if gh api "orgs/googlers/members/$author" --silent 2>/dev/null; then
echo "🧑‍💻 $author is a Googler."
author_is_googler[$author]=0
else
echo "🌍 $author is a community contributor."
author_is_googler[$author]=1
fi
return ${author_is_googler[$author]}
}
googler_issues=0
non_googler_issues=0
googler_prs=0
non_googler_prs=0
echo "🔎 Fetching issues and pull requests..."
ITEMS_JSON=$(gh search issues --repo "$REPO" "created:>$START_DATE" --json author,isPullRequest --limit 1000)
for row in $(echo "${ITEMS_JSON}" | jq -r '.[] | @base64'); do
_jq() {
echo ${row} | base64 --decode | jq -r ${1}
}
author=$(_jq '.author.login')
is_pr=$(_jq '.isPullRequest')
if [[ -z "$author" || "$author" == "null" ]]; then
continue
fi
if check_googler_status "$author"; then
if [[ "$is_pr" == "true" ]]; then
((googler_prs++))
else
((googler_issues++))
fi
else
if [[ "$is_pr" == "true" ]]; then
((non_googler_prs++))
else
((non_googler_issues++))
fi
fi
done
googler_discussions=0
non_googler_discussions=0
echo "🗣️ Fetching discussions..."
DISCUSSION_QUERY='''
query($q: String!) {
search(query: $q, type: DISCUSSION, first: 100) {
nodes {
... on Discussion {
author {
login
}
}
}
}
}'''
DISCUSSIONS_JSON=$(gh api graphql -f q="repo:$REPO created:>$START_DATE" -f query="$DISCUSSION_QUERY")
for row in $(echo "${DISCUSSIONS_JSON}" | jq -r '.data.search.nodes[] | @base64'); do
_jq() {
echo ${row} | base64 --decode | jq -r ${1}
}
author=$(_jq '.author.login')
if [[ -z "$author" || "$author" == "null" ]]; then
continue
fi
if check_googler_status "$author"; then
((googler_discussions++))
else
((non_googler_discussions++))
fi
done
echo "✍️ Generating report content..."
REPORT_TITLE="Community Contribution Report: $START_DATE to $END_DATE"
TOTAL_ISSUES=$((googler_issues + non_googler_issues))
TOTAL_PRS=$((googler_prs + non_googler_prs))
TOTAL_DISCUSSIONS=$((googler_discussions + non_googler_discussions))
REPORT_BODY=$(cat <<EOF
### 💖 Community Contribution Report
**Period:** $START_DATE to $END_DATE
| Category | Googlers | Community | Total |
|---|---:|---:|---:|
| **Issues** | $googler_issues | $non_googler_issues | **$TOTAL_ISSUES** |
| **Pull Requests** | $googler_prs | $non_googler_prs | **$TOTAL_PRS** |
| **Discussions** | $googler_discussions | $non_googler_discussions | **$TOTAL_DISCUSSIONS** |
_This report was generated automatically by a GitHub Action._
EOF
)
echo "report_body<<EOF" >> $GITHUB_OUTPUT
echo "$REPORT_BODY" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
echo "📊 Community Contribution Report:"
echo "$REPORT_BODY"
- name: 🤖 Get Insights from Report
if: steps.report.outputs.report_body != ''
uses: google-gemini/gemini-cli-action@df3f890f003d28c60a2a09d2c29e0126e4d1e2ff
env:
GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
with:
version: 0.1.8-rc.0
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
OTLP_GCP_WIF_PROVIDER: ${{ secrets.OTLP_GCP_WIF_PROVIDER }}
OTLP_GOOGLE_CLOUD_PROJECT: ${{ secrets.OTLP_GOOGLE_CLOUD_PROJECT }}
settings_json: |
{
"coreTools": [
"run_shell_command(gh issue list)",
"run_shell_command(gh pr list)",
"run_shell_command(gh search issues)",
"run_shell_command(gh search prs)"
]
}
prompt: |
You are a helpful assistant that analyzes community contribution reports.
Based on the following report, please provide a brief summary and highlight any interesting trends or potential areas for improvement.
Report:
${{ steps.report.outputs.report_body }}
+35 -11
View File
@@ -4,25 +4,25 @@ name: E2E Tests
on:
push:
branches: [main, release]
pull_request:
branches: [main, release]
branches: [main]
merge_group:
jobs:
e2e-test:
name: E2E Test - ${{ matrix.sandbox }}
e2e-test-linux:
name: E2E Test (Linux) - ${{ matrix.sandbox }}
runs-on: ubuntu-latest
strategy:
matrix:
sandbox: [sandbox:none, sandbox:docker]
node-version: [20.x, 22.x, 24.x]
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Set up Node.js
uses: actions/setup-node@v4
- name: Set up Node.js ${{ matrix.node-version }}
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 20.x
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
@@ -33,11 +33,11 @@ jobs:
- name: Set up Docker
if: matrix.sandbox == 'sandbox:docker'
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3
- name: Set up Podman
if: matrix.sandbox == 'sandbox:podman'
uses: redhat-actions/podman-login@v1
uses: redhat-actions/podman-login@4934294ad0449894bcd1e9f191899d7292469603 # v1
with:
registry: docker.io
username: ${{ secrets.DOCKERHUB_USERNAME }}
@@ -47,3 +47,27 @@ jobs:
env:
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
run: npm run test:integration:${{ matrix.sandbox }} -- --verbose --keep-output
e2e-test-macos:
name: E2E Test - macOS
runs-on: macos-latest
steps:
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Set up Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 20.x
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build project
run: npm run build
- name: Run E2E tests
env:
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
run: npm run test:e2e
@@ -0,0 +1,145 @@
name: Gemini Automated Issue Triage
on:
issues:
types: [opened, reopened]
jobs:
triage-issue:
timeout-minutes: 5
if: ${{ github.repository == 'google-gemini/gemini-cli' }}
permissions:
issues: write
contents: read
id-token: write
concurrency:
group: ${{ github.workflow }}-${{ github.event.issue.number }}
cancel-in-progress: true
runs-on: ubuntu-latest
steps:
- name: Generate GitHub App Token
id: generate_token
uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.PRIVATE_KEY }}
- name: Run Gemini Issue Triage
uses: google-gemini/gemini-cli-action@df3f890f003d28c60a2a09d2c29e0126e4d1e2ff
env:
GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
ISSUE_TITLE: ${{ github.event.issue.title }}
ISSUE_BODY: ${{ github.event.issue.body }}
with:
version: 0.1.8-rc.0
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
OTLP_GCP_WIF_PROVIDER: ${{ secrets.OTLP_GCP_WIF_PROVIDER }}
OTLP_GOOGLE_CLOUD_PROJECT: ${{ secrets.OTLP_GOOGLE_CLOUD_PROJECT }}
settings_json: |
{
"coreTools": [
"run_shell_command(gh label list)",
"run_shell_command(gh issue edit)",
"run_shell_command(gh issue list)"
],
"telemetry": {
"enabled": true,
"target": "gcp"
},
"sandbox": false
}
prompt: |
You are an issue triage assistant. Analyze the current GitHub issues apply the most appropriate existing labels. Do not remove labels titled help wanted or good first issue.
Steps:
1. Run: `gh label list --repo ${{ github.repository }} --limit 100` to get all available labels.
2. Review the issue title, body and any comments provided in the environment variables.
3. Ignore any existing priorities or tags on the issue. Just report your findings.
4. Select the most relevant labels from the existing labels, focusing on kind/*, area/*, sub-area/* and priority/*. For area/* and kind/* limit yourself to only the single most applicable label in each case.
6. Apply the selected labels to this issue using: `gh issue edit ${{ github.event.issue.number }} --repo ${{ github.repository }} --add-label "label1,label2"`
7. For each issue please check if CLI version is present, this is usually in the output of the /about command and will look like 0.1.5 for anything more than 6 versions older than the most recent should add the status/need-retesting label
8. If you see that the issue doesnt look like it has sufficient information recommend the status/need-information label
9. Use Area definitions mentioned below to help you narrow down issues
Guidelines:
- Only use labels that already exist in the repository.
- Do not add comments or modify the issue content.
- Triage only the current issue.
- Apply only one area/ label
- Apply only one kind/ label
- Apply all applicable sub-area/* and priority/* labels based on the issue content. It's ok to have multiple of these.
- Once you categorize the issue if it needs information bump down the priority by 1 eg.. a p0 would become a p1 a p1 would become a p2. P2 and P3 can stay as is in this scenario.
Categorization Guidelines:
P0: Critical / Blocker
- A P0 bug is a catastrophic failure that demands immediate attention. It represents a complete showstopper for a significant portion of users or for the development process itself.
Impact:
- Blocks development or testing for the entire team.
- Major security vulnerability that could compromise user data or system integrity.
- Causes data loss or corruption with no workaround.
- Crashes the application or makes a core feature completely unusable for all or most users in a production environment. Will it cause severe quality degration? Is it preventing contributors from contributing to the repository or is it a release blocker?
Qualifier: Is the main function of the software broken?
Example: The gemini auth login command fails with an unrecoverable error, preventing any user from authenticating and using the rest of the CLI.
P1: High
- A P1 bug is a serious issue that significantly degrades the user experience or impacts a core feature. While not a complete blocker, it's a major problem that needs a fast resolution. Feature requests are almost never P1.
Impact:
- A core feature is broken or behaving incorrectly for a large number of users or large number of use cases.
- Review the bug details and comments to try figure out if this issue affects a large set of use cases or if it's a narrow set of use cases.
- Severe performance degradation making the application frustratingly slow.
- No straightforward workaround exists, or the workaround is difficult and non-obvious.
Qualifier: Is a key feature unusable or giving very wrong results?
Example: The gemini -p "..." command consistently returns a malformed JSON response or an empty result, making the CLI's primary generation feature unreliable.
P2: Medium
- A P2 bug is a moderately impactful issue. It's a noticeable problem but doesn't prevent the use of the software's main functionality.
Impact:
- Affects a non-critical feature or a smaller, specific subset of users.
- An inconvenient but functional workaround is available and easy to execute.
- Noticeable UI/UX problems that don't break functionality but look unprofessional (e.g., elements are misaligned or overlapping).
Qualifier: Is it an annoying but non-blocking problem?
Example: An error message is unclear or contains a typo, causing user confusion but not halting their workflow.
P3: Low
- A P3 bug is a minor, low-impact issue that is trivial or cosmetic. It has little to no effect on the overall functionality of the application.
Impact:
- Minor cosmetic issues like color inconsistencies, typos in documentation, or slight alignment problems on a non-critical page.
- An edge-case bug that is very difficult to reproduce and affects a tiny fraction of users.
Qualifier: Is it a "nice-to-fix" issue?
Example: Spelling mistakes etc.
Things you should know:
- If users are talking about issues where the model gets downgraded from pro to flash then i want you to categorize that as a performance issue
- This product is designed to use different models eg.. using pro, downgrading to flash etc. when users report that they dont expect the model to change those would be categorized as feature requests.
Definition of Areas
area/ux:
- Issues concerning user-facing elements like command usability, interactive features, help docs, and perceived performance.
- I am seeing my screen flicker when using Gemini CLI
- I am seeing the output malformed
- Theme changes aren't taking effect
- My keyboard inputs arent' being recognzied
area/platform:
- Issues related to installation, packaging, OS compatibility (Windows, macOS, Linux), and the underlying CLI framework.
area/background: Issues related to long-running background tasks, daemons, and autonomous or proactive agent features.
area/models:
- i am not getting a response that is reasonable or expected. this can include things like
- I am calling a tool and the tool is not performing as expected.
- i am expecting a tool to be called and it is not getting called ,
- Including experience when using
- built-in tools (e.g., web search, code interpreter, read file, writefile, etc..),
- Function calling issues should be under this area
- i am getting responses from the model that are malformed.
- Issues concerning Gemini quality of response and inference,
- Issues talking about unnecessary token consumption.
- Issues talking about Model getting stuck in a loop be watchful as this could be the root cause for issues that otherwise seem like model performance issues.
- Memory compression
- unexpected responses,
- poor quality of generated code
area/tools:
- These are primarily issues related to Model Context Protocol
- These are issues that mention MCP support
- feature requests asking for support for new tools.
area/core: Issues with fundamental components like command parsing, configuration management, session state, and the main API client logic. Introducing multi-modality
area/contribution: Issues related to improving the developer contribution experience, such as CI/CD pipelines, build scripts, and test automation infrastructure.
area/authentication: Issues related to user identity, login flows, API key handling, credential storage, and access token management, unable to sign in selecting wrong authentication path etc..
area/security-privacy: Issues concerning vulnerability patching, dependency security, data sanitization, privacy controls, and preventing unauthorized data access.
area/extensibility: Issues related to the plugin system, extension APIs, or making the CLI's functionality available in other applications, github actions, ide support etc..
area/performance: Issues focused on model performance
- Issues with running out of capacity,
- 429 errors etc..
- could also pertain to latency,
- other general software performance like, memory usage, CPU consumption, and algorithmic efficiency.
- Switching models from one to the other unexpectedly.
@@ -0,0 +1,191 @@
name: Gemini Scheduled Issue Triage
on:
schedule:
- cron: '0 * * * *' # Runs every hour
workflow_dispatch: {}
jobs:
triage-issues:
timeout-minutes: 10
if: ${{ github.repository == 'google-gemini/gemini-cli' }}
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
issues: write
steps:
- name: Generate GitHub App Token
id: generate_token
uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.PRIVATE_KEY }}
- name: Find untriaged issues
id: find_issues
env:
GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
run: |
echo "🔍 Finding issues without labels..."
NO_LABEL_ISSUES=$(gh issue list --repo ${{ github.repository }} --search "is:open is:issue no:label" --json number,title,body)
echo "🏷️ Finding issues that need triage..."
NEED_TRIAGE_ISSUES=$(gh issue list --repo ${{ github.repository }} --search "is:open is:issue label:\"status/need-triage\"" --json number,title,body)
echo "🔄 Merging and deduplicating issues..."
ISSUES=$(echo "$NO_LABEL_ISSUES" "$NEED_TRIAGE_ISSUES" | jq -c -s 'add | unique_by(.number)')
echo "📝 Setting output for GitHub Actions..."
echo "issues_to_triage=$ISSUES" >> "$GITHUB_OUTPUT"
echo "✅ Found $(echo "$ISSUES" | jq 'length') issues to triage! 🎯"
- name: Run Gemini Issue Triage
if: steps.find_issues.outputs.issues_to_triage != '[]'
uses: google-gemini/gemini-cli-action@df3f890f003d28c60a2a09d2c29e0126e4d1e2ff
env:
GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
ISSUES_TO_TRIAGE: ${{ steps.find_issues.outputs.issues_to_triage }}
REPOSITORY: ${{ github.repository }}
with:
version: 0.1.8-rc.0
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
OTLP_GCP_WIF_PROVIDER: ${{ secrets.OTLP_GCP_WIF_PROVIDER }}
OTLP_GOOGLE_CLOUD_PROJECT: ${{ secrets.OTLP_GOOGLE_CLOUD_PROJECT }}
settings_json: |
{
"coreTools": [
"run_shell_command(echo)",
"run_shell_command(gh label list)",
"run_shell_command(gh issue edit)",
"run_shell_command(gh issue list)",
"run_shell_command(gh issue view)"
],
"telemetry": {
"enabled": true,
"target": "gcp"
},
"sandbox": false
}
prompt: |
You are an issue triage assistant. Analyze the current GitHub issues apply the most appropriate existing labels.
Steps:
1. Run: `gh label list --repo ${{ github.repository }} --limit 100` to get all available labels.
2. Check environment variable for issues to triage: $ISSUES_TO_TRIAGE (JSON array of issues)
3. Review the issue title, body and any comments provided in the environment variables.
4. Ignore any existing priorities or tags on the issue.
5. Select the most relevant labels from the existing labels, focusing on kind/*, area/*, sub-area/* and priority/*.
6. Get the list of labels already on the issue using `gh issue view ISSUE_NUMBER --repo ${{ github.repository }} --json labels -t '{{range .labels}}{{.name}}{{"\n"}}{{end}}'
7. For area/* and kind/* limit yourself to only the single most applicable label in each case.
8. Give me a single short paragraph about why you are selecting each label in the process. use the format Issue ID: , Title, Label applied:, Label removed, ovearll explanation
9. Parse the JSON array from step 2 and for EACH INDIVIDUAL issue, apply appropriate labels using separate commands:
- `gh issue edit ISSUE_NUMBER --repo ${{ github.repository }} --add-label "label1"`
- `gh issue edit ISSUE_NUMBER --repo ${{ github.repository }} --add-label "label2"`
- Continue for each label separately
- IMPORTANT: Label each issue individually, one command per issue, one label at a time if needed.
- Make sure after you apply labels there is only one area/* and one kind/* label per issue.
- To do this look for labels found in step 6 that no longer apply remove them one at a time using
- `gh issue edit ISSUE_NUMBER --repo ${{ github.repository }} --remove-label "label-name1"`
- `gh issue edit ISSUE_NUMBER --repo ${{ github.repository }} --remove-label "label-name2"`
- IMPORTANT: Remove each label one at a time, one command per issue if needed.
10. For each issue please check if CLI version is present, this is usually in the output of the /about command and will look like 0.1.5
- Anything more than 6 versions older than the most recent should add the status/need-retesting label
11. If you see that the issue doesnt look like it has sufficient information recommend the status/need-information label
- After applying appropriate labels to an issue, remove the "status/need-triage" label if present: `gh issue edit ISSUE_NUMBER --repo ${{ github.repository }} --remove-label "status/need-triage"`
- Execute one `gh issue edit` command per issue, wait for success before proceeding to the next
Process each issue sequentially and confirm each labeling operation before moving to the next issue.
Guidelines:
- Only use labels that already exist in the repository.
- Do not add comments or modify the issue content.
- Do not remove labels titled help wanted or good first issue.
- Triage only the current issue.
- Apply only one area/ label
- Apply only one kind/ label (Do not apply kind/duplicate or kind/parent-issue)
- Apply all applicable sub-area/* and priority/* labels based on the issue content. It's ok to have multiple of these.
- Once you categorize the issue if it needs information bump down the priority by 1 eg.. a p0 would become a p1 a p1 would become a p2. P2 and P3 can stay as is in this scenario.
Categorization Guidelines:
P0: Critical / Blocker
- A P0 bug is a catastrophic failure that demands immediate attention. It represents a complete showstopper for a significant portion of users or for the development process itself.
Impact:
- Blocks development or testing for the entire team.
- Major security vulnerability that could compromise user data or system integrity.
- Causes data loss or corruption with no workaround.
- Crashes the application or makes a core feature completely unusable for all or most users in a production environment. Will it cause severe quality degration?
- Is it preventing contributors from contributing to the repository or is it a release blocker?
Qualifier: Is the main function of the software broken?
Example: The gemini auth login command fails with an unrecoverable error, preventing any user from authenticating and using the rest of the CLI.
P1: High
- A P1 bug is a serious issue that significantly degrades the user experience or impacts a core feature. While not a complete blocker, it's a major problem that needs a fast resolution.
- Feature requests are almost never P1.
Impact:
- A core feature is broken or behaving incorrectly for a large number of users or large number of use cases.
- Review the bug details and comments to try figure out if this issue affects a large set of use cases or if it's a narrow set of use cases.
- Severe performance degradation making the application frustratingly slow.
- No straightforward workaround exists, or the workaround is difficult and non-obvious.
Qualifier: Is a key feature unusable or giving very wrong results?
Example: The gemini -p "..." command consistently returns a malformed JSON response or an empty result, making the CLI's primary generation feature unreliable.
P2: Medium
- A P2 bug is a moderately impactful issue. It's a noticeable problem but doesn't prevent the use of the software's main functionality.
Impact:
- Affects a non-critical feature or a smaller, specific subset of users.
- An inconvenient but functional workaround is available and easy to execute.
- Noticeable UI/UX problems that don't break functionality but look unprofessional (e.g., elements are misaligned or overlapping).
Qualifier: Is it an annoying but non-blocking problem?
Example: An error message is unclear or contains a typo, causing user confusion but not halting their workflow.
P3: Low
- A P3 bug is a minor, low-impact issue that is trivial or cosmetic. It has little to no effect on the overall functionality of the application.
Impact:
- Minor cosmetic issues like color inconsistencies, typos in documentation, or slight alignment problems on a non-critical page.
- An edge-case bug that is very difficult to reproduce and affects a tiny fraction of users.
Qualifier: Is it a "nice-to-fix" issue?
Example: Spelling mistakes etc.
Additional Context:
- If users are talking about issues where the model gets downgraded from pro to flash then i want you to categorize that as a performance issue
- This product is designed to use different models eg.. using pro, downgrading to flash etc.
- When users report that they dont expect the model to change those would be categorized as feature requests.
Definition of Areas
area/ux:
- Issues concerning user-facing elements like command usability, interactive features, help docs, and perceived performance.
- I am seeing my screen flicker when using Gemini CLI
- I am seeing the output malformed
- Theme changes aren't taking effect
- My keyboard inputs arent' being recognzied
area/platform:
- Issues related to installation, packaging, OS compatibility (Windows, macOS, Linux), and the underlying CLI framework.
area/background: Issues related to long-running background tasks, daemons, and autonomous or proactive agent features.
area/models:
- i am not getting a response that is reasonable or expected. this can include things like
- I am calling a tool and the tool is not performing as expected.
- i am expecting a tool to be called and it is not getting called ,
- Including experience when using
- built-in tools (e.g., web search, code interpreter, read file, writefile, etc..),
- Function calling issues should be under this area
- i am getting responses from the model that are malformed.
- Issues concerning Gemini quality of response and inference,
- Issues talking about unnecessary token consumption.
- Issues talking about Model getting stuck in a loop be watchful as this could be the root cause for issues that otherwise seem like model performance issues.
- Memory compression
- unexpected responses,
- poor quality of generated code
area/tools:
- These are primarily issues related to Model Context Protocol
- These are issues that mention MCP support
- feature requests asking for support for new tools.
area/core:
- Issues with fundamental components like command parsing, configuration management, session state, and the main API client logic. Introducing multi-modality
area/contribution:
- Issues related to improving the developer contribution experience, such as CI/CD pipelines, build scripts, and test automation infrastructure.
area/authentication:
- Issues related to user identity, login flows, API key handling, credential storage, and access token management, unable to sign in selecting wrong authentication path etc..
area/security-privacy:
- Issues concerning vulnerability patching, dependency security, data sanitization, privacy controls, and preventing unauthorized data access.
area/extensibility:
- Issues related to the plugin system, extension APIs, or making the CLI's functionality available in other applications, github actions, ide support etc..
area/performance:
- Issues focused on model performance
- Issues with running out of capacity,
- 429 errors etc..
- could also pertain to latency,
- other general software performance like, memory usage, CPU consumption, and algorithmic efficiency.
- Switching models from one to the other unexpectedly.
@@ -0,0 +1,36 @@
name: Gemini Scheduled PR Triage 🚀
on:
schedule:
- cron: '*/15 * * * *' # Runs every 15 minutes
workflow_dispatch: {}
jobs:
audit-prs:
timeout-minutes: 15
if: ${{ github.repository == 'google-gemini/gemini-cli' }}
permissions:
contents: read
id-token: write
issues: write
pull-requests: write
runs-on: ubuntu-latest
outputs:
prs_needing_comment: ${{ steps.run_triage.outputs.prs_needing_comment }}
steps:
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Generate GitHub App Token
id: generate_token
uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.PRIVATE_KEY }}
- name: Run PR Triage Script
id: run_triage
env:
GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
GITHUB_REPOSITORY: ${{ github.repository }}
run: ./.github/scripts/pr-triage.sh
+32
View File
@@ -0,0 +1,32 @@
name: No Response
# Run as a daily cron at 1:45 AM
on:
schedule:
- cron: '45 1 * * *'
workflow_dispatch: {}
jobs:
no-response:
runs-on: ubuntu-latest
if: ${{ github.repository == 'google-gemini/gemini-cli' }}
permissions:
issues: write
pull-requests: write
concurrency:
group: ${{ github.workflow }}-no-response
cancel-in-progress: true
steps:
- uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: -1
days-before-close: 14
stale-issue-label: 'status/need-information'
close-issue-message: >
This issue was marked as needing more information and has not received a response in 14 days.
Closing it for now. If you still face this problem, feel free to reopen with more details. Thank you!
stale-pr-label: 'status/need-information'
close-pr-message: >
This pull request was marked as needing more information and has had no updates in 14 days.
Closing it for now. You are welcome to reopen with the required info. Thanks for contributing!
+173
View File
@@ -0,0 +1,173 @@
name: Release
on:
schedule:
# Runs every day at midnight UTC for the nightly release.
- cron: '0 0 * * *'
workflow_dispatch:
inputs:
version:
description: 'The version to release (e.g., v0.1.11). Required for manual patch releases.'
required: false # Not required for scheduled runs
type: string
ref:
description: 'The branch or ref (full git sha) to release from.'
required: true
type: string
default: 'main'
dry_run:
description: 'Run a dry-run of the release process; no branches, npm packages or GitHub releases will be created.'
required: true
type: boolean
default: true
create_nightly_release:
description: 'Auto apply the nightly release tag, input version is ignored.'
required: false
type: boolean
default: false
force_skip_tests:
description: 'Select to skip the "Run Tests" step in testing. Prod releases should run tests'
required: false
type: boolean
default: false
jobs:
release:
runs-on: ubuntu-latest
environment:
name: production-release
url: ${{ github.server_url }}/${{ github.repository }}/releases/tag/${{ steps.version.outputs.RELEASE_TAG }}
if: github.repository == 'google-gemini/gemini-cli'
permissions:
contents: write
packages: write
id-token: write
issues: write # For creating issues on failure
outputs:
RELEASE_TAG: ${{ steps.version.outputs.RELEASE_TAG }}
steps:
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
ref: ${{ github.sha }}
fetch-depth: 0
- name: Set booleans for simplified logic
id: vars
run: |
is_nightly="false"
if [[ "${{ github.event_name }}" == "schedule" || "${{ github.event.inputs.create_nightly_release }}" == "true" ]]; then
is_nightly="true"
fi
echo "is_nightly=${is_nightly}" >> $GITHUB_OUTPUT
is_dry_run="false"
if [[ "${{ github.event.inputs.dry_run }}" == "true" ]]; then
is_dry_run="true"
fi
echo "is_dry_run=${is_dry_run}" >> $GITHUB_OUTPUT
- name: Setup Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: '20'
cache: 'npm'
- name: Install Dependencies
run: npm ci
- name: Get the version
id: version
run: |
VERSION_JSON=$(node scripts/get-release-version.js)
echo "RELEASE_TAG=$(echo $VERSION_JSON | jq -r .releaseTag)" >> $GITHUB_OUTPUT
echo "RELEASE_VERSION=$(echo $VERSION_JSON | jq -r .releaseVersion)" >> $GITHUB_OUTPUT
echo "NPM_TAG=$(echo $VERSION_JSON | jq -r .npmTag)" >> $GITHUB_OUTPUT
env:
IS_NIGHTLY: ${{ steps.vars.outputs.is_nightly }}
MANUAL_VERSION: ${{ inputs.version }}
- name: Run Tests
if: github.event.inputs.force_skip_tests != 'true'
run: |
npm run preflight
npm run test:integration:sandbox:none
npm run test:integration:sandbox:docker
env:
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
- name: Configure Git User
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
- name: Create and switch to a release branch
id: release_branch
run: |
BRANCH_NAME="release/${{ steps.version.outputs.RELEASE_TAG }}"
git switch -c $BRANCH_NAME
echo "BRANCH_NAME=${BRANCH_NAME}" >> $GITHUB_OUTPUT
- name: Update package versions
run: |
npm run release:version ${{ steps.version.outputs.RELEASE_VERSION }}
- name: Commit and Conditionally Push package versions
run: |
git add package.json package-lock.json packages/*/package.json
git commit -m "chore(release): ${{ steps.version.outputs.RELEASE_TAG }}"
if [[ "${{ steps.vars.outputs.is_dry_run }}" == "false" ]]; then
echo "Pushing release branch to remote..."
git push --set-upstream origin ${{ steps.release_branch.outputs.BRANCH_NAME }} --follow-tags
else
echo "Dry run enabled. Skipping push."
fi
- name: Build and Prepare Packages
run: |
npm run build:packages
npm run prepare:package
- name: Configure npm for publishing
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: '20'
registry-url: 'https://wombat-dressing-room.appspot.com'
scope: '@google'
- name: Publish @google/gemini-cli-core
run: npm publish --workspace=@google/gemini-cli-core --tag=${{ steps.version.outputs.NPM_TAG }} ${{ steps.vars.outputs.is_dry_run == 'true' && '--dry-run' || '' }}
env:
NODE_AUTH_TOKEN: ${{ secrets.WOMBAT_TOKEN_CORE }}
- name: Install latest core package
if: steps.vars.outputs.is_dry_run == 'false'
run: npm install @google/gemini-cli-core@${{ steps.version.outputs.RELEASE_VERSION }} --workspace=@google/gemini-cli --save-exact
- name: Publish @google/gemini-cli
run: npm publish --workspace=@google/gemini-cli --tag=${{ steps.version.outputs.NPM_TAG }} ${{ steps.vars.outputs.is_dry_run == 'true' && '--dry-run' || '' }}
env:
NODE_AUTH_TOKEN: ${{ secrets.WOMBAT_TOKEN_CLI }}
- name: Create GitHub Release and Tag
if: ${{ steps.vars.outputs.is_dry_run == 'false' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
RELEASE_BRANCH: ${{ steps.release_branch.outputs.BRANCH_NAME }}
run: |
gh release create ${{ steps.version.outputs.RELEASE_TAG }} \
bundle/gemini.js \
--target "$RELEASE_BRANCH" \
--title "Release ${{ steps.version.outputs.RELEASE_TAG }}" \
--generate-notes
- name: Create Issue on Failure
if: failure()
run: |
gh issue create \
--title "Release Failed for ${{ steps.version.outputs.RELEASE_TAG || 'N/A' }} on $(date +'%Y-%m-%d')" \
--body "The release workflow failed. See the full run for details: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \
--label "kind/bug,release-failure"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+38
View File
@@ -0,0 +1,38 @@
name: Mark stale issues and pull requests
# Run as a daily cron at 1:30 AM
on:
schedule:
- cron: '30 1 * * *'
workflow_dispatch: {}
jobs:
stale:
runs-on: ubuntu-latest
if: ${{ github.repository == 'google-gemini/gemini-cli' }}
permissions:
issues: write
pull-requests: write
concurrency:
group: ${{ github.workflow }}-stale
cancel-in-progress: true
steps:
- uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: >
This issue has been automatically marked as stale due to 60 days of inactivity.
It will be closed in 14 days if no further activity occurs.
stale-pr-message: >
This pull request has been automatically marked as stale due to 60 days of inactivity.
It will be closed in 14 days if no further activity occurs.
close-issue-message: >
This issue has been closed due to 14 additional days of inactivity after being marked as stale.
If you believe this is still relevant, feel free to comment or reopen the issue. Thank you!
close-pr-message: >
This pull request has been closed due to 14 additional days of inactivity after being marked as stale.
If this is still relevant, you are welcome to reopen or leave a comment. Thanks for contributing!
days-before-stale: 60
days-before-close: 14
exempt-issue-labels: pinned,security
exempt-pr-labels: pinned,security
+3
View File
@@ -6,6 +6,8 @@
.gemini/
!gemini/config.yaml
# Note: .gemini-clipboard/ is NOT in gitignore so Gemini can access pasted images
# Dependency directory
node_modules
bower_components
@@ -36,3 +38,4 @@ packages/*/coverage/
# Generated files
packages/cli/src/generated/
.integration-tests/
packages/vscode-ide-companion/*.vsix
+1 -1
View File
@@ -1 +1 @@
@gemini-cli:registry=https://us-west1-npm.pkg.dev/gemini-code-dev/gemini-code/
@google:registry=https://wombat-dressing-room.appspot.com
+1
View File
@@ -0,0 +1 @@
20
+21 -5
View File
@@ -12,19 +12,35 @@
"runtimeArgs": ["run", "start"],
"skipFiles": ["<node_internals>/**"],
"cwd": "${workspaceFolder}",
"console": "integratedTerminal",
"env": {
"GEMINI_SANDBOX": "false",
"GEMINI_API_KEY": "testkey"
"GEMINI_SANDBOX": "false"
}
},
{
"type": "node",
"request": "launch",
"name": "Launch E2E",
"runtimeExecutable": "npm",
"runtimeArgs": ["run", "test:e2e", "read_many_files"],
"program": "${workspaceFolder}/integration-tests/run-tests.js",
"args": ["--verbose", "--keep-output", "list_directory"],
"skipFiles": ["<node_internals>/**"],
"cwd": "${workspaceFolder}"
"cwd": "${workspaceFolder}",
"console": "integratedTerminal",
"env": {
"GEMINI_SANDBOX": "false"
}
},
{
"name": "Launch Companion VS Code Extension",
"type": "extensionHost",
"request": "launch",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}/packages/vscode-ide-companion"
],
"outFiles": [
"${workspaceFolder}/packages/vscode-ide-companion/dist/**/*.js"
],
"preLaunchTask": "npm: build: vscode-ide-companion"
},
{
"name": "Attach",
+9
View File
@@ -11,6 +11,15 @@
"problemMatcher": [],
"label": "npm: build",
"detail": "scripts/build.sh"
},
{
"type": "npm",
"script": "build",
"path": "packages/vscode-ide-companion",
"group": "build",
"problemMatcher": [],
"label": "npm: build: vscode-ide-companion",
"detail": "npm run build -w packages/vscode-ide-companion"
}
]
}
+17 -13
View File
@@ -76,9 +76,9 @@ In the PR description, explain the "why" behind your changes and link to the rel
## Forking
If you are forking the repository you will be able to run the Built, Test and Integration test workflows. However in order to make the integration tests run you'll need to add a [Github Repository Secret](<[url](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions#creating-secrets-for-a-repository)>) with a value of `GEMINI_API_KEY` and set that to a valid API key that you have available. Your key and secret it private to your repo; no one without access can see your key and you cannot see any secrets related to this repo.
If you are forking the repository you will be able to run the Build, Test and Integration test workflows. However in order to make the integration tests run you'll need to add a [GitHub Repository Secret](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions#creating-secrets-for-a-repository) with a value of `GEMINI_API_KEY` and set that to a valid API key that you have available. Your key and secret are private to your repo; no one without access can see your key and you cannot see any secrets related to this repo.
Additionally you will need to click on the `Actions` tab and enable workflows for your repository, you'll find its the large blue button in the center of the screen.
Additionally you will need to click on the `Actions` tab and enable workflows for your repository, you'll find it's the large blue button in the center of the screen.
## Development Setup and Workflow
@@ -88,8 +88,10 @@ This section guides contributors on how to build, modify, and understand the dev
**Prerequisites:**
1. Install [Node 18+](https://nodejs.org/en/download)
2. Git
1. **Node.js**:
- **Development:** Please use Node.js `~20.19.0`. This specific version is required due to an upstream development dependency issue. You can use a tool like [nvm](https://github.com/nvm-sh/nvm) to manage Node.js versions.
- **Production:** For running the CLI in a production environment, any version of Node.js `>=20` is acceptable.
2. **Git**
### Build Process
@@ -116,7 +118,7 @@ This command typically compiles TypeScript to JavaScript, bundles assets, and pr
### Enabling Sandboxing
Container-based [sandboxing](#sandboxing) is highly recommended and requires, at a minimum, setting `GEMINI_SANDBOX=true` in your `~/.env` and ensuring a container engine (e.g. `docker` or `podman`) is available. See [Sandboxing](#sandboxing) for details.
[Sandboxing](#sandboxing) is highly recommended and requires, at a minimum, setting `GEMINI_SANDBOX=true` in your `~/.env` and ensuring a sandboxing provider (e.g. `macOS Seatbelt`, `docker`, or `podman`) is available. See [Sandboxing](#sandboxing) for details.
To build both the `gemini` CLI utility and the sandbox container, run `build:all` from the root directory:
@@ -134,7 +136,7 @@ To start the Gemini CLI from the source code (after building), run the following
npm start
```
If youd like to run the source build outside of the gemini-cli folder you can utilize `npm link path/to/gemini-cli/packages/cli` (see: [docs](https://docs.npmjs.com/cli/v9/commands/npm-link)) or `alias gemini="node path/to/gemini-cli/packages/cli"` to run with `gemini`
If you'd like to run the source build outside of the gemini-cli folder you can utilize `npm link path/to/gemini-cli/packages/cli` (see: [docs](https://docs.npmjs.com/cli/v9/commands/npm-link)) or `alias gemini="node path/to/gemini-cli/packages/cli"` to run with `gemini`
### Running Tests
@@ -198,7 +200,7 @@ This command uses Prettier to format the code according to the project's style g
#### Linting
To separately lint the code in this project, run the following command fro the root directory:
To separately lint the code in this project, run the following command from the root directory:
```bash
npm run lint
@@ -208,13 +210,13 @@ npm run lint
- Please adhere to the coding style, patterns, and conventions used throughout the existing codebase.
- Consult [GEMINI.md](https://github.com/google-gemini/gemini-cli/blob/main/GEMINI.md) (typically found in the project root) for specific instructions related to AI-assisted development, including conventions for React, comments, and Git usage.
- **Imports:** Pay special attention to import paths. The project uses `eslint-rules/no-relative-cross-package-imports.js` to enforce restrictions on relative imports between packages.
- **Imports:** Pay special attention to import paths. The project uses ESLint to enforce restrictions on relative imports between packages.
### Project Structure
- `packages/`: Contains the individual sub-packages of the project.
- `cli/`: The command-line interface.
- `server/`: The backend server that the CLI interacts with.
- `core/`: The core backend logic for the Gemini CLI.
- `docs/`: Contains all project documentation.
- `scripts/`: Utility scripts for building, testing, and development tasks.
@@ -240,6 +242,8 @@ To hit a breakpoint inside the sandbox container run:
DEBUG=1 gemini
```
**Note:** If you have `DEBUG=true` in a project's `.env` file, it won't affect gemini-cli due to automatic exclusion. Use `.gemini/.env` files for gemini-cli specific debug settings.
### React DevTools
To debug the CLI's React-based UI, you can use React DevTools. Ink, the library used for the CLI's interface, is compatible with React DevTools version 4.x.
@@ -270,19 +274,19 @@ To debug the CLI's React-based UI, you can use React DevTools. Ink, the library
## Sandboxing
### MacOS Seatbelt
### macOS Seatbelt
On MacOS, `gemini` uses Seatbelt (`sandbox-exec`) under a `permissive-open` profile (see `packages/cli/src/utils/sandbox-macos-permissive-open.sb`) that restricts writes to the project folder but otherwise allows all other operations and outbound network traffic ("open") by default. You can switch to a `restrictive-closed` profile (see `.../sandbox-macos-strict.sb`) that declines all operations and outbound network traffic ("closed") by default by setting `SEATBELT_PROFILE=restrictive-closed` in your environment or `.env` file. Available built-in profiles are `{permissive,restrictive}-{open,closed,proxied}` (see below for proxied networking). You can also switch to a custom profile `SEATBELT_PROFILE=<profile>` if you also create a file `.gemini/sandbox-macos-<profile>.sb` under your project settings directory `.gemini`.
On macOS, `gemini` uses Seatbelt (`sandbox-exec`) under a `permissive-open` profile (see `packages/cli/src/utils/sandbox-macos-permissive-open.sb`) that restricts writes to the project folder but otherwise allows all other operations and outbound network traffic ("open") by default. You can switch to a `restrictive-closed` profile (see `packages/cli/src/utils/sandbox-macos-restrictive-closed.sb`) that declines all operations and outbound network traffic ("closed") by default by setting `SEATBELT_PROFILE=restrictive-closed` in your environment or `.env` file. Available built-in profiles are `{permissive,restrictive}-{open,closed,proxied}` (see below for proxied networking). You can also switch to a custom profile `SEATBELT_PROFILE=<profile>` if you also create a file `.gemini/sandbox-macos-<profile>.sb` under your project settings directory `.gemini`.
### Container-based Sandboxing (All Platforms)
For stronger container-based sandboxing on MacOS or other platforms, you can set `GEMINI_SANDBOX=true|docker|podman|<command>` in your environment or `.env` file. The specified command (or if `true` then either `docker` or `podman`) must be installed on the host machine. Once enabled, `npm run build:all` will build a minimal container ("sandbox") image and `npm start` will launch inside a fresh instance of that container. The first build can take 20-30s (mostly due to downloading of the base image) but after that both build and start overhead should be minimal. Default builds (`npm run build`) will not rebuild the sandbox.
For stronger container-based sandboxing on macOS or other platforms, you can set `GEMINI_SANDBOX=true|docker|podman|<command>` in your environment or `.env` file. The specified command (or if `true` then either `docker` or `podman`) must be installed on the host machine. Once enabled, `npm run build:all` will build a minimal container ("sandbox") image and `npm start` will launch inside a fresh instance of that container. The first build can take 20-30s (mostly due to downloading of the base image) but after that both build and start overhead should be minimal. Default builds (`npm run build`) will not rebuild the sandbox.
Container-based sandboxing mounts the project directory (and system temp directory) with read-write access and is started/stopped/removed automatically as you start/stop Gemini CLI. Files created within the sandbox should be automatically mapped to your user/group on host machine. You can easily specify additional mounts, ports, or environment variables by setting `SANDBOX_{MOUNTS,PORTS,ENV}` as needed. You can also fully customize the sandbox for your projects by creating the files `.gemini/sandbox.Dockerfile` and/or `.gemini/sandbox.bashrc` under your project settings directory (`.gemini`) and running `gemini` with `BUILD_SANDBOX=1` to trigger building of your custom sandbox.
#### Proxied Networking
All sandboxing methods, including MacOS Seatbelt using `*-proxied` profiles, support restricting outbound network traffic through a custom proxy server that can be specified as `GEMINI_SANDBOX_PROXY_COMMAND=<command>`, where `<command>` must start a proxy server that listens on `:::8877` for relevant requests. See `scripts/example-proxy.js` for a minimal proxy that only allows `HTTPS` connections to `example.com:443` (e.g. `curl https://example.com`) and declines all other requests. The proxy is started and stopped automatically alongside the sandbox.
All sandboxing methods, including macOS Seatbelt using `*-proxied` profiles, support restricting outbound network traffic through a custom proxy server that can be specified as `GEMINI_SANDBOX_PROXY_COMMAND=<command>`, where `<command>` must start a proxy server that listens on `:::8877` for relevant requests. See `docs/examples/proxy-script.md` for a minimal proxy that only allows `HTTPS` connections to `example.com:443` (e.g. `curl https://example.com`) and declines all other requests. The proxy is started and stopped automatically alongside the sandbox.
## Manual Publish
+4 -4
View File
@@ -40,11 +40,11 @@ ENV PATH=$PATH:/usr/local/share/npm-global/bin
USER node
# install gemini-cli and clean up
COPY packages/cli/dist/gemini-cli-cli-*.tgz /usr/local/share/npm-global/gemini-cli-cli.tgz
COPY packages/core/dist/gemini-cli-core-*.tgz /usr/local/share/npm-global/gemini-cli-core.tgz
RUN npm install -g /usr/local/share/npm-global/gemini-cli-cli.tgz /usr/local/share/npm-global/gemini-cli-core.tgz \
COPY packages/cli/dist/google-gemini-cli-*.tgz /usr/local/share/npm-global/gemini-cli.tgz
COPY packages/core/dist/google-gemini-cli-core-*.tgz /usr/local/share/npm-global/gemini-core.tgz
RUN npm install -g /usr/local/share/npm-global/gemini-cli.tgz /usr/local/share/npm-global/gemini-core.tgz \
&& npm cache clean --force \
&& rm -f /usr/local/share/npm-global/gemini-cli-{cli,core}.tgz
&& rm -f /usr/local/share/npm-global/gemini-{cli,core}.tgz
# default entrypoint when none specified
CMD ["gemini"]
+8 -5
View File
@@ -71,7 +71,8 @@ JavaScript classes, by their nature, are designed to encapsulate internal state
- Reduced Boilerplate and Increased Conciseness: Classes often promote the use of constructors, this binding, getters, setters, and other boilerplate that can unnecessarily bloat code. TypeScript interface and type declarations provide powerful static type checking without the runtime overhead or verbosity of class definitions. This allows for more succinct and readable code, aligning with JavaScript's strengths in functional programming.
- Enhanced Readability and Predictability: Plain objects, especially when their structure is clearly defined by TypeScript interfaces, are often easier to read and understand. Their properties are directly accessible, and there's no hidden internal state or complex inheritance chains to navigate. This predictability leads to fewer bugs and a more maintainable codebase.
Simplified Immutability: While not strictly enforced, plain objects encourage an immutable approach to data. When you need to modify an object, you typically create a new one with the desired changes, rather than mutating the original. This pattern aligns perfectly with React's reconciliation process and helps prevent subtle bugs related to shared mutable state.
- Simplified Immutability: While not strictly enforced, plain objects encourage an immutable approach to data. When you need to modify an object, you typically create a new one with the desired changes, rather than mutating the original. This pattern aligns perfectly with React's reconciliation process and helps prevent subtle bugs related to shared mutable state.
- Better Serialization and Deserialization: Plain JavaScript objects are naturally easy to serialize to JSON and deserialize back, which is a common requirement in web development (e.g., for API communication or local storage). Classes, with their methods and prototypes, can complicate this process.
@@ -90,7 +91,6 @@ Rather than relying on Java-esque private or public class members, which can be
TypeScript's power lies in its ability to provide static type checking, catching potential errors before your code runs. To fully leverage this, it's crucial to avoid the `any` type and be judicious with type assertions.
- **The Dangers of `any`**: Using any effectively opts out of TypeScript's type checking for that particular variable or expression. While it might seem convenient in the short term, it introduces significant risks:
- **Loss of Type Safety**: You lose all the benefits of type checking, making it easy to introduce runtime errors that TypeScript would otherwise have caught.
- **Reduced Readability and Maintainability**: Code with `any` types is harder to understand and maintain, as the expected type of data is no longer explicitly defined.
- **Masking Underlying Issues**: Often, the need for any indicates a deeper problem in the design of your code or the way you're interacting with external libraries. It's a sign that you might need to refine your types or refactor your code.
@@ -122,7 +122,7 @@ To further enhance code cleanliness and promote safe functional programming prac
Using these operators:
- Promotes Immutability: Most array operators return new arrays, leaving the original array untouched. This functional approach helps prevent unintended side effects and makes your code more predictable.
- Improves Readability: Chaining array operators often leads to more concise and expressive code than traditional for loops or imperative logic. The intent of the operation is clear at a glance.
- Improves Readability: Chaining array operators often lead to more concise and expressive code than traditional for loops or imperative logic. The intent of the operation is clear at a glance.
- Facilitates Functional Programming: These operators are cornerstones of functional programming, encouraging the creation of pure functions that take inputs and produce outputs without causing side effects. This paradigm is highly beneficial for writing robust and testable code that pairs well with React.
By consistently applying these principles, we can maintain a codebase that is not only efficient and performant but also a joy to work with, both now and in the future.
@@ -151,7 +151,7 @@ Use refs only when necessary: Avoid using useRef unless the task genuinely requi
Prefer composition and small components: Break down UI into small, reusable components rather than writing large monolithic components. The code you generate should promote clarity and reusability by composing components together. Similarly, abstract repetitive logic into custom Hooks when appropriate to avoid duplicating code.
Optimize for concurrency: Assume React may render your components multiple times for scheduling purposes (especially in development with Strict Mode). Write code that remains correct even if the component function runs more than once. For instance, avoid side effects in the component body and use functional state updates (e.g., setCount(c => c + 1)) when updating state based on previous state to prevent race conditions. Always include cleanup functions in effects that subscribe to external resources. Don't write useEffects for "do this when this changes" side-effects. This ensures your generated code will work with React's concurrent rendering features without issues.
Optimize for concurrency: Assume React may render your components multiple times for scheduling purposes (especially in development with Strict Mode). Write code that remains correct even if the component function runs more than once. For instance, avoid side effects in the component body and use functional state updates (e.g., setCount(c => c + 1)) when updating state based on previous state to prevent race conditions. Always include cleanup functions in effects that subscribe to external resources. Don't write useEffects for "do this when this changes" side effects. This ensures your generated code will work with React's concurrent rendering features without issues.
Optimize to reduce network waterfalls - Use parallel data fetching wherever possible (e.g., start multiple requests at once rather than one after another). Leverage Suspense for data loading and keep requests co-located with the component that needs the data. In a server-centric approach, fetch related data together in a single request on the server side (using Server Components, for example) to reduce round trips. Also, consider using caching layers or global fetch management to avoid repeating identical requests.
@@ -162,7 +162,6 @@ Design for a good user experience - Provide clear, minimal, and non-blocking UI
### Process
1. Analyze the user's code for optimization opportunities:
- Check for React anti-patterns that prevent compiler optimization
- Look for component structure issues that limit compiler effectiveness
- Think about each suggestion you are making and consult React docs for best practices
@@ -180,3 +179,7 @@ Design for a good user experience - Provide clear, minimal, and non-blocking UI
## Comments policy
Only write high-value comments if at all. Avoid talking to the user through comments.
## General style requirements
Use hyphens instead of underscores in flag names (e.g. `my-flag` instead of `my_flag`).
+1 -1
View File
@@ -187,7 +187,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Copyright 2025 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+3 -8
View File
@@ -7,9 +7,8 @@ help:
@echo ""
@echo "Usage:"
@echo " make install - Install npm dependencies"
@echo " make build - Build the entire project"
@echo " make build-sandbox - Build the sandbox container"
@echo " make build-all - Build the project and the sandbox"
@echo " make build - Build the main project"
@echo " make build-all - Build the main project and sandbox"
@echo " make test - Run the test suite"
@echo " make lint - Lint the code"
@echo " make format - Format the code"
@@ -17,7 +16,7 @@ help:
@echo " make clean - Remove generated files"
@echo " make start - Start the Gemini CLI"
@echo " make debug - Start the Gemini CLI in debug mode"
@echo " make release - Publish a new release"
@echo ""
@echo " make run-npx - Run the CLI using npx (for testing the published package)"
@echo " make create-alias - Create a 'gemini' alias for your shell"
@@ -27,8 +26,6 @@ install:
build:
npm run build
build-sandbox:
npm run build:sandbox
build-all:
npm run build:all
@@ -54,8 +51,6 @@ start:
debug:
npm run debug
release:
npm run publish:release
run-npx:
npx https://github.com/google-gemini/gemini-cli
+193 -99
View File
@@ -2,120 +2,214 @@
[![Gemini CLI CI](https://github.com/google-gemini/gemini-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/google-gemini/gemini-cli/actions/workflows/ci.yml)
This repository contains the Gemini CLI tool.
![Gemini CLI Screenshot](./docs/assets/gemini-screenshot.png)
This repository contains the Gemini CLI, a command-line AI workflow tool that connects to your
tools, understands your code and accelerates your workflows.
With the Gemini CLI you can:
- Query and edit large codebases in and beyond Gemini's 1M token context window.
- Generate new apps from PDFs or sketches, using Gemini's multimodal capabilities.
- Automate operational tasks, like querying pull requests or handling complex rebases.
- Use tools and MCP servers to connect new capabilities, including [media generation with Imagen,
Veo or Lyria](https://github.com/GoogleCloudPlatform/vertex-ai-creative-studio/tree/main/experiments/mcp-genmedia)
- Ground your queries with the [Google Search](https://ai.google.dev/gemini-api/docs/grounding)
tool, built into Gemini.
## Quickstart
1. [Install Node 18+](https://nodejs.org/en/download).
2. [Get an API key from Google AI Studio](https://aistudio.google.com/apikey).
3. Set the API key in your shell using the following command, replacing `YOUR_API_KEY` with the API key you obtained: `export GEMINI_API_KEY="YOUR_API_KEY"`.
4. Run the Gemini CLI from your shell using the following command: `npx https://github.com/google-gemini/gemini-cli`
5. Enjoy.
You have two options to install Gemini CLI.
### With Node
1. **Prerequisites:** Ensure you have [Node.js version 20](https://nodejs.org/en/download) or higher installed.
2. **Run the CLI:** Execute the following command in your terminal:
```bash
npx https://github.com/google-gemini/gemini-cli
```
Or install it with:
```bash
npm install -g @google/gemini-cli
```
Then, run the CLI from anywhere:
```bash
gemini
```
### With Homebrew
1. **Prerequisites:** Ensure you have [Homebrew](https://brew.sh/) installed.
2. **Install the CLI:** Execute the following command in your terminal:
```bash
brew install gemini-cli
```
Then, run the CLI from anywhere:
```bash
gemini
```
### Common Configuration steps
3. **Pick a color theme**
4. **Authenticate:** When prompted, sign in with your personal Google account. This will grant you up to 60 model requests per minute and 1,000 model requests per day using Gemini.
You are now ready to use the Gemini CLI!
### Use a Gemini API key:
The Gemini API provides a free tier with [100 requests per day](https://ai.google.dev/gemini-api/docs/rate-limits#free-tier) using Gemini 2.5 Pro, control over which model you use, and access to higher rate limits (with a paid plan):
1. Generate a key from [Google AI Studio](https://aistudio.google.com/apikey).
2. Set it as an environment variable in your terminal. Replace `YOUR_API_KEY` with your generated key.
```bash
export GEMINI_API_KEY="YOUR_API_KEY"
```
3. (Optionally) Upgrade your Gemini API project to a paid plan on the API key page (will automatically unlock [Tier 1 rate limits](https://ai.google.dev/gemini-api/docs/rate-limits#tier-1))
### Use a Vertex AI API key:
The Vertex AI API provides a [free tier](https://cloud.google.com/vertex-ai/generative-ai/docs/start/express-mode/overview) using express mode for Gemini 2.5 Pro, control over which model you use, and access to higher rate limits with a billing account:
1. Generate a key from [Google Cloud](https://cloud.google.com/vertex-ai/generative-ai/docs/start/api-keys).
2. Set it as an environment variable in your terminal. Replace `YOUR_API_KEY` with your generated key and set GOOGLE_GENAI_USE_VERTEXAI to true
```bash
export GOOGLE_API_KEY="YOUR_API_KEY"
export GOOGLE_GENAI_USE_VERTEXAI=true
```
3. (Optionally) Add a billing account on your project to get access to [higher usage limits](https://cloud.google.com/vertex-ai/generative-ai/docs/quotas)
For other authentication methods, including Google Workspace accounts, see the [authentication](./docs/cli/authentication.md) guide.
## Examples
Once the CLI is running, you can start interacting with Gemini from your shell. Try a simple query:
Once the CLI is running, you can start interacting with Gemini from your shell.
```
> How can I build a web app?
You can start a project from a new directory:
```sh
cd new-project/
gemini
> Write me a Gemini Discord bot that answers questions using a FAQ.md file I will provide
```
Or ask it to perform a task using its tools:
Or work with an existing project:
```
> List files in the current directory.
```sh
git clone https://github.com/google-gemini/gemini-cli
cd gemini-cli
gemini
> Give me a summary of all of the changes that went in yesterday
```
## API Key Setup
The Gemini CLI requires you to authenticate with Google's AI services. You'll need to configure **one** of the following authentication methods:
1. **Gemini Code Assist:**
- To enable this mode you only need set the GEMINI_CODE_ASSIST environment variable to true.
- You can temporarily set the environment variable in your current shell session using the following command:
```bash
export GEMINI_CODE_ASSIST="true"
```
- For repeated use, you can add the environment variable to your `.env` file (located in the project directory or user home directory) or your shell's configuration file (like `~/.bashrc`, `~/.zshrc`, or `~/.profile`). For example, the following command adds the environment variable to a `~/.bashrc` file:
```bash
echo 'export GEMINI_CODE_ASSIST="true"' >> ~/.bashrc
source ~/.bashrc
```
- There are two types of Google Accounts you can use with Gemini CLI:
- **Personal Google Account**: This is the standard, free account you use for services like Gmail, Google Photos, and Google Drive for personal use (e.g. your-name@gmail.com).
- **Google Workspace Account**: This is a paid service for businesses and organizations that provides a suite of productivity tools, including a custom email domain (e.g. your-name@your-company.com), enhanced security features, and administrative controls. These accounts are often managed by an employer or school.
- Google Workspace Account must configure a Google Cloud Project Id to use. You can temporarily set the environment variable in your current shell session using the following command:
```bash
export GOOGLE_CLOUD_PROJECT_ID="YOUR_PROJECT_ID"
```
- For repeated use, you can add the environment variable to your `.env` file (located in the project directory or user home directory) or your shell's configuration file (like `~/.bashrc`, `~/.zshrc`, or `~/.profile`). For example, the following command adds the environment variable to a `~/.bashrc` file:
```bash
echo 'export GOOGLE_CLOUD_PROJECT_ID="YOUR_PROJECT_ID"' >> ~/.bashrc
source ~/.bashrc
```
- During start up, Gemini CLI will direct you to a webpage for authentication. Once authenticated, your credentials will be cached locally so the web login can be skipped on subsequent runs. Cached credentials last about 20 hours before expiring.
- Note that the the web login must be done in a browser that can communicate with the machine Gemini Cli is being run from. (Specifically, the browser will be redirected to a localhost url that Gemini CLI will be listening on).
2. **Gemini API key:**
- Obtain your API key from Google AI Studio: [https://aistudio.google.com/app/apikey](https://aistudio.google.com/app/apikey)
- Set the `GEMINI_API_KEY` environment variable. In the following methods, replace `YOUR_GEMINI_API_KEY` with the API key you obtained from Google AI Studio:
- You can temporarily set the environment variable in your current shell session using the following command:
```bash
export GEMINI_API_KEY="YOUR_GEMINI_API_KEY"
```
- For repeated use, you can add the environment variable to your `.env` file (located in the project directory or user home directory) or your shell's configuration file (like `~/.bashrc`, `~/.zshrc`, or `~/.profile`). For example, the following command adds the environment variable to a `~/.bashrc` file:
```bash
echo 'export GEMINI_API_KEY="YOUR_GEMINI_API_KEY"' >> ~/.bashrc
source ~/.bashrc
```
3. **Google API Key (Vertex AI Express Mode):**
- You can use a general Google Cloud API key if it has been enabled for the Gemini API or Vertex AI.
- Set the `GOOGLE_API_KEY` and `GOOGLE_GENAI_USE_VERTEXAI` environment variables. In the following methods, replace `YOUR_GEMINI_API_KEY` with your Google Cloud API key:
- You can temporarily set these environment variables in your current shell session using the following commands:
```bash
export GOOGLE_API_KEY="YOUR_GOOGLE_API_KEY"
export GOOGLE_GENAI_USE_VERTEXAI=true
```
- For repeated use, you can add the environment variables to your `.env` file (located in the project directory or user home directory) or your shell's configuration file (like `~/.bashrc`, `~/.zshrc`, or `~/.profile`). For example, the following commands adds the environment variables to a `~/.bashrc` file:
```bash
echo 'export GEMINI_API_KEY="YOUR_GEMINI_API_KEY"' >> ~/.bashrc
echo 'export GOOGLE_GENAI_USE_VERTEXAI=true' >> ~/.bashrc
source ~/.bashrc
```
4. **Vertex AI (Project and Location):**
- Ensure you have a Google Cloud project and have enabled the Vertex AI API.
- Set up Application Default Credentials (ADC), using the following command:
```bash
gcloud auth application-default login
```
For more information, see [Set up Application Default Credentials for Google Cloud](https://cloud.google.com/docs/authentication/provide-credentials-adc).
- Set the `GOOGLE_CLOUD_PROJECT`, `GOOGLE_CLOUD_LOCATION`, and `GOOGLE_GENAI_USE_VERTEXAI` environment variables. In the following methods, replace `YOUR_PROJECT_ID` and `YOUR_PROJECT_LOCATION` with the relevant values for your project:
- You can temporarily set these environment variables in your current shell session using the following commands:
```bash
export GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"
export GOOGLE_CLOUD_LOCATION="YOUR_PROJECT_LOCATION" # e.g., us-central1
export GOOGLE_GENAI_USE_VERTEXAI=true
```
- For repeated use, you can add the environment variables to your `.env` file (located in the project directory or user home directory) or your shell's configuration file (like `~/.bashrc`, `~/.zshrc`, or `~/.profile`). For example, the following commands adds the environment variables to a `~/.bashrc` file:
```bash
echo 'export GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"' >> ~/.bashrc
echo 'export GOOGLE_CLOUD_LOCATION="YOUR_PROJECT_LOCATION"' >> ~/.bashrc
echo 'export GOOGLE_GENAI_USE_VERTEXAI=true' >> ~/.bashrc
source ~/.bashrc
```
### Next Steps
### Next steps
- Learn how to [contribute to or build from the source](./CONTRIBUTING.md).
- Explore the available **[CLI Commands](./docs/cli/commands.md)**.
- If you encounter any issues, review the **[Troubleshooting guide](./docs/troubleshooting.md)**.
- If you encounter any issues, review the **[troubleshooting guide](./docs/troubleshooting.md)**.
- For more comprehensive documentation, see the [full documentation](./docs/index.md).
- Take a look at some [popular tasks](#popular-tasks) for more inspiration.
- Check out our **[Official Roadmap](./ROADMAP.md)**
## Gemini APIs
### Troubleshooting
This project leverages the Gemini APIs to provide AI capabilities. For details on the terms of service governing the Gemini API, please refer to the [Gemini API Terms of Service](https://ai.google.dev/gemini-api/terms).
Head over to the [troubleshooting guide](docs/troubleshooting.md) if you're
having issues.
## Popular tasks
### Explore a new codebase
Start by `cd`ing into an existing or newly-cloned repository and running `gemini`.
```text
> Describe the main pieces of this system's architecture.
```
```text
> What security mechanisms are in place?
```
```text
> Provide a step-by-step dev onboarding doc for developers new to the codebase.
```
```text
> Summarize this codebase and highlight the most interesting patterns or techniques I could learn from.
```
```text
> Identify potential areas for improvement or refactoring in this codebase, highlighting parts that appear fragile, complex, or hard to maintain.
```
```text
> Which parts of this codebase might be challenging to scale or debug?
```
```text
> Generate a README section for the [module name] module explaining what it does and how to use it.
```
```text
> What kind of error handling and logging strategies does the project use?
```
```text
> Which tools, libraries, and dependencies are used in this project?
```
### Work with your existing code
```text
> Implement a first draft for GitHub issue #123.
```
```text
> Help me migrate this codebase to the latest version of Java. Start with a plan.
```
### Automate your workflows
Use MCP servers to integrate your local system tools with your enterprise collaboration suite.
```text
> Make me a slide deck showing the git history from the last 7 days, grouped by feature and team member.
```
```text
> Make a full-screen web app for a wall display to show our most interacted-with GitHub issues.
```
### Interact with your system
```text
> Convert all the images in this directory to png, and rename them to use dates from the exif data.
```
```text
> Organize my PDF invoices by month of expenditure.
```
### Uninstall
Head over to the [Uninstall](docs/Uninstall.md) guide for uninstallation instructions.
## Terms of Service and Privacy Notice
For details on the terms of service and privacy notice applicable to your use of Gemini CLI, see the [Terms of Service and Privacy Notice](./docs/tos-privacy.md).
## Security Disclosures
Please see our [security disclosure process](SECURITY.md). All [security advisories](https://github.com/google-gemini/gemini-cli/security/advisories) are managed on Github.
+63
View File
@@ -0,0 +1,63 @@
# Gemini CLI Roadmap
The [Official Gemini CLI Roadmap](https://github.com/orgs/google-gemini/projects/11/)
Gemini CLI is an open-source AI agent that brings the power of Gemini directly into your terminal. It provides lightweight access to Gemini, giving you the most direct path from your prompt to our model.
This document outlines our approach to the Gemini CLI roadmap. Here, you'll find our guiding principles and a breakdown of the key areas we are
focused on for development. Our roadmap is not a static list but a dynamic set of priorities that are tracked live in our GitHub Issues.
As an [Apache 2.0 open source project](https://github.com/google-gemini/gemini-cli?tab=Apache-2.0-1-ov-file#readme), we appreciate and welcome [public contributions](https://github.com/google-gemini/gemini-cli/blob/main/CONTRIBUTING.md), and will give first priority to those contributions aligned with our roadmap. If you want to propose a new feature or change to our roadmap, please start by [opening an issue for discussion](https://github.com/google-gemini/gemini-cli/issues/new/choose).
## Disclaimer
This roadmap represents our current thinking and is for informational purposes only. It is not a commitment or a guarantee of future delivery. The development, release, and timing of any features are subject to change, and we may update the roadmap based on community discussions as well as when our priorities evolve.
## Guiding Principles
Our development is guided by the following principles:
- **Power & Simplicity:** Deliver access to state-of-the-art Gemini models with an intuitive and easy-to-use lightweight command-line interface.
- **Extensibility:** An adaptable agent to help you with a variety of use cases and environments along with the ability to run these agents anywhere.
- **Intelligent:** Gemini CLI should be reliably ranked among the best agentic tools as measured by benchmarks like SWE Bench, Terminal Bench, and CSAT.
- **Free and Open Source:** Foster a thriving open source community where cost isnt a barrier to personal use, and PRs get merged quickly. This means resolving and closing issues, pull requests, and discussion posts quickly.
## How the Roadmap Works
Our roadmap is managed directly through Github Issues. See our entry point Roadmap Issue [here](https://github.com/google-gemini/gemini-cli/issues/4191). This approach allows for transparency and gives you a direct way to learn more or get involved with any specific initiative. All our roadmap items will be tagged as Type:`Feature` and Label:`maintainer` for features we are actively working on, or Type:`Task` and Label:`maintainer` for a more detailed list of tasks.
Issues are organized to provide key information at a glance:
- **Target Quarter:** `Milestone` denotes the anticipated delivery timeline.
- **Feature Area:** Labels such as `area/model` or `area/tooling` categorizes the work.
- **Issue Type:** _Workstream_ => _Epics_ => _Features_ => _Tasks|Bugs_
To see what we're working on, you can filter our issues by these dimensions. See all our items [here](https://github.com/orgs/google-gemini/projects/11/views/19)
## Focus Areas
To better organize our efforts, we categorize our work into several key feature areas. These labels are used on our GitHub Issues to help you filter and
find initiatives that interest you.
- **Authentication:** Secure user access via API keys, Gemini Code Assist login etc.
- **Model:** Support new Gemini models, multi-modality, local execution, and performance tuning.
- **User Experience:** Improve the CLI's usability, performance, interactive features, and documentation.
- **Tooling:** Built-in tools and the MCP ecosystem.
- **Core:** Core functionality of the CLI
- **Extensibility:** Bringing Gemini CLI to other surfaces e.g. GitHub.
- **Contribution:** Improve the contribution process via test automation and CI/CD pipeline enhancements.
- **Platform:** Manage installation, OS support, and the underlying CLI framework.
- **Quality:** Focus on testing, reliability, performance, and overall product quality.
- **Background Agents:** Enable long-running, autonomous tasks and proactive assistance.
- **Security and Privacy:** For all things related to security and privacy
## How to Contribute
Gemini CLI is an open-source project, and we welcome contributions from the community! Whether you're a developer, a designer, or just an enthusiastic user you can find our [Community Guidelines here](https://github.com/google-gemini/gemini-cli/blob/main/CONTRIBUTING.md) to learn how to get started. There are many ways to get involved:
- **Roadmap:** Please review and find areas in our [roadmap](https://github.com/google-gemini/gemini-cli/issues/4191) that you would like to contribute to. Contributions based on this will be easiest to integrate with.
- **Report Bugs:** If you find an issue, please create a [bug](https://github.com/google-gemini/gemini-cli/issues/new?template=bug_report.yml) with as much detail as possible. If you believe it is a critical breaking issue preventing direct CLI usage, please tag it as `priority/p0`.
- **Suggest Features:** Have a great idea? We'd love to hear it! Open a [feature request](https://github.com/google-gemini/gemini-cli/issues/new?template=feature_request.yml).
- **Contribute Code:** Check out our [CONTRIBUTING.md](https://github.com/google-gemini/gemini-cli/blob/main/CONTRIBUTING.md) file for guidelines on how to submit pull requests. We have a list of "good first issues" for new contributors.
- **Write Documentation:** Help us improve our documentation, tutorials, and examples.
We are excited about the future of Gemini CLI and look forward to building it with you!
+8
View File
@@ -0,0 +1,8 @@
# Reporting Security Issues
To report a security issue, please use [https://g.co/vulnz](https://g.co/vulnz).
We use g.co/vulnz for our intake, and do coordination and disclosure here on
GitHub (including using GitHub Security Advisory). The Google Security Team will
respond within 5 working days of your report on g.co/vulnz.
[GitHub Security Advisory]: https://github.com/google-gemini/gemini-cli/security/advisories
+42
View File
@@ -0,0 +1,42 @@
# Uninstalling the CLI
Your uninstall method depends on how you ran the CLI. Follow the instructions for either npx or a global npm installation.
## Method 1: Using npx
npx runs packages from a temporary cache without a permanent installation. To "uninstall" the CLI, you must clear this cache, which will remove gemini-cli and any other packages previously executed with npx.
The npx cache is a directory named `_npx` inside your main npm cache folder. You can find your npm cache path by running `npm config get cache`.
**For macOS / Linux**
```bash
# The path is typically ~/.npm/_npx
rm -rf "$(npm config get cache)/_npx"
```
**For Windows**
_Command Prompt_
```cmd
:: The path is typically %LocalAppData%\npm-cache\_npx
rmdir /s /q "%LocalAppData%\npm-cache\_npx"
```
_PowerShell_
```powershell
# The path is typically $env:LocalAppData\npm-cache\_npx
Remove-Item -Path (Join-Path $env:LocalAppData "npm-cache\_npx") -Recurse -Force
```
## Method 2: Using npm (Global Install)
If you installed the CLI globally (e.g., `npm install -g @google/gemini-cli`), use the `npm uninstall` command with the `-g` flag to remove it.
```bash
npm uninstall -g @google/gemini-cli
```
This command completely removes the package from your system.
-2
View File
@@ -7,7 +7,6 @@ This document provides a high-level overview of the Gemini CLI's architecture.
The Gemini CLI is primarily composed of two main packages, along with a suite of tools that can be used by the system in the course of handling command-line input:
1. **CLI package (`packages/cli`):**
- **Purpose:** This contains the user-facing portion of the Gemini CLI, such as handling the initial user input, presenting the final output, and managing the overall user experience.
- **Key functions contained in the package:**
- [Input processing](./cli/commands.md)
@@ -17,7 +16,6 @@ The Gemini CLI is primarily composed of two main packages, along with a suite of
- [CLI configuration settings](./cli/configuration.md)
2. **Core package (`packages/core`):**
- **Purpose:** This acts as the backend for the Gemini CLI. It receives requests sent from `packages/cli`, orchestrates interactions with the Gemini API, and manages the execution of available tools.
- **Key functions contained in the package:**
- API client for communicating with the Google Gemini API
Binary file not shown.

After

Width:  |  Height:  |  Size: 350 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 KiB

+75
View File
@@ -0,0 +1,75 @@
# Checkpointing
The Gemini CLI includes a Checkpointing feature that automatically saves a snapshot of your project's state before any file modifications are made by AI-powered tools. This allows you to safely experiment with and apply code changes, knowing you can instantly revert back to the state before the tool was run.
## How It Works
When you approve a tool that modifies the file system (like `write_file` or `replace`), the CLI automatically creates a "checkpoint." This checkpoint includes:
1. **A Git Snapshot:** A commit is made in a special, shadow Git repository located in your home directory (`~/.gemini/history/<project_hash>`). This snapshot captures the complete state of your project files at that moment. It does **not** interfere with your own project's Git repository.
2. **Conversation History:** The entire conversation you've had with the agent up to that point is saved.
3. **The Tool Call:** The specific tool call that was about to be executed is also stored.
If you want to undo the change or simply go back, you can use the `/restore` command. Restoring a checkpoint will:
- Revert all files in your project to the state captured in the snapshot.
- Restore the conversation history in the CLI.
- Re-propose the original tool call, allowing you to run it again, modify it, or simply ignore it.
All checkpoint data, including the Git snapshot and conversation history, is stored locally on your machine. The Git snapshot is stored in the shadow repository while the conversation history and tool calls are saved in a JSON file in your project's temporary directory, typically located at `~/.gemini/tmp/<project_hash>/checkpoints`.
## Enabling the Feature
The Checkpointing feature is disabled by default. To enable it, you can either use a command-line flag or edit your `settings.json` file.
### Using the Command-Line Flag
You can enable checkpointing for the current session by using the `--checkpointing` flag when starting the Gemini CLI:
```bash
gemini --checkpointing
```
### Using the `settings.json` File
To enable checkpointing by default for all sessions, you need to edit your `settings.json` file.
Add the following key to your `settings.json`:
```json
{
"checkpointing": {
"enabled": true
}
}
```
## Using the `/restore` Command
Once enabled, checkpoints are created automatically. To manage them, you use the `/restore` command.
### List Available Checkpoints
To see a list of all saved checkpoints for the current project, simply run:
```
/restore
```
The CLI will display a list of available checkpoint files. These file names are typically composed of a timestamp, the name of the file being modified, and the name of the tool that was about to be run (e.g., `2025-06-22T10-00-00_000Z-my-file.txt-write_file`).
### Restore a Specific Checkpoint
To restore your project to a specific checkpoint, use the checkpoint file from the list:
```
/restore <checkpoint_file>
```
For example:
```
/restore 2025-06-22T10-00-00_000Z-my-file.txt-write_file
```
After running the command, your files and conversation will be immediately restored to the state they were in when the checkpoint was created, and the original tool prompt will reappear.
+145
View File
@@ -0,0 +1,145 @@
# Authentication Setup
The Gemini CLI requires you to authenticate with Google's AI services. On initial startup you'll need to configure **one** of the following authentication methods:
1. **Login with Google (Gemini Code Assist):**
- Use this option to log in with your Google account.
- During initial startup, Gemini CLI will direct you to a webpage for authentication. Once authenticated, your credentials will be cached locally so the web login can be skipped on subsequent runs.
- Note that the web login must be done in a browser that can communicate with the machine Gemini CLI is being run from. (Specifically, the browser will be redirected to a localhost url that Gemini CLI will be listening on).
- <a id="workspace-gca">Users may have to specify a GOOGLE_CLOUD_PROJECT if:</a>
1. You have a Google Workspace account. Google Workspace is a paid service for businesses and organizations that provides a suite of productivity tools, including a custom email domain (e.g. your-name@your-company.com), enhanced security features, and administrative controls. These accounts are often managed by an employer or school.
1. You have received a Gemini Code Assist license through the [Google Developer Program](https://developers.google.com/program/plans-and-pricing) (including qualified Google Developer Experts)
1. You have been assigned a license to a current Gemini Code Assist standard or enterprise subscription.
1. You are using the product outside the [supported regions](https://developers.google.com/gemini-code-assist/resources/available-locations) for free individual usage.
1. You are a Google account holder under the age of 18
- If you fall into one of these categories, you must first configure a Google Cloud Project ID to use, [enable the Gemini for Cloud API](https://cloud.google.com/gemini/docs/discover/set-up-gemini#enable-api) and [configure access permissions](https://cloud.google.com/gemini/docs/discover/set-up-gemini#grant-iam).
You can temporarily set the environment variable in your current shell session using the following command:
```bash
export GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"
```
- For repeated use, you can add the environment variable to your [.env file](#persisting-environment-variables-with-env-files) or your shell's configuration file (like `~/.bashrc`, `~/.zshrc`, or `~/.profile`). For example, the following command adds the environment variable to a `~/.bashrc` file:
```bash
echo 'export GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"' >> ~/.bashrc
source ~/.bashrc
```
2. **<a id="gemini-api-key"></a>Gemini API key:**
- Obtain your API key from Google AI Studio: [https://aistudio.google.com/app/apikey](https://aistudio.google.com/app/apikey)
- Set the `GEMINI_API_KEY` environment variable. In the following methods, replace `YOUR_GEMINI_API_KEY` with the API key you obtained from Google AI Studio:
- You can temporarily set the environment variable in your current shell session using the following command:
```bash
export GEMINI_API_KEY="YOUR_GEMINI_API_KEY"
```
- For repeated use, you can add the environment variable to your [.env file](#persisting-environment-variables-with-env-files).
- Alternatively you can export the API key from your shell's configuration file (like `~/.bashrc`, `~/.zshrc`, or `~/.profile`). For example, the following command adds the environment variable to a `~/.bashrc` file:
```bash
echo 'export GEMINI_API_KEY="YOUR_GEMINI_API_KEY"' >> ~/.bashrc
source ~/.bashrc
```
:warning: Be advised that when you export your API key inside your shell configuration file, any other process executed from the shell can read it.
3. **Vertex AI:**
- Obtain your Google Cloud API key: [Get an API Key](https://cloud.google.com/vertex-ai/generative-ai/docs/start/api-keys?usertype=newuser)
- Set the `GOOGLE_API_KEY` environment variable. In the following methods, replace `YOUR_GOOGLE_API_KEY` with your Vertex AI API key:
- You can temporarily set these environment variables in your current shell session using the following commands:
```bash
export GOOGLE_API_KEY="YOUR_GOOGLE_API_KEY"
```
- For repeated use, you can add the environment variables to your [.env file](#persisting-environment-variables-with-env-files) or your shell's configuration file (like `~/.bashrc`, `~/.zshrc`, or `~/.profile`). For example, the following commands add the environment variables to a `~/.bashrc` file:
```bash
echo 'export GOOGLE_API_KEY="YOUR_GOOGLE_API_KEY"' >> ~/.bashrc
source ~/.bashrc
```
- To use Application Default Credentials (ADC), use the following command:
- Ensure you have a Google Cloud project and have enabled the Vertex AI API.
```bash
gcloud auth application-default login
```
For more information, see [Set up Application Default Credentials for Google Cloud](https://cloud.google.com/docs/authentication/provide-credentials-adc).
- Set the `GOOGLE_CLOUD_PROJECT` and `GOOGLE_CLOUD_LOCATION` environment variables. In the following methods, replace `YOUR_PROJECT_ID` and `YOUR_PROJECT_LOCATION` with the relevant values for your project:
- You can temporarily set these environment variables in your current shell session using the following commands:
```bash
export GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"
export GOOGLE_CLOUD_LOCATION="YOUR_PROJECT_LOCATION" # e.g., us-central1
```
- For repeated use, you can add the environment variables to your [.env file](#persisting-environment-variables-with-env-files)
- Alternatively you can export the environment variables from your shell's configuration file (like `~/.bashrc`, `~/.zshrc`, or `~/.profile`). For example, the following commands add the environment variables to a `~/.bashrc` file:
```bash
echo 'export GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"' >> ~/.bashrc
echo 'export GOOGLE_CLOUD_LOCATION="YOUR_PROJECT_LOCATION"' >> ~/.bashrc
source ~/.bashrc
```
:warning: Be advised that when you export your API key inside your shell configuration file, any other process executed from the shell can read it.
4. **Cloud Shell:**
- This option is only available when running in a Google Cloud Shell environment.
- It automatically uses the credentials of the logged-in user in the Cloud Shell environment.
- This is the default authentication method when running in Cloud Shell and no other method is configured.
:warning: Be advised that when you export your API key inside your shell configuration file, any other process executed from the shell can read it.
### Persisting Environment Variables with `.env` Files
You can create a **`.gemini/.env`** file in your project directory or in your home directory. Creating a plain **`.env`** file also works, but `.gemini/.env` is recommended to keep Gemini variables isolated from other tools.
**Important:** Some environment variables (like `DEBUG` and `DEBUG_MODE`) are automatically excluded from project `.env` files to prevent interference with gemini-cli behavior. Use `.gemini/.env` files for gemini-cli specific variables.
Gemini CLI automatically loads environment variables from the **first** `.env` file it finds, using the following search order:
1. Starting in the **current directory** and moving upward toward `/`, for each directory it checks:
1. `.gemini/.env`
2. `.env`
2. If no file is found, it falls back to your **home directory**:
- `~/.gemini/.env`
- `~/.env`
> **Important:** The search stops at the **first** file encountered—variables are **not merged** across multiple files.
#### Examples
**Project-specific overrides** (take precedence when you are inside the project):
```bash
mkdir -p .gemini
echo 'GOOGLE_CLOUD_PROJECT="your-project-id"' >> .gemini/.env
```
**User-wide settings** (available in every directory):
```bash
mkdir -p ~/.gemini
cat >> ~/.gemini/.env <<'EOF'
GOOGLE_CLOUD_PROJECT="your-project-id"
GEMINI_API_KEY="your-gemini-api-key"
EOF
```
## Non-Interactive Mode / Headless Environments
When running the Gemini CLI in a non-interactive environment, you cannot use the interactive login flow.
Instead, you must configure authentication using environment variables.
The CLI will automatically detect if it is running in a non-interactive terminal and will use one of the
following authentication methods if available:
1. **Gemini API Key:**
- Set the `GEMINI_API_KEY` environment variable.
- The CLI will use this key to authenticate with the Gemini API.
2. **Vertex AI:**
- Set the `GOOGLE_GENAI_USE_VERTEXAI=true` environment variable.
- **Using an API Key:** Set the `GOOGLE_API_KEY` environment variable.
- **Using Application Default Credentials (ADC):**
- Run `gcloud auth application-default login` in your environment to configure ADC.
- Ensure the `GOOGLE_CLOUD_PROJECT` and `GOOGLE_CLOUD_LOCATION` environment variables are set.
If none of these environment variables are set in a non-interactive session, the CLI will exit with an error.
+256 -17
View File
@@ -6,29 +6,62 @@ Gemini CLI supports several built-in commands to help you manage your session, c
Slash commands provide meta-level control over the CLI itself.
- **`/bug`**
### Built-in Commands
- **`/bug`**
- **Description:** File an issue about Gemini CLI. By default, the issue is filed within the GitHub repository for Gemini CLI. The string you enter after `/bug` will become the headline for the bug being filed. The default `/bug` behavior can be modified using the `bugCommand` setting in your `.gemini/settings.json` files.
- **`/clear`**
- **`/chat`**
- **Description:** Save and resume conversation history for branching conversation state interactively, or resuming a previous state from a later session.
- **Sub-commands:**
- **`save`**
- **Description:** Saves the current conversation history. You must add a `<tag>` for identifying the conversation state.
- **Usage:** `/chat save <tag>`
- **Details on Checkpoint Location:** The default locations for saved chat checkpoints are:
- Linux/macOS: `~/.config/google-generative-ai/checkpoints/`
- Windows: `C:\Users\<YourUsername>\AppData\Roaming\google-generative-ai\checkpoints\`
- When you run `/chat list`, the CLI only scans these specific directories to find available checkpoints.
- **Note:** These checkpoints are for manually saving and resuming conversation states. For automatic checkpoints created before file modifications, see the [Checkpointing documentation](../checkpointing.md).
- **`resume`**
- **Description:** Resumes a conversation from a previous save.
- **Usage:** `/chat resume <tag>`
- **`list`**
- **Description:** Lists available tags for chat state resumption.
- **`delete`**
- **Description:** Deletes a saved conversation checkpoint.
- **Usage:** `/chat delete <tag>`
- **`/clear`**
- **Description:** Clear the terminal screen, including the visible session history and scrollback within the CLI. The underlying session data (for history recall) might be preserved depending on the exact implementation, but the visual display is cleared.
- **Keyboard shortcut:** Press **Ctrl+L** at any time to perform a clear action.
- **`/compress`**
- **Description:** Replace the entire chat context with a summary. This saves on tokens used for future tasks while retaining a high level summary of what has happened.
- **`/editor`**
- **`/copy`**
- **Description:** Copies the last output produced by Gemini CLI to your clipboard, for easy sharing or reuse.
- **`/directory`** (or **`/dir`**)
- **Description:** Manage workspace directories for multi-directory support.
- **Sub-commands:**
- **`add`**:
- **Description:** Add a directory to the workspace. The path can be absolute or relative to the current working directory. Moreover, the reference from home directory is supported as well.
- **Usage:** `/directory add <path1>,<path2>`
- **Note:** Disabled in restrictive sandbox profiles. If you're using that, use `--include-directories` when starting the session instead.
- **`show`**:
- **Description:** Display all directories added by `/directory add` and `--include-directories`.
- **Usage:** `/directory show`
- **`/editor`**
- **Description:** Open a dialog for selecting supported editors.
- **`/help`** (or **`/?`**)
- **`/extensions`**
- **Description:** Lists all active extensions in the current Gemini CLI session. See [Gemini CLI Extensions](../extension.md).
- **Description:** Display help information about the Gemini CLI, including available commands and their usage.
- **`/help`** (or **`/?`**)
- **Description:** Display help information about Gemini CLI, including available commands and their usage.
- **`/mcp`**
- **Description:** List configured Model Context Protocol (MCP) servers, their connection status, server details, and available tools.
- **Sub-commands:**
- **`desc`** or **`descriptions`**:
@@ -40,25 +73,34 @@ Slash commands provide meta-level control over the CLI itself.
- **Keyboard Shortcut:** Press **Ctrl+T** at any time to toggle between showing and hiding tool descriptions.
- **`/memory`**
- **Description:** Manage the AI's instructional context (hierarchical memory loaded from `GEMINI.md` files).
- **Sub-commands:**
- **`add`**:
- **Description:** Adds the following text to the AI's memory. Usage: `/memory add <text to remember>`
- **`show`**:
- **Description:** Display the full, concatenated content of the current hierarchical memory that has been loaded from all `GEMINI.md` files. This lets you inspect the instructional context being provided to the Gemini model.
- **`refresh`**:
- **Description:** Reload the hierarchical instructional memory from all `GEMINI.md` files found in the configured locations (global, project/ancestors, and sub-directories). This command updates the model with the latest `GEMINI.md` content.
- **Note:** For more details on how `GEMINI.md` files contribute to hierarchical memory, see the [CLI Configuration documentation](./configuration.md#4-geminimd-files-hierarchical-instructional-context).
- **`/stats`**
- **`/restore`**
- **Description:** Restores the project files to the state they were in just before a tool was executed. This is particularly useful for undoing file edits made by a tool. If run without a tool call ID, it will list available checkpoints to restore from.
- **Usage:** `/restore [tool_call_id]`
- **Note:** Only available if the CLI is invoked with the `--checkpointing` option or configured via [settings](./configuration.md). See [Checkpointing documentation](../checkpointing.md) for more details.
- **Description:** Display detailed statistics for the current Gemini CLI session, such as the session duration.
- **`/stats`**
- **Description:** Display detailed statistics for the current Gemini CLI session, including token usage, cached token savings (when available), and session duration. Note: Cached token information is only displayed when cached tokens are being used, which occurs with API key authentication but not with OAuth authentication at this time.
- [**`/theme`**](./themes.md)
- **Description:** Open a dialog that lets you change the visual theme of Gemini CLI.
- [**`/tools`**](../tools/index.md)
- **`/auth`**
- **Description:** Open a dialog that lets you change the authentication method.
- **`/about`**
- **Description:** Show version info. Please share this information when filing issues.
- [**`/tools`**](../tools/index.md)
- **Description:** Display a list of tools that are currently available within Gemini CLI.
- **Sub-commands:**
- **`desc`** or **`descriptions`**:
@@ -66,16 +108,213 @@ Slash commands provide meta-level control over the CLI itself.
- **`nodesc`** or **`nodescriptions`**:
- **Description:** Hide tool descriptions, showing only the tool names.
- **`/quit`** (or **`/exit`**)
- **`/privacy`**
- **Description:** Display the Privacy Notice and allow users to select whether they consent to the collection of their data for service improvement purposes.
- **`/quit`** (or **`/exit`**)
- **Description:** Exit Gemini CLI.
- **`/vim`**
- **Description:** Toggle vim mode on or off. When vim mode is enabled, the input area supports vim-style navigation and editing commands in both NORMAL and INSERT modes.
- **Features:**
- **NORMAL mode:** Navigate with `h`, `j`, `k`, `l`; jump by words with `w`, `b`, `e`; go to line start/end with `0`, `$`, `^`; go to specific lines with `G` (or `gg` for first line)
- **INSERT mode:** Standard text input with escape to return to NORMAL mode
- **Editing commands:** Delete with `x`, change with `c`, insert with `i`, `a`, `o`, `O`; complex operations like `dd`, `cc`, `dw`, `cw`
- **Count support:** Prefix commands with numbers (e.g., `3h`, `5w`, `10G`)
- **Repeat last command:** Use `.` to repeat the last editing operation
- **Persistent setting:** Vim mode preference is saved to `~/.gemini/settings.json` and restored between sessions
- **Status indicator:** When enabled, shows `[NORMAL]` or `[INSERT]` in the footer
- **`/init`**
- **Description:** To help users easily create a `GEMINI.md` file, this command analyzes the current directory and generates a tailored context file, making it simpler for them to provide project-specific instructions to the Gemini agent.
### Custom Commands
For a quick start, see the [example](#example-a-pure-function-refactoring-command) below.
Custom commands allow you to save and reuse your favorite or most frequently used prompts as personal shortcuts within Gemini CLI. You can create commands that are specific to a single project or commands that are available globally across all your projects, streamlining your workflow and ensuring consistency.
#### File Locations & Precedence
Gemini CLI discovers commands from two locations, loaded in a specific order:
1. **User Commands (Global):** Located in `~/.gemini/commands/`. These commands are available in any project you are working on.
2. **Project Commands (Local):** Located in `<your-project-root>/.gemini/commands/`. These commands are specific to the current project and can be checked into version control to be shared with your team.
If a command in the project directory has the same name as a command in the user directory, the **project command will always be used.** This allows projects to override global commands with project-specific versions.
#### Naming and Namespacing
The name of a command is determined by its file path relative to its `commands` directory. Subdirectories are used to create namespaced commands, with the path separator (`/` or `\`) being converted to a colon (`:`).
- A file at `~/.gemini/commands/test.toml` becomes the command `/test`.
- A file at `<project>/.gemini/commands/git/commit.toml` becomes the namespaced command `/git:commit`.
#### TOML File Format (v1)
Your command definition files must be written in the TOML format and use the `.toml` file extension.
##### Required Fields
- `prompt` (String): The prompt that will be sent to the Gemini model when the command is executed. This can be a single-line or multi-line string.
##### Optional Fields
- `description` (String): A brief, one-line description of what the command does. This text will be displayed next to your command in the `/help` menu. **If you omit this field, a generic description will be generated from the filename.**
#### Handling Arguments
Custom commands support two powerful, low-friction methods for handling arguments. The CLI automatically chooses the correct method based on the content of your command's `prompt`.
##### 1. Shorthand Injection with `{{args}}`
If your `prompt` contains the special placeholder `{{args}}`, the CLI will replace that exact placeholder with all the text the user typed after the command name. This is perfect for simple, deterministic commands where you need to inject user input into a specific place in a larger prompt template.
**Example (`git/fix.toml`):**
```toml
# In: ~/.gemini/commands/git/fix.toml
# Invoked via: /git:fix "Button is misaligned on mobile"
description = "Generates a fix for a given GitHub issue."
prompt = "Please analyze the staged git changes and provide a code fix for the issue described here: {{args}}."
```
The model will receive the final prompt: `Please analyze the staged git changes and provide a code fix for the issue described here: "Button is misaligned on mobile".`
##### 2. Default Argument Handling
If your `prompt` does **not** contain the special placeholder `{{args}}`, the CLI uses a default behavior for handling arguments.
If you provide arguments to the command (e.g., `/mycommand arg1`), the CLI will append the full command you typed to the end of the prompt, separated by two newlines. This allows the model to see both the original instructions and the specific arguments you just provided.
If you do **not** provide any arguments (e.g., `/mycommand`), the prompt is sent to the model exactly as it is, with nothing appended.
**Example (`changelog.toml`):**
This example shows how to create a robust command by defining a role for the model, explaining where to find the user's input, and specifying the expected format and behavior.
```toml
# In: <project>/.gemini/commands/changelog.toml
# Invoked via: /changelog 1.2.0 added "Support for default argument parsing."
description = "Adds a new entry to the project's CHANGELOG.md file."
prompt = """
# Task: Update Changelog
You are an expert maintainer of this software project. A user has invoked a command to add a new entry to the changelog.
**The user's raw command is appended below your instructions.**
Your task is to parse the `<version>`, `<change_type>`, and `<message>` from their input and use the `write_file` tool to correctly update the `CHANGELOG.md` file.
## Expected Format
The command follows this format: `/changelog <version> <type> <message>`
- `<type>` must be one of: "added", "changed", "fixed", "removed".
## Behavior
1. Read the `CHANGELOG.md` file.
2. Find the section for the specified `<version>`.
3. Add the `<message>` under the correct `<type>` heading.
4. If the version or type section doesn't exist, create it.
5. Adhere strictly to the "Keep a Changelog" format.
"""
```
When you run `/changelog 1.2.0 added "New feature"`, the final text sent to the model will be the original prompt followed by two newlines and the command you typed.
##### 3. Executing Shell Commands with `!{...}`
You can make your commands dynamic by executing shell commands directly within your `prompt` and injecting their output. This is ideal for gathering context from your local environment, like reading file content or checking the status of Git.
When a custom command attempts to execute a shell command, Gemini CLI will now prompt you for confirmation before proceeding. This is a security measure to ensure that only intended commands can be run.
**How It Works:**
1. **Inject Commands:** Use the `!{...}` syntax in your `prompt` to specify where the command should be run and its output injected.
2. **Confirm Execution:** When you run the command, a dialog will appear listing the shell commands the prompt wants to execute.
3. **Grant Permission:** You can choose to:
- **Allow once:** The command(s) will run this one time.
- **Allow always for this session:** The command(s) will be added to a temporary allowlist for the current CLI session and will not require confirmation again.
- **No:** Cancel the execution of the shell command(s).
The CLI still respects the global `excludeTools` and `coreTools` settings. A command will be blocked without a confirmation prompt if it is explicitly disallowed in your configuration.
**Example (`git/commit.toml`):**
This command gets the staged git diff and uses it to ask the model to write a commit message.
````toml
# In: <project>/.gemini/commands/git/commit.toml
# Invoked via: /git:commit
description = "Generates a Git commit message based on staged changes."
# The prompt uses !{...} to execute the command and inject its output.
prompt = """
Please generate a Conventional Commit message based on the following git diff:
```diff
!{git diff --staged}
```
"""
````
When you run `/git:commit`, the CLI first executes `git diff --staged`, then replaces `!{git diff --staged}` with the output of that command before sending the final, complete prompt to the model.
---
#### Example: A "Pure Function" Refactoring Command
Let's create a global command that asks the model to refactor a piece of code.
**1. Create the file and directories:**
First, ensure the user commands directory exists, then create a `refactor` subdirectory for organization and the final TOML file.
```bash
mkdir -p ~/.gemini/commands/refactor
touch ~/.gemini/commands/refactor/pure.toml
```
**2. Add the content to the file:**
Open `~/.gemini/commands/refactor/pure.toml` in your editor and add the following content. We are including the optional `description` for best practice.
```toml
# In: ~/.gemini/commands/refactor/pure.toml
# This command will be invoked via: /refactor:pure
description = "Asks the model to refactor the current context into a pure function."
prompt = """
Please analyze the code I've provided in the current context.
Refactor it into a pure function.
Your response should include:
1. The refactored, pure function code block.
2. A brief explanation of the key changes you made and why they contribute to purity.
"""
```
**3. Run the Command:**
That's it! You can now run your command in the CLI. First, you might add a file to the context, and then invoke your command:
```
> @my-messy-function.js
> /refactor:pure
```
Gemini CLI will then execute the multi-line prompt defined in your TOML file.
## At commands (`@`)
At commands are used to include the content of files or directories as part of your prompt to Gemini. These commands include git-aware filtering.
- **`@<path_to_file_or_directory>`**
- **Description:** Inject the content of the specified file or files into your current prompt. This is useful for asking questions about specific code, text, or collections of files.
- **Examples:**
- `@path/to/your/file.txt Explain this text.`
@@ -103,14 +342,12 @@ At commands are used to include the content of files or directories as part of y
The `!` prefix lets you interact with your system's shell directly from within Gemini CLI.
- **`!<shell_command>`**
- **Description:** Execute the given `<shell_command>` in your system's default shell. Any output or errors from the command are displayed in the terminal.
- **Description:** Execute the given `<shell_command>` using `bash` on Linux/macOS or `cmd.exe` on Windows. Any output or errors from the command are displayed in the terminal.
- **Examples:**
- `!ls -la` (executes `ls -la` and returns to Gemini CLI)
- `!git status` (executes `git status` and returns to Gemini CLI)
- **`!` (Toggle shell mode)**
- **Description:** Typing `!` on its own toggles shell mode.
- **Entering shell mode:**
- When active, shell mode uses a different coloring and a "Shell Mode Indicator".
@@ -119,3 +356,5 @@ The `!` prefix lets you interact with your system's shell directly from within G
- When exited, the UI reverts to its standard appearance and normal Gemini CLI behavior resumes.
- **Caution for all `!` usage:** Commands you execute in shell mode have the same permissions and impact as if you ran them directly in your terminal.
- **Environment Variable:** When a command is executed via `!` or in shell mode, the `GEMINI_CLI=1` environment variable is set in the subprocess's environment. This allows scripts or tools to detect if they are being run from within the Gemini CLI.
+260 -52
View File
@@ -9,12 +9,13 @@ Configuration is applied in the following order of precedence (lower numbers are
1. **Default values:** Hardcoded defaults within the application.
2. **User settings file:** Global settings for the current user.
3. **Project settings file:** Project-specific settings.
4. **Environment variables:** System-wide or session-specific variables, potentially loaded from `.env` files.
5. **Command-line arguments:** Values passed when launching the CLI.
4. **System settings file:** System-wide settings.
5. **Environment variables:** System-wide or session-specific variables, potentially loaded from `.env` files.
6. **Command-line arguments:** Values passed when launching the CLI.
## The user settings file and project settings file
## Settings files
Gemini CLI uses `settings.json` files for persistent configuration. There are two locations for these files:
Gemini CLI uses `settings.json` files for persistent configuration. There are three locations for these files:
- **User settings file:**
- **Location:** `~/.gemini/settings.json` (where `~` is your home directory).
@@ -22,6 +23,9 @@ Gemini CLI uses `settings.json` files for persistent configuration. There are tw
- **Project settings file:**
- **Location:** `.gemini/settings.json` within your project's root directory.
- **Scope:** Applies only when running Gemini CLI from that specific project. Project settings override user settings.
- **System settings file:**
- **Location:** `/etc/gemini-cli/settings.json` (Linux), `C:\ProgramData\gemini-cli\settings.json` (Windows) or `/Library/Application Support/GeminiCli/settings.json` (macOS). The path can be overridden using the `GEMINI_CLI_SYSTEM_SETTINGS_PATH` environment variable.
- **Scope:** Applies to all Gemini CLI sessions on the system, for all users. System settings override user and project settings. May be useful for system administrators at enterprises to have controls over users' Gemini CLI setups.
**Note on environment variables in settings:** String values within your `settings.json` files can reference environment variables using either `$VAR_NAME` or `${VAR_NAME}` syntax. These variables will be automatically resolved when the settings are loaded. For example, if you have an environment variable `MY_API_TOKEN`, you could use it in `settings.json` like this: `"apiKey": "$MY_API_TOKEN"`.
@@ -30,80 +34,91 @@ Gemini CLI uses `settings.json` files for persistent configuration. There are tw
In addition to a project settings file, a project's `.gemini` directory can contain other project-specific files related to Gemini CLI's operation, such as:
- [Custom sandbox profiles](#sandboxing) (e.g., `.gemini/sandbox-macos-custom.sb`, `.gemini/sandbox.Dockerfile`).
- [Telemtry configurations](../core/telemetry.md#running-an-otel-collector) (e.g., `.gemini/otel/`).
### Available settings in `settings.json`:
- **`contextFileName`** (string or array of strings):
- **Description:** Specifies the filename for context files (e.g., `GEMINI.md`, `AGENTS.md`). Can be a single filename or a list of accepted filenames.
- **Default:** `GEMINI.md`
- **Example:** `"contextFileName": "AGENTS.md"`
- **`bugCommand`** (object):
- **Description:** Overrides the default URL for the `/bug` command.
- **Default:** `"urlTemplate": "https://github.com/google-gemini/gemini-cli/issues/new?template=bug_report.md&title={title}&body={body}"`
- **Default:** `"urlTemplate": "https://github.com/google-gemini/gemini-cli/issues/new?template=bug_report.yml&title={title}&info={info}"`
- **Properties:**
- **`urlTemplate`** (string): A URL that can contain `{title}` and `{body}` placeholders.
- **`urlTemplate`** (string): A URL that can contain `{title}` and `{info}` placeholders.
- **Example:**
```json
"bugCommand": {
"urlTemplate": "https://bug.example.com/new?title={title}&body={body}"
"urlTemplate": "https://bug.example.com/new?title={title}&info={info}"
}
```
- **`fileFiltering`** (object):
- **Description:** Controls git-aware file filtering behavior for @ commands and file discovery tools.
- **Default:** `"respectGitIgnore": true`
- **Default:** `"respectGitIgnore": true, "enableRecursiveFileSearch": true`
- **Properties:**
- **`respectGitIgnore`** (boolean): Whether to respect .gitignore patterns when discovering files. When set to `true`, git-ignored files (like `node_modules/`, `dist/`, `.env`) are automatically excluded from @ commands and file listing operations.
- **`enableRecursiveFileSearch`** (boolean): Whether to enable searching recursively for filenames under the current tree when completing @ prefixes in the prompt.
- **Example:**
```json
"fileFiltering": {
"respectGitIgnore": true
"respectGitIgnore": true,
"enableRecursiveFileSearch": false
}
```
- **`coreTools`** (array of strings):
- **Description:** Allows you to specify a list of core tool names that should be made available to the model. This can be used to restrict the set of built-in tools. See [Built-in Tools](../core/tools-api.md#built-in-tools) for a list of core tools.
- **Description:** Allows you to specify a list of core tool names that should be made available to the model. This can be used to restrict the set of built-in tools. See [Built-in Tools](../core/tools-api.md#built-in-tools) for a list of core tools. You can also specify command-specific restrictions for tools that support it, like the `ShellTool`. For example, `"coreTools": ["ShellTool(ls -l)"]` will only allow the `ls -l` command to be executed.
- **Default:** All tools available for use by the Gemini model.
- **Example:** `"coreTools": ["ReadFileTool", "GlobTool", "SearchText"]`.
- **Example:** `"coreTools": ["ReadFileTool", "GlobTool", "ShellTool(ls)"]`.
- **`excludeTools`** (array of strings):
- **Description:** Allows you to specify a list of core tool names that should be excluded from the model. A tool listed in both `excludeTools` and `coreTools` is excluded.
- **Description:** Allows you to specify a list of core tool names that should be excluded from the model. A tool listed in both `excludeTools` and `coreTools` is excluded. You can also specify command-specific restrictions for tools that support it, like the `ShellTool`. For example, `"excludeTools": ["ShellTool(rm -rf)"]` will block the `rm -rf` command.
- **Default**: No tools excluded.
- **Example:** `"excludeTools": ["run_shell_command", "findFiles"]`.
- **Security Note:** Command-specific restrictions in
`excludeTools` for `run_shell_command` are based on simple string matching and can be easily bypassed. This feature is **not a security mechanism** and should not be relied upon to safely execute untrusted code. It is recommended to use `coreTools` to explicitly select commands
that can be executed.
- **`allowMCPServers`** (array of strings):
- **Description:** Allows you to specify a list of MCP server names that should be made available to the model. This can be used to restrict the set of MCP servers to connect to. Note that this will be ignored if `--allowed-mcp-server-names` is set.
- **Default:** All MCP servers are available for use by the Gemini model.
- **Example:** `"allowMCPServers": ["myPythonServer"]`.
- **Security Note:** This uses simple string matching on MCP server names, which can be modified. If you're a system administrator looking to prevent users from bypassing this, consider configuring the `mcpServers` at the system settings level such that the user will not be able to configure any MCP servers of their own. This should not be used as an airtight security mechanism.
- **`excludeMCPServers`** (array of strings):
- **Description:** Allows you to specify a list of MCP server names that should be excluded from the model. A server listed in both `excludeMCPServers` and `allowMCPServers` is excluded. Note that this will be ignored if `--allowed-mcp-server-names` is set.
- **Default**: No MCP servers excluded.
- **Example:** `"excludeMCPServers": ["myNodeServer"]`.
- **Security Note:** This uses simple string matching on MCP server names, which can be modified. If you're a system administrator looking to prevent users from bypassing this, consider configuring the `mcpServers` at the system settings level such that the user will not be able to configure any MCP servers of their own. This should not be used as an airtight security mechanism.
- **`autoAccept`** (boolean):
- **Description:** Controls whether the CLI automatically accepts and executes tool calls that are considered safe (e.g., read-only operations) without explicit user confirmation. If set to `true`, the CLI will bypass the confirmation prompt for tools deemed safe.
- **Default:** `false`
- **Example:** `"autoAccept": true`
- **`theme`** (string):
- **Description:** Sets the visual [theme](./themes.md) for Gemini CLI.
- **Default:** `"Default"`
- **Example:** `"theme": "GitHub"`
- **`sandbox`** (boolean or string):
- **`vimMode`** (boolean):
- **Description:** Enables or disables vim mode for input editing. When enabled, the input area supports vim-style navigation and editing commands with NORMAL and INSERT modes. The vim mode status is displayed in the footer and persists between sessions.
- **Default:** `false`
- **Example:** `"vimMode": true`
- **`sandbox`** (boolean or string):
- **Description:** Controls whether and how to use sandboxing for tool execution. If set to `true`, Gemini CLI uses a pre-built `gemini-cli-sandbox` Docker image. For more information, see [Sandboxing](#sandboxing).
- **Default:** `false`
- **Example:** `"sandbox": "docker"`
- **`toolDiscoveryCommand`** (string):
- **Description:** Defines a custom shell command for discovering tools from your project. The shell command must return on `stdout` a JSON array of [function declarations](https://ai.google.dev/gemini-api/docs/function-calling#function-declarations). Tool wrappers are optional.
- **Default:** Empty
- **Example:** `"toolDiscoveryCommand": "bin/get_tools"`
- **`toolCallCommand`** (string):
- **Description:** Defines a custom shell command for calling a specific tool that was discovered using `toolDiscoveryCommand`. The shell command must meet the following criteria:
- It must take function `name` (exactly as in [function declaration](https://ai.google.dev/gemini-api/docs/function-calling#function-declarations)) as first command line argument.
- It must read function arguments as JSON on `stdin`, analogous to [`functionCall.args`](https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/inference#functioncall).
@@ -112,7 +127,6 @@ In addition to a project settings file, a project's `.gemini` directory can cont
- **Example:** `"toolCallCommand": "bin/call_tool"`
- **`mcpServers`** (object):
- **Description:** Configures connections to one or more Model-Context Protocol (MCP) servers for discovering and using custom tools. Gemini CLI attempts to connect to each configured MCP server to discover available tools. If multiple MCP servers expose a tool with the same name, the tool names will be prefixed with the server alias you defined in the configuration (e.g., `serverAlias__actualToolName`) to avoid conflicts. Note that the system might strip certain schema properties from MCP tool definitions for compatibility.
- **Default:** Empty
- **Properties:**
@@ -123,6 +137,8 @@ In addition to a project settings file, a project's `.gemini` directory can cont
- `cwd` (string, optional): The working directory in which to start the server.
- `timeout` (number, optional): Timeout in milliseconds for requests to this MCP server.
- `trust` (boolean, optional): Trust this server and bypass all tool call confirmations.
- `includeTools` (array of strings, optional): List of tool names to include from this MCP server. When specified, only the tools listed here will be available from this server (whitelist behavior). If not specified, all tools from the server are enabled by default.
- `excludeTools` (array of strings, optional): List of tool names to exclude from this MCP server. Tools listed here will not be available to the model, even if they are exposed by the server. **Note:** `excludeTools` takes precedence over `includeTools` - if a tool is in both lists, it will be excluded.
- **Example:**
```json
"mcpServers": {
@@ -130,26 +146,127 @@ In addition to a project settings file, a project's `.gemini` directory can cont
"command": "python",
"args": ["mcp_server.py", "--port", "8080"],
"cwd": "./mcp_tools/python",
"timeout": 5000
"timeout": 5000,
"includeTools": ["safe_tool", "file_reader"],
},
"myNodeServer": {
"command": "node",
"args": ["mcp_server.js"],
"cwd": "./mcp_tools/node"
"cwd": "./mcp_tools/node",
"excludeTools": ["dangerous_tool", "file_deleter"]
},
"myDockerServer": {
"command": "docker",
"args": ["run", "i", "--rm", "-e", "API_KEY", "ghcr.io/foo/bar"],
"args": ["run", "-i", "--rm", "-e", "API_KEY", "ghcr.io/foo/bar"],
"env": {
"API_KEY": "$MY_API_TOKEN"
}
},
}
}
```
- **`mcpServerCommand`** (string, **deprecated**):
- **`checkpointing`** (object):
- **Description:** Configures the checkpointing feature, which allows you to save and restore conversation and file states. See the [Checkpointing documentation](../checkpointing.md) for more details.
- **Default:** `{"enabled": false}`
- **Properties:**
- **`enabled`** (boolean): When `true`, the `/restore` command is available.
- This is a legacy setting for configuring a single MCP server. Use `mcpServers` instead.
- **`preferredEditor`** (string):
- **Description:** Specifies the preferred editor to use for viewing diffs.
- **Default:** `vscode`
- **Example:** `"preferredEditor": "vscode"`
- **`telemetry`** (object)
- **Description:** Configures logging and metrics collection for Gemini CLI. For more information, see [Telemetry](../telemetry.md).
- **Default:** `{"enabled": false, "target": "local", "otlpEndpoint": "http://localhost:4317", "logPrompts": true}`
- **Properties:**
- **`enabled`** (boolean): Whether or not telemetry is enabled.
- **`target`** (string): The destination for collected telemetry. Supported values are `local` and `gcp`.
- **`otlpEndpoint`** (string): The endpoint for the OTLP Exporter.
- **`logPrompts`** (boolean): Whether or not to include the content of user prompts in the logs.
- **Example:**
```json
"telemetry": {
"enabled": true,
"target": "local",
"otlpEndpoint": "http://localhost:16686",
"logPrompts": false
}
```
- **`usageStatisticsEnabled`** (boolean):
- **Description:** Enables or disables the collection of usage statistics. See [Usage Statistics](#usage-statistics) for more information.
- **Default:** `true`
- **Example:**
```json
"usageStatisticsEnabled": false
```
- **`hideTips`** (boolean):
- **Description:** Enables or disables helpful tips in the CLI interface.
- **Default:** `false`
- **Example:**
```json
"hideTips": true
```
- **`hideBanner`** (boolean):
- **Description:** Enables or disables the startup banner (ASCII art logo) in the CLI interface.
- **Default:** `false`
- **Example:**
```json
"hideBanner": true
```
- **`maxSessionTurns`** (number):
- **Description:** Sets the maximum number of turns for a session. If the session exceeds this limit, the CLI will stop processing and start a new chat.
- **Default:** `-1` (unlimited)
- **Example:**
```json
"maxSessionTurns": 10
```
- **`summarizeToolOutput`** (object):
- **Description:** Enables or disables the summarization of tool output. You can specify the token budget for the summarization using the `tokenBudget` setting.
- Note: Currently only the `run_shell_command` tool is supported.
- **Default:** `{}` (Disabled by default)
- **Example:**
```json
"summarizeToolOutput": {
"run_shell_command": {
"tokenBudget": 2000
}
}
```
- **`excludedProjectEnvVars`** (array of strings):
- **Description:** Specifies environment variables that should be excluded from being loaded from project `.env` files. This prevents project-specific environment variables (like `DEBUG=true`) from interfering with gemini-cli behavior. Variables from `.gemini/.env` files are never excluded.
- **Default:** `["DEBUG", "DEBUG_MODE"]`
- **Example:**
```json
"excludedProjectEnvVars": ["DEBUG", "DEBUG_MODE", "NODE_ENV"]
```
- **`includeDirectories`** (array of strings):
- **Description:** Specifies an array of additional absolute or relative paths to include in the workspace context. This allows you to work with files across multiple directories as if they were one. Paths can use `~` to refer to the user's home directory. This setting can be combined with the `--include-directories` command-line flag.
- **Default:** `[]`
- **Example:**
```json
"includeDirectories": [
"/path/to/another/project",
"../shared-library",
"~/common-utils"
]
```
- **`loadMemoryFromIncludeDirectories`** (boolean):
- **Description:** Controls the behavior of the `/memory refresh` command. If set to `true`, `GEMINI.md` files should be loaded from all directories that are added. If set to `false`, `GEMINI.md` should only be loaded from the current directory.
- **Default:** `false`
- **Example:**
```json
"loadMemoryFromIncludeDirectories": true
```
### Example `settings.json`:
@@ -167,10 +284,36 @@ In addition to a project settings file, a project's `.gemini` directory can cont
"command": "node",
"args": ["mcp_server.js", "--verbose"]
}
}
},
"telemetry": {
"enabled": true,
"target": "local",
"otlpEndpoint": "http://localhost:4317",
"logPrompts": true
},
"usageStatisticsEnabled": true,
"hideTips": false,
"hideBanner": false,
"maxSessionTurns": 10,
"summarizeToolOutput": {
"run_shell_command": {
"tokenBudget": 100
}
},
"excludedProjectEnvVars": ["DEBUG", "DEBUG_MODE", "NODE_ENV"],
"includeDirectories": ["path/to/dir1", "~/path/to/dir2", "../path/to/dir3"],
"loadMemoryFromIncludeDirectories": true
}
```
## Shell History
The CLI keeps a history of shell commands you run. To avoid conflicts between different projects, this history is stored in a project-specific directory within your user's home folder.
- **Location:** `~/.gemini/tmp/<project_hash>/shell_history`
- `<project_hash>` is a unique identifier generated from your project's root path.
- The history is stored in a file named `shell_history`.
## Environment Variables & `.env` Files
Environment variables are a common way to configure applications, especially for sensitive information like API keys or for settings that might change between environments.
@@ -181,37 +324,37 @@ The CLI automatically loads environment variables from an `.env` file. The loadi
2. If not found, it searches upwards in parent directories until it finds an `.env` file or reaches the project root (identified by a `.git` folder) or the home directory.
3. If still not found, it looks for `~/.env` (in the user's home directory).
**Environment Variable Exclusion:** Some environment variables (like `DEBUG` and `DEBUG_MODE`) are automatically excluded from being loaded from project `.env` files to prevent interference with gemini-cli behavior. Variables from `.gemini/.env` files are never excluded. You can customize this behavior using the `excludedProjectEnvVars` setting in your `settings.json` file.
- **`GEMINI_API_KEY`** (Required):
- Your API key for the Gemini API.
- **Crucial for operation.** The CLI will not function without it.
- Set this in your shell profile (e.g., `~/.bashrc`, `~/.zshrc`) or an `.env` file.
- **`GEMINI_MODEL`**:
- Specifies the default Gemini model to use.
- Overrides the hardcoded default, which is currently `gemini-2.5-pro-preview-05-06`.
- Example: `export GEMINI_MODEL="gemini-1.5-flash-latest"`
- Overrides the hardcoded default
- Example: `export GEMINI_MODEL="gemini-2.5-flash"`
- **`GOOGLE_API_KEY`**:
- Your Google Cloud API key.
- Required for using Vertex AI in express mode.
- Ensure you have the necessary permissions and set the `GOOGLE_GENAI_USE_VERTEXAI=true` environment variable.
- Example: `export GOOGLE_API_KEY="YOUR_GOOGLE_API_ KEY"`.
- Ensure you have the necessary permissions.
- Example: `export GOOGLE_API_KEY="YOUR_GOOGLE_API_KEY"`.
- **`GOOGLE_CLOUD_PROJECT`**:
- Your Google Cloud Project ID.
- Required for using Code Assist or Vertex AI.
- If using Vertex AI, ensure you have the necessary permissions and set the `GOOGLE_GENAI_USE_VERTEXAI=true` environment variable.
- If using Vertex AI, ensure you have the necessary permissions in this project.
- **Cloud Shell Note:** When running in a Cloud Shell environment, this variable defaults to a special project allocated for Cloud Shell users. If you have `GOOGLE_CLOUD_PROJECT` set in your global environment in Cloud Shell, it will be overridden by this default. To use a different project in Cloud Shell, you must define `GOOGLE_CLOUD_PROJECT` in a `.env` file.
- Example: `export GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"`.
- **`GOOGLE_APPLICATION_CREDENTIALS`** (string):
- **Description:** The path to your Google Application Credentials JSON file.
- **Example:** `export GOOGLE_APPLICATION_CREDENTIALS="/path/to/your/credentials.json"`
- **`OTLP_GOOGLE_CLOUD_PROJECT`**:
- Your Google Cloud Project ID for Telemetry in Google Cloud
- Example: `export OTLP_GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"`.
- **`GOOGLE_CLOUD_LOCATION`**:
- Your Google Cloud Project Location (e.g., us-central1).
- Required for using Vertex AI in non express mode.
- If using Vertex AI, ensure you have the necessary permissions and set the `GOOGLE_GENAI_USE_VERTEXAI=true` environment variable.
- Example: `export GOOGLE_CLOUD_LOCATION="YOUR_PROJECT_LOCATION"`.
- **`GEMINI_CODE_ASSIST`**:
- Enables Code Assist functionality.
- Accepts `true`, `false`, or a custom command string.
- If you are using an Enterprise account you should also set the `GOOGLE_CLOUD_PROJECT` environment variable.
- Example: `export GEMINI_CODE_ASSIST=true`.
- **`GEMINI_SANDBOX`**:
- Alternative to the `sandbox` setting in `settings.json`.
- Accepts `true`, `false`, `docker`, `podman`, or a custom command string.
@@ -222,6 +365,7 @@ The CLI automatically loads environment variables from an `.env` file. The loadi
- `<profile_name>`: Uses a custom profile. To define a custom profile, create a file named `sandbox-macos-<profile_name>.sb` in your project's `.gemini/` directory (e.g., `my-project/.gemini/sandbox-macos-custom.sb`).
- **`DEBUG` or `DEBUG_MODE`** (often used by underlying libraries or the CLI itself):
- Set to `true` or `1` to enable verbose debug logging, which can be helpful for troubleshooting.
- **Note:** These variables are automatically excluded from project `.env` files by default to prevent interference with gemini-cli behavior. Use `.gemini/.env` files if you need to set these for gemini-cli specifically.
- **`NO_COLOR`**:
- Set to any value to disable all color output in the CLI.
- **`CLI_TITLE`**:
@@ -234,19 +378,56 @@ The CLI automatically loads environment variables from an `.env` file. The loadi
Arguments passed directly when running the CLI can override other configurations for that specific session.
- **`--model <model_name>`** (or **`-m <model_name>`**):
- **`--model <model_name>`** (**`-m <model_name>`**):
- Specifies the Gemini model to use for this session.
- Example: `npm start -- --model gemini-1.5-pro-latest`
- **`--sandbox`** (or **`-s`**):
- Enables sandbox mode for this session. The exact behavior might depend on other sandbox configurations (environment variables, settings files).
- **`--debug_mode`** (or **`-d`**):
- **`--prompt <your_prompt>`** (**`-p <your_prompt>`**):
- Used to pass a prompt directly to the command. This invokes Gemini CLI in a non-interactive mode.
- **`--prompt-interactive <your_prompt>`** (**`-i <your_prompt>`**):
- Starts an interactive session with the provided prompt as the initial input.
- The prompt is processed within the interactive session, not before it.
- Cannot be used when piping input from stdin.
- Example: `gemini -i "explain this code"`
- **`--sandbox`** (**`-s`**):
- Enables sandbox mode for this session.
- **`--sandbox-image`**:
- Sets the sandbox image URI.
- **`--debug`** (**`-d`**):
- Enables debug mode for this session, providing more verbose output.
- **`--question <your_question>`** (or **`-q <your_question>`**):
- Used to pass a question directly to the command, especially when piping input to the CLI.
- **`--all_files`** (or **`-a`**):
- **`--all-files`** (**`-a`**):
- If set, recursively includes all files within the current directory as context for the prompt.
- **`--help`** (or **`-h`**):
- Displays help information about command-line arguments.
- **`--show-memory-usage`**:
- Displays the current memory usage.
- **`--yolo`**:
- Enables YOLO mode, which automatically approves all tool calls.
- **`--telemetry`**:
- Enables [telemetry](../telemetry.md).
- **`--telemetry-target`**:
- Sets the telemetry target. See [telemetry](../telemetry.md) for more information.
- **`--telemetry-otlp-endpoint`**:
- Sets the OTLP endpoint for telemetry. See [telemetry](../telemetry.md) for more information.
- **`--telemetry-log-prompts`**:
- Enables logging of prompts for telemetry. See [telemetry](../telemetry.md) for more information.
- **`--checkpointing`**:
- Enables [checkpointing](../checkpointing.md).
- **`--extensions <extension_name ...>`** (**`-e <extension_name ...>`**):
- Specifies a list of extensions to use for the session. If not provided, all available extensions are used.
- Use the special term `gemini -e none` to disable all extensions.
- Example: `gemini -e my-extension -e my-other-extension`
- **`--list-extensions`** (**`-l`**):
- Lists all available extensions and exits.
- **`--proxy`**:
- Sets the proxy for the CLI.
- Example: `--proxy http://localhost:7890`.
- **`--include-directories <dir1,dir2,...>`**:
- Includes additional directories in the workspace for multi-directory support.
- Can be specified multiple times or as comma-separated values.
- 5 directories can be added at maximum.
- Example: `--include-directories /path/to/project1,/path/to/project2` or `--include-directories /path/to/project1 --include-directories /path/to/project2`
- **`--version`**:
- Displays the version of the CLI.
## Context Files (Hierarchical Instructional Context)
@@ -266,7 +447,7 @@ Here's a conceptual example of what a context file at the root of a TypeScript p
- When generating new TypeScript code, please follow the existing coding style.
- Ensure all new functions and classes have JSDoc comments.
- Prefer functional programming paradigms where appropriate.
- All code should be compatible with TypeScript 5.0 and Node.js 18+.
- All code should be compatible with TypeScript 5.0 and Node.js 20+.
## Coding Style:
@@ -297,9 +478,10 @@ This example demonstrates how you can provide general project context, specific
- Location: The CLI searches for the configured context file in the current working directory and then in each parent directory up to either the project root (identified by a `.git` folder) or your home directory.
- Scope: Provides context relevant to the entire project or a significant portion of it.
3. **Sub-directory Context Files (Contextual/Local):**
- Location: The CLI also scans for the configured context file in subdirectories _below_ the current working directory (respecting common ignore patterns like `node_modules`, `.git`, etc.).
- Scope: Allows for highly specific instructions relevant to a particular component, module, or sub-section of your project.
- Location: The CLI also scans for the configured context file in subdirectories _below_ the current working directory (respecting common ignore patterns like `node_modules`, `.git`, etc.). The breadth of this search is limited to 200 directories by default, but can be configured with a `memoryDiscoveryMaxDirs` field in your `settings.json` file.
- Scope: Allows for highly specific instructions relevant to a particular component, module, or subsection of your project.
- **Concatenation & UI Indication:** The contents of all found context files are concatenated (with separators indicating their origin and path) and provided as part of the system prompt to the Gemini model. The CLI footer displays the count of loaded context files, giving you a quick visual cue about the active instructional context.
- **Importing Content:** You can modularize your context files by importing other Markdown files using the `@path/to/file.md` syntax. For more details, see the [Memory Import Processor documentation](../core/memport.md).
- **Commands for Memory Management:**
- Use `/memory refresh` to force a re-scan and reload of all context files from all configured locations. This updates the AI's instructional context.
- Use `/memory show` to display the combined instructional context currently loaded, allowing you to verify the hierarchy and content being used by the AI.
@@ -335,3 +517,29 @@ When `.gemini/sandbox.Dockerfile` exists, you can use `BUILD_SANDBOX` environmen
```bash
BUILD_SANDBOX=1 gemini -s
```
## Usage Statistics
To help us improve the Gemini CLI, we collect anonymized usage statistics. This data helps us understand how the CLI is used, identify common issues, and prioritize new features.
**What we collect:**
- **Tool Calls:** We log the names of the tools that are called, whether they succeed or fail, and how long they take to execute. We do not collect the arguments passed to the tools or any data returned by them.
- **API Requests:** We log the Gemini model used for each request, the duration of the request, and whether it was successful. We do not collect the content of the prompts or responses.
- **Session Information:** We collect information about the configuration of the CLI, such as the enabled tools and the approval mode.
**What we DON'T collect:**
- **Personally Identifiable Information (PII):** We do not collect any personal information, such as your name, email address, or API keys.
- **Prompt and Response Content:** We do not log the content of your prompts or the responses from the Gemini model.
- **File Content:** We do not log the content of any files that are read or written by the CLI.
**How to opt out:**
You can opt out of usage statistics collection at any time by setting the `usageStatisticsEnabled` property to `false` in your `settings.json` file:
```json
{
"usageStatisticsEnabled": false
}
```
+2
View File
@@ -4,8 +4,10 @@ Within Gemini CLI, `packages/cli` is the frontend for users to send and receive
## Navigating this section
- **[Authentication](./authentication.md):** A guide to setting up authentication with Google's AI services.
- **[Commands](./commands.md):** A reference for Gemini CLI commands (e.g., `/help`, `/tools`, `/theme`).
- **[Configuration](./configuration.md):** A guide to tailoring Gemini CLI behavior using configuration files.
- **[Token Caching](./token-caching.md):** Optimize API costs through token caching.
- **[Themes](./themes.md)**: A guide to customizing the CLI's appearance with different themes.
- **[Tutorials](tutorials.md)**: A tutorial showing how to use Gemini CLI to automate a development task.
+86 -1
View File
@@ -30,7 +30,92 @@ Gemini CLI comes with a selection of pre-defined themes, which you can list usin
### Theme Persistence
Selected themes are saved in Gemini CLI's [configuration](./docs/cli/configuration.md) so your preference is remembered across sessions.
Selected themes are saved in Gemini CLI's [configuration](./configuration.md) so your preference is remembered across sessions.
---
## Custom Color Themes
Gemini CLI allows you to create your own custom color themes by specifying them in your `settings.json` file. This gives you full control over the color palette used in the CLI.
### How to Define a Custom Theme
Add a `customThemes` block to your user, project, or system `settings.json` file. Each custom theme is defined as an object with a unique name and a set of color keys. For example:
```json
{
"customThemes": {
"MyCustomTheme": {
"name": "MyCustomTheme",
"type": "custom",
"Background": "#181818",
"Foreground": "#F8F8F2",
"LightBlue": "#82AAFF",
"AccentBlue": "#61AFEF",
"AccentPurple": "#C678DD",
"AccentCyan": "#56B6C2",
"AccentGreen": "#98C379",
"AccentYellow": "#E5C07B",
"AccentRed": "#E06C75",
"Comment": "#5C6370",
"Gray": "#ABB2BF",
"DiffAdded": "#A6E3A1",
"DiffRemoved": "#F38BA8",
"DiffModified": "#89B4FA",
"GradientColors": ["#4796E4", "#847ACE", "#C3677F"]
}
}
}
```
**Color keys:**
- `Background`
- `Foreground`
- `LightBlue`
- `AccentBlue`
- `AccentPurple`
- `AccentCyan`
- `AccentGreen`
- `AccentYellow`
- `AccentRed`
- `Comment`
- `Gray`
- `DiffAdded` (optional, for added lines in diffs)
- `DiffRemoved` (optional, for removed lines in diffs)
- `DiffModified` (optional, for modified lines in diffs)
**Required Properties:**
- `name` (must match the key in the `customThemes` object and be a string)
- `type` (must be the string `"custom"`)
- `Background`
- `Foreground`
- `LightBlue`
- `AccentBlue`
- `AccentPurple`
- `AccentCyan`
- `AccentGreen`
- `AccentYellow`
- `AccentRed`
- `Comment`
- `Gray`
You can use either hex codes (e.g., `#FF0000`) **or** standard CSS color names (e.g., `coral`, `teal`, `blue`) for any color value. See [CSS color names](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#color_keywords) for a full list of supported names.
You can define multiple custom themes by adding more entries to the `customThemes` object.
### Example Custom Theme
<img src="../assets/theme-custom.png" alt="Custom theme example" width="600" />
### Using Your Custom Theme
- Select your custom theme using the `/theme` command in Gemini CLI. Your custom theme will appear in the theme selection dialog.
- Or, set it as the default by adding `"theme": "MyCustomTheme"` to your `settings.json`.
- Custom themes can be set at the user, project, or system level, and follow the same [configuration precedence](./configuration.md) as other settings.
---
## Dark Themes
+14
View File
@@ -0,0 +1,14 @@
# Token Caching and Cost Optimization
Gemini CLI automatically optimizes API costs through token caching when using API key authentication (Gemini API key or Vertex AI). This feature reuses previous system instructions and context to reduce the number of tokens processed in subsequent requests.
**Token caching is available for:**
- API key users (Gemini API key)
- Vertex AI users (with project and location setup)
**Token caching is not available for:**
- OAuth users (Google Personal/Enterprise accounts) - the Code Assist API does not support cached content creation at this time
You can view your token usage and cached token savings using the `/stats` command. When cached tokens are available, they will be displayed in the stats output.
+22 -43
View File
@@ -1,76 +1,55 @@
# Gemini CLI Core
The Gemini CLI Core (`packages/core`) acts as the backend engine for the Gemini CLI, handling communication with the Gemini API, managing tools, and processing requests from the CLI client. For a general overview of the Gemini CLI, see the [main documentation page](../index.md).
Gemini CLI's core package (`packages/core`) is the backend portion of Gemini CLI, handling communication with the Gemini API, managing tools, and processing requests sent from `packages/cli`. For a general overview of Gemini CLI, see the [main documentation page](../index.md).
## Navigating this Section
## Navigating this section
- **[Core Configuration](../cli/configuration.md):** Details on how to configure the core component, including environment variables and specific settings.
- **[Core Tools API](./tools-api.md):** Information on how tools are defined, registered, and used by the core.
- **[Core tools API](./tools-api.md):** Information on how tools are defined, registered, and used by the core.
- **[Memory Import Processor](./memport.md):** Documentation for the modular GEMINI.md import feature using @file.md syntax.
## Role of the Core
## Role of the core
The core package is a crucial part of the Gemini CLI ecosystem. While the CLI (`packages/cli`) provides the user interface, the core is responsible for:
While the `packages/cli` portion of Gemini CLI provides the user interface, `packages/core` is responsible for:
- **API Interaction:** Securely communicating with the Google Gemini API, sending user prompts, and receiving model responses.
- **Prompt Engineering:** Constructing effective prompts for the Gemini model, potentially incorporating conversation history, tool definitions, and instructional context from `GEMINI.md` files.
- **Tool Management & Orchestration:**
- **Gemini API interaction:** Securely communicating with the Google Gemini API, sending user prompts, and receiving model responses.
- **Prompt engineering:** Constructing effective prompts for the Gemini model, potentially incorporating conversation history, tool definitions, and instructional context from `GEMINI.md` files.
- **Tool management & orchestration:**
- Registering available tools (e.g., file system tools, shell command execution).
- Interpreting tool use requests from the Gemini model.
- Executing the requested tools with the provided arguments.
- Returning tool execution results to the Gemini model for further processing.
- **Session and State Management:** Keeping track of the conversation state, including history and any relevant context required for coherent interactions.
- **Session and state management:** Keeping track of the conversation state, including history and any relevant context required for coherent interactions.
- **Configuration:** Managing core-specific configurations, such as API key access, model selection, and tool settings.
## Key Components and Functionality
While the exact implementation details are within the `packages/core/src/` directory, key conceptual components include:
- **API Client** (`client.ts`): A module responsible for making HTTP requests to the Gemini API, handling authentication, and parsing responses.
- **Prompt Management** (`prompts.ts`): Logic for creating and formatting the prompts sent to the Gemini model. This includes integrating user queries, historical context, and tool specifications.
- **Tool Registry and Execution** (`tool-registry.ts`, `tools.ts`, individual tool files like `read-file.ts`, `shell.ts`):
- A system for discovering, registering, and describing available tools to the Gemini model.
- Code for executing each tool safely and effectively, often involving interaction with the operating system or external services.
- **Configuration (`config.ts`):** Handles loading and providing access to core-side configurations, including API keys, model choices, and potentially tool-specific settings.
- **Turn Management (`turn.ts`):** Manages the flow of a single conversational turn, from receiving user input to generating a final response, potentially involving multiple tool calls.
## Interaction with the CLI
The CLI and Core typically communicate over a local interface (e.g., standard input/output, or a local network connection if designed for broader use, though the current structure suggests a tightly coupled Node.js application).
1. The CLI captures user input and forwards it to the Core.
2. The Core processes the input, interacts with the Gemini API and tools as needed.
3. The Core sends responses (text, tool calls, errors) back to the CLI.
4. The CLI formats and displays these responses to the user.
## Security Considerations
## Security considerations
The core plays a vital role in security:
- **API Key Management:** It handles the `GEMINI_API_KEY` and ensures it is used securely when communicating with the Gemini API.
- **Tool Execution:** When tools interact with the local system (e.g., `run_shell_command`), the core (and its underlying tool implementations) must do so with appropriate caution, often involving sandboxing mechanisms to prevent unintended side effects.
- **API key management:** It handles the `GEMINI_API_KEY` and ensures it's used securely when communicating with the Gemini API.
- **Tool execution:** When tools interact with the local system (e.g., `run_shell_command`), the core (and its underlying tool implementations) must do so with appropriate caution, often involving sandboxing mechanisms to prevent unintended modifications.
## Chat History Compression
## Chat history compression
To ensure that long conversations don't exceed the token limits of the Gemini model, the CLI includes a chat history compression feature.
To ensure that long conversations don't exceed the token limits of the Gemini model, the core includes a chat history compression feature.
When a conversation approaches the token limit for the configured model, the CLI will automatically compress the conversation history before sending it to the model. This compression is designed to be lossless in terms of the information conveyed, but it reduces the overall number of tokens used.
When a conversation approaches the token limit for the configured model, the core automatically compresses the conversation history before sending it to the model. This compression is designed to be lossless in terms of the information conveyed, but it reduces the overall number of tokens used.
You can find the token limits for each model in the [Google AI documentation](https://ai.google.dev/gemini-api/docs/models).
## Model Fallback
## Model fallback
The Gemini CLI includes a model fallback mechanism to ensure that you can continue to use the CLI even if the default "pro" model is rate-limited.
Gemini CLI includes a model fallback mechanism to ensure that you can continue to use the CLI even if the default "pro" model is rate-limited.
If you are using the default "pro" model and the CLI detects that you are being rate-limited, it will automatically switch to the "flash" model for the current session. This allows you to continue working without interruption.
If you are using the default "pro" model and the CLI detects that you are being rate-limited, it automatically switches to the "flash" model for the current session. This allows you to continue working without interruption.
## File Discovery Service
## File discovery service
The file discovery service is responsible for finding files in the project that are relevant to the current context. It is used by the `@` command and other tools that need to access files.
## Memory Discovery Service
## Memory discovery service
The memory discovery service is responsible for finding and loading the `GEMINI.md` files that provide context to the model. It searches for these files in a hierarchical manner, starting from the current working directory and moving up to the project root and the user's home directory. It also searches in subdirectories.
This allows you to have global, project-level, and component-level context files, which are all combined to provide the model with the most relevant information.
You can use the `/memory show` command to see the combined content of all loaded `GEMINI.md` files, and the `/memory refresh` command to reload them.
You can use the [`/memory` command](../cli/commands.md) to `show`, `add`, and `refresh` the content of loaded `GEMINI.md` files.
+215
View File
@@ -0,0 +1,215 @@
# Memory Import Processor
The Memory Import Processor is a feature that allows you to modularize your GEMINI.md files by importing content from other files using the `@file.md` syntax.
## Overview
This feature enables you to break down large GEMINI.md files into smaller, more manageable components that can be reused across different contexts. The import processor supports both relative and absolute paths, with built-in safety features to prevent circular imports and ensure file access security.
## Syntax
Use the `@` symbol followed by the path to the file you want to import:
```markdown
# Main GEMINI.md file
This is the main content.
@./components/instructions.md
More content here.
@./shared/configuration.md
```
## Supported Path Formats
### Relative Paths
- `@./file.md` - Import from the same directory
- `@../file.md` - Import from parent directory
- `@./components/file.md` - Import from subdirectory
### Absolute Paths
- `@/absolute/path/to/file.md` - Import using absolute path
## Examples
### Basic Import
```markdown
# My GEMINI.md
Welcome to my project!
@./getting-started.md
## Features
@./features/overview.md
```
### Nested Imports
The imported files can themselves contain imports, creating a nested structure:
```markdown
# main.md
@./header.md
@./content.md
@./footer.md
```
```markdown
# header.md
# Project Header
@./shared/title.md
```
## Safety Features
### Circular Import Detection
The processor automatically detects and prevents circular imports:
```markdown
# file-a.md
@./file-b.md
# file-b.md
@./file-a.md <!-- This will be detected and prevented -->
```
### File Access Security
The `validateImportPath` function ensures that imports are only allowed from specified directories, preventing access to sensitive files outside the allowed scope.
### Maximum Import Depth
To prevent infinite recursion, there's a configurable maximum import depth (default: 5 levels).
## Error Handling
### Missing Files
If a referenced file doesn't exist, the import will fail gracefully with an error comment in the output.
### File Access Errors
Permission issues or other file system errors are handled gracefully with appropriate error messages.
## Code Region Detection
The import processor uses the `marked` library to detect code blocks and inline code spans, ensuring that `@` imports inside these regions are properly ignored. This provides robust handling of nested code blocks and complex Markdown structures.
## Import Tree Structure
The processor returns an import tree that shows the hierarchy of imported files, similar to Claude's `/memory` feature. This helps users debug problems with their GEMINI.md files by showing which files were read and their import relationships.
Example tree structure:
```
Memory Files
L project: GEMINI.md
L a.md
L b.md
L c.md
L d.md
L e.md
L f.md
L included.md
```
The tree preserves the order that files were imported and shows the complete import chain for debugging purposes.
## Comparison to Claude Code's `/memory` (`claude.md`) Approach
Claude Code's `/memory` feature (as seen in `claude.md`) produces a flat, linear document by concatenating all included files, always marking file boundaries with clear comments and path names. It does not explicitly present the import hierarchy, but the LLM receives all file contents and paths, which is sufficient for reconstructing the hierarchy if needed.
Note: The import tree is mainly for clarity during development and has limited relevance to LLM consumption.
## API Reference
### `processImports(content, basePath, debugMode?, importState?)`
Processes import statements in GEMINI.md content.
**Parameters:**
- `content` (string): The content to process for imports
- `basePath` (string): The directory path where the current file is located
- `debugMode` (boolean, optional): Whether to enable debug logging (default: false)
- `importState` (ImportState, optional): State tracking for circular import prevention
**Returns:** Promise<ProcessImportsResult> - Object containing processed content and import tree
### `ProcessImportsResult`
```typescript
interface ProcessImportsResult {
content: string; // The processed content with imports resolved
importTree: MemoryFile; // Tree structure showing the import hierarchy
}
```
### `MemoryFile`
```typescript
interface MemoryFile {
path: string; // The file path
imports?: MemoryFile[]; // Direct imports, in the order they were imported
}
```
### `validateImportPath(importPath, basePath, allowedDirectories)`
Validates import paths to ensure they are safe and within allowed directories.
**Parameters:**
- `importPath` (string): The import path to validate
- `basePath` (string): The base directory for resolving relative paths
- `allowedDirectories` (string[]): Array of allowed directory paths
**Returns:** boolean - Whether the import path is valid
### `findProjectRoot(startDir)`
Finds the project root by searching for a `.git` directory upwards from the given start directory. Implemented as an **async** function using non-blocking file system APIs to avoid blocking the Node.js event loop.
**Parameters:**
- `startDir` (string): The directory to start searching from
**Returns:** Promise<string> - The project root directory (or the start directory if no `.git` is found)
## Best Practices
1. **Use descriptive file names** for imported components
2. **Keep imports shallow** - avoid deeply nested import chains
3. **Document your structure** - maintain a clear hierarchy of imported files
4. **Test your imports** - ensure all referenced files exist and are accessible
5. **Use relative paths** when possible for better portability
## Troubleshooting
### Common Issues
1. **Import not working**: Check that the file exists and the path is correct
2. **Circular import warnings**: Review your import structure for circular references
3. **Permission errors**: Ensure the files are readable and within allowed directories
4. **Path resolution issues**: Use absolute paths if relative paths aren't resolving correctly
### Debug Mode
Enable debug mode to see detailed logging of the import process:
```typescript
const result = await processImports(content, basePath, true);
```
-287
View File
@@ -1,287 +0,0 @@
# Gemini CLI Observability Guide
Telemetry provides crucial data about the Gemini CLI's performance, health, and usage. By enabling it, you can monitor operations, debug issues, and optimize tool usage through traces, metrics, and structured logs.
This entire system is built on the **[OpenTelemetry] (OTEL)** standard, allowing you to send data to any compatible backend, from your local terminal to a cloud service.
[OpenTelemetry]: https://opentelemetry.io/
## Quick Start
### Telemetry with Google Cloud
1. **Ensure Prerequisites:**
Ensure that:
- You have exported the `OTLP_GOOGLE_CLOUD_PROJECT` environment variable.
- You have authenticated with Google Cloud and have the necessary IAM roles.
For full details, see the [Google Cloud](#google-cloud) prerequisites.
1. **Run the Command:** Execute the following command from the project root:
```bash
npm run telemetry -- --target=gcp
```
1. **Run Gemini CLI:** In a separate terminal, run your Gemini CLI commands. This will generate telemetry data that the collector will capture.
1. **View Data:** The script will provide links to view your telemetry data (traces, metrics, logs) in the Google Cloud Console.
1. **Details:** Refer to documentation for telemetry in [Google Cloud](#google-cloud).
### Local Telemetry with Jaeger UI (for Traces)
1. **Run the Command:** Execute the following command from the project root:
```bash
npm run telemetry -- --target=local
```
2. **Run Gemini CLI:** In a separate terminal, run your Gemini CLI commands. This will generate telemetry data that the collector will capture.
3. **View Logs/Metrics:** Check the `.gemini/otel/collector.log` file for raw logs and metrics.
4. **View Traces:** Open your browser and go to `http://localhost:16686` to see traces in the Jaeger UI.
5. **Details:** Refer to documentation for telemetry in [Local](#local).
## Enabling Telemetry
You can enable telemetry in multiple ways. [Configuration](configuration.md) is primarily managed via the `.gemini/settings.json` file and environment variables, but CLI flags can override these settings for a specific session.
> **A Note on Sandbox Mode:** Telemetry is not compatible with sandbox mode at this time. Turn off sandbox mode before enabling telemetry. Tracked in #894.
**Order of Precedence:**
Telemetry settings are resolved in the following order (highest precedence first):
1. **CLI Flags (for `gemini` command):**
- `--telemetry` / `--no-telemetry`: Overrides `telemetry.enabled`. If this flag is not provided, telemetry is disabled unless enabled in settings files.
- `--telemetry-target <local|gcp>`: Overrides `telemetry.target`.
- `--telemetry-otlp-endpoint <URL>`: Overrides `telemetry.otlpEndpoint`.
- `--telemetry-log-prompts` / `--no-telemetry-log-prompts`: Overrides `telemetry.logPrompts`.
2. **Environment Variables:**
- `OTEL_EXPORTER_OTLP_ENDPOINT`: Overrides `telemetry.otlpEndpoint` if no `--telemetry-otlp-endpoint` flag is present.
3. **Workspace Settings File (`.gemini/settings.json`):** Values from the `telemetry` object in this project-specific file.
4. **User Settings File (`~/.gemini/settings.json`):** Values from the `telemetry` object in this global user file.
5. **Defaults:** applied if not set by any of the above.
- `telemetry.enabled`: `false`
- `telemetry.target`: `local`
- `telemetry.otlpEndpoint`: `http://localhost:4317`
- `telemetry.logPrompts`: `true`
**For the `npm run telemetry -- --target=<gcp|local>` script:**
The `--target` argument to this script _only_ overrides the `telemetry.target` for the duration and purpose of that script (i.e., choosing which collector to start). It does not permanently change your `settings.json`. The script will first look at `settings.json` for a `telemetry.target` to use as its default.
**Example settings:**
Add these lines to configure telemetry in your workspace (`.gemini/settings.json`) or user (`~/.gemini/settings.json`) settings for GCP:
```json
{
"telemetry": {
"enabled": true,
"target": "gcp"
},
"sandbox": false
}
```
## Running an OTEL Collector
An OTEL Collector is a service that receives, processes, and exports telemetry data.
The CLI sends data using the OTLP/gRPC protocol.
Learn more about OTEL exporter standard configuration in [documentation][otel-config-docs].
[otel-config-docs]: https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/
### Configuration
1. Install [otelcol-contrib] or use [docker]
[otelcol-contrib]: https://github.com/open-telemetry/opentelemetry-collector-contrib
[docker]: https://www.docker.com/
2. Create a folder for the OTEL configurations:
```
mkdir .gemini/otel
```
### Local
Use the `npm run telemetry -- --target=local` command which automates the entire process of setting up a local telemetry pipeline, including configuring the necessary settings in your `.gemini/settings.json` file. The underlying script installs `otelcol-contrib` (The OpenTelemetry Collector) and `jaeger` (The Jaeger UI for viewing traces). To use it:
1. **Run the Command**:
Execute the command from the root of the repository:
```bash
npm run telemetry -- --target=local
```
The script will:
- Download Jaeger and OTEL if needed.
- Start a local Jaeger instance.
- Start an OTEL collector configured to receive data from the Gemini CLI.
- Automatically enable telemetry in your workspace settings.
- On exit, disable telemetry.
2. **View Traces**:
Open your web browser and navigate to **http://localhost:16686** to access the Jaeger UI. Here you can inspect detailed traces of Gemini CLI operations.
3. **Inspect Logs and Metrics**:
The script redirects the OTEL collector's output (which includes logs and metrics) to `.gemini/otel/collector.log`. You can monitor this file to see the raw telemetry data:
```bash
tail -f .gemini/otel/collector.log
```
4. **Stop the Services**:
Press `Ctrl+C` in the terminal where the script is running to stop the OTEL Collector and Jaeger services.
### Google Cloud
Use the `npm run telemetry -- --target=gcp` command which automates setting up a local OpenTelemetry collector that forwards data to your Google Cloud project, including configuring the necessary settings in your `.gemini/settings.json` file. The underlying script installs `otelcol-contrib`. To use it:
1. **Prerequisites**:
- Ensure you have a Google Cloud Project ID.
- Export the `GOOGLE_CLOUD_PROJECT` environment variable to make it available to the OTEL collector.
```bash
export OTLP_GOOGLE_CLOUD_PROJECT="your-project-id"
```
- Authenticate with Google Cloud (e.g., run `gcloud auth application-default login` or ensure `GOOGLE_APPLICATION_CREDENTIALS` is set).
- Ensure your account/service account has the necessary roles: "Cloud Trace Agent", "Monitoring Metric Writer", and "Logs Writer".
2. **Run the Command**:
Execute the command from the root of the repository:
```bash
npm run telemetry -- --target=gcp
```
The script will:
- Download the `otelcol-contrib` binary if needed.
- Start an OTEL collector configured to receive data from the Gemini CLI and export it to your specified Google Cloud project.
- Automatically enable telemetry and disable sandbox mode in your workspace settings (`.gemini/settings.json`).
- Provide direct links to view traces, metrics, and logs in your Google Cloud Console.
- On exit (Ctrl+C), it will attempt to restore your original telemetry and sandbox settings.
3. **Run Gemini CLI:**
In a separate terminal, run your Gemini CLI commands. This will generate telemetry data that the collector will capture.
4. **View Telemetry in Google Cloud**:
Use the links provided by the script to navigate to the Google Cloud Console and view your traces, metrics, and logs.
5. **Inspect Local Collector Logs**:
The script redirects the local OTEL collector\'s output to `.gemini/otel/collector-gcp.log`. You can monitor this file for detailed information or troubleshooting:
```bash
tail -f .gemini/otel/collector-gcp.log
```
6. **Stop the Service**:
Press `Ctrl+C` in the terminal where the script is running to stop the OTEL Collector.
## Data Reference: Logs & Metrics
A `sessionId` is included as a common attribute on all logs and metrics.
### Logs
These are timestamped records of specific events.
- `gemini_cli.config`: Fired once at startup with the CLI's configuration.
- **Attributes**:
- `model` (string)
- `embedding_model` (string)
- `sandbox_enabled` (boolean)
- `core_tools_enabled` (string)
- `approval_mode` (string)
- `api_key_enabled` (boolean)
- `vertex_ai_enabled` (boolean)
- `code_assist_enabled` (boolean)
- `log_prompts_enabled` (boolean)
- `file_filtering_respect_git_ignore` (boolean)
- `debug_mode` (boolean)
- `mcp_servers` (string)
- `gemini_cli.user_prompt`: Fired when a user submits a prompt.
- **Attributes**:
- `prompt_length`
- `prompt` (except if `log_prompts_enabled` is false)
- `gemini_cli.tool_call`: Fired for every function call.
- **Attributes**:
- `function_name`
- `function_args`
- `duration_ms`
- `success` (boolean)
- `decision` (string: "accept", "reject", or "modify", optional)
- `error` (optional)
- `error_type` (optional)
- `gemini_cli.api_request`: Fired when making a request to the Gemini API.
- **Attributes**:
- `model`
- `request_text` (optional)
- `gemini_cli.api_error`: Fired if the API request fails.
- **Attributes**:
- `model`
- `error`
- `error_type`
- `status_code`
- `duration_ms`
- `gemini_cli.api_response`: Fired upon receiving a response from the Gemini API.
- **Attributes**:
- `model`
- `status_code`
- `duration_ms`
- `error` (optional)
- `input_token_count`
- `output_token_count`
- `cached_content_token_count`
- `thoughts_token_count`
- `tool_token_count`
- `response_text` (optional)
### Metrics
These are numerical measurements of behavior over time.
- `gemini_cli.session.count` (Counter, Int): Incremented once per CLI startup.
- `gemini_cli.tool.call.count` (Counter, Int): Counts tool calls.
- **Attributes**:
- `function_name`
- `success` (boolean)
- `decision` (string: "accept", "reject", or "modify", optional)
- `gemini_cli.tool.call.latency` (Histogram, ms): Measures tool call latency.
- **Attributes**:
- `function_name`
- `decision` (string: "accept", "reject", or "modify", optional)
- `gemini_cli.api.request.count` (Counter, Int): Counts all API requests.
- **Attributes**:
- `model`
- `status_code`
- `error_type` (optional)
- `gemini_cli.api.request.latency` (Histogram, ms): Measures API request latency.
- **Attributes**:
- `model`
- `gemini_cli.token.usage` (Counter, Int): Counts the number of tokens used.
- **Attributes**:
- `model`
- `type` (string: "input", "output", "thought", "cache", or "tool")
- `gemini_cli.file.operation.count` (Counter, Int): Counts file operations.
- **Attributes**:
- `operation` (string: "create", "read", "update"): The type of file operation.
- `lines` (optional, Int): Number of lines in the file.
- `mimetype` (optional, string): Mimetype of the file.
- `extension` (optional, string): File extension of the file.
+7 -4
View File
@@ -5,21 +5,21 @@ The Gemini CLI core (`packages/core`) features a robust system for defining, reg
## Core Concepts
- **Tool (`tools.ts`):** An interface and base class (`BaseTool`) that defines the contract for all tools. Each tool must have:
- `name`: A unique internal name (used in API calls to Gemini).
- `displayName`: A user-friendly name.
- `description`: A clear explanation of what the tool does, which is provided to the Gemini model.
- `parameterSchema`: A JSON schema defining the parameters the tool accepts. This is crucial for the Gemini model to understand how to call the tool correctly.
- `parameterSchema`: A JSON schema defining the parameters that the tool accepts. This is crucial for the Gemini model to understand how to call the tool correctly.
- `validateToolParams()`: A method to validate incoming parameters.
- `getDescription()`: A method to provide a human-readable description of what the tool will do with specific parameters before execution.
- `shouldConfirmExecute()`: A method to determine if user confirmation is required before execution (e.g., for potentially destructive operations).
- `execute()`: The core method that performs the tool's action and returns a `ToolResult`.
- **`ToolResult` (`tools.ts`):** An interface defining the structure of a tool's execution outcome:
- `llmContent`: The factual string content to be included in the history sent back to the LLM for context.
- `llmContent`: The factual content to be included in the history sent back to the LLM for context. This can be a simple string or a `PartListUnion` (an array of `Part` objects and strings) for rich content.
- `returnDisplay`: A user-friendly string (often Markdown) or a special object (like `FileDiff`) for display in the CLI.
- **Returning Rich Content:** Tools are not limited to returning simple text. The `llmContent` can be a `PartListUnion`, which is an array that can contain a mix of `Part` objects (for images, audio, etc.) and `string`s. This allows a single tool execution to return multiple pieces of rich content.
- **Tool Registry (`tool-registry.ts`):** A class (`ToolRegistry`) responsible for:
- **Registering Tools:** Holding a collection of all available built-in tools (e.g., `ReadFileTool`, `ShellTool`).
- **Discovering Tools:** It can also discover tools dynamically:
@@ -44,6 +44,9 @@ The core comes with a suite of pre-defined tools, typically found in `packages/c
- `ShellTool` (`shell.ts`): Executes arbitrary shell commands (requires careful sandboxing and user confirmation).
- **Web Tools:**
- `WebFetchTool` (`web-fetch.ts`): Fetches content from a URL.
- `WebSearchTool` (`web-search.ts`): Performs a web search.
- **Memory Tools:**
- `MemoryTool` (`memoryTool.ts`): Interacts with the AI's memory.
Each of these tools extends `BaseTool` and implements the required methods for its specific functionality.
+18 -17
View File
@@ -15,17 +15,20 @@ This is the recommended way for end-users to install Gemini CLI. It involves dow
- **Global install:**
```bash
# Install the CLI globally
npm install -g @gemini-cli/cli
npm install -g @google/gemini-cli
```
# Now you can run the CLI from anywhere
Then, run the CLI from anywhere:
```bash
gemini
```
- **NPX execution:**
```bash
# Execute the latest version from NPM without a global install
npx @gemini-cli/cli
npx @google/gemini-cli
```
---
@@ -38,12 +41,12 @@ For security and isolation, Gemini CLI can be run inside a container. This is th
You can run the published sandbox image directly. This is useful for environments where you only have Docker and want to run the CLI.
```bash
# Run the published sandbox image
docker run --rm -it us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.1.0
docker run --rm -it us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.1.1
```
- **Using the `--sandbox` flag:**
If you have Gemini CLI installed locally (using the standard installation described above), you can instruct it to run inside the sandbox container.
```bash
gemini --sandbox "your prompt here"
gemini --sandbox -y -p "your prompt here"
```
---
@@ -73,11 +76,11 @@ Contributors to the project will want to run the CLI directly from the source co
### 4. Running the latest Gemini CLI commit from GitHub
You can run the most recently committed version of Gemini CLI directly from the GitHub repository. This is useful for testing features still in developement.
You can run the most recently committed version of Gemini CLI directly from the GitHub repository. This is useful for testing features still in development.
```bash
# Execute the CLI directly from the main branch on GitHub
npx https://github.com/google/gemini-cli
npx https://github.com/google-gemini/gemini-cli
```
## Deployment architecture
@@ -88,8 +91,8 @@ The execution methods described above are made possible by the following archite
Gemini CLI project is a monorepo that publishes two core packages to the NPM registry:
- `@gemini-cli/core`: The backend, handling logic and tool execution.
- `@gemini-cli/cli`: The user-facing frontend.
- `@google/gemini-cli-core`: The backend, handling logic and tool execution.
- `@google/gemini-cli`: The user-facing frontend.
These packages are used when performing the standard installation and when running Gemini CLI from the source.
@@ -97,20 +100,18 @@ These packages are used when performing the standard installation and when runni
There are two distinct build processes used, depending on the distribution channel:
- **NPM publication:** For publishing to the NPM registry, the TypeScript source code in `@gemini-cli/core` and `@gemini-cli/cli` is transpiled into standard JavaScript using the TypeScript Compiler (`tsc`). The resulting `dist/` directory is what gets published in the NPM package. This is a standard approach for TypeScript libraries.
- **NPM publication:** For publishing to the NPM registry, the TypeScript source code in `@google/gemini-cli-core` and `@google/gemini-cli` is transpiled into standard JavaScript using the TypeScript Compiler (`tsc`). The resulting `dist/` directory is what gets published in the NPM package. This is a standard approach for TypeScript libraries.
- **GitHub `npx` execution:** When running the latest version of Gemini CLI directly from GitHub, a different process is triggered by the `prepare` script in `package.json`. This script uses `esbuild` to bundle the entire application and its dependencies into a single, self-contained JavaScript file. This bundle is created on-the-fly on the user's machine and is not checked into the repository.
**Docker sandbox image**
The Docker-based execution method is supported by the `gemini-cli-sandbox` container image. This image is published to a container registry and contains a pre-installed, global version of Gemini CLI. The `scripts/prepare-cli-packagejson.js` script dynamically injects the URI of this image into the CLI's `package.json` before publishing, so the CLI knows which image to pull when the `--sandbox` flag is used.
The Docker-based execution method is supported by the `gemini-cli-sandbox` container image. This image is published to a container registry and contains a pre-installed, global version of Gemini CLI.
## Release process
A unified script, `npm run publish:release`, orchestrates the release process. The script performs the following actions:
The release process is automated through GitHub Actions. The release workflow performs the following actions:
1. Build the NPM packages using `tsc`.
2. Update the CLI's `package.json` with the Docker image URI.
3. Build and tag the `gemini-cli-sandbox` Docker image.
4. Push the Docker image to the container registry.
5. Publish the NPM packages to the artifact registry.
2. Publish the NPM packages to the artifact registry.
3. Create GitHub releases with bundled assets.
+6
View File
@@ -1,3 +1,8 @@
# Example Proxy Script
The following is an example of a proxy script that can be used with the `GEMINI_SANDBOX_PROXY_COMMAND` environment variable. This script only allows `HTTPS` connections to `example.com:443` and declines all other requests.
```javascript
#!/usr/bin/env node
/**
@@ -73,3 +78,4 @@ server.listen(PROXY_PORT, () => {
`[PROXY] Allowing HTTPS connections to domains: ${ALLOWED_DOMAINS.join(', ')}`,
);
});
```
+38 -2
View File
@@ -28,13 +28,49 @@ The `gemini-extension.json` file contains the configuration for the extension. T
"command": "node my-server.js"
}
},
"contextFileName": "GEMINI.md"
"contextFileName": "GEMINI.md",
"excludeTools": ["run_shell_command"]
}
```
- `name`: The name of the extension. This is used to uniquely identify the extension. This should match the name of your extension directory.
- `name`: The name of the extension. This is used to uniquely identify the extension and for conflict resolution when extension commands have the same name as user or project commands.
- `version`: The version of the extension.
- `mcpServers`: A map of MCP servers to configure. The key is the name of the server, and the value is the server configuration. These servers will be loaded on startup just like MCP servers configured in a [`settings.json` file](./cli/configuration.md). If both an extension and a `settings.json` file configure an MCP server with the same name, the server defined in the `settings.json` file takes precedence.
- `contextFileName`: The name of the file that contains the context for the extension. This will be used to load the context from the workspace. If this property is not used but a `GEMINI.md` file is present in your extension directory, then that file will be loaded.
- `excludeTools`: An array of tool names to exclude from the model. You can also specify command-specific restrictions for tools that support it, like the `run_shell_command` tool. For example, `"excludeTools": ["run_shell_command(rm -rf)"]` will block the `rm -rf` command.
When Gemini CLI starts, it loads all the extensions and merges their configurations. If there are any conflicts, the workspace configuration takes precedence.
## Extension Commands
Extensions can provide [custom commands](./cli/commands.md#custom-commands) by placing TOML files in a `commands/` subdirectory within the extension directory. These commands follow the same format as user and project custom commands and use standard naming conventions.
### Example
An extension named `gcp` with the following structure:
```
.gemini/extensions/gcp/
├── gemini-extension.json
└── commands/
├── deploy.toml
└── gcs/
└── sync.toml
```
Would provide these commands:
- `/deploy` - Shows as `[gcp] Custom command from deploy.toml` in help
- `/gcs:sync` - Shows as `[gcp] Custom command from sync.toml` in help
### Conflict Resolution
Extension commands have the lowest precedence. When a conflict occurs with user or project commands:
1. **No conflict**: Extension command uses its natural name (e.g., `/deploy`)
2. **With conflict**: Extension command is renamed with the extension prefix (e.g., `/gcp.deploy`)
For example, if both a user and the `gcp` extension define a `deploy` command:
- `/deploy` - Executes the user's deploy command
- `/gcp.deploy` - Executes the extension's deploy command (marked with `[gcp]` tag)
+59
View File
@@ -0,0 +1,59 @@
# Ignoring Files
This document provides an overview of the Gemini Ignore (`.geminiignore`) feature of the Gemini CLI.
The Gemini CLI includes the ability to automatically ignore files, similar to `.gitignore` (used by Git) and `.aiexclude` (used by Gemini Code Assist). Adding paths to your `.geminiignore` file will exclude them from tools that support this feature, although they will still be visible to other services (such as Git).
## How it works
When you add a path to your `.geminiignore` file, tools that respect this file will exclude matching files and directories from their operations. For example, when you use the [`read_many_files`](./tools/multi-file.md) command, any paths in your `.geminiignore` file will be automatically excluded.
For the most part, `.geminiignore` follows the conventions of `.gitignore` files:
- Blank lines and lines starting with `#` are ignored.
- Standard glob patterns are supported (such as `*`, `?`, and `[]`).
- Putting a `/` at the end will only match directories.
- Putting a `/` at the beginning anchors the path relative to the `.geminiignore` file.
- `!` negates a pattern.
You can update your `.geminiignore` file at any time. To apply the changes, you must restart your Gemini CLI session.
## How to use `.geminiignore`
To enable `.geminiignore`:
1. Create a file named `.geminiignore` in the root of your project directory.
To add a file or directory to `.geminiignore`:
1. Open your `.geminiignore` file.
2. Add the path or file you want to ignore, for example: `/archive/` or `apikeys.txt`.
### `.geminiignore` examples
You can use `.geminiignore` to ignore directories and files:
```
# Exclude your /packages/ directory and all subdirectories
/packages/
# Exclude your apikeys.txt file
apikeys.txt
```
You can use wildcards in your `.geminiignore` file with `*`:
```
# Exclude all .md files
*.md
```
Finally, you can exclude files and directories from exclusion with `!`:
```
# Exclude all .md files except README.md
*.md
!README.md
```
To remove paths from your `.geminiignore` file, delete the relevant lines.
+5 -1
View File
@@ -16,10 +16,11 @@ This documentation is organized into the following sections:
- **[CLI Introduction](./cli/index.md):** Overview of the command-line interface.
- **[Commands](./cli/commands.md):** Description of available CLI commands.
- **[Configuration](./cli/configuration.md):** Information on configuring the CLI.
- **[Checkpointing](./checkpointing.md):** Documentation for the checkpointing feature.
- **[Extensions](./extension.md):** How to extend the CLI with new functionality.
- **[Telemetry](./telemetry.md):** Overview of telemetry in the CLI.
- **Core Details:** Documentation for `packages/core`.
- **[Core Introduction](./core/index.md):** Overview of the core component.
- **[Configuration](./core/configuration.md):** Information on configuring the core component.
- **[Tools API](./core/tools-api.md):** Information on how the core manages and exposes tools.
- **Tools:**
- **[Tools Overview](./tools/index.md):** Overview of the available tools.
@@ -28,7 +29,10 @@ This documentation is organized into the following sections:
- **[Shell Tool](./tools/shell.md):** Documentation for the `run_shell_command` tool.
- **[Web Fetch Tool](./tools/web-fetch.md):** Documentation for the `web_fetch` tool.
- **[Web Search Tool](./tools/web-search.md):** Documentation for the `google_web_search` tool.
- **[Memory Tool](./tools/memory.md):** Documentation for the `save_memory` tool.
- **[Contributing & Development Guide](../CONTRIBUTING.md):** Information for contributors and developers, including setup, building, testing, and coding conventions.
- **[NPM Workspaces and Publishing](./npm.md):** Details on how the project's packages are managed and published.
- **[Troubleshooting Guide](./troubleshooting.md):** Find solutions to common problems and FAQs.
- **[Terms of Service and Privacy Notice](./tos-privacy.md):** Information on the terms of service and privacy notices applicable to your use of Gemini CLI.
We hope this documentation helps you make the most of the Gemini CLI!
+2 -2
View File
@@ -18,7 +18,7 @@ The integration tests can also be run using the following shortcut:
npm run test:e2e
```
## Running a specfic set of tests
## Running a specific set of tests
To run a subset of test files, you can use `npm run <integration test command> <file_name1> ....` where <integration test command> is either `test:e2e` or `test:integration*` and `<file_name>` is any of the `.test.js` files in the `integration-tests/` directory. For example, the following command runs `list_directory.test.js` and `write_file.test.js`:
@@ -132,7 +132,7 @@ This structure makes it easy to locate the artifacts for a specific test run, fi
## Continuous integration
To ensure the integration tests are always run, a GitHub Actions workflow is defined in `.github/workflows/e2e.yml`. This workflow automatically runs the integration tests on every pull request and push to the `main` branch.
To ensure the integration tests are always run, a GitHub Actions workflow is defined in `.github/workflows/e2e.yml`. This workflow automatically runs the integrations tests for pull requests against the `main` branch, or when a pull request is added to a merge queue.
The workflow runs the tests in different sandboxing environments to ensure Gemini CLI is tested across each:
+84
View File
@@ -0,0 +1,84 @@
# Automation and Triage Processes
This document provides a detailed overview of the automated processes we use to manage and triage issues and pull requests. Our goal is to provide prompt feedback and ensure that contributions are reviewed and integrated efficiently. Understanding this automation will help you as a contributor know what to expect and how to best interact with our repository bots.
## Guiding Principle: Issues and Pull Requests
First and foremost, almost every Pull Request (PR) should be linked to a corresponding Issue. The issue describes the "what" and the "why" (the bug or feature), while the PR is the "how" (the implementation). This separation helps us track work, prioritize features, and maintain clear historical context. Our automation is built around this principle.
---
## Detailed Automation Workflows
Here is a breakdown of the specific automation workflows that run in our repository.
### 1. When you open an Issue: `Automated Issue Triage`
This is the first bot you will interact with when you create an issue. Its job is to perform an initial analysis and apply the correct labels.
- **Workflow File**: `.github/workflows/gemini-automated-issue-triage.yml`
- **When it runs**: Immediately after an issue is created or reopened.
- **What it does**:
- It uses a Gemini model to analyze the issue's title and body against a detailed set of guidelines.
- **Applies one `area/*` label**: Categorizes the issue into a functional area of the project (e.g., `area/ux`, `area/models`, `area/platform`).
- **Applies one `kind/*` label**: Identifies the type of issue (e.g., `kind/bug`, `kind/enhancement`, `kind/question`).
- **Applies one `priority/*` label**: Assigns a priority from P0 (critical) to P3 (low) based on the described impact.
- **May apply `status/need-information`**: If the issue lacks critical details (like logs or reproduction steps), it will be flagged for more information.
- **May apply `status/need-retesting`**: If the issue references a CLI version that is more than six versions old, it will be flagged for retesting on a current version.
- **What you should do**:
- Fill out the issue template as completely as possible. The more detail you provide, the more accurate the triage will be.
- If the `status/need-information` label is added, please provide the requested details in a comment.
### 2. When you open a Pull Request: `Continuous Integration (CI)`
This workflow ensures that all changes meet our quality standards before they can be merged.
- **Workflow File**: `.github/workflows/ci.yml`
- **When it runs**: On every push to a pull request.
- **What it does**:
- **Lint**: Checks that your code adheres to our project's formatting and style rules.
- **Test**: Runs our full suite of automated tests across macOS, Windows, and Linux, and on multiple Node.js versions. This is the most time-consuming part of the CI process.
- **Post Coverage Comment**: After all tests have successfully passed, a bot will post a comment on your PR. This comment provides a summary of how well your changes are covered by tests.
- **What you should do**:
- Ensure all CI checks pass. A green checkmark ✅ will appear next to your commit when everything is successful.
- If a check fails (a red "X" ❌), click the "Details" link next to the failed check to view the logs, identify the problem, and push a fix.
### 3. Ongoing Triage for Pull Requests: `PR Auditing and Label Sync`
This workflow runs periodically to ensure all open PRs are correctly linked to issues and have consistent labels.
- **Workflow File**: `.github/workflows/gemini-scheduled-pr-triage.yml`
- **When it runs**: Every 15 minutes on all open pull requests.
- **What it does**:
- **Checks for a linked issue**: The bot scans your PR description for a keyword that links it to an issue (e.g., `Fixes #123`, `Closes #456`).
- **Adds `status/need-issue`**: If no linked issue is found, the bot will add the `status/need-issue` label to your PR. This is a clear signal that an issue needs to be created and linked.
- **Synchronizes labels**: If an issue _is_ linked, the bot ensures the PR's labels perfectly match the issue's labels. It will add any missing labels and remove any that don't belong, and it will remove the `status/need-issue` label if it was present.
- **What you should do**:
- **Always link your PR to an issue.** This is the most important step. Add a line like `Resolves #<issue-number>` to your PR description.
- This will ensure your PR is correctly categorized and moves through the review process smoothly.
### 4. Ongoing Triage for Issues: `Scheduled Issue Triage`
This is a fallback workflow to ensure that no issue gets missed by the triage process.
- **Workflow File**: `.github/workflows/gemini-scheduled-issue-triage.yml`
- **When it runs**: Every hour on all open issues.
- **What it does**:
- It actively seeks out issues that either have no labels at all or still have the `status/need-triage` label.
- It then triggers the same powerful Gemini-based analysis as the initial triage bot to apply the correct labels.
- **What you should do**:
- You typically don't need to do anything. This workflow is a safety net to ensure every issue is eventually categorized, even if the initial triage fails.
### 5. Release Automation
This workflow handles the process of packaging and publishing new versions of the Gemini CLI.
- **Workflow File**: `.github/workflows/release.yml`
- **When it runs**: On a daily schedule for "nightly" releases, and manually for official patch/minor releases.
- **What it does**:
- Automatically builds the project, bumps the version numbers, and publishes the packages to npm.
- Creates a corresponding release on GitHub with generated release notes.
- **What you should do**:
- As a contributor, you don't need to do anything for this process. You can be confident that once your PR is merged into the `main` branch, your changes will be included in the very next nightly release.
We hope this detailed overview is helpful. If you have any questions about our automation or processes, please don't hesitate to ask!
+62
View File
@@ -0,0 +1,62 @@
# Gemini CLI Keyboard Shortcuts
This document lists the available keyboard shortcuts in the Gemini CLI.
## General
| Shortcut | Description |
| -------- | --------------------------------------------------------------------------------------------------------------------- |
| `Esc` | Close dialogs and suggestions. |
| `Ctrl+C` | Exit the application. Press twice to confirm. |
| `Ctrl+D` | Exit the application if the input is empty. Press twice to confirm. |
| `Ctrl+L` | Clear the screen. |
| `Ctrl+O` | Toggle the display of the debug console. |
| `Ctrl+S` | Allows long responses to print fully, disabling truncation. Use your terminal's scrollback to view the entire output. |
| `Ctrl+T` | Toggle the display of tool descriptions. |
| `Ctrl+Y` | Toggle auto-approval (YOLO mode) for all tool calls. |
## Input Prompt
| Shortcut | Description |
| -------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `!` | Toggle shell mode when the input is empty. |
| `\` (at end of line) + `Enter` | Insert a newline. |
| `Down Arrow` | Navigate down through the input history. |
| `Enter` | Submit the current prompt. |
| `Meta+Delete` / `Ctrl+Delete` | Delete the word to the right of the cursor. |
| `Tab` | Autocomplete the current suggestion if one exists. |
| `Up Arrow` | Navigate up through the input history. |
| `Ctrl+A` / `Home` | Move the cursor to the beginning of the line. |
| `Ctrl+B` / `Left Arrow` | Move the cursor one character to the left. |
| `Ctrl+C` | Clear the input prompt |
| `Ctrl+D` / `Delete` | Delete the character to the right of the cursor. |
| `Ctrl+E` / `End` | Move the cursor to the end of the line. |
| `Ctrl+F` / `Right Arrow` | Move the cursor one character to the right. |
| `Ctrl+H` / `Backspace` | Delete the character to the left of the cursor. |
| `Ctrl+K` | Delete from the cursor to the end of the line. |
| `Ctrl+Left Arrow` / `Meta+Left Arrow` / `Meta+B` | Move the cursor one word to the left. |
| `Ctrl+N` | Navigate down through the input history. |
| `Ctrl+P` | Navigate up through the input history. |
| `Ctrl+Right Arrow` / `Meta+Right Arrow` / `Meta+F` | Move the cursor one word to the right. |
| `Ctrl+U` | Delete from the cursor to the beginning of the line. |
| `Ctrl+V` | Paste clipboard content. If the clipboard contains an image, it will be saved and a reference to it will be inserted in the prompt. |
| `Ctrl+W` / `Meta+Backspace` / `Ctrl+Backspace` | Delete the word to the left of the cursor. |
| `Ctrl+X` / `Meta+Enter` | Open the current input in an external editor. |
## Suggestions
| Shortcut | Description |
| --------------- | -------------------------------------- |
| `Down Arrow` | Navigate down through the suggestions. |
| `Tab` / `Enter` | Accept the selected suggestion. |
| `Up Arrow` | Navigate up through the suggestions. |
## Radio Button Select
| Shortcut | Description |
| ------------------ | ------------------------------------------------------------------------------------------------------------- |
| `Down Arrow` / `j` | Move selection down. |
| `Enter` | Confirm selection. |
| `Up Arrow` / `k` | Move selection up. |
| `1-9` | Select an item by its number. |
| (multi-digit) | For items with numbers greater than 9, press the digits in quick succession to select the corresponding item. |
+280
View File
@@ -0,0 +1,280 @@
# Package Overview
This monorepo contains two main packages: `@google/gemini-cli` and `@google/gemini-cli-core`.
## `@google/gemini-cli`
This is the main package for the Gemini CLI. It is responsible for the user interface, command parsing, and all other user-facing functionality.
When this package is published, it is bundled into a single executable file. This bundle includes all of the package's dependencies, including `@google/gemini-cli-core`. This means that whether a user installs the package with `npm install -g @google/gemini-cli` or runs it directly with `npx @google/gemini-cli`, they are using this single, self-contained executable.
## `@google/gemini-cli-core`
This package contains the core logic for interacting with the Gemini API. It is responsible for making API requests, handling authentication, and managing the local cache.
This package is not bundled. When it is published, it is published as a standard Node.js package with its own dependencies. This allows it to be used as a standalone package in other projects, if needed. All transpiled js code in the `dist` folder is included in the package.
# Release Process
This project follows a structured release process to ensure that all packages are versioned and published correctly. The process is designed to be as automated as possible.
## How To Release
Releases are managed through the [release.yml](https://github.com/google-gemini/gemini-cli/actions/workflows/release.yml) GitHub Actions workflow. To perform a manual release for a patch or hotfix:
1. Navigate to the **Actions** tab of the repository.
2. Select the **Release** workflow from the list.
3. Click the **Run workflow** dropdown button.
4. Fill in the required inputs:
- **Version**: The exact version to release (e.g., `v0.2.1`).
- **Ref**: The branch or commit SHA to release from (defaults to `main`).
- **Dry Run**: Leave as `true` to test the workflow without publishing, or set to `false` to perform a live release.
5. Click **Run workflow**.
## Nightly Releases
In addition to manual releases, this project has an automated nightly release process to provide the latest "bleeding edge" version for testing and development.
### Process
Every night at midnight UTC, the [Release workflow](https://github.com/google-gemini/gemini-cli/actions/workflows/release.yml) runs automatically on a schedule. It performs the following steps:
1. Checks out the latest code from the `main` branch.
2. Installs all dependencies.
3. Runs the full suite of `preflight` checks and integration tests.
4. If all tests succeed, it calculates the next nightly version number (e.g., `v0.2.1-nightly.20230101`).
5. It then builds and publishes the packages to npm with the `nightly` dist-tag.
6. Finally, it creates a GitHub Release for the nightly version.
### Failure Handling
If any step in the nightly workflow fails, it will automatically create a new issue in the repository with the labels `bug` and `nightly-failure`. The issue will contain a link to the failed workflow run for easy debugging.
### How to Use the Nightly Build
To install the latest nightly build, use the `@nightly` tag:
```bash
npm install -g @google/gemini-cli@nightly
```
We also run a Google cloud build called [release-docker.yml](../.gcp/release-docker.yaml). Which publishes the sandbox docker to match your release. This will also be moved to GH and combined with the main release file once service account permissions are sorted out.
### After the Release
After the workflow has successfully completed, you can monitor its progress in the [GitHub Actions tab](https://github.com/google-gemini/gemini-cli/actions/workflows/release.yml). Once complete, you should:
1. Go to the [pull requests page](https://github.com/google-gemini/gemini-cli/pulls) of the repository.
2. Create a new pull request from the `release/vX.Y.Z` branch to `main`.
3. Review the pull request (it should only contain version updates in `package.json` files) and merge it. This keeps the version in `main` up-to-date.
## Release Validation
After pushing a new release smoke testing should be performed to ensure that the packages are working as expected. This can be done by installing the packages locally and running a set of tests to ensure that they are functioning correctly.
- `npx -y @google/gemini-cli@latest --version` to validate the push worked as expected if you were not doing a rc or dev tag
- `npx -y @google/gemini-cli@<release tag> --version` to validate the tag pushed appropriately
- _This is destructive locally_ `npm uninstall @google/gemini-cli && npm uninstall -g @google/gemini-cli && npm cache clean --force && npm install @google/gemini-cli@<version>`
- Smoke testing a basic run through of exercising a few llm commands and tools is recommended to ensure that the packages are working as expected. We'll codify this more in the future.
## When to merge the version change, or not?
The above pattern for creating patch or hotfix releases from current or older commits leaves the repository in the following state:
1. The Tag (`vX.Y.Z-patch.1`): This tag correctly points to the original commit on main
that contains the stable code you intended to release. This is crucial. Anyone checking
out this tag gets the exact code that was published.
2. The Branch (`release-vX.Y.Z-patch.1`): This branch contains one new commit on top of the
tagged commit. That new commit only contains the version number change in package.json
(and other related files like package-lock.json).
This separation is good. It keeps your main branch history clean of release-specific
version bumps until you decide to merge them.
This is the critical decision, and it depends entirely on the nature of the release.
### Merge Back for Stable Patches and Hotfixes
You almost always want to merge the `release-<tag>` branch back into `main` for any
stable patch or hotfix release.
- Why? The primary reason is to update the version in main's package.json. If you release
v1.2.1 from an older commit but never merge the version bump back, your main branch's
package.json will still say "version": "1.2.0". The next developer who starts work for
the next feature release (v1.3.0) will be branching from a codebase that has an
incorrect, older version number. This leads to confusion and requires manual version
bumping later.
- The Process: After the release-v1.2.1 branch is created and the package is successfully
published, you should open a pull request to merge release-v1.2.1 into main. This PR
will contain just one commit: "chore: bump version to v1.2.1". It's a clean, simple
integration that keeps your main branch in sync with the latest released version.
### Do NOT Merge Back for Pre-Releases (RC, Beta, Dev)
You typically do not merge release branches for pre-releases back into `main`.
- Why? Pre-release versions (e.g., v1.3.0-rc.1, v1.3.0-rc.2) are, by definition, not
stable and are temporary. You don't want to pollute your main branch's history with a
series of version bumps for release candidates. The package.json in main should reflect
the latest stable release version, not an RC.
- The Process: The release-v1.3.0-rc.1 branch is created, the npm publish --tag rc happens,
and then... the branch has served its purpose. You can simply delete it. The code for
the RC is already on main (or a feature branch), so no functional code is lost. The
release branch was just a temporary vehicle for the version number.
## Local Testing and Validation: Changes to the Packaging and Publishing Process
If you need to test the release process without actually publishing to NPM or creating a public GitHub release, you can trigger the workflow manually from the GitHub UI.
1. Go to the [Actions tab](https://github.com/google-gemini/gemini-cli/actions/workflows/release.yml) of the repository.
2. Click on the "Run workflow" dropdown.
3. Leave the `dry_run` option checked (`true`).
4. Click the "Run workflow" button.
This will run the entire release process but will skip the `npm publish` and `gh release create` steps. You can inspect the workflow logs to ensure everything is working as expected.
It is crucial to test any changes to the packaging and publishing process locally before committing them. This ensures that the packages will be published correctly and that they will work as expected when installed by a user.
To validate your changes, you can perform a dry run of the publishing process. This will simulate the publishing process without actually publishing the packages to the npm registry.
```bash
npm_package_version=9.9.9 SANDBOX_IMAGE_REGISTRY="registry" SANDBOX_IMAGE_NAME="thename" npm run publish:npm --dry-run
```
This command will do the following:
1. Build all the packages.
2. Run all the prepublish scripts.
3. Create the package tarballs that would be published to npm.
4. Print a summary of the packages that would be published.
You can then inspect the generated tarballs to ensure that they contain the correct files and that the `package.json` files have been updated correctly. The tarballs will be created in the root of each package's directory (e.g., `packages/cli/google-gemini-cli-0.1.6.tgz`).
By performing a dry run, you can be confident that your changes to the packaging process are correct and that the packages will be published successfully.
## Release Deep Dive
The main goal of the release process is to take the source code from the packages/ directory, build it, and assemble a
clean, self-contained package in a temporary `bundle` directory at the root of the project. This `bundle` directory is what
actually gets published to NPM.
Here are the key stages:
Stage 1: Pre-Release Sanity Checks and Versioning
- What happens: Before any files are moved, the process ensures the project is in a good state. This involves running tests,
linting, and type-checking (npm run preflight). The version number in the root package.json and packages/cli/package.json
is updated to the new release version.
- Why: This guarantees that only high-quality, working code is released. Versioning is the first step to signify a new
release.
Stage 2: Building the Source Code
- What happens: The TypeScript source code in packages/core/src and packages/cli/src is compiled into JavaScript.
- File movement:
- packages/core/src/\*_/_.ts -> compiled to -> packages/core/dist/
- packages/cli/src/\*_/_.ts -> compiled to -> packages/cli/dist/
- Why: The TypeScript code written during development needs to be converted into plain JavaScript that can be run by
Node.js. The core package is built first as the cli package depends on it.
Stage 3: Assembling the Final Publishable Package
This is the most critical stage where files are moved and transformed into their final state for publishing. A temporary
`bundle` folder is created at the project root to house the final package contents.
1. The `package.json` is Transformed:
- What happens: The package.json from packages/cli/ is read, modified, and written into the root `bundle`/ directory.
- File movement: packages/cli/package.json -> (in-memory transformation) -> `bundle`/package.json
- Why: The final package.json must be different from the one used in development. Key changes include:
- Removing devDependencies.
- Removing workspace-specific "dependencies": { "@gemini-cli/core": "workspace:\*" } and ensuring the core code is
bundled directly into the final JavaScript file.
- Ensuring the bin, main, and files fields point to the correct locations within the final package structure.
2. The JavaScript Bundle is Created:
- What happens: The built JavaScript from both packages/core/dist and packages/cli/dist are bundled into a single,
executable JavaScript file.
- File movement: packages/cli/dist/index.js + packages/core/dist/index.js -> (bundled by esbuild) -> `bundle`/gemini.js (or a
similar name).
- Why: This creates a single, optimized file that contains all the necessary application code. It simplifies the package
by removing the need for the core package to be a separate dependency on NPM, as its code is now included directly.
3. Static and Supporting Files are Copied:
- What happens: Essential files that are not part of the source code but are required for the package to work correctly
or be well-described are copied into the `bundle` directory.
- File movement:
- README.md -> `bundle`/README.md
- LICENSE -> `bundle`/LICENSE
- packages/cli/src/utils/\*.sb (sandbox profiles) -> `bundle`/
- Why:
- The README.md and LICENSE are standard files that should be included in any NPM package.
- The sandbox profiles (.sb files) are critical runtime assets required for the CLI's sandboxing feature to
function. They must be located next to the final executable.
Stage 4: Publishing to NPM
- What happens: The npm publish command is run from inside the root `bundle` directory.
- Why: By running npm publish from within the `bundle` directory, only the files we carefully assembled in Stage 3 are uploaded
to the NPM registry. This prevents any source code, test files, or development configurations from being accidentally
published, resulting in a clean and minimal package for users.
Summary of File Flow
```mermaid
graph TD
subgraph "Source Files"
A["packages/core/src/*.ts<br/>packages/cli/src/*.ts"]
B["packages/cli/package.json"]
C["README.md<br/>LICENSE<br/>packages/cli/src/utils/*.sb"]
end
subgraph "Process"
D(Build)
E(Transform)
F(Assemble)
G(Publish)
end
subgraph "Artifacts"
H["Bundled JS"]
I["Final package.json"]
J["bundle/"]
end
subgraph "Destination"
K["NPM Registry"]
end
A --> D --> H
B --> E --> I
C --> F
H --> F
I --> F
F --> J
J --> G --> K
```
This process ensures that the final published artifact is a purpose-built, clean, and efficient representation of the
project, rather than a direct copy of the development workspace.
## NPM Workspaces
This project uses [NPM Workspaces](https://docs.npmjs.com/cli/v10/using-npm/workspaces) to manage the packages within this monorepo. This simplifies development by allowing us to manage dependencies and run scripts across multiple packages from the root of the project.
### How it Works
The root `package.json` file defines the workspaces for this project:
```json
{
"workspaces": ["packages/*"]
}
```
This tells NPM that any folder inside the `packages` directory is a separate package that should be managed as part of the workspace.
### Benefits of Workspaces
- **Simplified Dependency Management**: Running `npm install` from the root of the project will install all dependencies for all packages in the workspace and link them together. This means you don't need to run `npm install` in each package's directory.
- **Automatic Linking**: Packages within the workspace can depend on each other. When you run `npm install`, NPM will automatically create symlinks between the packages. This means that when you make changes to one package, the changes are immediately available to other packages that depend on it.
- **Simplified Script Execution**: You can run scripts in any package from the root of the project using the `--workspace` flag. For example, to run the `build` script in the `cli` package, you can run `npm run build --workspace @google/gemini-cli`.
+70
View File
@@ -0,0 +1,70 @@
# Gemini CLI: Quotas and Pricing
Your Gemini CLI quotas and pricing depend on the type of account you use to authenticate with Google. Additionally, both quotas and pricing may be calculated differently based on the model version, requests, and tokens used. A summary of model usage is available through the `/stats` command and presented on exit at the end of a session. See [privacy and terms](./tos-privacy.md) for details on Privacy policy and Terms of Service. Note: published prices are list price; additional negotiated commercial discounting may apply.
This article outlines the specific quotas and pricing applicable to the Gemini CLI when using different authentication methods.
## 1. Log in with Google (Gemini Code Assist Free Tier)
For users who authenticate by using their Google account to access Gemini Code Assist for individuals:
- **Quota:**
- 60 requests per minute
- 1000 requests per day
- Token usage is not applicable
- **Cost:** Free
- **Details:** [Gemini Code Assist Quotas](https://developers.google.com/gemini-code-assist/resources/quotas#quotas-for-agent-mode-gemini-cli)
- **Notes:** A specific quota for different models is not specified; model fallback may occur to preserve shared experience quality.
## 2. Gemini API Key (Unpaid)
If you are using a Gemini API key for the free tier:
- **Quota:**
- Flash model only
- 10 requests per minute
- 250 requests per day
- **Cost:** Free
- **Details:** [Gemini API Rate Limits](https://ai.google.dev/gemini-api/docs/rate-limits)
## 3. Gemini API Key (Paid)
If you are using a Gemini API key with a paid plan:
- **Quota:** Varies by pricing tier.
- **Cost:** Varies by pricing tier and model/token usage.
- **Details:** [Gemini API Rate Limits](https://ai.google.dev/gemini-api/docs/rate-limits), [Gemini API Pricing](https://ai.google.dev/gemini-api/docs/pricing)
## 4. Login with Google (for Workspace or Licensed Code Assist users)
For users of Standard or Enterprise editions of Gemini Code Assist, quotas and pricing are based on a fixed price subscription with assigned license seats:
- **Standard Tier:**
- **Quota:** 120 requests per minute, 1500 per day
- **Enterprise Tier:**
- **Quota:** 120 requests per minute, 2000 per day
- **Cost:** Fixed price included with your Gemini for Google Workspace or Gemini Code Assist subscription.
- **Details:** [Gemini Code Assist Quotas](https://developers.google.com/gemini-code-assist/resources/quotas#quotas-for-agent-mode-gemini-cli), [Gemini Code Assist Pricing](https://cloud.google.com/products/gemini/pricing)
- **Notes:**
- Specific quota for different models is not specified; model fallback may occur to preserve shared experience quality.
- Members of the Google Developer Program may have Gemini Code Assist licenses through their membership.
## 5. Vertex AI (Express Mode)
If you are using Vertex AI in Express Mode:
- **Quota:** Quotas are variable and specific to your account. See the source for more details.
- **Cost:** After your Express Mode usage is consumed and you enable billing for your project, cost is based on standard [Vertex AI Pricing](https://cloud.google.com/vertex-ai/pricing).
- **Details:** [Vertex AI Express Mode Quotas](https://cloud.google.com/vertex-ai/generative-ai/docs/start/express-mode/overview#quotas)
## 6. Vertex AI (Regular Mode)
If you are using the standard Vertex AI service:
- **Quota:** Governed by a dynamic shared quota system or pre-purchased provisioned throughput.
- **Cost:** Based on model and token usage. See [Vertex AI Pricing](https://cloud.google.com/vertex-ai/pricing).
- **Details:** [Vertex AI Dynamic Shared Quota](https://cloud.google.com/vertex-ai/generative-ai/docs/resources/dynamic-shared-quota)
## 7. Google One and Ultra plans, Gemini for Workspace plans
These plans currently apply only to the use of Gemini web-based products provided by Google-based experiences (for example, the Gemini web app or the Flow video editor). These plans do not apply to the API usage which powers the Gemini CLI. Supporting these plans is under active consideration for future support.
+155
View File
@@ -0,0 +1,155 @@
# Sandboxing in the Gemini CLI
This document provides a guide to sandboxing in the Gemini CLI, including prerequisites, quickstart, and configuration.
## Prerequisites
Before using sandboxing, you need to install and set up the Gemini CLI:
```bash
npm install -g @google/gemini-cli
```
To verify the installation
```bash
gemini --version
```
## Overview of sandboxing
Sandboxing isolates potentially dangerous operations (such as shell commands or file modifications) from your host system, providing a security barrier between AI operations and your environment.
The benefits of sandboxing include:
- **Security**: Prevent accidental system damage or data loss.
- **Isolation**: Limit file system access to project directory.
- **Consistency**: Ensure reproducible environments across different systems.
- **Safety**: Reduce risk when working with untrusted code or experimental commands.
## Sandboxing methods
Your ideal method of sandboxing may differ depending on your platform and your preferred container solution.
### 1. macOS Seatbelt (macOS only)
Lightweight, built-in sandboxing using `sandbox-exec`.
**Default profile**: `permissive-open` - restricts writes outside project directory but allows most other operations.
### 2. Container-based (Docker/Podman)
Cross-platform sandboxing with complete process isolation.
**Note**: Requires building the sandbox image locally or using a published image from your organization's registry.
## Quickstart
```bash
# Enable sandboxing with command flag
gemini -s -p "analyze the code structure"
# Use environment variable
export GEMINI_SANDBOX=true
gemini -p "run the test suite"
# Configure in settings.json
{
"sandbox": "docker"
}
```
## Configuration
### Enable sandboxing (in order of precedence)
1. **Command flag**: `-s` or `--sandbox`
2. **Environment variable**: `GEMINI_SANDBOX=true|docker|podman|sandbox-exec`
3. **Settings file**: `"sandbox": true` in `settings.json`
### macOS Seatbelt profiles
Built-in profiles (set via `SEATBELT_PROFILE` env var):
- `permissive-open` (default): Write restrictions, network allowed
- `permissive-closed`: Write restrictions, no network
- `permissive-proxied`: Write restrictions, network via proxy
- `restrictive-open`: Strict restrictions, network allowed
- `restrictive-closed`: Maximum restrictions
### Custom Sandbox Flags
For container-based sandboxing, you can inject custom flags into the `docker` or `podman` command using the `SANDBOX_FLAGS` environment variable. This is useful for advanced configurations, such as disabling security features for specific use cases.
**Example (Podman)**:
To disable SELinux labeling for volume mounts, you can set the following:
```bash
export SANDBOX_FLAGS="--security-opt label=disable"
```
Multiple flags can be provided as a space-separated string:
```bash
export SANDBOX_FLAGS="--flag1 --flag2=value"
```
## Linux UID/GID handling
The sandbox automatically handles user permissions on Linux. Override these permissions with:
```bash
export SANDBOX_SET_UID_GID=true # Force host UID/GID
export SANDBOX_SET_UID_GID=false # Disable UID/GID mapping
```
## Troubleshooting
### Common issues
**"Operation not permitted"**
- Operation requires access outside sandbox.
- Try more permissive profile or add mount points.
**Missing commands**
- Add to custom Dockerfile.
- Install via `sandbox.bashrc`.
**Network issues**
- Check sandbox profile allows network.
- Verify proxy configuration.
### Debug mode
```bash
DEBUG=1 gemini -s -p "debug command"
```
**Note:** If you have `DEBUG=true` in a project's `.env` file, it won't affect gemini-cli due to automatic exclusion. Use `.gemini/.env` files for gemini-cli specific debug settings.
### Inspect sandbox
```bash
# Check environment
gemini -s -p "run shell command: env | grep SANDBOX"
# List mounts
gemini -s -p "run shell command: mount | grep workspace"
```
## Security notes
- Sandboxing reduces but doesn't eliminate all risks.
- Use the most restrictive profile that allows your work.
- Container overhead is minimal after first build.
- GUI applications may not work in sandboxes.
## Related documentation
- [Configuration](./cli/configuration.md): Full configuration options.
- [Commands](./cli/commands.md): Available commands.
- [Troubleshooting](./troubleshooting.md): General troubleshooting.
+254
View File
@@ -0,0 +1,254 @@
# Gemini CLI Observability Guide
Telemetry provides data about Gemini CLI's performance, health, and usage. By enabling it, you can monitor operations, debug issues, and optimize tool usage through traces, metrics, and structured logs.
Gemini CLI's telemetry system is built on the **[OpenTelemetry] (OTEL)** standard, allowing you to send data to any compatible backend.
[OpenTelemetry]: https://opentelemetry.io/
## Enabling telemetry
You can enable telemetry in multiple ways. Configuration is primarily managed via the [`.gemini/settings.json` file](./cli/configuration.md) and environment variables, but CLI flags can override these settings for a specific session.
### Order of precedence
The following lists the precedence for applying telemetry settings, with items listed higher having greater precedence:
1. **CLI flags (for `gemini` command):**
- `--telemetry` / `--no-telemetry`: Overrides `telemetry.enabled`.
- `--telemetry-target <local|gcp>`: Overrides `telemetry.target`.
- `--telemetry-otlp-endpoint <URL>`: Overrides `telemetry.otlpEndpoint`.
- `--telemetry-log-prompts` / `--no-telemetry-log-prompts`: Overrides `telemetry.logPrompts`.
- `--telemetry-outfile <path>`: Redirects telemetry output to a file. See [Exporting to a file](#exporting-to-a-file).
1. **Environment variables:**
- `OTEL_EXPORTER_OTLP_ENDPOINT`: Overrides `telemetry.otlpEndpoint`.
1. **Workspace settings file (`.gemini/settings.json`):** Values from the `telemetry` object in this project-specific file.
1. **User settings file (`~/.gemini/settings.json`):** Values from the `telemetry` object in this global user file.
1. **Defaults:** applied if not set by any of the above.
- `telemetry.enabled`: `false`
- `telemetry.target`: `local`
- `telemetry.otlpEndpoint`: `http://localhost:4317`
- `telemetry.logPrompts`: `true`
**For the `npm run telemetry -- --target=<gcp|local>` script:**
The `--target` argument to this script _only_ overrides the `telemetry.target` for the duration and purpose of that script (i.e., choosing which collector to start). It does not permanently change your `settings.json`. The script will first look at `settings.json` for a `telemetry.target` to use as its default.
### Example settings
The following code can be added to your workspace (`.gemini/settings.json`) or user (`~/.gemini/settings.json`) settings to enable telemetry and send the output to Google Cloud:
```json
{
"telemetry": {
"enabled": true,
"target": "gcp"
},
"sandbox": false
}
```
### Exporting to a file
You can export all telemetry data to a file for local inspection.
To enable file export, use the `--telemetry-outfile` flag with a path to your desired output file. This must be run using `--telemetry-target=local`.
```bash
gemini --telemetry --telemetry-target=local --telemetry-outfile=/path/to/telemetry.log "your prompt"
```
## Running an OTEL Collector
An OTEL Collector is a service that receives, processes, and exports telemetry data.
The CLI sends data using the OTLP/gRPC protocol.
Learn more about OTEL exporter standard configuration in [documentation][otel-config-docs].
[otel-config-docs]: https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/
### Local
Use the `npm run telemetry -- --target=local` command to automate the process of setting up a local telemetry pipeline, including configuring the necessary settings in your `.gemini/settings.json` file. The underlying script installs `otelcol-contrib` (the OpenTelemetry Collector) and `jaeger` (The Jaeger UI for viewing traces). To use it:
1. **Run the command**:
Execute the command from the root of the repository:
```bash
npm run telemetry -- --target=local
```
The script will:
- Download Jaeger and OTEL if needed.
- Start a local Jaeger instance.
- Start an OTEL collector configured to receive data from Gemini CLI.
- Automatically enable telemetry in your workspace settings.
- On exit, disable telemetry.
1. **View traces**:
Open your web browser and navigate to **http://localhost:16686** to access the Jaeger UI. Here you can inspect detailed traces of Gemini CLI operations.
1. **Inspect logs and metrics**:
The script redirects the OTEL collector output (which includes logs and metrics) to `~/.gemini/tmp/<projectHash>/otel/collector.log`. The script will provide links to view and a command to tail your telemetry data (traces, metrics, logs) locally.
1. **Stop the services**:
Press `Ctrl+C` in the terminal where the script is running to stop the OTEL Collector and Jaeger services.
### Google Cloud
Use the `npm run telemetry -- --target=gcp` command to automate setting up a local OpenTelemetry collector that forwards data to your Google Cloud project, including configuring the necessary settings in your `.gemini/settings.json` file. The underlying script installs `otelcol-contrib`. To use it:
1. **Prerequisites**:
- Have a Google Cloud project ID.
- Export the `GOOGLE_CLOUD_PROJECT` environment variable to make it available to the OTEL collector.
```bash
export OTLP_GOOGLE_CLOUD_PROJECT="your-project-id"
```
- Authenticate with Google Cloud (e.g., run `gcloud auth application-default login` or ensure `GOOGLE_APPLICATION_CREDENTIALS` is set).
- Ensure your Google Cloud account/service account has the necessary IAM roles: "Cloud Trace Agent", "Monitoring Metric Writer", and "Logs Writer".
1. **Run the command**:
Execute the command from the root of the repository:
```bash
npm run telemetry -- --target=gcp
```
The script will:
- Download the `otelcol-contrib` binary if needed.
- Start an OTEL collector configured to receive data from Gemini CLI and export it to your specified Google Cloud project.
- Automatically enable telemetry and disable sandbox mode in your workspace settings (`.gemini/settings.json`).
- Provide direct links to view traces, metrics, and logs in your Google Cloud Console.
- On exit (Ctrl+C), it will attempt to restore your original telemetry and sandbox settings.
1. **Run Gemini CLI:**
In a separate terminal, run your Gemini CLI commands. This generates telemetry data that the collector captures.
1. **View telemetry in Google Cloud**:
Use the links provided by the script to navigate to the Google Cloud Console and view your traces, metrics, and logs.
1. **Inspect local collector logs**:
The script redirects the local OTEL collector output to `~/.gemini/tmp/<projectHash>/otel/collector-gcp.log`. The script provides links to view and command to tail your collector logs locally.
1. **Stop the service**:
Press `Ctrl+C` in the terminal where the script is running to stop the OTEL Collector.
## Logs and metric reference
The following section describes the structure of logs and metrics generated for Gemini CLI.
- A `sessionId` is included as a common attribute on all logs and metrics.
### Logs
Logs are timestamped records of specific events. The following events are logged for Gemini CLI:
- `gemini_cli.config`: This event occurs once at startup with the CLI's configuration.
- **Attributes**:
- `model` (string)
- `embedding_model` (string)
- `sandbox_enabled` (boolean)
- `core_tools_enabled` (string)
- `approval_mode` (string)
- `api_key_enabled` (boolean)
- `vertex_ai_enabled` (boolean)
- `code_assist_enabled` (boolean)
- `log_prompts_enabled` (boolean)
- `file_filtering_respect_git_ignore` (boolean)
- `debug_mode` (boolean)
- `mcp_servers` (string)
- `gemini_cli.user_prompt`: This event occurs when a user submits a prompt.
- **Attributes**:
- `prompt_length`
- `prompt` (this attribute is excluded if `log_prompts_enabled` is configured to be `false`)
- `auth_type`
- `gemini_cli.tool_call`: This event occurs for each function call.
- **Attributes**:
- `function_name`
- `function_args`
- `duration_ms`
- `success` (boolean)
- `decision` (string: "accept", "reject", or "modify", if applicable)
- `error` (if applicable)
- `error_type` (if applicable)
- `gemini_cli.api_request`: This event occurs when making a request to Gemini API.
- **Attributes**:
- `model`
- `request_text` (if applicable)
- `gemini_cli.api_error`: This event occurs if the API request fails.
- **Attributes**:
- `model`
- `error`
- `error_type`
- `status_code`
- `duration_ms`
- `auth_type`
- `gemini_cli.api_response`: This event occurs upon receiving a response from Gemini API.
- **Attributes**:
- `model`
- `status_code`
- `duration_ms`
- `error` (optional)
- `input_token_count`
- `output_token_count`
- `cached_content_token_count`
- `thoughts_token_count`
- `tool_token_count`
- `response_text` (if applicable)
- `auth_type`
- `gemini_cli.flash_fallback`: This event occurs when Gemini CLI switches to flash as fallback.
- **Attributes**:
- `auth_type`
- `gemini_cli.slash_command`: This event occurs when a user executes a slash command.
- **Attributes**:
- `command` (string)
- `subcommand` (string, if applicable)
### Metrics
Metrics are numerical measurements of behavior over time. The following metrics are collected for Gemini CLI:
- `gemini_cli.session.count` (Counter, Int): Incremented once per CLI startup.
- `gemini_cli.tool.call.count` (Counter, Int): Counts tool calls.
- **Attributes**:
- `function_name`
- `success` (boolean)
- `decision` (string: "accept", "reject", or "modify", if applicable)
- `gemini_cli.tool.call.latency` (Histogram, ms): Measures tool call latency.
- **Attributes**:
- `function_name`
- `decision` (string: "accept", "reject", or "modify", if applicable)
- `gemini_cli.api.request.count` (Counter, Int): Counts all API requests.
- **Attributes**:
- `model`
- `status_code`
- `error_type` (if applicable)
- `gemini_cli.api.request.latency` (Histogram, ms): Measures API request latency.
- **Attributes**:
- `model`
- `gemini_cli.token.usage` (Counter, Int): Counts the number of tokens used.
- **Attributes**:
- `model`
- `type` (string: "input", "output", "thought", "cache", or "tool")
- `gemini_cli.file.operation.count` (Counter, Int): Counts file operations.
- **Attributes**:
- `operation` (string: "create", "read", "update"): The type of file operation.
- `lines` (Int, if applicable): Number of lines in the file.
- `mimetype` (string, if applicable): Mimetype of the file.
- `extension` (string, if applicable): File extension of the file.
+46 -36
View File
@@ -1,19 +1,20 @@
# Gemini CLI: File System Tools
# Gemini CLI file system tools
The Gemini CLI provides a comprehensive suite of tools for interacting with the local file system. These tools allow the Gemini model to read from, write to, list, search, and modify files and directories, all under your control and typically with confirmation for sensitive operations.
All file system tools operate within a `rootDirectory` (usually the current working directory where you launched the CLI) for security, preventing unintended access to other parts of your system. Paths provided to these tools are generally expected to be absolute or are resolved relative to this root directory.
**Note:** All file system tools operate within a `rootDirectory` (usually the current working directory where you launched the CLI) for security. Paths that you provide to these tools are generally expected to be absolute or are resolved relative to this root directory.
## 1. `list_directory` (ReadFolder)
- **Tool Name:** `list_directory`
- **Display Name:** ReadFolder
`list_directory` lists the names of files and subdirectories directly within a specified directory path. It can optionally ignore entries matching provided glob patterns.
- **Tool name:** `list_directory`
- **Display name:** ReadFolder
- **File:** `ls.ts`
- **Description:** Lists the names of files and subdirectories directly within a specified directory path. It can optionally ignore entries matching provided glob patterns.
- **Parameters:**
- `path` (string, required): The absolute path to the directory to list.
- `ignore` (array of strings, optional): A list of glob patterns to exclude from the listing (e.g., `["*.log", ".git"]`).
- `respect_git_ignore` (boolean, optional): Whether to respect .gitignore patterns when listing files. Defaults to true.
- `respect_git_ignore` (boolean, optional): Whether to respect `.gitignore` patterns when listing files. Defaults to `true`.
- **Behavior:**
- Returns a list of file and directory names.
- Indicates whether each entry is a directory.
@@ -23,17 +24,18 @@ All file system tools operate within a `rootDirectory` (usually the current work
## 2. `read_file` (ReadFile)
- **Tool Name:** `read_file`
- **Display Name:** ReadFile
`read_file` reads and returns the content of a specified file. This tool handles text, images (PNG, JPG, GIF, WEBP, SVG, BMP), and PDF files. For text files, it can read specific line ranges. Other binary file types are generally skipped.
- **Tool name:** `read_file`
- **Display name:** ReadFile
- **File:** `read-file.ts`
- **Description:** Reads and returns the content of a specified file. Handles text, images (PNG, JPG, GIF, WEBP, SVG, BMP), and PDF files. For text files, it can read specific line ranges. Other binary file types are generally skipped.
- **Parameters:**
- `path` (string, required): The absolute path to the file to read.
- `offset` (number, optional): For text files, the 0-based line number to start reading from. Requires `limit` to be set.
- `limit` (number, optional): For text files, the maximum number of lines to read. If omitted, reads a default maximum (e.g., 2000 lines) or the entire file if feasible.
- **Behavior:**
- For text files: Returns the content. If `offset` and `limit` are used, returns only that slice of lines. Indicates if content was truncated due to line limits or line length limits.
- For image and PDF files: Returns the file content as a base64 encoded data structure suitable for model consumption.
- For image and PDF files: Returns the file content as a base64-encoded data structure suitable for model consumption.
- For other binary files: Attempts to identify and skip them, returning a message indicating it's a generic binary file.
- **Output:** (`llmContent`):
- For text files: The file content, potentially prefixed with a truncation message (e.g., `[File content truncated: showing lines 1-100 of 500 total lines...]\nActual file content...`).
@@ -43,10 +45,11 @@ All file system tools operate within a `rootDirectory` (usually the current work
## 3. `write_file` (WriteFile)
- **Tool Name:** `write_file`
- **Display Name:** WriteFile
`write_file` writes content to a specified file. If the file exists, it will be overwritten. If the file doesn't exist, it (and any necessary parent directories) will be created.
- **Tool name:** `write_file`
- **Display name:** WriteFile
- **File:** `write-file.ts`
- **Description:** Writes content to a specified file. If the file exists, it will be overwritten. If it doesn't exist, it (and any necessary parent directories) will be created.
- **Parameters:**
- `file_path` (string, required): The absolute path to the file to write to.
- `content` (string, required): The content to write into the file.
@@ -58,15 +61,16 @@ All file system tools operate within a `rootDirectory` (usually the current work
## 4. `glob` (FindFiles)
- **Tool Name:** `glob`
- **Display Name:** FindFiles
`glob` finds files matching specific glob patterns (e.g., `src/**/*.ts`, `*.md`), returning absolute paths sorted by modification time (newest first).
- **Tool name:** `glob`
- **Display name:** FindFiles
- **File:** `glob.ts`
- **Description:** Efficiently finds files matching specific glob patterns (e.g., `src/**/*.ts`, `*.md`), returning absolute paths sorted by modification time (newest first).
- **Parameters:**
- `pattern` (string, required): The glob pattern to match against (e.g., `"*.py"`, `"src/**/*.js"`).
- `path` (string, optional): The absolute path to the directory to search within. If omitted, searches the tool's root directory.
- `case_sensitive` (boolean, optional): Whether the search should be case-sensitive. Defaults to false.
- `respect_git_ignore` (boolean, optional): Whether to respect .gitignore patterns when finding files. Defaults to true.
- `case_sensitive` (boolean, optional): Whether the search should be case-sensitive. Defaults to `false`.
- `respect_git_ignore` (boolean, optional): Whether to respect .gitignore patterns when finding files. Defaults to `true`.
- **Behavior:**
- Searches for files matching the glob pattern within the specified directory.
- Returns a list of absolute paths, sorted with the most recently modified files first.
@@ -76,20 +80,21 @@ All file system tools operate within a `rootDirectory` (usually the current work
## 5. `search_file_content` (SearchText)
- **Tool Name:** `search_file_content`
- **Display Name:** SearchText
`search_file_content` searches for a regular expression pattern within the content of files in a specified directory. Can filter files by a glob pattern. Returns the lines containing matches, along with their file paths and line numbers.
- **Tool name:** `search_file_content`
- **Display name:** SearchText
- **File:** `grep.ts`
- **Description:** Searches for a regular expression pattern within the content of files in a specified directory. Can filter files by a glob pattern. Returns the lines containing matches, along with their file paths and line numbers.
- **Parameters:**
- `pattern` (string, required): The regular expression (regex) to search for (e.g., `"function\s+myFunction"`).
- `path` (string, optional): The absolute path to the directory to search within. Defaults to the current working directory.
- `include` (string, optional): A glob pattern to filter which files are searched (e.g., `"*.js"`, `"src/**/*.{ts,tsx}"`). If omitted, searches most files (respecting common ignores).
- **Behavior:**
- Uses `git grep` if available in a Git repository for speed, otherwise falls back to system `grep` or a JavaScript-based search.
- Uses `git grep` if available in a Git repository for speed; otherwise, falls back to system `grep` or a JavaScript-based search.
- Returns a list of matching lines, each prefixed with its file path (relative to the search directory) and line number.
- **Output (`llmContent`):** A formatted string of matches, e.g.:
```
Found 3 match(es) for pattern "myFunction" in path "." (filter: "*.ts"):
Found 3 matches for pattern "myFunction" in path "." (filter: "*.ts"):
---
File: src/utils.ts
L15: export function myFunction() {
@@ -103,31 +108,36 @@ All file system tools operate within a `rootDirectory` (usually the current work
## 6. `replace` (Edit)
- **Tool Name:** `replace`
- **Display Name:** Edit
`replace` replaces text within a file. By default, replaces a single occurrence, but can replace multiple occurrences when `expected_replacements` is specified. This tool is designed for precise, targeted changes and requires significant context around the `old_string` to ensure it modifies the correct location.
- **Tool name:** `replace`
- **Display name:** Edit
- **File:** `edit.ts`
- **Description:** Replaces text within a file. By default, replaces a single occurrence, but can replace multiple occurrences when `expected_replacements` is specified. This tool is designed for precise, targeted changes and requires significant context around the `old_string` to ensure it modifies the correct location.
- **Parameters:**
- `file_path` (string, required): The absolute path to the file to modify.
- `old_string` (string, required): The exact literal text to replace. **CRITICAL:** This string must uniquely identify the single instance to change. It should include at least 3 lines of context _before_ and _after_ the target text, matching whitespace and indentation precisely. If `old_string` is empty, the tool attempts to create a new file at `file_path` with `new_string` as content.
- `old_string` (string, required): The exact literal text to replace.
**CRITICAL:** This string must uniquely identify the single instance to change. It should include at least 3 lines of context _before_ and _after_ the target text, matching whitespace and indentation precisely. If `old_string` is empty, the tool attempts to create a new file at `file_path` with `new_string` as content.
- `new_string` (string, required): The exact literal text to replace `old_string` with.
- `expected_replacements` (number, optional): The number of occurrences to replace. Defaults to 1.
- `expected_replacements` (number, optional): The number of occurrences to replace. Defaults to `1`.
- **Behavior:**
- If `old_string` is empty and `file_path` does not exist, creates a new file with `new_string` as content.
- If `old_string` is provided, it reads the `file_path` and attempts to find exactly one occurrence of `old_string`.
- If one occurrence is found, it replaces it with `new_string`.
- **Enhanced Reliability (Multi-Stage Edit Correction):** To significantly improve the success rate of edits, especially when the model-provided `old_string` might not be perfectly precise, the tool incorporates a multi-stage edit correction mechanism.
- If the initial `old_string` isn't found or matches multiple locations, the tool can leverage the Gemini model to iteratively refine `old_string` (and potentially `new_string`).
- This self-correction process attempts to identify the unique segment the model intended to modify, making the `replace` operation more robust even with slightly imperfect initial context from the AI.
- **Failure Conditions:** Despite the correction mechanism, the tool will fail if:
- `file_path` is not absolute or is outside the root directory.
- `old_string` is not empty, but the `file_path` does not exist.
- `old_string` is empty, but the `file_path` already exists.
- `old_string` is not found in the file after attempts to correct it.
- `old_string` is found multiple times, and the self-correction mechanism cannot resolve it to a single, unambiguous match.
- This self-correction process attempts to identify the unique segment the model intended to modify, making the `replace` operation more robust even with slightly imperfect initial context.
- **Failure conditions:** Despite the correction mechanism, the tool will fail if:
- `file_path` is not absolute or is outside the root directory.
- `old_string` is not empty, but the `file_path` does not exist.
- `old_string` is empty, but the `file_path` already exists.
- `old_string` is not found in the file after attempts to correct it.
- `old_string` is found multiple times, and the self-correction mechanism cannot resolve it to a single, unambiguous match.
- **Output (`llmContent`):**
- On success: `Successfully modified file: /path/to/file.txt (1 replacements).` or `Created new file: /path/to/new_file.txt with provided content.`
- On failure: An error message explaining the reason (e.g., `Failed to edit, 0 occurrences found...`, `Failed to edit, expected 1 occurrences but found 2...`).
- **Confirmation:** Yes. Shows a diff of the proposed changes and asks for user approval before writing to the file.
These file system tools provide a robust foundation for the Gemini CLI to understand and interact with your local project context.
These file system tools provide a foundation for the Gemini CLI to understand and interact with your local project context.
+26 -21
View File
@@ -1,22 +1,24 @@
# Gemini CLI Tools: Overview
# Gemini CLI tools
The Gemini CLI is equipped with a powerful set of built-in tools that the Gemini model can utilize to interact with your local environment, access information, and perform actions. These tools significantly enhance the CLI's capabilities, allowing it to go beyond text generation and assist with a wide range of tasks.
The Gemini CLI includes built-in tools that the Gemini model uses to interact with your local environment, access information, and perform actions. These tools enhance the CLI's capabilities, enabling it to go beyond text generation and assist with a wide range of tasks.
## What are Tools?
## Overview of Gemini CLI tools
In the context of the Gemini CLI, tools are specific functions or modules that the Gemini model can request to be executed. For example, if you ask Gemini to "Summarize the contents of `my_document.txt`," the model will likely identify the need to read that file and will request the execution of the `read_file` tool.
The core component (`packages/core`) manages these tools, presents their definitions (schemas) to the Gemini model, executes them when requested, and returns the results to the model for further processing into a user-facing response.
## Why are Tools Important?
These tools provide the following capabilities:
- **Access to Local Information:** Tools allow Gemini to access your local file system, read file contents, list directories, etc.
- **Execution of Commands:** With tools like `run_shell_command`, Gemini can run shell commands (with appropriate safety measures and user confirmation).
- **Interaction with the Web:** Tools can fetch content from URLs.
- **Action Taking:** Tools can modify files, write new files, or perform other actions on your system (again, typically with safeguards).
- **Grounding Responses:** By using tools to fetch real-time or specific local data, Gemini's responses can be more accurate, relevant, and grounded in your actual context.
- **Access local information:** Tools allow Gemini to access your local file system, read file contents, list directories, etc.
- **Execute commands:** With tools like `run_shell_command`, Gemini can run shell commands (with appropriate safety measures and user confirmation).
- **Interact with the web:** Tools can fetch content from URLs.
- **Take actions:** Tools can modify files, write new files, or perform other actions on your system (again, typically with safeguards).
- **Ground responses:** By using tools to fetch real-time or specific local data, Gemini's responses can be more accurate, relevant, and grounded in your actual context.
## How Tools are Used
## How to use Gemini CLI tools
To use Gemini CLI tools, provide a prompt to the Gemini CLI. The process works as follows:
1. You provide a prompt to the Gemini CLI.
2. The CLI sends the prompt to the core.
@@ -28,24 +30,27 @@ The core component (`packages/core`) manages these tools, presents their definit
You will typically see messages in the CLI indicating when a tool is being called and whether it succeeded or failed.
## Security and Confirmation
## Security and confirmation
Many tools, especially those that can modify your file system or execute commands (`write_file`, `edit`, `run_shell_command`), are designed with safety in mind. The Gemini CLI will typically:
- **Require Confirmation:** Prompt you before executing potentially sensitive operations, showing you what action is about to be taken.
- **Utilize Sandboxing:** All tools are subject to restrictions enforced by sandboxing (see [README](../../README.md#sandboxing)). This means that when operating in a sandbox, any tools (including MCP servers) you wish to use must be available _inside_ the sandbox environment. For example, to run an MCP server through `npx`, the `npx` executable must be installed within the sandbox's Docker image or be available in the `sandbox-exec` environment.
- **Require confirmation:** Prompt you before executing potentially sensitive operations, showing you what action is about to be taken.
- **Utilize sandboxing:** All tools are subject to restrictions enforced by sandboxing (see [Sandboxing in the Gemini CLI](../sandbox.md)). This means that when operating in a sandbox, any tools (including MCP servers) you wish to use must be available _inside_ the sandbox environment. For example, to run an MCP server through `npx`, the `npx` executable must be installed within the sandbox's Docker image or be available in the `sandbox-exec` environment.
It's important to always review confirmation prompts carefully before allowing a tool to proceed.
## Categories of Built-in Tools
## Learn more about Gemini CLI's tools
The built-in tools can be broadly categorized as follows:
Gemini CLI's built-in tools can be broadly categorized as follows:
- **[File System Tools](./file-system.md):** For interacting with files and directories (reading, writing, listing, searching, etc.).
- **[Shell Tool](./shell.md):** For executing shell commands.
- **[Web Fetch Tool](./web-fetch.md):** For retrieving content from URLs.
- **[Web Search Tool](./web-search.md):** For searching the web.
- **[Multi-File Read Tool](./multi-file.md):** A specialized tool for reading content from multiple files or directories, often used by the `@` command.
- **[Memory Tool](./memory.md):** For saving and recalling information across sessions.
- **[Shell Tool](./shell.md) (`run_shell_command`):** For executing shell commands.
- **[Web Fetch Tool](./web-fetch.md) (`web_fetch`):** For retrieving content from URLs.
- **[Web Search Tool](./web-search.md) (`web_search`):** For searching the web.
- **[Multi-File Read Tool](./multi-file.md) (`read_many_files`):** A specialized tool for reading content from multiple files or directories, often used by the `@` command.
- **[Memory Tool](./memory.md) (`save_memory`):** For saving and recalling information across sessions.
Understanding the available tools and how they work will help you make the most effective use of the Gemini CLI.
Additionally, these tools incorporate:
- **[MCP servers](./mcp-server.md)**: MCP servers act as a bridge between the Gemini model and your local environment or other services like APIs.
- **[Sandboxing](../sandbox.md)**: Sandboxing isolates the model and its changes from your environment to reduce potential risk.
+690
View File
@@ -0,0 +1,690 @@
# MCP servers with the Gemini CLI
This document provides a guide to configuring and using Model Context Protocol (MCP) servers with the Gemini CLI.
## What is an MCP server?
An MCP server is an application that exposes tools and resources to the Gemini CLI through the Model Context Protocol, allowing it to interact with external systems and data sources. MCP servers act as a bridge between the Gemini model and your local environment or other services like APIs.
An MCP server enables the Gemini CLI to:
- **Discover tools:** List available tools, their descriptions, and parameters through standardized schema definitions.
- **Execute tools:** Call specific tools with defined arguments and receive structured responses.
- **Access resources:** Read data from specific resources (though the Gemini CLI primarily focuses on tool execution).
With an MCP server, you can extend the Gemini CLI's capabilities to perform actions beyond its built-in features, such as interacting with databases, APIs, custom scripts, or specialized workflows.
## Core Integration Architecture
The Gemini CLI integrates with MCP servers through a sophisticated discovery and execution system built into the core package (`packages/core/src/tools/`):
### Discovery Layer (`mcp-client.ts`)
The discovery process is orchestrated by `discoverMcpTools()`, which:
1. **Iterates through configured servers** from your `settings.json` `mcpServers` configuration
2. **Establishes connections** using appropriate transport mechanisms (Stdio, SSE, or Streamable HTTP)
3. **Fetches tool definitions** from each server using the MCP protocol
4. **Sanitizes and validates** tool schemas for compatibility with the Gemini API
5. **Registers tools** in the global tool registry with conflict resolution
### Execution Layer (`mcp-tool.ts`)
Each discovered MCP tool is wrapped in a `DiscoveredMCPTool` instance that:
- **Handles confirmation logic** based on server trust settings and user preferences
- **Manages tool execution** by calling the MCP server with proper parameters
- **Processes responses** for both the LLM context and user display
- **Maintains connection state** and handles timeouts
### Transport Mechanisms
The Gemini CLI supports three MCP transport types:
- **Stdio Transport:** Spawns a subprocess and communicates via stdin/stdout
- **SSE Transport:** Connects to Server-Sent Events endpoints
- **Streamable HTTP Transport:** Uses HTTP streaming for communication
## How to set up your MCP server
The Gemini CLI uses the `mcpServers` configuration in your `settings.json` file to locate and connect to MCP servers. This configuration supports multiple servers with different transport mechanisms.
### Configure the MCP server in settings.json
You can configure MCP servers at the global level in the `~/.gemini/settings.json` file or in your project's root directory, create or open the `.gemini/settings.json` file. Within the file, add the `mcpServers` configuration block.
### Configuration Structure
Add an `mcpServers` object to your `settings.json` file:
```json
{ ...file contains other config objects
"mcpServers": {
"serverName": {
"command": "path/to/server",
"args": ["--arg1", "value1"],
"env": {
"API_KEY": "$MY_API_TOKEN"
},
"cwd": "./server-directory",
"timeout": 30000,
"trust": false
}
}
}
```
### Configuration Properties
Each server configuration supports the following properties:
#### Required (one of the following)
- **`command`** (string): Path to the executable for Stdio transport
- **`url`** (string): SSE endpoint URL (e.g., `"http://localhost:8080/sse"`)
- **`httpUrl`** (string): HTTP streaming endpoint URL
#### Optional
- **`args`** (string[]): Command-line arguments for Stdio transport
- **`headers`** (object): Custom HTTP headers when using `url` or `httpUrl`
- **`env`** (object): Environment variables for the server process. Values can reference environment variables using `$VAR_NAME` or `${VAR_NAME}` syntax
- **`cwd`** (string): Working directory for Stdio transport
- **`timeout`** (number): Request timeout in milliseconds (default: 600,000ms = 10 minutes)
- **`trust`** (boolean): When `true`, bypasses all tool call confirmations for this server (default: `false`)
- **`includeTools`** (string[]): List of tool names to include from this MCP server. When specified, only the tools listed here will be available from this server (whitelist behavior). If not specified, all tools from the server are enabled by default.
- **`excludeTools`** (string[]): List of tool names to exclude from this MCP server. Tools listed here will not be available to the model, even if they are exposed by the server. **Note:** `excludeTools` takes precedence over `includeTools` - if a tool is in both lists, it will be excluded.
### OAuth Support for Remote MCP Servers
The Gemini CLI supports OAuth 2.0 authentication for remote MCP servers using SSE or HTTP transports. This enables secure access to MCP servers that require authentication.
#### Automatic OAuth Discovery
For servers that support OAuth discovery, you can omit the OAuth configuration and let the CLI discover it automatically:
```json
{
"mcpServers": {
"discoveredServer": {
"url": "https://api.example.com/sse"
}
}
}
```
The CLI will automatically:
- Detect when a server requires OAuth authentication (401 responses)
- Discover OAuth endpoints from server metadata
- Perform dynamic client registration if supported
- Handle the OAuth flow and token management
#### Authentication Flow
When connecting to an OAuth-enabled server:
1. **Initial connection attempt** fails with 401 Unauthorized
2. **OAuth discovery** finds authorization and token endpoints
3. **Browser opens** for user authentication (requires local browser access)
4. **Authorization code** is exchanged for access tokens
5. **Tokens are stored** securely for future use
6. **Connection retry** succeeds with valid tokens
#### Browser Redirect Requirements
**Important:** OAuth authentication requires that your local machine can:
- Open a web browser for authentication
- Receive redirects on `http://localhost:7777/oauth/callback`
This feature will not work in:
- Headless environments without browser access
- Remote SSH sessions without X11 forwarding
- Containerized environments without browser support
#### Managing OAuth Authentication
Use the `/mcp auth` command to manage OAuth authentication:
```bash
# List servers requiring authentication
/mcp auth
# Authenticate with a specific server
/mcp auth serverName
# Re-authenticate if tokens expire
/mcp auth serverName
```
#### OAuth Configuration Properties
- **`enabled`** (boolean): Enable OAuth for this server
- **`clientId`** (string): OAuth client identifier (optional with dynamic registration)
- **`clientSecret`** (string): OAuth client secret (optional for public clients)
- **`authorizationUrl`** (string): OAuth authorization endpoint (auto-discovered if omitted)
- **`tokenUrl`** (string): OAuth token endpoint (auto-discovered if omitted)
- **`scopes`** (string[]): Required OAuth scopes
- **`redirectUri`** (string): Custom redirect URI (defaults to `http://localhost:7777/oauth/callback`)
- **`tokenParamName`** (string): Query parameter name for tokens in SSE URLs
- **`audiences`** (string[]): Audiences the token is valid for
#### Token Management
OAuth tokens are automatically:
- **Stored securely** in `~/.gemini/mcp-oauth-tokens.json`
- **Refreshed** when expired (if refresh tokens are available)
- **Validated** before each connection attempt
- **Cleaned up** when invalid or expired
#### Authentication Provider Type
You can specify the authentication provider type using the `authProviderType` property:
- **`authProviderType`** (string): Specifies the authentication provider. Can be one of the following:
- **`dynamic_discovery`** (default): The CLI will automatically discover the OAuth configuration from the server.
- **`google_credentials`**: The CLI will use the Google Application Default Credentials (ADC) to authenticate with the server. When using this provider, you must specify the required scopes.
```json
{
"mcpServers": {
"googleCloudServer": {
"httpUrl": "https://my-gcp-service.run.app/mcp",
"authProviderType": "google_credentials",
"oauth": {
"scopes": ["https://www.googleapis.com/auth/userinfo.email"]
}
}
}
}
```
### Example Configurations
#### Python MCP Server (Stdio)
```json
{
"mcpServers": {
"pythonTools": {
"command": "python",
"args": ["-m", "my_mcp_server", "--port", "8080"],
"cwd": "./mcp-servers/python",
"env": {
"DATABASE_URL": "$DB_CONNECTION_STRING",
"API_KEY": "${EXTERNAL_API_KEY}"
},
"timeout": 15000
}
}
}
```
#### Node.js MCP Server (Stdio)
```json
{
"mcpServers": {
"nodeServer": {
"command": "node",
"args": ["dist/server.js", "--verbose"],
"cwd": "./mcp-servers/node",
"trust": true
}
}
}
```
#### Docker-based MCP Server
```json
{
"mcpServers": {
"dockerizedServer": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-e",
"API_KEY",
"-v",
"${PWD}:/workspace",
"my-mcp-server:latest"
],
"env": {
"API_KEY": "$EXTERNAL_SERVICE_TOKEN"
}
}
}
}
```
#### HTTP-based MCP Server
```json
{
"mcpServers": {
"httpServer": {
"httpUrl": "http://localhost:3000/mcp",
"timeout": 5000
}
}
}
```
#### HTTP-based MCP Server with Custom Headers
```json
{
"mcpServers": {
"httpServerWithAuth": {
"httpUrl": "http://localhost:3000/mcp",
"headers": {
"Authorization": "Bearer your-api-token",
"X-Custom-Header": "custom-value",
"Content-Type": "application/json"
},
"timeout": 5000
}
}
}
```
#### MCP Server with Tool Filtering
```json
{
"mcpServers": {
"filteredServer": {
"command": "python",
"args": ["-m", "my_mcp_server"],
"includeTools": ["safe_tool", "file_reader", "data_processor"],
// "excludeTools": ["dangerous_tool", "file_deleter"],
"timeout": 30000
}
}
}
```
## Discovery Process Deep Dive
When the Gemini CLI starts, it performs MCP server discovery through the following detailed process:
### 1. Server Iteration and Connection
For each configured server in `mcpServers`:
1. **Status tracking begins:** Server status is set to `CONNECTING`
2. **Transport selection:** Based on configuration properties:
- `httpUrl``StreamableHTTPClientTransport`
- `url``SSEClientTransport`
- `command``StdioClientTransport`
3. **Connection establishment:** The MCP client attempts to connect with the configured timeout
4. **Error handling:** Connection failures are logged and the server status is set to `DISCONNECTED`
### 2. Tool Discovery
Upon successful connection:
1. **Tool listing:** The client calls the MCP server's tool listing endpoint
2. **Schema validation:** Each tool's function declaration is validated
3. **Tool filtering:** Tools are filtered based on `includeTools` and `excludeTools` configuration
4. **Name sanitization:** Tool names are cleaned to meet Gemini API requirements:
- Invalid characters (non-alphanumeric, underscore, dot, hyphen) are replaced with underscores
- Names longer than 63 characters are truncated with middle replacement (`___`)
### 3. Conflict Resolution
When multiple servers expose tools with the same name:
1. **First registration wins:** The first server to register a tool name gets the unprefixed name
2. **Automatic prefixing:** Subsequent servers get prefixed names: `serverName__toolName`
3. **Registry tracking:** The tool registry maintains mappings between server names and their tools
### 4. Schema Processing
Tool parameter schemas undergo sanitization for Gemini API compatibility:
- **`$schema` properties** are removed
- **`additionalProperties`** are stripped
- **`anyOf` with `default`** have their default values removed (Vertex AI compatibility)
- **Recursive processing** applies to nested schemas
### 5. Connection Management
After discovery:
- **Persistent connections:** Servers that successfully register tools maintain their connections
- **Cleanup:** Servers that provide no usable tools have their connections closed
- **Status updates:** Final server statuses are set to `CONNECTED` or `DISCONNECTED`
## Tool Execution Flow
When the Gemini model decides to use an MCP tool, the following execution flow occurs:
### 1. Tool Invocation
The model generates a `FunctionCall` with:
- **Tool name:** The registered name (potentially prefixed)
- **Arguments:** JSON object matching the tool's parameter schema
### 2. Confirmation Process
Each `DiscoveredMCPTool` implements sophisticated confirmation logic:
#### Trust-based Bypass
```typescript
if (this.trust) {
return false; // No confirmation needed
}
```
#### Dynamic Allow-listing
The system maintains internal allow-lists for:
- **Server-level:** `serverName` → All tools from this server are trusted
- **Tool-level:** `serverName.toolName` → This specific tool is trusted
#### User Choice Handling
When confirmation is required, users can choose:
- **Proceed once:** Execute this time only
- **Always allow this tool:** Add to tool-level allow-list
- **Always allow this server:** Add to server-level allow-list
- **Cancel:** Abort execution
### 3. Execution
Upon confirmation (or trust bypass):
1. **Parameter preparation:** Arguments are validated against the tool's schema
2. **MCP call:** The underlying `CallableTool` invokes the server with:
```typescript
const functionCalls = [
{
name: this.serverToolName, // Original server tool name
args: params,
},
];
```
3. **Response processing:** Results are formatted for both LLM context and user display
### 4. Response Handling
The execution result contains:
- **`llmContent`:** Raw response parts for the language model's context
- **`returnDisplay`:** Formatted output for user display (often JSON in markdown code blocks)
## How to interact with your MCP server
### Using the `/mcp` Command
The `/mcp` command provides comprehensive information about your MCP server setup:
```bash
/mcp
```
This displays:
- **Server list:** All configured MCP servers
- **Connection status:** `CONNECTED`, `CONNECTING`, or `DISCONNECTED`
- **Server details:** Configuration summary (excluding sensitive data)
- **Available tools:** List of tools from each server with descriptions
- **Discovery state:** Overall discovery process status
### Example `/mcp` Output
```
MCP Servers Status:
📡 pythonTools (CONNECTED)
Command: python -m my_mcp_server --port 8080
Working Directory: ./mcp-servers/python
Timeout: 15000ms
Tools: calculate_sum, file_analyzer, data_processor
🔌 nodeServer (DISCONNECTED)
Command: node dist/server.js --verbose
Error: Connection refused
🐳 dockerizedServer (CONNECTED)
Command: docker run -i --rm -e API_KEY my-mcp-server:latest
Tools: docker__deploy, docker__status
Discovery State: COMPLETED
```
### Tool Usage
Once discovered, MCP tools are available to the Gemini model like built-in tools. The model will automatically:
1. **Select appropriate tools** based on your requests
2. **Present confirmation dialogs** (unless the server is trusted)
3. **Execute tools** with proper parameters
4. **Display results** in a user-friendly format
## Status Monitoring and Troubleshooting
### Connection States
The MCP integration tracks several states:
#### Server Status (`MCPServerStatus`)
- **`DISCONNECTED`:** Server is not connected or has errors
- **`CONNECTING`:** Connection attempt in progress
- **`CONNECTED`:** Server is connected and ready
#### Discovery State (`MCPDiscoveryState`)
- **`NOT_STARTED`:** Discovery hasn't begun
- **`IN_PROGRESS`:** Currently discovering servers
- **`COMPLETED`:** Discovery finished (with or without errors)
### Common Issues and Solutions
#### Server Won't Connect
**Symptoms:** Server shows `DISCONNECTED` status
**Troubleshooting:**
1. **Check configuration:** Verify `command`, `args`, and `cwd` are correct
2. **Test manually:** Run the server command directly to ensure it works
3. **Check dependencies:** Ensure all required packages are installed
4. **Review logs:** Look for error messages in the CLI output
5. **Verify permissions:** Ensure the CLI can execute the server command
#### No Tools Discovered
**Symptoms:** Server connects but no tools are available
**Troubleshooting:**
1. **Verify tool registration:** Ensure your server actually registers tools
2. **Check MCP protocol:** Confirm your server implements the MCP tool listing correctly
3. **Review server logs:** Check stderr output for server-side errors
4. **Test tool listing:** Manually test your server's tool discovery endpoint
#### Tools Not Executing
**Symptoms:** Tools are discovered but fail during execution
**Troubleshooting:**
1. **Parameter validation:** Ensure your tool accepts the expected parameters
2. **Schema compatibility:** Verify your input schemas are valid JSON Schema
3. **Error handling:** Check if your tool is throwing unhandled exceptions
4. **Timeout issues:** Consider increasing the `timeout` setting
#### Sandbox Compatibility
**Symptoms:** MCP servers fail when sandboxing is enabled
**Solutions:**
1. **Docker-based servers:** Use Docker containers that include all dependencies
2. **Path accessibility:** Ensure server executables are available in the sandbox
3. **Network access:** Configure sandbox to allow necessary network connections
4. **Environment variables:** Verify required environment variables are passed through
### Debugging Tips
1. **Enable debug mode:** Run the CLI with `--debug` for verbose output
2. **Check stderr:** MCP server stderr is captured and logged (INFO messages filtered)
3. **Test isolation:** Test your MCP server independently before integrating
4. **Incremental setup:** Start with simple tools before adding complex functionality
5. **Use `/mcp` frequently:** Monitor server status during development
## Important Notes
### Security Considerations
- **Trust settings:** The `trust` option bypasses all confirmation dialogs. Use cautiously and only for servers you completely control
- **Access tokens:** Be security-aware when configuring environment variables containing API keys or tokens
- **Sandbox compatibility:** When using sandboxing, ensure MCP servers are available within the sandbox environment
- **Private data:** Using broadly scoped personal access tokens can lead to information leakage between repositories
### Performance and Resource Management
- **Connection persistence:** The CLI maintains persistent connections to servers that successfully register tools
- **Automatic cleanup:** Connections to servers providing no tools are automatically closed
- **Timeout management:** Configure appropriate timeouts based on your server's response characteristics
- **Resource monitoring:** MCP servers run as separate processes and consume system resources
### Schema Compatibility
- **Property stripping:** The system automatically removes certain schema properties (`$schema`, `additionalProperties`) for Gemini API compatibility
- **Name sanitization:** Tool names are automatically sanitized to meet API requirements
- **Conflict resolution:** Tool name conflicts between servers are resolved through automatic prefixing
This comprehensive integration makes MCP servers a powerful way to extend the Gemini CLI's capabilities while maintaining security, reliability, and ease of use.
## Returning Rich Content from Tools
MCP tools are not limited to returning simple text. You can return rich, multi-part content, including text, images, audio, and other binary data in a single tool response. This allows you to build powerful tools that can provide diverse information to the model in a single turn.
All data returned from the tool is processed and sent to the model as context for its next generation, enabling it to reason about or summarize the provided information.
### How It Works
To return rich content, your tool's response must adhere to the MCP specification for a [`CallToolResult`](https://modelcontextprotocol.io/specification/2025-06-18/server/tools#tool-result). The `content` field of the result should be an array of `ContentBlock` objects. The Gemini CLI will correctly process this array, separating text from binary data and packaging it for the model.
You can mix and match different content block types in the `content` array. The supported block types include:
- `text`
- `image`
- `audio`
- `resource` (embedded content)
- `resource_link`
### Example: Returning Text and an Image
Here is an example of a valid JSON response from an MCP tool that returns both a text description and an image:
```json
{
"content": [
{
"type": "text",
"text": "Here is the logo you requested."
},
{
"type": "image",
"data": "BASE64_ENCODED_IMAGE_DATA_HERE",
"mimeType": "image/png"
},
{
"type": "text",
"text": "The logo was created in 2025."
}
]
}
```
When the Gemini CLI receives this response, it will:
1. Extract all the text and combine it into a single `functionResponse` part for the model.
2. Present the image data as a separate `inlineData` part.
3. Provide a clean, user-friendly summary in the CLI, indicating that both text and an image were received.
This enables you to build sophisticated tools that can provide rich, multi-modal context to the Gemini model.
## MCP Prompts as Slash Commands
In addition to tools, MCP servers can expose predefined prompts that can be executed as slash commands within the Gemini CLI. This allows you to create shortcuts for common or complex queries that can be easily invoked by name.
### Defining Prompts on the Server
Here's a small example of a stdio MCP server that defines prompts:
```ts
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';
const server = new McpServer({
name: 'prompt-server',
version: '1.0.0',
});
server.registerPrompt(
'poem-writer',
{
title: 'Poem Writer',
description: 'Write a nice haiku',
argsSchema: { title: z.string(), mood: z.string().optional() },
},
({ title, mood }) => ({
messages: [
{
role: 'user',
content: {
type: 'text',
text: `Write a haiku${mood ? ` with the mood ${mood}` : ''} called ${title}. Note that a haiku is 5 syllables followed by 7 syllables followed by 5 syllables `,
},
},
],
}),
);
const transport = new StdioServerTransport();
await server.connect(transport);
```
This can be included in `settings.json` under `mcpServers` with:
```json
"nodeServer": {
"command": "node",
"args": ["filename.ts"],
}
```
### Invoking Prompts
Once a prompt is discovered, you can invoke it using its name as a slash command. The CLI will automatically handle parsing arguments.
```bash
/poem-writer --title="Gemini CLI" --mood="reverent"
```
or, using positional arguments:
```bash
/poem-writer "Gemini CLI" reverent
```
When you run this command, the Gemini CLI executes the `prompts/get` method on the MCP server with the provided arguments. The server is responsible for substituting the arguments into the prompt template and returning the final prompt text. The CLI then sends this prompt to the model for execution. This provides a convenient way to automate and share common workflows.
+41 -22
View File
@@ -1,25 +1,44 @@
# Memory Tool
# Memory Tool (`save_memory`)
This document provides details on the `save_memory` tool.
This document describes the `save_memory` tool for the Gemini CLI.
## `save_memory`
## Description
- **Purpose:** Saves a specific piece of information or fact to your long-term memory. This allows the CLI to remember key details across sessions, providing more personalized and effective assistance.
- **Arguments:**
- `fact` (string, required): The specific fact or piece of information to remember. This should be a clear, self-contained statement.
- **Behavior:**
- The tool appends the provided `fact` to a special `GEMINI.md` file located in the user's home directory (`~/.gemini/GEMINI.md`). This file can be configured to have a different name.
- The facts are stored under a `## Gemini Added Memories` section.
- This file is loaded as context in subsequent sessions, allowing the CLI to recall the saved information.
- **Examples:**
- Remembering a user preference:
```
save_memory(fact="My preferred programming language is Python.")
```
- Storing a project-specific detail:
```
save_memory(fact="The project I'm currently working on is called 'gemini-cli'.")
```
- **Important Notes:**
- This tool should be used for concise, important facts. It is not intended for storing large amounts of data or conversational history.
- The memory file is a plain text Markdown file, so you can view and edit it manually if needed.
Use `save_memory` to save and recall information across your Gemini CLI sessions. With `save_memory`, you can direct the CLI to remember key details across sessions, providing personalized and directed assistance.
### Arguments
`save_memory` takes one argument:
- `fact` (string, required): The specific fact or piece of information to remember. This should be a clear, self-contained statement written in natural language.
## How to use `save_memory` with the Gemini CLI
The tool appends the provided `fact` to a special `GEMINI.md` file located in the user's home directory (`~/.gemini/GEMINI.md`). This file can be configured to have a different name.
Once added, the facts are stored under a `## Gemini Added Memories` section. This file is loaded as context in subsequent sessions, allowing the CLI to recall the saved information.
Usage:
```
save_memory(fact="Your fact here.")
```
### `save_memory` examples
Remember a user preference:
```
save_memory(fact="My preferred programming language is Python.")
```
Store a project-specific detail:
```
save_memory(fact="The project I'm currently working on is called 'gemini-cli'.")
```
## Important notes
- **General usage:** This tool should be used for concise, important facts. It is not intended for storing large amounts of data or conversational history.
- **Memory file:** The memory file is a plain text Markdown file, so you can view and edit it manually if needed.
+65 -36
View File
@@ -1,39 +1,68 @@
# Multi-File Read Tool
# Multi File Read Tool (`read_many_files`)
This document provides details on the `read_many_files` tool.
This document describes the `read_many_files` tool for the Gemini CLI.
## `read_many_files`
## Description
- **Purpose:** Reads content from multiple files specified by paths or glob patterns. For text files, it concatenates their content into a single string. For image (e.g., PNG, JPEG) and PDF files, it reads and returns them as base64 encoded data, provided they are explicitly requested by name or extension. This is useful for getting an overview of a codebase, finding where specific functionality is implemented, reviewing documentation, or gathering context from multiple configuration files.
- **Arguments:**
- `paths` (list[string], required): An array of glob patterns or paths relative to the tool's target directory (e.g., `["src/**/*.ts"]`, `["README.md", "docs/", "assets/logo.png"]`).
- `exclude` (list[string], optional): Glob patterns for files/directories to exclude (e.g., `["**/*.log", "temp/"]`). These are added to default excludes if `useDefaultExcludes` is true.
- `include` (list[string], optional): Additional glob patterns to include. These are merged with `paths` (e.g., `["*.test.ts"]` to specifically add test files if they were broadly excluded, or `["images/*.jpg"]` to include specific image types).
- `recursive` (boolean, optional): Whether to search recursively. This is primarily controlled by `**` in glob patterns. Defaults to `true`.
- `useDefaultExcludes` (boolean, optional): Whether to apply a list of default exclusion patterns (e.g., `node_modules`, `.git`, non image/pdf binary files). Defaults to `true`.
- `respect_git_ignore` (boolean, optional): Whether to respect .gitignore patterns when finding files. Defaults to true.
- **Behavior:**
- The tool searches for files matching the provided `paths` and `include` patterns, while respecting `exclude` patterns and default excludes (if enabled).
- For text files: it reads the content of each matched file (attempting to skip binary files not explicitly requested as image/PDF) and concatenates it into a single string, with a separator `--- {filePath} ---` between the content of each file. Uses UTF-8 encoding by default.
- For image and PDF files: if explicitly requested by name or extension (e.g., `paths: ["logo.png"]` or `include: ["*.pdf"]`), the tool reads the file and returns its content as a base64 encoded string.
- The tool attempts to detect and skip other binary files (those not matching common image/PDF types or not explicitly requested) by checking for null bytes in their initial content.
- **Examples:**
- Reading all TypeScript files in the `src` directory:
```
read_many_files(paths=["src/**/*.ts"])
```
- Reading the main README, all Markdown files in the `docs` directory, and a specific logo image, excluding a specific file:
```
read_many_files(paths=["README.md", "docs/**/*.md", "assets/logo.png"], exclude=["docs/OLD_README.md"])
```
- Reading all JavaScript files but explicitly including test files and all JPEGs in an `images` folder:
```
read_many_files(paths=["**/*.js"], include=["**/*.test.js", "images/**/*.jpg"], useDefaultExcludes=False)
```
- **Important Notes:**
- **Binary File Handling:**
- **Image/PDF Files:** The tool can read common image types (PNG, JPEG, etc.) and PDF files, returning them as base64 encoded data. These files _must_ be explicitly targeted by the `paths` or `include` patterns (e.g., by specifying the exact filename like `image.png` or a pattern like `*.jpeg`).
- **Other Binary Files:** The tool attempts to detect and skip other types of binary files by examining their initial content for null bytes. Its behavior with such files is to exclude them from the output.
- **Performance:** Reading a very large number of files or very large individual files can be resource-intensive.
- **Path Specificity:** Ensure paths and glob patterns are correctly specified relative to the tool's target directory. For image/PDF files, ensure the patterns are specific enough to include them.
- **Default Excludes:** Be aware of the default exclusion patterns (like `node_modules`, `.git`) and use `useDefaultExcludes=False` if you need to override them, but do so cautiously.
Use `read_many_files` to read content from multiple files specified by paths or glob patterns. The behavior of this tool depends on the provided files:
- For text files, this tool concatenates their content into a single string.
- For image (e.g., PNG, JPEG), PDF, audio (MP3, WAV), and video (MP4, MOV) files, it reads and returns them as base64-encoded data, provided they are explicitly requested by name or extension.
`read_many_files` can be used to perform tasks such as getting an overview of a codebase, finding where specific functionality is implemented, reviewing documentation, or gathering context from multiple configuration files.
**Note:** `read_many_files` looks for files following the provided paths or glob patterns. A directory path such as `"/docs"` will return an empty result; the tool requires a pattern such as `"/docs/*"` or `"/docs/*.md"` to identify the relevant files.
### Arguments
`read_many_files` takes the following arguments:
- `paths` (list[string], required): An array of glob patterns or paths relative to the tool's target directory (e.g., `["src/**/*.ts"]`, `["README.md", "docs/*", "assets/logo.png"]`).
- `exclude` (list[string], optional): Glob patterns for files/directories to exclude (e.g., `["**/*.log", "temp/"]`). These are added to default excludes if `useDefaultExcludes` is true.
- `include` (list[string], optional): Additional glob patterns to include. These are merged with `paths` (e.g., `["*.test.ts"]` to specifically add test files if they were broadly excluded, or `["images/*.jpg"]` to include specific image types).
- `recursive` (boolean, optional): Whether to search recursively. This is primarily controlled by `**` in glob patterns. Defaults to `true`.
- `useDefaultExcludes` (boolean, optional): Whether to apply a list of default exclusion patterns (e.g., `node_modules`, `.git`, non image/pdf binary files). Defaults to `true`.
- `respect_git_ignore` (boolean, optional): Whether to respect .gitignore patterns when finding files. Defaults to true.
## How to use `read_many_files` with the Gemini CLI
`read_many_files` searches for files matching the provided `paths` and `include` patterns, while respecting `exclude` patterns and default excludes (if enabled).
- For text files: it reads the content of each matched file (attempting to skip binary files not explicitly requested as image/PDF) and concatenates it into a single string, with a separator `--- {filePath} ---` between the content of each file. Uses UTF-8 encoding by default.
- For image and PDF files: if explicitly requested by name or extension (e.g., `paths: ["logo.png"]` or `include: ["*.pdf"]`), the tool reads the file and returns its content as a base64 encoded string.
- The tool attempts to detect and skip other binary files (those not matching common image/PDF types or not explicitly requested) by checking for null bytes in their initial content.
Usage:
```
read_many_files(paths=["Your files or paths here."], include=["Additional files to include."], exclude=["Files to exclude."], recursive=False, useDefaultExcludes=false, respect_git_ignore=true)
```
## `read_many_files` examples
Read all TypeScript files in the `src` directory:
```
read_many_files(paths=["src/**/*.ts"])
```
Read the main README, all Markdown files in the `docs` directory, and a specific logo image, excluding a specific file:
```
read_many_files(paths=["README.md", "docs/**/*.md", "assets/logo.png"], exclude=["docs/OLD_README.md"])
```
Read all JavaScript files but explicitly include test files and all JPEGs in an `images` folder:
```
read_many_files(paths=["**/*.js"], include=["**/*.test.js", "images/**/*.jpg"], useDefaultExcludes=False)
```
## Important notes
- **Binary file handling:**
- **Image/PDF/Audio/Video files:** The tool can read common image types (PNG, JPEG, etc.), PDF, audio (mp3, wav), and video (mp4, mov) files, returning them as base64 encoded data. These files _must_ be explicitly targeted by the `paths` or `include` patterns (e.g., by specifying the exact filename like `video.mp4` or a pattern like `*.mov`).
- **Other binary files:** The tool attempts to detect and skip other types of binary files by examining their initial content for null bytes. The tool excludes these files from its output.
- **Performance:** Reading a very large number of files or very large individual files can be resource-intensive.
- **Path specificity:** Ensure paths and glob patterns are correctly specified relative to the tool's target directory. For image/PDF files, ensure the patterns are specific enough to include them.
- **Default excludes:** Be aware of the default exclusion patterns (like `node_modules`, `.git`) and use `useDefaultExcludes=False` if you need to override them, but do so cautiously.
+133 -38
View File
@@ -1,46 +1,141 @@
# Shell Tool
# Shell Tool (`run_shell_command`)
This document provides details on the shell tool.
This document describes the `run_shell_command` tool for the Gemini CLI.
## `run_shell_command`
## Description
- **Purpose:** Executes a given shell command. On Windows, this will be executed with `cmd.exe /c`. On other platforms, it will be executed with `bash -c`. This tool is essential for interacting with the underlying operating system, running scripts, or performing command-line operations.
- **Arguments:**
- `command` (string, required): The exact shell command to execute.
- `description` (string, optional): A brief description of the command's purpose, which will be shown to the user.
- `directory` (string, optional): The directory (relative to the project root) in which to execute the command. If not provided, the command runs in the project root.
- **Behavior:**
- The command is executed as a subprocess.
- It can start background processes using `&`.
- The tool returns detailed information about the execution, including:
- `Command`: The command that was executed.
- `Directory`: The directory where the command was run.
- `Stdout`: Output from the standard output stream.
- `Stderr`: Output from the standard error stream.
- `Error`: Any error message reported by the subprocess.
- `Exit Code`: The exit code of the command.
- `Signal`: The signal number if the command was terminated by a signal.
- `Background PIDs`: A list of PIDs for any background processes started.
- **Examples:**
Use `run_shell_command` to interact with the underlying system, run scripts, or perform command-line operations. `run_shell_command` executes a given shell command. On Windows, the command will be executed with `cmd.exe /c`. On other platforms, the command will be executed with `bash -c`.
- Listing files in the current directory:
### Arguments
```
`run_shell_command` takes the following arguments:
run_shell_command(command="ls -la")
```
- `command` (string, required): The exact shell command to execute.
- `description` (string, optional): A brief description of the command's purpose, which will be shown to the user.
- `directory` (string, optional): The directory (relative to the project root) in which to execute the command. If not provided, the command runs in the project root.
- Running a script in a specific directory:
```
run_shell_command(command="./my_script.sh", directory="scripts", description="Run my custom script")
```
- Starting a background server:
```
run_shell_command(command="npm run dev &", description="Start development server in background")
```
## How to use `run_shell_command` with the Gemini CLI
- **Important Notes:**
- **Security:** Be cautious when executing commands, especially those constructed from user input, to prevent security vulnerabilities.
- **Interactive Commands:** Avoid commands that require interactive user input, as this can cause the tool to hang. Use non-interactive flags if available (e.g., `npm init -y`).
- **Error Handling:** Check the `Stderr`, `Error`, and `Exit Code` fields to determine if a command executed successfully.
- **Background Processes:** When a command is run in the background with `&`, the tool will return immediately and the process will continue to run in the background. The `Background PIDs` field will contain the process ID of the background process.
When using `run_shell_command`, the command is executed as a subprocess. `run_shell_command` can start background processes using `&`. The tool returns detailed information about the execution, including:
- `Command`: The command that was executed.
- `Directory`: The directory where the command was run.
- `Stdout`: Output from the standard output stream.
- `Stderr`: Output from the standard error stream.
- `Error`: Any error message reported by the subprocess.
- `Exit Code`: The exit code of the command.
- `Signal`: The signal number if the command was terminated by a signal.
- `Background PIDs`: A list of PIDs for any background processes started.
Usage:
```
run_shell_command(command="Your commands.", description="Your description of the command.", directory="Your execution directory.")
```
## `run_shell_command` examples
List files in the current directory:
```
run_shell_command(command="ls -la")
```
Run a script in a specific directory:
```
run_shell_command(command="./my_script.sh", directory="scripts", description="Run my custom script")
```
Start a background server:
```
run_shell_command(command="npm run dev &", description="Start development server in background")
```
## Important notes
- **Security:** Be cautious when executing commands, especially those constructed from user input, to prevent security vulnerabilities.
- **Interactive commands:** Avoid commands that require interactive user input, as this can cause the tool to hang. Use non-interactive flags if available (e.g., `npm init -y`).
- **Error handling:** Check the `Stderr`, `Error`, and `Exit Code` fields to determine if a command executed successfully.
- **Background processes:** When a command is run in the background with `&`, the tool will return immediately and the process will continue to run in the background. The `Background PIDs` field will contain the process ID of the background process.
## Environment Variables
When `run_shell_command` executes a command, it sets the `GEMINI_CLI=1` environment variable in the subprocess's environment. This allows scripts or tools to detect if they are being run from within the Gemini CLI.
## Command Restrictions
You can restrict the commands that can be executed by the `run_shell_command` tool by using the `coreTools` and `excludeTools` settings in your configuration file.
- `coreTools`: To restrict `run_shell_command` to a specific set of commands, add entries to the `coreTools` list in the format `run_shell_command(<command>)`. For example, `"coreTools": ["run_shell_command(git)"]` will only allow `git` commands. Including the generic `run_shell_command` acts as a wildcard, allowing any command not explicitly blocked.
- `excludeTools`: To block specific commands, add entries to the `excludeTools` list in the format `run_shell_command(<command>)`. For example, `"excludeTools": ["run_shell_command(rm)"]` will block `rm` commands.
The validation logic is designed to be secure and flexible:
1. **Command Chaining Disabled**: The tool automatically splits commands chained with `&&`, `||`, or `;` and validates each part separately. If any part of the chain is disallowed, the entire command is blocked.
2. **Prefix Matching**: The tool uses prefix matching. For example, if you allow `git`, you can run `git status` or `git log`.
3. **Blocklist Precedence**: The `excludeTools` list is always checked first. If a command matches a blocked prefix, it will be denied, even if it also matches an allowed prefix in `coreTools`.
### Command Restriction Examples
**Allow only specific command prefixes**
To allow only `git` and `npm` commands, and block all others:
```json
{
"coreTools": ["run_shell_command(git)", "run_shell_command(npm)"]
}
```
- `git status`: Allowed
- `npm install`: Allowed
- `ls -l`: Blocked
**Block specific command prefixes**
To block `rm` and allow all other commands:
```json
{
"coreTools": ["run_shell_command"],
"excludeTools": ["run_shell_command(rm)"]
}
```
- `rm -rf /`: Blocked
- `git status`: Allowed
- `npm install`: Allowed
**Blocklist takes precedence**
If a command prefix is in both `coreTools` and `excludeTools`, it will be blocked.
```json
{
"coreTools": ["run_shell_command(git)"],
"excludeTools": ["run_shell_command(git push)"]
}
```
- `git push origin main`: Blocked
- `git status`: Allowed
**Block all shell commands**
To block all shell commands, add the `run_shell_command` wildcard to `excludeTools`:
```json
{
"excludeTools": ["run_shell_command"]
}
```
- `ls -l`: Blocked
- `any other command`: Blocked
## Security Note for `excludeTools`
Command-specific restrictions in `excludeTools` for `run_shell_command` are based on simple string matching and can be easily bypassed. This feature is **not a security mechanism** and should not be relied upon to safely execute untrusted code. It is recommended to use `coreTools` to explicitly select commands
that can be executed.
+41 -24
View File
@@ -1,27 +1,44 @@
# Web Fetch Tool
# Web Fetch Tool (`web_fetch`)
This document describes the `web_fetch` tool.
This document describes the `web_fetch` tool for the Gemini CLI.
## `web_fetch`
## Description
- **Purpose:** Processes content from one or more URLs (up to 20) embedded in a prompt. This tool is ideal for tasks that require summarizing, comparing, or extracting specific information from web pages.
- **Arguments:**
- `prompt` (string, required): A comprehensive prompt that includes the URL(s) (up to 20) to fetch and specific instructions on how to process their content. For example: `"Summarize https://example.com/article and extract key points from https://another.com/data"`. The prompt must contain at least one URL starting with `http://` or `https://`.
- **Behavior:**
- The tool will first ask for confirmation before fetching any URLs.
- It attempts to process URLs through the Gemini API's `urlContext` tool first.
- If the Gemini API cannot access a URL (e.g., it's on `localhost`, a private network, or behind a firewall), the tool will fall back to fetching the content directly from the local machine.
- The tool can fetch content from `localhost` and private network addresses via this fallback mechanism.
- The tool formats the response, including source attribution with citations where possible, and returns it to the user.
- **Examples:**
- Summarizing a single article:
```
web_fetch(prompt="Can you summarize the main points of https://example.com/news/latest")
```
- Comparing two articles:
```
web_fetch(prompt="What are the differences in the conclusions of these two papers: https://arxiv.org/abs/2401.0001 and https://arxiv.org/abs/2401.0002?")
```
- **Important Notes:**
- This tool relies on the Gemini API's ability to access and process the given URLs.
- The quality of the output depends on the clarity of the instructions in the prompt.
Use `web_fetch` to summarize, compare, or extract information from web pages. The `web_fetch` tool processes content from one or more URLs (up to 20) embedded in a prompt. `web_fetch` takes a natural language prompt and returns a generated response.
### Arguments
`web_fetch` takes one argument:
- `prompt` (string, required): A comprehensive prompt that includes the URL(s) (up to 20) to fetch and specific instructions on how to process their content. For example: `"Summarize https://example.com/article and extract key points from https://another.com/data"`. The prompt must contain at least one URL starting with `http://` or `https://`.
## How to use `web_fetch` with the Gemini CLI
To use `web_fetch` with the Gemini CLI, provide a natural language prompt that contains URLs. The tool will ask for confirmation before fetching any URLs. Once confirmed, the tool will process URLs through Gemini API's `urlContext`.
If the Gemini API cannot access the URL, the tool will fall back to fetching content directly from the local machine. The tool will format the response, including source attribution and citations where possible. The tool will then provide the response to the user.
Usage:
```
web_fetch(prompt="Your prompt, including a URL such as https://google.com.")
```
## `web_fetch` examples
Summarize a single article:
```
web_fetch(prompt="Can you summarize the main points of https://example.com/news/latest")
```
Compare two articles:
```
web_fetch(prompt="What are the differences in the conclusions of these two papers: https://arxiv.org/abs/2401.0001 and https://arxiv.org/abs/2401.0002?")
```
## Important notes
- **URL processing:** `web_fetch` relies on the Gemini API's ability to access and process the given URLs.
- **Output quality:** The quality of the output will depend on the clarity of the instructions in the prompt.
+32 -16
View File
@@ -1,20 +1,36 @@
# Google Web Search Tool
# Web Search Tool (`google_web_search`)
This document describes the `google_web_search` tool.
## `google_web_search`
## Description
- **Purpose:** Performs a web search using Google Search (via the Gemini API) and returns a summary of the results with sources. This tool is useful for finding up-to-date information on the internet.
- **Arguments:**
- `query` (string, required): The search query.
- **Behavior:**
- The tool sends the query to the Gemini API, which performs a Google Search.
- It returns a generated response based on the search results, complete with citations and a list of sources.
- **Examples:**
- Getting information on a topic:
```
google_web_search(query="latest advancements in AI-powered code generation")
```
- **Important Notes:**
- The tool returns a processed summary, not a raw list of search results.
- The response includes citations to the sources used to generate the summary.
Use `google_web_search` to perform a web search using Google Search via the Gemini API. The `google_web_search` tool returns a summary of web results with sources.
### Arguments
`google_web_search` takes one argument:
- `query` (string, required): The search query.
## How to use `google_web_search` with the Gemini CLI
The `google_web_search` tool sends a query to the Gemini API, which then performs a web search. `google_web_search` will return a generated response based on the search results, including citations and sources.
Usage:
```
google_web_search(query="Your query goes here.")
```
## `google_web_search` examples
Get information on a topic:
```
google_web_search(query="latest advancements in AI-powered code generation")
```
## Important notes
- **Response returned:** The `google_web_search` tool returns a processed summary, not a raw list of search results.
- **Citations:** The response includes citations to the sources used to generate the summary.
+93
View File
@@ -0,0 +1,93 @@
# Gemini CLI: Terms of Service and Privacy Notice
Gemini CLI is an open-source tool that lets you interact with Google's powerful language models directly from your command-line interface. The Terms of Service and Privacy Notices that apply to your usage of the Gemini CLI depend on the type of account you use to authenticate with Google.
This article outlines the specific terms and privacy policies applicable for different account types and authentication methods. Note: See [quotas and pricing](./quota-and-pricing.md) for the quota and pricing details that apply to your usage of the Gemini CLI.
## How to determine your authentication method
Your authentication method refers to the method you use to log into and access the Gemini CLI. There are four ways to authenticate:
- Logging in with your Google account to Gemini Code Assist for Individuals
- Logging in with your Google account to Gemini Code Assist for Workspace, Standard, or Enterprise Users
- Using an API key with Gemini Developer
- Using an API key with Vertex AI GenAI API
For each of these four methods of authentication, different Terms of Service and Privacy Notices may apply.
| Authentication | Account | Terms of Service | Privacy Notice |
| :---------------------------- | :------------------ | :------------------------------------------------------------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Gemini Code Assist via Google | Individual | [Google Terms of Service](https://policies.google.com/terms?hl=en-US) | [Gemini Code Assist Privacy Notice for Individuals](https://developers.google.com/gemini-code-assist/resources/privacy-notice-gemini-code-assist-individuals) |
| Gemini Code Assist via Google | Standard/Enterprise | [Google Cloud Platform Terms of Service](https://cloud.google.com/terms) | [Gemini Code Assist Privacy Notice for Standard and Enterprise](https://cloud.google.com/gemini/docs/codeassist/security-privacy-compliance#standard_and_enterprise_data_protection_and_privacy) |
| Gemini Developer API | Unpaid | [Gemini API Terms of Service - Unpaid Services](https://ai.google.dev/gemini-api/terms#unpaid-services) | [Google Privacy Policy](https://policies.google.com/privacy) |
| Gemini Developer API | Paid | [Gemini API Terms of Service - Paid Services](https://ai.google.dev/gemini-api/terms#paid-services) | [Google Privacy Policy](https://policies.google.com/privacy) |
| Vertex AI Gen API | | [Google Cloud Platform Service Terms](https://cloud.google.com/terms/service-terms/) | [Google Cloud Privacy Notice](https://cloud.google.com/terms/cloud-privacy-notice) |
## 1. If you have logged in with your Google account to Gemini Code Assist for Individuals
For users who use their Google account to access [Gemini Code Assist for Individuals](https://developers.google.com/gemini-code-assist/docs/overview#supported-features-gca), these Terms of Service and Privacy Notice documents apply:
- **Terms of Service:** Your use of the Gemini CLI is governed by the [Google Terms of Service](https://policies.google.com/terms?hl=en-US).
- **Privacy Notice:** The collection and use of your data is described in the [Gemini Code Assist Privacy Notice for Individuals](https://developers.google.com/gemini-code-assist/resources/privacy-notice-gemini-code-assist-individuals).
## 2. If you have logged in with your Google account to Gemini Code Assist for Workspace, Standard, or Enterprise Users
For users who use their Google account to access the [Standard or Enterprise edition](https://cloud.google.com/gemini/docs/codeassist/overview#editions-overview) of Gemini Code Assist, these Terms of Service and Privacy Notice documents apply:
- **Terms of Service:** Your use of the Gemini CLI is governed by the [Google Cloud Platform Terms of Service](https://cloud.google.com/terms).
- **Privacy Notice:** The collection and use of your data is described in the [Gemini Code Assist Privacy Notices for Standard and Enterprise Users](https://cloud.google.com/gemini/docs/codeassist/security-privacy-compliance#standard_and_enterprise_data_protection_and_privacy).
## 3. If you have logged in with a Gemini API key to the Gemini Developer API
If you are using a Gemini API key for authentication with the [Gemini Developer API](https://ai.google.dev/gemini-api/docs), these Terms of Service and Privacy Notice documents apply:
- **Terms of Service:** Your use of the Gemini CLI is governed by the [Gemini API Terms of Service](https://ai.google.dev/gemini-api/terms). These terms may differ depending on whether you are using an unpaid or paid service:
- For unpaid services, refer to the [Gemini API Terms of Service - Unpaid Services](https://ai.google.dev/gemini-api/terms#unpaid-services).
- For paid services, refer to the [Gemini API Terms of Service - Paid Services](https://ai.google.dev/gemini-api/terms#paid-services).
- **Privacy Notice:** The collection and use of your data is described in the [Google Privacy Policy](https://policies.google.com/privacy).
## 4. If you have logged in with a Gemini API key to the Vertex AI GenAI API
If you are using a Gemini API key for authentication with a [Vertex AI GenAI API](https://cloud.google.com/vertex-ai/generative-ai/docs/reference/rest) backend, these Terms of Service and Privacy Notice documents apply:
- **Terms of Service:** Your use of the Gemini CLI is governed by the [Google Cloud Platform Service Terms](https://cloud.google.com/terms/service-terms/).
- **Privacy Notice:** The collection and use of your data is described in the [Google Cloud Privacy Notice](https://cloud.google.com/terms/cloud-privacy-notice).
### Usage Statistics Opt-Out
You may opt-out from sending Usage Statistics to Google by following the instructions available here: [Usage Statistics Configuration](./cli/configuration.md#usage-statistics).
## Frequently Asked Questions (FAQ) for the Gemini CLI
### 1. Is my code, including prompts and answers, used to train Google's models?
Whether your code, including prompts and answers, is used to train Google's models depends on the type of authentication method you use and your account type.
By default (if you have not opted out):
- **Google account with Gemini Code Assist for Individuals**: Yes. When you use your personal Google account, the [Gemini Code Assist Privacy Notice for Individuals](https://developers.google.com/gemini-code-assist/resources/privacy-notice-gemini-code-assist-individuals) applies. Under this notice,
your **prompts, answers, and related code are collected** and may be used to improve Google's products, including for model training.
- **Google account with Gemini Code Assist for Workspace, Standard, or Enterprise**: No. For these accounts, your data is governed by the [Gemini Code Assist Privacy Notices](https://cloud.google.com/gemini/docs/codeassist/security-privacy-compliance#standard_and_enterprise_data_protection_and_privacy) terms, which treat your inputs as confidential. Your **prompts, answers, and related code are not collected** and are not used to train models.
- **Gemini API key via the Gemini Developer API**: Whether your code is collected or used depends on whether you are using an unpaid or paid service.
- **Unpaid services**: Yes. When you use the Gemini API key via the Gemini Developer API with an unpaid service, the [Gemini API Terms of Service - Unpaid Services](https://ai.google.dev/gemini-api/terms#unpaid-services) terms apply. Under this notice, your **prompts, answers, and related code are collected** and may be used to improve Google's products, including for model training.
- **Paid services**: No. When you use the Gemini API key via the Gemini Developer API with a paid service, the [Gemini API Terms of Service - Paid Services](https://ai.google.dev/gemini-api/terms#paid-services) terms apply, which treats your inputs as confidential. Your **prompts, answers, and related code are not collected** and are not used to train models.
- **Gemini API key via the Vertex AI GenAI API**: No. For these accounts, your data is governed by the [Google Cloud Privacy Notice](https://cloud.google.com/terms/cloud-privacy-notice) terms, which treat your inputs as confidential. Your **prompts, answers, and related code are not collected** and are not used to train models.
For more information about opting out, refer to the next question.
### 2. What are Usage Statistics and what does the opt-out control?
The **Usage Statistics** setting is the single control for all optional data collection in the Gemini CLI.
The data it collects depends on your account and authentication type:
- **Google account with Gemini Code Assist for Individuals**: When enabled, this setting allows Google to collect both anonymous telemetry (for example, commands run and performance metrics) and **your prompts and answers, including code,** for model improvement.
- **Google account with Gemini Code Assist for Workspace, Standard, or Enterprise**: This setting only controls the collection of anonymous telemetry. Your prompts and answers, including code, are never collected, regardless of this setting.
- **Gemini API key via the Gemini Developer API**:
**Unpaid services**: When enabled, this setting allows Google to collect both anonymous telemetry (like commands run and performance metrics) and **your prompts and answers, including code,** for model improvement. When disabled we will use your data as described in [How Google Uses Your Data](https://ai.google.dev/gemini-api/terms#data-use-unpaid).
**Paid services**: This setting only controls the collection of anonymous telemetry. Google logs prompts and responses for a limited period of time, solely for the purpose of detecting violations of the Prohibited Use Policy and any required legal or regulatory disclosures.
- **Gemini API key via the Vertex AI GenAI API:** This setting only controls the collection of anonymous telemetry. Your prompts and answers, including code, are never collected, regardless of this setting.
Please refer to the Privacy Notice that applies to your authentication method for more information about what data is collected and how this data is used.
You can disable Usage Statistics for any account type by following the instructions in the [Usage Statistics Configuration](./cli/configuration.md#usage-statistics) documentation.
+49 -21
View File
@@ -1,65 +1,93 @@
# Troubleshooting Guide
# Troubleshooting guide
This guide provides solutions to common issues and debugging tips.
This guide provides solutions to common issues and debugging tips, including topics on:
- Authentication or login errors
- Frequently asked questions (FAQs)
- Debugging tips
- Existing GitHub Issues similar to yours or creating new Issues
## Authentication or login errors
- **Error: `Failed to login. Message: Request contains an invalid argument`**
- Users with Google Workspace accounts or Google Cloud accounts
associated with their Gmail accounts may not be able to activate the free
tier of the Google Code Assist plan.
- For Google Cloud accounts, you can work around this by setting
`GOOGLE_CLOUD_PROJECT` to your project ID.
- Alternatively, you can obtain the Gemini API key from
[Google AI Studio](http://aistudio.google.com/app/apikey), which also includes a
separate free tier.
## Frequently asked questions (FAQs)
- **Q: How do I update Gemini CLI to the latest version?**
- A: If you installed it globally via `npm`, update it using the command `npm install -g @google/gemini-cli@latest`. If you compiled it from source, pull the latest changes from the repository, and then rebuild using the command `npm run build`.
- A: If installed globally via npm, update Gemini CLI using the command `npm install -g @gemini-cli/cli@latest`. If run from source, pull the latest changes from the repository and rebuild using `npm run build`.
- **Q: Where are the Gemini CLI configuration or settings files stored?**
- A: The Gemini CLI configuration is stored in two `settings.json` files:
1. In your home directory: `~/.gemini/settings.json`.
2. In your project's root directory: `./.gemini/settings.json`.
- **Q: Where are Gemini CLI configuration files stored?**
Refer to [Gemini CLI Configuration](./cli/configuration.md) for more details.
- A: The CLI configuration is stored within two `settings.json` files: one in your home directory and one in your project's root directory. In both locations, `settings.json` is found in the `.gemini/` folder. Refer to [CLI Configuration](./cli/configuration.md) for more details.
- **Q: Why don't I see cached token counts in my stats output?**
- A: Cached token information is only displayed when cached tokens are being used. This feature is available for API key users (Gemini API key or Google Cloud Vertex AI) but not for OAuth users (such as Google Personal/Enterprise accounts like Google Gmail or Google Workspace, respectively). This is because the Gemini Code Assist API does not support cached content creation. You can still view your total token usage using the `/stats` command in Gemini CLI.
## Common error messages and solutions
- **Error: `EADDRINUSE` (Address already in use) when starting an MCP server.**
- **Cause:** Another process is already using the port the MCP server is trying to bind to.
- **Cause:** Another process is already using the port that the MCP server is trying to bind to.
- **Solution:**
Either stop the other process that is using the port or configure the MCP server to use a different port.
- **Error: Command not found (when attempting the run Gemini CLI).**
- **Cause:** Gemini CLI is not correctly installed or not in your system's PATH.
- **Error: Command not found (when attempting to run Gemini CLI with `gemini`).**
- **Cause:** Gemini CLI is not correctly installed or it is not in your system's `PATH`.
- **Solution:**
1. Ensure Gemini CLI installation was successful.
2. If installed globally, check that your npm global binary directory is in your PATH.
3. If running from source, ensure you are using the correct command to invoke it (e.g., `node packages/cli/dist/index.js ...`).
The update depends on how you installed Gemini CLI:
- If you installed `gemini` globally, check that your `npm` global binary directory is in your `PATH`. You can update Gemini CLI using the command `npm install -g @google/gemini-cli@latest`.
- If you are running `gemini` from source, ensure you are using the correct command to invoke it (e.g., `node packages/cli/dist/index.js ...`). To update Gemini CLI, pull the latest changes from the repository, and then rebuild using the command `npm run build`.
- **Error: `MODULE_NOT_FOUND` or import errors.**
- **Cause:** Dependencies are not installed correctly, or the project hasn't been built.
- **Solution:**
1. Run `npm install` to ensure all dependencies are present.
2. Run `npm run build` to compile the project.
3. Verify that the build completed successfully with `npm run start`.
- **Error: "Operation not permitted", "Permission denied", or similar.**
- **Cause:** When sandboxing is enabled, Gemini CLI may attempt operations that are restricted by your sandbox configuration, such as writing outside the project directory or system temp directory.
- **Solution:** Refer to the [Configuration: Sandboxing](./cli/configuration.md#sandboxing) documentation for more information, including how to customize your sandbox configuration.
- **Cause:** If sandboxing is enabled, then the application is likely attempting an operation restricted by your sandbox, such as writing outside the project directory or system temp directory.
- **Solution:** See [Sandboxing](./cli/configuration.md#sandboxing) for more information, including how to customize your sandbox configuration.
- **Gemini CLI is not running in interactive mode in "CI" environments**
- **Issue:** The Gemini CLI does not enter interactive mode (no prompt appears) if an environment variable starting with `CI_` (e.g., `CI_TOKEN`) is set. This is because the `is-in-ci` package, used by the underlying UI framework, detects these variables and assumes a non-interactive CI environment.
- **Cause:** The `is-in-ci` package checks for the presence of `CI`, `CONTINUOUS_INTEGRATION`, or any environment variable with a `CI_` prefix. When any of these are found, it signals that the environment is non-interactive, which prevents the Gemini CLI from starting in its interactive mode.
- **Solution:** If the `CI_` prefixed variable is not needed for the CLI to function, you can temporarily unset it for the command. e.g., `env -u CI_TOKEN gemini`
- **DEBUG mode not working from project .env file**
- **Issue:** Setting `DEBUG=true` in a project's `.env` file doesn't enable debug mode for gemini-cli.
- **Cause:** The `DEBUG` and `DEBUG_MODE` variables are automatically excluded from project `.env` files to prevent interference with gemini-cli behavior.
- **Solution:** Use a `.gemini/.env` file instead, or configure the `excludedProjectEnvVars` setting in your `settings.json` to exclude fewer variables.
## Debugging Tips
- **CLI debugging:**
- Use the `--verbose` flag (if available) with CLI commands for more detailed output.
- Check the CLI logs, often found in a user-specific configuration or cache directory.
- **Core debugging:**
- Check the server console output for error messages or stack traces.
- Increase log verbosity if configurable.
- Use Node.js debugging tools (e.g., `node --inspect`) if you need to step through server-side code.
- **Tool issues:**
- If a specific tool is failing, try to isolate the issue by running the simplest possible version of the command or operation the tool performs.
- For `run_shell_command`, check that the command works directly in your shell first.
- For file system tools, double-check paths and permissions.
- For _file system tools_, verify that paths are correct and check the permissions.
- **Pre-flight checks:**
- Always run `npm run preflight` before committing code. This can catch many common issues related to formatting, linting, and type errors.
If you encounter an issue not covered here, consider searching the project's issue tracker on GitHub or reporting a new issue with detailed information.
## Existing GitHub Issues similar to yours or creating new Issues
If you encounter an issue that was not covered here in this _Troubleshooting guide_, consider searching the Gemini CLI [Issue tracker on GitHub](https://github.com/google-gemini/gemini-cli/issues). If you can't find an issue similar to yours, consider creating a new GitHub Issue with a detailed description. Pull requests are also welcome!
+7
View File
@@ -21,6 +21,13 @@ esbuild
outfile: 'bundle/gemini.js',
platform: 'node',
format: 'esm',
external: [],
alias: {
'is-in-ci': path.resolve(
__dirname,
'packages/cli/src/patches/is-in-ci.ts',
),
},
define: {
'process.env.CLI_VERSION': JSON.stringify(pkg.version),
},
@@ -1,159 +0,0 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @fileoverview Disallows relative imports between specified monorepo packages.
*/
'use strict';
import path from 'node:path';
import fs from 'node:fs';
/**
* Finds the package name by searching for the nearest `package.json` file
* in the directory hierarchy, starting from the given file's directory
* and moving upwards until the specified root directory is reached.
* It reads the `package.json` and extracts the `name` property.
*
* @requires module:path Node.js path module
* @requires module:fs Node.js fs module
*
* @param {string} filePath - The path (absolute or relative) to a file within the potential package structure.
* The search starts from the directory containing this file.
* @param {string} root - The absolute path to the root directory of the project/monorepo.
* The upward search stops when this directory is reached.
* @returns {string | undefined | null} The value of the `name` field from the first `package.json` found.
* Returns `undefined` if the `name` field doesn't exist in the found `package.json`.
* Returns `null` if no `package.json` is found before reaching the `root` directory.
* @throws {Error} Can throw an error if `fs.readFileSync` fails (e.g., permissions) or if `JSON.parse` fails on invalid JSON content.
*/
function findPackageName(filePath, root) {
let currentDir = path.dirname(path.resolve(filePath));
while (currentDir !== root) {
const parentDir = path.dirname(currentDir);
const packageJsonPath = path.join(currentDir, 'package.json');
if (fs.existsSync(packageJsonPath)) {
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
return pkg.name;
}
// Move up one level
currentDir = parentDir;
// Safety break if we somehow reached the root directly in the loop condition (less likely with path.resolve)
if (path.dirname(currentDir) === currentDir) break;
}
return null; // Not found within the expected structure
}
export default {
meta: {
type: 'problem',
docs: {
description: 'Disallow relative imports between packages.',
category: 'Best Practices',
recommended: 'error',
},
fixable: 'code',
schema: [
{
type: 'object',
properties: {
root: {
type: 'string',
description:
'Absolute path to the root of all relevant packages to consider.',
},
},
required: ['root'],
additionalProperties: false,
},
],
messages: {
noRelativePathsForCrossPackageImport:
"Relative import '{{importedPath}}' crosses package boundary from '{{importingPackage}}' to '{{importedPackage}}'. Use a direct package import ('{{importedPackage}}') instead.",
relativeImportIsInvalidPackage:
"Relative import '{{importedPath}}' does not reference a valid package. All source must be in a package directory.",
},
},
create(context) {
const options = context.options[0] || {};
const allPackagesRoot = options.root;
const currentFilePath = context.filename;
if (
!currentFilePath ||
currentFilePath === '<input>' ||
currentFilePath === '<text>'
) {
// Skip if filename is not available (e.g., linting raw text)
return {};
}
const currentPackage = findPackageName(currentFilePath, allPackagesRoot);
// If the current file isn't inside a package structure, don't apply the rule
if (!currentPackage) {
return {};
}
return {
ImportDeclaration(node) {
const importingPackage = currentPackage;
const importedPath = node.source.value;
// Only interested in relative paths
if (
!importedPath ||
typeof importedPath !== 'string' ||
!importedPath.startsWith('.')
) {
return;
}
// Resolve the absolute path of the imported module
const absoluteImportPath = path.resolve(
path.dirname(currentFilePath),
importedPath,
);
// Find the package information for the imported file
const importedPackage = findPackageName(
absoluteImportPath,
allPackagesRoot,
);
// If the imported file isn't in a recognized package, report issue
if (!importedPackage) {
context.report({
node: node.source,
messageId: 'relativeImportIsInvalidPackage',
data: { importedPath: importedPath },
});
return;
}
// The core check: Are the source and target packages different?
if (currentPackage !== importedPackage) {
// We found a relative import crossing package boundaries
context.report({
node: node.source, // Report the error on the source string literal
messageId: 'noRelativePathsForCrossPackageImport',
data: {
importedPath,
importedPackage,
importingPackage,
},
fix(fixer) {
return fixer.replaceText(node.source, `'${importedPackage}'`);
},
});
}
},
};
},
};
+46 -22
View File
@@ -12,7 +12,6 @@ import prettierConfig from 'eslint-config-prettier';
import importPlugin from 'eslint-plugin-import';
import globals from 'globals';
import licenseHeader from 'eslint-plugin-license-header';
import noRelativeCrossPackageImports from './eslint-rules/no-relative-cross-package-imports.js';
import path from 'node:path'; // Use node: prefix for built-ins
import url from 'node:url';
@@ -33,8 +32,9 @@ export default tseslint.config(
'packages/cli/dist/**',
'packages/core/dist/**',
'packages/server/dist/**',
'eslint-rules/*',
'packages/vscode-ide-companion/dist/**',
'bundle/**',
'package/bundle/**',
],
},
eslint.configs.recommended,
@@ -71,6 +71,14 @@ export default tseslint.config(
{
// General overrides and rules for the project (TS/TSX files)
files: ['packages/*/src/**/*.{ts,tsx}'], // Target only TS/TSX in the cli package
plugins: {
import: importPlugin,
},
settings: {
'import/resolver': {
node: true,
},
},
languageOptions: {
globals: {
...globals.node,
@@ -105,6 +113,13 @@ export default tseslint.config(
caughtErrorsIgnorePattern: '^_',
},
],
'import/no-internal-modules': [
'error',
{
allow: ['react-dom/test-utils', 'memfs/lib/volume.js', 'yargs/**'],
},
],
'import/no-relative-packages': 'error',
'no-cond-assign': 'error',
'no-debugger': 'error',
'no-duplicate-case': 'error',
@@ -175,6 +190,35 @@ export default tseslint.config(
],
},
},
{
files: ['packages/vscode-ide-companion/esbuild.js'],
languageOptions: {
globals: {
...globals.node,
process: 'readonly',
console: 'readonly',
},
},
rules: {
'no-restricted-syntax': 'off',
'@typescript-eslint/no-require-imports': 'off',
},
},
// extra settings for scripts that we run directly with node
{
files: ['packages/vscode-ide-companion/scripts/**/*.js'],
languageOptions: {
globals: {
...globals.node,
process: 'readonly',
console: 'readonly',
},
},
rules: {
'no-restricted-syntax': 'off',
'@typescript-eslint/no-require-imports': 'off',
},
},
// Prettier config must be last
prettierConfig,
// extra settings for scripts that we run directly with node
@@ -198,24 +242,4 @@ export default tseslint.config(
],
},
},
// Custom eslint rules for this repo
{
files: ['packages/**/*.{js,jsx,ts,tsx}'],
plugins: {
custom: {
rules: {
'no-relative-cross-package-imports': noRelativeCrossPackageImports,
},
},
},
rules: {
// Enable and configure your custom rule
'custom/no-relative-cross-package-imports': [
'error',
{
root: path.join(projectRoot, 'packages'),
},
],
},
},
);
+68 -9
View File
@@ -6,25 +6,84 @@
import { strict as assert } from 'assert';
import { test } from 'node:test';
import { TestRig } from './test-helper.js';
import { TestRig, printDebugInfo, validateModelOutput } from './test-helper.js';
test('reads a file', (t) => {
test('should be able to read a file', async () => {
const rig = new TestRig();
rig.setup(t.name);
await rig.setup('should be able to read a file');
rig.createFile('test.txt', 'hello world');
const output = rig.run(`read the file name test.txt`);
const result = await rig.run(
`read the file test.txt and show me its contents`,
);
assert.ok(output.toLowerCase().includes('hello'));
const foundToolCall = await rig.waitForToolCall('read_file');
// Add debugging information
if (!foundToolCall || !result.includes('hello world')) {
printDebugInfo(rig, result, {
'Found tool call': foundToolCall,
'Contains hello world': result.includes('hello world'),
});
}
assert.ok(foundToolCall, 'Expected to find a read_file tool call');
// Validate model output - will throw if no output, warn if missing expected content
validateModelOutput(result, 'hello world', 'File read test');
});
test('writes a file', (t) => {
test('should be able to write a file', async () => {
const rig = new TestRig();
rig.setup(t.name);
await rig.setup('should be able to write a file');
rig.createFile('test.txt', '');
rig.run(`edit test.txt to have a hello world message`);
const result = await rig.run(`edit test.txt to have a hello world message`);
// Accept multiple valid tools for editing files
const foundToolCall = await rig.waitForAnyToolCall([
'write_file',
'edit',
'replace',
]);
// Add debugging information
if (!foundToolCall) {
printDebugInfo(rig, result);
}
assert.ok(
foundToolCall,
'Expected to find a write_file, edit, or replace tool call',
);
// Validate model output - will throw if no output
validateModelOutput(result, null, 'File write test');
const fileContent = rig.readFile('test.txt');
assert.ok(fileContent.toLowerCase().includes('hello'));
// Add debugging for file content
if (!fileContent.toLowerCase().includes('hello')) {
const writeCalls = rig
.readToolLogs()
.filter((t) => t.toolRequest.name === 'write_file')
.map((t) => t.toolRequest.args);
printDebugInfo(rig, result, {
'File content mismatch': true,
'Expected to contain': 'hello',
'Actual content': fileContent,
'Write tool calls': JSON.stringify(writeCalls),
});
}
assert.ok(
fileContent.toLowerCase().includes('hello'),
'Expected file to contain hello',
);
// Log success info if verbose
if (process.env.VERBOSE === 'true') {
console.log('File written successfully with hello message.');
}
});
+61 -6
View File
@@ -6,14 +6,69 @@
import { test } from 'node:test';
import { strict as assert } from 'assert';
import { TestRig } from './test-helper.js';
import { TestRig, printDebugInfo, validateModelOutput } from './test-helper.js';
test('should be able to search the web', async (t) => {
test('should be able to search the web', async () => {
const rig = new TestRig();
rig.setup(t.name);
await rig.setup('should be able to search the web');
const prompt = `what planet do we live on`;
const result = await rig.run(prompt);
let result;
try {
result = await rig.run(`what is the weather in London`);
} catch (error) {
// Network errors can occur in CI environments
if (
error.message.includes('network') ||
error.message.includes('timeout')
) {
console.warn('Skipping test due to network error:', error.message);
return; // Skip the test
}
throw error; // Re-throw if not a network error
}
assert.ok(result.toLowerCase().includes('earth'));
const foundToolCall = await rig.waitForToolCall('google_web_search');
// Add debugging information
if (!foundToolCall) {
const allTools = printDebugInfo(rig, result);
// Check if the tool call failed due to network issues
const failedSearchCalls = allTools.filter(
(t) =>
t.toolRequest.name === 'google_web_search' && !t.toolRequest.success,
);
if (failedSearchCalls.length > 0) {
console.warn(
'google_web_search tool was called but failed, possibly due to network issues',
);
console.warn(
'Failed calls:',
failedSearchCalls.map((t) => t.toolRequest.args),
);
return; // Skip the test if network issues
}
}
assert.ok(foundToolCall, 'Expected to find a call to google_web_search');
// Validate model output - will throw if no output, warn if missing expected content
const hasExpectedContent = validateModelOutput(
result,
['weather', 'london'],
'Google web search test',
);
// If content was missing, log the search queries used
if (!hasExpectedContent) {
const searchCalls = rig
.readToolLogs()
.filter((t) => t.toolRequest.name === 'google_web_search');
if (searchCalls.length > 0) {
console.warn(
'Search queries used:',
searchCalls.map((t) => t.toolRequest.args),
);
}
}
});
+46 -6
View File
@@ -6,17 +6,57 @@
import { test } from 'node:test';
import { strict as assert } from 'assert';
import { TestRig } from './test-helper.js';
import { TestRig, printDebugInfo, validateModelOutput } from './test-helper.js';
import { existsSync } from 'fs';
import { join } from 'path';
test('should be able to list a directory', async (t) => {
test('should be able to list a directory', async () => {
const rig = new TestRig();
rig.setup(t.name);
await rig.setup('should be able to list a directory');
rig.createFile('file1.txt', 'file 1 content');
rig.mkdir('subdir');
rig.sync();
// Poll for filesystem changes to propagate in containers
await rig.poll(
() => {
// Check if the files exist in the test directory
const file1Path = join(rig.testDir, 'file1.txt');
const subdirPath = join(rig.testDir, 'subdir');
return existsSync(file1Path) && existsSync(subdirPath);
},
1000, // 1 second max wait
50, // check every 50ms
);
const prompt = `Can you list the files in the current directory. Display them in the style of 'ls'`;
const prompt = `Can you list the files in the current directory`;
const result = await rig.run(prompt);
assert.ok(result.includes('file1.txt'));
assert.ok(result.includes('subdir'));
const foundToolCall = await rig.waitForToolCall('list_directory');
// Add debugging information
if (
!foundToolCall ||
!result.includes('file1.txt') ||
!result.includes('subdir')
) {
const allTools = printDebugInfo(rig, result, {
'Found tool call': foundToolCall,
'Contains file1.txt': result.includes('file1.txt'),
'Contains subdir': result.includes('subdir'),
});
console.error(
'List directory calls:',
allTools
.filter((t) => t.toolRequest.name === 'list_directory')
.map((t) => t.toolRequest.args),
);
}
assert.ok(foundToolCall, 'Expected to find a list_directory tool call');
// Validate model output - will throw if no output, warn if missing expected content
validateModelOutput(result, ['file1.txt', 'subdir'], 'List directory test');
});
+34 -6
View File
@@ -6,17 +6,45 @@
import { test } from 'node:test';
import { strict as assert } from 'assert';
import { TestRig } from './test-helper.js';
import { TestRig, printDebugInfo, validateModelOutput } from './test-helper.js';
test.skip('should be able to read multiple files', async (t) => {
test('should be able to read multiple files', async () => {
const rig = new TestRig();
rig.setup(t.name);
await rig.setup('should be able to read multiple files');
rig.createFile('file1.txt', 'file 1 content');
rig.createFile('file2.txt', 'file 2 content');
const prompt = `Read the files in this directory, list them and print them to the screen`;
const prompt = `Please use read_many_files to read file1.txt and file2.txt and show me what's in them`;
const result = await rig.run(prompt);
assert.ok(result.includes('file 1 content'));
assert.ok(result.includes('file 2 content'));
// Check for either read_many_files or multiple read_file calls
const allTools = rig.readToolLogs();
const readManyFilesCall = await rig.waitForToolCall('read_many_files');
const readFileCalls = allTools.filter(
(t) => t.toolRequest.name === 'read_file',
);
// Accept either read_many_files OR at least 2 read_file calls
const foundValidPattern = readManyFilesCall || readFileCalls.length >= 2;
// Add debugging information
if (!foundValidPattern) {
printDebugInfo(rig, result, {
'read_many_files called': readManyFilesCall,
'read_file calls': readFileCalls.length,
});
}
assert.ok(
foundValidPattern,
'Expected to find either read_many_files or multiple read_file tool calls',
);
// Validate model output - will throw if no output, warn if missing expected content
validateModelOutput(
result,
['file 1 content', 'file 2 content'],
'Read many files test',
);
});
+50 -6
View File
@@ -6,17 +6,61 @@
import { test } from 'node:test';
import { strict as assert } from 'assert';
import { TestRig } from './test-helper.js';
import { TestRig, printDebugInfo, validateModelOutput } from './test-helper.js';
test('should be able to replace content in a file', async (t) => {
test('should be able to replace content in a file', async () => {
const rig = new TestRig();
rig.setup(t.name);
await rig.setup('should be able to replace content in a file');
const fileName = 'file_to_replace.txt';
rig.createFile(fileName, 'original content');
const originalContent = 'original content';
const expectedContent = 'replaced content';
rig.createFile(fileName, originalContent);
const prompt = `Can you replace 'original' with 'replaced' in the file 'file_to_replace.txt'`;
await rig.run(prompt);
const result = await rig.run(prompt);
const foundToolCall = await rig.waitForToolCall('replace');
// Add debugging information
if (!foundToolCall) {
printDebugInfo(rig, result);
}
assert.ok(foundToolCall, 'Expected to find a replace tool call');
// Validate model output - will throw if no output, warn if missing expected content
validateModelOutput(
result,
['replaced', 'file_to_replace.txt'],
'Replace content test',
);
const newFileContent = rig.readFile(fileName);
assert.strictEqual(newFileContent, 'replaced content');
// Add debugging for file content
if (newFileContent !== expectedContent) {
console.error('File content mismatch - Debug info:');
console.error('Expected:', expectedContent);
console.error('Actual:', newFileContent);
console.error(
'Tool calls:',
rig.readToolLogs().map((t) => ({
name: t.toolRequest.name,
args: t.toolRequest.args,
})),
);
}
assert.strictEqual(
newFileContent,
expectedContent,
'File content should be updated correctly',
);
// Log success info if verbose
if (process.env.VERBOSE === 'true') {
console.log('File replaced successfully. New content:', newFileContent);
}
});
+91 -32
View File
@@ -4,8 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { spawnSync } from 'child_process';
import { spawn } from 'child_process';
import { spawnSync, spawn } from 'child_process';
import { mkdirSync, rmSync, createWriteStream } from 'fs';
import { join, dirname, basename } from 'path';
import { fileURLToPath } from 'url';
@@ -62,6 +61,7 @@ async function main() {
console.log(`\tFound test file: ${testFileName}`);
}
const MAX_RETRIES = 3;
let allTestsPassed = true;
for (const testFile of testFiles) {
@@ -73,39 +73,98 @@ async function main() {
`------------- Running test file: ${testFileName} ------------------------------`,
);
const child = spawn('node', ['--test', testFile], {
stdio: 'pipe',
env: {
...process.env,
GEMINI_CLI_INTEGRATION_TEST: 'true',
INTEGRATION_TEST_FILE_DIR: testFileDir,
KEEP_OUTPUT: keepOutput.toString(),
TEST_FILE_NAME: testFileName,
},
});
let attempt = 0;
let testFilePassed = false;
let lastStdout = [];
let lastStderr = [];
if (verbose) {
child.stdout.pipe(process.stdout);
child.stderr.pipe(process.stderr);
while (attempt < MAX_RETRIES && !testFilePassed) {
attempt++;
if (attempt > 1) {
console.log(
`--- Retrying ${testFileName} (attempt ${attempt} of ${MAX_RETRIES}) ---`,
);
}
const nodeArgs = ['--test'];
if (verbose) {
nodeArgs.push('--test-reporter=spec');
}
nodeArgs.push(testFile);
const child = spawn('node', nodeArgs, {
stdio: 'pipe',
env: {
...process.env,
GEMINI_CLI_INTEGRATION_TEST: 'true',
INTEGRATION_TEST_FILE_DIR: testFileDir,
KEEP_OUTPUT: keepOutput.toString(),
VERBOSE: verbose.toString(),
TEST_FILE_NAME: testFileName,
TELEMETRY_LOG_FILE: join(testFileDir, 'telemetry.log'),
},
});
let outputStream;
if (keepOutput) {
const outputFile = join(testFileDir, `output-attempt-${attempt}.log`);
outputStream = createWriteStream(outputFile);
console.log(`Output for ${testFileName} written to: ${outputFile}`);
}
const stdout = [];
const stderr = [];
child.stdout.on('data', (data) => {
if (verbose) {
process.stdout.write(data);
} else {
stdout.push(data);
}
if (outputStream) {
outputStream.write(data);
}
});
child.stderr.on('data', (data) => {
if (verbose) {
process.stderr.write(data);
} else {
stderr.push(data);
}
if (outputStream) {
outputStream.write(data);
}
});
const exitCode = await new Promise((resolve) => {
child.on('close', (code) => {
if (outputStream) {
outputStream.end(() => {
resolve(code);
});
} else {
resolve(code);
}
});
});
if (exitCode === 0) {
testFilePassed = true;
} else {
lastStdout = stdout;
lastStderr = stderr;
}
}
if (keepOutput) {
const outputFile = join(testFileDir, 'output.log');
const outputStream = createWriteStream(outputFile);
child.stdout.pipe(outputStream);
child.stderr.pipe(outputStream);
console.log(`Output for ${testFileName} written to: ${outputFile}`);
} else if (!verbose) {
child.stdout.pipe(process.stdout);
child.stderr.pipe(process.stderr);
}
const exitCode = await new Promise((resolve) => {
child.on('close', resolve);
});
if (exitCode !== 0) {
console.error(`Test file failed: ${testFileName}`);
if (!testFilePassed) {
console.error(
`Test file failed after ${MAX_RETRIES} attempts: ${testFileName}`,
);
if (!verbose) {
process.stdout.write(Buffer.concat(lastStdout).toString('utf8'));
process.stderr.write(Buffer.concat(lastStderr).toString('utf8'));
}
allTestsPassed = false;
}
}
+49 -6
View File
@@ -6,15 +6,58 @@
import { test } from 'node:test';
import { strict as assert } from 'assert';
import { TestRig } from './test-helper.js';
import { TestRig, printDebugInfo, validateModelOutput } from './test-helper.js';
test('should be able to run a shell command', async (t) => {
test('should be able to run a shell command', async () => {
const rig = new TestRig();
rig.setup(t.name);
rig.createFile('blah.txt', 'some content');
await rig.setup('should be able to run a shell command');
const prompt = `Please run the command "echo hello-world" and show me the output`;
const prompt = `Can you use ls to list the contexts of the current folder`;
const result = await rig.run(prompt);
assert.ok(result.includes('blah.txt'));
const foundToolCall = await rig.waitForToolCall('run_shell_command');
// Add debugging information
if (!foundToolCall || !result.includes('hello-world')) {
printDebugInfo(rig, result, {
'Found tool call': foundToolCall,
'Contains hello-world': result.includes('hello-world'),
});
}
assert.ok(foundToolCall, 'Expected to find a run_shell_command tool call');
// Validate model output - will throw if no output, warn if missing expected content
// Model often reports exit code instead of showing output
validateModelOutput(
result,
['hello-world', 'exit code 0'],
'Shell command test',
);
});
test('should be able to run a shell command via stdin', async () => {
const rig = new TestRig();
await rig.setup('should be able to run a shell command via stdin');
const prompt = `Please run the command "echo test-stdin" and show me what it outputs`;
const result = await rig.run({ stdin: prompt });
const foundToolCall = await rig.waitForToolCall('run_shell_command');
// Add debugging information
if (!foundToolCall || !result.includes('test-stdin')) {
printDebugInfo(rig, result, {
'Test type': 'Stdin test',
'Found tool call': foundToolCall,
'Contains test-stdin': result.includes('test-stdin'),
});
}
assert.ok(foundToolCall, 'Expected to find a run_shell_command tool call');
// Validate model output - will throw if no output, warn if missing expected content
validateModelOutput(result, 'test-stdin', 'Shell command stdin test');
});
+24 -4
View File
@@ -6,16 +6,36 @@
import { test } from 'node:test';
import { strict as assert } from 'assert';
import { TestRig } from './test-helper.js';
import { TestRig, printDebugInfo, validateModelOutput } from './test-helper.js';
test('should be able to save to memory', async (t) => {
test('should be able to save to memory', async () => {
const rig = new TestRig();
rig.setup(t.name);
await rig.setup('should be able to save to memory');
const prompt = `remember that my favorite color is blue.
what is my favorite color? tell me that and surround it with $ symbol`;
const result = await rig.run(prompt);
assert.ok(result.toLowerCase().includes('$blue$'));
const foundToolCall = await rig.waitForToolCall('save_memory');
// Add debugging information
if (!foundToolCall || !result.toLowerCase().includes('blue')) {
const allTools = printDebugInfo(rig, result, {
'Found tool call': foundToolCall,
'Contains blue': result.toLowerCase().includes('blue'),
});
console.error(
'Memory tool calls:',
allTools
.filter((t) => t.toolRequest.name === 'save_memory')
.map((t) => t.toolRequest.args),
);
}
assert.ok(foundToolCall, 'Expected to find a save_memory tool call');
// Validate model output - will throw if no output, warn if missing expected content
validateModelOutput(result, 'blue', 'Save memory test');
});
+184 -43
View File
@@ -4,67 +4,208 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { test, describe, before, after } from 'node:test';
/**
* This test verifies MCP (Model Context Protocol) server integration.
* It uses a minimal MCP server implementation that doesn't require
* external dependencies, making it compatible with Docker sandbox mode.
*/
import { test, describe, before } from 'node:test';
import { strict as assert } from 'node:assert';
import { TestRig } from './test-helper.js';
import { spawn } from 'child_process';
import { TestRig, validateModelOutput } from './test-helper.js';
import { join } from 'path';
import { fileURLToPath } from 'url';
import { writeFileSync, unlinkSync } from 'fs';
import { writeFileSync } from 'fs';
const __dirname = fileURLToPath(new URL('.', import.meta.url));
const serverScriptPath = join(__dirname, './temp-server.js');
const serverScript = `
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';
// Create a minimal MCP server that doesn't require external dependencies
// This implements the MCP protocol directly using Node.js built-ins
const serverScript = `#!/usr/bin/env node
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
const server = new McpServer({
name: 'addition-server',
version: '1.0.0',
const readline = require('readline');
const fs = require('fs');
// Debug logging to stderr (only when MCP_DEBUG or VERBOSE is set)
const debugEnabled = process.env.MCP_DEBUG === 'true' || process.env.VERBOSE === 'true';
function debug(msg) {
if (debugEnabled) {
fs.writeSync(2, \`[MCP-DEBUG] \${msg}\\n\`);
}
}
debug('MCP server starting...');
// Simple JSON-RPC implementation for MCP
class SimpleJSONRPC {
constructor() {
this.handlers = new Map();
this.rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
terminal: false
});
this.rl.on('line', (line) => {
debug(\`Received line: \${line}\`);
try {
const message = JSON.parse(line);
debug(\`Parsed message: \${JSON.stringify(message)}\`);
this.handleMessage(message);
} catch (e) {
debug(\`Parse error: \${e.message}\`);
}
});
}
send(message) {
const msgStr = JSON.stringify(message);
debug(\`Sending message: \${msgStr}\`);
process.stdout.write(msgStr + '\\n');
}
async handleMessage(message) {
if (message.method && this.handlers.has(message.method)) {
try {
const result = await this.handlers.get(message.method)(message.params || {});
if (message.id !== undefined) {
this.send({
jsonrpc: '2.0',
id: message.id,
result
});
}
} catch (error) {
if (message.id !== undefined) {
this.send({
jsonrpc: '2.0',
id: message.id,
error: {
code: -32603,
message: error.message
}
});
}
}
} else if (message.id !== undefined) {
this.send({
jsonrpc: '2.0',
id: message.id,
error: {
code: -32601,
message: 'Method not found'
}
});
}
}
on(method, handler) {
this.handlers.set(method, handler);
}
}
// Create MCP server
const rpc = new SimpleJSONRPC();
// Handle initialize
rpc.on('initialize', async (params) => {
debug('Handling initialize request');
return {
protocolVersion: '2024-11-05',
capabilities: {
tools: {}
},
serverInfo: {
name: 'addition-server',
version: '1.0.0'
}
};
});
server.registerTool(
'add',
{
title: 'Addition Tool',
description: 'Add two numbers',
inputSchema: { a: z.number(), b: z.number() },
},
async ({ a, b }) => ({
content: [{ type: 'text', text: String(a + b) }],
}),
);
// Handle tools/list
rpc.on('tools/list', async () => {
debug('Handling tools/list request');
return {
tools: [{
name: 'add',
description: 'Add two numbers',
inputSchema: {
type: 'object',
properties: {
a: { type: 'number', description: 'First number' },
b: { type: 'number', description: 'Second number' }
},
required: ['a', 'b']
}
}]
};
});
const transport = new StdioServerTransport();
await server.connect(transport);
// Handle tools/call
rpc.on('tools/call', async (params) => {
debug(\`Handling tools/call request for tool: \${params.name}\`);
if (params.name === 'add') {
const { a, b } = params.arguments;
return {
content: [{
type: 'text',
text: String(a + b)
}]
};
}
throw new Error('Unknown tool: ' + params.name);
});
// Send initialization notification
rpc.send({
jsonrpc: '2.0',
method: 'initialized'
});
`;
describe('simple-mcp-server', () => {
const rig = new TestRig();
let child;
before(() => {
writeFileSync(serverScriptPath, serverScript);
child = spawn('node', [serverScriptPath], {
stdio: ['pipe', 'pipe', 'pipe'],
before(async () => {
// Setup test directory with MCP server configuration
await rig.setup('simple-mcp-server', {
settings: {
mcpServers: {
'addition-server': {
command: 'node',
args: ['mcp-server.cjs'],
},
},
},
});
child.stderr.on('data', (data) => {
console.error(`stderr: ${data}`);
});
// Wait for the server to be ready
return new Promise((resolve) => setTimeout(resolve, 500));
// Create server script in the test directory
const testServerPath = join(rig.testDir, 'mcp-server.cjs');
writeFileSync(testServerPath, serverScript);
// Make the script executable (though running with 'node' should work anyway)
if (process.platform !== 'win32') {
const { chmodSync } = await import('fs');
chmodSync(testServerPath, 0o755);
}
});
after(() => {
child.kill();
unlinkSync(serverScriptPath);
});
test('should add two numbers', async () => {
// Test directory is already set up in before hook
// Just run the command - MCP server config is in settings.json
const output = await rig.run('add 5 and 10');
test('should add two numbers', () => {
rig.setup('should add two numbers');
const output = rig.run('add 5 and 10');
assert.ok(output.includes('15'));
const foundToolCall = await rig.waitForToolCall('add');
assert.ok(foundToolCall, 'Expected to find an add tool call');
// Validate model output - will throw if no output, fail if missing expected content
validateModelOutput(output, '15', 'MCP server test');
assert.ok(output.includes('15'), 'Expected output to contain the sum (15)');
});
});
+533 -21
View File
@@ -4,11 +4,13 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { execSync } from 'child_process';
import { execSync, spawn } from 'child_process';
import { parse } from 'shell-quote';
import { mkdirSync, writeFileSync, readFileSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
import { env } from 'process';
import { fileExists } from '../scripts/telemetry_utils.js';
const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -19,17 +21,129 @@ function sanitizeTestName(name) {
.replace(/-+/g, '-');
}
// Helper to create detailed error messages
export function createToolCallErrorMessage(expectedTools, foundTools, result) {
const expectedStr = Array.isArray(expectedTools)
? expectedTools.join(' or ')
: expectedTools;
return (
`Expected to find ${expectedStr} tool call(s). ` +
`Found: ${foundTools.length > 0 ? foundTools.join(', ') : 'none'}. ` +
`Output preview: ${result ? result.substring(0, 200) + '...' : 'no output'}`
);
}
// Helper to print debug information when tests fail
export function printDebugInfo(rig, result, context = {}) {
console.error('Test failed - Debug info:');
console.error('Result length:', result.length);
console.error('Result (first 500 chars):', result.substring(0, 500));
console.error(
'Result (last 500 chars):',
result.substring(result.length - 500),
);
// Print any additional context provided
Object.entries(context).forEach(([key, value]) => {
console.error(`${key}:`, value);
});
// Check what tools were actually called
const allTools = rig.readToolLogs();
console.error(
'All tool calls found:',
allTools.map((t) => t.toolRequest.name),
);
return allTools;
}
// Helper to validate model output and warn about unexpected content
export function validateModelOutput(
result,
expectedContent = null,
testName = '',
) {
// First, check if there's any output at all (this should fail the test if missing)
if (!result || result.trim().length === 0) {
throw new Error('Expected LLM to return some output');
}
// If expectedContent is provided, check for it and warn if missing
if (expectedContent) {
const contents = Array.isArray(expectedContent)
? expectedContent
: [expectedContent];
const missingContent = contents.filter((content) => {
if (typeof content === 'string') {
return !result.toLowerCase().includes(content.toLowerCase());
} else if (content instanceof RegExp) {
return !content.test(result);
}
return false;
});
if (missingContent.length > 0) {
console.warn(
`Warning: LLM did not include expected content in response: ${missingContent.join(', ')}.`,
'This is not ideal but not a test failure.',
);
console.warn(
'The tool was called successfully, which is the main requirement.',
);
return false;
} else if (process.env.VERBOSE === 'true') {
console.log(`${testName}: Model output validated successfully.`);
}
return true;
}
return true;
}
export class TestRig {
constructor() {
this.bundlePath = join(__dirname, '..', 'bundle/gemini.js');
this.testDir = null;
}
setup(testName) {
// Get timeout based on environment
getDefaultTimeout() {
if (env.CI) return 60000; // 1 minute in CI
if (env.GEMINI_SANDBOX) return 30000; // 30s in containers
return 15000; // 15s locally
}
setup(testName, options = {}) {
this.testName = testName;
const sanitizedName = sanitizeTestName(testName);
this.testDir = join(env.INTEGRATION_TEST_FILE_DIR, sanitizedName);
mkdirSync(this.testDir, { recursive: true });
// Create a settings file to point the CLI to the local collector
const geminiDir = join(this.testDir, '.gemini');
mkdirSync(geminiDir, { recursive: true });
// In sandbox mode, use an absolute path for telemetry inside the container
// The container mounts the test directory at the same path as the host
const telemetryPath =
env.GEMINI_SANDBOX && env.GEMINI_SANDBOX !== 'false'
? join(this.testDir, 'telemetry.log') // Absolute path in test directory
: env.TELEMETRY_LOG_FILE; // Absolute path for non-sandbox
const settings = {
telemetry: {
enabled: true,
target: 'local',
otlpEndpoint: '',
outfile: telemetryPath,
},
sandbox: env.GEMINI_SANDBOX !== 'false' ? env.GEMINI_SANDBOX : false,
...options.settings, // Allow tests to override/add settings
};
writeFileSync(
join(geminiDir, 'settings.json'),
JSON.stringify(settings, null, 2),
);
}
createFile(fileName, content) {
@@ -39,34 +153,124 @@ export class TestRig {
}
mkdir(dir) {
mkdirSync(join(this.testDir, dir));
mkdirSync(join(this.testDir, dir), { recursive: true });
}
run(prompt, ...args) {
const output = execSync(
`node ${this.bundlePath} --yolo --prompt "${prompt}" ${args.join(' ')}`,
{
cwd: this.testDir,
encoding: 'utf-8',
},
);
sync() {
// ensure file system is done before spawning
execSync('sync', { cwd: this.testDir });
}
if (env.KEEP_OUTPUT === 'true') {
const testId = `${env.TEST_FILE_NAME.replace(
'.test.js',
'',
)}:${this.testName.replace(/ /g, '-')}`;
console.log(`--- TEST: ${testId} ---`);
console.log(output);
console.log(`--- END TEST: ${testId} ---`);
run(promptOrOptions, ...args) {
let command = `node ${this.bundlePath} --yolo`;
const execOptions = {
cwd: this.testDir,
encoding: 'utf-8',
};
if (typeof promptOrOptions === 'string') {
command += ` --prompt "${promptOrOptions}"`;
} else if (
typeof promptOrOptions === 'object' &&
promptOrOptions !== null
) {
if (promptOrOptions.prompt) {
command += ` --prompt "${promptOrOptions.prompt}"`;
}
if (promptOrOptions.stdin) {
execOptions.input = promptOrOptions.stdin;
}
}
return output;
command += ` ${args.join(' ')}`;
const commandArgs = parse(command);
const node = commandArgs.shift();
const child = spawn(node, commandArgs, {
cwd: this.testDir,
stdio: 'pipe',
});
let stdout = '';
let stderr = '';
// Handle stdin if provided
if (execOptions.input) {
child.stdin.write(execOptions.input);
child.stdin.end();
}
child.stdout.on('data', (data) => {
stdout += data;
if (env.KEEP_OUTPUT === 'true' || env.VERBOSE === 'true') {
process.stdout.write(data);
}
});
child.stderr.on('data', (data) => {
stderr += data;
if (env.KEEP_OUTPUT === 'true' || env.VERBOSE === 'true') {
process.stderr.write(data);
}
});
const promise = new Promise((resolve, reject) => {
child.on('close', (code) => {
if (code === 0) {
// Store the raw stdout for Podman telemetry parsing
this._lastRunStdout = stdout;
// Filter out telemetry output when running with Podman
// Podman seems to output telemetry to stdout even when writing to file
let result = stdout;
if (env.GEMINI_SANDBOX === 'podman') {
// Remove telemetry JSON objects from output
// They are multi-line JSON objects that start with { and contain telemetry fields
const lines = result.split('\n');
const filteredLines = [];
let inTelemetryObject = false;
let braceDepth = 0;
for (const line of lines) {
if (!inTelemetryObject && line.trim() === '{') {
// Check if this might be start of telemetry object
inTelemetryObject = true;
braceDepth = 1;
} else if (inTelemetryObject) {
// Count braces to track nesting
for (const char of line) {
if (char === '{') braceDepth++;
else if (char === '}') braceDepth--;
}
// Check if we've closed all braces
if (braceDepth === 0) {
inTelemetryObject = false;
// Skip this line (the closing brace)
continue;
}
} else {
// Not in telemetry object, keep the line
filteredLines.push(line);
}
}
result = filteredLines.join('\n');
}
resolve(result);
} else {
reject(new Error(`Process exited with code ${code}:\n${stderr}`));
}
});
});
return promise;
}
readFile(fileName) {
const content = readFileSync(join(this.testDir, fileName), 'utf-8');
if (env.KEEP_OUTPUT === 'true') {
if (env.KEEP_OUTPUT === 'true' || env.VERBOSE === 'true') {
const testId = `${env.TEST_FILE_NAME.replace(
'.test.js',
'',
@@ -77,4 +281,312 @@ export class TestRig {
}
return content;
}
async cleanup() {
// Clean up test directory
if (this.testDir && !env.KEEP_OUTPUT) {
try {
execSync(`rm -rf ${this.testDir}`);
} catch (error) {
// Ignore cleanup errors
if (env.VERBOSE === 'true') {
console.warn('Cleanup warning:', error.message);
}
}
}
}
async waitForTelemetryReady() {
// In sandbox mode, telemetry is written to a relative path in the test directory
const logFilePath =
env.GEMINI_SANDBOX && env.GEMINI_SANDBOX !== 'false'
? join(this.testDir, 'telemetry.log')
: env.TELEMETRY_LOG_FILE;
if (!logFilePath) return;
// Wait for telemetry file to exist and have content
await this.poll(
() => {
if (!fileExists(logFilePath)) return false;
try {
const content = readFileSync(logFilePath, 'utf-8');
// Check if file has meaningful content (at least one complete JSON object)
return content.includes('"event.name"');
} catch (_e) {
return false;
}
},
2000, // 2 seconds max - reduced since telemetry should flush on exit now
100, // check every 100ms
);
}
async waitForToolCall(toolName, timeout) {
// Use environment-specific timeout
if (!timeout) {
timeout = this.getDefaultTimeout();
}
// Wait for telemetry to be ready before polling for tool calls
await this.waitForTelemetryReady();
return this.poll(
() => {
const toolLogs = this.readToolLogs();
return toolLogs.some((log) => log.toolRequest.name === toolName);
},
timeout,
100,
);
}
async waitForAnyToolCall(toolNames, timeout) {
// Use environment-specific timeout
if (!timeout) {
timeout = this.getDefaultTimeout();
}
// Wait for telemetry to be ready before polling for tool calls
await this.waitForTelemetryReady();
return this.poll(
() => {
const toolLogs = this.readToolLogs();
return toolNames.some((name) =>
toolLogs.some((log) => log.toolRequest.name === name),
);
},
timeout,
100,
);
}
async poll(predicate, timeout, interval) {
const startTime = Date.now();
let attempts = 0;
while (Date.now() - startTime < timeout) {
attempts++;
const result = predicate();
if (env.VERBOSE === 'true' && attempts % 5 === 0) {
console.log(
`Poll attempt ${attempts}: ${result ? 'success' : 'waiting...'}`,
);
}
if (result) {
return true;
}
await new Promise((resolve) => setTimeout(resolve, interval));
}
if (env.VERBOSE === 'true') {
console.log(`Poll timed out after ${attempts} attempts`);
}
return false;
}
_parseToolLogsFromStdout(stdout) {
const logs = [];
// The console output from Podman is JavaScript object notation, not JSON
// Look for tool call events in the output
// Updated regex to handle tool names with hyphens and underscores
const toolCallPattern =
/body:\s*'Tool call:\s*([\w-]+)\..*?Success:\s*(\w+)\..*?Duration:\s*(\d+)ms\.'/g;
const matches = [...stdout.matchAll(toolCallPattern)];
for (const match of matches) {
const toolName = match[1];
const success = match[2] === 'true';
const duration = parseInt(match[3], 10);
// Try to find function_args nearby
const matchIndex = match.index || 0;
const contextStart = Math.max(0, matchIndex - 500);
const contextEnd = Math.min(stdout.length, matchIndex + 500);
const context = stdout.substring(contextStart, contextEnd);
// Look for function_args in the context
let args = '{}';
const argsMatch = context.match(/function_args:\s*'([^']+)'/);
if (argsMatch) {
args = argsMatch[1];
}
// Also try to find function_name to double-check
// Updated regex to handle tool names with hyphens and underscores
const nameMatch = context.match(/function_name:\s*'([\w-]+)'/);
const actualToolName = nameMatch ? nameMatch[1] : toolName;
logs.push({
timestamp: Date.now(),
toolRequest: {
name: actualToolName,
args: args,
success: success,
duration_ms: duration,
},
});
}
// If no matches found with the simple pattern, try the JSON parsing approach
// in case the format changes
if (logs.length === 0) {
const lines = stdout.split('\n');
let currentObject = '';
let inObject = false;
let braceDepth = 0;
for (const line of lines) {
if (!inObject && line.trim() === '{') {
inObject = true;
braceDepth = 1;
currentObject = line + '\n';
} else if (inObject) {
currentObject += line + '\n';
// Count braces
for (const char of line) {
if (char === '{') braceDepth++;
else if (char === '}') braceDepth--;
}
// If we've closed all braces, try to parse the object
if (braceDepth === 0) {
inObject = false;
try {
const obj = JSON.parse(currentObject);
// Check for tool call in different formats
if (
obj.body &&
obj.body.includes('Tool call:') &&
obj.attributes
) {
const bodyMatch = obj.body.match(/Tool call: (\w+)\./);
if (bodyMatch) {
logs.push({
timestamp: obj.timestamp || Date.now(),
toolRequest: {
name: bodyMatch[1],
args: obj.attributes.function_args || '{}',
success: obj.attributes.success !== false,
duration_ms: obj.attributes.duration_ms || 0,
},
});
}
} else if (
obj.attributes &&
obj.attributes['event.name'] === 'gemini_cli.tool_call'
) {
logs.push({
timestamp: obj.attributes['event.timestamp'],
toolRequest: {
name: obj.attributes.function_name,
args: obj.attributes.function_args,
success: obj.attributes.success,
duration_ms: obj.attributes.duration_ms,
},
});
}
} catch (_e) {
// Not valid JSON
}
currentObject = '';
}
}
}
}
return logs;
}
readToolLogs() {
// For Podman, first check if telemetry file exists and has content
// If not, fall back to parsing from stdout
if (env.GEMINI_SANDBOX === 'podman') {
// Try reading from file first
const logFilePath = join(this.testDir, 'telemetry.log');
if (fileExists(logFilePath)) {
try {
const content = readFileSync(logFilePath, 'utf-8');
if (content && content.includes('"event.name"')) {
// File has content, use normal file parsing
// Continue to the normal file parsing logic below
} else if (this._lastRunStdout) {
// File exists but is empty or doesn't have events, parse from stdout
return this._parseToolLogsFromStdout(this._lastRunStdout);
}
} catch (_e) {
// Error reading file, fall back to stdout
if (this._lastRunStdout) {
return this._parseToolLogsFromStdout(this._lastRunStdout);
}
}
} else if (this._lastRunStdout) {
// No file exists, parse from stdout
return this._parseToolLogsFromStdout(this._lastRunStdout);
}
}
// In sandbox mode, telemetry is written to a relative path in the test directory
const logFilePath =
env.GEMINI_SANDBOX && env.GEMINI_SANDBOX !== 'false'
? join(this.testDir, 'telemetry.log')
: env.TELEMETRY_LOG_FILE;
if (!logFilePath) {
console.warn(`TELEMETRY_LOG_FILE environment variable not set`);
return [];
}
// Check if file exists, if not return empty array (file might not be created yet)
if (!fileExists(logFilePath)) {
return [];
}
const content = readFileSync(logFilePath, 'utf-8');
// Split the content into individual JSON objects
// They are separated by "}\n{" pattern
const jsonObjects = content
.split(/}\s*\n\s*{/)
.map((obj, index, array) => {
// Add back the braces we removed during split
if (index > 0) obj = '{' + obj;
if (index < array.length - 1) obj = obj + '}';
return obj.trim();
})
.filter((obj) => obj);
const logs = [];
for (const jsonStr of jsonObjects) {
try {
const logData = JSON.parse(jsonStr);
// Look for tool call logs
if (
logData.attributes &&
logData.attributes['event.name'] === 'gemini_cli.tool_call'
) {
const toolName = logData.attributes.function_name;
logs.push({
toolRequest: {
name: toolName,
args: logData.attributes.function_args,
success: logData.attributes.success,
duration_ms: logData.attributes.duration_ms,
},
});
}
} catch (_e) {
// Skip objects that aren't valid JSON
if (env.VERBOSE === 'true') {
console.error('Failed to parse telemetry object:', _e.message);
}
}
}
return logs;
}
}
+52 -5
View File
@@ -6,16 +6,63 @@
import { test } from 'node:test';
import { strict as assert } from 'assert';
import { TestRig } from './test-helper.js';
import {
TestRig,
createToolCallErrorMessage,
printDebugInfo,
validateModelOutput,
} from './test-helper.js';
test('should be able to write a file', async (t) => {
test('should be able to write a file', async () => {
const rig = new TestRig();
rig.setup(t.name);
await rig.setup('should be able to write a file');
const prompt = `show me an example of using the write tool. put a dad joke in dad.txt`;
await rig.run(prompt);
const result = await rig.run(prompt);
const foundToolCall = await rig.waitForToolCall('write_file');
// Add debugging information
if (!foundToolCall) {
printDebugInfo(rig, result);
}
const allTools = rig.readToolLogs();
assert.ok(
foundToolCall,
createToolCallErrorMessage(
'write_file',
allTools.map((t) => t.toolRequest.name),
result,
),
);
// Validate model output - will throw if no output, warn if missing expected content
validateModelOutput(result, 'dad.txt', 'Write file test');
const newFilePath = 'dad.txt';
const newFileContent = rig.readFile(newFilePath);
assert.notEqual(newFileContent, '');
// Add debugging for file content
if (newFileContent === '') {
console.error('File was created but is empty');
console.error(
'Tool calls:',
rig.readToolLogs().map((t) => ({
name: t.toolRequest.name,
args: t.toolRequest.args,
})),
);
}
assert.notEqual(newFileContent, '', 'Expected file to have content');
// Log success info if verbose
if (process.env.VERBOSE === 'true') {
console.log(
'File created successfully with content:',
newFileContent.substring(0, 100) + '...',
);
}
});
+1997 -1641
View File
File diff suppressed because it is too large Load Diff
+40 -27
View File
@@ -1,46 +1,53 @@
{
"name": "gemini-cli",
"version": "0.1.0",
"name": "@google/gemini-cli",
"version": "0.1.18",
"engines": {
"node": ">=20.0.0"
},
"type": "module",
"workspaces": [
"packages/*"
],
"private": "true",
"repository": {
"type": "git",
"url": "git+https://github.com/google-gemini/gemini-cli.git"
},
"config": {
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.1.18"
},
"scripts": {
"start": "node scripts/start.js",
"debug": "cross-env DEBUG=1 node --inspect-brk scripts/start.js",
"auth:npm": "npx google-artifactregistry-auth",
"auth:docker": "gcloud auth configure-docker us-west1-docker.pkg.dev",
"auth": "npm run auth:npm && npm run auth:docker",
"generate": "node scripts/generate-git-commit-info.js",
"build": "node scripts/build.js",
"build:sandbox": "node scripts/build_sandbox.js",
"build:all": "npm run build && npm run build:sandbox",
"clean": "node scripts/clean.js",
"prepare": "npm run bundle",
"build:vscode": "node scripts/build_vscode_companion.js",
"build:all": "npm run build && npm run build:sandbox && npm run build:vscode",
"build:packages": "npm run build --workspaces",
"build:sandbox": "node scripts/build_sandbox.js --skip-npm-install-build",
"bundle": "npm run generate && node esbuild.config.js && node scripts/copy_bundle_assets.js",
"test": "npm run test --workspaces",
"test:ci": "npm run test:ci --workspaces --if-present",
"test:ci": "npm run test:ci --workspaces --if-present && npm run test:scripts",
"test:scripts": "vitest run --config ./scripts/tests/vitest.config.ts",
"test:e2e": "npm run test:integration:sandbox:none -- --verbose --keep-output",
"test:integration:all": "npm run test:integration:sandbox:none && npm run test:integration:sandbox:docker && npm run test:integration:sandbox:podman",
"test:integration:sandbox:none": "GEMINI_SANDBOX=false node integration-tests/run-tests.js",
"test:integration:sandbox:docker": "GEMINI_SANDBOX=docker node integration-tests/run-tests.js",
"test:integration:sandbox:podman": "GEMINI_SANDBOX=podman node integration-tests/run-tests.js",
"start": "node scripts/start.js",
"debug": "cross-env DEBUG=1 node scripts/start.js",
"lint:fix": "eslint . --fix && eslint integration-tests --fix",
"lint": "eslint . --ext .ts,.tsx && eslint integration-tests",
"typecheck": "npm run typecheck --workspaces --if-present",
"lint:fix": "eslint . --fix && eslint integration-tests --fix",
"lint:ci": "eslint . --ext .ts,.tsx --max-warnings 0 && eslint integration-tests --max-warnings 0",
"format": "prettier --write .",
"preflight": "npm run clean && npm ci && npm run format && npm run lint:fix && npm run build && npm run typecheck && npm run test:ci",
"auth:npm": "npx google-artifactregistry-auth",
"auth:docker": "gcloud auth configure-docker us-west1-docker.pkg.dev",
"auth": "npm run auth:npm && npm run auth:docker",
"prerelease:dev": "npm run prerelease:version --workspaces && npm run prerelease:deps --workspaces",
"bundle": "npm run generate && node esbuild.config.js && node scripts/copy_bundle_assets.js",
"build:cli": "npm run build --workspace packages/cli",
"build:core": "npm run build --workspace packages/core",
"build:packages": "npm run build:core && npm run build:cli",
"build:docker": "node scripts/build_sandbox.js -s",
"prepare:cli-packagejson": "node scripts/prepare-cli-packagejson.js",
"publish:sandbox": "node scripts/publish-sandbox.js",
"publish:npm": "npm publish --workspaces ${NPM_PUBLISH_TAG:+--tag=$NPM_PUBLISH_TAG} ${NPM_DRY_RUN:+--dry-run}",
"publish:release": "npm run build:packages && npm run prepare:cli-packagejson && npm run build:docker && npm run publish:sandbox && npm run publish:npm",
"typecheck": "npm run typecheck --workspaces --if-present",
"preflight": "npm run clean && npm ci && npm run format && npm run lint:ci && npm run build && npm run typecheck && npm run test:ci",
"prepare": "npm run bundle",
"prepare:package": "node scripts/prepare-package.js",
"release:version": "node scripts/version.js",
"telemetry": "node scripts/telemetry.js",
"start:gcp": "npm run telemetry -- --target=gcp & npm start"
"clean": "node scripts/clean.js"
},
"bin": {
"gemini": "bundle/gemini.js"
@@ -51,10 +58,14 @@
"LICENSE"
],
"devDependencies": {
"@types/marked": "^5.0.2",
"@types/micromatch": "^4.0.9",
"@types/mime-types": "^2.1.4",
"@types/mime-types": "^3.0.1",
"@types/minimatch": "^5.1.2",
"@types/mock-fs": "^4.13.4",
"@types/shell-quote": "^1.7.5",
"@vitest/coverage-v8": "^3.1.1",
"concurrently": "^9.2.0",
"cross-env": "^7.0.3",
"esbuild": "^0.25.0",
"eslint": "^9.24.0",
@@ -68,9 +79,11 @@
"json": "^11.0.0",
"lodash": "^4.17.21",
"memfs": "^4.17.2",
"mock-fs": "^5.5.0",
"prettier": "^3.5.3",
"react-devtools-core": "^4.28.5",
"typescript-eslint": "^8.30.1",
"vitest": "^3.2.4",
"yargs": "^17.7.2"
}
}
-48
View File
@@ -1,48 +0,0 @@
# Gemini CLI
This package contains the core command-line interface for Gemini CLI.
## Setup
1. **Get a Gemini API Key:** Obtain your API key from Google AI Studio: [https://aistudio.google.com/app/apikey](https://aistudio.google.com/app/apikey)
2. **Set Environment Variable:** Set the `GEMINI_API_KEY` environment variable to your obtained key. You can do this temporarily in your current shell session:
```bash
export GEMINI_API_KEY="YOUR_API_KEY"
```
Or add it to your shell's configuration file (like `~/.bashrc`, `~/.zshrc`, or `~/.profile`) for persistence:
```bash
echo 'export GEMINI_API_KEY="YOUR_API_KEY"' >> ~/.bashrc # Or your preferred shell config file
source ~/.bashrc # Reload the config
```
Replace `"YOUR_API_KEY"` with your actual key.
## Building
To build only the CLI package, navigate to this directory (`packages/cli`) and run:
```bash
npm run build
```
This command executes the TypeScript compiler (`tsc`) as defined in this package's `package.json`. Ensure dependencies have been installed from the root directory (`npm install`) first.
## Running
To start the Gemini CLI directly from this directory:
```bash
npm start
```
This command executes `node dist/gemini.js` as defined in this package's `package.json`.
## Debugging
To debug the CLI application using VS Code:
1. Start the CLI in debug mode from this directory (`packages/cli`):
```bash
npm run debug
```
This command runs `node --inspect-brk dist/gemini.js`, pausing execution until a debugger attaches.
2. In VS Code (opened at the root of the monorepo), use the "Attach" launch configuration (found in `.vscode/launch.json`). This configuration is set up to attach to the Node.js process listening on port 9229, which is the default port used by `--inspect-brk`.
+30 -22
View File
@@ -1,7 +1,11 @@
{
"name": "@gemini-cli/cli",
"version": "0.1.0",
"name": "@google/gemini-cli",
"version": "0.1.18",
"description": "Gemini CLI",
"repository": {
"type": "git",
"url": "git+https://github.com/google-gemini/gemini-cli.git"
},
"type": "module",
"main": "dist/index.js",
"bin": {
@@ -9,66 +13,70 @@
},
"scripts": {
"build": "node ../../scripts/build_package.js",
"clean": "rm -rf dist",
"start": "node dist/index.js",
"debug": "node --inspect-brk dist/index.js",
"lint": "eslint . --ext .ts,.tsx",
"format": "prettier --write .",
"test": "vitest run",
"test:ci": "vitest run --coverage",
"typecheck": "tsc --noEmit",
"prerelease:version": "node ../../scripts/bind_package_version.js",
"prerelease:deps": "node ../../scripts/bind_package_dependencies.js",
"prepublishOnly": "npm publish --workspace=@gemini-cli/core",
"prepack": "npm run build"
"typecheck": "tsc --noEmit"
},
"files": [
"dist"
],
"config": {
"sandboxImageUri": "gemini-cli-sandbox"
"sandboxImageUri": "us-docker.pkg.dev/gemini-code-dev/gemini-cli/sandbox:0.1.18"
},
"dependencies": {
"@gemini-cli/core": "file:../core",
"@google/gemini-cli-core": "file:../core",
"@google/genai": "1.9.0",
"@iarna/toml": "^2.2.5",
"@types/update-notifier": "^6.0.8",
"command-exists": "^1.2.9",
"diff": "^7.0.0",
"dotenv": "^16.4.7",
"dotenv": "^17.1.0",
"glob": "^10.4.1",
"highlight.js": "^11.11.1",
"ink": "^5.2.0",
"ink": "^6.0.1",
"ink-big-text": "^2.0.0",
"ink-gradient": "^3.0.0",
"ink-select-input": "^6.0.0",
"ink-link": "^4.1.0",
"ink-select-input": "^6.2.0",
"ink-spinner": "^5.0.0",
"ink-text-input": "^6.0.0",
"lowlight": "^3.3.0",
"mime-types": "^2.1.4",
"mime-types": "^3.0.1",
"open": "^10.1.2",
"react": "^18.3.1",
"react": "^19.1.0",
"read-package-up": "^11.0.0",
"shell-quote": "^1.8.2",
"shell-quote": "^1.8.3",
"string-width": "^7.1.0",
"strip-ansi": "^7.1.0",
"strip-json-comments": "^3.1.1",
"update-notifier": "^7.3.1",
"yargs": "^17.7.2"
"yargs": "^17.7.2",
"zod": "^3.23.8"
},
"devDependencies": {
"@testing-library/react": "^14.0.0",
"@babel/runtime": "^7.27.6",
"@testing-library/react": "^16.3.0",
"@types/command-exists": "^1.2.3",
"@types/diff": "^7.0.2",
"@types/dotenv": "^6.1.1",
"@types/node": "^20.11.24",
"@types/react": "^18.3.1",
"@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6",
"@types/semver": "^7.7.0",
"@types/shell-quote": "^1.7.5",
"@types/yargs": "^17.0.32",
"ink-testing-library": "^4.0.0",
"jsdom": "^26.1.0",
"pretty-format": "^30.0.2",
"react-dom": "^19.1.0",
"typescript": "^5.3.3",
"vitest": "^3.1.1"
"vitest": "^3.1.1",
"@google/gemini-cli-test-utils": "file:../test-utils"
},
"engines": {
"node": ">=18"
"node": ">=20"
}
}
+464
View File
@@ -0,0 +1,464 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/* ACP defines a schema for a simple (experimental) JSON-RPC protocol that allows GUI applications to interact with agents. */
import { Icon } from '@google/gemini-cli-core';
import { WritableStream, ReadableStream } from 'node:stream/web';
export class ClientConnection implements Client {
#connection: Connection<Agent>;
constructor(
agent: (client: Client) => Agent,
input: WritableStream<Uint8Array>,
output: ReadableStream<Uint8Array>,
) {
this.#connection = new Connection(agent(this), input, output);
}
/**
* Streams part of an assistant response to the client
*/
async streamAssistantMessageChunk(
params: StreamAssistantMessageChunkParams,
): Promise<void> {
await this.#connection.sendRequest('streamAssistantMessageChunk', params);
}
/**
* Request confirmation before running a tool
*
* When allowed, the client returns a [`ToolCallId`] which can be used
* to update the tool call's `status` and `content` as it runs.
*/
requestToolCallConfirmation(
params: RequestToolCallConfirmationParams,
): Promise<RequestToolCallConfirmationResponse> {
return this.#connection.sendRequest('requestToolCallConfirmation', params);
}
/**
* pushToolCall allows the agent to start a tool call
* when it does not need to request permission to do so.
*
* The returned id can be used to update the UI for the tool
* call as needed.
*/
pushToolCall(params: PushToolCallParams): Promise<PushToolCallResponse> {
return this.#connection.sendRequest('pushToolCall', params);
}
/**
* updateToolCall allows the agent to update the content and status of the tool call.
*
* The new content replaces what is currently displayed in the UI.
*
* The [`ToolCallId`] is included in the response of
* `pushToolCall` or `requestToolCallConfirmation` respectively.
*/
async updateToolCall(params: UpdateToolCallParams): Promise<void> {
await this.#connection.sendRequest('updateToolCall', params);
}
}
type AnyMessage = AnyRequest | AnyResponse;
type AnyRequest = {
id: number;
method: string;
params?: unknown;
};
type AnyResponse = { jsonrpc: '2.0'; id: number } & Result<unknown>;
type Result<T> =
| {
result: T;
}
| {
error: ErrorResponse;
};
type ErrorResponse = {
code: number;
message: string;
data?: { details?: string };
};
type PendingResponse = {
resolve: (response: unknown) => void;
reject: (error: ErrorResponse) => void;
};
class Connection<D> {
#pendingResponses: Map<number, PendingResponse> = new Map();
#nextRequestId: number = 0;
#delegate: D;
#peerInput: WritableStream<Uint8Array>;
#writeQueue: Promise<void> = Promise.resolve();
#textEncoder: TextEncoder;
constructor(
delegate: D,
peerInput: WritableStream<Uint8Array>,
peerOutput: ReadableStream<Uint8Array>,
) {
this.#peerInput = peerInput;
this.#textEncoder = new TextEncoder();
this.#delegate = delegate;
this.#receive(peerOutput);
}
async #receive(output: ReadableStream<Uint8Array>) {
let content = '';
const decoder = new TextDecoder();
for await (const chunk of output) {
content += decoder.decode(chunk, { stream: true });
const lines = content.split('\n');
content = lines.pop() || '';
for (const line of lines) {
const trimmedLine = line.trim();
if (trimmedLine) {
const message = JSON.parse(trimmedLine);
this.#processMessage(message);
}
}
}
}
async #processMessage(message: AnyMessage) {
if ('method' in message) {
const response = await this.#tryCallDelegateMethod(
message.method,
message.params,
);
await this.#sendMessage({
jsonrpc: '2.0',
id: message.id,
...response,
});
} else {
this.#handleResponse(message);
}
}
async #tryCallDelegateMethod(
method: string,
params?: unknown,
): Promise<Result<unknown>> {
const methodName = method as keyof D;
if (typeof this.#delegate[methodName] !== 'function') {
return RequestError.methodNotFound(method).toResult();
}
try {
const result = await this.#delegate[methodName](params);
return { result: result ?? null };
} catch (error: unknown) {
if (error instanceof RequestError) {
return error.toResult();
}
let details;
if (error instanceof Error) {
details = error.message;
} else if (
typeof error === 'object' &&
error != null &&
'message' in error &&
typeof error.message === 'string'
) {
details = error.message;
}
return RequestError.internalError(details).toResult();
}
}
#handleResponse(response: AnyResponse) {
const pendingResponse = this.#pendingResponses.get(response.id);
if (pendingResponse) {
if ('result' in response) {
pendingResponse.resolve(response.result);
} else if ('error' in response) {
pendingResponse.reject(response.error);
}
this.#pendingResponses.delete(response.id);
}
}
async sendRequest<Req, Resp>(method: string, params?: Req): Promise<Resp> {
const id = this.#nextRequestId++;
const responsePromise = new Promise((resolve, reject) => {
this.#pendingResponses.set(id, { resolve, reject });
});
await this.#sendMessage({ jsonrpc: '2.0', id, method, params });
return responsePromise as Promise<Resp>;
}
async #sendMessage(json: AnyMessage) {
const content = JSON.stringify(json) + '\n';
this.#writeQueue = this.#writeQueue
.then(async () => {
const writer = this.#peerInput.getWriter();
try {
await writer.write(this.#textEncoder.encode(content));
} finally {
writer.releaseLock();
}
})
.catch((error) => {
// Continue processing writes on error
console.error('ACP write error:', error);
});
return this.#writeQueue;
}
}
export class RequestError extends Error {
data?: { details?: string };
constructor(
public code: number,
message: string,
details?: string,
) {
super(message);
this.name = 'RequestError';
if (details) {
this.data = { details };
}
}
static parseError(details?: string): RequestError {
return new RequestError(-32700, 'Parse error', details);
}
static invalidRequest(details?: string): RequestError {
return new RequestError(-32600, 'Invalid request', details);
}
static methodNotFound(details?: string): RequestError {
return new RequestError(-32601, 'Method not found', details);
}
static invalidParams(details?: string): RequestError {
return new RequestError(-32602, 'Invalid params', details);
}
static internalError(details?: string): RequestError {
return new RequestError(-32603, 'Internal error', details);
}
toResult<T>(): Result<T> {
return {
error: {
code: this.code,
message: this.message,
data: this.data,
},
};
}
}
// Protocol types
export const LATEST_PROTOCOL_VERSION = '0.0.9';
export type AssistantMessageChunk =
| {
text: string;
}
| {
thought: string;
};
export type ToolCallConfirmation =
| {
description?: string | null;
type: 'edit';
}
| {
description?: string | null;
type: 'execute';
command: string;
rootCommand: string;
}
| {
description?: string | null;
type: 'mcp';
serverName: string;
toolDisplayName: string;
toolName: string;
}
| {
description?: string | null;
type: 'fetch';
urls: string[];
}
| {
description: string;
type: 'other';
};
export type ToolCallContent =
| {
type: 'markdown';
markdown: string;
}
| {
type: 'diff';
newText: string;
oldText: string | null;
path: string;
};
export type ToolCallStatus = 'running' | 'finished' | 'error';
export type ToolCallId = number;
export type ToolCallConfirmationOutcome =
| 'allow'
| 'alwaysAllow'
| 'alwaysAllowMcpServer'
| 'alwaysAllowTool'
| 'reject'
| 'cancel';
/**
* A part in a user message
*/
export type UserMessageChunk =
| {
text: string;
}
| {
path: string;
};
export interface StreamAssistantMessageChunkParams {
chunk: AssistantMessageChunk;
}
export interface RequestToolCallConfirmationParams {
confirmation: ToolCallConfirmation;
content?: ToolCallContent | null;
icon: Icon;
label: string;
locations?: ToolCallLocation[];
}
export interface ToolCallLocation {
line?: number | null;
path: string;
}
export interface PushToolCallParams {
content?: ToolCallContent | null;
icon: Icon;
label: string;
locations?: ToolCallLocation[];
}
export interface UpdateToolCallParams {
content: ToolCallContent | null;
status: ToolCallStatus;
toolCallId: ToolCallId;
}
export interface RequestToolCallConfirmationResponse {
id: ToolCallId;
outcome: ToolCallConfirmationOutcome;
}
export interface PushToolCallResponse {
id: ToolCallId;
}
export interface InitializeParams {
/**
* The version of the protocol that the client supports.
* This should be the latest version supported by the client.
*/
protocolVersion: string;
}
export interface SendUserMessageParams {
chunks: UserMessageChunk[];
}
export interface InitializeResponse {
/**
* Indicates whether the agent is authenticated and
* ready to handle requests.
*/
isAuthenticated: boolean;
/**
* The version of the protocol that the agent supports.
* If the agent supports the requested version, it should respond with the same version.
* Otherwise, the agent should respond with the latest version it supports.
*/
protocolVersion: string;
}
export interface Error {
code: number;
data?: unknown;
message: string;
}
export interface Client {
streamAssistantMessageChunk(
params: StreamAssistantMessageChunkParams,
): Promise<void>;
requestToolCallConfirmation(
params: RequestToolCallConfirmationParams,
): Promise<RequestToolCallConfirmationResponse>;
pushToolCall(params: PushToolCallParams): Promise<PushToolCallResponse>;
updateToolCall(params: UpdateToolCallParams): Promise<void>;
}
export interface Agent {
/**
* Initializes the agent's state. It should be called before any other method,
* and no other methods should be called until it has completed.
*
* If the agent is not authenticated, then the client should prompt the user to authenticate,
* and then call the `authenticate` method.
* Otherwise the client can send other messages to the agent.
*/
initialize(params: InitializeParams): Promise<InitializeResponse>;
/**
* Begins the authentication process.
*
* This method should only be called if `initialize` indicates the user isn't already authenticated.
* The Promise MUST not resolve until authentication is complete.
*/
authenticate(): Promise<void>;
/**
* Allows the user to send a message to the agent.
* This method should complete after the agent is finished, during
* which time the agent may update the client by calling
* streamAssistantMessageChunk and other methods.
*/
sendUserMessage(params: SendUserMessageParams): Promise<void>;
/**
* Cancels the current generation.
*/
cancelSendMessage(): Promise<void>;
}
+674
View File
@@ -0,0 +1,674 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { WritableStream, ReadableStream } from 'node:stream/web';
import {
AuthType,
Config,
GeminiChat,
ToolRegistry,
logToolCall,
ToolResult,
convertToFunctionResponse,
ToolCallConfirmationDetails,
ToolConfirmationOutcome,
clearCachedCredentialFile,
isNodeError,
getErrorMessage,
isWithinRoot,
getErrorStatus,
} from '@google/gemini-cli-core';
import * as acp from './acp.js';
import { Agent } from './acp.js';
import { Readable, Writable } from 'node:stream';
import { Content, Part, FunctionCall, PartListUnion } from '@google/genai';
import { LoadedSettings, SettingScope } from '../config/settings.js';
import * as fs from 'fs/promises';
import * as path from 'path';
export async function runAcpPeer(config: Config, settings: LoadedSettings) {
const stdout = Writable.toWeb(process.stdout) as WritableStream;
const stdin = Readable.toWeb(process.stdin) as ReadableStream<Uint8Array>;
// Stdout is used to send messages to the client, so console.log/console.info
// messages to stderr so that they don't interfere with ACP.
console.log = console.error;
console.info = console.error;
console.debug = console.error;
new acp.ClientConnection(
(client: acp.Client) => new GeminiAgent(config, settings, client),
stdout,
stdin,
);
}
class GeminiAgent implements Agent {
chat?: GeminiChat;
pendingSend?: AbortController;
constructor(
private config: Config,
private settings: LoadedSettings,
private client: acp.Client,
) {}
async initialize(_: acp.InitializeParams): Promise<acp.InitializeResponse> {
let isAuthenticated = false;
if (this.settings.merged.selectedAuthType) {
try {
await this.config.refreshAuth(this.settings.merged.selectedAuthType);
isAuthenticated = true;
} catch (error) {
console.error('Failed to refresh auth:', error);
}
}
return { protocolVersion: acp.LATEST_PROTOCOL_VERSION, isAuthenticated };
}
async authenticate(): Promise<void> {
await clearCachedCredentialFile();
await this.config.refreshAuth(AuthType.LOGIN_WITH_GOOGLE);
this.settings.setValue(
SettingScope.User,
'selectedAuthType',
AuthType.LOGIN_WITH_GOOGLE,
);
}
async cancelSendMessage(): Promise<void> {
if (!this.pendingSend) {
throw new Error('Not currently generating');
}
this.pendingSend.abort();
delete this.pendingSend;
}
async sendUserMessage(params: acp.SendUserMessageParams): Promise<void> {
this.pendingSend?.abort();
const pendingSend = new AbortController();
this.pendingSend = pendingSend;
if (!this.chat) {
const geminiClient = this.config.getGeminiClient();
this.chat = await geminiClient.startChat();
}
const promptId = Math.random().toString(16).slice(2);
const chat = this.chat!;
const toolRegistry: ToolRegistry = await this.config.getToolRegistry();
const parts = await this.#resolveUserMessage(params, pendingSend.signal);
let nextMessage: Content | null = { role: 'user', parts };
while (nextMessage !== null) {
if (pendingSend.signal.aborted) {
chat.addHistory(nextMessage);
return;
}
const functionCalls: FunctionCall[] = [];
try {
const responseStream = await chat.sendMessageStream(
{
message: nextMessage?.parts ?? [],
config: {
abortSignal: pendingSend.signal,
tools: [
{
functionDeclarations: toolRegistry.getFunctionDeclarations(),
},
],
},
},
promptId,
);
nextMessage = null;
for await (const resp of responseStream) {
if (pendingSend.signal.aborted) {
return;
}
if (resp.candidates && resp.candidates.length > 0) {
const candidate = resp.candidates[0];
for (const part of candidate.content?.parts ?? []) {
if (!part.text) {
continue;
}
this.client.streamAssistantMessageChunk({
chunk: part.thought
? { thought: part.text }
: { text: part.text },
});
}
}
if (resp.functionCalls) {
functionCalls.push(...resp.functionCalls);
}
}
} catch (error) {
if (getErrorStatus(error) === 429) {
throw new acp.RequestError(
429,
'Rate limit exceeded. Try again later.',
);
}
throw error;
}
if (functionCalls.length > 0) {
const toolResponseParts: Part[] = [];
for (const fc of functionCalls) {
const response = await this.#runTool(
pendingSend.signal,
promptId,
fc,
);
const parts = Array.isArray(response) ? response : [response];
for (const part of parts) {
if (typeof part === 'string') {
toolResponseParts.push({ text: part });
} else if (part) {
toolResponseParts.push(part);
}
}
}
nextMessage = { role: 'user', parts: toolResponseParts };
}
}
}
async #runTool(
abortSignal: AbortSignal,
promptId: string,
fc: FunctionCall,
): Promise<PartListUnion> {
const callId = fc.id ?? `${fc.name}-${Date.now()}`;
const args = (fc.args ?? {}) as Record<string, unknown>;
const startTime = Date.now();
const errorResponse = (error: Error) => {
const durationMs = Date.now() - startTime;
logToolCall(this.config, {
'event.name': 'tool_call',
'event.timestamp': new Date().toISOString(),
prompt_id: promptId,
function_name: fc.name ?? '',
function_args: args,
duration_ms: durationMs,
success: false,
error: error.message,
});
return [
{
functionResponse: {
id: callId,
name: fc.name ?? '',
response: { error: error.message },
},
},
];
};
if (!fc.name) {
return errorResponse(new Error('Missing function name'));
}
const toolRegistry: ToolRegistry = await this.config.getToolRegistry();
const tool = toolRegistry.getTool(fc.name as string);
if (!tool) {
return errorResponse(
new Error(`Tool "${fc.name}" not found in registry.`),
);
}
let toolCallId;
const confirmationDetails = await tool.shouldConfirmExecute(
args,
abortSignal,
);
if (confirmationDetails) {
let content: acp.ToolCallContent | null = null;
if (confirmationDetails.type === 'edit') {
content = {
type: 'diff',
path: confirmationDetails.fileName,
oldText: confirmationDetails.originalContent,
newText: confirmationDetails.newContent,
};
}
const result = await this.client.requestToolCallConfirmation({
label: tool.getDescription(args),
icon: tool.icon,
content,
confirmation: toAcpToolCallConfirmation(confirmationDetails),
locations: tool.toolLocations(args),
});
await confirmationDetails.onConfirm(toToolCallOutcome(result.outcome));
switch (result.outcome) {
case 'reject':
return errorResponse(
new Error(`Tool "${fc.name}" not allowed to run by the user.`),
);
case 'cancel':
return errorResponse(
new Error(`Tool "${fc.name}" was canceled by the user.`),
);
case 'allow':
case 'alwaysAllow':
case 'alwaysAllowMcpServer':
case 'alwaysAllowTool':
break;
default: {
const resultOutcome: never = result.outcome;
throw new Error(`Unexpected: ${resultOutcome}`);
}
}
toolCallId = result.id;
} else {
const result = await this.client.pushToolCall({
icon: tool.icon,
label: tool.getDescription(args),
locations: tool.toolLocations(args),
});
toolCallId = result.id;
}
try {
const toolResult: ToolResult = await tool.execute(args, abortSignal);
const toolCallContent = toToolCallContent(toolResult);
await this.client.updateToolCall({
toolCallId,
status: 'finished',
content: toolCallContent,
});
const durationMs = Date.now() - startTime;
logToolCall(this.config, {
'event.name': 'tool_call',
'event.timestamp': new Date().toISOString(),
function_name: fc.name,
function_args: args,
duration_ms: durationMs,
success: true,
prompt_id: promptId,
});
return convertToFunctionResponse(fc.name, callId, toolResult.llmContent);
} catch (e) {
const error = e instanceof Error ? e : new Error(String(e));
await this.client.updateToolCall({
toolCallId,
status: 'error',
content: { type: 'markdown', markdown: error.message },
});
return errorResponse(error);
}
}
async #resolveUserMessage(
message: acp.SendUserMessageParams,
abortSignal: AbortSignal,
): Promise<Part[]> {
const atPathCommandParts = message.chunks.filter((part) => 'path' in part);
if (atPathCommandParts.length === 0) {
return message.chunks.map((chunk) => {
if ('text' in chunk) {
return { text: chunk.text };
} else {
throw new Error('Unexpected chunk type');
}
});
}
// Get centralized file discovery service
const fileDiscovery = this.config.getFileService();
const respectGitIgnore = this.config.getFileFilteringRespectGitIgnore();
const pathSpecsToRead: string[] = [];
const atPathToResolvedSpecMap = new Map<string, string>();
const contentLabelsForDisplay: string[] = [];
const ignoredPaths: string[] = [];
const toolRegistry = await this.config.getToolRegistry();
const readManyFilesTool = toolRegistry.getTool('read_many_files');
const globTool = toolRegistry.getTool('glob');
if (!readManyFilesTool) {
throw new Error('Error: read_many_files tool not found.');
}
for (const atPathPart of atPathCommandParts) {
const pathName = atPathPart.path;
// Check if path should be ignored by git
if (fileDiscovery.shouldGitIgnoreFile(pathName)) {
ignoredPaths.push(pathName);
const reason = respectGitIgnore
? 'git-ignored and will be skipped'
: 'ignored by custom patterns';
console.warn(`Path ${pathName} is ${reason}.`);
continue;
}
let currentPathSpec = pathName;
let resolvedSuccessfully = false;
try {
const absolutePath = path.resolve(this.config.getTargetDir(), pathName);
if (isWithinRoot(absolutePath, this.config.getTargetDir())) {
const stats = await fs.stat(absolutePath);
if (stats.isDirectory()) {
currentPathSpec = pathName.endsWith('/')
? `${pathName}**`
: `${pathName}/**`;
this.#debug(
`Path ${pathName} resolved to directory, using glob: ${currentPathSpec}`,
);
} else {
this.#debug(
`Path ${pathName} resolved to file: ${currentPathSpec}`,
);
}
resolvedSuccessfully = true;
} else {
this.#debug(
`Path ${pathName} is outside the project directory. Skipping.`,
);
}
} catch (error) {
if (isNodeError(error) && error.code === 'ENOENT') {
if (this.config.getEnableRecursiveFileSearch() && globTool) {
this.#debug(
`Path ${pathName} not found directly, attempting glob search.`,
);
try {
const globResult = await globTool.execute(
{
pattern: `**/*${pathName}*`,
path: this.config.getTargetDir(),
},
abortSignal,
);
if (
globResult.llmContent &&
typeof globResult.llmContent === 'string' &&
!globResult.llmContent.startsWith('No files found') &&
!globResult.llmContent.startsWith('Error:')
) {
const lines = globResult.llmContent.split('\n');
if (lines.length > 1 && lines[1]) {
const firstMatchAbsolute = lines[1].trim();
currentPathSpec = path.relative(
this.config.getTargetDir(),
firstMatchAbsolute,
);
this.#debug(
`Glob search for ${pathName} found ${firstMatchAbsolute}, using relative path: ${currentPathSpec}`,
);
resolvedSuccessfully = true;
} else {
this.#debug(
`Glob search for '**/*${pathName}*' did not return a usable path. Path ${pathName} will be skipped.`,
);
}
} else {
this.#debug(
`Glob search for '**/*${pathName}*' found no files or an error. Path ${pathName} will be skipped.`,
);
}
} catch (globError) {
console.error(
`Error during glob search for ${pathName}: ${getErrorMessage(globError)}`,
);
}
} else {
this.#debug(
`Glob tool not found. Path ${pathName} will be skipped.`,
);
}
} else {
console.error(
`Error stating path ${pathName}. Path ${pathName} will be skipped.`,
);
}
}
if (resolvedSuccessfully) {
pathSpecsToRead.push(currentPathSpec);
atPathToResolvedSpecMap.set(pathName, currentPathSpec);
contentLabelsForDisplay.push(pathName);
}
}
// Construct the initial part of the query for the LLM
let initialQueryText = '';
for (let i = 0; i < message.chunks.length; i++) {
const chunk = message.chunks[i];
if ('text' in chunk) {
initialQueryText += chunk.text;
} else {
// type === 'atPath'
const resolvedSpec = atPathToResolvedSpecMap.get(chunk.path);
if (
i > 0 &&
initialQueryText.length > 0 &&
!initialQueryText.endsWith(' ') &&
resolvedSpec
) {
// Add space if previous part was text and didn't end with space, or if previous was @path
const prevPart = message.chunks[i - 1];
if (
'text' in prevPart ||
('path' in prevPart && atPathToResolvedSpecMap.has(prevPart.path))
) {
initialQueryText += ' ';
}
}
if (resolvedSpec) {
initialQueryText += `@${resolvedSpec}`;
} else {
// If not resolved for reading (e.g. lone @ or invalid path that was skipped),
// add the original @-string back, ensuring spacing if it's not the first element.
if (
i > 0 &&
initialQueryText.length > 0 &&
!initialQueryText.endsWith(' ') &&
!chunk.path.startsWith(' ')
) {
initialQueryText += ' ';
}
initialQueryText += `@${chunk.path}`;
}
}
}
initialQueryText = initialQueryText.trim();
// Inform user about ignored paths
if (ignoredPaths.length > 0) {
const ignoreType = respectGitIgnore ? 'git-ignored' : 'custom-ignored';
this.#debug(
`Ignored ${ignoredPaths.length} ${ignoreType} files: ${ignoredPaths.join(', ')}`,
);
}
// Fallback for lone "@" or completely invalid @-commands resulting in empty initialQueryText
if (pathSpecsToRead.length === 0) {
console.warn('No valid file paths found in @ commands to read.');
return [{ text: initialQueryText }];
}
const processedQueryParts: Part[] = [{ text: initialQueryText }];
const toolArgs = {
paths: pathSpecsToRead,
respectGitIgnore, // Use configuration setting
};
const toolCall = await this.client.pushToolCall({
icon: readManyFilesTool.icon,
label: readManyFilesTool.getDescription(toolArgs),
});
try {
const result = await readManyFilesTool.execute(toolArgs, abortSignal);
const content = toToolCallContent(result) || {
type: 'markdown',
markdown: `Successfully read: ${contentLabelsForDisplay.join(', ')}`,
};
await this.client.updateToolCall({
toolCallId: toolCall.id,
status: 'finished',
content,
});
if (Array.isArray(result.llmContent)) {
const fileContentRegex = /^--- (.*?) ---\n\n([\s\S]*?)\n\n$/;
processedQueryParts.push({
text: '\n--- Content from referenced files ---',
});
for (const part of result.llmContent) {
if (typeof part === 'string') {
const match = fileContentRegex.exec(part);
if (match) {
const filePathSpecInContent = match[1]; // This is a resolved pathSpec
const fileActualContent = match[2].trim();
processedQueryParts.push({
text: `\nContent from @${filePathSpecInContent}:\n`,
});
processedQueryParts.push({ text: fileActualContent });
} else {
processedQueryParts.push({ text: part });
}
} else {
// part is a Part object.
processedQueryParts.push(part);
}
}
processedQueryParts.push({ text: '\n--- End of content ---' });
} else {
console.warn(
'read_many_files tool returned no content or empty content.',
);
}
return processedQueryParts;
} catch (error: unknown) {
await this.client.updateToolCall({
toolCallId: toolCall.id,
status: 'error',
content: {
type: 'markdown',
markdown: `Error reading files (${contentLabelsForDisplay.join(', ')}): ${getErrorMessage(error)}`,
},
});
throw error;
}
}
#debug(msg: string) {
if (this.config.getDebugMode()) {
console.warn(msg);
}
}
}
function toToolCallContent(toolResult: ToolResult): acp.ToolCallContent | null {
if (toolResult.returnDisplay) {
if (typeof toolResult.returnDisplay === 'string') {
return {
type: 'markdown',
markdown: toolResult.returnDisplay,
};
} else {
return {
type: 'diff',
path: toolResult.returnDisplay.fileName,
oldText: toolResult.returnDisplay.originalContent,
newText: toolResult.returnDisplay.newContent,
};
}
} else {
return null;
}
}
function toAcpToolCallConfirmation(
confirmationDetails: ToolCallConfirmationDetails,
): acp.ToolCallConfirmation {
switch (confirmationDetails.type) {
case 'edit':
return { type: 'edit' };
case 'exec':
return {
type: 'execute',
rootCommand: confirmationDetails.rootCommand,
command: confirmationDetails.command,
};
case 'mcp':
return {
type: 'mcp',
serverName: confirmationDetails.serverName,
toolName: confirmationDetails.toolName,
toolDisplayName: confirmationDetails.toolDisplayName,
};
case 'info':
return {
type: 'fetch',
urls: confirmationDetails.urls || [],
description: confirmationDetails.urls?.length
? null
: confirmationDetails.prompt,
};
default: {
const unreachable: never = confirmationDetails;
throw new Error(`Unexpected: ${unreachable}`);
}
}
}
function toToolCallOutcome(
outcome: acp.ToolCallConfirmationOutcome,
): ToolConfirmationOutcome {
switch (outcome) {
case 'allow':
return ToolConfirmationOutcome.ProceedOnce;
case 'alwaysAllow':
return ToolConfirmationOutcome.ProceedAlways;
case 'alwaysAllowMcpServer':
return ToolConfirmationOutcome.ProceedAlwaysServer;
case 'alwaysAllowTool':
return ToolConfirmationOutcome.ProceedAlwaysTool;
case 'reject':
case 'cancel':
return ToolConfirmationOutcome.Cancel;
default: {
const unreachable: never = outcome;
throw new Error(`Unexpected: ${unreachable}`);
}
}
}
+75
View File
@@ -0,0 +1,75 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { AuthType } from '@google/gemini-cli-core';
import { vi } from 'vitest';
import { validateAuthMethod } from './auth.js';
vi.mock('./settings.js', () => ({
loadEnvironment: vi.fn(),
}));
describe('validateAuthMethod', () => {
const originalEnv = process.env;
beforeEach(() => {
vi.resetModules();
process.env = {};
});
afterEach(() => {
process.env = originalEnv;
});
it('should return null for LOGIN_WITH_GOOGLE', () => {
expect(validateAuthMethod(AuthType.LOGIN_WITH_GOOGLE)).toBeNull();
});
it('should return null for CLOUD_SHELL', () => {
expect(validateAuthMethod(AuthType.CLOUD_SHELL)).toBeNull();
});
describe('USE_GEMINI', () => {
it('should return null if GEMINI_API_KEY is set', () => {
process.env.GEMINI_API_KEY = 'test-key';
expect(validateAuthMethod(AuthType.USE_GEMINI)).toBeNull();
});
it('should return an error message if GEMINI_API_KEY is not set', () => {
expect(validateAuthMethod(AuthType.USE_GEMINI)).toBe(
'GEMINI_API_KEY environment variable not found. Add that to your environment and try again (no reload needed if using .env)!',
);
});
});
describe('USE_VERTEX_AI', () => {
it('should return null if GOOGLE_CLOUD_PROJECT and GOOGLE_CLOUD_LOCATION are set', () => {
process.env.GOOGLE_CLOUD_PROJECT = 'test-project';
process.env.GOOGLE_CLOUD_LOCATION = 'test-location';
expect(validateAuthMethod(AuthType.USE_VERTEX_AI)).toBeNull();
});
it('should return null if GOOGLE_API_KEY is set', () => {
process.env.GOOGLE_API_KEY = 'test-api-key';
expect(validateAuthMethod(AuthType.USE_VERTEX_AI)).toBeNull();
});
it('should return an error message if no required environment variables are set', () => {
expect(validateAuthMethod(AuthType.USE_VERTEX_AI)).toBe(
'When using Vertex AI, you must specify either:\n' +
'• GOOGLE_CLOUD_PROJECT and GOOGLE_CLOUD_LOCATION environment variables.\n' +
'• GOOGLE_API_KEY environment variable (if using express mode).\n' +
'Update your environment and try again (no reload needed if using .env)!',
);
});
});
it('should return an error message for an invalid auth method', () => {
expect(validateAuthMethod('invalid-method')).toBe(
'Invalid auth method selected.',
);
});
});
+17 -19
View File
@@ -4,38 +4,36 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { AuthType } from '@gemini-cli/core';
import { loadEnvironment } from './config.js';
import { AuthType } from '@google/gemini-cli-core';
import { loadEnvironment } from './settings.js';
export const validateAuthMethod = (authMethod: string): string | null => {
loadEnvironment();
if (authMethod === AuthType.LOGIN_WITH_GOOGLE_PERSONAL) {
return null;
}
if (authMethod === AuthType.LOGIN_WITH_GOOGLE_ENTERPRISE) {
if (!process.env.GOOGLE_CLOUD_PROJECT) {
return 'GOOGLE_CLOUD_PROJECT environment variable not found. Add that to your .env and try again, no reload needed!';
}
if (
authMethod === AuthType.LOGIN_WITH_GOOGLE ||
authMethod === AuthType.CLOUD_SHELL
) {
return null;
}
if (authMethod === AuthType.USE_GEMINI) {
if (!process.env.GEMINI_API_KEY) {
return 'GEMINI_API_KEY environment variable not found. Add that to your .env and try again, no reload needed!';
return 'GEMINI_API_KEY environment variable not found. Add that to your environment and try again (no reload needed if using .env)!';
}
return null;
}
if (authMethod === AuthType.USE_VERTEX_AI) {
if (!process.env.GOOGLE_API_KEY) {
return 'GOOGLE_API_KEY environment variable not found. Add that to your .env and try again, no reload needed!';
}
if (!process.env.GOOGLE_CLOUD_PROJECT) {
return 'GOOGLE_CLOUD_PROJECT environment variable not found. Add that to your .env and try again, no reload needed!';
}
if (!process.env.GOOGLE_CLOUD_LOCATION) {
return 'GOOGLE_CLOUD_LOCATION environment variable not found. Add that to your .env and try again, no reload needed!';
const hasVertexProjectLocationConfig =
!!process.env.GOOGLE_CLOUD_PROJECT && !!process.env.GOOGLE_CLOUD_LOCATION;
const hasGoogleApiKey = !!process.env.GOOGLE_API_KEY;
if (!hasVertexProjectLocationConfig && !hasGoogleApiKey) {
return (
'When using Vertex AI, you must specify either:\n' +
'• GOOGLE_CLOUD_PROJECT and GOOGLE_CLOUD_LOCATION environment variables.\n' +
'GOOGLE_API_KEY environment variable (if using express mode).\n' +
'Update your environment and try again (no reload needed if using .env)!'
);
}
return null;
}
@@ -12,7 +12,7 @@ import {
Config,
ConfigParameters,
ContentGeneratorConfig,
} from '@gemini-cli/core';
} from '@google/gemini-cli-core';
const TEST_CONTENT_GENERATOR_CONFIG: ContentGeneratorConfig = {
apiKey: 'test-key',
@@ -21,8 +21,8 @@ const TEST_CONTENT_GENERATOR_CONFIG: ContentGeneratorConfig = {
};
// Mock file discovery service and tool registry
vi.mock('@gemini-cli/core', async () => {
const actual = await vi.importActual('@gemini-cli/core');
vi.mock('@google/gemini-cli-core', async () => {
const actual = await vi.importActual('@google/gemini-cli-core');
return {
...actual,
FileDiscoveryService: vi.fn().mockImplementation(() => ({
@@ -75,7 +75,9 @@ describe('Configuration Integration Tests', () => {
sandbox: false,
targetDir: tempDir,
debugMode: false,
fileFilteringRespectGitIgnore: false,
fileFiltering: {
respectGitIgnore: false,
},
};
const config = new Config(configParams);
@@ -109,7 +111,9 @@ describe('Configuration Integration Tests', () => {
sandbox: false,
targetDir: tempDir,
debugMode: false,
fileFilteringRespectGitIgnore: false,
fileFiltering: {
respectGitIgnore: false,
},
};
const config = new Config(configParams);
@@ -178,7 +182,9 @@ describe('Configuration Integration Tests', () => {
sandbox: false,
targetDir: tempDir,
debugMode: false,
fileFilteringRespectGitIgnore: false, // CI might need to see all files
fileFiltering: {
respectGitIgnore: false,
}, // CI might need to see all files
};
const config = new Config(configParams);
@@ -204,4 +210,34 @@ describe('Configuration Integration Tests', () => {
expect(config.getCheckpointingEnabled()).toBe(true);
});
});
describe('Extension Context Files', () => {
it('should have an empty array for extension context files by default', () => {
const configParams: ConfigParameters = {
cwd: '/tmp',
contentGeneratorConfig: TEST_CONTENT_GENERATOR_CONFIG,
embeddingModel: 'test-embedding-model',
sandbox: false,
targetDir: tempDir,
debugMode: false,
};
const config = new Config(configParams);
expect(config.getExtensionContextFilePaths()).toEqual([]);
});
it('should correctly store and return extension context file paths', () => {
const contextFiles = ['/path/to/file1.txt', '/path/to/file2.js'];
const configParams: ConfigParameters = {
cwd: '/tmp',
contentGeneratorConfig: TEST_CONTENT_GENERATOR_CONFIG,
embeddingModel: 'test-embedding-model',
sandbox: false,
targetDir: tempDir,
debugMode: false,
extensionContextFilePaths: contextFiles,
};
const config = new Config(configParams);
expect(config.getExtensionContextFilePaths()).toEqual(contextFiles);
});
});
});
File diff suppressed because it is too large Load Diff
+273 -64
View File
@@ -4,6 +4,9 @@
* SPDX-License-Identifier: Apache-2.0
*/
import * as fs from 'fs';
import * as path from 'path';
import { homedir } from 'node:os';
import yargs from 'yargs/yargs';
import { hideBin } from 'yargs/helpers';
import process from 'node:process';
@@ -13,21 +16,19 @@ import {
setGeminiMdFilename as setServerGeminiMdFilename,
getCurrentGeminiMdFilename,
ApprovalMode,
GEMINI_CONFIG_DIR as GEMINI_DIR,
DEFAULT_GEMINI_MODEL,
DEFAULT_GEMINI_EMBEDDING_MODEL,
DEFAULT_MEMORY_FILE_FILTERING_OPTIONS,
FileDiscoveryService,
TelemetryTarget,
} from '@gemini-cli/core';
FileFilteringOptions,
} from '@google/gemini-cli-core';
import { Settings } from './settings.js';
import { Extension } from './extension.js';
import { Extension, annotateActiveExtensions } from './extension.js';
import { getCliVersion } from '../utils/version.js';
import * as dotenv from 'dotenv';
import * as fs from 'node:fs';
import * as path from 'node:path';
import * as os from 'node:os';
import { loadSandboxConfig } from './sandboxConfig.js';
import { resolvePath } from '../utils/resolvePath.js';
// Simple console logger for now - replace with actual logger if available
const logger = {
@@ -39,13 +40,16 @@ const logger = {
error: (...args: any[]) => console.error('[ERROR]', ...args),
};
interface CliArgs {
export interface CliArgs {
model: string | undefined;
sandbox: boolean | string | undefined;
'sandbox-image': string | undefined;
sandboxImage: string | undefined;
debug: boolean | undefined;
prompt: string | undefined;
promptInteractive: string | undefined;
allFiles: boolean | undefined;
all_files: boolean | undefined;
showMemoryUsage: boolean | undefined;
show_memory_usage: boolean | undefined;
yolo: boolean | undefined;
telemetry: boolean | undefined;
@@ -53,21 +57,41 @@ interface CliArgs {
telemetryTarget: string | undefined;
telemetryOtlpEndpoint: string | undefined;
telemetryLogPrompts: boolean | undefined;
telemetryOutfile: string | undefined;
allowedMcpServerNames: string[] | undefined;
experimentalAcp: boolean | undefined;
extensions: string[] | undefined;
listExtensions: boolean | undefined;
ideModeFeature: boolean | undefined;
proxy: string | undefined;
includeDirectories: string[] | undefined;
loadMemoryFromIncludeDirectories: boolean | undefined;
}
async function parseArguments(): Promise<CliArgs> {
const argv = await yargs(hideBin(process.argv))
export async function parseArguments(): Promise<CliArgs> {
const yargsInstance = yargs(hideBin(process.argv))
.scriptName('gemini')
.usage(
'$0 [options]',
'Gemini CLI - Launch an interactive CLI, use -p/--prompt for non-interactive mode',
)
.option('model', {
alias: 'm',
type: 'string',
description: `Model`,
default: process.env.GEMINI_MODEL || DEFAULT_GEMINI_MODEL,
default: process.env.GEMINI_MODEL,
})
.option('prompt', {
alias: 'p',
type: 'string',
description: 'Prompt. Appended to input on stdin (if any).',
})
.option('prompt-interactive', {
alias: 'i',
type: 'string',
description:
'Execute the provided prompt and continue in interactive mode',
})
.option('sandbox', {
alias: 's',
type: 'boolean',
@@ -83,17 +107,35 @@ async function parseArguments(): Promise<CliArgs> {
description: 'Run in debug mode?',
default: false,
})
.option('all_files', {
alias: 'a',
.option('all-files', {
alias: ['a'],
type: 'boolean',
description: 'Include ALL files in context?',
default: false,
})
.option('all_files', {
type: 'boolean',
description: 'Include ALL files in context?',
default: false,
})
.deprecateOption(
'all_files',
'Use --all-files instead. We will be removing --all_files in the coming weeks.',
)
.option('show-memory-usage', {
type: 'boolean',
description: 'Show memory usage in status bar',
default: false,
})
.option('show_memory_usage', {
type: 'boolean',
description: 'Show memory usage in status bar',
default: false,
})
.deprecateOption(
'show_memory_usage',
'Use --show-memory-usage instead. We will be removing --show_memory_usage in the coming weeks.',
)
.option('yolo', {
alias: 'y',
type: 'boolean',
@@ -122,19 +164,81 @@ async function parseArguments(): Promise<CliArgs> {
description:
'Enable or disable logging of user prompts for telemetry. Overrides settings files.',
})
.option('telemetry-outfile', {
type: 'string',
description: 'Redirect all telemetry output to the specified file.',
})
.option('checkpointing', {
alias: 'c',
type: 'boolean',
description: 'Enables checkpointing of file edits',
default: false,
})
.option('experimental-acp', {
type: 'boolean',
description: 'Starts the agent in ACP mode',
})
.option('allowed-mcp-server-names', {
type: 'array',
string: true,
description: 'Allowed MCP server names',
})
.option('extensions', {
alias: 'e',
type: 'array',
string: true,
description:
'A list of extensions to use. If not provided, all extensions are used.',
})
.option('list-extensions', {
alias: 'l',
type: 'boolean',
description: 'List all available extensions and exit.',
})
.option('ide-mode-feature', {
type: 'boolean',
description: 'Run in IDE mode?',
})
.option('proxy', {
type: 'string',
description:
'Proxy for gemini client, like schema://user:password@host:port',
})
.option('include-directories', {
type: 'array',
string: true,
description:
'Additional directories to include in the workspace (comma-separated or multiple --include-directories)',
coerce: (dirs: string[]) =>
// Handle comma-separated values
dirs.flatMap((dir) => dir.split(',').map((d) => d.trim())),
})
.option('load-memory-from-include-directories', {
type: 'boolean',
description:
'If true, when refreshing memory, GEMINI.md files should be loaded from all directories that are added. If false, GEMINI.md files should only be loaded from the primary working directory.',
default: false,
})
.version(await getCliVersion()) // This will enable the --version flag based on package.json
.alias('v', 'version')
.help()
.alias('h', 'help')
.strict().argv;
.strict()
.check((argv) => {
if (argv.prompt && argv.promptInteractive) {
throw new Error(
'Cannot use both --prompt (-p) and --prompt-interactive (-i) together',
);
}
return true;
});
return argv;
yargsInstance.wrap(yargsInstance.terminalWidth());
const result = yargsInstance.parseSync();
// The import format is now only controlled by settings.memoryImportFormat
// We no longer accept it as a CLI argument
return result as CliArgs;
}
// This function is now a thin wrapper around the server's implementation.
@@ -142,22 +246,39 @@ async function parseArguments(): Promise<CliArgs> {
// TODO: Consider if App.tsx should get memory via a server call or if Config should refresh itself.
export async function loadHierarchicalGeminiMemory(
currentWorkingDirectory: string,
includeDirectoriesToReadGemini: readonly string[] = [],
debugMode: boolean,
fileService: FileDiscoveryService,
settings: Settings,
extensionContextFilePaths: string[] = [],
memoryImportFormat: 'flat' | 'tree' = 'tree',
fileFilteringOptions?: FileFilteringOptions,
): Promise<{ memoryContent: string; fileCount: number }> {
// FIX: Use real, canonical paths for a reliable comparison to handle symlinks.
const realCwd = fs.realpathSync(path.resolve(currentWorkingDirectory));
const realHome = fs.realpathSync(path.resolve(homedir()));
const isHomeDirectory = realCwd === realHome;
// If it is the home directory, pass an empty string to the core memory
// function to signal that it should skip the workspace search.
const effectiveCwd = isHomeDirectory ? '' : currentWorkingDirectory;
if (debugMode) {
logger.debug(
`CLI: Delegating hierarchical memory load to server for CWD: ${currentWorkingDirectory}`,
`CLI: Delegating hierarchical memory load to server for CWD: ${currentWorkingDirectory} (memoryImportFormat: ${memoryImportFormat})`,
);
}
// Directly call the server function.
// The server function will use its own homedir() for the global path.
// Directly call the server function with the corrected path.
return loadServerHierarchicalMemory(
currentWorkingDirectory,
effectiveCwd,
includeDirectoriesToReadGemini,
debugMode,
fileService,
extensionContextFilePaths,
memoryImportFormat,
fileFilteringOptions,
settings.memoryDiscoveryMaxDirs,
);
}
@@ -165,11 +286,28 @@ export async function loadCliConfig(
settings: Settings,
extensions: Extension[],
sessionId: string,
argv: CliArgs,
): Promise<Config> {
loadEnvironment();
const debugMode =
argv.debug ||
[process.env.DEBUG, process.env.DEBUG_MODE].some(
(v) => v === 'true' || v === '1',
) ||
false;
const memoryImportFormat = settings.memoryImportFormat || 'tree';
const argv = await parseArguments();
const debugMode = argv.debug || false;
const ideMode = settings.ideMode ?? false;
const ideModeFeature =
argv.ideModeFeature ?? settings.ideModeFeature ?? false;
const allExtensions = annotateActiveExtensions(
extensions,
argv.extensions || [],
);
const activeExtensions = extensions.filter(
(_, i) => allExtensions[i].isActive,
);
// Set the context filename in the server's memoryTool module BEFORE loading memory
// TODO(b/343434939): This is a bit of a hack. The contextFileName should ideally be passed
@@ -182,18 +320,82 @@ export async function loadCliConfig(
setServerGeminiMdFilename(getCurrentGeminiMdFilename());
}
const extensionContextFilePaths = extensions.flatMap((e) => e.contextFiles);
const extensionContextFilePaths = activeExtensions.flatMap(
(e) => e.contextFiles,
);
const fileService = new FileDiscoveryService(process.cwd());
const fileFiltering = {
...DEFAULT_MEMORY_FILE_FILTERING_OPTIONS,
...settings.fileFiltering,
};
const includeDirectories = (settings.includeDirectories || [])
.map(resolvePath)
.concat((argv.includeDirectories || []).map(resolvePath));
// Call the (now wrapper) loadHierarchicalGeminiMemory which calls the server's version
const { memoryContent, fileCount } = await loadHierarchicalGeminiMemory(
process.cwd(),
settings.loadMemoryFromIncludeDirectories ? includeDirectories : [],
debugMode,
fileService,
settings,
extensionContextFilePaths,
memoryImportFormat,
fileFiltering,
);
const mcpServers = mergeMcpServers(settings, extensions);
let mcpServers = mergeMcpServers(settings, activeExtensions);
const excludeTools = mergeExcludeTools(settings, activeExtensions);
const blockedMcpServers: Array<{ name: string; extensionName: string }> = [];
if (!argv.allowedMcpServerNames) {
if (settings.allowMCPServers) {
const allowedNames = new Set(settings.allowMCPServers.filter(Boolean));
if (allowedNames.size > 0) {
mcpServers = Object.fromEntries(
Object.entries(mcpServers).filter(([key]) => allowedNames.has(key)),
);
}
}
if (settings.excludeMCPServers) {
const excludedNames = new Set(settings.excludeMCPServers.filter(Boolean));
if (excludedNames.size > 0) {
mcpServers = Object.fromEntries(
Object.entries(mcpServers).filter(([key]) => !excludedNames.has(key)),
);
}
}
}
if (argv.allowedMcpServerNames) {
const allowedNames = new Set(argv.allowedMcpServerNames.filter(Boolean));
if (allowedNames.size > 0) {
mcpServers = Object.fromEntries(
Object.entries(mcpServers).filter(([key, server]) => {
const isAllowed = allowedNames.has(key);
if (!isAllowed) {
blockedMcpServers.push({
name: key,
extensionName: server.extensionName || '',
});
}
return isAllowed;
}),
);
} else {
blockedMcpServers.push(
...Object.entries(mcpServers).map(([key, server]) => ({
name: key,
extensionName: server.extensionName || '',
})),
);
mcpServers = {};
}
}
const sandboxConfig = await loadSandboxConfig(settings, argv);
@@ -202,11 +404,16 @@ export async function loadCliConfig(
embeddingModel: DEFAULT_GEMINI_EMBEDDING_MODEL,
sandbox: sandboxConfig,
targetDir: process.cwd(),
includeDirectories,
loadMemoryFromIncludeDirectories:
argv.loadMemoryFromIncludeDirectories ||
settings.loadMemoryFromIncludeDirectories ||
false,
debugMode,
question: argv.prompt || '',
fullContext: argv.all_files || false,
question: argv.promptInteractive || argv.prompt || '',
fullContext: argv.allFiles || argv.all_files || false,
coreTools: settings.coreTools || undefined,
excludeTools: settings.excludeTools || undefined,
excludeTools,
toolDiscoveryCommand: settings.toolDiscoveryCommand,
toolCallCommand: settings.toolCallCommand,
mcpServerCommand: settings.mcpServerCommand,
@@ -215,7 +422,10 @@ export async function loadCliConfig(
geminiMdFileCount: fileCount,
approvalMode: argv.yolo || false ? ApprovalMode.YOLO : ApprovalMode.DEFAULT,
showMemoryUsage:
argv.show_memory_usage || settings.showMemoryUsage || false,
argv.showMemoryUsage ||
argv.show_memory_usage ||
settings.showMemoryUsage ||
false,
accessibility: settings.accessibility,
telemetry: {
enabled: argv.telemetry ?? settings.telemetry?.enabled,
@@ -226,11 +436,19 @@ export async function loadCliConfig(
process.env.OTEL_EXPORTER_OTLP_ENDPOINT ??
settings.telemetry?.otlpEndpoint,
logPrompts: argv.telemetryLogPrompts ?? settings.telemetry?.logPrompts,
outfile: argv.telemetryOutfile ?? settings.telemetry?.outfile,
},
usageStatisticsEnabled: settings.usageStatisticsEnabled ?? true,
// Git-aware file filtering settings
fileFilteringRespectGitIgnore: settings.fileFiltering?.respectGitIgnore,
fileFiltering: {
respectGitIgnore: settings.fileFiltering?.respectGitIgnore,
respectGeminiIgnore: settings.fileFiltering?.respectGeminiIgnore,
enableRecursiveFileSearch:
settings.fileFiltering?.enableRecursiveFileSearch,
},
checkpointing: argv.checkpointing || settings.checkpointing?.enabled,
proxy:
argv.proxy ||
process.env.HTTPS_PROXY ||
process.env.https_proxy ||
process.env.HTTP_PROXY ||
@@ -238,7 +456,17 @@ export async function loadCliConfig(
cwd: process.cwd(),
fileDiscoveryService: fileService,
bugCommand: settings.bugCommand,
model: argv.model!,
model: argv.model || settings.model || DEFAULT_GEMINI_MODEL,
extensionContextFilePaths,
maxSessionTurns: settings.maxSessionTurns ?? -1,
experimentalAcp: argv.experimentalAcp || false,
listExtensions: argv.listExtensions || false,
extensions: allExtensions,
blockedMcpServers,
noBrowser: !!process.env.NO_BROWSER,
summarizeToolOutput: settings.summarizeToolOutput,
ideMode,
ideModeFeature,
});
}
@@ -253,44 +481,25 @@ function mergeMcpServers(settings: Settings, extensions: Extension[]) {
);
return;
}
mcpServers[key] = server;
mcpServers[key] = {
...server,
extensionName: extension.config.name,
};
},
);
}
return mcpServers;
}
function findEnvFile(startDir: string): string | null {
let currentDir = path.resolve(startDir);
while (true) {
// prefer gemini-specific .env under GEMINI_DIR
const geminiEnvPath = path.join(currentDir, GEMINI_DIR, '.env');
if (fs.existsSync(geminiEnvPath)) {
return geminiEnvPath;
}
const envPath = path.join(currentDir, '.env');
if (fs.existsSync(envPath)) {
return envPath;
}
const parentDir = path.dirname(currentDir);
if (parentDir === currentDir || !parentDir) {
// check .env under home as fallback, again preferring gemini-specific .env
const homeGeminiEnvPath = path.join(os.homedir(), GEMINI_DIR, '.env');
if (fs.existsSync(homeGeminiEnvPath)) {
return homeGeminiEnvPath;
}
const homeEnvPath = path.join(os.homedir(), '.env');
if (fs.existsSync(homeEnvPath)) {
return homeEnvPath;
}
return null;
}
currentDir = parentDir;
}
}
export function loadEnvironment(): void {
const envFilePath = findEnvFile(process.cwd());
if (envFilePath) {
dotenv.config({ path: envFilePath });
function mergeExcludeTools(
settings: Settings,
extensions: Extension[],
): string[] {
const allExcludeTools = new Set(settings.excludeTools || []);
for (const extension of extensions) {
for (const tool of extension.config.excludeTools || []) {
allExcludeTools.add(tool);
}
}
return [...allExcludeTools];
}
+77
View File
@@ -11,6 +11,7 @@ import * as path from 'path';
import {
EXTENSIONS_CONFIG_FILENAME,
EXTENSIONS_DIRECTORY_NAME,
annotateActiveExtensions,
loadExtensions,
} from './extension.js';
@@ -41,6 +42,31 @@ describe('loadExtensions', () => {
fs.rmSync(tempHomeDir, { recursive: true, force: true });
});
it('should include extension path in loaded extension', () => {
const workspaceExtensionsDir = path.join(
tempWorkspaceDir,
EXTENSIONS_DIRECTORY_NAME,
);
fs.mkdirSync(workspaceExtensionsDir, { recursive: true });
const extensionDir = path.join(workspaceExtensionsDir, 'test-extension');
fs.mkdirSync(extensionDir, { recursive: true });
const config = {
name: 'test-extension',
version: '1.0.0',
};
fs.writeFileSync(
path.join(extensionDir, EXTENSIONS_CONFIG_FILENAME),
JSON.stringify(config),
);
const extensions = loadExtensions(tempWorkspaceDir);
expect(extensions).toHaveLength(1);
expect(extensions[0].path).toBe(extensionDir);
expect(extensions[0].config.name).toBe('test-extension');
});
it('should load context file path when GEMINI.md is present', () => {
const workspaceExtensionsDir = path.join(
tempWorkspaceDir,
@@ -85,6 +111,57 @@ describe('loadExtensions', () => {
});
});
describe('annotateActiveExtensions', () => {
const extensions = [
{ config: { name: 'ext1', version: '1.0.0' }, contextFiles: [] },
{ config: { name: 'ext2', version: '1.0.0' }, contextFiles: [] },
{ config: { name: 'ext3', version: '1.0.0' }, contextFiles: [] },
];
it('should mark all extensions as active if no enabled extensions are provided', () => {
const activeExtensions = annotateActiveExtensions(extensions, []);
expect(activeExtensions).toHaveLength(3);
expect(activeExtensions.every((e) => e.isActive)).toBe(true);
});
it('should mark only the enabled extensions as active', () => {
const activeExtensions = annotateActiveExtensions(extensions, [
'ext1',
'ext3',
]);
expect(activeExtensions).toHaveLength(3);
expect(activeExtensions.find((e) => e.name === 'ext1')?.isActive).toBe(
true,
);
expect(activeExtensions.find((e) => e.name === 'ext2')?.isActive).toBe(
false,
);
expect(activeExtensions.find((e) => e.name === 'ext3')?.isActive).toBe(
true,
);
});
it('should mark all extensions as inactive when "none" is provided', () => {
const activeExtensions = annotateActiveExtensions(extensions, ['none']);
expect(activeExtensions).toHaveLength(3);
expect(activeExtensions.every((e) => !e.isActive)).toBe(true);
});
it('should handle case-insensitivity', () => {
const activeExtensions = annotateActiveExtensions(extensions, ['EXT1']);
expect(activeExtensions.find((e) => e.name === 'ext1')?.isActive).toBe(
true,
);
});
it('should log an error for unknown extensions', () => {
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
annotateActiveExtensions(extensions, ['ext4']);
expect(consoleSpy).toHaveBeenCalledWith('Extension not found: ext4');
consoleSpy.mockRestore();
});
});
function createExtension(
extensionsDir: string,
name: string,
+64 -10
View File
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { MCPServerConfig } from '@gemini-cli/core';
import { MCPServerConfig, GeminiCLIExtension } from '@google/gemini-cli-core';
import * as fs from 'fs';
import * as path from 'path';
import * as os from 'os';
@@ -13,6 +13,7 @@ export const EXTENSIONS_DIRECTORY_NAME = path.join('.gemini', 'extensions');
export const EXTENSIONS_CONFIG_FILENAME = 'gemini-extension.json';
export interface Extension {
path: string;
config: ExtensionConfig;
contextFiles: string[];
}
@@ -22,6 +23,7 @@ export interface ExtensionConfig {
version: string;
mcpServers?: Record<string, MCPServerConfig>;
contextFileName?: string | string[];
excludeTools?: string[];
}
export function loadExtensions(workspaceDir: string): Extension[] {
@@ -30,19 +32,14 @@ export function loadExtensions(workspaceDir: string): Extension[] {
...loadExtensionsFromDir(os.homedir()),
];
const uniqueExtensions: Extension[] = [];
const seenNames = new Set<string>();
const uniqueExtensions = new Map<string, Extension>();
for (const extension of allExtensions) {
if (!seenNames.has(extension.config.name)) {
console.log(
`Loading extension: ${extension.config.name} (version: ${extension.config.version})`,
);
uniqueExtensions.push(extension);
seenNames.add(extension.config.name);
if (!uniqueExtensions.has(extension.config.name)) {
uniqueExtensions.set(extension.config.name, extension);
}
}
return uniqueExtensions;
return Array.from(uniqueExtensions.values());
}
function loadExtensionsFromDir(dir: string): Extension[] {
@@ -94,6 +91,7 @@ function loadExtension(extensionDir: string): Extension | null {
.filter((contextFilePath) => fs.existsSync(contextFilePath));
return {
path: extensionDir,
config,
contextFiles,
};
@@ -113,3 +111,59 @@ function getContextFileNames(config: ExtensionConfig): string[] {
}
return config.contextFileName;
}
export function annotateActiveExtensions(
extensions: Extension[],
enabledExtensionNames: string[],
): GeminiCLIExtension[] {
const annotatedExtensions: GeminiCLIExtension[] = [];
if (enabledExtensionNames.length === 0) {
return extensions.map((extension) => ({
name: extension.config.name,
version: extension.config.version,
isActive: true,
path: extension.path,
}));
}
const lowerCaseEnabledExtensions = new Set(
enabledExtensionNames.map((e) => e.trim().toLowerCase()),
);
if (
lowerCaseEnabledExtensions.size === 1 &&
lowerCaseEnabledExtensions.has('none')
) {
return extensions.map((extension) => ({
name: extension.config.name,
version: extension.config.version,
isActive: false,
path: extension.path,
}));
}
const notFoundNames = new Set(lowerCaseEnabledExtensions);
for (const extension of extensions) {
const lowerCaseName = extension.config.name.toLowerCase();
const isActive = lowerCaseEnabledExtensions.has(lowerCaseName);
if (isActive) {
notFoundNames.delete(lowerCaseName);
}
annotatedExtensions.push({
name: extension.config.name,
version: extension.config.version,
isActive,
path: extension.path,
});
}
for (const requestedName of notFoundNames) {
console.error(`Extension not found: ${requestedName}`);
}
return annotatedExtensions;
}
+10 -5
View File
@@ -4,7 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { SandboxConfig } from '@gemini-cli/core';
import { SandboxConfig } from '@google/gemini-cli-core';
import commandExists from 'command-exists';
import * as os from 'node:os';
import { getPackageJson } from '../utils/package.js';
@@ -14,7 +14,7 @@ import { Settings } from './settings.js';
// to avoid circular dependencies.
interface SandboxCliArgs {
sandbox?: boolean | string;
'sandbox-image'?: string;
sandboxImage?: string;
}
const VALID_SANDBOX_COMMANDS: ReadonlyArray<SandboxConfig['command']> = [
@@ -36,7 +36,12 @@ function getSandboxCommand(
}
// note environment variable takes precedence over argument (from command line or settings)
sandbox = process.env.GEMINI_SANDBOX?.toLowerCase().trim() ?? sandbox;
const environmentConfiguredSandbox =
process.env.GEMINI_SANDBOX?.toLowerCase().trim() ?? '';
sandbox =
environmentConfiguredSandbox?.length > 0
? environmentConfiguredSandbox
: sandbox;
if (sandbox === '1' || sandbox === 'true') sandbox = true;
else if (sandbox === '0' || sandbox === 'false' || !sandbox) sandbox = false;
@@ -53,7 +58,7 @@ function getSandboxCommand(
);
process.exit(1);
}
// confirm that specfied command exists
// confirm that specified command exists
if (commandExists.sync(sandbox)) {
return sandbox;
}
@@ -94,7 +99,7 @@ export async function loadSandboxConfig(
const packageJson = await getPackageJson();
const image =
argv['sandbox-image'] ??
argv.sandboxImage ??
process.env.GEMINI_SANDBOX_IMAGE ??
packageJson?.config?.sandboxImageUri;
+694 -15
View File
@@ -6,15 +6,14 @@
/// <reference types="vitest/globals" />
const MOCK_HOME_DIR = '/mock/home/user'; // MUST BE FIRST
// Mock 'os' first. Its factory uses MOCK_HOME_DIR.
// Mock 'os' first.
import * as osActual from 'os'; // Import for type info for the mock factory
vi.mock('os', async (importOriginal) => {
const actualOs = await importOriginal<typeof osActual>();
return {
...actualOs,
homedir: vi.fn(() => MOCK_HOME_DIR),
homedir: vi.fn(() => '/mock/home/user'),
platform: vi.fn(() => 'linux'),
};
});
@@ -45,9 +44,9 @@ import stripJsonComments from 'strip-json-comments'; // Will be mocked separatel
// These imports will get the versions from the vi.mock('./settings.js', ...) factory.
import {
LoadedSettings,
loadSettings,
USER_SETTINGS_PATH, // This IS the mocked path.
getSystemSettingsPath,
SETTINGS_DIRECTORY_NAME, // This is from the original module, but used by the mock.
SettingScope,
} from './settings.js';
@@ -60,7 +59,21 @@ const MOCK_WORKSPACE_SETTINGS_PATH = pathActual.join(
'settings.json',
);
vi.mock('fs');
vi.mock('fs', async (importOriginal) => {
// Get all the functions from the real 'fs' module
const actualFs = await importOriginal<typeof fs>();
return {
...actualFs, // Keep all the real functions
// Now, just override the ones we need for the test
existsSync: vi.fn(),
readFileSync: vi.fn(),
writeFileSync: vi.fn(),
mkdirSync: vi.fn(),
realpathSync: (p: string) => p,
};
});
vi.mock('strip-json-comments', () => ({
default: vi.fn((content) => content),
}));
@@ -77,7 +90,7 @@ describe('Settings Loading and Merging', () => {
mockFsMkdirSync = vi.mocked(fs.mkdirSync);
mockStripJsonComments = vi.mocked(stripJsonComments);
vi.mocked(osActual.homedir).mockReturnValue(MOCK_HOME_DIR);
vi.mocked(osActual.homedir).mockReturnValue('/mock/home/user');
(mockStripJsonComments as unknown as Mock).mockImplementation(
(jsonString: string) => jsonString,
);
@@ -93,12 +106,50 @@ describe('Settings Loading and Merging', () => {
describe('loadSettings', () => {
it('should load empty settings if no files exist', () => {
const settings = loadSettings(MOCK_WORKSPACE_DIR);
expect(settings.system.settings).toEqual({});
expect(settings.user.settings).toEqual({});
expect(settings.workspace.settings).toEqual({});
expect(settings.merged).toEqual({});
expect(settings.merged).toEqual({
customThemes: {},
mcpServers: {},
includeDirectories: [],
});
expect(settings.errors.length).toBe(0);
});
it('should load system settings if only system file exists', () => {
(mockFsExistsSync as Mock).mockImplementation(
(p: fs.PathLike) => p === getSystemSettingsPath(),
);
const systemSettingsContent = {
theme: 'system-default',
sandbox: false,
};
(fs.readFileSync as Mock).mockImplementation(
(p: fs.PathOrFileDescriptor) => {
if (p === getSystemSettingsPath())
return JSON.stringify(systemSettingsContent);
return '{}';
},
);
const settings = loadSettings(MOCK_WORKSPACE_DIR);
expect(fs.readFileSync).toHaveBeenCalledWith(
getSystemSettingsPath(),
'utf-8',
);
expect(settings.system.settings).toEqual(systemSettingsContent);
expect(settings.user.settings).toEqual({});
expect(settings.workspace.settings).toEqual({});
expect(settings.merged).toEqual({
...systemSettingsContent,
customThemes: {},
mcpServers: {},
includeDirectories: [],
});
});
it('should load user settings if only user file exists', () => {
const expectedUserSettingsPath = USER_SETTINGS_PATH; // Use the path actually resolved by the (mocked) module
@@ -125,7 +176,12 @@ describe('Settings Loading and Merging', () => {
);
expect(settings.user.settings).toEqual(userSettingsContent);
expect(settings.workspace.settings).toEqual({});
expect(settings.merged).toEqual(userSettingsContent);
expect(settings.merged).toEqual({
...userSettingsContent,
customThemes: {},
mcpServers: {},
includeDirectories: [],
});
});
it('should load workspace settings if only workspace file exists', () => {
@@ -152,7 +208,12 @@ describe('Settings Loading and Merging', () => {
);
expect(settings.user.settings).toEqual({});
expect(settings.workspace.settings).toEqual(workspaceSettingsContent);
expect(settings.merged).toEqual(workspaceSettingsContent);
expect(settings.merged).toEqual({
...workspaceSettingsContent,
customThemes: {},
mcpServers: {},
includeDirectories: [],
});
});
it('should merge user and workspace settings, with workspace taking precedence', () => {
@@ -187,6 +248,59 @@ describe('Settings Loading and Merging', () => {
sandbox: true,
coreTools: ['tool1'],
contextFileName: 'WORKSPACE_CONTEXT.md',
customThemes: {},
mcpServers: {},
includeDirectories: [],
});
});
it('should merge system, user and workspace settings, with system taking precedence over workspace, and workspace over user', () => {
(mockFsExistsSync as Mock).mockReturnValue(true);
const systemSettingsContent = {
theme: 'system-theme',
sandbox: false,
allowMCPServers: ['server1', 'server2'],
telemetry: { enabled: false },
};
const userSettingsContent = {
theme: 'dark',
sandbox: true,
contextFileName: 'USER_CONTEXT.md',
};
const workspaceSettingsContent = {
sandbox: false,
coreTools: ['tool1'],
contextFileName: 'WORKSPACE_CONTEXT.md',
allowMCPServers: ['server1', 'server2', 'server3'],
};
(fs.readFileSync as Mock).mockImplementation(
(p: fs.PathOrFileDescriptor) => {
if (p === getSystemSettingsPath())
return JSON.stringify(systemSettingsContent);
if (p === USER_SETTINGS_PATH)
return JSON.stringify(userSettingsContent);
if (p === MOCK_WORKSPACE_SETTINGS_PATH)
return JSON.stringify(workspaceSettingsContent);
return '';
},
);
const settings = loadSettings(MOCK_WORKSPACE_DIR);
expect(settings.system.settings).toEqual(systemSettingsContent);
expect(settings.user.settings).toEqual(userSettingsContent);
expect(settings.workspace.settings).toEqual(workspaceSettingsContent);
expect(settings.merged).toEqual({
theme: 'system-theme',
sandbox: false,
telemetry: { enabled: false },
coreTools: ['tool1'],
contextFileName: 'WORKSPACE_CONTEXT.md',
allowMCPServers: ['server1', 'server2'],
customThemes: {},
mcpServers: {},
includeDirectories: [],
});
});
@@ -226,6 +340,86 @@ describe('Settings Loading and Merging', () => {
expect(settings.merged.contextFileName).toBe('PROJECT_SPECIFIC.md');
});
it('should handle excludedProjectEnvVars correctly when only in user settings', () => {
(mockFsExistsSync as Mock).mockImplementation(
(p: fs.PathLike) => p === USER_SETTINGS_PATH,
);
const userSettingsContent = {
excludedProjectEnvVars: ['DEBUG', 'NODE_ENV', 'CUSTOM_VAR'],
};
(fs.readFileSync as Mock).mockImplementation(
(p: fs.PathOrFileDescriptor) => {
if (p === USER_SETTINGS_PATH)
return JSON.stringify(userSettingsContent);
return '';
},
);
const settings = loadSettings(MOCK_WORKSPACE_DIR);
expect(settings.merged.excludedProjectEnvVars).toEqual([
'DEBUG',
'NODE_ENV',
'CUSTOM_VAR',
]);
});
it('should handle excludedProjectEnvVars correctly when only in workspace settings', () => {
(mockFsExistsSync as Mock).mockImplementation(
(p: fs.PathLike) => p === MOCK_WORKSPACE_SETTINGS_PATH,
);
const workspaceSettingsContent = {
excludedProjectEnvVars: ['WORKSPACE_DEBUG', 'WORKSPACE_VAR'],
};
(fs.readFileSync as Mock).mockImplementation(
(p: fs.PathOrFileDescriptor) => {
if (p === MOCK_WORKSPACE_SETTINGS_PATH)
return JSON.stringify(workspaceSettingsContent);
return '';
},
);
const settings = loadSettings(MOCK_WORKSPACE_DIR);
expect(settings.merged.excludedProjectEnvVars).toEqual([
'WORKSPACE_DEBUG',
'WORKSPACE_VAR',
]);
});
it('should merge excludedProjectEnvVars with workspace taking precedence over user', () => {
(mockFsExistsSync as Mock).mockReturnValue(true);
const userSettingsContent = {
excludedProjectEnvVars: ['DEBUG', 'NODE_ENV', 'USER_VAR'],
};
const workspaceSettingsContent = {
excludedProjectEnvVars: ['WORKSPACE_DEBUG', 'WORKSPACE_VAR'],
};
(fs.readFileSync as Mock).mockImplementation(
(p: fs.PathOrFileDescriptor) => {
if (p === USER_SETTINGS_PATH)
return JSON.stringify(userSettingsContent);
if (p === MOCK_WORKSPACE_SETTINGS_PATH)
return JSON.stringify(workspaceSettingsContent);
return '';
},
);
const settings = loadSettings(MOCK_WORKSPACE_DIR);
expect(settings.user.settings.excludedProjectEnvVars).toEqual([
'DEBUG',
'NODE_ENV',
'USER_VAR',
]);
expect(settings.workspace.settings.excludedProjectEnvVars).toEqual([
'WORKSPACE_DEBUG',
'WORKSPACE_VAR',
]);
expect(settings.merged.excludedProjectEnvVars).toEqual([
'WORKSPACE_DEBUG',
'WORKSPACE_VAR',
]);
});
it('should default contextFileName to undefined if not in any settings file', () => {
(mockFsExistsSync as Mock).mockReturnValue(true);
const userSettingsContent = { theme: 'dark' };
@@ -298,6 +492,168 @@ describe('Settings Loading and Merging', () => {
(fs.readFileSync as Mock).mockReturnValue('{}');
const settings = loadSettings(MOCK_WORKSPACE_DIR);
expect(settings.merged.telemetry).toBeUndefined();
expect(settings.merged.customThemes).toEqual({});
expect(settings.merged.mcpServers).toEqual({});
});
it('should merge MCP servers correctly, with workspace taking precedence', () => {
(mockFsExistsSync as Mock).mockReturnValue(true);
const userSettingsContent = {
mcpServers: {
'user-server': {
command: 'user-command',
args: ['--user-arg'],
description: 'User MCP server',
},
'shared-server': {
command: 'user-shared-command',
description: 'User shared server config',
},
},
};
const workspaceSettingsContent = {
mcpServers: {
'workspace-server': {
command: 'workspace-command',
args: ['--workspace-arg'],
description: 'Workspace MCP server',
},
'shared-server': {
command: 'workspace-shared-command',
description: 'Workspace shared server config',
},
},
};
(fs.readFileSync as Mock).mockImplementation(
(p: fs.PathOrFileDescriptor) => {
if (p === USER_SETTINGS_PATH)
return JSON.stringify(userSettingsContent);
if (p === MOCK_WORKSPACE_SETTINGS_PATH)
return JSON.stringify(workspaceSettingsContent);
return '';
},
);
const settings = loadSettings(MOCK_WORKSPACE_DIR);
expect(settings.user.settings).toEqual(userSettingsContent);
expect(settings.workspace.settings).toEqual(workspaceSettingsContent);
expect(settings.merged.mcpServers).toEqual({
'user-server': {
command: 'user-command',
args: ['--user-arg'],
description: 'User MCP server',
},
'workspace-server': {
command: 'workspace-command',
args: ['--workspace-arg'],
description: 'Workspace MCP server',
},
'shared-server': {
command: 'workspace-shared-command',
description: 'Workspace shared server config',
},
});
});
it('should handle MCP servers when only in user settings', () => {
(mockFsExistsSync as Mock).mockImplementation(
(p: fs.PathLike) => p === USER_SETTINGS_PATH,
);
const userSettingsContent = {
mcpServers: {
'user-only-server': {
command: 'user-only-command',
description: 'User only server',
},
},
};
(fs.readFileSync as Mock).mockImplementation(
(p: fs.PathOrFileDescriptor) => {
if (p === USER_SETTINGS_PATH)
return JSON.stringify(userSettingsContent);
return '';
},
);
const settings = loadSettings(MOCK_WORKSPACE_DIR);
expect(settings.merged.mcpServers).toEqual({
'user-only-server': {
command: 'user-only-command',
description: 'User only server',
},
});
});
it('should handle MCP servers when only in workspace settings', () => {
(mockFsExistsSync as Mock).mockImplementation(
(p: fs.PathLike) => p === MOCK_WORKSPACE_SETTINGS_PATH,
);
const workspaceSettingsContent = {
mcpServers: {
'workspace-only-server': {
command: 'workspace-only-command',
description: 'Workspace only server',
},
},
};
(fs.readFileSync as Mock).mockImplementation(
(p: fs.PathOrFileDescriptor) => {
if (p === MOCK_WORKSPACE_SETTINGS_PATH)
return JSON.stringify(workspaceSettingsContent);
return '';
},
);
const settings = loadSettings(MOCK_WORKSPACE_DIR);
expect(settings.merged.mcpServers).toEqual({
'workspace-only-server': {
command: 'workspace-only-command',
description: 'Workspace only server',
},
});
});
it('should have mcpServers as empty object if not in any settings file', () => {
(mockFsExistsSync as Mock).mockReturnValue(false); // No settings files exist
(fs.readFileSync as Mock).mockReturnValue('{}');
const settings = loadSettings(MOCK_WORKSPACE_DIR);
expect(settings.merged.mcpServers).toEqual({});
});
it('should merge includeDirectories from all scopes', () => {
(mockFsExistsSync as Mock).mockReturnValue(true);
const systemSettingsContent = {
includeDirectories: ['/system/dir'],
};
const userSettingsContent = {
includeDirectories: ['/user/dir1', '/user/dir2'],
};
const workspaceSettingsContent = {
includeDirectories: ['/workspace/dir'],
};
(fs.readFileSync as Mock).mockImplementation(
(p: fs.PathOrFileDescriptor) => {
if (p === getSystemSettingsPath())
return JSON.stringify(systemSettingsContent);
if (p === USER_SETTINGS_PATH)
return JSON.stringify(userSettingsContent);
if (p === MOCK_WORKSPACE_SETTINGS_PATH)
return JSON.stringify(workspaceSettingsContent);
return '{}';
},
);
const settings = loadSettings(MOCK_WORKSPACE_DIR);
expect(settings.merged.includeDirectories).toEqual([
'/system/dir',
'/user/dir1',
'/user/dir2',
'/workspace/dir',
]);
});
it('should handle JSON parsing errors gracefully', () => {
@@ -335,7 +691,11 @@ describe('Settings Loading and Merging', () => {
// Check that settings are empty due to parsing errors
expect(settings.user.settings).toEqual({});
expect(settings.workspace.settings).toEqual({});
expect(settings.merged).toEqual({});
expect(settings.merged).toEqual({
customThemes: {},
mcpServers: {},
includeDirectories: [],
});
// Check that error objects are populated in settings.errors
expect(settings.errors).toBeDefined();
@@ -376,10 +736,13 @@ describe('Settings Loading and Merging', () => {
);
const settings = loadSettings(MOCK_WORKSPACE_DIR);
// @ts-expect-error: dynamic property for test
expect(settings.user.settings.apiKey).toBe('user_api_key_from_env');
// @ts-expect-error: dynamic property for test
expect(settings.user.settings.someUrl).toBe(
'https://test.com/user_api_key_from_env',
);
// @ts-expect-error: dynamic property for test
expect(settings.merged.apiKey).toBe('user_api_key_from_env');
delete process.env.TEST_API_KEY;
});
@@ -408,10 +771,58 @@ describe('Settings Loading and Merging', () => {
expect(settings.workspace.settings.nested.value).toBe(
'workspace_endpoint_from_env',
);
// @ts-expect-error: dynamic property for test
expect(settings.merged.endpoint).toBe('workspace_endpoint_from_env/api');
delete process.env.WORKSPACE_ENDPOINT;
});
it('should prioritize user env variables over workspace env variables if keys clash after resolution', () => {
const userSettingsContent = { configValue: '$SHARED_VAR' };
const workspaceSettingsContent = { configValue: '$SHARED_VAR' };
(mockFsExistsSync as Mock).mockReturnValue(true);
const originalSharedVar = process.env.SHARED_VAR;
// Temporarily delete to ensure a clean slate for the test's specific manipulations
delete process.env.SHARED_VAR;
(fs.readFileSync as Mock).mockImplementation(
(p: fs.PathOrFileDescriptor) => {
if (p === USER_SETTINGS_PATH) {
process.env.SHARED_VAR = 'user_value_for_user_read'; // Set for user settings read
return JSON.stringify(userSettingsContent);
}
if (p === MOCK_WORKSPACE_SETTINGS_PATH) {
process.env.SHARED_VAR = 'workspace_value_for_workspace_read'; // Set for workspace settings read
return JSON.stringify(workspaceSettingsContent);
}
return '{}';
},
);
const settings = loadSettings(MOCK_WORKSPACE_DIR);
// @ts-expect-error: dynamic property for test
expect(settings.user.settings.configValue).toBe(
'user_value_for_user_read',
);
// @ts-expect-error: dynamic property for test
expect(settings.workspace.settings.configValue).toBe(
'workspace_value_for_workspace_read',
);
// Merged should take workspace's resolved value
// @ts-expect-error: dynamic property for test
expect(settings.merged.configValue).toBe(
'workspace_value_for_workspace_read',
);
// Restore original environment variable state
if (originalSharedVar !== undefined) {
process.env.SHARED_VAR = originalSharedVar;
} else {
delete process.env.SHARED_VAR; // Ensure it's deleted if it wasn't there before
}
});
it('should prioritize workspace env variables over user env variables if keys clash after resolution', () => {
const userSettingsContent = { configValue: '$SHARED_VAR' };
const workspaceSettingsContent = { configValue: '$SHARED_VAR' };
@@ -456,6 +867,93 @@ describe('Settings Loading and Merging', () => {
}
});
it('should prioritize system env variables over workspace env variables if keys clash after resolution', () => {
const workspaceSettingsContent = { configValue: '$SHARED_VAR' };
const systemSettingsContent = { configValue: '$SHARED_VAR' };
(mockFsExistsSync as Mock).mockReturnValue(true);
const originalSharedVar = process.env.SHARED_VAR;
// Temporarily delete to ensure a clean slate for the test's specific manipulations
delete process.env.SHARED_VAR;
(fs.readFileSync as Mock).mockImplementation(
(p: fs.PathOrFileDescriptor) => {
if (p === getSystemSettingsPath()) {
process.env.SHARED_VAR = 'system_value_for_system_read'; // Set for system settings read
return JSON.stringify(systemSettingsContent);
}
if (p === MOCK_WORKSPACE_SETTINGS_PATH) {
process.env.SHARED_VAR = 'workspace_value_for_workspace_read'; // Set for workspace settings read
return JSON.stringify(workspaceSettingsContent);
}
return '{}';
},
);
const settings = loadSettings(MOCK_WORKSPACE_DIR);
// @ts-expect-error: dynamic property for test
expect(settings.system.settings.configValue).toBe(
'system_value_for_system_read',
);
// @ts-expect-error: dynamic property for test
expect(settings.workspace.settings.configValue).toBe(
'workspace_value_for_workspace_read',
);
// Merged should take system's resolved value
// @ts-expect-error: dynamic property for test
expect(settings.merged.configValue).toBe('system_value_for_system_read');
// Restore original environment variable state
if (originalSharedVar !== undefined) {
process.env.SHARED_VAR = originalSharedVar;
} else {
delete process.env.SHARED_VAR; // Ensure it's deleted if it wasn't there before
}
});
it('should correctly merge dnsResolutionOrder with workspace taking precedence', () => {
(mockFsExistsSync as Mock).mockReturnValue(true);
const userSettingsContent = {
dnsResolutionOrder: 'ipv4first',
};
const workspaceSettingsContent = {
dnsResolutionOrder: 'verbatim',
};
(fs.readFileSync as Mock).mockImplementation(
(p: fs.PathOrFileDescriptor) => {
if (p === USER_SETTINGS_PATH)
return JSON.stringify(userSettingsContent);
if (p === MOCK_WORKSPACE_SETTINGS_PATH)
return JSON.stringify(workspaceSettingsContent);
return '{}';
},
);
const settings = loadSettings(MOCK_WORKSPACE_DIR);
expect(settings.merged.dnsResolutionOrder).toBe('verbatim');
});
it('should use user dnsResolutionOrder if workspace is not defined', () => {
(mockFsExistsSync as Mock).mockImplementation(
(p: fs.PathLike) => p === USER_SETTINGS_PATH,
);
const userSettingsContent = {
dnsResolutionOrder: 'verbatim',
};
(fs.readFileSync as Mock).mockImplementation(
(p: fs.PathOrFileDescriptor) => {
if (p === USER_SETTINGS_PATH)
return JSON.stringify(userSettingsContent);
return '{}';
},
);
const settings = loadSettings(MOCK_WORKSPACE_DIR);
expect(settings.merged.dnsResolutionOrder).toBe('verbatim');
});
it('should leave unresolved environment variables as is', () => {
const userSettingsContent = { apiKey: '$UNDEFINED_VAR' };
(mockFsExistsSync as Mock).mockImplementation(
@@ -592,12 +1090,57 @@ describe('Settings Loading and Merging', () => {
delete process.env.TEST_HOST;
delete process.env.TEST_PORT;
});
describe('when GEMINI_CLI_SYSTEM_SETTINGS_PATH is set', () => {
const MOCK_ENV_SYSTEM_SETTINGS_PATH = '/mock/env/system/settings.json';
beforeEach(() => {
process.env.GEMINI_CLI_SYSTEM_SETTINGS_PATH =
MOCK_ENV_SYSTEM_SETTINGS_PATH;
});
afterEach(() => {
delete process.env.GEMINI_CLI_SYSTEM_SETTINGS_PATH;
});
it('should load system settings from the path specified in the environment variable', () => {
(mockFsExistsSync as Mock).mockImplementation(
(p: fs.PathLike) => p === MOCK_ENV_SYSTEM_SETTINGS_PATH,
);
const systemSettingsContent = {
theme: 'env-var-theme',
sandbox: true,
};
(fs.readFileSync as Mock).mockImplementation(
(p: fs.PathOrFileDescriptor) => {
if (p === MOCK_ENV_SYSTEM_SETTINGS_PATH)
return JSON.stringify(systemSettingsContent);
return '{}';
},
);
const settings = loadSettings(MOCK_WORKSPACE_DIR);
expect(fs.readFileSync).toHaveBeenCalledWith(
MOCK_ENV_SYSTEM_SETTINGS_PATH,
'utf-8',
);
expect(settings.system.path).toBe(MOCK_ENV_SYSTEM_SETTINGS_PATH);
expect(settings.system.settings).toEqual(systemSettingsContent);
expect(settings.merged).toEqual({
...systemSettingsContent,
customThemes: {},
mcpServers: {},
includeDirectories: [],
});
});
});
});
describe('LoadedSettings class', () => {
it('setValue should update the correct scope and recompute merged settings', () => {
(mockFsExistsSync as Mock).mockReturnValue(false);
const loadedSettings = loadSettings(MOCK_WORKSPACE_DIR) as LoadedSettings;
const loadedSettings = loadSettings(MOCK_WORKSPACE_DIR);
vi.mocked(fs.writeFileSync).mockImplementation(() => {});
// mkdirSync is mocked in beforeEach to return undefined, which is fine for void usage
@@ -627,11 +1170,147 @@ describe('Settings Loading and Merging', () => {
'utf-8',
);
// Workspace theme overrides user theme
loadedSettings.setValue(SettingScope.Workspace, 'theme', 'ocean');
// System theme overrides user and workspace themes
loadedSettings.setValue(SettingScope.System, 'theme', 'ocean');
expect(loadedSettings.workspace.settings.theme).toBe('ocean');
expect(loadedSettings.system.settings.theme).toBe('ocean');
expect(loadedSettings.merged.theme).toBe('ocean');
});
});
describe('excludedProjectEnvVars integration', () => {
const originalEnv = { ...process.env };
beforeEach(() => {
process.env = { ...originalEnv };
});
afterEach(() => {
process.env = originalEnv;
});
it('should exclude DEBUG and DEBUG_MODE from project .env files by default', () => {
// Create a workspace settings file with excludedProjectEnvVars
const workspaceSettingsContent = {
excludedProjectEnvVars: ['DEBUG', 'DEBUG_MODE'],
};
(mockFsExistsSync as Mock).mockImplementation(
(p: fs.PathLike) => p === MOCK_WORKSPACE_SETTINGS_PATH,
);
(fs.readFileSync as Mock).mockImplementation(
(p: fs.PathOrFileDescriptor) => {
if (p === MOCK_WORKSPACE_SETTINGS_PATH)
return JSON.stringify(workspaceSettingsContent);
return '{}';
},
);
// Mock findEnvFile to return a project .env file
const originalFindEnvFile = (
loadSettings as unknown as { findEnvFile: () => string }
).findEnvFile;
(loadSettings as unknown as { findEnvFile: () => string }).findEnvFile =
() => '/mock/project/.env';
// Mock fs.readFileSync for .env file content
const originalReadFileSync = fs.readFileSync;
(fs.readFileSync as Mock).mockImplementation(
(p: fs.PathOrFileDescriptor) => {
if (p === '/mock/project/.env') {
return 'DEBUG=true\nDEBUG_MODE=1\nGEMINI_API_KEY=test-key';
}
if (p === MOCK_WORKSPACE_SETTINGS_PATH) {
return JSON.stringify(workspaceSettingsContent);
}
return '{}';
},
);
try {
// This will call loadEnvironment internally with the merged settings
const settings = loadSettings(MOCK_WORKSPACE_DIR);
// Verify the settings were loaded correctly
expect(settings.merged.excludedProjectEnvVars).toEqual([
'DEBUG',
'DEBUG_MODE',
]);
// Note: We can't directly test process.env changes here because the mocking
// prevents the actual file system operations, but we can verify the settings
// are correctly merged and passed to loadEnvironment
} finally {
(loadSettings as unknown as { findEnvFile: () => string }).findEnvFile =
originalFindEnvFile;
(fs.readFileSync as Mock).mockImplementation(originalReadFileSync);
}
});
it('should respect custom excludedProjectEnvVars from user settings', () => {
const userSettingsContent = {
excludedProjectEnvVars: ['NODE_ENV', 'DEBUG'],
};
(mockFsExistsSync as Mock).mockImplementation(
(p: fs.PathLike) => p === USER_SETTINGS_PATH,
);
(fs.readFileSync as Mock).mockImplementation(
(p: fs.PathOrFileDescriptor) => {
if (p === USER_SETTINGS_PATH)
return JSON.stringify(userSettingsContent);
return '{}';
},
);
const settings = loadSettings(MOCK_WORKSPACE_DIR);
expect(settings.user.settings.excludedProjectEnvVars).toEqual([
'NODE_ENV',
'DEBUG',
]);
expect(settings.merged.excludedProjectEnvVars).toEqual([
'NODE_ENV',
'DEBUG',
]);
});
it('should merge excludedProjectEnvVars with workspace taking precedence', () => {
const userSettingsContent = {
excludedProjectEnvVars: ['DEBUG', 'NODE_ENV', 'USER_VAR'],
};
const workspaceSettingsContent = {
excludedProjectEnvVars: ['WORKSPACE_DEBUG', 'WORKSPACE_VAR'],
};
(mockFsExistsSync as Mock).mockReturnValue(true);
(fs.readFileSync as Mock).mockImplementation(
(p: fs.PathOrFileDescriptor) => {
if (p === USER_SETTINGS_PATH)
return JSON.stringify(userSettingsContent);
if (p === MOCK_WORKSPACE_SETTINGS_PATH)
return JSON.stringify(workspaceSettingsContent);
return '{}';
},
);
const settings = loadSettings(MOCK_WORKSPACE_DIR);
expect(settings.user.settings.excludedProjectEnvVars).toEqual([
'DEBUG',
'NODE_ENV',
'USER_VAR',
]);
expect(settings.workspace.settings.excludedProjectEnvVars).toEqual([
'WORKSPACE_DEBUG',
'WORKSPACE_VAR',
]);
expect(settings.merged.excludedProjectEnvVars).toEqual([
'WORKSPACE_DEBUG',
'WORKSPACE_VAR',
]);
});
});
});

Some files were not shown because too many files have changed in this diff Show More